MicroPython的午睡(2) ラズパイ上のMuエディタつづき

Joseph Halfmoon

昨日の回で、「リモート」のmicro:bitから「PC脇」のmicro:bit経由でPCまで、無線でデータを送りました。今回は、そのスクリプトを改造しながら、ラズパイ上のMuエディタに慣れて行きたいと思います。だいたいmicro:bit上で走るスクリプトにバグがあったらどうなるの?

※「MicroPython的午睡」投稿順 Indexはこちら

ラズパイ上のMuエディタは、BBC mico:bit上で実行可能なMicroPythonスクリプトの「クロス開発」が可能、かつ、ラズパイ上の普通のPython3スクリプトの「セルフ」開発が可能です。昨日は、2台のmicro:bitの間で無線通信するMicroPythonスクリプトを作成してみました。本日は、以下が目標です。

  1. リモートのmicro:bit(緑)は電池動作、MicroPythonでサーミスタの「値」を読み取り無線で送信。
  2. 無線レシーバ担当micro:bit(赤)はRaspberry PiにUSBシリアル接続。MicroPythonで無線で送られてきたサーミスタの「値」を受信。UART経由でラズパイへ転送(赤機の接続先が昨日のPCからラズパイに変わった。)
  3. ラズパイ上ではPython3のスクリプトが走っていてUARTからもたらされるデータを「若干」加工して表示

3台それぞれでPythonスクリプトが走りますが、これをすべてMuエディタで作成する、と。

まずは、Muエディタの上部にあるアイコン群を御覧ください(下のキャプチャはmicro:bitモードのとき。)一番左の「モード」がmicro:bit用のMicroPythonモードとラズパイ上のPython3モードの切り替えボタンとなります。

Mu_Menumicro:bitに「転送」したスクリプトにバグがあったらどうなるか?

上の転送ボタン(micro:bitを接続し、認識されると黄色の〇になる)を押せばエディタで編集したスクリプトが書きこまれ、即実行されます。エラーが発生すると

micro:bitのLEDアレイにエラーメッセージがスクロール表示

されるので、やっちまった、ことだけは分かります。しかし、小さな「画面」上をあれよあれよと流れていってしまうので、正直何だか読み取れません。

そのようなトホホな状況に陥らないためには以下の2つかと思います。

  1. 「転送」前に右から2つめの「チェック」ボタン使う
  2. 実行時のエラー発生時にはREPLを開く

チェックボタンで静的なチェックがなされ、ソースコードの問題部分付近に的確な指摘が表示されます。つまらないエラーは大体なくなるように思われます。お節介ついでに、文法違反ではないけれど、こうした方がいいみたいなことも指摘されます。この辺の塩梅を制御する方法は今のところ不明なので、指摘0を目指します。すると「褒めて」くれます。

しかしチェックボタンでは実行時のエラーまで分かりません。そのとき、REPLボダンでREPLウインドウを開くとエラーメッセージを確認できるかもしれません。REPLウインドウを開くと、実行中のスクリプトは停止されMicroPythonのプロンプトが表示されます。行の先頭で

CTRL-D

すれば、ソフトリセットされて書きこまれているスクリプトが先頭から再実行されるので経過観察もできます。また、スクリプトに割り込んでプロンプトに制御を戻すのは CTRL-Cでできます。なお、REPLは、USBポートにmicro:bitを接続すると生成される以下のシリアルデバイス経由で動作しているようです(Raspbian buster)。

/dev/ttyACM0

micro:bit上のスクリプトで外部端子に向けてuartを再初期化していなければ、uart.write()すると上記のデバイスに送られるので、スクリプトから「伝統的」printfデバッグに使うことができると思います。MuエディタのREPLを開くと実行中のスクリプトは必ず中断されてしまうので、中断したくない場合は、エディタの外の cu コマンドなどで表示した方が便利かもしれません。なお、このREPLの挙動はmicro:bitモードの場合で、python3ではまた異なります。

リモートで電池でうごくmicro:bit「センサ・ノード」

