【Pythonで始めるシリアル通信】pyserialライブラリでPCやマイコンにデータを送る方法(Jetson/ラズパイ対応)

「Jetson NanoやRaspberry Piから、Arduinoや他のマイコンにデータを送りたい!」
「センサーの値をPCにリアルタイムで表示するにはどうすればいい?」
「Pythonでシリアル通信って、なんだか難しそう…」

こんにちは! Pythonプログラミング探検隊、隊長のPythonistaです! これまで私たちは、ソフトウェアの世界で完結する多くのプロジェクトに挑戦してきました。今回は、プログラムの世界と現実世界のハードウェアを繋ぐ、非常に重要で面白い技術、「シリアル通信」に挑戦します!

シリアル通信は、コンピュータや電子機器同士が1本の線でデータをやり取りするための、古くからある信頼性の高い通信方式です。この記事では、Pythonの標準的なシリアル通信ライブラリであるpyserialを使い、Jetson NanoやRaspberry Piといったデバイスから、**定期的にデータを送信する**簡単なスクリプトの作り方を、ステップバイステップで徹底解説します。この記事を読めば、あなたのPythonプログラムが、外部のハードウェアと「対話」するための第一歩を踏み出せます!


1. シリアル通信とは? なぜ使うの?

シリアル通信とは、データを1ビットずつ順番に、1本の信号線で送受信する通信方式のことです。まるで、一列に並んだ人々が一人ずつ改札を通っていくようなイメージですね。構造がシンプルなため、多くの電子機器やマイコン(マイクロコントローラ)間の近距離通信で標準的に使われています。

どんな時に使うの?

  • PCからArduinoやM5Stackなどのマイコンを制御したり、データを送ったりする。
  • GPSモジュールや各種センサーから送られてくるデータをPCやRaspberry Piで受信する。
  • 組み込み機器のデバッグ情報を、PCのターミナル画面に表示する(シリアルコンソール)。

シリアル通信を行うには、通信する両者で「通信速度(ボーレート)」などのお約束事を合わせておく必要があります。


2. 準備:ライブラリのインストールとポートの確認

2.1. `pyserial`ライブラリのインストール

Pythonでシリアル通信を行うための定番ライブラリがpyserialです。まずはpipでインストールしましょう。ターミナルやコマンドプロンプトで以下を実行します。

pip install pyserial

2.2. シリアルポートの確認

次に、どの「ドア」からデータを送り出すか、つまりシリアルポートのデバイス名を特定する必要があります。これはお使いの環境によって異なります。

  • Jetson Nano/Xavier/Orinの場合: 基板上のピンヘッダ(40ピンのGPIOヘッダ)にあるシリアル通信用のピン(TXD, RXD)は、多くの場合/dev/ttyS0または/dev/ttyTHS1として認識されます。
  • Raspberry Piの場合: GPIOピンヘッダのシリアルポートは、/dev/serial0/dev/ttyS0といった名前で認識されることが多いです。
  • USB-シリアル変換アダプタを使う場合: PCやJetson/ラズパイのUSBポートにアダプタを接続すると、新しいシリアルポートが作成されます。Linux(Ubuntu)では/dev/ttyUSB0、macOSでは/dev/cu.usbserial-XXXXといった名前になることが多いです。
    • Linux/macOSでは、ターミナルでls /dev/tty*と打つと、利用可能なポートの一覧を確認できます。

どのポートを使うか、ご自身の環境に合わせて確認しておきましょう。


3. スクリプトの全体像(完成コード)

それでは、今回作成する「現在時刻を1秒ごとにシリアルポートから送信する」スクリプトの全体像を見てみましょう。このコードをserial_sender.pyなどの名前で保存してください。

import serial
import time
import datetime

# --- シリアルポートの設定 ---
# Jetson Nano/Xavier/OrinのGPIOピン(8,10)は通常ttyS0またはttyTHS1です
# USB-シリアル変換アダプタの場合は /dev/ttyUSB0 など、環境に合わせて変更してください
SERIAL_PORT = '/dev/ttyTHS1'
# ボーレート(通信速度) - 受信側と合わせる
BAUD_RATE = 115200

ser = None  # シリアルオブジェクトをグローバルスコープで初期化

try:
    # シリアルポートを開く
    ser = serial.Serial(SERIAL_PORT, BAUD_RATE)
    print(f"シリアルポート {SERIAL_PORT} を開きました。")

    # 無限ループで1秒ごとにデータを送信
    while True:
        # 現在時刻を取得
        now = datetime.datetime.now()
        
        # 時と分を4桁の文字列にフォーマット (例: 15時07分 -> "1507")
        time_str = now.strftime('%H%M')
        
        # 文字列をバイト列にエンコードして送信
        ser.write(time_str.encode('utf-8'))
        
        # 送信したデータをコンソールにも表示(動作確認用)
        print(f"送信データ: {time_str}")
        
        # 1秒待機
        time.sleep(1)

