6
8

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

Raspberry Pi PicoをUSBシリアル接続で制御する

Last updated at Posted at 2023-11-06

目的

RaspberryPiPicoにもともと付いてるUSBポートを使ってPCからコマンドを送ってPico上に実装されたセンサー数値等の応答を返す仕組みを作りたかったんだけどGPIOのUARTを使う作例ばかりで難儀したのでメモ。

いるもの

RaspberryPiPico 1台(今回はPicoWを使った)
ThonnyをインストールしたPC 1台(今回はWindows)
USB A to micro Bのケーブル 1本(動けば何でもいい)

Pico側で実行するコード

test_device.py
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)側で実行するコード

test_host.py
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を実行すると以下のようになる、はず。

shell
>>> %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')と書けば扱いやすいかな?

6
8
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
8

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?