目的
RaspberryPiPicoにもともと付いてるUSBポートを使ってPCからコマンドを送ってPico上に実装されたセンサー数値等の応答を返す仕組みを作りたかったんだけどGPIOのUARTを使う作例ばかりで難儀したのでメモ。
いるもの
RaspberryPiPico 1台(今回はPicoWを使った)
ThonnyをインストールしたPC 1台(今回はWindows)
USB A to micro Bのケーブル 1本(動けば何でもいい)
Pico側で実行するコード
import machine, utime
def blink(sec):
led = machine.Pin("LED", machine.Pin.OUT) # Picoの場合は25がオンボードLEDのピン
led.value(1)
utime.sleep(sec)
led.value(0)
def internal_temp():
sensor_temp = machine.ADC(4)
conversion_factor = 3.3/(65535) #convert value to voltage
reading = sensor_temp.read_u16()*conversion_factor
temp = 27-(reading-0.706)/0.001721
return(temp)
try:
while True:
s = input()
blink(0.1)
if s == 'Hello!':
print('World!')
elif s == 'name?':
print('Im PICO!')
elif s == 'temp?':
temp = internal_temp()
print(temp)
else:
print('?',s)
except KeyboardInterrupt:
pass
Pico上で動かしたときはShellに直接 Hello! 等と入力するとオンボードのLEDが光って返事がShellに表示される。temp?でRP2040内部の温度センサーの値を読み取って返す。
>>> %Run -c $EDITOR_CONTENT
MPY: soft reboot
Hello!
World!
name?
Im PICO!
temp?
30.78955
XYZ
? XYZ
PC(Thonny)側で実行するコード
import serial,time
PORT = 'COM7' # Windows の場合COM*
ser = serial.Serial(port=PORT,baudrate=115200,timeout=1)
command = 'Hello!'
send_bytes = bytes(command+'\r\n','utf-8')
ser.write(send_bytes)
buf = ser.readlines()
print(buf,type(buf))
command = 'name?'
send_bytes = bytes(command+'\r\n','utf-8')
ser.write(send_bytes)
buf = ser.readlines()
buf = [i.decode().strip() for i in buf]
print(buf)
ser.close()
try:
while True:
ser.open()
command = 'temp?'
send_bytes = (command+'\r\n').encode('utf-8')
ser.write(send_bytes)
buf = ser.readlines()
buf = [i.decode().strip() for i in buf]
print(buf)
ser.close()
time.sleep(1)
except KeyboardInterrupt:
print('stop')
pass
動かす
Pico上でtest_device.pyを実行してからlocal Pythonに切り替えてtest_host.pyを実行すると以下のようになる、はず。
>>> %Run test_host.py
[b'Hello!\r\n', b'World!\r\n'] <class 'list'>
['name?', 'Im PICO!']
['temp?', '31.2577']
['temp?', '31.2577']
stop
>>>
ser.write()でコマンドを送ってser.readlines()で読み込む。このときインスタンス生成時に設定したタイムアウト(1秒)に達するまで読み取りを続けるためレスポンスがやや悪い。in_waitingでバイト数を取得してread(bytes)で読み取ろうとしたんだけど上手くいかなかった。まあ環境制御装置用途なので1分に1回読み込めれば十分だしとりあえず動くからヨシ!
Picoのserial consoleは送ったコマンドと返事をバイナリのリストで返すみたいでser.readline()で読むとコマンドしか表示されなくて悩んだ。誰か良い感じのやり方を教えてください。
とにかくこれでコマンドを送って返事をする仕組みができたので最初にポートに順番に名前を訊いてデバイスを判別するとか、測定ノードから取得した値に基づいてと制御ノードに接続されたモーターを動かすといったことができる、はず。有線接続なのでWiFiみたいな干渉を心配しなくて良い。
追記
readlines()でタイムアウトになるまで読み取りを続けるとレスポンスが悪いけどreadline()だとCRLFが二回出てくるから後半が読み取れないのなら2回読み取ればいいじゃないと思ってやってみた。
(前略)
buf1 = ser.readline().decode().strip()
buf2 = ser.readline().decode().strip()
print(buf1,buf2)
(後略)
まあまあレスポンス早くなったからヨシ!
もしくはinput('>>')とすると応答の最後に>>がつくのでread_until(b'>>')でも良いかもしれない。
ser.read_until(b'>>').decode().split('\r\n')と書けば扱いやすいかな?