LoginSignup
16
16

More than 1 year has passed since last update.

【C#】com0comを使ったシリアル通信テスト

Last updated at Posted at 2021-08-22

この記事では

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 にインストールした

img3.png

com0com をセットアップする

ツールの場所: "C:\Program Files (x86)\com0com\setupg.exe"

今回は{COM20, COM21}{COM22, COM23} の2セットペアを生成した

image.png

デバイスマネージャーで確認する

ドライバの更新が正常ではない場合、黄色三角のエラーマークが表示される

解消するためには、Windows Update -> "オプション更新プログラム" から所定のプログラムを更新する
(ドライバを更新する方法について、先述の記事に記載あり)

image.png

TeraTermの設定

COM21COM23をコンソールで監視する

image.png

シリアルポートの設定はデフォルトのままとした

image.png

C#アプリからシリアル通信コマンドを送信する

コードの詳細は記事の最下部の付録に掲載する

ウィンドウにはSendボタンのみが配置される
ボタンを押すと、COM20->COM21数字18桁COM22->COM23英字18桁がそれぞれ送信される

TeraTermでコマンドを受け取る

TeraTermをCOM21, COM23に接続する
アプリのSendボタンを押すたびに、受け取ったコマンドがそれぞれのコンソールに表示される

  • COM21COM20から受け取った数字を表示
  • COM23COM22から受け取った英字を表示

Animation2.gif

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

Animation3.gif

おわりに

参考にさせていただいた記事にもある通り

「com0com」はとても便利ですが、所詮は仮想ですので最終的な確認は本物のシリアルポートがあるPCでやりましょう。

付録

検証に使ったコードを掲載する

シリアルポート操作用モデル

コマンドを送信するSendメソッドだけを公開している

データを受け取った際にイベントが発火し、Receiveメソッドにより受け取ったデータをbyte[]に格納する

MySerialPort.cs
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も同様)

MainWindow.xaml.cs
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"));
    }
}
16
16
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
16
16