Buffalo wmr-300のVLAN

ついこの間、ルーターに使っていたTVBOXのemmcが物理的に壊れて再構築することになりました。fsckで修復を試みたのですが、lxcのファイルがかなり壊れて修復不可能だったのでファイルシステムを再構築してクリーンインストールを試みました。ところがしばらくするとファイルシステムがread-onlyになってしましい、syslogにもファイルシステムのエラーが記録されてました。とりあえずはインターネットにつなぐルーターなので、余っているfon2405eで急場を凌ぎました。

同スペックの余っているTVBOXがあるので、それを使うことにしたのですが、元のルーターには複雑な設定になっており、そう簡単には復旧できません。LANポートが一つなのでVLANの設定、firewall、IPフォン用のsiproxd、WiFi用のfreeradiusとsnmpログのcactiと他にTinc、StrongSwan、WireguardのVPNとDDNSの設定と盛りだくさんです。急場凌ぎならfonでもいいのですが、メモリが32MBのため、freeradiusはちょっと無理そうです。そこでwmr-300なのですが64MBのメモリがあるので、バックアップ用のルータとして適任と思われるのですが、一つ問題がありました。OpenWrtの初期値でVLANが使えません。もともとLANポートが1つしかないので必要ないと言えばそれまでですが、MediatekのポピュラーのCPUなのでVLANが使えます。

そこで試行錯誤でVLANを使えるようにしました。fonの時もそうだったのですが、物理的のLANポートを探すことから始めます。物理ポートと違うポートにするとまず繋がりません。VLANを設定するには次の3つのファイルを編集します。

/etc/board.d/02_network: wmr-300-16Mを追加しました。wmr-300-16Mはwmr-300のSPIフラッシュを16MBに換装したものです。

ramips_setup_interfaces()
{
        local board="$1"

        case $board in
        ...
        
         wmr-300-16M)
                ucidef_add_switch "switch0" \
                        "4:lan" "6@eth0"
                ;;
        *)
                RT3X5X=`cat /proc/cpuinfo | egrep "(RT3.5|RT5350)"`
        ...

/etc/board.json: VLANのトポロジーを定義しているようです。追加します。

        ....
        "switch": {
                "switch0": {
                        "enable": true,
                        "reset": true,
                        "ports": [
                                {
                                        "num": 4,
                                        "role": "lan",
                                },
                                {
                                        "num": 6,
                                        "device": "eth0",
                                        "need_tag": false,
                                        "want_untag": false
                                }
                        ],
                        "roles": [
                                {
                                        "role": "lan",
                                        "ports": "4 6t",
                                        "device": "eth0.1"
                                },
                        ]
                }
        },
        ....

/etc/config/network: VLANの設定です

config switch
        option name 'switch0'
        option reset '1'
        option enable_vlan '1'

config switch_vlan
        option device 'switch0'
        option vlan '1'
        option vid '1'
        option ports '4 6t'

これでVLANが使えるようになりました。

WireGuard with bridge interface?

色々調べてみてもWireGuardはLayer 3なのでTapデバイスは使えない。それゆえブリッジは無理っていう結論なのですが、調べてみるとなんとなくできそうなサイトが見つかりました。

GRE tunneling adds an additional GRE header between the inside and outside IP headers. In theory, GRE could encapsulate any Layer 3 protocol with a valid Ethernet type, unlike IPIP, which can only encapsulate IP.

An introduction to Linux virtual interfaces: Tunnels

2つのネットワークを同一ネットワークとして使うにはTunデバイスではなくTapデバイスを使います。WireGuardではTapデバイスが使えないのですが、サイトによると鍵はGRETAPを使うことです。そこが少し混乱するのですがIPv6で動くようです。これを使うにはカーネルモジュールのbr_netfilterをブート時にロードしておく必要があります。私のルーターで調べてみると、

br_netfilter           28672  0
bridge                233472  1 br_netfilter
ipv6                  622592  77 bridge,br_netfilter,wireguard

このようになっておりIPv6にbr_netfilterがリンクしています。そのあとはWireGuardのwg0.confでタップデバイスを立ち上げてブリッジインターフェースにリンクするという方法をとります。

