144Labの入江田です。
https://tech.144lab.com/entry/papyr にも書きましたが、 最近、CircuitPythonをいろんなデバイスで試しています。
なぜCircuitPythonは作られたの?
組み込み向けPythonとしてpython-on-a-chip
を始めとしてMicroPython
という歴史ある実装があるにもかかわらずMicroPython
をフォークしてCircuitPython
は生まれました。
CircuitPythonはボードベンダであるAdafruitが作りました。 Adafruitは自社製品向けに調整済みのCircuitPythonをリリースすることで より教育に優しいPythonによる開発環境を提供していく考えのようです。
- これまでの類似製品よりもより組み込み初心者をターゲットにしています
- ドキュメンテーションの一元化で初心者への教育に優しい
- ボードサポートやドライバーの関係を単純化しサポートボードの追加を効率化しました
- USBマスストレージ疑似ファイルシステム経由で開発できるものを中心にサポートボードを強化していきます
- ATSAMDシリーズおよびUSBサポート付きマイコンへの対応を強化、AdafruitのExpressシリーズなどがそれに該当します
- シリアル経由でコードを書き込む(ampyサポート)しかないものは順次廃止(v4にてESP8266サポートは廃止されました)
要するにつまづきにくいPython開発環境を提供することにフォーカスしたMicroPythonの派生バージョンということなのです。
CircuitPythonはなにが嬉しいの?
- Arduino以前の組み込み開発ってどうしても面倒なところがなくせなかった
- ArduinoはボードにArduinoCoreブートローダーさえ入っていてシリアルポートドライバーさえあればあとは全部用意してくれたのが画期的だった
- MycroPythonはArduinoの利点に加えて疑似ファイルシステム(以下「疑似FS」)を持ち、Python言語で書けるという利便性を提供します
- CircuitPythonではさらにシリアルポートドライバーも不要でUSBストレージの形で疑似FSへのアクセス機能を提供します
- ドキュメントはボード専用部分からCircuitPythonの使い方までをトータルでAdafruitのドキュメントで統一されてる
- PythonコードをUSBストレージに見えるフォルダに投げ込むだけ
- 開発環境は好みのエディタで十分
- もちろんPython向け統合開発環境を使うのも良い
- 複数ファイルを利用する際の関係性はPythonを学べば単純で理解しやすいし、それをほとんどそのまま疑似ファイルシステムに置くだけで動いちゃう
- ボードサポートからチュートリアルまで一つのドキュメントサイトで追うことができます
- BLEサポートを追加してきたので比較的容易にBLEアプリケーションが書けちゃう
- また、AdafruitのGitHubリポジトリには豊富なドライバーやライブラリ、サンプルコードがあるのでとても参考になる
まとめると開発速度は
従来の組み込み開発手法 <<< Arduino << MicroPython < CircuitPython
といったところでしょうか。 カバーされているフィーチャーは
従来の組み込み開発手法(100%) > Arduino(90%) >>> MicroPython = CircuitPython
で、Python系では手続き的に書けるというところに主眼をおいたフィーチャーだけをサポートします。割り込み(DMAなど)や電源制御といったあたりはサポートしません。また、アウトプットの性能(パフォーマンス、省サイズともに)は
従来の組み込み開発手法 > Arduino > MicroPython = CircuitPython
こんな感じですね。足りない性能はワンランク上のマイコンをチョイスすることである程度カバーできます。
従来の組み込み開発手法の最大のメリットは苦労する代わりに「ターゲットマイコン選定の自由度に制約がない」というところですね。(もちろん不慣れなターゲットを選べばそれだけ苦労も増えるのですが)
入門のしやすさから評価されつつあって、すこしづつですが、サポートボードもAdafruitだけじゃなくなってきています。
https://circuitpython.org/downloads
SparkFun, Electronut Labsなどが参入しています。
CircuitPythonのコアモジュール構成
https://circuitpython.readthedocs.io/en/latest/shared-bindings/index.html
PC向けPythonと似た役割のモジュール
sys
os
time
math
random
ハード依存のモジュール
microcontroller
board
microcontrollerはマイコンチップ依存定義のモジュール。 boardはmicrocontrollerをベースにしたボード依存定義モジュール。
入出力用モジュール
digitalio
analogio
pulseio
frequencyio
bitbangio
busio
bleio
terminalio
displayio
fontio
etc...
bitbangio
とbusio
は同じ目的でI2CやSPIバス機能のモジュールですが、前者はCPUパワーでバスエミュレーションし、後者はハードウェアサポートがある場合のみ利用可能です。
その他のモジュール
ここに周辺装置向けライブラリアーカイブがあります。 https://circuitpython.org/libraries
これらの必要なライブラリだけをターゲットの /lib/フォルダ配下に入れることで利用可能になります。
インタラクティブモード
接続方法
$ screen /dev/tty.usbserial#### 115200
コードが動作中ならCtrl+Cで止めれます。その時Enterキーを押すことでインタラクティブモードに入ることができます。
Auto-reload is on. Simply save files over USB to run them or enter REPL to disable. Press any key to enter the REPL. Use CTRL-D to reload. Adafruit CircuitPython 4.0.1 on 2019-05-22; SparkFun Pro nRF52840 Mini with nRF52840 >>> import sys >>> sys.path ['', '/', '.frozen', '/lib'] >>>
sys.pathを確認してもらえば、CIRCUITPYボリューム(ドライブ)
のルートやその直下のlibフォルダがライブラリを探す対象になっていることが確認できます。
ちなみにfrozen
というのはCircuitPythonの標準ライブラリをアーカイブしたものです。
サンプルを動かす
CircuitPythonは4.0.1安定版を前提にしています。
bleペリフェラルサンプル
CIRCUITPY/ble_sample.py
import bleio # Create a Characteristic. characteristic = bleio.Characteristic(bleio.UUID(0x2919), read=True, notify=True) # Create a Service providing that one Characteristic. service = bleio.Service(bleio.UUID(0x180F), [characteristic]) # Create a peripheral and start it up. periph = bleio.Peripheral([service]) while True: periph.start_advertising() while not periph.connected: pass print("connected") while periph.connected: pass print("disconnected") periph.stop_advertising()
動作確認
インタラクティブモードで動作確認できます。
> import ble_sample
この状態でスマホからnRF Connectアプリ等でスキャン&接続してみたり切断してみたりすることができると思います。
便利なライブラリ
https://github.com/adafruit/Adafruit_CircuitPython_Bundle
リリースファイルページ にてライブラリとライブラリを使ったサンプルがあります。
CIRCUITPY/lib
フォルダを作成してその配下に必要なライブラリを入れておくと起動スクリプトからimport可能になります。
BME280デバイスをつないで環境センサデータを読み出す例
CIRCUITPY/lib/adafruit_bme280.mpy
をおいておき、
以下のようなコードをおいておくと、
インタラクティブモードで動作確認できます。
CIRCUITPY/sample.py
import time import board import busio import adafruit_bme280 i2c = busio.I2C(board.SCL, board.SDA) bme280 = adafruit_bme280.Adafruit_BME280_I2C(i2c) bme280.sea_level_pressure = 1013.25 bme280.mode = adafruit_bme280.MODE_NORMAL bme280.standby_period = adafruit_bme280.STANDBY_TC_500 bme280.iir_filter = adafruit_bme280.IIR_FILTER_X16 bme280.overscan_pressure = adafruit_bme280.OVERSCAN_X16 bme280.overscan_humidity = adafruit_bme280.OVERSCAN_X1 bme280.overscan_temperature = adafruit_bme280.OVERSCAN_X2 time.sleep(1.0) print("Temperature: %0.1f C" % bme280.temperature) print("Humidity: %0.1f %%" % bme280.humidity) print("Pressure: %0.1f hPa" % bme280.pressure) print("Altitude = %0.2f meters" % bme280.altitude)
動作確認
> import sample Temperature: 20.0 C Humidity: 40.0 % Pressure: 999.0 hPa Altitude = 50.0 meters
自動起動
給電したときに自動的に起動するようにするには 起動対象のスクリプトファイル名を「code.py」か「main.py」にします。 その状態から動作を止めるのは若干コツがいります。
- インタラクティブモードからCtrl+Cを送る
- ファイル名を書き換えてリセット
以上でも止まらないような場合はFlashストレージを初期化する必要があったりします。
Adafruit-Blinka
https://pypi.org/project/Adafruit-Blinka/
こちらを使うと、LinuxベースのマシンでCircuitPythonの互換モジュールを利用できます。 つまり、RaspberryPiやDockerエミュレーションコンテナなどでPoCを作成し、マイコンターゲットのCircuitPython上に移植するという形を取れば、最小の修正で移植可能になる。
アルゴリズムの検証をDocker上で済ましておくという手法が使えます。
ポーティングのコスト
nRF52840派生モジュールやATSAMD21/51を載せたボードは近年たくさんリリースされていますが、これらはチップコア単体の機能でCircuitPythonの要件を満たせているので、殆どの場合既存のリリース済みのCircuitPythonをインストールして動作させることができます。
ブートローダーの専有する1〜2ボタン、ボードのアセットの差分くらいしか異なる点はなく、ちゃんとした調整もそんなに大きくコストが掛かりません。
マイコンがサポート済みと同じものであるうちはボードサポート程度であれば大きく調整コストがかからないということです。 (また、ブートローダーさえ適合するのならCircuitPython自体は完全に整合するものでなくとも動作します。ボードサポート部分をアプリケーション毎に書くという方法で。)
Cによる拡張
- Cによる拡張ができれば割り込みやDMA利用や電源制御なども可能です
- CircuitPython本体ごと追加拡張モジュールと一緒にビルドが必須です
- そうなってくるとCircuitPythonのお手軽さ自体は無意味になるのでおすすめはしません
- あくまで用意された機能セットだけでPoCを作るのに向いています
- 機能不足を感じたら早めにArduinoやmbed、PlatformIO、チップセットのSDK利用などに切り替えていくのが良いでしょう
まとめ
CircuitPythonはハードウェア制御の入門やPoC作成に向いています。導入も対応ボードであればほとんど躓くことはないでしょう。 Python自体もプログラミング入門者にやさしいので、取っ掛かりでとにかく動くところまでを追うのには最適だと思います。