Issues with Serial communication on Windows

Hello,

I try to create a new application that needs to communicate with an external serial device.

On Mac OS X, no problem it works perfectly! But on Windows I don’t understand why my Application does not receive all bytes from the device.

1 - I list all serial devices
2 - I send an order to each device, one by one with a timer that let time between each order
3 - At the same time, my Serial receives bytes after the order was sent

This is the point 3 that does not work properly on Windows. I’m waiting for a string of 50 bytes, and I receive all on Mac OS X, but on Windows I receive a random number of bytes between 41 et 49 and then, nothing…

Any idea? Thank you for your help!

Without knowing how you’re doing this, I can only speak generally. With incoming data through serial ports (and other IP too) you cannot know how many bytes in advance you will receive at a time. You must have a way to note the start and end of the data that you consider to be “complete”. So you start a new string on receipt of the “start” byte, add to it with subsequent data until you see an “end” byte. Then you process that string.

If you don’t do this you can sometimes sit endlessly waiting for exactly 50 bytes. It’s completely indeterministic (for all intents and purposes) so sometimes it may work and sometimes it may not. Maybe you’re just lucky (or not at an internal threshold) on the Mac version.

Hope that helps in general terms (and apologies if I’m teaching you to suck eggs!)

Ok… I will try to explain my code.

[code]// First function that list all serial devices of the computer, and then launch a timer that will try to call them
ReDim USB.connectedDevices(-1)
dim sp As new SerialPort
For i As Integer = 0 To System.SerialPortCount-1
dim h As new Handle
h.port = new HandleSerialConnected
h.port.id = i
h.port.Baud = Serial.Baud9600
h.port.Bits = Serial.Bits8
h.port.Parity = serial.ParityNone
h.port.Stop = serial.StopBits1
h.port.SerialPort = System.SerialPort(i)
connectedDevices.Append(h)
Next

if(connectedDevices.Ubound>-1) then
askID = 0
deviceTimer.Mode = timer.ModeOff
askTimer.Mode = timer.ModeMultiple
end if[/code]

[code]// askTimer function that calls the device and update the status of its answer
if(askID<>-1) then
if(connectedDevices(askID).status=0) then // Not called
askDevices()
connectedDevices(askID).status=1

elseif(connectedDevices(askID).connected) then // Match the device we want
	askID = askID+1
					
else // Called and waiting for an answer
	if(NotRespondingTimeout=kNotRespondingTimeout)then
		askID = askID+1
		NotRespondingTimeout = 0
	else
		NotRespondingTimeout = NotRespondingTimeout +1
	end if
end if
			
if(askID =  connectedDevices.Ubound+1) then // After all devices are called
	askID = -1
end if

end if[/code]

At the same time, my Serial class is waiting for bytes from the device

[code]// DataAvailable function
serialInput = serialInput + me.ReadAll(Encodings.MacRoman) // Buffer

if(serialInput.LenB=50) then // 50 = number of bytes our device sends to us after we called it
dim b As new BinaryStream(serialInput)
dim talkID As UInt8 = b.ReadUInt8
if(talkID=1) then // The ID we are waiting for as true answer
USB.deviceFound = true
connectedDevices(id).ID.init(serialInput)
connectedDevices(id).status = 2
end if

serialInput = ""

end if[/code]

Your serial port coding isn’t valid. The DataAvailable event means there is data there. It doesn’t mean it’s ALL there, or even that more than one message isn’t there. It’s just means that there’s data there.

Here is some working code for one of my apps. In the DataAvailable event I have this:

[code]while me.BytesAvailable > 0
dim s as string = me.ReadAll
sSerialBuffer = sSerialBuffer + s
me.poll
wend

HandleDataRecieved
[/code]

In HandleDataReceived I have to parse my sSerialBuffer string. Our messages have a terminator that I can split on. 999 times out of 1000 I have whole complete messages (our serial data packets are really small), but occasionally I have a partial message that I need to keep around waiting for the end of it.

Serial coding is not very fun because of this, IMO. Once you do it a few times you get the hang of it.

