[C#] SerialPortクラスでシリアル通信を実現する方法

C#のSerialPortクラスは、シリアル通信を行うための機能を提供します。

シリアル通信を実現するには、まずSystem.IO.Ports名前空間をインポートし、SerialPortオブジェクトを作成します。

次に、通信ポート名(例:COM1)、ボーレート、パリティ、データビット、ストップビットなどのプロパティを設定します。

Openメソッドを呼び出してポートを開き、Writeメソッドでデータを送信し、Readメソッドでデータを受信します。

通信が終了したら、Closeメソッドでポートを閉じます。

イベントハンドラを使用してデータ受信を非同期に処理することも可能です。

この記事でわかること
  • C#のSerialPortクラスの基本的な使い方
  • シリアル通信の非同期処理の実装方法
  • エラーハンドリングの重要性と実装例
  • 複数ポートの同時通信の実現方法
  • バイナリデータの送受信の手法

目次から探す

シリアル通信の実装手順

SerialPortオブジェクトの作成

C#でシリアル通信を行うためには、まずSerialPortクラスのインスタンスを作成します。

以下のコードは、COMポートを指定してSerialPortオブジェクトを作成する例です。

using System;
using System.IO.Ports;
class Program
{
    static void Main()
    {
        // SerialPortオブジェクトの作成
        SerialPort serialPort = new SerialPort("COM3", 9600);
        
        // その他の設定(必要に応じて)
        serialPort.Parity = Parity.None; // パリティビットの設定
        serialPort.DataBits = 8;          // データビットの設定
        serialPort.StopBits = StopBits.One; // ストップビットの設定
        
        // オブジェクトの状態を確認
        Console.WriteLine("SerialPortオブジェクトが作成されました。");
    }
}
SerialPortオブジェクトが作成されました。

ポートのオープンとクローズ

シリアルポートを使用する前に、ポートをオープンする必要があります。

オープン後は、通信が終了したら必ずポートをクローズします。

以下のコードは、ポートのオープンとクローズの例です。

using System;
using System.IO.Ports;
class Program
{
    static void Main()
    {
        SerialPort serialPort = new SerialPort("COM3", 9600);
        
        try
        {
            // ポートをオープン
            serialPort.Open();
            Console.WriteLine("ポートがオープンしました。");
            // 通信処理(省略)
        }
        catch (Exception ex)
        {
            Console.WriteLine("エラー: " + ex.Message);
        }
        finally
        {
            // ポートをクローズ
            if (serialPort.IsOpen)
            {
                serialPort.Close();
                Console.WriteLine("ポートがクローズしました。");
            }
        }
    }
}
ポートがオープンしました。
ポートがクローズしました。

データの送信

シリアルポートを通じてデータを送信するには、Writeメソッドを使用します。

以下のコードは、文字列データを送信する例です。

using System;
using System.IO.Ports;
class Program
{
    static void Main()
    {
        SerialPort serialPort = new SerialPort("COM3", 9600);
        
        try
        {
            serialPort.Open();
            Console.WriteLine("ポートがオープンしました。");
            // データの送信
            string dataToSend = "こんにちは、シリアル通信!";
            serialPort.Write(dataToSend); // データを送信
            Console.WriteLine("データを送信しました: " + dataToSend);
        }
        catch (Exception ex)
        {
            Console.WriteLine("エラー: " + ex.Message);
        }
        finally
        {
            if (serialPort.IsOpen)
            {
                serialPort.Close();
                Console.WriteLine("ポートがクローズしました。");
            }
        }
    }
}
ポートがオープンしました。
データを送信しました: こんにちは、シリアル通信!
ポートがクローズしました。

データの受信

シリアルポートからデータを受信するには、Readメソッドを使用します。

以下のコードは、受信したデータを表示する例です。

using System;
using System.IO.Ports;
class Program
{
    static void Main()
    {
        SerialPort serialPort = new SerialPort("COM3", 9600);
        
        try
        {
            serialPort.Open();
            Console.WriteLine("ポートがオープンしました。");
            // データの受信
            string receivedData = serialPort.ReadLine(); // データを受信
            Console.WriteLine("受信したデータ: " + receivedData);
        }
        catch (Exception ex)
        {
            Console.WriteLine("エラー: " + ex.Message);
        }
        finally
        {
            if (serialPort.IsOpen)
            {
                serialPort.Close();
                Console.WriteLine("ポートがクローズしました。");
            }
        }
    }
}
ポートがオープンしました。
受信したデータ: [受信したデータ内容]
ポートがクローズしました。

このように、C#のSerialPortクラスを使用することで、シリアル通信を簡単に実装することができます。

非同期通信の実装

DataReceivedイベントの利用

シリアル通信では、データの受信を非同期で行うことができます。

これを実現するために、DataReceivedイベントを利用します。

このイベントは、データが受信されたときに自動的に発生します。

以下のコードは、DataReceivedイベントを使用してデータを受信する例です。

using System;
using System.IO.Ports;
class Program
{
    static SerialPort serialPort;
    static void Main()
    {
        serialPort = new SerialPort("COM3", 9600);
        
        // DataReceivedイベントの設定
        serialPort.DataReceived += new SerialDataReceivedEventHandler(DataReceivedHandler);
        
        try
        {
            serialPort.Open();
            Console.WriteLine("ポートがオープンしました。");
            // メインスレッドを維持するための無限ループ
            while (true) { }
        }
        catch (Exception ex)
        {
            Console.WriteLine("エラー: " + ex.Message);
        }
        finally
        {
            if (serialPort.IsOpen)
            {
                serialPort.Close();
                Console.WriteLine("ポートがクローズしました。");
            }
        }
    }
    // DataReceivedイベントハンドラ
    private static void DataReceivedHandler(object sender, SerialDataReceivedEventArgs e)
    {
        string receivedData = serialPort.ReadLine(); // データを受信
        Console.WriteLine("受信したデータ: " + receivedData);
    }
}
ポートがオープンしました。
受信したデータ: [受信したデータ内容]

イベントハンドラの設定

DataReceivedイベントを利用するためには、イベントハンドラを設定する必要があります。

上記のコードでは、DataReceivedHandlerメソッドがイベントハンドラとして設定されています。

このメソッドは、データが受信されるたびに呼び出され、受信したデータを処理します。

イベントハンドラの設定は、serialPort.DataReceived += new SerialDataReceivedEventHandler(DataReceivedHandler);のように行います。

非同期処理の注意点

非同期通信を行う際には、いくつかの注意点があります。

以下に主なポイントを示します。

スクロールできます
注意点説明
スレッドセーフUIスレッドと異なるスレッドで実行されるため、UIの更新にはInvokeメソッドを使用する必要があります。
データの整合性受信データが複数回に分かれて到着する可能性があるため、データの整合性を保つための処理が必要です。
エラーハンドリング非同期処理では例外が発生する可能性があるため、適切なエラーハンドリングを行う必要があります。

これらの注意点を考慮しながら、非同期通信を実装することが重要です。

エラーハンドリング

通信エラーの種類

シリアル通信を行う際には、さまざまな通信エラーが発生する可能性があります。

以下に代表的な通信エラーの種類を示します。

スクロールできます
エラーの種類説明
ポートが使用中他のアプリケーションがポートを使用している場合に発生します。
タイムアウトデータの送受信が指定した時間内に完了しない場合に発生します。
データオーバーラン受信バッファが満杯になり、新しいデータが失われる場合に発生します。
フレーミングエラー受信したデータのフォーマットが正しくない場合に発生します。

エラーの検出と対処法

通信エラーを検出するためには、SerialPortクラスのプロパティやイベントを利用します。

以下は、エラーを検出し、適切に対処する方法の例です。

using System;
using System.IO.Ports;
class Program
{
    static SerialPort serialPort;
    static void Main()
    {
        serialPort = new SerialPort("COM3", 9600);
        
        // エラーハンドリングのためのイベント設定
        serialPort.ErrorReceived += new SerialErrorReceivedEventHandler(ErrorReceivedHandler);
        
        try
        {
            serialPort.Open();
            Console.WriteLine("ポートがオープンしました。");
            // 通信処理(省略)
        }
        catch (Exception ex)
        {
            Console.WriteLine("エラー: " + ex.Message);
        }
        finally
        {
            if (serialPort.IsOpen)
            {
                serialPort.Close();
                Console.WriteLine("ポートがクローズしました。");
            }
        }
    }
    // エラー受信イベントハンドラ
    private static void ErrorReceivedHandler(object sender, SerialErrorReceivedEventArgs e)
    {
        Console.WriteLine("通信エラーが発生しました: " + e.EventType.ToString());
    }
}
ポートがオープンしました。
通信エラーが発生しました: [エラーの種類]
ポートがクローズしました。

例外処理の実装

シリアル通信では、さまざまな例外が発生する可能性があります。

これらの例外を適切に処理するためには、try-catchブロックを使用します。

以下は、例外処理を実装したコードの例です。

using System;
using System.IO.Ports;
class Program
{
    static void Main()
    {
        SerialPort serialPort = new SerialPort("COM3", 9600);
        
        try
        {
            serialPort.Open();
            Console.WriteLine("ポートがオープンしました。");
            // データの送信や受信処理(省略)
        }
        catch (UnauthorizedAccessException ex)
        {
            Console.WriteLine("ポートにアクセスできません: " + ex.Message);
        }
        catch (TimeoutException ex)
        {
            Console.WriteLine("通信タイムアウト: " + ex.Message);
        }
        catch (IOException ex)
        {
            Console.WriteLine("入出力エラー: " + ex.Message);
        }
        catch (Exception ex)
        {
            Console.WriteLine("予期しないエラー: " + ex.Message);
        }
        finally
        {
            if (serialPort.IsOpen)
            {
                serialPort.Close();
                Console.WriteLine("ポートがクローズしました。");
            }
        }
    }
}
ポートがオープンしました。
ポートにアクセスできません: [エラーメッセージ]
ポートがクローズしました。

このように、エラーハンドリングを適切に実装することで、シリアル通信の信頼性を向上させることができます。

応用例

複数ポートの同時通信

C#のSerialPortクラスを使用して、複数のシリアルポートを同時に通信することができます。

以下のコードは、2つのCOMポートを同時にオープンし、それぞれからデータを受信する例です。

using System;
using System.IO.Ports;
class Program
{
    static SerialPort serialPort1;
    static SerialPort serialPort2;
    static void Main()
    {
        serialPort1 = new SerialPort("COM3", 9600);
        serialPort2 = new SerialPort("COM4", 9600);
        // DataReceivedイベントの設定
        serialPort1.DataReceived += new SerialDataReceivedEventHandler(DataReceivedHandler1);
        serialPort2.DataReceived += new SerialDataReceivedEventHandler(DataReceivedHandler2);
        try
        {
            serialPort1.Open();
            serialPort2.Open();
            Console.WriteLine("ポートがオープンしました。");
            // メインスレッドを維持するための無限ループ
            while (true) { }
        }
        catch (Exception ex)
        {
            Console.WriteLine("エラー: " + ex.Message);
        }
        finally
        {
            if (serialPort1.IsOpen) serialPort1.Close();
            if (serialPort2.IsOpen) serialPort2.Close();
            Console.WriteLine("ポートがクローズしました。");
        }
    }
    private static void DataReceivedHandler1(object sender, SerialDataReceivedEventArgs e)
    {
        string receivedData = serialPort1.ReadLine();
        Console.WriteLine("COM3から受信したデータ: " + receivedData);
    }
    private static void DataReceivedHandler2(object sender, SerialDataReceivedEventArgs e)
    {
        string receivedData = serialPort2.ReadLine();
        Console.WriteLine("COM4から受信したデータ: " + receivedData);
    }
}
ポートがオープンしました。
COM3から受信したデータ: [受信したデータ内容]
COM4から受信したデータ: [受信したデータ内容]
ポートがクローズしました。

バイナリデータの送受信

シリアル通信では、テキストデータだけでなく、バイナリデータも送受信できます。

以下のコードは、バイナリデータを送信し、受信する例です。

using System;
using System.IO.Ports;
class Program
{
    static void Main()
    {
        SerialPort serialPort = new SerialPort("COM3", 9600);
        
        try
        {
            serialPort.Open();
            Console.WriteLine("ポートがオープンしました。");
            // バイナリデータの送信
            byte[] dataToSend = { 0x01, 0x02, 0x03, 0x04 };
            serialPort.Write(dataToSend, 0, dataToSend.Length);
            Console.WriteLine("バイナリデータを送信しました。");
            // バイナリデータの受信
            byte[] receivedData = new byte[4];
            serialPort.Read(receivedData, 0, receivedData.Length);
            Console.WriteLine("受信したバイナリデータ: " + BitConverter.ToString(receivedData));
        }
        catch (Exception ex)
        {
            Console.WriteLine("エラー: " + ex.Message);
        }
        finally
        {
            if (serialPort.IsOpen)
            {
                serialPort.Close();
                Console.WriteLine("ポートがクローズしました。");
            }
        }
    }
}
ポートがオープンしました。
バイナリデータを送信しました。
受信したバイナリデータ: 01-02-03-04
ポートがクローズしました。

シリアル通信のデバッグ方法

シリアル通信のデバッグには、いくつかの方法があります。

以下に代表的なデバッグ方法を示します。

スクロールできます
デバッグ方法説明
ログ出力受信したデータやエラーをコンソールに出力し、通信の状態を確認します。
シリアルモニタツール外部ツール(例:PuTTY、Tera Term)を使用して、送受信データを確認します。
デバッグ用フラグデバッグ用のフラグを設定し、特定の条件下で詳細な情報を出力します。

これらの方法を組み合わせることで、シリアル通信の問題を特定しやすくなります。

よくある質問

SerialPortクラスで通信ができない場合の原因は?

SerialPortクラスで通信ができない場合、以下のような原因が考えられます。

  • ポートが使用中: 他のアプリケーションが同じCOMポートを使用している場合、通信ができません。
  • 不正なポート名: 指定したCOMポート名が存在しない場合、通信は失敗します。
  • ボーレートの不一致: 通信相手のデバイスとボーレートが一致していないと、データが正しく送受信できません。
  • ドライバの問題: シリアルポートのドライバが正しくインストールされていない場合、通信ができないことがあります。
  • ハードウェアの接続不良: ケーブルや接続端子に問題があると、通信ができません。

ボーレートの設定はどのように決めるべき?

ボーレートの設定は、通信するデバイスの仕様に基づいて決定する必要があります。

以下のポイントを考慮してください。

  • デバイスの仕様: 通信相手のデバイスがサポートするボーレートを確認し、それに合わせて設定します。
  • 通信の安定性: 高いボーレートはデータ転送速度を向上させますが、通信の安定性が低下する可能性があります。

安定性が求められる場合は、低めのボーレートを選択します。

  • データ量: 大量のデータを送信する場合は、高いボーレートを選ぶことが有効ですが、エラー率も考慮する必要があります。

非同期通信でデータが欠落するのはなぜ?

非同期通信でデータが欠落する原因はいくつかあります。

主な要因は以下の通りです。

  • バッファのオーバーフロー: 受信バッファが満杯になると、新しいデータが失われることがあります。

バッファサイズを適切に設定し、データを迅速に処理することが重要です。

  • データの分割受信: データが複数回に分かれて到着する場合、受信側で正しく組み立てられないと、データが欠落したように見えることがあります。

データの終端を示す特定の文字やフレームを使用することが有効です。

  • スレッドの競合: 非同期処理では、複数のスレッドが同時にデータを処理するため、適切なロック機構を使用しないと、データが競合して欠落することがあります。
  • 通信エラー: 通信中にエラーが発生した場合、データが正しく受信されないことがあります。

エラーハンドリングを適切に実装することが重要です。

まとめ

この記事では、C#のSerialPortクラスを使用したシリアル通信の実装方法や、非同期通信の実装、エラーハンドリング、応用例について詳しく解説しました。

シリアル通信を行う際には、通信エラーの種類やボーレートの設定、データの送受信方法など、さまざまな要素を考慮する必要があります。

これらの知識を活用して、実際のプロジェクトにおいてシリアル通信を効果的に実装し、トラブルシューティングを行うことが重要です。

当サイトはリンクフリーです。出典元を明記していただければ、ご自由に引用していただいて構いません。

関連カテゴリーから探す

  • URLをコピーしました!
目次から探す