> 作業効率UP!! オススメのモバイルモニターを紹介

【C#】シリアル通信(SerialPort)の使用方法を紹介!

当ページのリンクには広告が含まれています。
  • URLをコピーしました!

シリアル通信て何?

シリアル通信は、パソコン同士や異なる周辺機器間で送受信を行う用途として古くから使用されていた通信プロトコルで、現在でも使用される場面が多い通信手段の1つです。

この記事では、シリアル通信について簡単におさらいをしつつ、C# の SerialPort クラスを使って通信する方法を紹介します。

手軽にデータの送受信を可能にする通信手段なので、この記事を参考にしてパソコンや周辺設備を制御できるようになりましょう。

動作環境

  • OS : Windows10
  • IDE: Visual Studio 2019
  • フレームワーク : .NET Core 3.1
  • UI フレームワーク : WPF アプリ

オススメの参考書

C#の使い方が丁寧に解説しており、「基礎からしっかりと学びたい」という初心者の方にオススメの一冊です。サンプルコードも記載してあり、各章の最後に復習問題があるので理解度を確認しながら読み進めることができます。新しい C# のバージョンにも対応している書籍です。

シリアル通信とは?

シリアル通信は信号の通り道が1つしかなく、信号を順番通りに1つずつ連続的に送信する通信方式です。

通信相手から送信された信号は必ず取り込む必要があるので、送信をする信号線と受信をする信号線の2つが必要になります。

シリアル通信では通信機器同士がデータを転送する速度を合わせて、0(Low)または1(High)の2進数を連続し受け渡します。イメージはこんな感じです。

シリアル通信の通信規格は RS-232C や RS-422、RS-485 があり、中でも RS-232C は通信規格の中でも用途を問わず多く普及し、パソコンに搭載されている機種があったり、産業機器で使用される設備は標準で搭載されているものが多く存在します。

現在では周辺機器との接続・通信手段に USB やイーサネット等が用いられるようになったこともあり、ひと昔前の通信インターフェースという位置づけになっています。

もう少し詳しく RS-232C についてみてましょう。

RS-232Cについて

RS-232Cには9ピンまたは25ピンの D-Sub コネクタで接続します。

9ピンの RS232C は「EIA574」で、25ピンのRS232は「EIA-232」という規格です。25ピンのコネクタは不要な信号も多いため一般的には9ピンのコネクタが良く使われています。

シリアル通信では、1本の通信線を用いてデータを1bitずつ送るので、正確な時間間隔で送ることが重要となります。

通信を成功させるためには、データの始まりと終わりを見分け、送信されたデータを正確に取り込む必要があります。このことを同期と言い、同期式と非同期式の2種類があります。

  • 同期式:データ送信用の回線とは別にクロックと呼ぶ定周期の信号が同時に送られ、クロック信号のタイミングでデータ信号を読み取る方式を同期式といいます。
  • 非同期式:クロック信号がなく、最初に届いた信号(スタートビット)の長さを測定し、その間隔で信号が送られるとして受信する方式を非同期式といいます。

一般的に非同期式が採用されていることが多いです。

シリアル通信の設定

非同期式で通信をするには必ず守らなければならない条件が1つあります。

それは同期を取る為に以下の通信設定を通信相手と同じにしなければなりません。

この通信設定が同じでないと通信トラブルが発生しますので、必ず合わせましょう。

シリアル通信の通信設定

  • ポート番号:シリアル通信で接続する際、窓口となる番号です。「COM1」や「COM2」という書き方をします。
  • ボーレート:1秒間あたりのデータ転送速度を指定します。
  • データビット:1回の送受信でどれくらいのデータを送受信するかを指定します。
  • ストップビット:送信終了を合図するビット数を指定します。
  • パリティ:転送するデータの誤り検出の有無を指定します。有りの場合、偶数または奇数が選択できます。

※スタートビットはデフォルトで1ビット送信されます。

ソースコード作成

それでは早速C#でシリアル通信をするためのコードを記述していきましょう。

「port.cs」という新しい項目(ファイル)を作成して、プロジェクトに追加します。

シリアル関連で使用する名前空間 System.IO.Ports を参照するために、usingディレクティブを用いてコードの先頭に記述します。

using System.IO.Ports;

接続

通信各種の設定や送受信についてはSerialPortクラスを使用します。PortOpenメソッド内で SerialPort のインスタンスを生成し、オブジェクトの初期化を行います。

COM番号は引数として渡し、その他の通信設定は通信機器に合わせておきましょう。通信相手への接続には Open を使います。

private SerialPort sport = null;

