次のコードを使用して、COMポートから値を読み取ります。
Private port As New SerialPort("COM13", 9600, Parity.None, 8, StopBits.One)
Private Sub port_DataReceived(ByVal sender As Object, ByVal e As SerialDataReceivedEventArgs)
Debug.Print(port.ReadExisting())
End Sub
Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
AddHandler port.DataReceived, New SerialDataReceivedEventHandler(AddressOf port_DataReceived)
port.Open()
End Sub
これは問題なく機能しますが、すべてのデータを取得するわけではなく、結果として1つではなく2つの文字列になります。
例として、comポートが「HELLO2YOU」という単語を送信していた場合、次のようになります。
HEL
LO2YOU
または
HELLO2
YOU
バッファをそこに配置して、表示する前にすべてのデータが読み取られていることを確認するにはどうすればよいですか?
ありがとう!
シリアルポート通信をストリーミングデータと考える必要があります。データを受信するときはいつでも、それが完全なメッセージ、部分的なメッセージのみ、または複数のメッセージである可能性があることを期待する必要があります。それはすべて、データが入ってくる速度と、アプリケーションがキューから読み取ることができる速度に依存します。したがって、バッファが必要だと考えるのは正しいことです。ただし、まだ気付いていないかもしれませんが、各メッセージが開始および終了するシリアルポートを厳密に介して知る方法はありません。これは、送信者と受信者の間で合意されたプロトコルを介して処理する必要があります。たとえば、多くの人は、標準のテキスト開始(STX)文字とテキスト終了(ETX)文字を使用して、各メッセージ送信の開始と終了を示します。そうすれば、データを受信したときに、完全なメッセージを受信したことを知ることができます。
たとえば、STXおよびETX文字を使用した場合、次のようなクラスを作成できます。
Public Class DataBuffer
Private ReadOnly _startOfText As String = ASCII.GetChars(New Byte() {2})
Private ReadOnly _endOfText As String = ASCII.GetChars(New Byte() {4})
Public Event MessageReceived(ByVal message As String)
Public Event DataIgnored(ByVal text As String)
Private _buffer As StringBuilder = New StringBuilder
Public Sub AppendText(ByVal text As String)
_buffer.Append(text)
While processBuffer(_buffer)
End While
End Sub
Private Function processBuffer(ByVal buffer As StringBuilder) As Boolean
Dim foundSomethingToProcess As Boolean = False
Dim current As String = buffer.ToString()
Dim stxPosition As Integer = current.IndexOf(_startOfText)
Dim etxPosition As Integer = current.IndexOf(_endOfText)
If (stxPosition >= 0) And (etxPosition >= 0) And (etxPosition > stxPosition) Then
Dim messageText As String = current.Substring(0, etxPosition + 1)
buffer.Remove(0, messageText.Length)
If stxPosition > 0 Then
RaiseEvent DataIgnored(messageText.Substring(0, stxPosition))
messageText = messageText.Substring(stxPosition)
End If
RaiseEvent MessageReceived(messageText)
foundSomethingToProcess = True
ElseIf (stxPosition = -1) And (current.Length <> 0) Then
buffer.Remove(0, current.Length)
RaiseEvent DataIgnored(current)
foundSomethingToProcess = True
End If
Return foundSomethingToProcess
End Function
Public Sub Flush()
If _buffer.Length <> 0 Then
RaiseEvent DataIgnored(_buffer.ToString())
End If
End Sub
End Class
また、通信プロトコルでは、送信者と受信者の間の送信中にメッセージが破損したかどうかを判断できるチェックサムバイトが一般的であることにも言及する必要があります。
これはごく普通のことで、シリアルポートは非常に遅いデバイスです。 9600のようなボーレートがあり、マシンがあまり動かなくなっていない場合、ReadExisting()を使用すると、ポートから1バイトまたは2バイトしか取得できません。 Debug.Print()はラインターミネータを出力するので、受信したものはすべて分割されて表示されます。
これを修正する最も簡単な方法は、代わりにReadLine()を使用することです。そのためには、デバイスが行の終わりに、SerialPort.NewLineプロパティ値と一致する特殊文字を送信する必要があります。これは非常に一般的ですが、改行は定型です。
そうでない場合は、他の種類のバッファリングスキームが必要になります。