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; ```