忍者ブログ

VB.NET-TIPS などプログラミングについて

VB.NETのTIPS(小技集)を中心に、Javascript、PHP その他のプログラミングについて少し役に立つ情報を発信します。いわゆる個人的な忘備録ですが、みなさんのお役に立てれば幸いです。

SerialPortコントロールの使い方その2

SerialPortコントロールを使い方を以下の記事で紹介しましたが、 今回は外部装置との簡単な送受信ができるクラスの紹介を行います。

SerialPortコントロールの使用方法:[SerialPort,Invoke]

クラスの概要は以下の通りです。

  • 外部装置との通信は調歩同期とし、Shift-JIS文字列での送受信を対象とする。
  • 通信ポート設定は「ボーレート:9600BPS、データビット:8、パリティビット:無し、ストップビット:1」とする。
  • 送受信文字列の終端文字は「CR:0xD」とする。
  • 外部装置からのデータ受信は、最初にコマンド文字列を送信し、その後データ受信する手順とする。
  • コマンド文字列送信後、約5秒以内にデータ受信できない場合はエラーとする。
  • 受信文字列は4096バイトのバッファで行う。

SerialPortコントロールの使い方その2(通信用クラス)

Imports System.IO.Ports

''' -----------------------------------------------------------------------
''' 
''' シリアル通信クラス
''' 
''' -----------------------------------------------------------------------
Public Class ClsSerial

    ''' 
    ''' シリアルポートクラス
    ''' 
    ''' 
    Private SerialPort As SerialPort

    ''' 
    ''' 読込データ格納先
    ''' 
    ''' データ受信イベントで蓄えられる
    Private RxBuf() As Byte

    ''' 
    ''' コマンド応答受信フラグ
    ''' 
    ''' 
    ''' コマンドを送信した後での応答を受信したフラグ。送受信文字列のターミネータ[CR](0x0d)を発見したとにオンする
    ''' 
    Private fRxResponse As Boolean = False

    ''' 
    ''' 送受信文字列のターミネータ(0x0d)
    ''' 
    Private Const CMD_TERM As Byte = &HD


    ''' -----------------------------------------------------------------------
    ''' 
    ''' コンストラクタ
    ''' 
    ''' <param name="SerialPort">シリアルポートコントロール</param>
    ''' -----------------------------------------------------------------------
    Sub New(ByVal SerialPort As SerialPort)
        'シリアルポートの退避
        Me.SerialPort = SerialPort
        '受信イベントのハンドラ設定
        AddHandler Me.SerialPort.DataReceived, AddressOf DataReceivedHandler
    End Sub

    ''' -----------------------------------------------------------------------
    ''' 
    ''' ポートオープン
    ''' 
    ''' True:正常終了, False:エラー
    ''' -----------------------------------------------------------------------
    Function Open() As Boolean
        '戻り値初期化
        Open = False
        Try
            'ポートチェック
            If Me.SerialPort.IsOpen = False Then
                '未オープンならば、オープンする

                'ポート設定(ここは通信相手に合わせる)
                Me.SerialPort.PortName = "COM7"
                Me.SerialPort.BaudRate = 9600
                Me.SerialPort.DataBits = 8
                Me.SerialPort.Parity = Parity.None
                Me.SerialPort.StopBits = StopBits.One
                'オープン
                Me.SerialPort.Open()
            End If
            '結果を返す
            Open = Me.SerialPort.IsOpen

        Catch ex As Exception
            'エラー処理
        End Try
    End Function

    ''' -----------------------------------------------------------------------
    ''' 
    ''' ポートクローズ
    ''' 
    ''' True:正常終了, False:エラー
    ''' -----------------------------------------------------------------------
    Function Close() As Boolean
        '戻り値初期化
        Close = False
        Try
            'ポートチェック
            If Me.SerialPort.IsOpen = True Then
                'オープン済みならば、クローズする
                Me.SerialPort.Close()
            End If
            '結果を返す
            Close = Not (Me.SerialPort.IsOpen)

        Catch ex As Exception
            'エラー処理
        End Try
    End Function

    ''' -----------------------------------------------------------------------
    ''' 
    ''' データ受信ハンドラ
    ''' 
    ''' -----------------------------------------------------------------------
    Private Sub DataReceivedHandler(sender As Object, e As SerialDataReceivedEventArgs)
        Try
            Dim SP As SerialPort = CType(sender, SerialPort)

            'バッファのバイト数チェック
            Dim DataLen As Integer = SP.BytesToRead
            If DataLen = 0 Then
                Exit Sub
            End If

            'データ長チェック
            If DataLen > 4096 Then
                DataLen = 4096
            End If

            '読込バッファの確保
            Dim Buf(DataLen) As Byte
            '読込
            SP.Read(Buf, 0, DataLen)
            '
            If RxBuf Is Nothing Then
                '格納バッファの領域確保
                RxBuf = Array.CreateInstance(GetType(Byte), DataLen)
                'コピー
                Array.Copy(Buf, RxBuf, DataLen)
            Else
                '後ろに追加する
                Dim Length As Integer = RxBuf.Length
                ReDim Preserve RxBuf(Length + DataLen - 1)
                Buf.CopyTo(RxBuf, Length)
            End If

            '格納先の最後の文字が「CR」(0x0d)か確認する
            If RxBuf(UBound(RxBuf)) = CMD_TERM Then
                'コマンド応答受信フラグON
                Me.fRxResponse = True
            End If

        Catch ex As Exception
            'エラー処理
        End Try
    End Sub

    ''' -----------------------------------------------------------------------
    ''' 
    ''' コマンド送信・レスポンス受信処理
    ''' 
    ''' <param name="strCmd">コマンド文字列</param>
    ''' <param name="strRsv">レスポンス文字列</param>
    ''' 応答受信結果(True:OK, False:NG)
    ''' -----------------------------------------------------------------------
    Public Function SendCmdGetRsv(ByVal strCmd As String, ByRef strRsv As String) As Boolean
        '戻り値初期化
        SendCmdGetRsv = False
        Try
            'コマンド送信
            If Me.SendData(strCmd) = False Then
                Return False
            End If

            'コマンド受信
            Dim strRX As String = ""
            If Me.ReceiveData(strRX) = False Then
                Return False
            End If
            strRsv = strRX

            '正常を返す
            Return True

        Catch ex As Exception
            'エラー処理
        End Try
    End Function

    ''' -----------------------------------------------------------------------
    ''' 
    ''' コマンド送信
    ''' 
    ''' <param name="strSend">送信文字列</param>
    ''' 送信結果(True:OK, False:NG)
    ''' -----------------------------------------------------------------------
    Private Function SendData(ByVal strSend As String) As Boolean
        '戻り値初期化
        SendData = False
        Try
            'ポートオープンチェック
            If Me.SerialPort.IsOpen = False Then
                Return False
            End If

            'Shift JISとしてバイト型配列に変換
            Dim bytesData As Byte()
            bytesData = System.Text.Encoding.GetEncoding(932).GetBytes(strSend)

            'CRの1バイト分領域拡張
            Dim nIdx As Integer = UBound(bytesData)
            ReDim Preserve bytesData(nIdx + 1)
            bytesData(nIdx + 1) = CMD_TERM

            'コマンド応答受信フラグOFF
            Me.fRxResponse = False

            'コマンド送信
            Me.RxBuf = Nothing  '受信バッファのクリア
            Me.SerialPort.Write(bytesData, 0, bytesData.Length)
            '正常
            Return True

        Catch ex As Exception
            'エラー処理
        End Try
    End Function

    ''' -----------------------------------------------------------------------
    ''' 
    ''' レスポンス受信処理
    ''' 
    ''' <param name="RcvRes">受信レスポンス</param>
    ''' 受信結果(True:OK, False:NG)
    ''' -----------------------------------------------------------------------
    Private Function ReceiveData(ByRef RcvRes As String) As Boolean
        '戻り値初期化
        ReceiveData = False
        Try
            'リトライ件数
            Dim nRetry As Integer = 50  '50×100msec=5sec

            While nRetry > 0
                System.Threading.Thread.Sleep(100)
                Application.DoEvents()
                If Me.fRxResponse = True Then
                    '受信フラグONの場合、ループを抜ける
                    Exit While
                End If
                'リトライカウンタ--
                nRetry -= 1
            End While

            '-----
            'タイムアウトチェック
            '-----
            If nRetry = 0 Then
                'タイムアウトエラー
                Return False
            End If

            '受信バイトを文字列変換
            Dim strRx As String = System.Text.Encoding.GetEncoding(932).GetString(Me.RxBuf)
            strRx = strRx.Substring(0, strRx.Length - 1)    '最後の「CR」は省く

            '-----
            '受信データ処理
            '-----
            '以下の処理などが在れば記述
            '・BCC等のチェック処理
            '・レスポンスチェック処理(送信側のエラーメッセージ等)
            '-----

            '受信レスポンスを返す
            RcvRes = strRx

            '結果正常
            Return True

        Catch ex As Exception
            'エラー処理
        End Try
    End Function

End Class


突っ込みどころが満載なクラスですが、簡単なコマンドを送信し、レスポンスを受信するぐらいであれば使用できると思います。
尚、受信イベントでの処理で、受信バッファを4096に限定していますので、この辺りを改良してみて下さい。
また、レスポンスの受信データをそのまま返していますが、エラー処理などは通信先の装置に合わせて下さい。

このクラスを使用した例を以下のソースに示します。

SerialPortコントロールの使い方その2(通信用クラスの使用例)

Public Class frmSerial2

    'シリアル通信クラス
    Private mclsSerial As ClsSerial

    ''' 
    ''' フォームクローズイベント
    ''' 
    Private Sub frmSerial2_FormClosed(sender As Object, e As FormClosedEventArgs) Handles Me.FormClosed
        'シリアル通信のクローズ
        mclsSerial.Close()
    End Sub

    ''' 
    ''' フォームロードイベント
    ''' 
    Private Sub frmSerial2_Load(sender As Object, e As EventArgs) Handles Me.Load
        'シリアル通信クラスの生成
        mclsSerial = New ClsSerial(Me.SerialPort1)
        'シリアル通信のオープン
        If mclsSerial.Open() = False Then
            'オープンエラー(必要があれば処理する)
        End If
    End Sub

    ''' 
    ''' [コマンド送信]クリックイベント
    ''' 
    Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
        'レスポンス表示クリア
        Me.TextBox2.Text = ""
        'コマンド取得
        Dim strCmd As String = Me.TextBox1.Text.Trim
        If strCmd <> "" Then
            Dim strRsv As String = ""
            If Me.mclsSerial.SendCmdGetRsv(strCmd, strRsv) = True Then
                '正常受信、レスポンス表示
                Me.TextBox2.Text = strRsv
            Else
                'エラー
                Me.TextBox2.Text = "エラー発生"
            End If
        End If
    End Sub

