Passa al contenuto principale

Ricezione e decodifica stringa seriale

Presentiamo un programma per la ricezione e decodifica di una stringa seriale, viene ricevuta la stringa "Gill format– Polar, Continuous" che è la stringa di default inviata in modo continuo dall'anemometro ad ultrasuoni WindSonic della Gill Instruments. Si tratta di una stringa ascii del tipo:

<STX>Q,229,002.74,M,00,<ETX>16<CR><LF>
Dove:
Q: Indirizzo di nodo (Lettera da A...Z)
229: Direzione vento (Intero da 0 a 359 gradi)
002.74: Velocità vento (Float in metri/sec)
M: Unità misura (Lettera M per metri)
00: Stato (Intero 2 cifre)
16: Checksum (Intero esadecimale 2 cifre)

La stringa con i dati è compresa tra lo "Start of text" e "End of text" a cui segue il checksum di controllo.

Foto anemometro WindSonic

Descrizione programma​

  • Il programma dopo avere impostato la porta seriale per la ricezione del messaggio nel case "0" si pone in attesa del primo carattere <STX>.

  • Nel case "1" viene eseguita la ricezione dell'intero messaggio fino al carattere <CR> di fine.

  • Nel case "2" si controlla il messaggio, dopo il <CR> ci deve essere un <LF> in caso contrario errore. Il messaggio ricevuto è ritornato sulla console di spionaggio.

  • Si controlla la lunghezza del messaggio che deve essere di 22 caratteri.

  • Segue il calcolo e controllo del checksum del messaggio. Il calcolo è facilitato dalla lunghezza fissa del mesaggio per cui è possibile iterare sui caratteri ricevuti all'interno del buffer di ricezione.

  • Viene acquisita la direzione del vento (Numero intero a 3 cfre), la velocità (Numero float) e lo stato (Numero intero a 2 cifre).

  • Vengono inviati i valori acquisiti alla console di spionaggio.

  • Il programma terimna incrementando il numeratore dei messaggi ricevuti.

  • In caso di errore la ricezione viene reinizializzata e alla console di spionaggio è inviato il codice di errore.

LogficLab (Ptp116, ST_WindSonicDriver)

PROGRAM ST_WindSonicDriver
VAR
i : UDINT; (* Auxiliary variable *)
Ch : BYTE; (* Rx character *)
ErrorNr : USINT; (* Error number *)
CaseNr : USINT; (* Program case *)
Ptr : PVOID; (* Auxiliary pointer *)
TimeBf : UDINT; (* Time buffer (uS) *)
WDirection : UINT; (* Wind direction *)
WSpeed : REAL; (* Wind speed *)
Status : USINT; (* Status *)
Cks : ARRAY[0..1] OF BYTE; (* Checksum *)
RxChrs : UDINT; (* Rx characters *)
Errors : UDINT; (* Total errors *)
Messages : UDINT; (* Total messages *)
RxBuf : STRING[ 64 ]; (* Rx data buffer *)
SpyBuffer : STRING[ 64 ]; (* Spy buffer *)
Sp : SysSerialPort; (* Serial port management *)
END_VAR

// *****************************************************************************
// PROGRAM "ST_WindSonicDriver"
// *****************************************************************************
// Receives data from WindSonic a Gill Instruments ultrasonic Wind Sensor.
// In the "Gill format Polar, Continuous" (Default) the device continuously
// transmit a message like the following.
//
// Q,,000.37,M,00,xx
// Q,088,000.37,M,00,xx
//
// Where:
// Q:WindSonic node address
// 088: Wind direction, if detected otherwise none
// 000.37: Wind speed
// M: Units
// 00: Status
// xx: Check sum,this is the XOR of the bytes between (and not including) the
// and characters.
// -----------------------------------------------------------------------------

// -------------------------------------------------------------------------
// INITIALIZATION
// -------------------------------------------------------------------------
// Configure serial port.