PostUp = ip link add name gretap1 type gretap local 192.168.92.1 remote 192.168.178.1 
PostUp = ip link set gretap1 up
PostUp = ip link set gretap1 master br0

Tinc VPNではTapデバイスが使えるので簡単に同じネットワーク同士を繋ぐことができるのですが、WireGuardでも同様のことができるかもしれません。

OpenWrt

この情報をもとに早速OpenWrtで試してみました。機種はなんでも良いのですが先日、大量生産したOpenWrt化したfon2405eがあるのでそれを使いました。なかなか思うようにいかずOpenWrtのフォーラム等で検索して何度かトライしたらできました。

まずは802.11sの暗号なしのメッシュネットワークを作ります。次にWireGuardのネットワークを作ります。この時問題なのがWireGuardの設定をするとパケットが全てDefualt Gatewayに流れてしまい接続ができなくなるので、手動でルーティングから削除します。OpenWrtのリポジトリからGretapのパッケージをインストールして/etc/config/networkの設定をします。

packages

gre - 1-11
kmod-gre - 4.14.195-1
kmod-gre6 - 4.14.195-1
kmod-br-netfilter - 4.14.195-1

WireGuard (interface)

Firewall

General SettingsのInputを有効にしないとなぜか繋がりません。

/etc/config/network

