
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機のスピーカが「鳴る」ようになりました。結構聞こえやすい音だ。