Thanks Bob, I changed my code like this and it finally works :

[code]
// DataAvailable function
while me.bytesAvailable
serialInput = serialInput + me.ReadAll(Encodings.MacRoman) // Buffer
me.poll
wend

if(serialInput.LenB=50) then // 50 = number of bytes our device sends to us after we called it
dim b As new BinaryStream(serialInput)
dim talkID As UInt8 = b.ReadUInt8
if(talkID=1) then // The ID we are waiting for as true answer
USB.deviceFound = true
connectedDevices(id).ID.init(serialInput)
connectedDevices(id).status = 2
end if

serialInput = ""

end if[/code]

You may still have a problem if you have more than one message sent. You’re looking at it only if it’s exactly 50 bytes long. What happens if you have 51? It will just accumulate and it will never get processed. Does your message have a terminator sequence?

Your application, but I suspect you’ll have problems in the long run.

In my case, when I send an order I should receive only one message with a fixed size. So If I receive 51 bytes, it seems it’s not the good device :slight_smile:

My experience says that a stray character can sometimes get introduced from noise. In your current configuration you will never recover from it. But, as I said, your merry go round, your rules.

I am going to echo Bob’s advice here. I would not assume that you will always receive 50 bytes and only 50 bytes. You need a provision in your code to deal with these fringe cases. For example, what happens if the serial device loses power after sending 10 bytes, then powers back on? The next request for data and return data will be 50 bytes, but what happens to the 10 bytes you already received?

You’re dealing with four elements here; your computer, your app, the serial device and wiring in between. A lot can go wrong and you should consider building some fault tolerance into your app.

Serial programming is a different animal. You can have it working only to find out there are all these circumstances you didn’t plan for that can drive your users and yourself nuts trying to figure out why.

In addition to Bob’s and Joseph’s experiences; when I deal with a hardware device which will only communicate back once I query it, I always flush the serial port before initiating communication, I timeout the exchange in case the communication back doesn’t complete (or is absent), and I parse all the received data for what I am looking for. That way the serial channel is prepped to only receive what I know should be there, it times out if I don’t receive the entire data on the time allowed, and I can scrap the data if it is incorrect.

Hello Bob ,
we need to communicate with a frame tracer.
we are using serial class for it.
On Mac OS X, no problem it works perfectly! But on Windows my Application does not receive all bytes from the device and tracer gives Error 71.
We have used 9600 baud rate . Packet data is quite large in size.

//In data available event 
		dim s as string
		
		while me.BytesAvailable>0 
				s=me.ReadAll(Encodings.ASCII)
				bufferdata=bufferdata+s
				me.poll
		wend
		if InStr(tracedata,chr(29))<> -1 then
  If tempACKCounter = 0 Then
    
    serial1.Write( chr(6))
    serial1.write(chr(28)+"ANS=INI"+Chr(13)+Chr(10)+"STATUS=0"+Chr(13 )+Chr(10)+Chr(30)+Chr(29))
 
  ElseIf tempACKCounter = 1 Then
    If ((InStr(TextArea1.Text,trcfmt)<>0) AND (InStr(TextArea1.Text,drlfmt)<>0) AND (InStr(TextArea1.Text,zfmt)<>0)) Then
      serial1.Write( chr(6))
      serial1.write(chr(28)+"ANS=INI"+chr(13)+chr(10)+"STATUS=0"+chr(13 )+chr(10)+"DEF=""GT5000"";1234"+chr(13 )+Chr(10)+"TRCFMT=" + trcfmt+chr(13 )+chr(10)+"DRLFMT=" + drlfmt+chr(13)+chr(10)+"ZFMT="+zfmt+chr(13)+chr(10) +chr(30)+chr(29))
      
    End if
  ElseIf tempACKCounter = 2 Then
    
    
    serial1.Write( chr(6))
    serial1.write(chr(28)+"ANS=1234"+chr(13)+chr(10)+Mid(TextArea1.Text,Instr(TextArea1.Text,"JOB="),14)+Chr(13)+Chr(10)+"STATUS=0"+chr(13)+chr(10)+chr(30)+chr(29))
  
     ElseIf tempACKCounter = 3 Then
    
    serial1.Write( Chr(6))
    serial1.write(Chr(28)+"ANS=1234"+Chr(13)+Chr(10)+Mid(TextArea1.Text,Instr(TextArea1.Text,"JOB="),14)+Chr(13)+Chr(10)+"STATUS=0"+Chr(13)+Chr(10)+Chr(30)+Chr(29))

    End if
  tempACKCounter = tempACKCounter + 1
  end if
  
  If tempACKCounter <> 3 Then
    TextArea1.Text = TextArea1.text+tracedata
  End if
  If tempACKCounter = 3 Then
    TextArea1.Text =tracedata
  End if
  

