Eccomi.
Allora, questa è la parte di ricezione / invio. Ovviamente deve essere uguale su tutti gli arduino:
/*
* Funzioni per l'invio e la ricezione di dati byte / byte
*
*/
#ifndef CHR_START
#define CHR_START '['
#endif
#ifndef CHR_STOP
#define CHR_STOP ']'
#endif
#ifndef CHR_SEP
#define CHR_SEP ','
#endif
#ifndef TIMEOUT
#define TIMEOUT 500
#endif
// - 0 = ricezione non completata
// - 1 = dati ricevuti correttamente
// - 2 = errore timeout
// - 3 = errore dati in arrivo oltre la dimensione massima
// - 4 = errore dati incompleti
#define RX_WAIT 0
#define RX_OK 1
#define RX_TIMEOUT 2
#define RX_HIGH 3
#define RX_LOW 4
// funzione che trasmette
// i dati vengono trasmessi un byte alla volta
// parametri:
// - seriale = canale su cui trasmettere (serial / HC12)
// - pacchetto = dati da trasmettere. serve un puntatore per scorrere la struttura dati un byte alla volta
// - dimensione = numero di byte da trasmettere
void Trasmetti(Stream &seriale, unsigned char * pacchetto, size_t dimensione) {
// invio il carattere di start
seriale.print(CHR_START);
// invio i dati
for (byte i=0; i<dimensione; i++) {
seriale.print(*(pacchetto+i), HEX);
seriale.print(CHR_SEP);
}
// invio il carattere di stop
seriale.print(CHR_STOP);
return;
}
// funzion che riceve
// i dati arrivano sotto forma di caratteri che presi a 2 a 2 formano
// un byte rappresentato in HEX
// parametri:
// - seriale = canale da cui ricevere (serial / HC12)
// - pacchetto = contenitore dei dati da ricevere. serve un puntatore per scorrere la struttura dati un byte alla volta
// - dimensione = numero di byte massimo da ricevere
// ritorna:
// - 0 = ricezione non completata
// - 1 = dati ricevuti correttamente
// - 2 = errore timeout
// - 3 = errore dati in arrivo oltre la dimensione massima
// - 4 = errore dati incompleti
byte Ricevi(Stream &seriale, unsigned char * pacchetto, size_t dimensione) {
// dimensione array di appoggio
#define MAX 2
//indice del byte ricevuto all'interno della struttura di destinazione
//inizializzo con un valore fuori range massimo
static uint32_t i = dimensione;
// indice del carattere arrivato dentro l'array di appoggio
static uint8_t j = 0;
// array di appoggio per decodificare la stringa HEX in numero
static char stringa[MAX + 1] = "";
// valore millis per controllo timeout
static uint32_t inizio = millis();
// valore appena ricevuto
byte valore;
while (seriale.available()) {
valore = seriale.read();
// controllo che carattere è
switch (valore) {
case CHR_START:
// è il carattere di inizio trasmissione
// azzero gli indici
i = 0;
j = 0;
// azzero l'array di appoggio;
stringa[0] = '\0';
stringa[1] = '\0';
stringa[2] = '\0';
// faccio partire il tempo per il timeout
inizio = millis();
break;
case CHR_STOP:
// è il carattere di fine trasmissione
if (i == dimensione) {
return RX_OK;
} else {
//il terminatore è arrivato troppo presto
return RX_LOW;
}
break;
case CHR_SEP:
// è il carattere separatore
// salvo il byte
if (i < dimensione) {
*(pacchetto+i) = strtol(stringa, NULL, 16);
} else {
// ho superato la dimensione della struttura
return RX_HIGH;
}
// devo passare al byte successivo
i++;
j = 0;
// azzero l'array di appoggio;
stringa[0] = '\0';
stringa[1] = '\0';
stringa[2] = '\0';
break;
default:
// è un carattere da convertire
if (j < MAX) {
stringa[j] = valore;
j++;
}
break;
}
// controllo il timeout
if ((millis() - inizio) > TIMEOUT) {
// azzero tutto prima di restituire errore
i = 0;
j = 0;
// azzero l'array di appoggio;
stringa[0] = '\0';
stringa[1] = '\0';
stringa[2] = '\0';
return RX_TIMEOUT;
}
}
return RX_WAIT;
}
Questa è la parte dove definisco la struttura. In teoria è l'unica cosa che serve cambiare secondo necessità. Ovviamente anche questa parte deve essere uguale su tutti gli arduino.
// DEFINIZIONE STRUTTURE DATI - deve essere uguale su trasmettitore e ricevitore
typedef struct {
// id messaggio
uint32_t idmsg;
// id del destinatario
uint32_t destinatario;
// valori da comunicare al destinatario
uint32_t comando;
uint32_t valore;
} t_dati;
#define DIMDATI (sizeof(t_dati))
typedef struct {
t_dati dati;
uint32_t crc;
} t_pack;
#define DIMPACK (sizeof(t_pack))
Esempio trasmissione
SoftwareSerial HC12(HC12TxdPin,HC12RxdPin);
t_pack tx;
tx.dati.idmsg = millis();
tx.dati.destinatario = 1;
tx.dati.comando = 99;
tx.dati.valore = 12345;
tx.crc = calc_crc32((unsigned char *)&tx.dati,DIMDATI);
Trasmetti(HC12, (unsigned char *)&tx, DIMPACK);
Esempio ricezione:
SoftwareSerial HC12(HC12TxdPin,HC12RxdPin);
t_pack rx;
void loop() {
// sono in attesa di comandi dal master
ricevuto = Ricevi(HC12, (unsigned char *)&rx, DIMPACK);
if (ricevuto == RX_OK) {
// i dati sono arrivati
// verifico il CRC
if (rx.crc == calc_crc32((unsigned char *)&rx.dati, DIMDATI)) {
// i dati sono validi
// verifico che la richiesta sia per questo slave
if (rx.dati.destinatario == 1 {
// faccio quello che devo fare
}
}
}
// altre operazioni del loop()
}
Io uso gli HC12, ma funziona con qualunque seriale sia hardware che software.
Non garantisco che sia tutto perfetto. Finora non ho risontrato errori, ma potrebbero comunque essercene.
Se ne trovi qualcuno avvisami.
EDIT: dimenticavo... la funzione per il calcolo del crc l'ho spudoratamente copiata da @gpb01 nella discussione che ho linkato sopra.
RIEDIT: ho provato il codice su arduino nano, pro-mini, mega2560. Su esp non l'ho provato.