How to increase number of serial data assigned to variable?

delphi, delphi-7, serial-port

Solution

A minimalistic solution with full error checking would be:

Update :

(Given that the received string ends with a CRLF combination and the packet length is not constant. This is a NMEA 0183 packet)

var
  finalBuf: AnsiString;

// Example packet: $HEHDT,10.17,T*28 + CRLF
// This is a NMEA 0183 protocol (Marine and GPS standard)
// Subset for reading the heading.
// HDT Heading – True
//       1   2 3
//       |   | |
//$--HDT,x.x,T*hh
//1) Heading Degrees, true
//2) T = True
//3) Checksum
// HE stands for: Heading – North Seeking Gyro

{- Checking packet and checksum, calculating heading }
Function ParseAndCheckNMEA_HDT(const parseS: AnsiString;
  var heading: Double): Integer;
// Example packet: $HEHDT,10.17,T*28 + CRLF
var
  i, p, err: Integer;
  xorSum: Byte;
  xorStr: AnsiString;
  headingStr: AnsiString;
begin
  Result := 0; // Assume ok
  if (Pos('$HEHDT', parseS) = 1) then // Start header ok ?
  begin
    p := Pos('*', parseS);
    if (p <> 0) and (Length(parseS) >= p + 2) then
    // Assumes a checksum in packet
    begin
      xorSum := Ord(parseS[2]);
      for i := 3 to p - 1 do // Calculate checksum
        xorSum := xorSum xor Ord(parseS[i]);
      xorStr := IntToHex(xorSum, 2);
      if (UpperCase(xorStr) = Copy(parseS, p + 1, 2)) then // Checksum ok ?
      begin
        // Validate heading
        headingStr := Copy(parseS, 8, p - 10);
        Val(headingStr, heading, err);
        if (err <> 0) then
          Result := 4; // Not a valid float
      end
      else
        Result := 3; // Wrong checksum
    end
    else
      Result := 2; // No checksum
  end
  else
    Result := 1; // Wrong header
end;


procedure TMainForm.ComPortRxChar(Sender: TObject; Count: Integer);
var
  i,err: Integer;
  strBuf: AnsiString;
  heading: Double;
begin
  ComPort.ReadStr(CPort.Str, Count);
  strBuf := CPort.str;
  for i := 1 to Length(strBuf) do
    case strBuf[i] of
      '$' : 
        finalBuf := '$';  // Start of package
      #10 :
        begin
          if (finalBuf <> '') and (finalBuf[1] = '$') then  // Simple validate check 
            begin
              SetLength( finalBuf, Length(finalBuf) - 1); // Strips CR
              err := ParseAndCheckNMEA_HDT(finalBuf, heading);
              if (err = 0) then 
                Memo.Lines.Add(finalBuf); // Add validated string
              //else
              //  Memo.Lines.Add('Error:' + IntToStr(err)); 
            end;
          finalBuf := '';
        end; 
    else
      finalBuf := finalBuf + strBuf[i];  
    end;
end;

Knowing the start character and packet ending, this should be fairly safe to use. The #13 and #10 (CR LF) characters that marks the end of package, are stripped and the package is checked for a valid checksum and the resulting heading value is calculated. The validated string is then added to the memo.

Update 2

To answer the direct question, why your receiving method can add your 'haha' string in the middle of the data string:

The comport routine delivers data, one or more characters, at its own pace. You cannot control when you are getting the data or how many characters there will be. Using the scheme in my answer, data is buffered until a complete package is delivered. With the packet support of TComPort it is possible to do the same.

From your comments it seems as there are several NMEA 0183 sensor types connected to the serial port. (Giving other lengths of the packages but all starting with $ character).

Replace the ParseAndCheckNMEA_HDT with the following function to validate the strings in that case :

Function ParseAndCheckNMEA(const parseS: AnsiString): Integer;
// Example packet: $HEHDT,10.17,T*28 + CRLF
var
  i, p: Integer;
  xorSum: Byte;
  xorStr: AnsiString;
