C#でSerialPortを使いたいと思っても、設定項目が多くて「何から始めればいいのかわからない」と感じやすいですよね。
とくに、COMポートの選び方、BaudRateの設定、送受信の書き方、ReadLineで止まる原因などは、初心者の方がつまずきやすいポイントです。
この記事では、SerialPortの基本的な使い方から、接続・送信・受信・COMポート取得・ハマりやすい対処法まで、やさしく整理して解説します。
サンプルコードの意味がわかるように順番に説明しているので、これからC#でシリアル通信を始めたい方にも取り組みやすい内容です。
「動くコードを書けるようになりたい」「外部機器と通信できるようになりたい」という方は、ぜひこのまま読み進めてみてください。
| この記事が向いている方 | 内容 |
|---|---|
| 初心者の方 | SerialPortの基本と流れをつかみたい |
| 実装で困っている方 | ReadLineやCOMポート周りのつまずきを整理したい |
| 業務で使う方 | 外部機器との通信処理を安全に書きたい |
この記事でわかること
- C#でSerialPortを使う基本的な流れ
- 接続・切断・送受信の書き方
- COMポート一覧の取得方法
- よくあるつまずきと対処の考え方
SerialPortとは?C#でできることを最初に整理しよう
C#で外部機器とデータをやり取りしたいときに、まず候補に挙がるのがSerialPortです。
USB変換ケーブル経由のCOMポートを含めて、パソコンと計測器、マイコン、産業機器などを接続するときに使われることが多く、今でも現場で出番があります。
特に初心者の方は、「何から設定すればいいのか」「送信と受信はどう書けばいいのか」で迷いやすいです。
そこで最初に、SerialPortでできることと、実際に使う前に知っておきたいポイントをやさしく整理していきます。
SerialPortでできること
SerialPortは、C#からシリアル通信を扱うためのクラスです。
これを使うと、COMポートを開く、文字列やバイト列を送る、受信したデータを読み取る、といった一連の処理をまとめて実装できます。
つまり、接続・送信・受信・切断という基本の流れを、ひとつの仕組みで扱えるのが魅力です。
はじめは難しく見えますが、実際によく使う処理はそれほど多くありません。
最初の段階では、「ポートを開く」「送る」「受け取る」「閉じる」の4つを押さえるだけでも十分です。
| できること | 内容 |
|---|---|
| 接続 | COM3 などのポートを指定して通信を開始する |
| 送信 | コマンドや文字列、バイトデータを機器へ送る |
| 受信 | 機器から返ってきた文字列やデータを読み取る |
| 切断 | ポートを閉じて安全に終了する |
このように見ると、SerialPortは「外部機器と会話するための窓口」と考えるとわかりやすいです。
まずは全体像をつかんでからコードに入ると、途中で混乱しにくくなります。
シリアル通信で合わせるべき設定項目
シリアル通信では、送る側と受け取る側で設定が合っていないと、うまく通信できません。
そのため、コードを書く前に通信条件の確認がとても大切です。
ここが合っていないと、接続できているように見えても文字化けしたり、受信できなかったりします。
| 設定項目 | 意味 | 例 |
|---|---|---|
| PortName | 接続先のCOM番号 | COM3、COM5 |
| BaudRate | 通信速度 | 9600、115200 |
| DataBits | 1回で扱うデータ長 | 7、8 |
| Parity | 誤り検出の有無 | None、Even、Odd |
| StopBits | データ終端のビット数 | One、Two |
| Handshake | フロー制御 | None、XOnXOff |
特に初心者の方は、まずPortNameとBaudRateを確認しましょう。
この2つがズレているだけでも、通信が成立しないことは珍しくありません。
さらに、機器によっては改行コードや文字コードの指定が必要になることもあります。
そのため、説明書や通信仕様書がある場合は、最初に目を通しておくのがおすすめです。
まず押さえたい利用シーン
SerialPortは、学習用のサンプルだけでなく、実務でも意外と使われています。
たとえば、バーコードリーダー、センサー、PLC、測定器、マイコンとの連携などが代表例です。
最近はUSBやネットワーク通信が増えていますが、内部的にはCOMポートとして認識される機器も多く、C#から扱いやすい場面が残っています。
つまり、SerialPortを覚えておくと、パソコンアプリから現場の機器を制御する入口として役立ちやすいです。
「業務で突然必要になった」というケースも多いため、基本の使い方だけでも知っておくと安心です。
次の章からは、実際にC#でどう書けばよいのかを、順番に見ていきましょう。
C#でSerialPortを使う基本手順
ここからは、C#でSerialPortを使う基本の流れを見ていきます。
難しく感じるかもしれませんが、手順としては準備する、開く、送る、受け取る、閉じるの順番です。
この流れをひとつずつ分けて考えると、コードも読みやすくなります。
まずは再利用しやすいように、通信処理をクラスにまとめる形で進めていきましょう。
SerialPortの準備と初期設定
最初に行うのは、SerialPortインスタンスの準備です。
このときに、ポート番号や通信速度などの設定をまとめて書いておくと、あとから見直しやすくなります。
機器に合わせて値を変更しやすいように、初期化部分はわかりやすく整理しておくのがおすすめです。
using System;
using System.IO.Ports;
using System.Text;
public class SerialPortService
{
private SerialPort? _port;
public bool Open(string portName)
{
try
{
_port = new SerialPort
{
PortName = portName,
BaudRate = 9600,
DataBits = 8,
Parity = Parity.None,
StopBits = StopBits.One,
Handshake = Handshake.None,
Encoding = Encoding.UTF8,
ReadTimeout = 3000,
WriteTimeout = 3000,
NewLine = "\r\n"
};
_port.Open();
return true;
}
catch
{
return false;
}
}
}
この段階で大切なのは、相手機器の仕様に合わせて設定値を変えることです。
サンプルコードの値をそのまま使うのではなく、必要に応じてBaudRateやParityを調整してください。
また、文字列通信をする場合は、EncodingやNewLineも確認しておくとトラブルを減らしやすいです。
ポートを開く・閉じる方法
設定が終わったら、次はポートを開いて通信できる状態にします。
通信終了後は、必ず閉じる処理も入れておきましょう。
ここを丁寧に書いておくと、アプリ終了時のエラーやポートの取り合いを防ぎやすくなります。
public void Close()
{
if (_port == null)
{
return;
}
if (_port.IsOpen)
{
_port.Close();
}
_port.Dispose();
_port = null;
}
「開けたら閉じる」をセットで考えるのがコツです。
とくに開発中は、前回の実行でポートが閉じられていないままになり、再接続に失敗することがあります。
そのため、例外が起きても終了処理へ進める設計にしておくと安心です。
安全に片付けるコードほど、あとで自分を助けてくれます。
文字列を送信して受信する方法
ポートが開けたら、次は送受信です。
まずは文字列ベースで考えると理解しやすく、学習の入口としても取り組みやすいです。
たとえば、機器へコマンドを送り、返ってきた応答を読み取る流れは次のように書けます。
public void Send(string message)
{
if (_port == null || !_port.IsOpen)
{
return;
}
_port.WriteLine(message);
}
public string ReceiveLine()
{
if (_port == null || !_port.IsOpen)
{
return string.Empty;
}
try
{
return _port.ReadLine();
}
catch (TimeoutException)
{
return string.Empty;
}
}
ここで覚えておきたいのは、WriteLineは末尾に改行を付けて送ることです。
また、ReadLineは改行が来るまで待つため、相手機器が同じ終端文字を送らないと止まったように見えることがあります。
そのため、送信側と受信側でNewLineの考え方を合わせることが重要です。
まずは文字列通信で流れをつかみ、その後に必要ならバイト配列の送受信へ広げると理解しやすいです。
C#のSerialPortでよくあるつまずきと対処法
SerialPortは基本の流れ自体はシンプルですが、実際に動かしてみると細かなところでつまずきやすいです。
特に初心者の方は、コードの書き方よりも環境や設定のズレで困ることが少なくありません。
ここでは、よくある悩みを先回りして整理しておきます。
COMポートが見つからないときの確認ポイント
「そもそもCOMポートが出てこない」という悩みはとても多いです。
この場合は、まずパソコンが機器を正しく認識しているかを確認しましょう。
アプリ側の問題に見えても、実際にはケーブルやドライバー、接続先の問題であることがあります。
using System.IO.Ports;
string[] ports = SerialPort.GetPortNames();
foreach (var port in ports)
{
Console.WriteLine(port);
}
COM番号の一覧取得には上のような書き方が使えます。
ただし、一覧が取れたとしても、その順番どおりに表示されるとは限りません。
画面に表示するときは、必要に応じて並び替えたり、再読み込みボタンを用意したりすると親切です。
| 確認項目 | 見るポイント |
|---|---|
| デバイスマネージャー | 対象機器がCOMポートとして認識されているか |
| USBケーブル | 充電専用ではなくデータ通信対応か |
| ドライバー | 変換チップ用のドライバーが入っているか |
| 他アプリの使用状況 | Tera Termなどが同じポートを開いていないか |
コードだけを追いかけるより、まず環境を疑うほうが早く解決するケースは多いです。
ReadLineで止まる原因と対策
送信はできたのに、受信で止まってしまうというケースもよくあります。
このときに多いのが、ReadLineが終端文字を待ち続けている状態です。
つまり、相手機器が改行を送っていない、またはNewLineの指定が一致していない可能性があります。
この問題を避けるには、最初に文字列通信なのか、バイナリ通信なのかをはっきりさせることが大切です。
文字列なら終端文字をそろえ、バイナリならRead系メソッドでバイト単位に処理するほうが向いています。
また、UIアプリでは受信待ちで画面を止めないために、別スレッドや非同期処理を意識すると扱いやすくなります。
「止まる」の多くは不具合というより、待っている条件が満たされていないだけという視点を持つと、原因を切り分けやすいです。
例外処理と安全に終了するコツ
シリアル通信では、相手の電源が落ちた、ケーブルが抜けた、ポートが使用中だった、という状況が起こりえます。
そのため、正常時だけでなく、失敗時の動きもあらかじめ考えておくことが大切です。
たとえば、接続時、送信時、受信時にはそれぞれ例外処理を入れておくと、アプリ全体が落ちにくくなります。
public bool SafeSend(string message)
{
if (_port == null || !_port.IsOpen)
{
return false;
}
try
{
_port.WriteLine(message);
return true;
}
catch
{
return false;
}
}
もちろん、実際の運用ではログ出力もあるとさらに安心です。
「なぜ失敗したのか」を残しておくと、再現しにくい不具合にも対応しやすくなります。
また、アプリ終了時にCloseとDisposeを忘れないことも重要です。
通信処理は、成功パターンより失敗パターンの設計が品質を左右しやすいので、最初から丁寧に考えておきましょう。
実務で使いやすくするための実装ポイント
基本の送受信ができるようになったら、次は「実際のアプリでどう使いやすくするか」を考えていきます。
この視点が入ると、サンプルコードから一歩進んだ実装にしやすくなります。
特にデスクトップアプリでは、受信タイミングや画面更新の扱いが使いやすさに直結します。
DataReceivedと同期処理の考え方
SerialPortには受信をきっかけに処理できるイベントの考え方があります。
この仕組みを使うと、ポーリングで何度も確認しなくても、データ到着時に反応しやすくなります。
ただし、イベントで受けたデータをそのままUIへ触りにいくと、画面側で扱いにくくなることがあります。
そのため、まずは「受信処理」と「画面表示」を分けて考えるのがおすすめです。
たとえば、受信したデータを一度バッファやプロパティに渡し、そこからUIへ反映する形にすると整理しやすくなります。
通信処理と画面処理を分離するだけで、コードの見通しはかなりよくなります。
WPFやWindowsフォームで扱うときの注意点
WPFやWindowsフォームでは、通信処理をそのままボタンクリックの中へ全部書くと、あとで管理しにくくなります。
そのため、SerialPortの処理はサービスクラスやヘルパークラスに切り出しておくと便利です。
画面側は、接続する、送信する、結果を表示する、といった役割だけに寄せると、修正にも強くなります。
| 分ける場所 | 役割 |
|---|---|
| 画面コード | ボタン操作、入力値取得、表示更新 |
| 通信クラス | 接続、切断、送受信、例外処理 |
| 設定管理 | COM番号、通信速度、改行コードの保持 |
こうして役割を分けておくと、将来的に設定画面を追加したいときも広げやすいです。
最初から完璧でなくても大丈夫ですが、ひとつのボタンイベントに全部詰め込まないことは意識しておきたいポイントです。
テスト環境を用意して動作確認する方法
SerialPortの学習で意外と困るのが、手元に通信相手がないことです。
そんなときは、ターミナルソフトや仮想COMポート環境を使って確認すると進めやすくなります。
いきなり実機だけで試すより、送った文字列が返るか、改行コードが合っているかを小さく確認できる環境があると安心です。
また、動作確認のときは「接続できたか」だけでなく、次のような流れで見ると原因を切り分けやすいです。
- COMポートが見えているか確認する
- Openできるか確認する
- 固定文字列を送れるか確認する
- 受信データをログに出して確認する
- 切断後に再接続できるか確認する
この順番なら、どこで止まっているかがわかりやすくなります。
実務では「たまたま動いた」よりも、「どこまで確認できたか」のほうが大切です。
だからこそ、地味でもひとつずつ確かめる進め方が、結果的にいちばん近道になります。
まとめ
この記事のポイントをまとめます。
- SerialPortはC#でシリアル通信を扱う基本クラスとして使いやすいです。
- 最初に覚える流れは、接続・送信・受信・切断の4つです。
- 通信設定は相手機器と一致させることが大前提です。
- PortNameやBaudRateが合っていないと通信できない原因になります。
- WriteLineとReadLineを使うと文字列通信を始めやすいです。
- 改行コードが合わないとReadLineで待ち続けることがあります。
- GetPortNamesでCOMポート一覧を取得できます。
- 例外処理とClose・Disposeは早い段階から意識するのが大切です。
- 画面処理と通信処理を分けると、保守しやすい構成になります。
- テスト環境を用意して小さく確認すると、原因を切り分けやすくなります。
C#でSerialPortを使ったシリアル通信は、一見すると専門的に見えますが、基本の流れを順番に押さえれば初心者の方でも十分取り組めます。
大切なのは、難しいことを一気にやろうとせず、まずは接続できること、送れること、受け取れることをひとつずつ確認することです。
設定を丁寧に合わせることと、止まったときに原因を切り分けることを意識すれば、実務でも使いやすい知識になっていきます。
まずは小さなサンプルから試して、C#で外部機器とつながる感覚をぜひつかんでみてください。