public bool PortOpen(string com)
{
    // シリアルポートの設定
    sport = new SerialPort
    {
        PortName = com,                    //ポート番号
        BaudRate = 9600,                   //ボーレート
        DataBits = 8,                      //データビット
        Parity = Parity.None,              //パリティ
        StopBits = StopBits.One,           //ストップビット
        sport.Handshake = Handshake.None,  //ハンドシェイク
        Encoding = Encoding.UTF8,          //エンコード
        WriteTimeout = 100000,             //書き込みタイムアウト
        ReadTimeout = 100000,              //読み取りタイムアウト
        NewLine = "\r\n"                   //改行コード指定
    };

    // シリアルポートに接続
    if (!sport.IsOpen)
    {
        try
        {
            sport.Open();  // ポートオープン
        }
        catch (Exception ex)
        {
            Debug.WriteLine(ex.Message);
            return false;
        }
    }
    return true;
}

切断

接続された通信を切断するには Colse を使います。sportがnullの時に行うとエラーが発生するのでnull条件演算子でチェックをします。

public void PortClose()
{
    sport?.Close();  // ポートクローズ
    sport = null;
}

送信

送信するデータは引数として渡して、WriteLine でデータを送信します。COM ポートがオープンされていないとエラーが発生しますので、IsOpen で確認を入れましょう。

WriteLine を使うことで送信データの末尾に文字(”\r\n”)を自動で追加してくれます。この文字のことをデリミタ文字といい、これがないと通信相手側が送信したコマンド等のデータを認識してくれません。

WriteLine ではなく Write メソッドでデータを送信する場合は、このデリミタ文字を付けましょう。

public void Send(string data)
{
    if (!sport.IsOpen) return;  //ポートが閉じている場合は戻す

    try
    {
        sport.WriteLine(data);  //データ書き込み
    }
    catch (Exception ex)
    {
        Debug.WriteLine(ex.Message);
    }
}

受信

受信する時はReadLineでデータを受信します。ReadLineはデリミタ文字が通信相手から送信されるまでロックされる為、UI画面が固まってしまいます(デッドロック)。

なので、非同期処理で受信が完了するまで待機するようにしましょう。

public async Task<string> ReceiveAsync()
{
    if (!sport.IsOpen) return "";  //ポートが閉じている場合は戻す

    var data = await Task.Run(() => ReadData());
    return data;
}

private string ReadData()
{
    var data = "";
    try
    {
        data = sport.ReadLine();  //データ読み取り
    }
    catch (Exception ex)
    {
        Debug.WriteLine(ex.Message);
        data = "ERROR";
    }
    return data;
}

COM番号の取得

UI画面のコンボボックスにCOM番号の一覧を表示します。パソコンが認識しているCOM番号の一覧を取得するメソッドを用意しておきましょう。

public string[] GetPort()
{
    return SerialPort.GetPortNames();  //ポート番号のリストを返す
}

サンプルの使用例

上記で説明したサンプルソースコードを使用して、WPF でシリアル通信を行うアプリケーションを作成してみましょう。

コンボボックスから COM 番号を選択して、接続開始ボタンをクリックすれば、通信相手にデータを送信したり受信したりすることができます。画面はマテリアルデザインを適用させてオシャレな画面に仕上げました。


マテリアルデザインの適用方法については、以下の記事で詳しく記載しています。

コードは以下のように記述しました。

public partial class MainWindow : Window
{
    port p = new port();

    public MainWindow()
    {
        InitializeComponent();

        btnOpen.IsEnabled = true;
        btnSend.IsEnabled = false;
        cmbComPort.ItemsSource = p.GetPort();
    }

    private void btnOpen_Click(object sender, RoutedEventArgs e)
    {
        if (cmbComPort.Text == "") return;

        if (p.PortOpen(cmbComPort.Text))
        {
            btnSend.IsEnabled = true;  //送信ボタン許可
        }
        else
        {
            btnSend.IsEnabled = false;  //送信ボタン禁止
        }
    }

    private async void btnSend_Click(object sender, RoutedEventArgs e)
    {
        if (txtSendData.Text == "") return;

        p.Send(txtSendData.Text);  //データを送信
        txtLog.Text = await p.ReceiveAsync();
    }
}

動作確認

手元にシリアル通信ができる設備がないので、仮想シリアルポートドライバである「com0com」を使用します。

デジタル署名付きでないとWindows10のOSでは動作しない為、ver2.2.2.0をダウンロードしましょう。

com0com の設定が終わったら、デバイスマネージャーからCOM番号を確認してアプリの COM 番号を設定します。

あとはアプリから TeraTerm へデータを送ったり、TeraTerm からデータを送信してアプリで受信ができていることを確認してみましょう。

まとめ

この記事では、SerialPort クラスを使ってC#でシリアル通信をする方法を紹介しました。

パソコンとマイコンもしくは産業用機器などを制御する通信手段として、シリアル通信を用いる機会はまだまだありますので、この記事が参考になれば幸いです。

このブログでは他にもシリアル通信を使ったサンプルを紹介していますので、興味ある方は参考にしてみてください。

以上、最後まで読んでいただきありがとうございました。

よかったらシェアしてね!
  • URLをコピーしました!