この記事では
C#のアプリで、シリアル通信テストを行うための方法を書き残す
使用したツールの情報
- com0com 3.0.0.0
- TeraTerm 4.105
- .NET Core 3.1
- System.IO.Ports 5.0.1
com0com の設定
PC単体でCOMポートの接続テストをするために、com0comをインストールする
インストールからデバイスドライバの更新まで
「com0com」でGoogle検索(.jp)すると、この記事が1番目にヒットする
記事の説明通りに進めれば、問題なくインストールできる
インストール時のWindows10バージョン
Windows10 21H1 にインストールした
com0com をセットアップする
ツールの場所: "C:\Program Files (x86)\com0com\setupg.exe"
今回は {COM20, COM21}
と {COM22, COM23}
の2セットペアを生成した
デバイスマネージャーで確認する
ドライバの更新が正常ではない場合、黄色三角のエラーマークが表示される
解消するためには、Windows Update -> "オプション更新プログラム"
から所定のプログラムを更新する
(ドライバを更新する方法について、先述の記事に記載あり)
TeraTermの設定
COM21
とCOM23
をコンソールで監視する
シリアルポートの設定はデフォルトのままとした
C#アプリからシリアル通信コマンドを送信する
コードの詳細は記事の最下部の付録に掲載する
ウィンドウにはSend
ボタンのみが配置される
ボタンを押すと、COM20
->COM21
へ数字18桁、COM22->COM23
へ英字18桁がそれぞれ送信される
TeraTermでコマンドを受け取る
TeraTermをCOM21
, COM23
に接続する
アプリのSend
ボタンを押すたびに、受け取ったコマンドがそれぞれのコンソールに表示される
-
COM21
はCOM20
から受け取った数字を表示 -
COM23
はCOM22
から受け取った英字を表示
MySerialPortクラスでコマンドを受け取る
送信につかったMySerialPort
クラス同様に
受信側もMySerialPort
を使う
public MainWindow()
{
InitializeComponent();
_com20 = new MySerialPort("COM20");
_com21 = new MySerialPort("COM21"); // <-- TeraTermを使うときはコメントアウト
_com22 = new MySerialPort("COM22");
_com23 = new MySerialPort("COM23"); // <-- TeraTermを使うときはコメントアウト
}
TeraTermのときと違い、受け取ったByte配列は16進数で表示される
例) "1"は16進数31
、 "A"は16進数41
おわりに
参考にさせていただいた記事にもある通り
「com0com」はとても便利ですが、所詮は仮想ですので最終的な確認は本物のシリアルポートがあるPCでやりましょう。
付録
検証に使ったコードを掲載する
シリアルポート操作用モデル
コマンドを送信するSend
メソッドだけを公開している
データを受け取った際にイベントが発火し、Receive
メソッドにより受け取ったデータをbyte[]
に格納する
using System;
using System.Diagnostics;
using System.IO.Ports;
public class MySerialPort
{
private readonly SerialPort _sp;
public MySerialPort(string portName)
{
_sp = new SerialPort()
{
PortName = portName,
BaudRate = 9600,
DataBits = 8,
Parity = Parity.None,
StopBits = StopBits.One,
Handshake = Handshake.None
};
_sp.Open();
_sp.DataReceived += OnDataReceived;
}
private void OnDataReceived(object sender, SerialDataReceivedEventArgs e)
{
Debug.Write($"{_sp.PortName} Data Received");
byte[] receivedBytes= Receive();
int receivedBytesLength = Buffer.ByteLength(receivedBytes);
for(int i = 0; i < receivedBytesLength; i++)
{
Debug.Write($"{receivedBytes[i]:X},");
}
Debug.Write("\n");
}
public void Send(string message = "DefaultMessage")
{
_sp.Write(message);
Debug.WriteLine($"{_sp.PortName} send {message}");
}
private byte[] Receive()
{
byte[] buf = new byte[128];
int totalReadBytes = 0;
while(true)
{
int bytesToRead = _sp.BytesToRead;
if (bytesToRead == 0)
break;
int readBytes = _sp.Read(buf, totalReadBytes, bytesToRead);
totalReadBytes += readBytes;
Debug.WriteLine($"{_sp.PortName} toRead: {bytesToRead}, readBytes:{readBytes}, total: {totalReadBytes}");
}
byte[] receivedBytes = new byte[totalReadBytes];
Array.Copy(buf, receivedBytes, totalReadBytes);
return receivedBytes;
}
}
画面のコードビハインド
MySerialPort
クラスのインスタンスを4つ生成する
com0comで設定した4つのポート名をコンストラクタの引数に与えている
ボタンにはイベントハンドラSend(object sender, RoutedEventArgs e)
が紐づいており
COM20からは数字18桁、COM22からは英字18桁が送信される
com0comで20と21はペアになっているので、20から送信した内容は21のポートで受信される
(COM22->COM23も同様)
using System.Threading.Tasks;
using System.Windows;
public partial class MainWindow : Window
{
private readonly MySerialPort _com20;
private readonly MySerialPort _com21;
private readonly MySerialPort _com22;
private readonly MySerialPort _com23;
public MainWindow()
{
InitializeComponent();
_com20 = new MySerialPort("COM20");
//_com21 = new MySerialPort("COM21"); // <-- TeraTermを使うときはコメントアウト
_com22 = new MySerialPort("COM22");
//_com23 = new MySerialPort("COM23"); // <-- TeraTermを使うときはコメントアウト
}
private void Send(object sender, RoutedEventArgs e)
{
_ = Task.Run(() => _com20.Send("123456789123456789"));
_ = Task.Run(() => _com22.Send("ABCDEFGHIABCDEFGHI"));
}
}