Raspberry Pi と Arduino が Python を使ってシリアルポートで通信する

Raspberry Pi と Arduino が Python を使ってシリアルポートで通信する

Raspberry Pi と Arduino の間で少量のデータをやり取りする必要がある場合、通常はシリアルポートがもっともシンプルな橋渡しになります。Arduino は USB シリアル経由でバイトを送受信し、Raspberry Pi 側の Python は pyserial を使ってそれらのバイトを読み書きできます。

覚えておくべき重要な点は、シリアル通信はバイトベースだということです。Python の文字列はそのまま送信されるわけではありません。シリアルポートへ書き込む前にバイトへエンコードする必要があり、受信したバイトはテキストとして扱う前にデコードする必要があります。

message = "string"
data = message.encode("utf-8")

text = data.decode("utf-8")

1 バイトの整数値であれば、Python は整数と文字の間の変換もできます。

value = 65
char = chr(value)      # 'A'
number = ord(char)    # 65

Arduino 側

シリアルから 1 行を読み取り、応答を返す簡単なスケッチをアップロードします。Python 側で使うものと同じボーレートでシリアルポートを開きます。

void setup() {
  Serial.begin(9600);
}

void loop() {
  if (Serial.available() > 0) {
    String input = Serial.readStringUntil('n');
    input.trim();

    Serial.print("Arduino received: ");
    Serial.println(input);
  }
}

このスケッチは、改行で終端されたメッセージを待ちます。受信すると、受け取ったテキストをシリアルポートへ出力し返します。

Raspberry Pi 側

pyserial がまだ利用できない場合はインストールします。

python3 -m pip install pyserial

Arduino を Raspberry Pi に接続したあと、Arduino のシリアルデバイスを探します。

ls /dev/ttyACM* /dev/ttyUSB* 2>/dev/null

一般的なデバイス名は、Arduino Uno なら /dev/ttyACM0、USB シリアルアダプターを使うボードなら /dev/ttyUSB0 です。

次に Python を使ってメッセージを送信し、応答を読み取ります。

import time
import serial

PORT = "/dev/ttyACM0"
BAUD = 9600

with serial.Serial(PORT, BAUD, timeout=2) as ser:
    time.sleep(2)  # Give the Arduino time to reset after opening serial.

    ser.write(b"hello from raspberry pin")
    ser.flush()

    line = ser.readline()
    print(line.decode("utf-8", errors="replace").strip())

ポートが正しく、両側で同じボーレートを使っていれば、Raspberry Pi には次のような内容が表示されるはずです。

Arduino received: hello from raspberry pi

バイト、文字列、Unicode

Python 3 では、シリアルデータを明示的に扱うべきです。

text = "hello"
bytes_to_send = text.encode("utf-8")

received_bytes = b"hellon"
received_text = received_bytes.decode("utf-8").strip()

便利な変換は次のとおりです。

bytes_value = b"string"
text_value = bytes_value.decode("utf-8")

single_char = chr(65)
single_number = ord("A")

変数名に strbytes を使うのは避けてください。これらの名前は Python の組み込み型を隠してしまうためです。

トラブルシューティング

何も受信できない場合は、まず次の点を確認してください。

  1. Arduino のスケッチと Python スクリプトが同じボーレートを使っている。
  2. Python スクリプトが正しいシリアルデバイスを開いている。
  3. Arduino コードで readStringUntil('n') を使っている場合、メッセージが n で終わっている。
  4. Python スクリプトを実行しているユーザーに、シリアルデバイスへアクセスする権限がある。

Raspberry Pi OS では、シリアル権限は通常 dialout グループで管理されます。

sudo usermod -aG dialout "$USER"

グループメンバーシップを変更したあとは、ログアウトしてから再度ログインしてください。

短いコマンドやセンサーメッセージであれば、このパターンで十分です。一方から改行終端の UTF-8 テキストを送信し、もう一方でそれをデコードし、Python ではバイトと文字列の境界を明確に保ちます。

Leave a Reply