OK, below is the simplified version of my function to built the command strings, it is in Delphi, but I'm sure if you are not familiar with Delphi, google will be able to assist.
You get 2 command types (CmdType in the function below) namely Poll commands and Set commands, a poll command is used to inquire something from the inverter and a set command is used to set something to the inverter (adjust a setting). The reply to a poll command will be a value depending on what you've requested and the reply to a set command will either be ACK (acknowledge) or NAK (not acknowledge). All responses might or might not include the security string and/or crc check-sum, depending on the inverter model - see below.
Some inverter models require the CRC check-sum (UseCRC in function below) added to command strings and will sometimes include the crc check-sums added to the responses.
Some inverter models require the "security string" (UseSec in function below) added to the command strings and will sometimes include the "security strings" added to the responses.
So you have to start off by sending the the PI and QPI commands to the inverter with different combinations of UseCRC and UseSec until you get a valid response from the inverter. Once you get a valid response you'll have to use that specific combination of UseCRC and UseSec for all other commands to that inverter. A valid response (to a PI or QPI command) from the inverter will be something like ^D00517Êì or (PI16œ¯ which indicates the model number of the inverter, in this case 17 ( ^D00517Êì ) or 16 ( (PI16œ¯ ).
So you will use the function below with the following parameters until you get a valid response from the inverter:
BuildCommandString('PI', ctPoll, False, False)
BuildCommandString('PI', ctPoll, False, True)
BuildCommandString('PI', ctPoll, True, False)
BuildCommandString('PI', ctPoll, True, True)
BuildCommandString('QPI', ctPoll, False, False)
BuildCommandString('QPI', ctPoll, False, True)
BuildCommandString('QPI', ctPoll, True, False)
BuildCommandString('QPI', ctPoll, True, True)
As example: If you got a valid response from the inverter after sending the command from BuildCommandString('PI', ctPoll, True, False), you don't have to send any more PI or QPI commands to the inverter (because you received a valid response) and then you have to use the setting for UseCRC and UseSec which gave you a valid response for all other commands for that inverter, as explained above. So in this example each and every command send to this specific inverter needs to include the CRC checksum, but not the "security string", because you received a valid response by sending a command with UseCRC = True and UseSec = False.
So examples of other poll commands to this inverter will be:
BuildCommandString('QMD', ctPoll, True, False)
BuildCommandString('QVFW', ctPoll, True, False)
BuildCommandString('QFLAG', ctPoll, True, False)
BuildCommandString('QPIGS', ctPoll, True, False)
TIP: Sometimes data corruption, noise on the lines, partial old commands on the input-buffer of the inverter etc. prevents the inverter from returning a valid response and therefore it is good practice to send each command up to 3 consecutive times, obviously if you've received a valid response after the first or second time you're sending the command, you won't send it again.
type
TCommandType = (ctPoll, ctSet);
function BuildCommandString(InpStr: string; CmdType: TCommandType; UseCRC, UseSec: boolean): string;
function PadInteger(Val, FinalStrLen: integer): string;
begin
Result := IntToStr(Val);
while Length(Result) < FinalStrLen do
Result := '0'+Result;
end;
function AddSetHeader(DataToSend: string; CRCLength: integer): string;
begin
Result := '^S'+PadInteger(Length(DataToSend)+CRCLength+1, 3)+DataToSend;
end;
function AddPollHeader(DataToSend: string; CRCLength: integer): string;
begin
Result := '^P'+PadInteger(Length(DataToSend)+CRCLength+1, 3)+DataToSend;
end;
function GetCRC(ptr: pchar; len: word): word;
const
crc_ta: array [0..255] of word =
($0000, $1021, $2042, $3063, $4084, $50A5, $60C6, $70E7, $8108, $9129,
$A14A, $B16B, $C18C, $D1AD, $E1CE, $F1EF, $1231, $0210, $3273, $2252,
$52B5, $4294, $72F7, $62D6, $9339, $8318, $B37B, $A35A, $D3BD, $C39C,
$F3FF, $E3DE, $2462, $3443, $0420, $1401, $64E6, $74C7, $44A4, $5485,
$A56A, $B54B, $8528, $9509, $E5EE, $F5CF, $C5AC, $D58D, $3653, $2672,
$1611, $0630, $76D7, $66F6, $5695, $46B4, $B75B, $A77A, $9719, $8738,
$F7DF, $E7FE, $D79D, $C7BC, $48C4, $58E5, $6886, $78A7, $0840, $1861,
$2802, $3823, $C9CC, $D9ED, $E98E, $F9AF, $8948, $9969, $A90A, $B92B,
$5AF5, $4AD4, $7AB7, $6A96, $1A71, $0A50, $3A33, $2A12, $DBFD, $CBDC,
$FBBF, $EB9E, $9B79, $8B58, $BB3B, $AB1A, $6CA6, $7C87, $4CE4, $5CC5,
$2C22, $3C03, $0C60, $1C41, $EDAE, $FD8F, $CDEC, $DDCD, $AD2A, $BD0B,
$8D68, $9D49, $7E97, $6EB6, $5ED5, $4EF4, $3E13, $2E32, $1E51, $0E70,
$FF9F, $EFBE, $DFDD, $CFFC, $BF1B, $AF3A, $9F59, $8F78, $9188, $81A9,
$B1CA, $A1EB, $D10C, $C12D, $F14E, $E16F, $1080, $00A1, $30C2, $20E3,
$5004, $4025, $7046, $6067, $83B9, $9398, $A3FB, $B3DA, $C33D, $D31C,
$E37F, $F35E, $02B1, $1290, $22F3, $32D2, $4235, $5214, $6277, $7256,
$B5EA, $A5CB, $95A8, $8589, $F56E, $E54F, $D52C, $C50D, $34E2, $24C3,
$14A0, $0481, $7466, $6447, $5424, $4405, $A7DB, $B7FA, $8799, $97B8,
$E75F, $F77E, $C71D, $D73C, $26D3, $36F2, $0691, $16B0, $6657, $7676,
$4615, $5634, $D94C, $C96D, $F90E, $E92F, $99C8, $89E9, $B98A, $A9AB,
$5844, $4865, $7806, $6827, $18C0, $08E1, $3882, $28A3, $CB7D, $DB5C,
$EB3F, $FB1E, $8BF9, $9BD8, $ABBB, $BB9A, $4A75, $5A54, $6A37, $7A16,
$0AF1, $1AD0, $2AB3, $3A92, $FD2E, $ED0F, $DD6C, $CD4D, $BDAA, $AD8B,
$9DE8, $8DC9, $7C26, $6C07, $5C64, $4C45, $3CA2, $2C83, $1CE0, $0CC1,
$EF1F, $FF3E, $CF5D, $DF7C, $AF9B, $BFBA, $8FD9, $9FF8, $6E17, $7E36,
$4E55, $5E74, $2E93, $3EB2, $0ED1, $1EF0);
var
crc, bCRCLow, bCRCHign, tmp: word;
da: byte;
begin
crc := 0;
while (len <> 0) do
begin
len := len - 1;
da := $FF and ($FF and (crc shr 8)) shr 4;
crc := crc shl 4;
crc := crc xor (crc_ta[$FF and (da xor (Ord(ptr^) shr 4))]);
da := $FF and ($FF and (crc shr 8)) shr 4;
crc := crc shl 4;
tmp := $FF and (da xor (Ord(ptr^) and $F));
crc := crc xor crc_ta[tmp];
ptr := ptr + 1;
end;
bCRCLow := $FF and crc;
bCRCHign := $FF and (crc shr 8);
if (bCRCLow = $28) or (bCRCLow = $0D) or (bCRCLow = $0A) then
bCRCLow := bCRCLow+1;
if (bCRCHign = $28) or (bCRCHign = $0D) or (bCRCHign = $0A) then
bCRCHign := bCRCHign+1;
crc := ($FF and bCRCHign) shl 8;
crc := crc+bCRCLow;
Result := crc;
end;
begin
Result := '';
if Trim(InpStr) = '' then
Exit;
if UseCRC then
begin
if UseSec then
begin
if CmdType = ctPoll then
InpStr := AddPollHeader(InpStr, 2)
else
InpStr := AddSetHeader(InpStr, 2);
end;
Result := InpStr+HexStrToStr(Format('%.4x', [GetCRC(PChar(InpStr), Length(InpStr))]));
end
else
begin
if UseSec then
begin
if CmdType = ctPoll then
Result := AddPollHeader(InpStr, 0)
else
Result := AddSetHeader(InpStr, 0);
end
else
Result := InpStr;
end;
end;