how do i make CF 2 serialport talk to VB6 MSComm32 in VB.NET desktop app

  • Thread starter davewilliamson55555
  • Start date
D

davewilliamson55555

I have an existing VB.NET 2003 app that uses the vb6 mscomm32.ocx
control to interface with the serial port on the desktop. I can't seem
to figure out how to code the compactframework 2 VB.NET 2005 app
running on Windows Mobile 2005 to talk reliably to the desktop.

What I am finding is that when the message "NEXT:{255}" (where {255} is
actually the character 255) is sent to the Windows Mobile device the
Mobile Device is not always seeing the message. I have confirmed using
SysInternals PortMon that the data is being sent by the PC app to the
port. And the app will get the message a random number of times
successfully. The cycle is the Windows Mobile app sends a data record
to the desktop and the desktop replies with the NEXT when it is ready
for another data record. However, after 15 or 20 cycles the Windows
Mobile app acts as if nothing came across the wire. Sometimes it runs
for 400 to 500 cycles before getting the condition. Sometimes it never
reads the 1st NEXT coming back.

My guess is that depending on what character(s) happen to be in the
inbuffer on the Windows Mobile port at the time of reading is the
problem ... I suspect that reading just a nonprintable character
somehow the serialport control throws it out before passing it along to
it's stream. By adding some junk text to the NEXT message
"NEXT:{1}this is junk to fill space{255}" the cycles ran without fail
up to 3000 cycles. My guess is the longer data meant less of a chance
that only the character 255 would be in the inbuffer for a read.

Can anyone help me get this new serialport control talking to the old
one?

The desktop app sets up the port this way:

With MSComm1
.CommPort = CType(portNum, Short)
.Handshaking = MSCommLib.HandshakeConstants.comRTS
.InBufferSize = 4096
'.InputLen = 0
.InputMode = MSCommLib.InputModeConstants.comInputModeText
.OutBufferSize = 4096
.RThreshold = 1
.Settings = "57600,N,8,1"
.PortOpen
End With

The desktop app reads data off the port this way:

Private Sub MSComm1_OnComm1(ByVal sender As System.Object, ByVal e As
System.EventArgs) Handles MSComm1.OnComm
Try
Dim dataBuffer As System.String = CType(MSComm1.Input,
System.String)
comData &= dataBuffer
Catch ex As Exception
Call LogException(ex)
MsgBox(ex.Message)
End Try
End Sub

The desktop app writes data to the port this way (where term is the
character 255):

MSComm1.Output = HELLO & term


The Windows Mobile app sets up the port this way:

With SerialPort1
.PortName = "COM1" 'windows mobile 5
.Handshake = IO.Ports.Handshake.RequestToSend
.Encoding = System.Text.Encoding.ASCII
.ReadTimeout = 500
.WriteTimeout = 500
.ReadBufferSize = 4096
.WriteBufferSize = 4096
.ReceivedBytesThreshold = 1
.BaudRate = 57600
.Parity = IO.Ports.Parity.None
.DataBits = 8
.StopBits = IO.Ports.StopBits.One
.DiscardNull = True
.Open()
End With

The Windows Mobile app reads data from the port this way (note comdata
is a stringbuilder):

Private Sub ReadFromPort()
Try
Dim b(SerialPort1.ReadBufferSize) As System.Byte
Dim numOfBytesRead As System.Int32 = SerialPort1.Read(b, 0,
b.Length)
If numOfBytesRead > 0 Then
comData.Append(BytesToString(b))
End If
Catch
End Try
End Sub

Private Function BytesToString(ByVal inBytes() As System.Byte) As
System.String
Dim s As System.String = ""
Try
Dim sa As System.Text.StringBuilder = New
System.Text.StringBuilder(inBytes.Length)
Dim i As System.Int32 = 0
For Each b As System.Byte In inBytes
If inBytes(i) > 0 Then 'skip null chars
sa.Append(Chr(inBytes(i)))
End If
i += 1
Next
s = sa.ToString
Catch ex As Exception
MsgBox("BytesToString - " & ex.Message)
End Try
Return s
End Function

The Windows Mobile app writes data to the port this way:

