Search code examples
delphiserial-portdelphi-7delimiterarduino-uno

Split up serial data in delphi


I am a newbie in Delphi programming and I need some help. I have a problem with spliting my serial data. This is my code:

procedure TForm1.ComPort1RxChar(Sender: TObject; Count: Integer); 
var 
  DataByte : string;  
  x, i: integer;   
  save_data : TStringList;  
begin 
  save_data := TStringList.create;

  for x := 0 to Count-1 do begin
    ComPort1.ReadStr(DataByte,1);
    if DataByte = 'n' then
    begin
      memo1.Text := '';
    end
    else
    begin
      memo1.Text := memo1.Text + DataByte;
      Split(' ', DataByte, save_data);
    end; 
  end;   
  save_gyroX := save_data[0];   
  save_gyroY := save_data[1];   
  save_gyroZ := save_data[2];   
  save_accelX := save_data[3];   
  save_accelY := save_data[4];   
  save_accelZ := save_data[5];   
  SerialProcess();
  save_data.Free; 
end;

My Split(' ', DataByte, save_data); doesn't work. I don't understand because I just split String data which is taken from the serial port. This is my Split() procedure:

procedure TForm1.Split(const Delimiter: Char; Input: string; const Strings: TStrings) ; 
begin
  Assert(Assigned(Strings));
  Strings.Clear;
  Strings.Delimiter := Delimiter;
  Strings.DelimitedText := Input;
end;

I do not know why my program is giving me a EStringListError error.


Solution

  • You are calling ReadStr() to read individual bytes, and calling Split() on every byte (except for 'n'). So the TStringList will only ever hold 1 string at a time. Like MBo said, you need to fix your loop to avoid that, eg:

    procedure TForm1.ComPort1RxChar(Sender: TObject; Count: Integer); 
    var 
      DataByte : string;  
      x: integer;   
      save_data : TStringList;  
    begin 
      ComPort1.ReadStr(DataByte, Count);
    
      for x := 1 to Length(DataByte) do
      begin
        if DataByte[x] = 'n' then
        begin
          Memo1.Text := '';
        end
        else
        begin
          Memo1.Text := Memo1.Text + DataByte[x];
        end; 
      end;   
    
      save_data := TStringList.create;
      try
        Split(' ', DataByte, save_data);
    
        save_gyroX := save_data[0];   
        save_gyroY := save_data[1];   
        save_gyroZ := save_data[2];   
        save_accelX := save_data[3];   
        save_accelY := save_data[4];   
        save_accelZ := save_data[5];   
        SerialProcess();
      finally
        save_data.Free; 
      end;
    end;
    

    That being said, you are not taking into account that the number of bytes you receive for any given OnRxChar event call is arbitrary. It is whatever raw bytes have been read at that exact moment. You are assuming a full string with at least 6 delimited substrings, and that is simply not guaranteed. You need to buffer the raw data as you receive it, and then parse and remove only completed strings from the buffer as needed.

    Try something more like this:

    var
      DataBuffer: string;
    
    // consider using the OnRxBuf event instead...
    procedure TForm1.ComPort1RxChar(Sender: TObject; Count: Integer); 
    var 
      DataByte : string;  
      x: integer;   
      save_data : TStringList;  
    begin 
      ComPort1.ReadStr(DataByte, Count);
      DataBuffer := DataBuffer + DataByte;
    
      x := Pos('n', DataBuffer);
      if x = 0 then Exit;
    
      save_data := TStringList.Create;
      try
        repeat
          DataByte := Copy(DataBuffer, 1, x-1);
          Delete(DataBuffer, 1, x);
    
          Memo1.Text := DataByte;
    
          Split(' ', DataByte, save_data);
          if save_data.Count >= 6 then
          begin
            save_gyroX := save_data[0];   
            save_gyroY := save_data[1];   
            save_gyroZ := save_data[2];   
            save_accelX := save_data[3];   
            save_accelY := save_data[4];   
            save_accelZ := save_data[5];   
            SerialProcess();
          end;
    
          x := Pos('n', DataBuffer);
        until x = 0;
      finally
        save_data.Free; 
      end;
    end;