IF (SysFirstLoop) THEN
Sp.COM:=ADR('COM2'); //COM port definition
Sp.Baudrate:=9600; //Baudrate
Sp.Parity:='N'; //Parity
Sp.DataBits:=8; //Data bits
Sp.StopBits:=1; //Stop bits
Sp.DTRManagement:=DTR_AUTO_WO_TIMES; //DTR management
Sp.DTRComplement:=FALSE; //DTR complement
Sp.EchoFlush:=FALSE; //Received echo flush
Sp.DTROnTime:=0; //DTR On time delay (mS)
Sp.DTROffTime:=0; //DTR Off time delay (mS)
Sp.FlushTm:=0; //Flush time (mS)
Sp.RxSize:=0; //Rx buffer size
Sp.TxSize:=0; //Tx buffer size
END_IF;

// -------------------------------------------------------------------------
// MANAGE THE SERIAL PORT
// -------------------------------------------------------------------------
// Manage the serial port.

Sp(Open:=TRUE); //Serial port management
IF NOT(Sp.Opened) THEN CaseNr:=0; RETURN; END_IF;

// -------------------------------------------------------------------------
// EXECUTION ERROR REPORT
// -------------------------------------------------------------------------
// On execution error the reception loop is ended and the error number is
// reported on spy console