Private Sub WriteToPort(ByVal dataString As System.String)
Try
Dim b() As System.Byte = ToBytes(dataString)
SerialPort1.Write(b, 0, b.Length)
Catch
End Try
End Sub

Private Function ToBytes(ByVal inString As System.String) As Byte()
Dim b() As System.Byte = Nothing
Try
ReDim b(inString.Length - 1)
Dim ca() As System.Char = inString.ToCharArray
Dim i As System.Int32 = 0
For Each c As System.Char In ca
b(i) = Asc(c)
i += 1
Next
Catch ex As Exception
MsgBox("ToBytes - " & ex.Message)
End Try
Return b
End Function
 
D

Dick Grier

Hi,

There are only a couple of small problems that I see with your code.

First, Dim b(SerialPort1.ReadBufferSize) As System.Byte
should be (IMO):

Dim b(SerialPort1.ReadBufferSize) As Int16

A Byte may not be sufficient -- obviously, if the chunks of data your
desktop app is sending is less than 254 bytes, then this won't be an issue.
However, if your desktop app streams data (where the chunks become
continuous data, without appreciable time between "chunks"), then you may
not receive all of the data available.

Second, your code that converts data from the receive Byte array into a
string will be slow. Using a Stringbuilder DOES NOT speed up this
process -- in fact, it actually is slightly slower than simply using a
String.

The most interesting question is, "What do you do with the the string "s"?
Is this read via some sort of synchronous call? Realize that the
DataReceived even is generated in the background thread context of the
SerialPort object. This background (free) thread is NOT synchronized with
the STAThread of the UI where I suspect that you are examining the content
of the buffer "s". Thus, buffer "s" may be changing value at the same time
that you are accessing it from your other code -- the result may be a
partial message that causes you to thing that then entire message was
missed, when, in fact, it simply was discarded accidentially. You can use
SyncLock or Monitor to synchronize these two threads. However.... A more
reliable method (IMO, based on much practice) is to dispense with the
DataReceived event, and to read data in a Windows fomrs Timer_Tick event.
This transfers all reading to the STAThread, thus no synchronization is
required.

Dick

--
Richard Grier, MVP
Hard & Software
Author of Visual Basic Programmer's Guide to Serial Communications, Fourth
Edition,
ISBN 1-890422-28-2 (391 pages, includes CD-ROM). July 2004, Revised March
2006.
See www.hardandsoftware.net for details and contact information.
 
D

davewilliamson55555

Dick,

Thanks for your time.

Regarding the use of Int16 versus Byte. All characters tranmitted are
ascii 1 through 255. Thus a byte will hold the chr(255). And what I'm
seeing on SysInternals PortMon is hex FF so chr(255) is properly making
it to the outgoing desktop port with good bit alignment. But since I
wasn't sure if the mscomm32 control was doing something funky with the
nonprintable chars I switched to binary mode.

I also suspected that the background threading by using the
DataReceived event might be corrupting the string buffer my program was
using so I stopped using the event and used a timer. And I also
suspected the CF 2 serialport control was not properly reading the data
.... because there are comments all through the .Read* methods stating
that if receiving text and binary data intermixed then it is best to
read the data directly and decode it manually.

So the code (post 2 AM this morning) looks like the following but still
shows the same results.

The desktop app sets up the port this way:

With MSComm1
.CommPort = CType(portNum, Short)
.Handshaking = MSCommLib.HandshakeConstants.comRTS
.InBufferSize = 4096
.InputLen = 0
.InputMode = MSCommLib.InputModeConstants.comInputModeBinary
.NullDiscard = True
.OutBufferSize = 4096
.RThreshold = 0
.Settings = "57600,N,8,1"
Try
.PortOpen = True
End With

The desktop app reads data off the port this way via a timer or within
other inline code loops (comdata is a string not a textbuilder on the
desktop side):

Private Sub ReadFromPort()
Try
If MSComm1.InBufferCount = 0 Then Exit Sub
Dim b(MSComm1.InBufferSize) As System.Byte
b = CType(MSComm1.Input, System.Byte())
If Not b Is Nothing Then
comData &= BytesToString(b)
End If
Catch
End Try
End Sub

