delphiuser-interfaceandroid-activityserial-portcorrupt-data

How is user GUI activity corrupting my serial port input


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();

Solution

  • 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?