SerialPort

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

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

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

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

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

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

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

シリアル通信の実装手順

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)を使用して、送受信データを確認します。
デバッグ用フラグデバッグ用のフラグを設定し、特定の条件下で詳細な情報を出力します。

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

まとめ

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

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

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

関連記事

Back to top button
目次へ