Private Function BytesToString(ByVal inBytes() As System.Byte) As
System.String
'using a stringbuilder here needs to change to a string once the
protocol is figured out
Dim s As System.String = ""
Try
Dim sa As System.Text.StringBuilder = New
System.Text.StringBuilder(inBytes.Length)
Dim i As System.Int32 = 0
For Each b As System.Byte In inBytes
If inBytes(i) > 0 Then 'skip null chars
sa.Append(Chr(inBytes(i)))
End If
i += 1
Next
s = sa.ToString
Catch ex As Exception
Call LogException(ex)
MsgBox(ex.Message)
End Try
Return s
End Function


The desktop app writes data to the port this way (datastring can
contain any asii character from 1 to 255):

Private Sub WriteToPort(ByVal dataString As System.String)
Try
Dim b() As System.Byte = ToBytes(dataString)
If Not b Is Nothing Then
MSComm1.Output = b
End If
Catch
End Try
End Sub

Private Function ToBytes(ByVal inString As System.String) As Byte()
Dim b() As System.Byte = Nothing
Try
ReDim b(inString.Length - 1)
Dim ca() As System.Char = inString.ToCharArray
Dim i As System.Int32 = 0
For Each c As System.Char In ca
b(i) = CType(Microsoft.VisualBasic.Asc(c), System.Byte)
i += 1
Next
Catch ex As Exception
Call LogException(ex)
MsgBox(ex.Message)
End Try
Return b
End Function


The Windows Mobile app sets up the port this way:

With SerialPort1
.DiscardNull = True
.PortName = "COM1" 'windows mobile 5
.Handshake = IO.Ports.Handshake.RequestToSend
.Encoding = System.Text.Encoding.UTF8
.ReadTimeout = 5000
.WriteTimeout = 5000
.ReadBufferSize = 4096
.WriteBufferSize = 4096
.ReceivedBytesThreshold = 1
.BaudRate = 57600
.Parity = IO.Ports.Parity.None
.DataBits = 8
.StopBits = IO.Ports.StopBits.One
.Open()
End With

The Windows Mobile app reads data from the port (using a timer or
inline with other code) this way (note comdata is a stringbuilder):

Private Sub ReadFromPort()
Try
Dim b2r As Integer = SerialPort1.BytesToRead
If b2r = 0 Then Exit Sub
Dim b(b2r) As System.Byte
Dim numOfBytesRead As System.Int32 = SerialPort1.Read(b, 0,
b.Length)
If numOfBytesRead > 0 Then
Dim s As System.String = BytesToString(b)
comData.Append(s)
End If
Catch
End Try
End Sub

Private Function BytesToString(ByVal inBytes() As System.Byte) As
System.String
Dim s As System.String = ""
Try
Dim sa As System.Text.StringBuilder = New
System.Text.StringBuilder(inBytes.Length)
Dim i As System.Int32 = 0
For Each b As System.Byte In inBytes
If inBytes(i) > System.Convert.ToByte(0) Then 'skip null chars
sa.Append(System.Convert.ToChar(inBytes(i)))
End If
i += 1
Next
s = sa.ToString
Catch ex As Exception
MsgBox("BytesToString - " & ex.Message)
End Try
Return s
End Function

The Windows Mobile app writes data to the port this way:

Private Sub WriteToPort(ByVal dataString As System.String)
Try
Dim b() As System.Byte = ToBytes(dataString)
SerialPort1.Write(b, 0, b.Length)
Catch
End Try
End Sub

Private Function ToBytes(ByVal inString As System.String) As Byte()
Dim b() As System.Byte = Nothing
Try
ReDim b(inString.Length - 1)
Dim ca() As System.Char = inString.ToCharArray
Dim i As System.Int32 = 0
For Each c As System.Char In ca
b(i) = Asc(c)
i += 1
Next
Catch ex As Exception
MsgBox("ToBytes - " & ex.Message)
End Try
Return b
End Function
 
D

davewilliamson55555

Dick,

Let me do some more work based on your IMO comments. I forgot about
the stop bit and I think that is why you stated data less than chr 254.
 
D

davewilliamson55555

Dick,

I'm not sure why you stated less than chr 254. I implemented the 2
byte so I could handle future double byte chars (I think that is why
you were suggesting Int16).

