昨日届いたRaspberry Pi Pico Wのうち、ピンヘッダーをつけたやつを、以前買ったPico Inky Packに取り付けました。
で、こいつで天気予報を表示するやつを作るお話です。
元ネタ
元ネタは昔から動いている普通のラズパイ向けのやつです。今も現役なので、今回作ったもので置き換えるというわけではないです。
Inky pHATで天気予報を出す(常設ラズピッピがカオスになった話) - あっきぃ日誌
Pico W向けのCircuitPythonフラッシュストレージ小さすぎ問題
去年の12月からやりたいと考えていて、事前にネットワーク以外の部分をふつうのPicoで作り込んでおこうと思っていたものの、フォントをどうしようかなと思って少し調べたまま放置していました。
CircuitPythonのフラッシュストレージ領域はそれほど大きくなく、最新のバージョン8.0.4では、ふつうのPicoは1MB、PicoW向けは500KBしか用意されていません。ご、500!?キロバイト!?過去のバージョンをたどると、8.0.0のBeta1までは1MBだったようですが、Beta2から500KBに減らされてしまったようです。おそらく一つ前の記事に書いた"便利な"Web workflowのための確保と思われますが、ちょっと代償が大きすぎますかねェ……。去年ふつうのPicoでやってても足りねえと思ってたくらいなのに、厳しい〜〜。
今回使うライブラリを一通り配置しただけで40KBも取られますし、BMP画像も切り詰めて7KB、スクリプトが7KBなので、のこりは446KBくらいとなります。あと、この調子ではMacやWindowsが勝手に作る隠しファイルもファイル数によっては致命的なので、カツカツになってきたらゴリッと消してやると良さそうです。
Mac の .DS_Store や ._ から始まるファイルを自動的に消す方法 | 餃子マナー
find . \( -name '.DS_Store' -o -name '._*' -o -name '.apdisk' -o -name 'Thumbs.db' -o -name 'Desktop.ini' \) -delete -print
残り446KBあるとはいえ、おもな表示に使っているフォント2種はTTFの時点で200KBあり、CircuitPythonで使用できるBDF/PCF形式にすると結構でかくなるので、到底収まりません。漢字用のVLゴシックは4MBあるのでそもそも論外です。2種のほうは変換する前に使用しない文字をFontForgeでちまちま削っても良いですが、あまりにも手間なので、Pico W向けは思いきってアルファベットのみで対応します。
昔見つけたBIOSのフォントを再現してるところから東芝のBIOSフォントを拾ってきて、BDFファイルに変換しました。Adafruitのチュートリアル曰く、さらにPCFに変換すると小さくなるとのことでしたが、小さくならなかったのでBDFのまま使いました。
インストール
ライブラリはCircuitPythonのページからバンドルをダウンロードしてきて、以下をPico W上の/libに配置します。
- adafruit_bitmap_font
- adafruit_display_text
- adafruit_ntp.mpy
- adafruit_requests.mpy
- adafruit_uc8151d.mpy
コードはGitHubに置いたので、これらをダウンロードして、tenki-jma-for-picow.pyをcode.pyとして、settings-example.tomlの中身をいい感じに記述してsettings.tomlとしてそれぞれPico W上の/に配置します。あと、画像をディレクトリごと/に配置します。
コード
Pico Inky PackはABCの3つのボタンがあるので、それぞれを押すことで3地点まで天気予報が表示できるようにしました。元の状態では、今住んでる横浜市と地元の根室市が登録されています。また、起動時には横浜の天気を出すようにしています。コードの調べ方は各自でお願いします(雑)。
# 天気リスト(3つまで) weather_list = [ { # エリアコード: Kanagawa 'area_code': "140000", # サブエリアコード: East 'area_sub_code': "140010", # 市町村コード: Yokohama-shi 'area_city_code': "46106", # Label 'area_label': "Yokohama-shi" }, { # エリアコード: Kushiro 'area_code': "014100", # サブエリアコード: Nemuro 'area_sub_code': "014010", # 市町村コード: Nemuro-shi 'area_city_code': "18273", # Label 'area_label': "Nemuro-shi" }, ] startup_weather = 0
Pico Inky Packの電子ペーパーをCircuitPythonでいじる方法については、すでにやっている人がいたので、それに習いました。
Circuitpython Raspberry pi pico w SPI Invalid pins - adafruit industries
描画周りはCircuitPythonのdisplayioに沿ってイイカンジにやります。このあたりはちょっと前に画像の用意とともに少しやっていたので、それをガッチャンコしつつ、フォントを導入して体裁を整えました。あと、CircuitPython8.0.0からdisplay.update()がdeprecateになったらしいので、新しい書き方にしてみました。
ネットワーク接続周りはチュートリアルを参考にして、天気予報の情報取得は自分の元のスクリプトから流用しました。
途中までは順調に取得できていたのですが、気づいたらSSL証明書の確認でコケるようになってしまったので、やむを得ず確認を無効化しました。やり方はIssueを漁ったら出てきたので、ひとまずこれで対処。
ssl_context = ssl.create_default_context() ssl_context.load_verify_locations(cadata="") requests = adafruit_requests.Session(pool, ssl_context)
NTPは、明日の予報を出すか今日の予報を出すかの判定のために時刻が欲しいので、adafruit_ntpモジュールを使って取得します。取得後はntp.datetimeをそのまま使ってもいいですが、rtcモジュールを使ってPico Wの時刻に同期しました。これをやると、timeモジュールを使っても時刻が取得できるようになります。RTCの部分は前に作ったOTP表示機からのノウハウですね。
ntp = adafruit_ntp.NTP(pool, tz_offset=9, server=os.getenv('NTP_ADDRESS')) r = rtc.RTC() r.datetime = ntp.datetime rtc.set_time_source(r)
最後にループ部分。どうも電子ペーパーの連続書き換えを防止するためにdisplay.refresh()したあとは180秒のクールダウンタイムが設定されているらしく、書き換え可能かどうかがわからないので、クールダウンタイムの秒数を監視して、書き換え不可の間はPico W本体のLEDを点灯するようにしました。180秒とか意味あるのかな。どうせスクリプトがコケたらdisplayioがtraceを描画してくるし、リセットで回避もできるのに……。
まとめ
Pico Wで天気予報表示機を作るお話でした。フォントを諦めた時点で、あとは移植元のスクリプトとチュートリアルのコードの組み合わせでできたのであっさりではありましたが、SSL周りでだいぶ時間を持っていかれました……。
消費電力を測ってみたら、0.06Aほどでした。現状はWi-Fiにつないだまま次のボタン操作のためにループしているので、取得時以外はWi-Fiを切ったり、ディープスリープに入っても良さそうな気がしますね。これは次回挑戦しましょう。
あと、Pico W向けCircuitPythonのフラッシュストレージ500KB縛りはけっこう辛いので、可能ならWeb workflowなしでストレージが1MBあるイメージファイルも用意してほしいなァと思うなどしました……。まぁ、便利なんですけど、500KB削られてまで欲しくはないかなァ〜。