コマンドだけ先に見たい方へ(いずれも本文で解説しているものです。当サイトで実際に実行して確かめたのは、インストールと (4) の loop:// による送受信、そして (5) の miniterm が pyserial に同梱されていて起動できること(python -m serial.tools.miniterm --help)までです。(2) は当サイトの環境に実機の変換器が無いため、出力例を確認していません。COM3 のような実ポート名を指定する (3)・(5) も公式ドキュメントの記述に沿った書き方で、実機での確認は含みません)

(1) インストールする(venv を有効にしてから実行)

python -m pip install pyserial

(2) つながっているポートを調べる(2.2

python -m serial.tools.list_ports -v

(3) 開いて 1 行読む(最短形。timeout は必ず入れる=4 章

import serial

with serial.Serial("COM3", 9600, timeout=1.0) as ser:
    ser.write(b"PING\r\n")
    print(ser.readline())

(4) 実機が無いところで挙動を確かめる(ドライバも管理者権限も不要。本記事の実測環境=2.4

import serial

ser = serial.serial_for_url("loop://", timeout=1.0)
ser.write(b"OK\n")
print(ser.readline())   # -> b'OK\n'
ser.close()

(5) 機器と手打ちで対話する(Tera Term を入れられない PC でも使える=6.1

python -m serial.tools.miniterm COM3 9600

引数の意味は 3.1 早見表、読み取り API の選び方は 5.1、エラーが出たときは 12 章へ。

2026 年 8 月 31 日更新: 「使い方の全体像」「Serial() の引数早見表」「timeout の 3 挙動(実測)」「読み取り API の使い分け」「よくあるエラーと対処」を新設し、図解を 3 枚追加しました。あわせて、旧版で 「com0com はドライバ署名の問題が 2026 年現在も未解決」と書いていた記述を訂正しています。公式の配布ディレクトリには署名済みパッケージが置かれており、「未解決」は事実として不正確でした(訂正後の記述は 6.2)。

この記事の目次

1. pyserial とは — 何をするライブラリで、なぜ名前が紛らわしいのか

pyserial は、OS が持っているシリアルポートの API を Python から扱えるようにするライブラリです。責務は「ポートを開く・閉じる」「バイト列を送る・受け取る」「通信条件(ボーレートやパリティ)と制御線を設定する」まで。その上に乗るプロトコル(Modbus RTU や機器メーカー独自のコマンド体系)は扱いません。フレームの切り出しも CRC の検算も、こちら側で書くか、上位のライブラリに任せることになります。ここを最初に押さえておくと、後述する「1 回の read でメッセージが揃わない」問題(8 章)が想定内になります。

項目値(2026 年 8 月 31 日時点)
最新安定版3.5(PyPI へのアップロードは 2020-11-23、git タグも同日)
ライセンスBSD
対応 Python(公式の要件)Python 2.7 または 3.4 以降。Windows で動かす場合は Windows 7 以降
パッケージの requires_python未指定(pip 側でのバージョン制限はかかっていない)
開発の状況リポジトリはアーカイブされていない。master への push は 2026-05-19。未リリースの変更として「Python 3.10 以上への対応」「EOL となった Python 系の切り捨て」「Jython / IronPython 対応の削除」が入っている
本記事の検証環境(筆者の自宅検証環境)Windows 11 Pro / Python 3.12.10 / pyserial 3.5(venv)。仮想ポートは loop://

ひとつ注意があります。GitHub の Releases ページで v3.5 を見ると 2025 年の日付が表示されますが、これは Release エントリが後年になって作成されたものです。git タグの作成日も PyPI への配布日も 2020-11-23 で、2025 年に新しい版が出たわけではありません。バージョンの新しさを日付で判断するときは、PyPI 側の配布日を見るのが安全です。

「5 年以上リリースが無いライブラリを本番で使ってよいのか」という懸念はもっともですが、シリアル通信は仕様そのものが動かない領域で、ライブラリ側に変えるべきものが少ないのも事実です。実際、リポジトリはアーカイブされておらず master には変更が入り続けています。次の版は Python 3.10 以上が必須になる見込みなので、「開発停止したライブラリ」と断じるのは正確ではありません。とはいえ 3.5 が長く据え置かれているのは事実なので、新しい Python での不具合報告は Issue トラッカーを確認してから採用するのが無難です。

1.1 pip 名は pyserial、import 名は serialpip install serial は別物)

pyserial で最初につまずくのがこの名前の非対称です。

python -m pip install pyserial   # インストールはこの名前
import serial                     # import はこの名前

import pyserial と書くと ModuleNotFoundError になります。ここまでは多くの解説記事に書かれていますが、もう一段、名前の衝突による分かりにくい事故があります。PyPI には serial という名前のパッケージが別に存在していて、pip install serial はそちらを取ってきます。当サイトで pip index versions serial を実行して確認したところ、返ってきたのは serial (0.0.97) でした。PyPI 上の説明によれば、これは JSON / YAML / XML と Python のクラスを相互変換するためのライブラリで、シリアル通信とはまったく関係がありません

厄介なのは、どちらも serial という名前で import できてしまう点です。「import は通るのに serial.Serial が見つからない」という分かりにくい壊れ方をしたら、まずこれを疑ってください。対処は入れ直すだけです。

python -m pip uninstall serial
python -m pip install pyserial
python -c "import serial; print(serial.__version__)"   # -> 3.5

当サイトで実測したのは「PyPI 上の serial が 0.0.97 という別のライブラリである」ところまでで、誤インストール後にどんなエラーが出るかまでは再現していません。社内プロキシで pip 自体が通らない場合の対処は 学習ロードマップの STEP 2 にまとめています。

1.2 なぜシリアル通信が今も現役なのか

Ethernet と TCP/IP が標準になった現在でも、産業機器の世界にはシリアル通信が大量に残っています。理由は単純で、構成がシンプルで安価、そして長年の実績があるからです。温調器、流量計、はかり、バーコードリーダ、古い PLC、計測機器——どれも RS-232C か RS-485 で動いています。

新規設備でも、コスト要件と安定性の観点から RS-485(Modbus RTU を含む)が選ばれることは珍しくありません。Python 側で「シリアルが必要になったら躊躇なく書ける」状態を作っておくと、既設と新設が混在する現場で選択肢が減らずに済みます。

2. 使い方の全体像 — 3 ステップで動かす

最小構成は「入れる → ポートを見つける → 開いて読み書きする」の 3 ステップです。細かいオプションは後回しにして、まずここを通してください。

2.1 ステップ 1: インストールとバージョン確認

python -m venv .venv
.venv\Scripts\activate
python -m pip install pyserial
python -c "import serial; print(serial.__version__)"

最後の行で 3.5 が出れば準備完了です(当サイトの環境で確認しています)。仮想環境を切らずにグローバルへ入れると、後で「入れたはずなのに ModuleNotFoundError」という定番の事故(12 章)が起きやすくなるので、venv を有効にしてから実行してください。

ここで 2 行目の activate が「スクリプトの実行が無効になっているため…」と弾かれることがあります。PowerShell の実行ポリシーが既定のままだと起きる現象で、緩和が必要です(コマンドプロンプトならそのまま通ります)。社内プロキシで pip install が止まるケースと合わせて、回避策を挙げておきます。どちらも管理者権限は不要です。

# activate が「スクリプトの実行が無効…」で弾かれる場合
# この PowerShell の窓の中だけ緩和する。管理者権限は不要
Set-ExecutionPolicy -Scope Process -ExecutionPolicy RemoteSigned

# 社内プロキシで pip install が通らない場合(コマンドプロンプトでも同じコマンドです)
python -m pip install --proxy http://<プロキシのホスト>:<ポート> pyserial

activate を実行したあとにプロンプトの先頭へ (.venv) が付いていれば、仮想環境に入れています。ここが付かないまま先へ進むと、pip の導入先と実行時の Python が食い違ったままになります。認証が必要なプロキシや外部に出られない閉域網では、社内ミラーやオフラインインストールの手順を情報システム部門に確認してください。

2.2 ステップ 2: ポートを見つける

pyserial には、つながっているシリアルポートを列挙するツールが同梱されています。コマンド 1 行で確認できます。

python -m serial.tools.list_ports        # 一覧
python -m serial.tools.list_ports -v    # 説明とハードウェア ID 付き

スクリプトから使う場合は comports() を呼びます。

import serial.tools.list_ports

for p in serial.tools.list_ports.comports():
    print(p.device, "|", p.description, "|", p.hwid)

返ってくるオブジェクト(ListPortInfo)は deviceCOM3/dev/ttyUSB0 といったパス)、namedescriptionhwid を持ち、USB 接続の機器ならさらに vid / pid / serial_number / location / manufacturer / product / interface が入ります。この serial_number が、Windows で COM 番号がずれる問題への対策に効いてきます(11.1)。名前で絞り込みたいときは list_ports.grep(regexp) も使えます。

実務では、USB-シリアル変換ケーブルを挿す前と後で 2 回実行して、増えた行を目的のポートと判断するのが最も確実です。Windows ならデバイスマネージャーの「ポート (COM と LPT)」でも同じ確認ができます。Linux では USB 変換器が /dev/ttyUSB0、マイコンボード類が /dev/ttyACM0 として見えることが多いです。

なお、実際の出力例は環境ごとに変わるため本記事には掲載していません。当サイトの検証環境は loop:// の仮想ポートで、実機の変換器を挿した状態の出力は取得していないためです。

2.3 ステップ 3: 開いて、書いて、読む

import serial

ser = serial.Serial(
    port="COM3",        # Linux なら "/dev/ttyUSB0"
    baudrate=9600,
    bytesize=serial.EIGHTBITS,
    parity=serial.PARITY_NONE,
    stopbits=serial.STOPBITS_ONE,
    timeout=1.0,        # read のタイムアウト(秒)
    write_timeout=1.0,
)

try:
    ser.write(b"PING\r\n")
    response = ser.readline()
    print(response.decode("utf-8", errors="replace").rstrip())
finally:
    ser.close()

閉じ忘れるとポートが掴まれたままになり、次の実行で開けなくなります。with 文を使えば自動で閉じます。

with serial.Serial("COM3", 9600, timeout=1.0) as ser:
    ser.write(b"PING\r\n")
    print(ser.readline())

送受信するのは bytes であって str ではありません。b"PING\r\n" のように bytes リテラルで書き、受け取った側は必要なときだけ decode() します。ここを str のまま渡すと TypeError になります。

そして最も事故が多いのが通信条件です。ボーレート・データビット・パリティ・ストップビットは、機器側の設定と完全に一致させる必要があります。ここがズレると、無音(何も返ってこない)か文字化けのどちらかになり、原因がコード側にあるのかハード側にあるのか分からないまま時間が溶けます。機器の取扱説明書で「9600, 8, None, 1」のように書かれている 4 つの値を、必ず先に確認してください。

2.4 実機が届く前に動かす — loop:// なら何も要らない

「機器はまだ現場にあって手元に無い」「変換ケーブルの手配待ち」という状況でも、pyserial の挙動確認だけなら今すぐできます。pyserial は serial_for_url() という関数を持っていて、loop:// を渡すと書いたものがそのまま読める仮想ポートが手に入ります。ドライバのインストールも管理者権限も不要です。

import serial

ser = serial.serial_for_url("loop://", timeout=1.0)
ser.write(b"OK\n")
print(ser.readline())    # -> b'OK\n'
ser.close()

本記事の実測値(4 章timeout5.2readline5.4in_waiting)は、すべてこの loop:// 環境で計測したものです。管理者権限が下りない会社 PC でも、API の使い方とパーサのテストはここまで進められます。

できないことも明確です。loop:// は物理的な信号線を持たないので、ボーレートやパリティの不一致、配線の誤り、送受信の切り替えタイミングは再現できません。そこを試したい場合は 6 章の仮想 COM ペアや実機に進んでください。

3. serial.Serial() の引数早見表

3.1 早見表

公式が受け付ける引数は次のとおりです(並びは使う頻度順で、公式のシグネチャの並びとは異なります)。ほとんどの引数は既定値のままで動くので、実際に指定するのは上から 6 つ程度になります。

引数既定値意味と使いどころ
portNoneデバイス名(COM3 / /dev/ttyUSB0)。None のままにするとポートを開かずにオブジェクトだけ作れる(後述)
baudrate9600ボーレート。機器の設定と一致させる
bytesizeEIGHTBITSデータビット長(FIVEBITSEIGHTBITS)。産業機器はほぼ 8
parityPARITY_NONEパリティ(NONE / EVEN / ODD / MARK / SPACE)。Modbus RTU の機器では EVEN が既定のことも多い
stopbitsSTOPBITS_ONEストップビット(ONE / ONE_POINT_FIVE / TWO
timeoutNone読み取りタイムアウト(秒・小数可)。既定は無限ブロック。実運用では必ず指定する(4 章
write_timeoutNone書き込みタイムアウト。公式 API リファレンスは、write_timeout を設定しない限り write() はブロックすると説明している
xonxoffFalseソフトウェアフロー制御。データ中に制御文字が現れるバイナリ通信では使わない
rtsctsFalseRTS / CTS によるハードウェアフロー制御。RS-485 の方向制御を手で行う場合はここを False のままにする10.2
dsrdtrFalseDSR / DTR によるハードウェアフロー制御
inter_byte_timeoutNoneバイト間タイムアウト(None で無効)。無通信をフレーム境界とみなす方式で使う(8.3
exclusiveNone排他アクセス。POSIX 限定で、すでに排他で開かれているポートは開けなくなる

この表のうち、当サイトの環境で実際にキーワード引数として受け付けることを確認したのは port / baudrate / bytesize / parity / stopbits / timeout / write_timeout / rtscts / dsrdtr / xonxoff です。inter_byte_timeoutexclusive は公式 API リファレンスの記述に基づく整理で、動作の実測はしていません。

3.2 「9600 8N1」の読み方と、ポートを後から開く書き方

機器の取扱説明書によく出てくる「9600 8N1」は、ボーレート 9600 / データビット 8 / パリティ None / ストップビット 1 の略記です。pyserial の既定値がちょうどこの組み合わせなので、8N1 の機器なら serial.Serial("COM3", 9600, timeout=1.0) だけで条件は揃います。逆に「9600 7E1」(データ 7・偶数パリティ)のような機器では、bytesize=serial.SEVENBITS, parity=serial.PARITY_EVEN を明示しないと通信できません。

port を省略してオブジェクトだけ先に作り、設定を詰めてから開くこともできます。RS-485 で「開いた瞬間に送信状態になってバスを占有する」のを避けたいときなど、開く前に制御線の状態を決めておきたい場面で使います。公式ドキュメントも、rts / dtrポートを開く前に代入しておくと open() の時点で適用されると説明しています。

ser = serial.Serial()          # まだ開かない
ser.port = "COM3"
ser.baudrate = 9600
ser.timeout = 1.0
ser.rts = False                # 開く前に受信側へ倒しておく
ser.open()

4. timeout の 3 つの意味(実測)

timeout は単なる「待ち時間」ではなく、渡す値によって読み取りの性格そのものが変わる引数です。ここを理解していないと「プログラムが固まったまま返ってこない」「何も読めていないのにエラーも出ない」という 2 大症状に必ず出会います。

timeout の値ごとに read が戻るまでの時間が変わることを示すシーケンス図 timeout が None のときは戻らず、0 のときは即座に空の bytes を返し、1.5 のときは 1.5 秒待ってから空の bytes を返す。 ser.read(10) を呼んでから戻ってくるまで(相手からデータが来ない場合) timeout=None データが来るまで戻らない(永久にブロック) timeout=0 0.0 ミリ秒で b'' を返す(当サイト実測) timeout=1.5 1.50 秒待ってから b'' を返す(当サイト実測) 0 0.5 1.0 1.5 2.0 秒
図 1: timeout の値と read() が戻るまでの時間(スマートフォンでは図を横にスクロールできます)
  • timeout=0 はノンブロッキングで、待たずに戻ります。途中まで届いていれば、その分だけ返ります(read() は要求バイト数に満たなくても返り、例外にはなりません)。
  • 実測環境は筆者の自宅検証環境(Windows 11 Pro / Python 3.12.10 / pyserial 3.5)で、仮想ポートは loop:// です。timeout=None は仕様を確認しただけで、時間は計測していません。

4.1 None / 0 / 秒数の使い分け

設定公式の説明当サイトでの実測(loop://・データを送らずに read(10)
timeout=None(既定)要求したバイト数が届くまで永久に待つ戻らないため計測せず(仕様の確認のみ)
timeout=0ノンブロッキング。どんな場合でも即座に返す0.0 ミリ秒で b'' が返る
timeout=1.5(秒・小数可)要求バイト数が揃えば即座に返り、揃わなければタイムアウトまで待つ1.50 秒経過後に b'' が返る

実務での既定はひとつです。timeout は必ず指定してください。既定値が None なので、serial.Serial("COM3", 9600) とだけ書いたコードは、機器が黙った瞬間に戻ってこなくなります。GUI アプリなら画面が固まり、常駐スクリプトならログも出さずに止まります。timeout=0 は自前でポーリングループを組む場合の選択肢ですが、まずは 0.5〜2 秒程度の実数値から始めるのが扱いやすいはずです。

4.2 読み取りのタイムアウトは例外にならない

ここが最も誤解されやすい点です。読み取りがタイムアウトしても例外は発生しません。空の bytesb'')が返るだけです。名前が紛らわしいのですが、公式 API リファレンスによれば SerialTimeoutException書き込み側のタイムアウトで送出される例外で、読み取り側では投げられません(この点は公式の記述に基づくもので、当サイトでは書き込みタイムアウトの再現実測は行っていません)。

# 悪い例: 例外で気づけるつもりのコード
try:
    data = ser.read(10)
except serial.SerialTimeoutException:
    print("タイムアウト")      # ← 読み取りでは、ここには来ない

# 良い例: 戻り値の長さで判定する
data = ser.read(10)
if len(data) < 10:
    raise TimeoutError(f"応答不足: {len(data)} / 10 バイト")

「例外が出ないから通信できているはず」と考えてしまうと、実際には 1 バイトも受け取れていない状態が延々と続きます。読み取りの成否は必ず戻り値の長さで判定する——これがシリアル通信のコードで最初に身につける作法です。

5. 読み取り API の使い分け — read / readline / read_until / in_waiting

5.1 早見表

API何をするか使う場面
read(size=1)size バイト読む。タイムアウトが設定されていれば、要求より少ない数で返ることがある固定長フレーム。長さプレフィックス方式(8.2)の本体読み取り
readline()\n までを読む。実体は io.IOBase.readline() で、pyserial 独自の実装ではない改行終端のテキスト機器。終端が \n のときだけ5.2
read_until(expected=b"\n", size=None)指定した区切りが見つかるか、size バイト受け取るか、タイムアウトするまで読む\r\nETX など \n 以外で終わるプロトコル(8.1
readlines()複数行を読む実運用では避ける。内部で read を繰り返すため、公式が「実効的なタイムアウトがはるかに大きくなりうる」と警告している
in_waiting受信バッファに溜まっているバイト数を返すプロパティ「来た分だけ読む」連続受信ループ(5.4
reset_input_buffer()受信バッファの中身を捨てる要求を送る直前。前回の残りかすを応答と誤認するのを防ぐ
reset_output_buffer()送信バッファの中身を捨て、送信中の出力を中断する異常時のリセット処理

選び方は単純です。終端文字で区切られているなら read_until、長さが先頭に入っているなら read の繰り返し、来た分だけ処理したいなら in_waiting + readreadline() は「終端が \n であることが確実な機器」に限定するのが安全です。

5.2 readline() が返ってこない・末尾が欠ける(実測)

readline() は手軽ですが、機器が改行を返さないと途端に牙をむきます。loop:// で、改行あり・改行なしの 2 通りを試した結果が次のとおりです(timeout=1.5)。

送ったものreadline() の戻り値戻るまでの時間(実測)
b"OK\n"b'OK\n'即座
b"PARTIAL"(改行なし)b'PARTIAL'(末尾に \n が無い)1.52 秒(タイムアウトまで待たされる)

ここから 2 つのことが分かります。第一に、改行を返さない機器に readline() を使うと、1 回の読み取りごとにタイムアウト時間まるごと待たされます。1 秒のタイムアウトで 10 台をポーリングすれば、それだけで 10 秒かかる計算です。第二に、返ってきたのは「途中までのデータ」であって、正常な 1 行ではありません

では、正常受信とタイムアウトをどう区別するのか。公式ドキュメントが明快な判定法を示しています——戻り値の末尾に \n が無ければ、それはタイムアウトで返ってきたものです

line = ser.readline()
if not line.endswith(b"\n"):
    raise TimeoutError(f"改行が来ないまま打ち切られた: {line!r}")
text = line.decode("ascii", errors="replace").rstrip()

この 2 行を入れておくだけで、「たまに値が途中で切れる」という再現しづらいバグの大半を検出できます。公式ドキュメントも次のように明記しています。

readline() を使うときは注意すること。シリアルポートを開くときはタイムアウトを指定すること。さもないと、改行文字が受信されない場合に永久にブロックしうる(拙訳)

pyserial 公式ドキュメント — Short introduction

なお、readline() の終端を \r\n などに変えたい場合、古い記事にある eol 引数は現在の Python では使えません(実体が io モジュール由来になったため)。終端を変えたいときは read_until(expected=b"\r\n") を使ってください。

5.3 read_until の打ち切り条件は 3 つある

read_until は「区切りが来るまで読む」関数ですが、実際には次の 3 つのうちどれか 1 つでも満たされれば返ります

  • 指定した区切り列(expected、既定は b"\n")が見つかった
  • size バイトを受け取った
  • 読み取りタイムアウトが経過した

つまり readline() と同じで、タイムアウトでも部分データが返ります。戻り値に区切り列が含まれているかを確認せずに次の処理へ流すと、途中で切れたフレームをそのまま解析してしまいます。

frame = ser.read_until(expected=b"\r\n")   # 3.4 以前は terminator= という名前だった
if not frame.endswith(b"\r\n"):
    raise TimeoutError(f"終端が来ていない: {frame!r}")

size を併せて指定しておくと、想定外に長いデータが延々と流れ込んできたときの歯止めになります。仕様上の最大フレーム長が分かっているなら入れておくと安全です。

5.4 in_waiting で「来た分だけ」読む連続受信ループ

機器が周期的にデータを垂れ流してくるタイプ(測定値の連続出力など)では、read(n) で待ち構えるより「受信バッファに溜まった分を取り出す」ほうが素直です。in_waiting は今バッファにあるバイト数を返します。当サイトの実測では次のように動きました。

操作in_waiting の値(実測)
ser.write(b"ABC") の直後3
ser.read(2)b'AB' を取得)した後1

受信バッファの残量が正しく追えることが確認できます。これを使った連続受信ループの雛形が次の形です。

import time

buf = bytearray()
while True:
    n = ser.in_waiting
    if n:
        buf += ser.read(n)
        # ここでフレームを切り出す(切り出し方は 8 章)
        while b"\r\n" in buf:
            line, _, buf = buf.partition(b"\r\n")
            handle(line)
    else:
        time.sleep(0.01)   # 空回りで CPU を食い潰さないための待ち

2 点だけ注意があります。else: time.sleep() を省かないでください。データが来ていない間ループが全速で回り、1 コアを使い切ります。24 時間動かす常駐プログラムでは致命的です。もう 1 点、in_waiting が返すのは「今バッファにある数」であって、フレームが揃っている保証ではありません。上の例のように、いったんバッファへ貯めてから区切りを探す形にしてください。この考え方は 8 章で詳しく扱います。

6. 実機が無くても動作確認できる環境を作る

シリアル通信の開発でいちばん効くのは、機器を触る前に PC 単体で組み上げておくことです。現場に入ってから API の使い方で悩むと、ライン停止時間をそのまま消費することになります。選択肢は 4 つあります。

方法必要なもの確認できること / できないこと
loop://2.4pyserial だけ。管理者権限不要API の挙動・パーサのテストまで。通信条件や配線は確認できない
miniterm(6.1pyserial だけ。実機か仮想ポートが要る手打ちでの対話・生バイトの確認
com0com(6.2Windows・ドライバのインストール(管理者権限)2 つの COM ポートを結んでシミュレータとクライアントを同居できる
socat(6.3Linux同上(仮想の疑似端末ペアを作る)

6.1 miniterm — pyserial に同梱されたターミナル

「まず手打ちで機器と話してみる」のは切り分けの基本ですが、業務 PC に Tera Term や PuTTY を入れるには情報システム部門の承認が要る、という現場は珍しくありません。pyserial を入れた時点で、ターミナルソフトも一緒に入っていますserial.tools.miniterm です。

python -m serial.tools.miniterm COM3 9600
python -m serial.tools.miniterm --help          # オプション一覧

公式ドキュメントによれば、起動後の操作は Ctrl+] で終了、Ctrl+T でメニュー(ファイル送信・RTS / DTR の切り替え・エコー切り替え・ボーレート変更)、Ctrl+T に続けて Ctrl+H でヘルプ表示です。表示フィルタも用意されていて、--filter debug を付けると受け取ったバイトを制御文字も含めてすべて見える形で表示できます(13.2)。

当サイトで確認したのは python -m serial.tools.miniterm --help が実行でき、--parity / --rtscts / --eol / --encoding / --raw といったオプションが存在することまでです。実ポートに接続しての対話は行っていません。それでも、「別途ソフトを申請せずにターミナルが手に入る」という一点は、閉域網の現場では十分に大きな利点です。

6.2 com0com(Windows の仮想 COM ポートペア)— 旧版の記述を訂正

Windows で「COM10 と COM11 を内部で結ぶ」ような仮想ポートペアを作る定番が com0com です。片方をシミュレータ、もう片方をクライアントが開けば、PC 1 台で送受信を確認できます。

本記事の旧版では「com0com はドライバ署名の問題が 2026 年現在も未解決」と書いていましたが、これは不正確でした。訂正します。実際の状況は次の 3 段階に分かれます。

  • 署名済みパッケージは公式に存在する: 公式の SourceForge にある 3.0.0.0 の配布ディレクトリには、署名版の zip(ファイル名に signed を含むもの・2017-07-13 付)が未署名版と並んで置かれています
  • 未署名版はそのままでは読み込まれない: 公式の ReadMe に、64 ビット環境向けのドライバはテスト署名であり既定では読み込まれないと明記されています。bcdedit でテスト署名モードを有効にする必要があり、ReadMe 自身が「コンピュータのセキュリティを損なう」と警告しています。業務 PC でこれをやるべきではありません
  • Secure Boot 有効な Windows 11 で署名版が入るかは、当サイトでは確認できていません: 公式フォーラムには Secure Boot 環境で読み込みに失敗した旨の報告があり、非公式のサードパーティが独自の対応版を配布してもいます。当サイトは com0com を導入していないため、どの条件で入るかを実測していません。導入は自己判断でお願いします

いずれの版を使うにせよ、ドライバのインストールには管理者権限が必要です。権限が下りない会社 PC では、2.4loop:// で進められるところまで進めておき、仮想ポートペアが必要な段階で情シスに相談するのが現実的な順序になります。導入できた場合は、付属の setupc を管理者として実行し、install PortName=COM10 PortName=COM11 のようにペアを作ります。

com0com には、テスト用として面白い設定項目があります。公式 ReadMe に記載されているもので、当サイトではいずれも未実測ですが、シミュレータを書くなら知っておく価値があります。

  • EmuBR: ボーレートを模擬する。既定は無効=仮想ポートはボーレート設定を無視します。「9600 と 19200 を食い違わせて文字化けを再現する」といった検証は、これを有効にしないと再現しません
  • EmuNoise: 指定した確率でデータをランダムに壊す。9 章の CRC 検査が本当に効いているかを、机上で確かめられます
  • EmuOverrun: 受信バッファの溢れを模擬する。読み出しが遅いときの取りこぼしを再現できます

6.3 Linux は socat

# Linux: 仮想シリアルペアを作る
socat -d -d pty,raw,echo=0 pty,raw,echo=0
# 出力例:
# N PTY is /dev/pts/3
# N PTY is /dev/pts/4

表示された 2 つの疑似端末の片方をシミュレータが、もう片方をクライアントが開けば、com0com と同じことができます。ドライバのインストールが要らないぶん、Linux 環境ではこちらのほうが手軽です。

7. RS-232C と RS-485 の違い

ここから物理層の話に入ります。Python 側のコードはどちらもほぼ同じで、違うのは配線と、送受信の切り替えが要るかどうかです。

RS-232C の 1 対 1 接続と RS-485 のマルチドロップ接続の比較図 左は PC と機器を 1 対 1 でつなぐ RS-232C。右は 1 本のバスに複数の機器を数珠つなぎにし、バスの両端にだけ 120 オームの終端抵抗を入れる RS-485 の配線。 RS-232C = 1 対 1 PC USB-RS232C 変換器 機器 (温調器など) COM3 / /dev/ttyUSB0 として見える RS-485(2 線式)= 1 対多 PC USB-RS485 変換器 120Ω 120Ω A / B の 2 本を共通のバスに 機器 1 addr=1 機器 2 addr=2 機器 N addr=N 終端抵抗(120Ω)はバスの両端だけに 1 本ずつ。中間の機器に入れると反射の原因になります。
図 2: RS-232C の 1 対 1 接続と、RS-485 のマルチドロップ配線(スマートフォンでは図を横にスクロールできます)
項目RS-232CRS-485(2 線式)
接続形態1 対 1(途中で分岐できない)1 対多のマルチドロップ(規格上の上限 32 台)
通信方向全二重(送信線と受信線が別)半二重(同じ線を共有。同時には送受信できない)。
4 線式にすれば全二重にできますが、産業機器では 2 線式が一般的です
信号の伝え方シングルエンド(GND 基準の電圧)差動信号(A / B の電位差)。外来ノイズに強い
信号レベル一般に ±5〜15VA / B の電位差で表す差動電圧。メーカー資料では、規格上の最小駆動電圧を 1.5V としている
距離の目安15 m 程度まで低速時で 1.2 km 程度まで(速度を上げるほど短くなる)
コネクタ / 配線D-sub 9 ピンが定番端子台に A / B(+ GND)。両端にだけ終端抵抗(一般に 120Ω)
Python 側のコードどちらも同じserial.Serial で開いて read / write)。違うのは配線と、送受信の切り替えが要るかどうか(10 章

距離・台数・電圧の数値は、メーカーの技術資料で広く共有されている一般的な目安です。RS-485 の「最大 32 台」は規格上の値、「1.2 km」は低速時の目安で、ボーレートを上げるほど到達距離は短くなります。終端抵抗の 120Ω も業界で広く使われる値ですが、規格本文は有償のため当サイトでは一次規格の記述を直接確認していません。実際の設計では、使用するケーブルの特性インピーダンスと機器メーカーの推奨に従ってください。

速度と距離の関係は、メーカーのアプリケーションノート(MaxLinear AN-292)が載せている「ケーブル長 対 データレート」の曲線が目安になります。約 90 kbps 以下では 1.2 km(4,000 ft)で頭打ち、90 kbps から 10 Mbps のあいだは速度を上げるほど距離が縮み、10 Mbps を超えると約 15 m(50 ft)まで落ちるという形です。24AWG の非シールドツイストペア・100Ω 終端という条件での経験的なデータです(この 100Ω は曲線を測るときの負荷条件で、実務で使う終端抵抗の目安 120Ω とは別の数字です)。この 2 つの端点から逆算すると、反比例する区間では「ボーレート[bps] × 距離[m]」がおおむね 108 前後に収まります。たとえば 115200 bps なら数百 m 級が計算上は視野に入りますが、これはあくまで条件のよいケーブルでの目安で、実際の到達距離はケーブル品質・ノイズ環境・機器の駆動能力で変わります。

PC から見れば、RS-232C も RS-485 も「USB-シリアル変換ケーブル経由の COM ポート」です。serial.Serial でポートを開いて read / write する、という書き方は変わりません。RS-485 で追加になるのは、誰が今バスを使っているかを管理する責任だけです(10 章)。

8. パケット境界の扱い

シリアル通信はストリームベースです。データは小包ではなく、水道の水のように区切りなく流れてきます。送信側が 1 メッセージずつ送っても、受信側で「ちょうど 1 メッセージが 1 回の read で来る」保証はありません。2 メッセージがまとまって届くことも、1 メッセージが途中で切れて届くこともあります。フレーム境界はプロトコル側で明示する必要があります(この性質と対策は TCP/IP 通信の記事の「recv はストリーム」の章と同じ話です。ただしシリアルで使える 8.3 のタイマ方式は、TCP では使えません)。以降の断片コードの ser2.3 で開いたポートを使う前提です。

8.1 終端文字方式

テキスト系プロトコルでは \r\n\n 終端が一般的です。5.3read_until をそのまま使います。

frame = ser.read_until(expected=b"\r\n")
if not frame.endswith(b"\r\n"):
    raise TimeoutError(f"終端が来ていない: {frame!r}")
text = frame.decode("ascii", errors="replace").rstrip()

8.2 長さプレフィックス方式

バイナリプロトコルでは「最初の N バイトに長さが入っている」方式が多用されます。4.2 で書いたとおり読み取りのタイムアウトは例外にならないので、戻り値の長さを見て自分で例外を作るのがポイントです。

def recv_exact(ser: serial.Serial, n: int) -> bytes:
    """正確に n バイトを読む。タイムアウト時は TimeoutError を送出する。"""
    buf = bytearray()
    while len(buf) < n:
        chunk = ser.read(n - len(buf))
        if not chunk:
            raise TimeoutError(f"recv timeout: got {len(buf)} of {n}")
        buf.extend(chunk)
    return bytes(buf)


def recv_frame(ser: serial.Serial) -> bytes:
    header = recv_exact(ser, 2)
    length = int.from_bytes(header, "big")
    return recv_exact(ser, length)

8.3 タイマ方式(無通信時間で区切る)

Modbus RTU など一部のプロトコルでは、「一定時間以上の無通信」をフレーム境界とみなします。pyserial 側では inter_byte_timeout(バイト間タイムアウト)を設定するか、上位のプロトコルライブラリ(pymodbus など)に任せるのが現実的です。

境界とされる時間は「3.5 文字時間」で、1 文字あたり 11 ビットとして計算します。Modbus over Serial Line 仕様 V1.02 に基づく解説では、9600 bps で約 4 ミリ秒、19200 bps を超える速度では 1.75 ミリ秒固定とされています。ただし当サイトは一次仕様書(有償ではないものの、調査時点でオンラインから取得できませんでした)の本文を直接確認できていません。この値をタイミング設計に使う場合は、必ず一次仕様書に当たってください。

9. CRC(誤り検出)

シリアル通信は外乱に弱いため、ほとんどのバイナリプロトコルで CRC 16 が付加されます。Modbus RTU の CRC-16/Modbus を例に、Python での実装を示します。

def crc16_modbus(data: bytes) -> bytes:
    crc = 0xFFFF
    for b in data:
        crc ^= b
        for _ in range(8):
            if crc & 1:
                crc = (crc >> 1) ^ 0xA001
            else:
                crc >>= 1
    return crc.to_bytes(2, "little")  # Modbus は LSB ファースト


# 使用例: スレーブ番号 1, FC03, アドレス 0, 1 ワード
# (ser は 2.3 で開いたポートを使う前提の断片コードです)
frame = b"\x01\x03\x00\x00\x00\x01"
frame_with_crc = frame + crc16_modbus(frame)
ser.write(frame_with_crc)

Modbus RTU そのものを書くなら pymodbus を使うほうが早く済みます。CRC を自前で書く必要があるのは、機器メーカー独自のバイナリプロトコルを実装するときです。

「CRC の検査が本当に効いているか」を確かめたいときは、6.2 で触れた com0com の EmuNoise(指定確率でデータを壊す設定)を使う手があります。正常なデータしか流れない環境では、CRC 検査の分岐は一度も通らないまま本番を迎えることになるためです。当サイトでは未実測ですが、テストの手立てとして覚えておく価値があります。

10. RS-485 半二重の制御

10.1 コードより先に配線を疑う 3 点

ソフトの話に入る前に——RS-485 の現場トラブルの大半は配線です。通信できないとき、コードを直す前にこの 3 点を確認してください(配線の形は図 2)。ただし、稼働中のバスで結線を触ると、同じバス上の他の機器まで通信断になります。以下の確認・入れ替えは通電状態で行わず、計画停止と保全担当の承認のうえで実施してください(14 章の注意)。

  • A / B 線の極性逆接続: メーカーによって A / B(D+ / D−)の呼称が逆のことがあり、逆につなぐと一切通信できません。入れ替えて試すのが最速です
  • 終端抵抗(一般に 120Ω)の入れ忘れ / 二重挿入: バスの両端にだけ入れます。中間の機器に入れると反射や信号品質の劣化を招きます。長距離・高速ほど効きます
  • スレーブアドレスの重複: マルチドロップで同一アドレスの機器が 2 台いると、応答が衝突して化けます。工場出荷時の既定値(1 が多い)のまま複数台つなぐのが定番の事故です

スター配線(マスタから複数機器へ枝分かれ)も反射の原因になります。物理的に 1 本のバスをデイジーチェーンで通し、両端にだけ終端抵抗——これが基本形です。

10.2 送受信の切り替えと、flush() が保証しない範囲

RS-485 の 2 線式は半二重で、送信と受信が同じ線を共有します。送信中は受信できず、受信中は送信できません。多くの USB-RS485 変換器は内部で自動的に方向を切り替えてくれますが、安価な製品ではユーザー側で RTS を切り替える必要があります。

結論を先に書きます。迷ったら方向制御をハードウェアで自動処理する変換器(Auto Direction Control 対応品)を選んでください。方向制御のコードを書かずに済むぶん、この節の面倒ごとを回避できます(価格差は製品によって異なり、当サイトでは実機での比較をしていません)。以下は、安価な変換器しか使えない場合の回避策です。

# 送受信の前後で RTS を切り替える例
ser.rts = True   # 送信モード
ser.write(frame)
ser.flush()      # 送信完了を待つ
ser.rts = False  # 受信モード
response = ser.read(64)

このコードには 2 つ注意があります。第一に、RTS の極性(True が送信なのか受信なのか)は変換器のハードによって逆のことがあるため、必ず変換器の仕様書で確認してください。第二に、flush() の保証範囲です。

半二重で RTS を手動切り替えするときのタイムチャート flush が戻った時点ではまだ最後のビットが線上に出ておらず、その直後に RTS を落とすとフレーム末尾が欠ける区間があることを示す。 半二重で RTS を手動切り替えするときのタイミング 1 2 3 4 アプリの処理 ser.write(frame) → ser.flush() RTS 信号 送信側に倒れている 4 まで待ってから落とすのが正 OS 送信バッファ 掃き出し中 ← flush() が待つのはここまで 線に出る信号 送出中 まだ送出中 時間 → この区間で RTS を落とすと末尾が欠ける
図 3: flush() が保証する範囲と、フレーム末尾が欠ける区間(RTS の極性は変換器によって逆のことがあります。当サイトでは RS-485 実機での再現実測を行っていません)(スマートフォンでは図を横にスクロールできます)
  1. ser.rts = True(送信側へ切り替え)
  2. ser.write(frame) を呼ぶ
  3. ser.flush() が戻る
  4. 最後のビットが線から出きる

flush() は公式ドキュメントでは「すべてのデータが書き込まれるまで待つ」と説明されている API です。ここで待つのは OS の送信バッファが掃き出されるところまでで、UART から最後のビットが線上に出きるところまで待つとは書かれていません。図 3 の 3 と 4 のあいだにこの隙間があり、3 の直後に ser.rts = False を実行すると、まだ送出中のフレーム末尾が削れます。確実にしたい場合は flush() の後に「送信バイト数 × 1 文字あたりの時間」ぶんのウェイトを足します(9600 bps・1 文字 11 ビットなら 1 文字あたり約 1.15 ミリ秒)。

この面倒さこそ、先に Auto Direction Control 対応の変換器をすすめた理由です。それなら方向制御のコードは一切不要で、通常の serial.Serial だけで書けます。なお、末尾欠けの発生そのものは筆者の経験と flush() の仕様に基づく説明で、当サイトでは RS-485 実機での再現実測を行っていません

10.3 serial.rs485.RS485 を使う場合の制約

pyserial には RTS の切り替えを肩代わりするクラス serial.rs485.RS485 が用意されています。設定は RS485Settings で渡し、当サイトで確認したシグネチャは次のとおりでした。

serial.rs485.RS485Settings(
    rts_level_for_tx=True,    # 送信中の RTS レベル
    rts_level_for_rx=False,   # 受信中の RTS レベル
    loopback=False,
    delay_before_tx=None,     # 送信開始前の待ち(秒)
    delay_before_rx=None,     # 受信へ戻す前の待ち(秒)
)

ソースを見ると、このクラスの write() は「RTS を送信レベルにする → delay_before_tx があれば待つ → 実際に書く → flush()delay_before_rx があれば待つ → RTS を受信レベルに戻す」という手順で動きます。10.2 で手書きしたコードと同じことを、ライブラリ側でやってくれる形です。

ただし、採用する前に知っておくべき制約が 2 つあります。

  • 公式が信頼性について警告している: 公式 API リファレンスには次の記述があります。

    一部のシリアルポートでは、この方法は信頼性を欠くことがある(制御信号がデータに対して同期しない、あるいは遅延する)。遅延を使う方法も、OS がごく細かい待ちに対応していない(数十ミリ秒のオーダーより細かくできない)ため、時間がばらついたり想定より長くなったりして信頼できないことがある(拙訳)

    pyserial 公式 API リファレンス — rs485 の注記
    これは 10.2 の手書きコードにもそのまま当てはまる話です
  • プラットフォームの制約がある: Serial.rs485_mode の項には、対応プラットフォームとして「POSIX(Linux・限られたハードウェアのみ)」「Windows(送信時の RTS のみ可能)」と明記されています。OS とハードウェアが対応している場合に限り、送信中だけ RTS が有効になる、という条件付きの機能です

Linux 側には、RS-485 モードをカーネルに通知するインターフェース(ioctl(TIOCSRS485, ...))があり、ハードウェアが対応していればドライバに任せられます。公式も、エミュレーションよりネイティブ実装のほうが性能面で優れると述べています。本番設備で長期間動かす前提なら、ソフトで頑張るより方向制御を持つ変換器を選ぶほうが、実装と切り分けにかける時間を減らせます(費用まで含めた比較は、機器構成と工数の見積もり次第です)。

11. OS 別の注意 — Windows の COM ポートと Linux の /dev

11.1 ポート番号がずれる問題への対策(Windows)

USB-シリアル変換ケーブルを抜き差ししたり、別の USB ポートに挿し替えたりすると、Windows が COM ポート番号を変えてしまうことがあります。本番運用では「デバイスマネージャー → ポートのプロパティ → 詳細設定」で COM 番号を固定するか、ハードウェアのシリアル番号で機器を識別するコードにします。

import serial
import serial.tools.list_ports


def find_port_by_serial_number(target: str) -> str:
    for p in serial.tools.list_ports.comports():
        if p.serial_number == target:
            return p.device        # "COM3" などが返る
    raise RuntimeError(f"変換器が見つからない (serial_number={target})")


ser = serial.Serial(find_port_by_serial_number("AB0123CD"), 9600, timeout=1.0)

この形にしておくと、番号が変わっても、担当者が別の USB ポートに挿し替えても動き続けます。複数台の変換器を 1 台の PC に挿す構成では、どの機器がどの変換器につながっているかを取り違えない意味でも効きます。

なぜ Windows が番号を変えるのか(ComDB・PortName・インスタンス ID の仕組み)と、VID/PID → シリアル番号 → 差込口の位置の優先順位で機器を特定する find_port() の実装は、COM ポート番号が毎回変わる — 原因の仕組みと pyserial で機器を自動特定する方法で詳しく扱っています。

11.2 USB-シリアル変換器のドライバ

  • FTDI 系(FT232R など): Windows / Linux / macOS 向けの公式ドライバが揃っており(FTDI 公式ドライバ配布ページ)、業務用の第一候補として検討しやすい選択肢です。当サイトではドライバ挙動の実機比較は行っていません
  • Prolific 系(PL2303): 安価ですが要注意です。旧チップ(PL2303HXA / TA など)は、Windows 11 に配布される現行の公式ドライバでは「PL2303HXA PHASED OUT SINCE 2012. PLEASE CONTACT YOUR SUPPLIER.」と表示され、そのままでは動作しないという報告が多数あります。模倣品の流通が背景にあるとする解説が多いものの、当サイトはメーカーの公式見解も実機の挙動も直接確認できていません(旧版ドライバを使う回避策も知られていますが、これも当サイトでは未確認です)。新規購入なら現行チップ(PL2303GC 等)の搭載を確認し、判断はメーカーの案内(Prolific 公式サイト)で確かめてください
  • CH340 系: 安価な製品でよく見かけます。Windows 11 では公式ドライバでの動作確認を推奨します

いずれの場合も、ドライバのインストールには管理者権限が必要です。現場 PC に挿す前に、情報システム部門の手順を確認しておくと当日の作業が止まりません。

11.3 Linux 側の注意(/dev/ttyUSB0・権限・名前の固定)

  • デバイス名: USB-シリアル変換器は /dev/ttyUSB0、マイコンボード類は /dev/ttyACM0 として見えることが多いです(2.2)。Windows の COM 番号と同じく、挿す順番で番号が入れ替わります
  • 権限: 開こうとして PermissionError になる場合、ユーザーが dialout グループに入っていないのが定番の原因です(12 章
  • 名前を固定する: Windows の「COM 番号を固定する」に相当するのが udev ルールです。2.2list_ports で拾える serial_number / vid / pid を条件にして、機器ごとに固定名のシンボリックリンクを割り当てます。11.1 の「シリアル番号で探す」コードは Linux でもそのまま動くので、udev を触れない環境ではコード側で吸収するのが現実的です
  • 排他アクセス: serial.Serial(..., exclusive=True)POSIX 限定の引数で、すでに排他で開かれているポートを開けなくします(3.1)。常駐プロセスの二重起動対策に使えます

当サイトの検証環境は Windows 11 Pro のため、Linux 実機での動作確認は行っていません。ここに書いたのは公式ドキュメントの記述と一般的な運用手順の整理です。

12. よくあるエラーと対処

動かないときの切り分け表です。メッセージの文面は OS の言語設定や環境によって変わります。下表に挙げているのは原因を分類するための目安で、当サイトで文面まで実測したものではありません。

症状主な原因対処
ModuleNotFoundError: No module named 'serial'pyserial が未インストール/別の venv に入っている/import pyserial と書いているpython -m pip install pyserial を、実行するのと同じ venv で(1.1
import は通るのに serial.Serial が見つからないpip install serial で別ライブラリ(0.0.97)を入れたpip uninstall serialpip install pyserial1.1
ポートを開こうとして PermissionError になる他のプロセスがポートを掴んでいる(Tera Term・miniterm・機器メーカーの設定ツール・前回落ちたプロセス)。Linux では dialout グループに入っていない掴んでいるアプリを閉じる。Linux はユーザーを dialout グループに追加
ポートが存在しないと言われる(SerialExceptionCOM 番号が変わった/変換器が抜けている/ドライバ未導入python -m serial.tools.list_ports -v で実在を確認(2.2)。番号ずれは 11.1
エラーは出ないが b'' しか返らないタイムアウト内に応答が来ていない/TX と RX が逆(クロスケーブルが必要)/機器がコマンド待ちで、こちらの要求形式が違うまず 6.1 の miniterm で手打ちして、機器が応答するか切り分ける
readline() が返ってこないtimeout 未指定(既定は無限ブロック)+機器が改行を返さないtimeout を指定し、末尾の \n で判定する(5.2
文字化けする原因は 2 種類ある。(a) 通信条件の不一致(ボーレート・パリティ)(b) decode するコードページの違い生バイトを 16 進で見て切り分ける。値がランダムに散っていれば (a)、日本語部分だけ化けるなら (b)(CP932 / UTF-8 の記事
送信できているのに RS-485 の機器が応答しないA / B 極性の逆接続/終端抵抗/スレーブアドレス重複/送受信の切り替えが間に合っていない10.1 の 3 点を順に確認。切り替えの問題は 10.2
送信が時々途中で切れる(RS-485)flush() の直後に RTS を落としているウェイトを足すか、方向制御が自動の変換器に替える(10.2 の図 3)

順番も大事です。電源 → 結線 → 通信条件 → プロトコル → コードの順に疑ってください。コードから疑い始めると、A / B が逆に挿さっているだけの案件に半日を使うことになります。

13. デバッグの定石

13.1 まずは生データを 16 進でダンプ

raw = ser.read(64)
print(" ".join(f"{b:02X}" for b in raw))

「何バイト来ているか」「先頭は何か」「終端は何か」を、まず目で見ます。プロトコル仕様書と突き合わせるときの基本動作です。デコードするのはその後です。

13.2 miniterm の --filter debug で流れを見る

python -m serial.tools.miniterm --filter debug COM3 9600

公式ドキュメントによれば、miniterm には colorize / debug / direct / nocontrol / printable といった表示フィルタがあります。debug は制御文字も含めてすべてを可視化するため、「見えない終端文字が何なのか」を調べるのに向きます。ターミナルソフトを別途申請しなくても、この確認まで到達できるのが pyserial 同梱ツールの利点です。

13.3 ロジックアナライザで波形を見る

ソフト側で切り分けきれないときは、ロジックアナライザ(Saleae Logic など。安価な互換機なら数千円)でビット波形を見るのが確実です。ただし電気的な注意があります——ロジックアナライザの入力は 3.3〜5V のロジックレベルを前提にしているため、RS-232C の ±12V 系信号を直結すると入力段を壊す恐れがあります。観測するのは USB-シリアル変換器の後段(TTL レベル側)にするか、RS-232C レベルにはレベル変換や専用のアナライザを使ってください。

13.4 PuTTY / Tera Term を使う場面

機器が ASCII 系のコマンドを使う場合、手打ちで通信が成立することを先に確認すると、Python 実装に進む前に問題が切り分けられます。6.1 の miniterm で足りることも多いですが、ログの保存や複数セッションの管理といった機能が必要なら、PuTTY や Tera Term のほうが快適です。

14. おわりに — 本記事の検証範囲と実機接続時の注意

pyserial の使い方そのものは、pip install pyserial から read / write までの数行で完結します。実務で効いてくるのはその外側で、timeout をどう置くか、読み取りの成否を何で判定するか、フレーム境界を誰が切るか、そして RS-485 なら誰が今バスを使っているか——この 4 つを決めていれば、シリアル通信で長時間詰まることはほぼなくなります。

常時稼働させるアプリでは、通信が切れたときの自動再接続や、二重起動の防止(POSIX なら 11.3exclusive=True)といった長時間運用の設計が必要になります。この考え方は TCP 通信でも共通なので、実装例は TCP/IP 記事の 24 時間運用に耐える DeviceClient を参照してください。アプリを PC の起動と同時に常駐させる手順は Python アプリを Windows サービスとして常駐させる方法、切断・再接続の履歴をあとから追えるログの残し方は 24 時間動くアプリのためのロギング設計 にまとめています。

本記事の実測値の範囲を明示しておきます。4 章timeout の挙動、5.2readline() の末尾欠け(1.52 秒)、5.4in_waitingSerial()RS485Settings の引数、pip index versions serial の結果は、Windows 11 Pro / Python 3.12.10 / pyserial 3.5 の筆者の自宅検証環境で、pyserial 内蔵の loop:// を使って実際に確認したものです。一方で、実機の USB-シリアル変換器・com0com・RS-485 の配線と波形については、本記事では実測していません。それらの記述は公式ドキュメントとメーカー技術資料、および筆者の現場経験に基づく整理です。ここを混ぜないことが、この種の記事でいちばん大事だと考えています。

⚠️ 実機接続時の注意: 本記事のサンプルは検証用(ダミー機器・ループバック・シミュレータ想定)です。実際の産業機器・PLC・温調器・流量計等に接続する場合は、必ず読み出し専用モードで先に疎通確認し、書き込み(設定変更・制御コマンド)は安全インターロック・計画停止のうえで実施してください。RS-485 の配線変更(A / B 極性・終端抵抗・アドレス変更)や機器の電源投入順序は、機器のマニュアル・保全側の手順書に従うのが前提です。24 時間稼働機器の停止・書き込みは、自社の安全基準と保全側の承認を経ることが前提です。本記事のコード・配線例による損害・事故(機器故障・ライン停止・人身事故を含む)について筆者・GenbaPy は責任を負いません。

関連記事

参考文献・一次情報

いずれも 2026 年 8 月 31 日に参照。本文の実測値は Windows 11 Pro / Python 3.12.10 / pyserial 3.5(仮想ポート loop://)の筆者の自宅検証環境によるものです。