Vai al contenuto

Ricezione e decodifica stringa seriale

Vai all indice del manuale di programmazione

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]
Was this article helpful?