VB.NET Tips - SerialPortコントロールの使い方
最近では通信と言えば、BlueTooth, USBやネットでの通信が当たり前で、
シリアルポート(RS232C)を使って外部装置と通信することも少なくなりました。
しかし稀に外部装置との接続をシリアル通信(調歩同期)で行うこともありますので
今回はシリアルポートを使う方法を説明します。
SerialPortコントロールを使うには、フォーム上にツールボックスからSerialPortを選択し貼り付けます。
以下の図は、SerialPortコントロールを貼り付けた後の様子です。
MSDNの説明によれば以下の様になっています。(代表的なものを示しています。青色は今回使用しているものです。)
SerialPort コントロール・について
<プロパティ>
・PortName :通信用のポートを取得または設定します。
このポートには、使用可能なすべての COM ポートが含まれますが、
これに限定されるわけではありません。
・BaudRate :シリアル ボー レートを取得または設定します。
・BytesToRead :受信バッファー内のデータのバイト数を取得します。
・BytesToWrite:送信バッファー内のデータのバイト数を取得します。
・CtsHolding :Clear To Send ラインの状態を取得します。
・DsrHolding :DSR (Data Set Ready) シグナルの状態を取得します。
・IsOpen :SerialPort オブジェクトの開いている状態または
閉じた状態を示す値を取得します。
・Parity :パリティ チェック プロトコルを取得または設定します。
・DtrEnable :シリアル通信中に、DTR (Data Terminal Ready)シグナルを
有効にする値を取得または設定します。
・DataBits :バイトごとのデータ ビットの標準の長さを取得または設定します。
・StopBits :バイトごとのストップ ビットの標準の数を取得または設定します。
・ReadBufferSize:SerialPortの入力バッファーのサイズを取得または設定します。
・ReadTimeout :読み取り操作が完了していないときに、
タイムアウトになるまでのミリ秒数を取得または設定します。
<メソッド>
・Close() :ポート接続を閉じ、IsOpenプロパティを false に設定し、
内部 Stream オブジェクトを破棄します。
・Open() :新しいシリアル ポート接続を開きます。
・Read(Byte(), Int32, Int32):
SerialPort の入力バッファーから複数のバイトを読み取り、
読み取ったバイトを指定したオフセットでバイト配列に書き込みます。
・ReadByte() :SerialPort の入力バッファーから、同期で 1 バイトを読み取ります。
・Write(Byte(), Int32, Int32):
バッファーのデータを使用して、指定したバイト数をシリアル ポートに書き込みます。
<イベント>
・DataReceived:SerialPort オブジェクトによって表される
ポートを介してデータが受信されたことを示します。
今回のプログラムをテストするためには外部装置か、もう一台別のPCが必要になりますが、
そんなことをしなくてもデバッグができるソフト(ドライバ)が在ります。
com0com というドライバで、PC上に仮想シリアルポートを2個作成し、
その2個をループバックできる様に仮想的に接続できるという優れものです。
以下のリンク先でソフトをダウンロードしてインストールしてみて下さい。
(尚、もう一台のPCなどがあれば必要ありませんが)
⇒http://sourceforge.net/projects/com0com/
このドライバをインストールすると、私のPCではCOM7、COM8が追加で生成されました。
インストール後のPCの状況を以下の図に示します。
COM7側をテスト用送信プログラムとするのですが、
通信テスト用に以下のフリーソフトを使いました。
「のん」さんといわれる方が作成された「RS232Cテストツール」です。
以下のサイトにアクセスすれば、ダウンロードできます。
⇒
http://nonsoft.la.coocan.jp/Download/Rs232cTool/index.html
この方はいろいろなフリーソフトを作成されている様です。使えるソフトが満載です。
インストール後の「RS232Cテストツール」を動作させている様子を以下に示します。
では実際のプログラムを以下に示しますので、説明します。
フォームにSerialPort、TextBox、Buttonを各1個ずつ貼り付けます。
フォームロードイベントでは、シリアルポートの設定を行い、ポートのオープン処理を行います。
ポートの設定は、ポート名「COM8」、通信速度「9600BPS」、パリティ「無し」、データビット数「8」、
ストップビット「1Bit」です。
SerialPortの受信イベントにより、受信バッファー内のデータのバイト数分のバイト配列を宣言し
SerialPortのReadコマンドで受信バイトを全て読込みます。
読込んだバイト配列を文字列に変換し、TextBoxに設定します。
プログラム的には出来ている様な感じがしますが、これをデバッグモードで実行してみます。
SerialPortコントロールの使い方(最初)
06 | Private Sub Form1_Load( ByVal sender As System. Object , ByVal e As System.EventArgs) Handles MyBase .Load |
11 | .Parity = IO.Ports.Parity.None |
13 | .StopBits = IO.Ports.StopBits.One |
22 | Private Sub Form1_FormClosed( ByVal sender As Object , ByVal e As System.Windows.Forms.FormClosedEventArgs) Handles Me .FormClosed |
30 | Private Sub SerialPort1_DataReceived( ByVal sender As System. Object , ByVal e As System.IO.Ports.SerialDataReceivedEventArgs) Handles SerialPort1.DataReceived |
32 | Dim arrByte As Byte () = New Byte (SerialPort1.BytesToRead - 1) {} |
34 | SerialPort1.Read(arrByte, 0, arrByte.GetLength(0)) |
36 | Dim str As String = System.Text.Encoding.GetEncoding( "SHIFT-JIS" ).GetString(arrByte) |
45 | Private Sub DisplayText( ByVal strDisp As String ) |
47 | Me .TextBox1.Text &= strDisp |
53 | Private Sub btnClear_Click( ByVal sender As Object , ByVal e As EventArgs) Handles btnClear.Click |
このプログラムをデバッグモードで実行させて待機状態にして置き、
先ほどの「RS232Cテストツール」を実行し、送信TEXTにデータを設定し、送信します。
送信ボタン押下した時に、今回のプログラムのTextBoxにデータが表示されるはずですが
実際は以下の様なエラーが発生しました。
スレッド間のアクセスが起きてエラーが発生した様です。
そこで調べたところ、MSDNの説明によれば、
DataReceived イベントは、データが SerialPort オブジェクトから受信されたときにセカンダリ スレッドで発生します。
メインの Form または Control で要素を変更する必要がある場合は、
Invoke を使用して変更要求をポストバックします。これにより、適切なスレッドで処理が実行されるようになります。
とあります。結局、DataReceivedイベントは別スレッドなので、フォームのコントロールなどにアクセスする場合は
Invoke等を使用しなさいとのことの様です。
プログラムを以下の様に書き換えました。
SerialPortコントロールの使い方(正常処理)
06 | Private Sub Form1_Load( ByVal sender As System. Object , ByVal e As System.EventArgs) Handles MyBase .Load |
11 | .Parity = IO.Ports.Parity.None |
13 | .StopBits = IO.Ports.StopBits.One |
22 | Private Sub Form1_FormClosed( ByVal sender As Object , ByVal e As System.Windows.Forms.FormClosedEventArgs) Handles Me .FormClosed |
30 | Private Sub SerialPort1_DataReceived( ByVal sender As System. Object , ByVal e As System.IO.Ports.SerialDataReceivedEventArgs) Handles SerialPort1.DataReceived |
32 | Dim arrByte As Byte () = New Byte (SerialPort1.BytesToRead - 1) {} |
34 | SerialPort1.Read(arrByte, 0, arrByte.GetLength(0)) |
36 | Dim dlg As New DisplayTextDelegate( AddressOf DisplayText) |
38 | Dim str As String = System.Text.Encoding.GetEncoding( "SHIFT-JIS" ).GetString(arrByte) |
40 | Me .Invoke(dlg, New Object () {str}) |
44 | Delegate Sub DisplayTextDelegate( ByVal strDisp As String ) |
50 | Private Sub DisplayText( ByVal strDisp As String ) |
52 | Me .TextBox1.Text &= strDisp |
58 | Private Sub btnClear_Click( ByVal sender As Object , ByVal e As EventArgs) Handles btnClear.Click |