忍者ブログ

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

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

SerialPortコントロールの使い方その3(外部装置からの垂れ流しデータ受信)

前回のSerialPortコントロールを使い方その2では、 最初にこちら側からデータを送信し、その後で装置側からのデータ受信を行うことを想定していました。
SerialPortコントロールの使い方その2

しかし、外部装置でもコマンドのハンドシェイクを行わないで、常にデータを送信してくるものもあります。
表現としては汚いのですが、いわゆる「垂れ流し」的にデータが向こうからやってくる場合があります。 今回はこの様な場合を想定した「垂れ流し」受信専門のクラスを作成してみます。
「その2」のクラスと初期化・終了処理は全く同様ですが、今回はデータ受信時のイベント処理が変更されています。 クラスの概要は以下の通りです。

  • 外部装置との通信は調歩同期とし、Shift-JIS文字列での送受信を対象とする。
  • 通信ポート設定は「ボーレート:9600BPS、データビット:8、パリティビット:無し、ストップビット:1」とする。
  • 送受信文字列の開始コードは「STX:0x02」、終了コードは「ETX:0x03」とする。
  • 外部装置からのデータは常に「STX」「ETX」の間にデータがはさまれているとする。
  • 受信文字列は4096バイトのバッファで行う。

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

Imports System.IO.Ports

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

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

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

    ''' 
    ''' STX受信フラグ(装置の送信が開始された証拠)
    ''' 
    Private fSTX As Boolean = False

    ''' 
    ''' 最終受信文字列(受信データからSTX,ETXを除いたもの)
    ''' 
    Private strLastRxData As String = ""

    ''' 
    ''' 送受信文字列の開始コード(STX:0x02)
    ''' 
    Private Const CMD_STX As Byte = &H2

    ''' 
    ''' 送受信文字列の終了コード(ETX:0x03)
    ''' 
    Private Const CMD_ETX As Byte = &H3


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

    ''' -----------------------------------------------------------------------
    ''' 
    ''' 最終受信データプロパティ
    ''' 
    ''' 受信データ文字列
    ''' -----------------------------------------------------------------------
    ReadOnly Property LastRxData As String
        Get
            Return Me.strLastRxData
        End Get
    End Property

    ''' -----------------------------------------------------------------------
    ''' 
    ''' ポートオープン
    ''' 
    ''' 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

    ''' -----------------------------------------------------------------------
    ''' 
    ''' データ受信ハンドラ
    ''' 
    ''' 
    ''' [STX]から始まるデータをコマンドバッファに格納し[ETX]受信時に文字列に変換
    ''' 
    ''' -----------------------------------------------------------------------
    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 - 1) As Byte
            '読込
            SP.Read(Buf, 0, DataLen)
            '
            '受信バッファを先頭からチェックする
            For i As Integer = 0 To Buf.Length - 1
                Select Case Buf(i)
                    Case CMD_STX
                        If Me.fSTX = True Then
                            '再度のSTXはコマンドバッファをクリア
                            Me.CmdBuf = Nothing
                        End If
                        'STXフラグON
                        Me.fSTX = True

                    Case CMD_ETX
                        If Me.fSTX = True Then
                            'STXフラグONの場合
                            'コマンドバッファを文字列変換
                            Me.strLastRxData = _
                                System.Text.Encoding.GetEncoding(932).GetString(Me.CmdBuf)
                            'STXフラグOFF
                            Me.fSTX = False
                        End If
                        'コマンドバッファクリア
                        Me.CmdBuf = Nothing

                    Case Else
                        '[STX][ETX]以外
                        If Me.fSTX = True Then
                            'STXフラグONの場合
                            Dim intIdx As Integer = 0
                            If Me.CmdBuf Is Nothing Then
                                '格納バッファの領域確保
                                Me.CmdBuf = Array.CreateInstance(GetType(Byte), 1)
                                '先頭指定
                                intIdx = 0
                            Else
                                '後ろに追加する
                                Dim Length As Integer = Me.CmdBuf.Length
                                ReDim Preserve Me.CmdBuf(Length)
                                '最後尾指定
                                intIdx = Length
                            End If
                            '1文字コピー
                            Me.CmdBuf(intIdx) = Buf(i)
                        End If
                End Select
            Next

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

End Class


データ受信ハンドラでは、「STX」を受信するまでは受信されたデータを対象としていません。 「STX」受信でフラグをONし、そのフラグを見てONしていれば受信されたデータを対象とします。 「ETX」受信でも「STX」受信フラグがOFFであれば、「ETX」の処理は行いません。

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

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

Public Class frmSerialRcv

    'シリアル通信クラス
    Private mclsSerialRcv As ClsSerialRcv

    '前回受信データ
    Private mstrLastData As String

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

    ''' 
    ''' フォームロードイベント
    ''' 
    Private Sub frmSerialRcv_Load(sender As Object, e As EventArgs) Handles Me.Load
        'シリアル通信クラスの生成
        mclsSerialRcv = New ClsSerialRcv(Me.SerialPort1)
        'シリアル通信のオープン
        If mclsSerialRcv.Open() = False Then
            'オープンエラー(必要があれば処理する)
        End If
        'タイマ設定
        Me.Timer1.Interval = 100    '100msec
        Me.Timer1.Enabled = True
    End Sub

    ''' 
    ''' 100msec毎のタイマ処理
    ''' 
    Private Sub Timer1_Elapsed(sender As Object, e As Timers.ElapsedEventArgs) Handles Timer1.Elapsed
        '最終受信データ取得
        Dim strLastData As String = Me.mclsSerialRcv.LastRxData
        '前回処理データとの比較
        If strLastData <> Me.mstrLastData Then
            'データ表示
            Me.RichTextBox1.Text &= strLastData & vbCrLf
            '前回値へ退避
            Me.mstrLastData = strLastData
        End If
    End Sub
End Class


このフォームは、シリアルコントロールとタイマコントロール及び、受信データ表示用のリッチテキストボックスを画面に張り付けてあります。
タイマコントロールのイベントは100msec毎に発生させて、受信データが変化したかを判定し、変化が在った場合にリッチテキストボックスに表示しています。

通信相手のシミュレーションとしてフリーソフトの「RS232Cテストツール」を使用しています。 VBのプログラムを実行後、「RS232Cテストツール」を起動します。「RS232Cテストツール」で「起動」ボタン押下後、 「送信TEXT」のボックスに送信文字列を入力し、「TEXT送信」ボタン押下で文字列が送信されます。


上図は以下の文字列を送信しています。

  • <STX>DATA=1001<ETX>
  • <STX>DATA=2002<ETX>
  • <STX>DATA=3003<ETX>

尚、特殊な例として以下の文字列を送信すると(後ろの ETX を送信していません)

  • <STX>DATA=4004<ETX><STX>DATA=5555

内部的に「DATA=5555」が保留されています。

その後、 ETX のみを送信すると、最後のデータが表示されます。

  • <ETX>

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

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

com0com ドライバ」

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

「RS232Cテストツール」

関連する記事

SerialPortコントロールの使用方法     :[SerialPort,Invoke]
SerialPortコントロールの使い方その2
SerialPortコントロールの使い方その4(データ受信時にイベントを発生させる)

おすすめ本

PR

コメント

コメントを書く