Particle XenonのCircuitPython化
今年始めにParticleがMeshからの撤退を発表し、Mesh専用機のXenonのディスコンが決まったのだが、まあ驚きはなかった。この記事で書いた通り、最初は「おおっ」と思うが全然つながらずテンションだだ下がり、結局そのまま不安定で使い物にならなかったもんな。
うちには自分で買った分と配送ミスのお詫びに送られてきたものと計6台のXenonがあるのだが、これをどうするか。どうやらCircuitPythonが走るらしいので、1台試してみようというのが今回のお題。
1. XenonへのCircuitPythonの書き込み
基本的には CircuitPython on Xenon | Tutorials | Particle を追って行けばよいが、いろいろハマったのでそれらの場所を中心にメモしてみる。作業は最初はWindowsで始めて、途中で問題が発生したのでUbuntuのVMに切り替えることになったが、最初からUbuntuでも構わないはず。
準備: Particle Debuggerを買う
ブートローダーの書き換えにParticle Debuggerが必要らしいので、お詫びクレジット$30で買ってみる。送料はクレジットが使えないので約$8を仕方なく支払い、一週間ほどで届いた。
準備: OpenOCDをインストールする
Liviu Ionescu氏によるビルド済みバイナリを使わせてもらう。
https://github.com/xpack-dev-tools/openocd-xpack/releases/
Note: 最初Freddie Chopin氏のバイナリを使ったのだが、確認コマンドを実行すると Error: invalid command name "dap"
と言われて使えなかった。調べてみると同じ問題↓に当たっている人たちがいて、原因はわからないがなんかダメらしい。
https://stackoverflow.com/questions/53714503/openocd-error-invalid-command-name-dap-cant-connect-blue-pill-via-st-link
ブートローダーの置き換え
ブートローダーをここ↓からダウンロードする。自分でビルドする必要はない。
https://github.com/adafruit/Adafruit_nRF52_Bootloader/releases
ここから "particle_xenon_bootloader-xxx.hex" という名前のものを選ぶ。
Particle DebuggerでXenonをPCに接続し、OpenOCDを使って書き込む。実際に打ち込むコマンドは下記の通りで、チュートリアルにあるもの(Particle Workbench付属のOpenOCDを使う方法)と微妙に違った。
bin/openocd -f scripts/interface/cmsis-dap.cfg -f scripts/target/nrf52.cfg -c "adapter_khz 1000" -c "init" -c "program particle_xenon_bootloader-xxx.hex 0x000000 verify reset" -c "exit"
このステップが正常に完了すると、Xenonが再起動し、XENONBOOTという名前のドライブがPCから見えるようになる。
CircuitPythonの書き込み
ここ↓からCircuitPythonの最新安定版のUF2ファイルを取ってきて、XENONBOOTフォルダに放り込む。
https://circuitpython.org/board/particle_xenon/
するとXenonが再起動し、CIRCUITPYという名前のドライブがPCから見えるようになる…はずが、見えない。FAQにはAntiVirusをオフにしろなどと書かれているが、簡単に解決しそうもない予感がするので、Windowsでの作業はやめてここから先はUbuntuで行うことにする。
VMware上のUbuntuを立ち上げ、Xenonをゲスト側に接続すると、ちゃんと /media/<ユーザ名>/CIRCUITPY が見えるようになった。
ここでサンプルにある code.py をCIRCUITPYに置いて、LEDがチカチカすればとりあえずは成功である。
REPLのテスト
CircuitPythonは対話的インタプリターが使える。詳しいやり方はここ。
https://learn.adafruit.com/welcome-to-circuitpython/advanced-serial-console-on-mac-and-linux
Ubuntuでのフローは概ねこんな感じ↓である。
- ターミナルから
sudo screen /dev/ttyACM0
してシリアルモニタを起動する。 - これだけだと無反応だが、CTRL+Cを押すとcode.pyの実行が中断されてメッセージが表示され、さらに何かキーを押すとプロンプト
>>>
が出てREPLモードになる。 - 対話的にPythonスクリプトを実行できる。
import board
してdir(board)
とかやるとピンの一覧が見れたりしておもしろい。 - CTRL+Dでcode.pyの実行モードに戻る。つまりCTRL+CとCTRL+Dで両モードを行ったり来たりできる。
確認が済んだら以後sudoしなくてもいいように sudo usermod -a -G dialout $USER
しておく。
これで最低限のセットアップは完了と言える。
2. 開発環境の整備
まともにコードを書くには補完の効くIDEを使いたい、というわけでいつものVSCodeを使う。
素晴らしいことにExtensionにCircuitPythonというのがあるので、黙ってインストールする。インストールしたら一番下のステータスバーでボードの種類(Particle Xenon)を選んでおく。ここからシリアルモニタも開くことができる。
ワークスペースにCIRCUITPYを追加してcode.pyを直接編集すると、保存のたびに再読み込みされてすぐに実行結果がわかる。若干乱暴な気がするが、そこそこちゃんと動くし、ライブラリの追加とか一発でできるのはうれしい。ときどきCIRCUITPYフォルダに書き込めなくなったり、シリアルモニタが開けなくなったり怪しい動きをすることがあるが、そこはVSCodeの再起動やXenonの再接続などでだましだまし使っていく感じになる。
3. ステータスLEDを使うための修正
import board
すると RGB_LED_RED/GREEN/BLUE
というメンバーが乗っているのが見えて「お、ステータスLEDでフルカラー使えるじゃん?」と思うわけだが、実際に DigitalInOut しようとするとエラーになってしまう。原因はこれ。
RGB_LED_GREEN in use error on Particle Xenon · Issue #2707 · adafruit/circuitpython · GitHub
CircuitPythonのXenon用コードをちょっと直せば回避できるらしい。というわけでやってみよう。
ビルド環境を作る
GitHubからCircuitPythonをgit cloneしたら、基本的にはこれ↓を見て進める。
https://github.com/adafruit/circuitpython/blob/main/BUILDING.md#setup
Ubuntu固有の設定はこちら。
https://learn.adafruit.com/building-circuitpython/linux#install-build-tools-on-ubuntu-2986713-2
基本的なビルドツールが入っていれば、追加で必要なのは下記の2点くらいか。
- pip3 install huffman
- ARM gcc toolchain: GNU Toolchain | GNU Arm Embedded Toolchain Downloads – Arm Developer からアーカイブをダウンロードして適当なところに展開、binにパスを通しておけばよい。
コード修正
こっちのArgonのと同じコードになるようにする。
#define MICROPY_HW_RGB_LED_RED (&pin_P0_13)
#define MICROPY_HW_RGB_LED_GREEN (&pin_P0_14)
#define MICROPY_HW_RGB_LED_BLUE (&pin_P0_15)
ビルド
ここ↓を参照してmakeを叩くだけで特に問題なく完了。
https://github.com/adafruit/circuitpython/blob/main/BUILDING.md#building
cd ports/nrf make BOARD=particle_xenon
成果物は build-particle_xenon/firmware.uf2
に作られる。
Xenonへのインストール
最初にCircuitPythonをインストールしたときと同様、XENONBOOTがマウントされた状態にする必要がある。MODEボタンとリセット同時押し→リセット離す→MODEボタン離すを素早く且つ確実に行うとこの状態になる。成功するとステータスLEDが赤く、青LEDが点滅する状態になり、PCにXENONBOOTがマウントされるはず。そこに先ほど作った firmware.uf2 をコピーする。
XENONが再起動し、ステータスLEDが白く光れば成功である。
これで board.RGB_LED_RED/GREEN/BLUE
がデジタルピンとして普通に使えるようになっているはず。ピンは負論理、つまりFalseで点灯状態になることに注意。明るさの調整はPWMでできる。
4. 熱中症警告メーター
これでPythonでアプリケーションが書けるようになったので、サーボモーターSG90と温度センサーDHT11を使って、WBGT(WetBulb Globe Temperature)値に基づいて部屋の温度の上がり過ぎがわかるメーターを作ってみよう。
- 温度と湿度からWBGT値を見積もり、25℃を基準(12時方向)とし、それより低い(注意域)と左、それより高い(警戒域)と右に触れるようにする。28℃(厳重警戒域)に達するとちょうど右に振り切れるようにサーボを調整する。
- DHT11は精度がイマイチなので2個使って平均を取ってみる。
- ステータスLEDで稼働状態を表示してみる。
参考:
- WBGT値と危険度の目安: https://www.wbgt.env.go.jp/pdf/ic_rma/2301/mat05_3.pdf 温度と湿度からWBGT値を見積もる表があるので適当な式で近似する。
- サーボ制御: https://learn.adafruit.com/circuitpython-essentials/circuitpython-servo
- DHTセンサー: https://pypi.org/project/adafruit-circuitpython-dht/
コードは最後に。
5. 感想
保存即実行や対話的実行はなかなか新鮮な経験だし、これを動かすのは大したものだとは思うが、まあそこまでかな。
マイコンでやるタスクは基本シンプルで低レベルなので、PythonもC++も書き味はそれほど変わらず、それなら堅実で動きが予測しやすく、またdeep sleepのような低レベル機能にもアクセスしやすいC++でいいじゃん、という感覚。NeoPixelガンガン光らせるとか、ゲームのようなインタラクティブなものを作るなら、CircuitPythonのパラメタを変えながらすぐ試せるというのは便利かもしれない。あとは(私はあまり縁がないのだが)ガチで複雑な論理が必要になるロボット系のプロトタイピングとかか。