The desktop app sets up the port this way:

With MSComm1
.CommPort = CType(portNum, Short)
.Handshaking = MSCommLib.HandshakeConstants.comRTS
.InBufferSize = 4096
.InputLen = 0
.InputMode = MSCommLib.InputModeConstants.comInputModeBinary
.OutBufferSize = 4096
.RThreshold = 0
.Settings = "57600,N,8,1"
.PortOpen = True
End With

The desktop app reads data off the port this way:

Private Sub ReadFromPort()
Try
If MSComm1.InBufferCount = 0 Then Exit Sub
Dim b(MSComm1.InBufferSize) As System.Byte
b = CType(MSComm1.Input, System.Byte())
If b.Length > 0 Then

'we have to keep our own internal byte buffer in case the port
reads 1 byte of a 2 byte char but not the 2nd byte
'until a later read ... totally depends on how the bytes come
across the wire in relation to when we read off wire.
Dim numOfRead As System.Int32 = b.Length
Dim bl As System.Int32 = 0
If Not inBuffer Is Nothing Then bl = inBuffer.Length
ReDim Preserve inBuffer(bl + numOfRead - 1) 'expand our buffer
b.CopyTo(inBuffer, bl)
If inBuffer.Length Mod 2 = 0 Then 'don't process a group of
bytes until we have pairs of bytes
Dim s As System.String = BytesToString(inBuffer)
comData &= s
inBuffer = Nothing
End If
End If
Catch
End Try
End Sub

Private Function BytesToString(ByVal inBytes() As System.Byte) As
System.String
Dim s As System.String = ""
Try
Dim enc As System.Text.UnicodeEncoding = New
System.Text.UnicodeEncoding
s = enc.GetString(inBytes)
Catch ex As Exception
Call LogException(ex)
MsgBox(ex.Message)
End Try
Return s
End Function

The desktop app writes data to the port this way:

Private Sub WriteToPort(ByVal dataString As System.String)
Try
Dim b() As System.Byte = ToBytes(dataString)
If Not b Is Nothing Then
MSComm1.Output = b
End If
Catch
End Try
End Sub

Private Function ToBytes(ByVal inString As System.String) As
System.Byte()
Dim b() As System.Byte = Nothing
Try
Dim enc As System.Text.UnicodeEncoding = New
System.Text.UnicodeEncoding
b = enc.GetBytes(inString)
Catch ex As Exception
Call LogException(ex)
MsgBox(ex.Message)
End Try
Return b
End Function


The Windows Mobile app sets up the port this way:

With SerialPort1
.PortName = "COM1"
.Handshake = IO.Ports.Handshake.RequestToSend
.Encoding = System.Text.Encoding.UTF8
.ReadTimeout = 5000
.WriteTimeout = 5000
.ReadBufferSize = 4096
.WriteBufferSize = 4096
.ReceivedBytesThreshold = 1
.BaudRate = 57600
.Parity = IO.Ports.Parity.None
.DataBits = 8
.StopBits = IO.Ports.StopBits.One
.Open()
End With

The Windows Mobile app reads data from the port this way:

Private Sub ReadFromPort()
Try
Dim b2r As Integer = SerialPort1.BytesToRead
If b2r = 0 Then Exit Sub
Dim b(b2r - 1) As System.Byte

Dim numOfRead As System.Int32 = SerialPort1.Read(b, 0, b2r)
If numOfRead > 0 Then

'we have to keep our own internal byte buffer in case the port
reads 1 byte of a 2 byte char but not the 2nd byte
'until a later read ... totally depends on how the bytes come
across the wire in relation to when we read off wire.
Dim bl As System.Int32 = 0
If Not inBuffer Is Nothing Then bl = inBuffer.Length
ReDim Preserve inBuffer(bl + numOfRead - 1) 'expand our buffer
b.CopyTo(inBuffer, bl)
If inBuffer.Length Mod 2 = 0 Then 'don't process a group of
bytes until we have pairs of bytes
Dim s As System.String = BytesToString(inBuffer)
comData.Append(s)
inBuffer = Nothing
End If
End If
Catch argumentnullexception As ArgumentNullException
ShowInBuffer("ReadFromPort argument null error.")
Catch invalidoperationex As InvalidOperationException
ShowInBuffer("ReadFromPort invalid operation error.")
Catch argumentoutofrangeex As ArgumentOutOfRangeException
ShowInBuffer("ReadFromPort argument out of range error.")
Catch argumentex As ArgumentException
ShowInBuffer("ReadFromPort argument error.")
Catch timeoutex As TimeoutException
ShowInBuffer("ReadFromPort read timeout.")
Catch ex As Exception
ShowInBuffer("ReadFromPort error.")
End Try
End Sub