IF (ErrorNr <> 0) THEN
eTO_JUNK(SysVsnprintf(ADR(SpyBuffer), SIZEOF(SpyBuffer), ADR('Error:%d'), USINT_TYPE, ADR(ErrorNr)));
eTO_JUNK(SysCVsnprintf(ADR(SpyBuffer), SIZEOF(SpyBuffer), ADR(', On case:%d'), USINT_TYPE, ADR(CaseNr)));
eTO_JUNK(SysWrSpyData(SPY_ASCII, 0, 16#10000000, ADR('WindSonicDriver:Er'), ADR(SpyBuffer)));
Errors:=Errors+1; //Total errors
ErrorNr:=0; //Error number
CaseNr:=0; //Program case
END_IF;

// -------------------------------------------------------------------------
// CASE LOOP
// -------------------------------------------------------------------------
// If reception starts it must be ended in a defined time.

IF (CaseNr = 0) THEN TimeBf:=SysGetSysTime(TRUE); END_IF;
IF ((SysGetSysTime(TRUE)-TimeBf) > 1000000) THEN
ErrorNr:=10; //Error number
RETURN;
END_IF;

// -------------------------------------------------------------------------
// CASE MANAGEMENT
// -------------------------------------------------------------------------
// Program cases.

CASE (CaseNr) OF

// ---------------------------------------------------------------------
// WAITS RECEPTION START
// ---------------------------------------------------------------------
// Waits until is been received.

0:
IF NOT(TO_BOOL(SysFGetIChars(Sp.File))) THEN RETURN; END_IF;
Ch:=TO_BYTE(Sysfgetc(Sp.File)); //Rx character
IF (Ch <> 16#02) THEN RETURN; END_IF;

// Initialize the reception.

RxChrs:=0; //Rx characters
eTO_JUNK(Sysmemset(ADR(RxBuf), 0, SIZEOF(RxBuf))); //Clear data buffer
eTO_JUNK(eSetBYTE(ADR(RxBuf)+RxChrs, Ch)); //Store character in buffer
RxChrs:=RxChrs+1; //Rx characters
CaseNr:=CaseNr+1; //Program case

// ---------------------------------------------------------------------
// Here the string is received.

1:
WHILE TO_BOOL(SysFGetIChars(Sp.File)) DO
Ch:=TO_BYTE(Sysfgetc(Sp.File)); //Rx character

// terminates the string.

IF (Ch = 16#0D) THEN CaseNr:=CaseNr+1; RETURN; END_IF;
eTO_JUNK(eSetBYTE(ADR(RxBuf)+RxChrs, Ch)); //Store character in buffer
RxChrs:=RxChrs+1; //Rx characters

// Check if string is longer than defined buffer.

IF (RxChrs >= SIZEOF(RxBuf)) THEN
ErrorNr:=20; //Error number
RETURN;
END_IF;
END_WHILE;

// -----------------------------------------------------------------
// After the must be received.

2:
IF NOT(TO_BOOL(SysFGetIChars(Sp.File))) THEN RETURN; END_IF;
Ch:=TO_BYTE(Sysfgetc(Sp.File)); //Rx character
IF (Ch <> 16#0A) THEN ErrorNr:=30; RETURN; END_IF;

// Here the string has been received, it's sent to spy console.

eTO_JUNK(SysWrSpyData(SPY_ASCHEX, 0, 16#00000001, ADR('WindSonicDriver:Rx'), ADR(RxBuf)));

// Calculate checksum on characters between and .

Cks[0]:=eGetBYTE(ADR(RxBuf)+1); //Checksum
FOR i:=0 TO RxChrs-1-3 DO Cks[0]:=Cks[0] XOR eGetBYTE(ADR(RxBuf)+1+i); END_FOR;

// Acquire "Checksum".

Ptr:=SysStrFind(ADR(RxBuf), ADR('$03'), FIND_GET_END);
IF (Ptr = eNULL) THEN ErrorNr:=31; RETURN; END_IF;
IF NOT(SysVsscanf(Ptr, ADR('%02X'), BYTE_TYPE, ADR(Cks[1]))) THEN ErrorNr:=32; RETURN; END_IF;
IF (Cks[0] <> Cks[1]) THEN ErrorNr:=33; RETURN; END_IF;

// Acquire "Wind direction".
// If not detected the field is empty.

Ptr:=SysStrFind(ADR(RxBuf), ADR(','), FIND_GET_END);
IF (Ptr = eNULL) THEN ErrorNr:=40; RETURN; END_IF;

IF (TO_UDINT(SysStrFind(Ptr, ADR(','), FIND_GET_END)-Ptr) > 1) THEN
IF NOT(SysVsscanf(Ptr, ADR('%d'), UINT_TYPE, ADR(WDirection))) THEN ErrorNr:=41; RETURN; END_IF;
END_IF;

// Acquire "Wind speed".

Ptr:=SysStrFind(Ptr, ADR(','), FIND_GET_END);
IF (Ptr = eNULL) THEN ErrorNr:=50; RETURN; END_IF;
IF NOT(SysVsscanf(Ptr, ADR('%f'), REAL_TYPE, ADR(WSpeed))) THEN ErrorNr:=51; RETURN; END_IF;

// Acquire "Status". Meter unit has been expected.

Ptr:=SysStrFind(Ptr, ADR(',M,'), FIND_GET_END);
IF (Ptr = eNULL) THEN ErrorNr:=60; RETURN; END_IF;
IF NOT(SysVsscanf(Ptr, ADR('%d'), USINT_TYPE, ADR(Status))) THEN ErrorNr:=61; RETURN; END_IF;

// Report on Spy console the acquired data.

eTO_JUNK(SysVsnprintf(ADR(SpyBuffer), SIZEOF(SpyBuffer), ADR('WDirection:%03d'), UINT_TYPE, ADR(WDirection)));
eTO_JUNK(SysCVsnprintf(ADR(SpyBuffer), SIZEOF(SpyBuffer), ADR(', WSpeed:%06.2f'), REAL_TYPE, ADR(WSpeed)));
eTO_JUNK(SysCVsnprintf(ADR(SpyBuffer), SIZEOF(SpyBuffer), ADR(', Status:%02d'), USINT_TYPE, ADR(Status)));
eTO_JUNK(SysWrSpyData(SPY_ASCII, 0, 16#00000001, ADR('WindSonicDriver:Rx'), ADR(SpyBuffer)));
Messages:=Messages+1; //Total messages
CaseNr:=0; //Program case
END_CASE;

// [End of file]