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.