Gestione remota di I/O digitali

...

Grazie.

Da remoto intendi via seriale ? leggo rx, tx. O intendi via seriale del bluetooth ? Hc12=modulino radio 433 Mhz ?

docsavage:
Naturalmente ogni critica, commento e/o suggerimento è gradita
Prossimo sviluppo?

Si potrebbe implementare un indirizzo broadcast, ad esempio '.'
In 'ricevi' verrà settato anche un flag 'broad' in modo che 'esegui' possa scegliere cosa fare.
Un messaggio broadcast può essere utile per informare tutti gli ascoltatori di uno stato globale senza contattarli singolarmente, o ancora un ascoltatore può rispondere in modo broadcast per aggiornare tutti gli altri con un'informazione comune (ad esempio l'orario).

Mancherebbe ancora un controllo errori, che potrebbe essere una coppia di caratteri hex rappresentanti un qualsiasi CRC8 o checksum a 8 bit, ma in questo caso specifico mi domando se sia davvero necessario.

In una trasmissione di byte "normale" ci sono meno vincoli rispetto ad una trasmissione "testuale", ogni byte può valere da 0 a 255 e comparire in qualsiasi punto, mentre mi chiedo se il fatto di obbligare i valori entro limiti ristretti (solo alcuni caratteri del set ASCII), e successivamente controllare anche la congruenza di quanto ricevuto "ricomponendo" i caratteri (ad esempio nel caso di valori numerici), non svolgano già un'adeguata funzione di error detect.

Alla fine è tornato comodo anche a me, ho aggiunto anche la possibilità di ricevere un CRC8 in formato HEX (due caratteri aggiuntivi dopo il *)

Le variabili di lavoro:

#define   MODULE_ADDR  '0'     // indirizzo/nome del modulo
#define   MAX_LEN_BUF  16      // lunghezza massima payload
#define   CRC_ON       false   // controllo CRC disattivo
byte      rxBufi;              // indice buffer, alla fine=numero byte ricevuti
byte      rxBuf[MAX_LEN_BUF];  // buffer di ricezione
boolean   onBroad = false;     // true se ricevuto indirizzo broadcast '&'

Le funzioni di supporto:

//------------------------------------------------------------------------------
// SNAP CRC8 - initial val 0 - reflect data in - reflect crc out - polynom 31h
//------------------------------------------------------------------------------
void crcUpdate(byte &crc, byte n)
{
    crc ^= n;
    for(byte i=8; i; i--)
    {
        byte lsb = crc & 1;
        crc >>= 1;
        if(lsb) crc ^= 0x8C;  // reflected polynom
    }
}
boolean isAsciiHex(byte n)
{
    return (n >= '0' && n <= '9') ? true : (n >= 'A' && n <= 'F') ? true : false;
}
//------------------------------------------------------------------------------
byte hexToInt(byte n)
{
    byte m = n - '0';
    return (m > 9) ? m - 7 : m;
}

La ricezione:

//------------------------------------------------------------------------------
//  FORMATO CON CRC:   #<addr><msg>*<crc>  ESEMPIO: #0L01*0F
//  FORMATO SENZA CRC: #<addr><msg>*       ESEMPIO: #0L01*
//  #          -> start char
//  <addr>     -> 1 char (& = broadcast)
//  <payload>  -> 0..MAX_LEN_BUF char
//  *          -> end msg char
//  <crc>      -> 2 char hex SNAP CRC8 facoltativi
//------------------------------------------------------------------------------
boolean ricevuto()
{
    static byte stat = 0;
    static byte crc;
    static byte crcH;

    while(Serial.available())
    {
        byte rx = Serial.read();

        if(rx == '#') { rxBufi = onBroad = crc = 0;  stat = 1; }  // start

        else if(rx < ' ')  stat = 0;  // esclude 0..31  128..255

        else
            switch (stat)
            {
            case 1:  // ricezione indirizzo modulo
                if(CRC_ON) crcUpdate(crc, rx);
                if(rx == MODULE_ADDR)  stat = 2;
                else if(rx == '&') {   onBroad = true;  stat = 2;  }
                else                   stat = 0;
                break;

            case 2:  // ricezione dati
                if(rx == '*')  // fine dati
                {
                    if(CRC_ON)  stat = 3;
                    else {     stat = 0;  return true; }
                }
                else if(rxBufi == MAX_LEN_BUF)  stat = 0;  // over buffer
                else  // salva byte
                {
                    if(CRC_ON) crcUpdate(crc, rx);
                    rxBuf[rxBufi++] = rx;
                }
                break;

            case 3:  // ricezione carattere alto crc8
                if(isAsciiHex(rx)) { crcH = hexToInt(rx);  stat = 4; }
                else                 stat = 0;
                break;

            case 4:  // ricezione carattere basso crc8
                stat = 0;
                if (isAsciiHex(rx)  &&  crc == (crcH << 4) + hexToInt(rx))
                    return true;
            }//switch
    }//while
    return false;
}

Quando la funzione ritorna true, rxBufi contiene il numero di caratteri ricevuti e onBroad vale true se l'indirizzo era '&'.

I caratteri ricevuti nel buffer non comprendono: start, indirizzo, fine, eventuale crc.

L'eventuale crc si calcola sui soli caratteri racchiusi tra # e * (quindi indirizzo e dati)

La funzione cerca, tra tutti i caratteri presenti in quel momento nel buffer di ricezione seriale, una sequenza valida, scartando tutto il resto e ritornando true appena ne trova una. Qualche carattere può restare nel buffer seriale e verrà analizzato/scartato alla successiva chiamata, qualsiasi sequenza errata viene ignorata senza alcun avviso.

La risposta alla domanda se limitare la comunicazione all' ASCII la rendeva intrinsecamente più adatta/sicura a rilevare/evitare gli errori è: NO

Se c'è la possibilità di linea errorata serve proprio un CRC.

Un normale checksum o uno xor ad esempio non rilevano differenze tra 0L01 e 01L0.

Fondamentalmente un current loop parallelo.

Master:

Slave:

I valori o la configurazione possono sembrare strani, ma sono ottenuti con misure all'oscilloscopio e mezzo chilometro di linea tra master e slave (caricando la linea in modo da simulare 30 slave connessi contemporaneamente).

Velocità 9600 bps.

Mi interessava il totale isolamento dei nodi.

La tensione ausiliaria 13V non è critica (da 10 a 16 mi pare funzionasse sempre).