信頼できるシリアル接続に必要なのは、ボーレートを揃えることだけではありません。アプリケーションコマンドを作る前に、物理接続、フレーム境界、エンコーディング、最大メッセージ長、タイムアウト、確認応答、リセット動作を定義します。
この2026年メンテナンス層では、元の5行のメモを、Arduino の公式 Serial API と pySerial を使う完全な USB シリアル手順へ拡張しました。2019年のエクスポート全文は出典アーカイブとして末尾に残しています。
Table of Contents
1. USB シリアルか GPIO UART を選ぶ
最初の実装では、Arduino の通常の USB データポートを Raspberry Pi の USB ポートへ接続するのが簡単です。Arduino は一般に /dev/ttyACM0 または /dev/ttyUSB0 として現れます。この方法では Raspberry Pi の GPIO UART を有効にしたり、TX/RX ピンを配線したりする必要はありません。ポートを開くと DTR によってリセットされる Arduino ボードもあるため、ソフトウェア側で短い再起動を許容します。
TTL UART の直結は別の電気設計です。TX と RX を交差させ、GND を共有します。Raspberry Pi の UART ピンは 3.3 V ロジックであり、公式文書は 5 V 信号を接続すると破損すると警告しています。相手機器が 3.3 V 対応でなければ、適切なレベルシフターまたは USB-3.3 V シリアルアダプターを使います。UART を直結する場合は raspi-config で UART ハードウェアを有効にし、シリアルのログインコンソールを無効にして、モデル固有の /dev/serial* 対応を確認します。
回路全体とボード動作をレビューしていない限り、USB シリアルと TX/RX の直結を同時に使わないでください。
2. 先に小さなプロトコルを定義する
この例は意図的に狭い契約を使います。
| 項目 | 契約 |
|---|---|
| 転送 | USB シリアル、115200 baud、8 data bits、no parity、1 stop bit |
| フレーム境界 | 1行に1つの ASCII コマンド。LF(n)で終端し、CR は無視 |
| 最大コマンド | 終端文字の前に63バイトまで |
| リクエスト | PING <10進リクエストID> |
| 成功 | OK <同じリクエストID> |
| 失敗 | ERR bad_command、ERR invalid_byte、ERR line_too_long |
| 起動通知 | READY 1。ホストがポートを開く前に送られる場合があるため任意 |
リクエスト ID があれば、ホストは応答を対応付けられます。PING は副作用がないので安全に再試行できます。ハードウェアの移動、課金、状態書き込みを行うコマンドには明示的な冪等性設計が必要です。無条件に再試行すると動作が重複します。
プロトコルのキーワードと数値 ID には ASCII で十分です。後で人間向けテキストを運ぶなら UTF-8 と明記し、バイト長を制限して、不正入力の拒否方法を決めます。両端で偶然有効になっているエンコーディングに依存してはいけません。
3. 上限付き Arduino パーサーをアップロードする
次のスケッチは、上限のない String を使わず、loop() 内でも待機しません。1行をバッファリングし、ASCII 以外の制御/データバイトを拒否し、長すぎるフレームは改行まで破棄します。
#include <Arduino.h>
#include <string.h>
constexpr unsigned long BAUD_RATE = 115200;
constexpr size_t MAX_LINE = 64;
enum class DropReason {
none,
invalid_byte,
line_too_long
};
char line_buffer[MAX_LINE];
size_t line_length = 0;
DropReason drop_reason = DropReason::none;
bool is_request_id(const char *text) {
if (*text == '