PythonとArduinoのシリアル通信「つながらない」を完全解決!トラブルシューティング総合ガイド
「pyserialを使ってArduinoとつなごうとしたけど、ポートが開けない…」「データは受信できているはずなのに文字化けする…」「Serial Monitorを閉じ忘れていたらエラーになった…」
PythonとArduinoのシリアル通信は、一度ハマると原因が見えにくいトラブルが多いのが正直なところです。この記事では、よくある接続トラブルをパターン別に徹底整理し、『pyserial 接続できない』『arduino シリアル通信 文字化け』といった問題をまとめて解決できるハブ記事として構成しました。
基本的な接続手順やサンプルコードは以下の入門記事をご参照ください。本記事はトラブル解決に特化して進めます。
- 👉 PythonとArduinoをシリアル通信で連携させる方法【pyserial入門・サンプルコード付き】
- 👉 Python × Arduino シリアル通信入門!センサーデータをリアルタイム可視化する方法
📋 この記事の対象読者

- ✅ pyserialでArduinoに接続しようとしてエラーが出た方
- ✅ COMポートやttyデバイスがどれかわからない方
- ✅ シリアル通信は動いているのにデータがおかしい方
- ✅ Serial Monitorとの競合でハマっている方
🔌 トラブル①:COMポート・ttyデバイスが特定できない
「そもそもどのポートにArduinoが接続されているかわからない」というのは、シリアル通信トラブルの入口でもっともよくある問題です。OS別に確認方法を整理しましょう。
Windowsの場合:デバイスマネージャーで確認
スタートメニューで「デバイスマネージャー」と検索して開き、「ポート(COMとLPT)」の項目を展開します。
- 「Arduino Uno(COM3)」のように表示されていれば、そのCOM番号を使います
- 「USB-SERIAL CH340(COM4)」のように表示される場合もあります(互換機に多い)
- Arduinoを抜き差しすると表示が増減するので、それで判別するのが確実です
Pythonコードでは以下のように指定します。
import serial
# WindowsのCOMポート指定例
ser = serial.Serial('COM3', 9600, timeout=1)
Mac / Linuxの場合:ls /dev/tty* で確認
ターミナルで以下のコマンドを実行します。
# Arduinoを接続する前に実行
ls /dev/tty*
# Arduinoを接続した後に再度実行
ls /dev/tty*
接続前後で増えたデバイス名がArduinoです。典型的には以下のような名前です。
- Mac:
/dev/tty.usbmodem1401、/dev/tty.usbserial-110 - Linux:
/dev/ttyUSB0、/dev/ttyACM0
pyserialを使ってポートを自動スキャンする方法もあります。
import serial.tools.list_ports
# 接続中のシリアルポートを全部リストアップ
ports = serial.tools.list_ports.comports()
for port in ports:
print(f'ポート: {port.device} 説明: {port.description}')
このスクリプトを実行すると、接続中のポート一覧と説明が表示されます。「Arduino」「CH340」「usbmodem」などのキーワードが含まれているものが対象です。
🚫 トラブル②:ポートが開けない・SerialExceptionが出る
COMポートは特定できたのに、serial.Serial()を呼び出した時点でエラーになるケースです。代表的なエラーメッセージと原因を整理します。
「could not open port COM3: PermissionError」
Windowsでよく出るこのエラーは、主に以下の原因が考えられます。
- Arduino IDEのSerial Monitorが開いている(後述のトラブル④で詳解)
- 別のPythonスクリプトが同じポートを掴んだまま終了した
- Windowsの「排他制御」によって他プロセスがポートを占有している
Linux / Macでのパーミッションエラー(PermissionError: [Errno 13])の詳しい解決手順は、以下の専門記事をご参照ください。
👉 PythonのPermissionError: [Errno 13] の原因と解決方法【専門記事】
「serial.serialutil.SerialException: device reports readiness to read but returned no data」
主にLinux環境でttyACM系デバイスを使う際に出ることがあります。典型的な原因は、デバイスの瞬断・自動リセットや、LinuxのModemManagerがポートを掴んでしまうケースです。まずはModemManagerの無効化(sudo systemctl disable --now ModemManager)やudevルールの確認を試しましょう。あわせて補助的な対策として、timeoutをNone(ブロッキングモード)のままにせず、明示的に指定しておくのがおすすめです。
import serial
# timeout=1 を必ず指定する(Noneにしない)
ser = serial.Serial('/dev/ttyACM0', 9600, timeout=1)
with文でポートを確実に閉じる
スクリプトが途中で落ちてもポートが解放されるよう、with文を使うのがベストプラクティスです。
import serial
import time
with serial.Serial('COM3', 9600, timeout=1) as ser:
time.sleep(2) # リセット待ち(重要)
ser.write(b'H')
response = ser.readline()
print(response)
# ここでポートは自動的に閉じられる
⏱️ トラブル③:文字化け・データ欠落・空白行ばかり返ってくる
接続自体は成功しているのに、受け取るデータがおかしい——このケースはいくつかのパターンに分類できます。
原因A:time.sleep(2) を入れていない(リセット待ち不足)
これがもっとも多い原因です。ArduinoはPCからシリアル接続を確立した瞬間に自動リセットがかかります。このリセット中にPythonがデータを送っても、Arduinoは受け取れません。
import serial
import time
ser = serial.Serial('COM3', 9600, timeout=1)
# ❌ NGパターン:sleep なしですぐ送信
ser.write(b'H') # Arduinoがリセット中で受け取れない!
# ✅ OKパターン:2秒待ってから送信
time.sleep(2) # ← これが必須!
ser.write(b'H')
待ち時間の目安は2秒ですが、Arduino Megaや一部の互換機では3秒必要な場合もあります。うまくいかない場合は3秒に延ばしてみてください。
原因B:decode()の失敗・エンコーディング不一致
ser.readline()が返すのはbytes型です。これを文字列として扱うには.decode()が必要ですが、エンコーディング指定を誤ると例外が発生します。
import serial
import time
ser = serial.Serial('COM3', 9600, timeout=1)
time.sleep(2)
line = ser.readline()
print(type(line)) # <class 'bytes'>
print(line) # b'1023\r\n' のようなバイト列
# ✅ utf-8でデコード+strip()で改行・空白を除去
decoded = line.decode('utf-8').strip()
print(decoded) # '1023'
# ⚠️ デコードできない不正バイトが混ざる場合
decoded_safe = line.decode('utf-8', errors='replace').strip()
print(decoded_safe) # 不正バイトを ? に置き換えて表示
受信データが空文字列になるケースでは、if line:で空行ガードを入れることも有効です。
while True:
line = ser.readline()
if not line: # タイムアウト時はb''が返る
continue
decoded = line.decode('utf-8', errors='replace').strip()
if decoded: # 空文字列もスキップ
print(f'受信: {decoded}')
原因C:受信バッファにゴミデータが残っている
接続直後のバッファには、過去の通信残滓が入っていることがあります。reset_input_buffer()で一掃してから読み始めましょう。
ser = serial.Serial('COM3', 9600, timeout=1)
time.sleep(2)
# バッファをクリアしてから読み始める
ser.reset_input_buffer()
while True:
line = ser.readline().decode('utf-8', errors='replace').strip()
if line:
print(line)
📡 トラブル④:ボーレート不一致でデータが壊れる
シリアル通信でもっとも見落としやすい設定ミスがボーレートの不一致です。Arduinoのスケッチ側と、Python側で指定するボーレートが一致していないと、受信したデータはすべて文字化けします。
チェックポイント
Arduinoスケッチのsetup()内を確認してください。
void setup() {
Serial.begin(115200); // ← ここのボーレートを控える
}
Python側では同じ値を指定します。
import serial
# Arduino側が115200なら、Python側も115200に合わせる
ser = serial.Serial('COM3', 115200, timeout=1)
よくある間違いパターン
| Arduino側 | Python側 | 結果 |
|---|---|---|
| 9600 | 9600 | ✅ 正常 |
| 115200 | 9600 | ❌ 文字化け |
| 9600 | 115200 | ❌ 空データ or 文字化け |
スケッチを書き換えてボーレートを変えた場合、Arduino IDEのSerial Monitorも同じ値に変更しないと確認できません。Python側も同様に更新が必要です。
🔒 トラブル⑤:Serial Monitorとの二重接続エラー
「Pythonコードを実行したらエラーになるのに、ArduinoのSerial Monitorでは動いている」という場合、Serial MonitorがCOMポートを占有していることがほぼ確実に原因です。
シリアルポートは基本的に1つのプロセスしか同時接続できません。Arduino IDEのSerial MonitorがポートをOPENにしたまま、Pythonからも同じポートを開こうとすれば、必ずエラーになります。
解決手順
- Arduino IDEの Serial Monitor(🔍アイコン)を閉じる
- Arduino IDE自体を完全に終了する(念のため)
- Pythonスクリプトを実行する
# NG:Serial Monitor が開いたまま実行すると以下のエラーが出る
# serial.serialutil.SerialException: could not open port 'COM3':
# PermissionError(13, 'アクセスが拒否されました。', None, 5)
# ✅ Serial Monitor を閉じてから実行すれば接続できる
import serial
import time
ser = serial.Serial('COM3', 9600, timeout=1)
time.sleep(2)
print('接続OK')
ser.close()
他ツールとの競合にも注意
Serial Monitor以外にも、以下のツールが同じポートを掴んでいる場合があります。
- PlatformIOのモニター機能
- CoolTermやPuTTYなどのシリアルターミナルソフト
- 過去のPythonスクリプトがクラッシュして
ser.close()を呼ばずに終了した場合
最後のケースでは、PCを再起動するか、タスクマネージャー(Windows)やlsof /dev/ttyUSB0(Linux)/lsof /dev/tty.usbmodem*(Mac)でポートを掴んでいるプロセスを終了させましょう。
🛠️ トラブル⑥:送信データが届いているのにArduinoが反応しない
pyserialからの送信は成功しているはずなのに、Arduinoが反応しない場合のチェック項目です。
チェックA:バイト列として送っているか
ser.write()に渡すのはバイト列(bytes型)でなければなりません。文字列を渡すとTypeErrorになります。
# ❌ NG:文字列を直接渡すとTypeError
ser.write('H')
# ✅ OK:bプレフィックスでバイト列として送る
ser.write(b'H')
# ✅ OK:encode()を使う方法
ser.write('H'.encode('utf-8'))
チェックB:Arduino側がSerial.available()で待っているか
Arduinoのloop()内でシリアル受信を処理しているか確認します。Serial.available()が0より大きい場合だけ読み取る構造になっていないと、データを無視してしまいます。
// ✅ 正しい受信待ちパターン
void loop() {
if (Serial.available() > 0) {
char command = Serial.read();
// commandを使った処理
}
}
チェックC:改行コードを期待しているArduinoスケッチへの対応
Serial.readStringUntil('\n')を使うスケッチでは、Python側が改行コードを付けて送る必要があります。
# 改行コードを付けて送る
ser.write(b'HELLO\n')
# または
command = 'HELLO'
ser.write((command + '\n').encode('utf-8'))
🔍 トラブル⑦:接続は成功するが通信が途中で途切れる
長時間動かしていると途中でデータが来なくなる、というトラブルもあります。
タイムアウト設計を見直す
serial.Serial(timeout=1)で設定したタイムアウトが短すぎると、Arduinoの送信間隔によっては空データを返し続けます。Arduino側のdelay()値と合わせて調整しましょう。
import serial
import time
# Arduinoがdelay(500)で送信している場合、timeout=2くらいに余裕を持たせる
ser = serial.Serial('COM3', 9600, timeout=2)
time.sleep(2)
ser.reset_input_buffer()
try:
while True:
line = ser.readline().decode('utf-8', errors='replace').strip()
if line:
print(f'受信: {line}')
except KeyboardInterrupt:
print('停止しました')
finally:
ser.close() # 必ずcloseする
in_waiting でバッファを確認する
ser.in_waitingで受信バッファに何バイト溜まっているかを確認できます。これを使って「データが来てから読む」制御も可能です。
import serial
import time
ser = serial.Serial('COM3', 9600, timeout=1)
time.sleep(2)
while True:
if ser.in_waiting > 0: # バッファにデータがある時だけ読む
line = ser.readline().decode('utf-8', errors='replace').strip()
if line:
print(f'受信({ser.in_waiting}bytes残り): {line}')
time.sleep(0.1)
📌 トラブルシューティング早見表
| 症状 | 主な原因 | 対処法 |
|---|---|---|
| COMポートが見つからない | ドライバ未インストール / ケーブルが充電専用 | デバイスマネージャー確認 / ケーブル交換 |
| PermissionError / port open失敗 | Serial Monitor占有 / 別プロセス競合 | Serial Monitorを閉じる / PC再起動 |
| 文字化け・ゴミデータ | ボーレート不一致 / sleep不足 | ボーレートを合わせる / time.sleep(2)追加 |
| 空データ・空行ばかり | タイムアウト / リセット待ち不足 | timeout値を増やす / sleep延長 / reset_input_buffer() |
| decode()エラー | 不正バイト混入 | errors=’replace’オプション追加 |
| Arduinoが反応しない | 文字列でwrite / 改行コード不足 | b’…’のバイト列で送る / \n追加 |
| 途中でデータが途切れる | timeout短すぎ / バッファ溢れ | timeoutを延長 / in_waitingで制御 |
🚀 次のステップ:動いたら可視化・IoTへ
トラブルを解決して通信が安定したら、次はセンサーデータの活用に進んでみましょう。リアルタイムグラフ化やIoTシステムへの発展については、以下の記事で詳しく解説しています。
- 📊 Python × Arduino シリアル通信入門!センサーデータをリアルタイム可視化する方法
- 🌡️ ArduinoとPythonで作るIoT温湿度モニタリングシステム【ゼロから動かす入門ガイド】
✅ まとめ
PythonとArduinoのシリアル通信でつながらない・うまくいかない時のトラブルを、パターン別に整理しました。
- 🔌 COMポートはデバイスマネージャー / ls /dev/tty* で特定する
- 🚫 PermissionErrorはSerial Monitorの二重接続が最多原因
- ⏱️ time.sleep(2) はリセット待ちに必須、省略禁止
- 📡 ボーレートはArduinoとPythonで必ず一致させる
- 🔤 decode()は errors=’replace’ で安全に、空行ガードも忘れずに
- 📦 with文でポートを確実にcloseする習慣をつける
「それでも解決しない!」という場合は、コメント欄にエラーメッセージを貼っていただければ一緒に確認します 😊 この記事がシリアル通信トラブルの早期解決に役立てば幸いです!
こちらも読まれています
📡 Arduinoをもっと深く学ぼう!
Arduino・ラズパイ・ロボットプログラミングを体系的に学びたい方へ。おすすめのUdemyコースや電子部品もまとめています。
📚 関連商品・おすすめ書籍
※本記事にはアフィリエイトリンクが含まれます。





