Trying to use TComPort in Delphi XE.
I have a string which I send to my device and I then expect 2 responses from that device. The first one always contains 10 bytes (characters) and the second one is of a variable length up to maybe 1000 characters…
To send the message I use WriteStr - eg ComPort1.WriteStr(Msg) where Msg is a string variable containing my message.
Because I know i'm expecting 10 bytes back I then immediately call ComPort1.ReadStr(Reply, 10). I have set my timeouts according and I believe they are working ok. My ReadTotalConstant is set to 2000 and ReadTotalMultiplier is set to 1.
The problem I am having is even though ComPort1.ReadStr does return 10 as a result, the 10 bytes stored in the Reply string contains garbage. I know that data coming back is correct and I can see it using a serial port monitor as being correct and what I expect.
If I debug the code, setting a break point on the ComPort1.ReadStr always allows my result to return correctly. ie tracing through the code always returns the correct result. So to me, it seems to be a timing issue as that is the only difference I can see. The wait that occurs during debugging gives the buffer enough time to read in what it needs.
Any thoughts on how I can do this. Adding sleeps between the write and read doesn't work. Executing in a thread doesn't resolve it.
Sounds like a 'FlowContol' issue to me.
Try setting FlowControl to NONE.
Thanks, but unfortunately I already had FlowControl set to None - the default.
I meant to post my code before, but here it is:
msg := #1'010101'#2'05F259'#3;
c := ComPort1.ReadStr(s, 10);
if c = 10 then
memo1.lines.add('Non Data Packet: '+s);
I might have to work around it. This would have just been a neat option…
in the statement
c := ComPort1.ReadStr(s, 10);
s is the buffer;
10 is the max length of the buffer
ReadStr returns characters to the 's' buffer with a max number of chars is 10
It does NOT wait for 10 characters AND THEN return.
It checks to see if there is anything and returns a maximum of 10 characters.
Thanks. That makes a lot of sense. Although I don't see the point of the Timeouts then. Does it always wait the timeout period before returning (after calling ReadStr). What does the timeouts wait for? I guess what I'm after is a way to wait until I've received 10 bytes (which is also always marked by a ETX char) or timeout after a period of time if I haven't received 10 bytes - or the ETX character at the 10th position.
But, looking at the code, it implements the TComThread so I am curious why putting a sleep between the write and the read didn't allow the TComThread to receive the data before attempting the read. Unless my SyncMethod prevented it from working this way.
If I create an instance of TComPort in a secondary thread, then do all my reading/writing within the context of that main thread will this cause additional problems? That way I can handle the timings a bit easier from my own side such as throwing in sleeps and WaitForSingleObject/Mutexes etc calls. Does TComPort work ok like this? I've played around a bit and not sure - particularly with the SyncMethod. Once I have received a valid 10 byte input then I use TDataPacket to retrieve the next variable length response.
Using the comport is mostly a matter of the particular application required.
But in general I think you should look at the events provided, for example OnReceiveChar, if you have fixed length strings and or fixed start/end characters consider using the Packet Handler component.
My most recent application, inputs long strings of indeterminate length, and I do this by calling the OnReceiveChar, and in this call ReadString(S, 250) 250 is much larger than the maximum string expected, as my application is not multithreaded, I cannot rely on the actual packet size being constant (it isnt). I handle the protocol by building the string and processing post download.
Presume you are using the new version 4.1? dont use the Beta. and look at the help files that come with Version 3
Good luck with using it multithreaded and creating your own instances!
Timeouts relate to the events, But you are correct about the usefulness, just leave them at the defaults.
Timeouts has more to do with waiting for flow control like DTR/RTS signaling.
The default model for receiving is already threaded, creating a TComport in a thread is redundant.
Use COMDatapackets to receive known expected/data strings;
Or OnRxChar or OnRxBuf event and buffer the received chars in your own processing.
Yep. I have used DataPackets before. In fact I utilise that in a section of the code after I attempt to read this first 10 bytes. The timeouts as explained above make sense to me now - or rather what they're for. DataPackets are perfect really for the way I am reading the data. All packets - I always receive two for each Write - start with an SOH (#1) and finish with a ETX (#3).
My question though is if I use TComPort in the main thread and say link it with a TDataPacket, if I send a WriteStr command but want to notify the user after a period of time that I didn't receive the two expected packets, how could I achieve that without blocking the main thread?
msg := #1'010101'#2'05F259'#3;
After the WriteStr, ideally I would like the application to continue normal processing - ie functional - while reading the received bytes as they come in. If after (say) 2 seconds I haven't received both packets, a show an error and/or try the write again. During that 2 seconds I'm waiting, theoretically the user could send through a different write string so I want to be able to queue them. While I'm not after you to write the code for me, I am curious how that could be achieved without threading. That is the only reason I was looking at threading. I have legacy apps which use this TComPort component (I am using v4.11) without threading and they work perfectly outside of causing blocking issues while I wait for the responses to come back. ie I just just effectively run a loop with Application.ProcessMessages in it (in basic terms). So putting all that in a thread seems to make more sense to me going forward.
Thanks for your time. I tend to only open my Comport while it's required then close it again
<wait for response or timeout>
I might need to change that to keep the port open for basically the whole time the application is open or provide an open/close function which the user can use. This might help the receiving of the data.
I'd say use a TDataPacket and a TTimer (system timer).
The datapacket waits for the data you're expecting ; while the timer event gives you a chance to check if the datapacket has found the data after a 'timeout'
The datapacket trigger could set the timer.enable := false - to keep it from firing too.
Hope that makes sense.
Log in to post a comment.