Raspberry Pi Picoが買えなかった「衝撃」が後を引いております。が、気をとりなおして? MicroPython。しかしこちらでも「衝撃」が。普段、普通にできていることができないとダメージが大きい。なんでもコロナのせい、にしたくなりますが、MicroPythonとPython3の「微妙な」違い。知っていればなんてこともないんだ、なんてことも。。。
※「MicroPython的午睡」投稿順 Indexはこちら
このところ作業中の「システム」、2台のmicro:bitとRaspberry Piで構成しております。2台のmicro:bitの制御ソフトおよびラズパイ上で走っているmicro:bitとのインタフェースソフトは全てPythonで記述しています。緑色の方のmicro:bitが電池で動く「先っぽ」で、センサの情報を無線で送ってまいります。それを赤色の方のmicro:bitが受信してuart経由でラズパイ上のPythonスクリプトに報告する(その後MQTT経由でNode-RED)仕組み。「センサ・ノード」から「サーバ」方向への「上り」は動作しているので、今回は「下り」を実装することにいたしました。micro:bitの機能を「活かし」(お手軽だから)てラズパイの指示のもと音楽を鳴らそうという目論見です。最近のmicro:bit v2であればスピーカも搭載されているようですが、そうでないので外付けです(アイキャッチ画像ご参照ください。)
問題はね、無線でもない追加のスピーカでもない、micro:bit赤とラズパイ間のUartでおきました。ラズパイからUart経由でmicro:bitにコマンド文字列を送信するとエラー発生、こんな感じ。
Uart経由で通信した結果は、bytes型の「文字列」オブジェクトになります。ちょっと見普通の文字列に見えるけれど頭にbがついている奴。まあUARTは8ビット(または7ビット)単位での通信なのでRaspbian OS上で走っているPython3でもMicroPythonでもbytes型であることは変わりないです。そのままだと普通の文字列str型と混在させて扱いずらいので、Python3では、
decode(‘utf-8’)
メソッドを使って、bytes型からstr型に変換して使っていました。なおどちらもオブジェクトとしてはイミュータブルであります。PC上のPython3のREPLでの動作のキャプチャですが、Python3でのbytes型のdecode()はこんな感じ。
しかし、同じことを micro:bit上のMicroPythonのREPLで実行してみると、制御のスクリプト同様エラーとなります。
普段やっていることができないとパニックになります。decode()使えなかったらどうしたらよいの?一瞬放心状態(大袈裟か。)
結構時間を使って試行錯誤、とりあえず変換する方法その1を見つけたのがこちら。
上の青い方がPC上のPython3、下の黒いバックがMicroPython。
しかし、上の方法はかなりマドロッコしい。一端リスト(こちらはミュータブルなオブジェクト)作ってそこからstr型の文字列に戻すなど負荷きっと大。それに1byte毎にchr()にかけているのが心配。もっとよい方法ないものか。
ところが別件(UARTのタイムアウト時間)を探していたら「ソリューション」をヒョッコリ見つけましたぜ。
まずは探していたタイムアウトの件を引用させていただくと、
すべての UART 読込みのタイムアウトはボーレートに依存していて、Pythonからは変更できません。タイムアウト値は次の式でミリ秒単位で決まります:
書いてあった式にボーレートを代入して計算してみると1.1m秒くらい。タイムアウト早すぎ、どうしようもないけど。ま、この件は納得できたところで引用場所のふと上をみると bytesからstrへの変換らしきものがかかれています。(ただ残念なことに、例題で使われている変数名が間違っているような気がするですが。)
とりあえず、PC上のPython3で実行。変換OK.
あれま、出来てしまった。簡単。decode()使えないとパニックっていたのがウソのよう。一番最初にstrにbytes型を食わしてみていたのだけれど、その時は第2引数に’UTF-8’など与えていなかったです。分かってみたら超簡単、拍子抜け。
そういう分けで、
結局のmicro:bit REDの本日のコード
import radio from microbit import * radio_on = False def receiveCommand(): temp = uart.readline() if temp is not None: temp = str(temp, 'UTF-8') return temp def receivePacket(): dataStr = None try: dataStr = radio.receive() except ValueError: display.show(Image.SAD) dataStr = None if dataStr is not None: display.show(Image.HEART) uart.write(dataStr) def sendPacket(payload): radio.send(payload) 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: comStr = receiveCommand() if comStr is not None: uart.write(">{0}<\r\n".format(comStr)) if comStr == "1": sendPacket("1:MUSIC:1") elif comStr == "2": sendPacket("1:MUSIC:2") elif comStr == "3": sendPacket("1:MUSIC:3") else: sendPacket(comStr) uart.write("sendPacket\r\n") receivePacket() else: display.show(Image.TRIANGLE) sleep(100) display.clear() sleep(500)
もう一方、micro:bit GREENのコード
import radio import utime import music 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 = pin2.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) def receivePacket(): dataStr = None try: dataStr = radio.receive() except ValueError: display.show(Image.SAD) dataStr = None sleep(200) display.clear() return dataStr def decodeCommand(comStr): keyStr = None valStr = None if comStr is not None: com = comStr.split(":") if len(com) == 3: _recNum = com[0] keyStr = com[1] valStr = com[2] return (keyStr, valStr) def musicCommand(nMusic): if type(nMusic) is int: if nMusic == 1: music.play(music.RINGTONE) elif nMusic == 2: music.play(music.JUMP_UP) elif nMusic == 3: music.play(music.JUMP_DOWN) def tryIntParse(intStr): try: result = int(intStr) except ValueError: result = None return result while True: comTPL = decodeCommand(receivePacket()) if comTPL[0] == "MUSIC": musicCommand(tryIntParse(comTPL[1])) if button_a.was_pressed(): uartLog = True music.play(music.NYAN) if button_b.was_pressed(): uartLog = False music.play(music.PYTHON) measureTemperature() sendPacket(uartLog) display.scroll(str(packetNum)) packetNum += 1 utime.sleep(interval)
これにてRED機からの「無線コマンド」に反応し、GREEN機のスピーカが「鳴る」ようになりました。結構聞こえやすい音だ。