Private Function BytesToString(ByVal inBytes() As System.Byte) As
System.String
Dim s As System.String = ""
Try
Dim enc As System.Text.UnicodeEncoding = New
System.Text.UnicodeEncoding
s = enc.GetString(inBytes, 0, inBytes.Length)
Catch ex As Exception
MsgBox("BytesToString - " & ex.Message)
End Try
Return s
End Function

The Windows Mobile app writes data to the port this way:

Private Sub WriteToPort(ByVal dataString As System.String)
Try
Dim b() As System.Byte = ToBytes(dataString)
SerialPort1.Write(b, 0, b.Length)
Catch argumentnullexception As ArgumentNullException
ShowOutBuffer("WriteToPort argument null error.")
Catch invalidoperationex As InvalidOperationException
ShowOutBuffer("WriteToPort invalid operation error.")
Catch argumentoutofrangeex As ArgumentOutOfRangeException
ShowOutBuffer("WriteToPort argument out of range error.")
Catch argumentex As ArgumentException
ShowOutBuffer("WriteToPort argument error.")
Catch timeoutex As TimeoutException
ShowOutBuffer("WriteToPort write timeout.")
Catch ex As Exception
ShowOutBuffer("WriteToPort error.")
End Try
End Sub

Private Function ToBytes(ByVal inString As System.String) As
System.Byte()
Dim b() As System.Byte = Nothing
Try
ReDim b((inString.Length * 2) - 1)
Dim n As System.Int32 = inString.Length - 1
For i As System.Int32 = 0 To n
Dim thisB() As System.Byte =
System.BitConverter.GetBytes(inString.Substring(i, 1).ToCharArray(0))
b(i * 2) = thisB(0)
b((i * 2) + 1) = thisB(1)
Next
Catch ex As Exception
MsgBox("ToBytes - " & ex.Message)
End Try
Return b
End Function


Testing so far shows no hang up like before and the speed smokes any
previous attempts.

If you see any places for improvement please comment.
 
D

Dick Grier

Hi Dave,

I suggested Int16 because a message may be more than 255 bytes in length,
not because of the value of any individual byte.

If your data mixes ASCII text and binary data (as it does), you cannot use a
string to buffer any portion of it. You should use an array of type Byte
(as in your Read code). Then, process the data out of that array...
Perhaps something like this "air code:"

Dim ImFinished As Boolean

For I As Integer = 0 to InBytes.GetUpperBound(0)
If InBytes(I) > &H7F Then
'InBytes(I) is binary
If InBytes(I) = &HFF Then
ImFinished = True
End If
Else
ActualString += Chr(InBytes(I)
End If

If ImFinished = True Then
Process (ActualString)
Else
'there is more data yet to come...
End If

This code assumes that ActualString is scoped (Private, probably) such that
it is retained between calls. In the Process routine, you must make sure to
clean up ActualString (ActualString = "") so that it is in the correct state
for subsequent calls.

Dick

--
Richard Grier, MVP
Hard & Software
Author of Visual Basic Programmer's Guide to Serial Communications, Fourth
Edition,
ISBN 1-890422-28-2 (391 pages, includes CD-ROM). July 2004, Revised March
2006.
See www.hardandsoftware.net for details and contact information.
 
D

Dick Grier

fragment correction:

ActualString += Chr(InBytes(I))

--
Richard Grier, MVP
Hard & Software
Author of Visual Basic Programmer's Guide to Serial Communications, Fourth
Edition,
ISBN 1-890422-28-2 (391 pages, includes CD-ROM). July 2004, Revised March
2006.
See www.hardandsoftware.net for details and contact information.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top