I am using a 3rd party serial port component from one of "the big names" (yes, I have asked them for support, but there is a time zone difference and I need to fix this really quickly). The component has been around for years and I have no reason to believe that the problem lies with it (ditto the harwdware).
The h/w spec says that if I write a certain string to the seial port, terminated by carriage return, and then read, it will reply with a spcifically formatted 8 character string, again terminated by carriage return.
The code can run for hours doing this correctly and updating the GUI based on what it reads.
However, when there is any user activity on the GUI, I read junk from the serial port.
I first noticed it when clicking a button which caused a modal form to open and then closing the form.
However, I also see it when simply dragging the scollbar of a TStringGrid.
Here is the code. Any advice?
Update: the component is threaded and the suppliers agree with the posters here - a serial port is an asynchrnonous device. I have changed the code to write a request for data and handle each receied character in the component's OnCharReceived() event handler. Tahnsk for all of the help.
function TForm1.ReadChannelValueFromSerialPort(
device_number : String; channel_number : String) : Real;
const SLEEP_TIME = 50; // ms
NUM_READ_ATTEMPTS = 10;
var serialPortInput : String;
read_attempt_counter : Integer;
messageString : String;
begin
WriteToSerialPort('#' + device_number + channel_number + #13);
serialPortInput := '';
read_attempt_counter := 0;
while Length(serialPortInput) = 0 do
begin
try
Application.ProcessMessages();
serialPortInput := serialPortInput + SerialPort.ReadText();
except
on E: Exception do
begin
messageString := 'Can''t read from serial port' ;
MessageDlg(messageString, mtError, [mbOK], 0);
Halt(0);
end;
end;
Inc(read_attempt_counter);
if (read_attempt_counter = NUM_READ_ATTEMPTS)
and (Length(serialPortInput) = 0) then
begin
messageString := 'Can''t read from serial port after trying ' +
IntToStr(NUM_READ_ATTEMPTS) + ' times in ' +
FloatToStr((SLEEP_TIME * NUM_READ_ATTEMPTS) / 1000) + ' seconds';
MessageDlg(messageString, mtError, [mbOK], 0);
Halt(0);
end;
if (Length(serialPortInput) = 0) then
Sleep(SLEEP_TIME);
end;
if Copy(serialPortInput, 1, 1) <> '>' then
begin
DebugBreak();
MessageDlg('Invalid value read from serial port "' +
serialPortInput + '"', mtError, [mbOK], 0);
Halt(0);
end;
// drop the3 leading >
serialPortInput := Copy(serialPortInput, 2, Length(serialPortInput) - 1);
serialPortInput := TrimRight(serialPortInput); // just in case
Result := StrToFloat(serialPortInput);
end; // ReadChannelValueFromSerialPort();
Without knowing which component you are actually using, or what it does internally, it is hard to say. But user activity goes through the main message queue, and your code is manually pumping the queue for new messages, so that is likely related to your problem. You are allowing user activity to be processed in between your writes and reads. Have you tried moving the serial port logic into a separate worker thread?