web-dev-qa-db-ja.com

C#でSerialPortポートオブジェクトのdataReceivedイベントを使用するにはどうすればよいですか?

COM10に接続された外部センサーから受信したデータを収集する小さなアプリケーションを作成しようとしています。ポートを開き、forループを使用して一定期間ファイルにデータをストリーミングする小さなC#コンソールオブジェクトとアプリケーションを正常に作成しました。

このアプリケーションを変換して、代わりにdataReceivedイベントを使用してストリーミングしたいと思います。 Top 5 SerialPort Tips を読んだ後、まだ動作していないようで、何が欠けているのかわかりません。すべてのコードがメインにあり、以下に貼り付けられるように、コンソールアプリケーションを書き直しました。ハードウェアによってポートにデータが送信されていることを知っていても、誰かがイベントハンドラーport_OnReceiveDatazzが呼び出されない理由を教えてください。

ありがとう

アジム

PS:すべての提案について @ Gabe@ Jason Down 、および @ abatishchev に感謝します。私は困惑し、イベントハンドラーを動作させることができないようです。おそらくそれはデバイスと関係があるのでしょう。スレッドでポートを読み取り、データをファイルに直接ストリーミングするだけで生きることができます。


コード


namespace serialPortCollection
{   class Program
    {
        static void Main(string[] args)
        {

            const int bufSize = 2048;
            Byte[] buf = new Byte[bufSize]; //To store the received data.

            SerialPort sp = new SerialPort("COM10", 115200);
            sp.DataReceived += port_OnReceiveDatazz; // Add DataReceived Event Handler

            sp.Open();
            sp.WriteLine("$"); //Command to start Data Stream

            // Wait for data or user input to continue.
            Console.ReadLine();

            sp.WriteLine("!"); //Stop Data Stream Command
            sp.Close();
        }

       // My Event Handler Method
        private static void port_OnReceiveDatazz(object sender, 
                                   SerialDataReceivedEventArgs e)
        {
            SerialPort spL = (SerialPort) sender;
            const int bufSize = 12;
            Byte[] buf = new Byte[bufSize];
            Console.WriteLine("DATA RECEIVED!");
            Console.WriteLine(spL.Read(buf, 0, bufSize));
        }
    }
}
19
Azim

あなたの問題は次のようなものだと思います:**

sp.DataReceived + = port_OnReceiveDatazz;

すべきではない:

sp.DataReceived + = new SerialDataReceivedEventHandler(port_OnReceiveDatazz);

**決して気にしないでください、構文は問題ありません(この質問に最初に答えた時点でショートカットを認識していませんでした)。

また、シリアルポートで次のオプションをオンにする必要があるという提案もあります。

sp.DtrEnable = true;    // Data-terminal-ready
sp.RtsEnable = true;    // Request-to-send

また、ハンドシェイクをRequestToSendに設定する必要があります(ハンドシェイクの列挙を使用)。


更新:

最初にポートを開き、イベントハンドラーを割り当てる必要があるという提案を見つけました。たぶんそれはバグですか?

したがって、これの代わりに:

sp.DataReceived += new SerialDataReceivedEventHandler (port_OnReceiveDatazz);
sp.Open();

これを行う:

sp.Open();
sp.DataReceived += new SerialDataReceivedEventHandler (port_OnReceiveDatazz);

その方法を教えてください。

12
Jason Down

まず、現在使用しているコンストラクタではなく、次のコンストラクタを使用することをお勧めします。

new SerialPort("COM10", 115200, Parity.None, 8, StopBits.One);

次に、本当にこのコードを削除する必要があります。

// Wait 10 Seconds for data...
for (int i = 0; i < 1000; i++)
{
    Thread.Sleep(10);
    Console.WriteLine(sp.Read(buf,0,bufSize)); //prints data directly to the Console
}

代わりに、ユーザーがキーなどを押すまでループします。

namespace serialPortCollection
{   class Program
    {
        static void Main(string[] args)
        {
            SerialPort sp = new SerialPort("COM10", 115200);
            sp.DataReceived += port_OnReceiveDatazz; // Add DataReceived Event Handler

            sp.Open();
            sp.WriteLine("$"); //Command to start Data Stream

            Console.ReadLine();

            sp.WriteLine("!"); //Stop Data Stream Command
            sp.Close();
        }

       // My Event Handler Method
        private static void port_OnReceiveDatazz(object sender, 
                                   SerialDataReceivedEventArgs e)
        {
            SerialPort spL = (SerialPort) sender;
            byte[] buf = new byte[spL.BytesToRead];
            Console.WriteLine("DATA RECEIVED!");
            spL.Read(buf, 0, buf.Length);
            foreach (Byte b in buf)
            {
                Console.Write(b.ToString());
            }
            Console.WriteLine();
        }
    }
}

また、データ受信イベントハンドラーのリビジョンに注意してください。実際にバッファーを印刷するはずです。

更新1