End Class


このフォームは、送信用の文字列を入力する TextBox と、受信結果を表示する TextBox を画面に張り付け、 通信の処理をさせるボタンを張り付けてあります。

実行の様子を下図に示します。


送信する文字列を 送信コマンド: TextBox に入力し、「コマンド送信」ボタンを押下します。
「RS232Cテストツール」側でコマンドの受信を確認して、「TEXT送信」ボタンを押下しますと、 受信レスポンス: TextBox にデータが表示されます。
(尚、通信相手のシミュレーションとしてフリーソフトの「RS232Cテストツール」を使用しています。)


尚、通信相手用の仮想シリアルポートとしてcom0com というドライバを使用し、 2個のシリアルポートを作成し、ループバックで接続しています。 今回のプログラムをテストするためには外部装置か、もう一台別のPCが必要になりますが、 そんなことをしなくてもデバッグができるソフト(ドライバ)が在ります。

com0com というドライバで、PC上に仮想シリアルポートを2個作成し、 その2個をループバックできる様に仮想的に接続できるという優れものです。 以下のリンク先でソフトをダウンロードしてインストールしてみて下さい。 (尚、もう一台のPCなどがあれば必要ありませんが)

com0com ドライバ」

COM8側を通信相手側(外部装置)とするのですが、通信テスト用に以下のフリーソフトを使いました。
「のん」さんといわれる方が作成された「RS232Cテストツール」です。
以下のサイトにアクセスすれば、ダウンロードできます。

「RS232Cテストツール」

USB ⇒ RS232C 変換ケーブル

シリアル通信に関連してですが、 最近のPC、特にノートPCの場合ではRS232Cのコネクタがついていることは殆んどありません。
そのため、以下の様なUSBを使ったRS232Cの変換ケーブルが使えそうです。
iBUFFALO USBシリアルケーブル(USBtypeA to D-sub9ピン)1.0m ブラックスケルトン BSUSRC0610BS
サンワサプライ USB-RS232Cコンバータ USB-CVRS9
Plugable USB‐9ピンRS232シリアルアダプター (Prolific社製 PL2303HX Rev Dチップセット採用)


関連する記事

SerialPortコントロールの使用方法     :[SerialPort,Invoke]
SerialPortコントロールの使い方その3(外部装置からの垂れ流しデータ受信)
SerialPortコントロールの使い方その4(データ受信時にイベントを発生させる)
SerialPortコントロールの使い方その5(データ受信時及び、信号変化時にイベントを発生させる)
SerialPortコントロールの使い方その6(ハンドシェィクによるデータ送受信)

おすすめ本












PR

コメント

コメントを書く