'''

config interface 'lan'
	option type 'bridge'
	option proto 'static'
	option ipaddr '192.168.1.17'
	option netmask '255.255.255.192'
	option ip6assign '60'
	option gateway '192.168.1.1'
	list dns '8.8.8.8'
	list dns '1.1.1.1'
	option macaddr 'xx:xx:xx:xx:xx:xx'
	option ifname 'eth0.1 gre4t-gt'


...

config interface 'mesh'
	option proto 'static'
	option netmask '255.255.255.0'
	option ipaddr '10.101.0.17'

config interface 'wg'
	option proto 'wireguard'
	option private_key 'xxxxxxxxxxxxxxxx'
	option listen_port '8889'
	list addresses '192.168.2.17/26'

config wireguard_wg
	option public_key 'xxxxxxxxxxxxxxxx'
	option persistent_keepalive '25'
	option endpoint_port '8888'
	option endpoint_host '10.101.0.14'
	list allowed_ips '192.168.2.0/26'
	list allowed_ips '10.101.0.0/24'
	list allowed_ips '192.168.1.0/26'

config interface 'gt'
    option type 'bridge'
	option proto 'gretap'
	option ipaddr '192.168.2.17'
	option peeraddr '192.168.2.14'
	#option force_link '1'
	#option mtu '1500'
    option delegate '0'

ルーティングの修正

ip r del 10.101.0.14 via 192.168.1.1
ip r del 192.168.2.14 via 192.168.1.1

追記

firewallの追加です。

/etc/firewall.user

iptables -A FORWARD -p tcp --tcp-flags SYN,RST SYN -j TCPMSS --clamp-mss-to-pmtu;

routingの問題はOpenWrt仕様の特有の問題なので、それを回避するためにInterfacesの設定ではDefault GWの設定はしません。その代わりにあまり良い方法とは言えませんがrc.localで設定します。

/etc/rc.local

sleep 20
ip route add default via 192.168.1.1 dev br-lan

exit 0

追記1.1

OpenWrtのソースを眺めていたら、隠し設定があるらしいことに気がつきました。検索して調べてみると公式HPでも記述がありました。Wireguardをデフォルトルート以外で使うときはこの設定(option nohostroute '1')を使えば良さそうです。

  • https://openwrt.org/docs/guide-user/network/tunneling_interface_protocols

/etc/config/network (例)

onfig interface 'wg'
	option proto 'wireguard'
	option private_key 'xxxxxxxxxxxxxxxx'
	option listen_port '8001'
	option nohostroute '1'
	list addresses '192.168.2.6/26'

config wireguard_wg
	option endpoint_port '8000'
	option public_key 'xxxxxxxxxxxxxxxx'
	option endpoint_host '10.200.0.100'
	option persistent_keepalive '25'
	option nohostroute '1'
	list allowed_ips '192.168.1.0/26'
	list allowed_ips '192.168.2.0/26'

config interface 'gt'
	option type 'bridge'
	option proto 'gretap'
	option ipaddr '192.168.2.6'
	option peeraddr '192.168.2.5'
	option nohostroute '1'
	option delegate '0'

追記2

Firewallルールを少し変えてみました。

General settingsのInputをrejectにした場合、greプロトコルを通すためにiptablesのルールを追加します。

/etc/firewall.user

iptables -I INPUT -p gre -j ACCEPT
iptables -A FORWARD -p tcp --tcp-flags SYN,RST SYN -j TCPMSS --clamp-mss-to-pmtu;

さらにOpenWrtのパッケージをインストールします。

opkg install kmod-ipt-nat-extra

参考

  • https://notes.superlogical.ch/pages/note_wg/nolayer2/
  • https://openwrt.org/docs/guide-user/network/tunneling_interface_protocols
  • https://forum.openwrt.org/t/setup-fastest-pseudowire-protocol/36219/11
  • https://forum.openwrt.org/t/solved-gretap-tunnel-with-vlan-802-1q-tagging/20742/2
  • https://backreference.org/2013/07/23/gre-bridging-ipsec-and-nfqueue/
  • https://developers.redhat.com/blog/2019/05/17/an-introduction-to-linux-virtual-interfaces-tunnels/
  • https://medium.com/@david.waiting/a-beginners-guide-to-generic-routing-encapsulation-fb2b4fb63abb
  • https://forum.archive.openwrt.org/viewtopic.php?id=59150

UPDATE: Kindle Weather Display

OpenWeatherMapのHPを見ていたらOne Call APIというのが新設されていて、現在、時間ごと、日付ごとの天気情報のデータを一度にダウンロードできるAPIがありました。今までは3時間ごとの天気情報をマニアックなやり方で日ごとの予測を弾き出していたのですが、このAPIを使えばシンプルなプログラムになります。この際なのでAPIをモジュール化して整理しました。ところが問題があって、すべてのローケーションでOne Call APIが使えるわけではなく、一部データが欠損するということがわかりました。まだ安定して使えないということです。それなので旧バージョンと新バージョンを分けることにしました。One Call APIは降雨予想があったので雲アイコンに載せてみました。

OpenWrt: mac addressの書き換え

数台のfon2405eのSPiフラッシュを載せ替えたまではいいのですが、一つ困った大きな問題があります。同じROMなのでmac addressが同じになってしまい、同時に起動していると通信障害が置きます。arpコマンドでHW addressを見てみればわかるように、同じmac addressだと衝突します。OpenWrtには/config/networkでmac addressのカスタム設定という方法があるのですが、なぜかfonには使えません。そこでフラッシュROMのEEPROM書き換えてmac addressを変えてみます。OpenWrtで予備知識を得られるのでまず目を通して置きます。

  • https://openwrt.org/docs/guide-user/installation/restore_art_partition
  • https://openwrt.org/docs/guide-developer/mac.address

これによると、rt305xの場合は+4なのでhexdumpで確認します。

hexdump -n 100 -C mtd2.bin
00000000  50 30 00 01 00 0c 43 30  50 88 ff ff ff ff ff ff  |P0....C0P.......|
00000010  ff ff ff ff ff ff ff ff  ff ff ff ff ff ff ff ff  |................|
00000020  ff ff ff ff ff ff ff ff  00 0c 43 30 50 77 00 0c  |..........C0Pw..|
00000030  43 30 50 66 11 05 24 00  ff ff 2f 01 55 77 a8 aa  |C0Pf..$.../.Uw..|
00000040  8c 88 ff ff 0c 00 00 00  00 00 00 00 00 00 ff ff  |................|
00000050  ff ff 0d 0d 0d 0d 0c 0c  0c 0c 0c 0c 0c 0c 0c 0c  |................|
00000060  10 10 11 11                                       |....|
00000064

00 0c 43 30 50 88がralink SDKで作ったEEPROMのmac addressと一致しました。

そこで先頭のmac addressだけ変更することにします。

echo -n -e '\x01' | dd of=mtd2.bin bs=1 seek=4 conv=notrunc

hexdump -n 100 -C mtd2.bin
00000000  50 30 00 01 01 0c 43 30  50 88 ff ff ff ff ff ff  |P0....C0P.......|
00000010  ff ff ff ff ff ff ff ff  ff ff ff ff ff ff ff ff  |................|
00000020  ff ff ff ff ff ff ff ff  00 0c 43 30 50 77 00 0c  |..........C0Pw..|
00000030  43 30 50 66 11 05 24 00  ff ff 2f 01 55 77 a8 aa  |C0Pf..$.../.Uw..|
00000040  8c 88 ff ff 0c 00 00 00  00 00 00 00 00 00 ff ff  |................|
00000050  ff ff 0d 0d 0d 0d 0c 0c  0c 0c 0c 0c 0c 0c 0c 0c  |................|
00000060  10 10 11 11                                       |....|
00000064

先頭が変わって01 0c 43 30 50 88になりました。これをmtdに書き込んで再起動します。

opkg install kmod-mtd-rw --force-depends
insmod /lib/modules/4.14.195/mtd-rw.ko i_want_a_brick=1
mtd -r write mtd2.bin factory

確かに変わってるのですが、期待したものと全く違っていました。原因は不明なもののmac addressの変更はこれでできました。

4: br-lan: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP qlen 1000
    link/ether 12:16:xx:xx:xx:xx brd ff:ff:ff:ff:ff:ff

Kindle: timelitのdark mode

Kindle weather displayとKindle news feed displayをダークモード対応にしたのでtimelitも対応させることにしました。自動的に切り替えは意外と落とし穴があって難しい。Kindle weatherは取り寄せるデータに日の出、日の入りがあるのでそのまま使えます。タイムゾーンに合わせてあるので問題は起こりません。ところがPythonモジュールで計算するとUTCを基準にしているので日付がずれます。これはなかなか厄介ですが、日付部分を取り除いて比較すればできます。当然、1日分違うため数分のずれが生じるので正確ではありませんがアプリケーションの精度からみて問題ありません。

timelitはもう少し複雑です。シェルでPythonを動かすのであまり複雑なスクリプトは組めません。さらにPython2なのでPython3より複雑になります。さらにpip等でモジュールをインストールできないので自前で用意しなければなりません。

色々調べると解決策がいくつかあり、特にモジュールをインストールしなくても比較的簡単に算出できる方法がありました。このスクリプトの問題は、日付を無視するので時間だけの情報になります。

Sun.py

import math
import datetime
import time

class Sun:

    def getSunriseTime( self, coords ):
        return self.calcSunTime( coords, True )

    def getSunsetTime( self, coords ):
        return self.calcSunTime( coords, False )

    def getCurrentUTC( self ):
        now = datetime.datetime.now()
        return [ now.day, now.month, now.year ]

    def calcSunTime( self, coords, isRiseTime, zenith = 90.8 ):

        # isRiseTime == False, returns sunsetTime

        day, month, year = self.getCurrentUTC()

        longitude = coords['longitude']
        latitude = coords['latitude']

        TO_RAD = math.pi/180

        #1. first calculate the day of the year
        N1 = math.floor(275 * month / 9)
        N2 = math.floor((month + 9) / 12)
        N3 = (1 + math.floor((year - 4 * math.floor(year / 4) + 2) / 3))
        N = N1 - (N2 * N3) + day - 30

        #2. convert the longitude to hour value and calculate an approximate time
        lngHour = longitude / 15

        if isRiseTime:
            t = N + ((6 - lngHour) / 24)
        else: #sunset
            t = N + ((18 - lngHour) / 24)

        #3. calculate the Sun's mean anomaly
        M = (0.9856 * t) - 3.289

        #4. calculate the Sun's true longitude
        L = M + (1.916 * math.sin(TO_RAD*M)) + (0.020 * math.sin(TO_RAD * 2 * M)) + 282.634
        L = self.forceRange( L, 360 ) #NOTE: L adjusted into the range [0,360)

        #5a. calculate the Sun's right ascension

        RA = (1/TO_RAD) * math.atan(0.91764 * math.tan(TO_RAD*L))
        RA = self.forceRange( RA, 360 ) #NOTE: RA adjusted into the range [0,360)

        #5b. right ascension value needs to be in the same quadrant as L
        Lquadrant  = (math.floor( L/90)) * 90
        RAquadrant = (math.floor(RA/90)) * 90
        RA = RA + (Lquadrant - RAquadrant)

        #5c. right ascension value needs to be converted into hours
        RA = RA / 15

        #6. calculate the Sun's declination
        sinDec = 0.39782 * math.sin(TO_RAD*L)
        cosDec = math.cos(math.asin(sinDec))

        #7a. calculate the Sun's local hour angle
        cosH = (math.cos(TO_RAD*zenith) - (sinDec * math.sin(TO_RAD*latitude))) / (cosDec * math.cos(TO_RAD*latitude))

        if cosH > 1:
            return {'status': False, 'msg': 'the sun never rises on this location (on the specified date)'}

        if cosH < -1:
            return {'status': False, 'msg': 'the sun never sets on this location (on the specified date)'}

        #7b. finish calculating H and convert into hours

        if isRiseTime:
            H = 360 - (1/TO_RAD) * math.acos(cosH)
        else: #setting
            H = (1/TO_RAD) * math.acos(cosH)

        H = H / 15

        #8. calculate local mean time of rising/setting
        T = H + RA - (0.06571 * t) - 6.622

        #9. adjust back to UTC
        UT = T - lngHour
        UT = self.forceRange( UT, 24) # UTC time in decimal format (e.g. 23.23)

        #10. Return
        hr = self.forceRange(int(UT), 24)
        min = round((UT - int(UT))*60,0)

        def convert_unix_time(datetime_obj):
            return int(time.mktime(datetime_obj.timetuple()))

        unixtime = convert_unix_time(datetime.datetime(int(year), int(month), int(day), int(hr), int(min), int(0)))

        return {
            'status': True,
            'decimal': UT,
            'hr': hr,
            'min': min,
            'unixtime': unixtime
        }

    def forceRange( self, v, max ):
        # force v to be >= 0 and < max
        if v < 0:
            return v + max
        elif v >= max:
            return v - max

        return v

timelit.sh.diff

--- timelit.sh.orig	2020-09-17 22:45:55.414873702 +0000
+++ timelit.sh	2020-09-17 22:45:37.886601266 +0000
@@ -1,11 +1,19 @@
 #!/bin/bash

+cd "$(dirname "$0")"
+
 # if the Kindle is not being used as clock, then just quit
 test -f /mnt/us/timelit/clockisticking || exit

+# my latitude and longitude
+lat=35.8034315
+lng=139.4275599
+offset=+9
+dark_mode='Auto'

 # find the current minute of the day
-MinuteOTheDay="$(env TZ=CEST date -R +"%H%M")";
+#MinuteOTheDay="$(env TZ=CEST date -R +"%H%M")";
+MinuteOTheDay="$(env TZ=GMT-9 date -R +"%H%M")";

 # check if there is at least one image for this minute
 lines="$(find /mnt/us/timelit/images/quote_$MinuteOTheDay* 2>/dev/null | wc -l)"
@@ -16,9 +24,40 @@
 	echo $lines' files found for '$MinuteOTheDay
 fi

+exec_dark_mode() {
+    t_sunrise=$(python -c "from Sun import Sun; coords = {'longitude' : $lng, 'latitude' : $lat }; sun = Sun(); print sun.getSunriseTime( coords )['unixtime']")
+    t_sunset=$(python -c "from Sun import Sun; coords = {'longitude' : $lng, 'latitude' : $lat }; sun = Sun(); print sun.getSunsetTime( coords )['unixtime']")
+    t_now=$(date '+%s')
+
+    [ "$t_sunrise" -gt "$t_sunset" ] && t_sunrise=$(( $t_sunrise - 86400 ))
+
+    t_sunrise=$(( ($t_sunrise + $offset * 3600) % 86400 ))
+    t_sunset=$(( ($t_sunset + $offset * 3600) % 86400 ))
+    t_now=$(( ($t_now + $offset * 3600) % 86400 ))
+
+    if [ "$t_now" -lt "$t_sunrise" ] || [ "$t_now" -gt "$t_sunset" ]; then
+        mode='dark_mode/'
+    else
+        mode=''
+    fi
+}
+
+case "$dark_mode" in
+    "Auto")
+        exec_dark_mode
+        ;;
+    "True")
+        mode='dark_mode/'
+        ;;
+    *)
+        mode=''
+        ;;
+esac
+

 # randomly pick a png file for that minute (since we have multiple for some minutes)
-ThisMinuteImage=$( find /mnt/us/timelit/images/quote_$MinuteOTheDay* 2>/dev/null | python -c "import sys; import random; print(''.join(random.sample(sys.stdin.readlines(), int(sys.argv[1]))).rstrip())" 1)
+#ThisMinuteImage=$( find /mnt/us/timelit/images/quote_$MinuteOTheDay* 2>/dev/null | python -c "import sys; import random; print(''.join(random.sample(sys.stdin.readlines(), int(sys.argv[1]))).rstrip())" 1)
+ThisMinuteImage=$( find /mnt/us/timelit/images/${mode}quote_$MinuteOTheDay* 2>/dev/null | python -c "import sys; import random; print ''.join(random.sample(sys.stdin.readlines(), int(sys.argv[1]))).rstrip()" 1)

 echo $ThisMinuteImage > /mnt/us/timelit/clockisticking

dark mode用の画像を作成します。

mkdir /mnt/us/timelit/images/dark_mode
cd /mnt/us/timelit/images/dark_mode

convert.sh

#!/bin/sh

ls ../*.png |while read f; do
    echo -n "- converting ${f#../}:"
    convert -size 600x800 -background white -depth 8 -negate "$f" "${f#../}"
    pngcrush -s -c 0 -ow "${f#../}"
    echo " result $?"
done

ソース

  • https://www.instructables.com/id/Literary-Clock-Made-From-E-reader/
  • https://stackoverflow.com/questions/19615350/calculate-sunrise-and-sunset-times-for-a-given-gps-coordinate-within-postgresql

dropbear client: pub-key authentication

今まではOpenssh Clientを使ったpub-keyのsshアクセスをやっていましたが、どうも漠然とした疑問があり、デフォルトインストールの dropbearでpub-keyアクセスはできないものかとずっと思っていました。というのはOpenWrtをインストールしたルーターは厳しい容量で運用しなければなりません。せっかくdropbeaarがあるのにOpensshクライアントを入れるのは無駄だと思われるからです。

色々と調べてみてどうやらできることがわかりました。

手順

OpenWrtは導入後に秘密鍵を持っています。それをpub-keyに変換します。

cd /etc/dropbear
dropbearkey -y -f dropbear_rsa_host_key | grep "^ssh-rsa " > dropbear_rsa_host_key.pub

秘密鍵をデフォルトローケーションに置きます。

mkdir /root/.ssh
cd /root/.ssh
ln -s /etc/dropbear/dropbear_rsa_host_key id_dropbear

pug-keyをリモート先のdropbearに登録します。

scp dropbear_rsa_host_key.pub root@<remote IP>:/tmp
ssh root@<remote IP>

[remote login]

cd /etc/dropbear
cat /tmp/dropbear_rsa_host_key.pub >> authorized_keys

秘密鍵を指定してログインする方法

ssh root@<remote IP> ~i /etc/dropbear/dropbear_rsa_host_key

buffalo wmr-300(トラベルルータ)のフラッシュROMを16MBに換装

fon2405eでSPIフラッシュROMを取り替えが成功したので、他のルーターもやってみました。取り寄せたSPIフラッシュメモリの在庫がかなりあるのでできれば16MBにすれば使い勝手がかなり向上します。8MBでも十分OpenWrtが使えるのですが、本格的に使おうとすると最低16MBは必要です。wmr-300はかなり小さいくトラベルルータとして持ち運びでき重宝するので、SPIフラッシュを取り替えることにしました。

今回はfon2405eの時のようにu-bootからファームウェアを焼かずに、フラッシュROM全体を移し替えます。下図のログは換装後の16MBフラッシュROMですが、”u-boot”、”u-boot-env”、 “factory” 、”firmware”をddコマンドで吸い出して、catコマンド繋ぎ合わせれば、アドレスの連続したファイルが出来上がり全体のフラッシュROMのコピーになります。”firmware”パーティションのアドレスが物理アドレスより大きくなっていますが、物理的に切られるので問題ありません。そのコピーを直接、フラッシュROMに書き込みます。書き込み後、ブートしてデバイスツリーでパーティションを拡大してビルドした新しいファームウェアにアップグレードします。

[    0.540941] m25p80 spi0.0: mx25l12805d (16384 Kbytes)
[    0.551116] 4 fixed-partitions partitions found on MTD device spi0.0
[    0.563789] Creating 4 MTD partitions on "spi0.0":
[    0.573341] 0x000000000000-0x000000030000 : "u-boot"
[    0.584223] 0x000000030000-0x000000040000 : "u-boot-env"
[    0.595739] 0x000000040000-0x000000050000 : "factory"
[    0.606752] 0x000000050000-0x000001000000 : "firmware"
[    0.620737] 2 uimage-fw partitions found on MTD device firmware
[    0.632600] Creating 2 MTD partitions on "firmware":
[    0.642510] 0x000000000000-0x00000017e04d : "kernel"
[    0.653323] 0x00000017e04d-0x000000fb0000 : "rootfs"
[    0.664142] mtd: device 5 (rootfs) set to be root filesystem
[    0.676980] 1 squashfs-split partitions found on MTD device rootfs
[    0.689368] 0x000000380000-0x000000fb0000 : "rootfs_data"

mt7620だと16MB以上のフラッシュROMを積めるのですが、問題はu-bootが対応していないとブートできないという問題があります。u-bootのソースはあるにはあるのですが、いざコンパイルして使うとなるとリスクが大きすぎます。今回は余っているフラッシュROMを使うことにしたので、16MBまでならオリジナルのu-bootで十分対応可能です。

OpenWrtはデバイスツリーをSPIフラッシュROMを16MB用に変えるだけで他には変更は加えていません。

リソース

UPDATE

バグはできるだけ潰したのですがどうしても一つだけ潰せないものがありました。デフォルトのネットワークの設定がどうしてもswitchの仕様になります。解決方法はfailesafeでsshログインしてmount_rootでrootfsにマウントし/overlay/upper/etc/config/networkを書き換えます。

config interface 'loopback'
	option ifname 'lo'
	option proto 'static'
	option ipaddr '127.0.0.1'
	option netmask '255.0.0.0'

config globals 'globals'
	option ula_prefix 'fd7a:751a:4b5e::/48'

config interface 'lan'
	option type 'bridge'
	option ifname 'eth0'
	option proto 'static'
	option ipaddr '192.168.211.1'
    option netmask '255.255.255.0'
	option ip6assign '60'

config device 'lan_eth0_dev'
	option name 'eth0'
	option macaddr 'xx:xx:xx:xx:xx:xx'

Kindle news feed

前回、Kindle weather displayが思った以上に良い出来でした。news feedのアイディアが浮かんだので作ってみることにしました。今回はsubprocessモジュールを使うことになったのですが、どうも使ってみると外部コマンドが終了しないうちから次のPythonスクリプトを実行してしまうらしく、外部コマンドの実行に失敗します。あまり良い方法ではありませんが、実行終了まで十分な時間を入れて問題を回避しました。このnews feedの肝は画像をベクター化したことです。ただのnews feedで文字列を表示しても面白くないので画像を入れてヴィジュアル化しました。

リソース

Kindle Weather Display

前回は古いキンドル3を洒落た時計にしました。今回は天気情報にチャレンジしてみます。参考したサイトではDarkSkyを使っていたのですが、調べたらどうも終了らしい、ということでOpenWeatherMapを使うことにしました。やってみると意外と大変で、特に摺り合わせ作業が多く検証にとまどりましたが、どうにかできました。次はサーバからどうやって送り込むかという課題のみです。

ホストサーバーはOpenWrtとArmbian(TV-Box)で試してみました。OpenWrtのフラッシュROMは16GB以上必要です。16MBのフラッシュで収まるか難しいところで、パッケージを整理すればなんとか収まりそうです。USBのある32MBの古いルーターがあったのでそれを使いました。OpenWrtの問題はSVG処理ができないため、Kindle側ですることになります。Kindle側では画像処理のプログラムがないので、ChrootのDebian Wheezyを使います。これ以降のバージョンはKindleのカーネルが古いため使えません。Armbianは最新のDebianが動くのでそちらの方がおすすめです。

この天気アプリは先日セットアップした時計プログラムと交互に表示するように調整します。

0,10,15,20,25,30,40,45,50,55 * * * * sh /mnt/us/timelit/timelit.sh
5,35 * * * * sh /mnt/us/weather-kindle/weather-script.sh
56 0,8,16 * * * sh /mnt/us/weather-kindle/update_time.sh

ホスト側のcron設定例

/etc/cron.d/weather-kindle
0,15,30,45 * * * * root sh -c "/var/lib/weather-kindle-host/weather-kindle.sh"

Kindleとホスト間はP2PのUSBnetで繋げてセキュリティ上問題がある古いキンドルのパケットが外に流れないようにファイヤーウォールを導入します。その際、時間の調整が必要になるのでホスト側にntpサーバをインストールしてKindleからアクセスできるようにしてntpdateで時間を定期的にアップデートします。Kindleのntpdateはどうも相性が悪いらしくアップデートしてくれないので、Debianのntpdateを使っています。

Source

参考

  • https://gitlab.com/iero/Kindle-weather-station/-/tree/master/

KINDLE 3: 時計、Timelitをインストールする

Timelitというプログラムがなかなか良さそうなのでインストールしました。最初起動に失敗したので、ファームウェアのアップデートを行ったところ、以前のkindle 3 hackの非公式のプログラムは起動できなくなったようです。代わりにこのtimelitプログラムが起動できるようになりました。ショートカットはShift+cです。

古いキンドルを引っ張り出してきたのはE-inkを何かに使えないか調べてみるためでした。オープンソースでGPUにアクセスできないため、これは無理だという結論になったのですが、有効利用できるか調べたところ、Tlimelitというプログラムに出会ったというわけです。

このプログラムはimagesのフォルダーにある画像を1分刻みで更新するプログラムなので、応用すれば使えそうだなという気がしますが、なかなか粋なプログラムなのでしばらく使ってみることにしました。

日本時間のタイムゾーンの変更は参考サイトを参照して、TZ=GMT-9に変更します。/etc/rtcrontab/rootを変更して1分刻みで更新するように設定します。

サイトでは1分おきになってますが、好みで1分おきだと気になってしょうがないので5分おきに変えて使っています。cronは起動スクリプトに難ありで起動はできるが止めることができません。killコマンドでプロセスと止めます。

*/5 * * * * sh /mnt/us/timelit/timelit.sh

ちなみにspiインターフェースのE-inkがアマゾンで売られており、これならばルーターやOPiにつなげて使えそうですが、小さい画面でも値段が高いですね。これは今後どうするか検討してみたいと思います。

時間が数分ずれているので調整してみました。相性の関係かwifiは切れるので古いルーター、おそらく802.11bなのかも知れません。それなのでwifi は諦めてusbnetで接続します。

ホスト側 (IP: 192.168.2.1/24)

sysctl -w net.ipv4.ip_forward=1
sysctl net.ipv4.ip_forward
iptables -P FORWARD ACCEPT
iptables -A POSTROUTING -t nat -j MASQUERADE -s 192.168.2.0/24

KIndle 3 (IP: 192.168.2.2/24)

ip route add default via 192.168.2.1
echo "nameserver 8.8.8.8" >> /etc/resolv.conf
ntpdate ntp.nict.jp
19 Aug 07:23:36 ntpdate[12585]: step time server 133.243.238.164 offset 165.691015 sec

参考

  • Literary Clock Made From E-reader : https://www.instructables.com/id/Literary-Clock-Made-From-E-reader/