except serial.SerialException as e:
    print(f"シリアルポートエラー: {e}")
    print(f"ポート {SERIAL_PORT} が存在しないか、アクセス権がありません。")
    print("ポート名が正しいか確認するか、スクリプトを 'sudo python3 ...' で実行してみてください。")

except KeyboardInterrupt:
    # Ctrl+Cが押されたらループを抜ける
    print("\nプログラムを終了します。")

finally:
    # プログラム終了時にシリアルポートを閉じる
    if ser and ser.is_open:
        ser.close()
        print(f"シリアルポート {SERIAL_PORT} を閉じました。")

4. コードの仕組みを徹底解説!

4.1. ポートのオープンとクローズ (`try-finally`)

try:
    ser = serial.Serial(SERIAL_PORT, BAUD_RATE)
    # ... 送信処理 ...
finally:
    if ser and ser.is_open:
        ser.close()

このtry-finallyブロックは、シリアル通信において非常に重要です。serial.Serial()でポートを開いたら、プログラムが正常に終了しても、エラーで中断しても、最後に必ずser.close()でポートを閉じる必要があります。ポートを開きっぱなしにすると、他のプログラムがそのポートを使えなくなるなどの問題が発生する可能性があるためです。finally節は、エラーの有無にかかわらず必ず実行されるため、後片付け処理に最適です。

4.2. データの送信 (`ser.write()`とエンコード)

time_str = now.strftime('%H%M')
ser.write(time_str.encode('utf-8'))

ここがデータを送信する核心部分ですが、重要なポイントが2つあります。

  1. データ形式の準備: まず、datetimeオブジェクトをstrftime()"1507"のような文字列に変換しています。
  2. バイト列へのエンコード: シリアル通信で送受信されるのは、Pythonの文字列(str)ではなく、**バイト列(bytes)** です。そのため、ser.write()で送信する前に、文字列を.encode('utf-8')メソッドを使ってバイト列に変換(エンコード)する必要があります。'utf-8'は最も一般的な文字エンコーディングです。

4.3. エラーハンドリング (`try-except`)

except serial.SerialException as e:
    # ...
except KeyboardInterrupt:
    # ...

プログラムをより頑丈にするために、エラー処理を入れています。

  • serial.SerialException: ポート名が間違っている、デバイスが接続されていない、アクセス権限がない、といったシリアルポート関連のエラーをキャッチします。
  • KeyboardInterrupt: ユーザーがCtrl+Cを押してプログラムを中断した際に発生するエラーをキャッチし、安全にループを抜けてfinallyブロックの終了処理に移れるようにしています。

5. 使い方と動作確認

  1. 上記のコードをserial_sender.pyとして保存します。
  2. コード冒頭のSERIAL_PORTBAUD_RATEを、ご自身の環境と、通信相手の機器の設定に合わせて正しく書き換えます。
  3. ターミナルでスクリプトを実行します。
    python3 serial_sender.py
    (ポートへのアクセス権限がない場合は sudo python3 serial_sender.py で試してみてください)

送信したデータを確認する方法は?
送りっぱなしでは面白くないですよね。データが実際に送信されているかを確認するには、受信側を用意する必要があります。

  • PCで受信する場合: USB-シリアル変換アダプタなどを使い、送信側のTXDピンを受信側PCのRXDピンに接続します。PC側では、**Tera Term** (Windows) や **minicom**, **screen** (Linux/macOS) といったシリアル通信ターミナルソフトを起動し、同じポートとボーレートで待機すると、1秒ごとに「1507」「1508」…といったデータが流れてくるのが見えます。
  • ループバックテスト: 受信機がない場合の一番簡単なテスト方法です。JetsonやRaspberry Piの**TXDピンとRXDピンを一本のジャンパー線で直結**します。こうすると、自分で送信したデータがそのまま自分の受信ピンに戻ってきます。この状態で、ターミナルをもう一つ開き、cat /dev/ttyTHS1のようなコマンドを実行すると、送信データが表示されるのが確認できます。

まとめ

今回は、Pythonのpyserialライブラリを使って、シリアル通信でデータを送信する基本的な方法を学びました。

  • pyserialライブラリのインストールと、シリアルポートの設定方法。
  • try-finally構文を使った、安全なポートのオープンとクローズ処理。
  • ser.write()でデータを送信する際は、文字列をバイト列にエンコード(.encode())する必要があること。
  • serial.SerialExceptionKeyboardInterruptを考慮した、より頑丈なエラーハンドリング。

シリアル通信は、Pythonプログラムと物理的な世界(マイコン、センサー、各種モジュール)を繋ぐための、基本的かつ非常に重要なインターフェースです。次回は、逆に外部の機器から送られてくるデータをPythonで**受信する**方法について探求していくかもしれません。あなたのプロジェクトの可能性が、ここからさらに広がっていきます!

その他の投稿はこちら

コメント

このブログの人気の投稿

タイトルまとめ

これで迷わない!WindowsでPython環境構築する一番やさしい方法 #0

【Python標準ライブラリ完結!】11の冒険をありがとう!君のPython力が飛躍する「次の一歩」とは? 🚀