さて、リモート側(緑)に、サーミスタモジュールと、電池ボックスをとりつけました。サーミスタモジュールは、こちらで使用したものをmicro:bitらしくワニ口クリップ経由で接続いたしました。現物の様子は先頭のアイキャッチ画像を御覧ください。回路はこんな感じ。現在の室温にてP0ポートで扱いやすい値が出るように抵抗を「調整」したらこんなんなってしまいました。
micro:bit thermistor circuit

ここで、分かったのは micro:bit 上のMicroPythonではArduinoのようなPinModeの指定が不要ということです。最初、入力端子でプルアップをオフにするにはどうしたらよいの?と探し回ってしまいました。ドキュメントの入出力端子のページから1箇所引用させていただきます。

端子のプルモードは、端子が入力モードに変わると自動的に構成されます。入力モードは read_analog read_digital is_touched を呼び出すと自動で設定されます。

というわけで非常に簡単。昨日のコードを改造し、サーミスタ端子の電圧を read_analog(0~1023の整数値がよめる)して送信するようにしたもの。ついでにmicro:bitの「温度」(Nordic nRF51822チップのだと思う)も送信。

import radio
import utime
from microbit import *

radio.on()
packetNum = 0
tempExt = -999
tempInt = -999
uartLog = False
interval = 6

def measureTemperature():
    global tempExt, tempInt
    tempInt = temperature()  # microbit module, internal temp sensor.
    tempExt = pin0.read_analog()

def sendPacket(opt):
    global packetNum, tempExt, tempInt
    packet = ":".join([str(packetNum), str(tempExt), str(tempInt)]) + "\r\n"
    radio.send(packet)
    if opt:
        uart.write(packet)

while True:
    if button_a.was_pressed():
        uartLog = True
    if button_b.was_pressed():
        uartLog = False
    measureTemperature()
    sendPacket(uartLog)
    display.scroll(str(packetNum))
    packetNum += 1
    utime.sleep(interval)
Raspberry PiのUSBポートに接続したmicro:bit「レシーバ」

送られてきたデータを受ける「レシーバ」側は昨日とほとんど変わりませぬ。ちょっと画面をいじったくらい。ただUART接続先は、PCではなくRaspberry PiのUSBシリアル(ttyACM0)に変更しています。

# radio to UART(via USB) test
import radio
from microbit import *

radio_on = False

while True:
    if button_a.was_pressed():
        radio.on()
        radio_on = True
    if button_b.was_pressed():
        radio.off()
        radio_on = False
    if radio_on:
        dataStr = None
        try:
            dataStr = radio.receive()
        except:
            display.show(Image.SAD)
            dataStr = None
        if dataStr is not None:
            display.show(Image.HEART)
            uart.write(dataStr)
    else:
        display.show(Image.TRIANGLE)
    sleep(100)
    display.clear()
    sleep(500)
Raspberry Pi上で動作するPython3スクリプト

さてラズパイ上でttyACM0から到来するデータを処理するスクリプトはこちら。テスト用なので決めた回数処理すると終わってしまいますが。

#! /usr/bin/python3
import serial

recMax = 500
rec = 0
ser = serial.Serial('/dev/ttyACM0', '115200', timeout=0.5)

while (rec < recMax):
    mes = ser.readline().decode('utf-8').strip()
    dat = mes.split(":")
    if len(dat) == 3:
        recSend = dat[0]
        thermistorV = dat[1]
        chipTemp = dat[2]
        print( \
            "REC={0} Thermistor={1} ChipTemp={2}".format(recSend, thermistorV, chipTemp))
    rec += 1
ser.close()

なお、Python3モードでは、スクリプトの実行ボタンを押すとそのコンソール的なウインドウが自動で開くので即結果をみれます。
こんな感じ。

REPL_getDataFromRemoteMB
大した進捗なかったですが、まあ、少し慣れたですかね。

MicroPython的午睡(1) ラズパイ上のMuエディタでmicro:bit に戻る

MicroPython的午睡(3) bytesからstr変換につまずく に進む