私は自分のマシンで次のコードを正常に実行しました(COM33とCOM34の間のヌルモデムケーブルを使用)

namespace TestApp
{
    class Program
    {
        static void Main(string[] args)
        {
            Thread writeThread = new Thread(new ThreadStart(WriteThread));
            SerialPort sp = new SerialPort("COM33", 115200, Parity.None, 8, StopBits.One);
            sp.DataReceived += port_OnReceiveDatazz; // Add DataReceived Event Handler

            sp.Open();
            sp.WriteLine("$"); //Command to start Data Stream

            writeThread.Start();

            Console.ReadLine();

            sp.WriteLine("!"); //Stop Data Stream Command
            sp.Close();
        }

        private static void port_OnReceiveDatazz(object sender, 
                                   SerialDataReceivedEventArgs e)
        {
            SerialPort spL = (SerialPort) sender;
            byte[] buf = new byte[spL.BytesToRead];
            Console.WriteLine("DATA RECEIVED!");
            spL.Read(buf, 0, buf.Length);
            foreach (Byte b in buf)
            {
                Console.Write(b.ToString() + " ");
            }
            Console.WriteLine();
        }

        private static void WriteThread()
        {
            SerialPort sp2 = new SerialPort("COM34", 115200, Parity.None, 8, StopBits.One);
            sp2.Open();
            byte[] buf = new byte[100];
            for (byte i = 0; i < 100; i++)
            {
                buf[i] = i;
            }
            sp2.Write(buf, 0, buf.Length);
            sp2.Close();
        }
    }
}

更新2


最近、この質問のすべてのトラフィックを考えてみましょう。シリアルポートが正しく構成されていないか、デバイスが応答していないのではないかと疑い始めています。

他の手段を使用してデバイスと通信することを強くお勧めします(ハイパーターミナルを頻繁に使用します)。動作するセットが見つかるまで、これらの設定(ビットレート、パリティ、データビット、ストップビット、フロー制御)をすべて試すことができます。デバイスのドキュメントでもこれらの設定を指定する必要があります。それらを理解したら、.NET SerialPortがそれらの設定を使用するように適切に構成されていることを確認します。

シリアルポートの設定に関するヒント:

次のコンストラクターを使用する必要があると言ったとき、必ずしもそれらのパラメーターではなく、その関数を使用することを意味したことに注意してください!デバイスのパラメータを入力する必要があります。以下の設定は一般的ですが、デバイスによって異なる場合があります。

new SerialPort("COM10", 115200, Parity.None, 8, StopBits.One);

また、デバイスと同じフロー制御を使用するように.NET SerialPortをセットアップすることも重要です(他の人が以前に述べたように)。詳細についてはこちらをご覧ください。

http://www.lammertbies.nl/comm/info/RS-232_flow_control.html

9
Gabe

以前に動作していたモデムでも同じ問題が発生していましたが、ある日、DataReceivedイベントの発生を停止しました。

私の場合の解決策は、非常にランダムに、RTSを有効にすることでした。

sp.RtsEnable = true;

なぜそれがこの特定のキットで動作したのか(実際には通信員ではない)、それが動作して停止した理由はわかりませんが、いつか他の人を助けるかもしれないので、念のために投稿してください...

4
Justin Wignall

ところで、イベントハンドラーで次のコードを使用できます。

switch(e.EventType)
{
  case SerialData.Chars:
  {
    // means you receives something
    break;
  }
  case SerialData.Eof:
  {
    // means receiving ended
    break;
  }
}
3
abatishchev

コンソールアプリケーションを使用していて、イベントループが実行されていないため、これは機能しないと思います。イベント処理に使用されるイベントループ/メッセージポンプは、Winformsアプリケーションの作成時に自動的にセットアップされますが、コンソールアプリ用ではありません。

1
Matt

Console.ReadLineコールバックのブロックConsole.Writeline、 実際には。 MSDNのサンプルは、ReadKeyを使用する(コンソールをロックしない)ことを除いて、ALMOSTと同じように見えます。

1
Bart de Boer

.NET/C#およびCOM9よりも上位のCOMポートの使用には問題があることに注意してください。

参照: HOWTO:COM9より大きいシリアルポートを指定

根本的なCreateFileメソッドでサポートされている「\\。\ COM10」という形式で回避策がありますが、.NETではその回避策形式を使用できません。 SerialPortコンストラクターもPortNameプロパティも、「\」で始まるポート名を許可しません。

C#/。NETでCOM10との信頼できる通信を得るのに苦労しました。例として、COM9とCOM10にデバイスがある場合、COM10向けのトラフィックはCOM9のデバイスに送られます! COM9のデバイスを削除すると、COM10トラフィックはCOM10のデバイスに送られます。

CreateFileによって返されたハンドルを使用してC#/。NETスタイルのSerialPortオブジェクトを作成する方法をまだ考えていません。その方法を知っていれば、C#からCOM10 +を使用できると思います。

0
MikeZ