【Pythonで始めるシリアル通信 第2回】ハードウェアの声を聞く!pyserialでデータを受信する方法 📡
「Pythonでデータを送れるようになったけど、逆はどうやるの?」
「Arduinoやセンサーから送られてくるデータを、Pythonで受け取って処理したい!」
「受信したデータが文字化けしたり、プログラムが固まったりするのはなぜ?」
こんにちは! Pythonシリアル通信探検隊、隊長のPythonistaです! 前回の第1回では、pyserial
ライブラリを使って、Pythonスクリプトから外部へ「こんにちは!」と挨拶を送る方法(データ送信)を学びましたね。これで、あなたのプログラムは外の世界へ情報を発信する能力を手に入れました。
しかし、コミュニケーションは一方通行では成り立ちません。シリーズ第2回となる今回は、その逆、つまり外部のデバイスから送られてくるデータをPythonで受信する方法を徹底解説します! 今回のゴールは、第1回で作成した「時刻送信スクリプト」から送られてくるデータを受け取り、コンソールに表示することです。 この回をマスターすれば、あなたはハードウェアからの「声」を聞き、Pythonプログラムで意味のある情報として扱うことができるようになります。さあ、データの受信に挑戦しましょう!
1. 準備:ライブラリとハードウェア
今回も、pyserial
ライブラリを使用します。まだインストールしていない場合は、ターミナルでインストールしてください。
pip install pyserial
また、データを受信するためには、当然ながら**データを送信する側の機器**が必要です。これには、以下のようないくつかの方法が考えられます。
- 第1回の時刻送信スクリプトを、別のPCや、別のターミナルで実行する。
- Arduinoなどのマイコンに、簡単なデータ送信プログラムを書き込んでおく。
- GPSモジュールのような、常にデータを送信し続けるセンサーを接続する。
- (後述)1台のPCだけで完結する**「ループバックテスト」**を行う。
この記事では、第1回のスクリプトがどこかで動いていることを想定して進めます。
2. 受信プログラミングの重要ポイント
データを正しく受信するには、送信の時にも増していくつかの重要なポイントを理解しておく必要があります。
2.1. データは「バイト列(bytes)」でやってくる
送信時に文字列(str
)をバイト列(bytes
)にエンコードしたのを覚えていますか? 受信時はその逆です。シリアルポートから直接読み込んだデータはバイト列なので、人間が読める文字列に変換するために**デコード (.decode()
)** する必要があります。これを忘れると、文字化けやエラーの原因になります。
2.2. 処理が止まる?「ブロッキング」と「タイムアウト」
pyserial
の読み込み関数は、デフォルトでは**ブロッキングモード**で動作します。これは、「データが来るまで、プログラムの実行を止めて待ち続ける」という意味です。もし相手からデータが送られてこないと、あなたのプログラムは永久にそこで固まってしまいます。
これを避けるために、serial.Serial()
でポートを開く際にtimeout
引数を設定するのが一般的です。例えばtimeout=1
と設定すると、読み込み関数は最大1秒だけ待ち、データが来なければ空のバイト列を返して次の処理に進みます。
3. データを受信する主要なメソッド
pyserial
には、データを受信するためのメソッドがいくつか用意されています。
ser.read(size=1)
: 指定したsize
のバイト数だけデータを読み込もうとします。バイナリデータなど、決まった長さのデータを扱うのに便利です。ser.readline()
: 改行コード(\n
)が現れるまで、最大で1行分のデータを読み込みます。テキストベースの通信で非常によく使われます。ser.in_waiting
: (メソッドではなくプロパティ) 受信バッファに溜まっている、まだ読み込んでいないデータのバイト数を返します。これを使えば、「データがある時だけ読み込む」という非ブロッキングな処理も書けます。
今回は、第1回の送信スクリプトが改行コードを送らないため、read()
を基本に考えますが、一般的にはreadline()
が非常に便利です。
4. 実践!時刻データ受信スクリプト
それでは、第1回の「時刻送信スクリプト」から送られてくる4桁の時刻データ(例: "1507")を受信するスクリプトを作成しましょう。
import serial
import time
# --- シリアルポートの設定 ---
# 送信側と通信するポートを指定します。
# Windowsなら "COM3" など、環境に合わせて変更してください。
SERIAL_PORT = '/dev/ttyUSB0'
BAUD_RATE = 115200
ser = None
try:
# ポートを開く。timeoutを1秒に設定
ser = serial.Serial(SERIAL_PORT, BAUD_RATE, timeout=1)
print(f"シリアルポート {SERIAL_PORT} を開きました。データ受信待機中...")
while True:
# 4バイトのデータを読み込む (送信側が4桁の数値を送るため)
received_bytes = ser.read(4)
# データが受信できたかチェック
if received_bytes:
try:
# バイト列をUTF-8で文字列にデコード
received_str = received_bytes.decode('utf-8')
# 文字列を整形して表示
hour = received_str[:2]
minute = received_str[2:]
print(f"受信データ: '{received_str}' -> {hour}時{minute}分")
except UnicodeDecodeError:
print(f"デコードエラー: 不正なバイト列を受信しました - {received_bytes}")
else:
# タイムアウトした場合はメッセージを表示 (任意)
print("データがありません...")
except serial.SerialException as e:
print(f"シリアルポートエラー: {e}")
except KeyboardInterrupt:
print("\nプログラムを終了します。")
finally:
if ser and ser.is_open:
ser.close()
print(f"シリアルポート {SERIAL_PORT} を閉じました。")
このスクリプトを実行すると、1秒ごとに送信されてくる時刻データを受信し、整形してコンソールに表示し続けます。
5. 動作確認の方法:「ループバックテスト」
「送信する機器と受信する機器、2台も用意できない!」という場合でも大丈夫です。1台のデバイスだけで送受信をテストする**「ループバックテスト」**という簡単な方法があります。
やり方は、お使いのデバイス(Jetson, Raspberry Pi, USB-シリアル変換アダプタなど)の**送信ピン(TXD)と受信ピン(RXD)を、1本のジャンパー線で直結する**だけです。これにより、デバイスが自分で送信したデータを、自分で受信することができます。
テスト手順:
- TXDピンとRXDピンをジャンパー線で接続します。
- ターミナルを**2つ**開きます。
- 1つ目のターミナルで、第1回で作成した送信スクリプト(
serial_sender.py
)を実行します。python3 serial_sender.py
- 2つ目のターミナルで、今回作成した受信スクリプト(
serial_receiver.py
)を実行します。python3 serial_receiver.py
すると、1つ目のターミナルに「送信データ: 1507」と表示され、ほぼ同時に2つ目のターミナルに「受信データ: '1507' -> 15時07分」と表示されるはずです。これで、送受信プログラムが正しく動作していることを確認できます。
まとめと次回予告
今回は、Pythonのpyserial
ライブラリを使って、シリアル通信でデータを受信する基本的な方法を学びました。
- 受信データはバイト列(bytes)であり、
.decode()
で文字列に変換する必要があること。 - プログラムが固まるのを防ぐためのタイムアウト設定の重要性。
ser.read()
やser.readline()
といった、基本的なデータ読み込みメソッド。- 1台で送受信テストができる便利なループバックテストの方法。
これで、あなたは外部デバイスからの信号をPythonで受け取る能力を手に入れました。送信と受信、通信の基本サイクルが完成したわけです!
さて、データを送ったり受け取ったりできるようになったら、次は何をしたくなるでしょうか? そう、両方を同時に行い、デバイスと「対話」したくなりますよね。
次回、第3回では、いよいよ**「双方向通信」**に挑戦します! Pythonスクリプトからマイコンに「LEDを点けて!」と命令を送り、マイコンから「点けました!」と返事をもらう、そんな**LED遠隔操作プロジェクト**を通して、よりインタラクティブなハードウェア制御の世界を探検します。お楽しみに!
コメント
コメントを投稿