begin
  Result := 0; // Assume ok
  if (Pos('$', parseS) = 1) then // Start header ok ?
  begin
    p := Pos('*', parseS);
    if (p <> 0) and (Length(parseS) >= p + 2) then
    // Assumes a checksum in packet
    begin
      xorSum := Ord(parseS[2]);
      for i := 3 to p - 1 do // Calculate checksum
        xorSum := xorSum xor Ord(parseS[i]);
      xorStr := IntToHex(xorSum, 2);
      if (UpperCase(xorStr) <> Copy(parseS, p + 1, 2)) then // Checksum ok ?
        Result := 3; // Wrong checksum
    end
    else
      Result := 2; // No checksum
  end
  else
    Result := 1; // Wrong header
end;

Problem

I have downloaded and installed the comport library. I have a simulator sending data through serial RS232 to a Selphi program. This is the following code snippet. ``` procedure TMainForm.ComPortRxChar(Sender: TObject; Count: Integer); begin ComPort.ReadStr(CPort.Str, Count); Memo.Text := Memo.Text + CPort.Str; end; ``` As for the CPort library portion, I added: ``` var Str: String; here is the problem. ``` The data example that is coming through is roughly like ``` $HEHDT,288.45,T*1D $HEHDT,288.46,T*18 $HEHDT,288.47,T*1A ``` and so on. Each line is sent per second. So with the code above, the memo will display all these data as it. However, if I change the code to: ``` procedure TMainForm.ComPortRxChar(Sender: TObject; Count: Integer); begin ComPort.ReadStr(CPort.Str, Count); Memo.Text := Memo.Text + CPort.Str + 'haha'; end; ``` This is what appears on the memo: ``` $HEHDT,2haha88.45,T*haha1Dhaha $HEHDT,2haha88.46,T*haha18haha $HEHDT,2haha88.47,T*haha1Ahaha ``` The "haha" appears after 8 ASCII characters. so does that mean in the CPort.pas library, under the asynchronous/synchronous portion, only 8ASCII characters max is assigned to the variable Str? How do I go about changing the codes such that the whole data string, regardless of its size, will be allocated to the variable Str instead of only 8 bytes. UPDATE**I realised that theres this part of the CPort library that contains the following code. can anyone enlighten me on how to edit the code? or whether it is the correct block that i have sourced out. thanks! ``` // split buffer in packets procedure TComDataPacket.HandleBuffer; procedure DiscardPacketToPos(Pos: Integer); var Str: string; begin FInPacket := True; if Pos > 1 then begin Str := Copy(Buffer, 1, Pos - 1); // some discarded data Buffer := Copy(Buffer, Pos, Length(Buffer) - Pos + 1); DoDiscard(Str); end; end; procedure FormPacket(CutSize: Integer); var Str: string; begin Str := Copy(Buffer, 1, CutSize); Buffer := Copy(Buffer, CutSize + 1, Length(Buffer) - CutSize); CheckIncludeStrings(Str); DoPacket(Str); end; procedure StartPacket; var Found: Integer; begin // check for custom start condition Found := -1; DoCustomStart(Buffer, Found); if Found > 0 then DiscardPacketToPos(Found); if Found = -1 then begin if Length(FStartString) > 0 then // start string valid begin Found := Pos(Upper(FStartString), Upper(Buffer)); if Found > 0 then DiscardPacketToPos(Found); end else FInPacket := True; end; end; procedure EndPacket; var Found, CutSize, Len: Integer; begin // check for custom stop condition Found := -1; DoCustomStop(Buffer, Found); if Found > 0 then begin // custom stop condition detected CutSize := Found; FInPacket := False; end else if Found = -1 then begin Len := Length(Buffer); if (FSize > 0) and (Len >= FSize) then begin // size stop condition detected FInPacket := False; CutSize := FSize; end else begin Len := Length(FStartString); Found := Pos(Upper(FStopString), Upper(Copy(Buffer, Len + 1, Length(Buffer) - Len))); if Found > 0 then begin // stop string stop condition detected CutSize := Found + Length(FStopString) + Len - 1; FInPacket := False; end; end; end; if not FInPacket then FormPacket(CutSize); // create packet end; ```

Original source