Sign in Join

Live chat for Free Pascal, Lazarus, Delphi and every Pascal programmer.

Mailing lists / lazarus / 2010-11

Advice needed for serial comm with Lazarus

4 messages · 3 people · –

Reply by email Subscribe to this list Your subscription

Read-only archive. Use j and k to move between messages.

Bo Berglund

I have already been adviced to use the SdpoSerial component for my serial comm project. The project is a platform-independent rewrite of a data acquisition instrument communications program. It was originally written in Delphi7 using a Windows based serial communications component (not AsyncPro).

So now I have to refrain from Windows specific stuff... Therefore I am looking for advice on platform-independent programming using the SdpoSerial component..

The protocol used is basically as follows:
1) There are 1-byte commands to switch the instrument into and out from two remote modes. All of these 4 commands work the same way:
- The host sends a control byte
- Then it waits with timeout for the response (ACK or NAK)
- The timeout is 2s.

2) Data transfer from the instrument is a bit different:
- The host sends the command, which is identified by a leading command byte followed by command dependent parameters, for example the start address and length for reading a memory block.
- Then the host waits for the response with timeout.
- In this case the response will be longer, it is always a chunk of data starting with the length of the data to follow, then the actual data and finally a checksum.

In the Delphi implementation I have done as follows:

1) There is a function to send the simple command and wait for response, which purges the buffers, then sends the command and then enters a loop approximately like below (there is also an OnRxData event for the serial component where incoming data are written into a string buffer):

var
  T1: Cardinal; //for timeout
begin
  Result := false;
  T1 := GetTickCount;
  FComm.ZapRxqueue;
  FRxBuf := '';
  FComm.SendChar(cmd);
  while Length(FRxBuf) = 0 do
  begin
    Sleep(2);
    if GetTickCount - T1 > FTimeout then break;
  end;
  if Length(FRxBuf) = 0 then exit;
  Result := FRxBuf[1] = ACK;
end;

Here I have a few possible compatibility problems:
- GetTickCount is used to handle the timeout. Windows specific?
- Sleep(2) is used to yield in the loop so serial data can arrive.

How should I do here in order to get the code portable?

2) In the case of the data transfer I need to wait for the first 4 bytes of the response to know how big the incoming packet will be. So this is done in the same way as above, only here I have to wait (with timeout) until the buffer length becomes >= 4 at which time I can calculate the total number of bytes to expect. Then I will wait in a loop similar to the one above until the buffer string length becomes this plus the checksum.

In the Delphi implementation I have an incrementing progress bar inside the loop (whenever 512 new bytes have arrived it is incremented) plus a check for a user break condition on a Cancel button. There is also a timeout check for the case when characters stop arriving before all are collected.

What is the recommended way to handle such situations? The data transfer may take several minutes for large data chunks and I have to keep the application responsive while giving feedback to the user about the progress...

I am also thinking of replacing the string buffer with a TMemoryStream object.

Bo Berglund

Reply by email

Michael Schnell
to Bo Berglund
Bo Berglund wrote
How should I do here in order to get the code portable?

I have no infos about the SdpoSerial component, so I don't know whether it already uses threads. But the problem is best handled using a thread for the serial receiving that creates an event when something useful has been received. In a perfect world you would use an inter-thread event (captured by "Procedure..Message" in the main thread) but AFAIK, this is broken in (at least) the current Lazarus version in Linux. (I did successfully use it some time ago).

So - second best - you could use a TTimer and poll in the timer event for a ready-flag set by the thread or - third best - receive and decode a many serial characters as are available in the TTimer event..

Moreover the TTimer event can manage a counter and use same to detect a timeout (beware: TTimer events can be dropped when the main thread is too busy, so the counter might run too slow and the timeout might be delayed.

Show quoted text 5 lines
In the Delphi implementation I have an incrementing progress bar inside the loop (whenever 512 new bytes have arrived it is incremented) plus a check for a user break condition on a Cancel button. There is also a timeout check for the case when characters stop arriving before all are collected.

Could be done in a TTimer event or in a "Procedure..Message" event.

What is the recommended way to handle such situations? The data transfer may take several minutes for large data chunks and I have to keep the application responsive while giving feedback to the user about the progress...

A thread of course does not prevent the application from being responsive (That is why Async Pro is such a good tool for handling serial and TCP/IP streams, doing everything in thread and throwing Main-Thread events only when necessary).

But reading chunks of data in a TTimer event (and storing them in an appropriate buffer) should work, as well.

-Michael

Reply by email

Mark Morgan Lloyd
to Bo Berglund: I have already been adviced to use the SdpoSerial component for my serial comm project.…
Bo Berglund wrote
What is the recommended way to handle such situations? The data transfer may take several minutes for large data chunks and I have to keep the application responsive while giving feedback to the user about the progress...

Put a state machine or equivalent /inside/ the thread that's reading the data from the port, using select() etc. Only fire the "got data" event when you've received an intact message or know that it's timed out or corrupt.

Mark Morgan Lloyd markMLl .AT. telemetry.co .DOT. uk [Opinions above are the author's, not those of his employers or colleagues]

Reply by email

Michael Schnell
to Mark Morgan Lloyd
Mark Morgan Lloyd wrote
Put a state machine or equivalent /inside/ the thread that's reading the data from the port, using select() etc.

Select is Linux-only, I suppose he want to do portable code,

Only fire the "got data" event

How to "fire an event" in the thread that the main thread is supposed to handle ?

when you've received an intact message or know that it's timed out or corrupt.

Michael

Reply by email