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…
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!)
[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
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
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.
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.