In case when tempACKCounter=2 , Tracer gives error 71.
we think that it might be due to tracer does not receive packet from Host within time (12 secs) and it aborts session.
Thanks in advance.

Hello @Bob Keeney, @Sébastien Angot ,
we need to communicate with a frame tracer.
we are using serial class for it.
On Mac OS X, no problem it works perfectly! But on Windows my Application does not receive all bytes from the device and tracer gives Error 71.
We have used 9600 baud rate . Packet data is quite large in size.

//In data available event 
		dim s as string
		
		while me.BytesAvailable>0 
				s=me.ReadAll(Encodings.ASCII)
				bufferdata=bufferdata+s
				me.poll
		wend
		if InStr(tracedata,chr(29))<> -1 then
  If tempACKCounter = 0 Then
    
    serial1.Write( chr(6))
    serial1.write(chr(28)+"ANS=INI"+Chr(13)+Chr(10)+"STATUS=0"+Chr(13 )+Chr(10)+Chr(30)+Chr(29))
 
  ElseIf tempACKCounter = 1 Then
    If ((InStr(TextArea1.Text,trcfmt)<>0) AND (InStr(TextArea1.Text,drlfmt)<>0) AND (InStr(TextArea1.Text,zfmt)<>0)) Then
      serial1.Write( chr(6))
      serial1.write(chr(28)+"ANS=INI"+chr(13)+chr(10)+"STATUS=0"+chr(13 )+chr(10)+"DEF=""GT5000"";1234"+chr(13 )+Chr(10)+"TRCFMT=" + trcfmt+chr(13 )+chr(10)+"DRLFMT=" + drlfmt+chr(13)+chr(10)+"ZFMT="+zfmt+chr(13)+chr(10) +chr(30)+chr(29))
      
    End if
  ElseIf tempACKCounter = 2 Then
    
    
    serial1.Write( chr(6))
    serial1.write(chr(28)+"ANS=1234"+chr(13)+chr(10)+Mid(TextArea1.Text,Instr(TextArea1.Text,"JOB="),14)+Chr(13)+Chr(10)+"STATUS=0"+chr(13)+chr(10)+chr(30)+chr(29))
  
     ElseIf tempACKCounter = 3 Then
    
    serial1.Write( Chr(6))
    serial1.write(Chr(28)+"ANS=1234"+Chr(13)+Chr(10)+Mid(TextArea1.Text,Instr(TextArea1.Text,"JOB="),14)+Chr(13)+Chr(10)+"STATUS=0"+Chr(13)+Chr(10)+Chr(30)+Chr(29))

    End if
  tempACKCounter = tempACKCounter + 1
  end if
  
  If tempACKCounter <> 3 Then
    TextArea1.Text = TextArea1.text+tracedata
  End if
  If tempACKCounter = 3 Then
    TextArea1.Text =tracedata
  End if
  

In case when tempACKCounter=2 , Tracer gives error 71.
we think that it might be due to tracer does not receive packet from Host within time (12 secs) and it aborts session.
Thanks in advance.[/quote]

Are you using a Serial to USB Converter anywhere in the mix? We had problems in Windows, a few years ago with a Prolific converter where the drivers were simply garbage. Try either finding up to date drivers or a different converter.

After that, I have no suggestions. All our data packets were tiny.