Code:
Public Class FastSerialManager
Implements IDisposable
Private WithEvents mSerial As System.IO.Ports.SerialPort
Private lastData(4096) As Byte
Private mLastPointer As Integer
Private mHasData As Boolean
Private mLocalData(4096) As Byte
Private mLocalPointer As Integer
Private mDataList As List(Of String)
Private mResidual As Byte
Private disposedValue As Boolean = False ' To detect redundant calls
Private resetFlag As Boolean
Private mFailCount As Integer
Private mAlertCount As Integer
Private mAlertHolder As Integer
Private mDoneCount As Integer
'Private mCount As Integer
'The timer
Private myTimer As System.Windows.Forms.Timer
Private timeCounter As Boolean
Private myContext As System.Threading.SynchronizationContext
#Region "Constructors and Destructors"
Public Sub New()
mSerial = New System.IO.Ports.SerialPort
mSerial.BaudRate = 9600
mSerial.DataBits = 8
mSerial.StopBits = IO.Ports.StopBits.One
mSerial.Parity = IO.Ports.Parity.None
mSerial.PortName = "COM4"
mSerial.WriteTimeout = 500
mSerial.ReadTimeout = 3000
mSerial.ReadBufferSize = 4096
mSerial.ReceivedBytesThreshold = 1
myContext = System.Threading.SynchronizationContext.Current
'Need to do a few things to look for the com ports and decide which one to use.
mSerial.Open()
resetFlag = False
'Create the timer.
myTimer = New System.Windows.Forms.Timer
mLocalPointer = -1
'Need to tell it where to send time tick messages.
AddHandler myTimer.Tick, AddressOf TimerEventProcessor
myTimer.Enabled = False
myTimer.Interval = 2000
End Sub
Public Sub New(ByVal nm As String)
mSerial = New System.IO.Ports.SerialPort
mSerial.BaudRate = 9600
mSerial.DataBits = 8
mSerial.StopBits = IO.Ports.StopBits.One
mSerial.Parity = IO.Ports.Parity.None
mSerial.PortName = nm
mSerial.WriteTimeout = 500
mSerial.ReadTimeout = 500
mSerial.ReceivedBytesThreshold = 1
myContext = System.Threading.SynchronizationContext.Current
'Need to do a few things to look for the com ports and decide which one to use.
mSerial.Open()
'Create the timer.
myTimer = New System.Windows.Forms.Timer
mLocalPointer = -1
'Need to tell it where to send time tick messages.
AddHandler myTimer.Tick, AddressOf TimerEventProcessor
myTimer.Enabled = False
myTimer.Interval = 2000
End Sub
' IDisposable
Protected Overridable Sub Dispose(ByVal disposing As Boolean)
If Not Me.disposedValue Then
mSerial.Dispose()
End If
Me.disposedValue = True
myTimer.Dispose()
End Sub
#Region " IDisposable Support "
' This code added by Visual Basic to correctly implement the disposable pattern.
Public Sub Dispose() Implements IDisposable.Dispose
' Do not change this code. Put cleanup code in Dispose(ByVal disposing As Boolean) above.
Dispose(True)
GC.SuppressFinalize(Me)
End Sub
#End Region
#End Region
#Region "Properties and Events"
Public ReadOnly Property NewData() As Boolean
Get
Return mHasData
End Get
End Property
Public Event DataReady()
Public Event Alert(ByVal alertVal As Integer)
Public Event Stopped()
Public Event Failed()
#End Region
#Region "Public Functions"
Public Sub ReSet()
mSerial.Dispose()
mFailCount = 0
mSerial = New System.IO.Ports.SerialPort
mSerial.BaudRate = 9600
mSerial.DataBits = 8
mSerial.StopBits = IO.Ports.StopBits.One
mSerial.Parity = IO.Ports.Parity.None
mSerial.PortName = "COM5"
mSerial.WriteTimeout = 500
mSerial.ReadTimeout = 3000
mSerial.ReadBufferSize = 4096
'Need to do a few things to look for the com ports and decide which one to use.
mSerial.Open()
End Sub
'Pass in a buffer, and it will be filled. The return is the number of bytes read.
'The buffer should be sized to 4096.
Public Function GetData(ByRef buffHolder() As Byte) As Integer
buffHolder = CType(lastData.Clone(), Byte())
Array.Clear(lastData, 0, 4096)
End Function
'This sub mostly is used for calling sound.
Public Sub SendOutput(ByVal senNo As Byte, ByVal senVal As Byte)
Dim buf(2) As Byte
'Confirm that the escape char (92) is not included in the arguments.
If senVal = 92 Then
senVal += CByte(1)
End If
If senNo = 92 Then
RaiseEvent Failed()
Return
End If
buf(0) = 255
buf(1) = senNo
buf(2) = senVal
mSerial.Write(buf, 0, 3)
End Sub
'This assumes that senNo is a number less than 256. No checking is
'Done if that is not the case.
Public Sub ReadSensor(ByVal senNo As Byte, ByVal senVal As Byte)
Dim buf(2) As Byte
'Confirm that the escape char (92) is not included in the arguments.
If senVal = 92 Then
senVal += CByte(1)
End If
If senNo = 92 Then
'This should NEVER happen, so just fail it.
RaiseEvent Failed()
Return
End If
buf(0) = 255
buf(1) = senNo
buf(2) = senVal
mLastPointer = 0
mLocalPointer = -1
mHasData = False
Array.Clear(mLocalData, 0, 4096)
Array.Clear(lastData, 0, 4096)
mSerial.Write(buf, 0, 3)
myTimer.Enabled = True
timeCounter = True
End Sub
Public Sub ClearAlert()
Dim buf(2) As Byte
buf(0) = 255 'The flag
buf(1) = 111 'Sensor 111 just clears the alert.
buf(2) = 0 'This doesn't do anything.
mSerial.Write(buf, 0, 3)
End Sub
Public Sub WriteMove(ByVal lWhl As Byte, ByVal rWhl As Byte, ByVal dur As Byte)
Dim buf(2) As Byte
'Check to see that 92 (the escape character) is not one of the values.
If lWhl = 92 Then
lWhl += CByte(1)
End If
If rWhl = 92 Then
rWhl += CByte(1)
End If
If dur = 92 Then
dur += CByte(1)
End If
buf(0) = 255
buf(1) = 100
buf(2) = lWhl
mSerial.Write(buf, 0, 3)
buf(0) = 255
buf(1) = 101
buf(2) = rWhl
mSerial.Write(buf, 0, 3)
buf(0) = 255
buf(1) = 102
buf(2) = dur
mSerial.Write(buf, 0, 3)
End Sub
'A relic to setting addresses for the SRF08. Don't use it.
Public Sub WriteAddress(ByVal no As Byte)
Dim buf(2) As Byte
buf(0) = 255
buf(1) = 10
buf(2) = no
mSerial.Write(buf, 0, 3)
End Sub
Public Sub SendReset()
resetFlag = True
mSerial.Write("\0")
mSerial.Write("W")
resetFlag = False
End Sub
#End Region
#Region "Private Functions"
Private Sub TimerEventProcessor(ByVal myobject As Object, ByVal myEventArgs As EventArgs)
If timeCounter Then
SendReset()
myTimer.Enabled = False
RaiseEvent Failed()
mFailCount += 1
If mFailCount > 3 Then
ReSet()
End If
Else
timeCounter = True 'Set false by data received.
End If
End Sub
'This is the way data is read if the timer is not in use (no sensor read).
Private Sub DataReceivedProcessor(ByVal sender As Object, ByVal e As System.IO.Ports.SerialDataReceivedEventArgs) Handles mSerial.DataReceived
Dim inByte As Byte
Dim cnt As Integer
Dim buff(4096) As Byte
Dim x As Integer
'This is a special case, not a sensor read.
mFailCount = 0 'ANY bytes coming in means that something is happening.
timeCounter = False
'If the flag is set, bail out.
If resetFlag Then
Return
End If
cnt = mSerial.Read(buff, 0, 4096)
For x = 0 To cnt - 1
inByte = buff(x)
mLocalPointer += 1
mLocalData(mLocalPointer) = inByte
If inByte = 128 Then
mAlertCount += 1
ElseIf inByte = 255 Then
mDoneCount += 1
mAlertCount = 0
If mDoneCount = 3 Then
mDoneCount = 0
'If there is something in the alert holder, then this
'is an alert. Only the alert holder byte matters, so the three 128 bytes
'will simply be ignored.
If mAlertHolder > 0 Then
myContext.Post(AddressOf EventRaiser, mAlertHolder) 'The stop alert.
mAlertHolder = 0 'Clear the holder.
Else
'Need to strip off those final three bytes.
mLocalPointer -= 3
'Check that this didn't do something truly bizarre.
If mLocalPointer < 0 Then
mLocalPointer = 0
'This should never happen.
Windows.Forms.MessageBox.Show("A too short message was found.", "Error")
End If
lastData = CType(mLocalData.Clone, Byte())
mLastPointer = mLocalPointer
mHasData = True
myTimer.Enabled = False
myContext.Post(AddressOf EventRaiser, 255)
mLocalPointer = -1
Array.Clear(mLocalData, 0, 4096)
End If
End If
Else
If mAlertCount = 3 Then
mAlertCount = 0
mAlertHolder = CInt(inByte)
'clear these two.
End If
mAlertCount = 0
mDoneCount = 0
End If
Next
End Sub
Private Sub EventRaiser(ByVal typ As Object)
Select Case CInt(typ)
Case 255
RaiseEvent DataReady()
Case 101
RaiseEvent Stopped()
Case Else
RaiseEvent Alert(CInt(typ))
End Select
End Sub
#End Region
End Class