Ricevere e salvare dati via seriale da una union

La struct/union è solo una variabile come le altre.
Torna utile se si vogliono scambiare informazioni con un altro sistema perché consente di “impacchettare” i dati, ma quello che è realmente necessario quando si vogliono far comunicare per bene dei sistemi tra loro è un protocollo di comunicazione.

Io non l’ho mai usato, ma in questo forum viene consigliato spesso la libreria PJON che fa dietro le quinte tutto ciò che è necessario (e sembrerebbe molto di più).
E’ una possibile opzione, ma se i dati sono pochi e “semplici” io tenterei di fare senza.

Prima di buttarti a capo fitto nello scrivere codice però, devi “progettare” un protocollo. Tu invii un qualcosa sulla seriale, ma dall’altra parte il micro che riceve non può sapere quando e cosa stai inviando.
Se usi un protocollo di “basso livello” ovvero inviare byte per byte i tuoi dati, di solito si struttura un “telegramma” in cui ci sono le informazioni necessarie per decodificare e ricostruire.
Ad esempio, mettiamo che devi inviare una sola variabile unsigned long (4 byte) farei tipo cosi:
[START] [LEN==4] [DATO_0] [DATO_1] [DATO_0] [DATO_0] [CRC8] [STOP]

Il byte START informa l’altro device che sta arrivando un qualcosa, LEN informa il micro che il dato trasmesso è lungo X, DATA_n sono i byte che compongono il tuo dato. CRC8 è un byte di controllo (Ciclic Redundance Check) che ti consente di sapere se c’è stato un errore di trasmissione. E’ bene usare sempre un meccanismo del genere, ma diciamo che all’inizio per semplificare puoi anche omettere. Una volta ricevuto il pacchetto completo, ovvero è arrivato il terminatore STOP, sai che puoi ricostruite DATA con tutti i pezzi necessari.

Questo è sicuramente un approccio molto “efficiente”, ma in realtà io lo uso raramente e solo se ci sono esigenze specifiche di timing/velocità.
Anche con micro con poche risorse come ATMega328 quando posso, preferisco usare qualcosa di più alto livello come ad esempio una stringa formattata JSON per migliorare leggibilità, scalabilità e manutenibilità del codice.

P.S.
Sei proprio sicuro che sia necessario usare due distinte MCU per fare quel che devi?

Ciao @cotestatnt ,
ti ringrazio intanto per il tempo che mi hai dedicato e per avermi spiegato con chiarezza le varie questioni relative a questo tema invece di scrivermi direttamente alcune righe di codice che magari sarebbero funzionate ma che non avrei compreso a pieno.
Io sto realizzando una stazione meteo che metterò in esterno, e voglio semplicemente inviare periodicamente dei dati (es. temperatura, umidità, pm10, ecc.) su Thingspeak via wifi.
Ho collegato i vari sensori ad Arduino UNO e ho collegato ad Arduino UNO un ESP8266 NodeMCU per trasmettere i dati dei sensori via Wifi a Thingspeak ricevuti da Arduino UNO.
Non riesco a capire quale è la soluzione migliore, semplice e sicura per trasmettere questi dati dall’ESP8266 NodeMCU ad Arduino UNO.
Le variabili da trasmettere sarebbero al massimo 5 o 6.
In questo caso come mi suggerisci di procedere?
Esiste qualche guida passo passo che posso seguire?
Io ho intuito quello che vuoi dirmi sulla questione “telegramma” ma non so realizzarla, avrei bisogno di una guida da seguire o di un esempio semplice.

Grazie infinite

La soluzione di gran lunga più semplice (come immaginavo) è usare il NodeMCU al posto dell’Arduino. Ci sono schede che hanno anche la stesso forma/piedinatura.

Se invece vuoi comunque percorrere la strada delle due MCU, guide non so consigliartene semplicemente perché non ne ho mai usate.
Google è da tempo il mio migliore amico in questi casi :sunglasses:
Se riesco oggi provo a preparare un esempio (sulla base del link che hai messo)

Ho letto anche io che si può usare direttamente il NodeMCU al posto dell’Arduino.
Sto facendo questo progetto per imparare ad utilizzare Arduino, è il mio primo progetto, e vorrei quindi percorrere la strada delle due MCU per imparare qualcosa di nuovo che ancora non conosco, anche se magari non è la strada più semplice.
Se vorrai dedicarmi altro tempo per preparare un esempio per inviare e ricevere i dati sulla base del link che ho segnalato all’inizio mi sarebbe sicuramente utilissimo (e spero anche per altri che stanno provando a percorrere questa strada).
Sul web e su Google ci sono molte informazioni frammentate e confusionarie e poco chiare, almeno quelle che ho trovato finora.

Grazie mille comunque per l’aiuto che mi hai dato fino adesso

ciao.
aggiungo la mi opinione…

  • se lo fai a scopo didattico benvenga, ma usare solo nodemcu invece di uno + nodemcu equivale comunque ad imparare ad usare arduino, visto che stai programmando nodemcu dall’ide di arduino…
  • riguardo il tuo problmema specifico (invio ricezione dati), ho affrontato un problema simile tempo fa. puoi vedere la discussione qui. se serve posso postarti il codice che uso io per inviare / ricevere.

Allora, questo è un possibile esempio molto basilare che implementa il minimo indispensabile per una trasmissione dati affidabile basato sull’utilizzo di struct/union
Prendilo con le molle perché l’ho testato solo con l’utilizzo di un terminale seriale inviando “manualmente” i byte che corrispondono all’equivalente struct dove:
dato1 = 9999
dato2 = 696
dato3 = 99999999
dato4 = -10237

equivalenti alla sequenza di 15 byte 02 0F 27 B8 02 FF E0 F5 05 03 D8 FF FF 78 03
(scelti anche per fare in modo che all’interno dei dati “buoni” capiti sia il byte equivalente a START che quello di STOP)

#define START  0x02  // Corrisponde al codice ascii STX (start of trasmission)
#define STOP   0x03  // Corrisponde al codice ascii ETX (end of trasmission)

// struttura dati
typedef struct
{
  int dato1;  // 2 byte
  int dato2;  // 2 byte
  long dato3; // 4 byte
  long dato4; // 4 byte  => totale 12 byte
} Dati_S;

#define PAYLOAD_LEN  12   
union {
  Dati_S myData;
  byte raw_data[PAYLOAD_LEN];  // raw_data condivide la stessa area di memoria di myData
} payload;

// Questa funzione prende come parametro un puntatore ad un array di byte che sarà quello definito nella union
// e stampa sulla seriale il contenuto "incapsulato" in un telegramma [START][DATA_Byte]*N [CRC8][STOP]
void txData(const byte *data) {
  byte crc = CRC8(data, PAYLOAD_LEN);
  Serial.write(START);
  Serial.write(data, PAYLOAD_LEN);
  Serial.write(crc);
  Serial.write(STOP);
}

// Questa funzione prende come parametro il puntatore ad un array di byte.
// Quando viene ricevuto un messaggio coerente allo schema definito, 
// copia il contenuto del messaggio nell'array di byte passato come parametro.
bool rxData(byte *data) {
  static byte rx_buffer[PAYLOAD_LEN + 2];  // Lunghezza payload +CRC +STOP
  static int pos = -1;
  while (Serial.available()) {
    byte b = Serial.read();

    // Se pos > -1 significa che è arrivato il byte START 
    // e che quindi devo accumulare i byte ricevuti dalla seriale nel buffer locale
    if (pos > -1) {
      rx_buffer[pos++] = b;
    }

    // Se c'è un errore e non arriva il byte di STOP, tralascio il messaggio
    if (pos > PAYLOAD_LEN + 2) {
        // Errore in ricezione (troppi byte ricevuti)
        Serial.println("Il numero dei byte ricevuti non corrisponde alla lunghezza prevista");
        pos = -1;              // Pronto per nuova ricezione
        return false;
    }

    // E' arrivato il byte di START e non sto ricevendo dati, inizializzo l'indice del buffer di ricezione a 0
    // e cancello il contenuto del buffer di ricezione (non è strettamente necessario);
    if (b == START && pos < 0) {
      pos = 0;
      memset(rx_buffer, '\0', sizeof(rx_buffer));   // Reset del buffer locale
    }

    // E' arrivato il byte di STOP esattamente quando era atteso
    if ((b == STOP) && (pos == PAYLOAD_LEN + 2)) {
      byte crc_calcolato = CRC8(rx_buffer, PAYLOAD_LEN);
      byte crc_ricevuto = rx_buffer[pos - 2];
      Serial.print("\nCRC calcolato: 0x");
      Serial.println(crc_calcolato, HEX);

      Serial.print("CRC ricevuto: 0x");
      Serial.println(crc_ricevuto, HEX);

      if (crc_calcolato == crc_ricevuto ) {
        // Il messaggio è stato ricevuto correttamente
        memcpy(data, rx_buffer, PAYLOAD_LEN);        
        pos = -1;              // Pronto per nuova ricezione
        return true;
      }
      else {
        // Errore in ricezione.
        Serial.println("Dati tricevuti non coerenti");
        pos = -1;              // Pronto per nuova ricezione
        return false;
      }
    }
  }
  return false;
}


void setup() {
  Serial.begin(115200);
  Serial.println("\nTEST invio struct");
}

void loop() {
  bool res = rxData(payload.raw_data);
  if (res) {
    Serial.print("Messaggio ricevuto correttamente. RAW byte: ");
    for (uint8_t i = 0; i < PAYLOAD_LEN; i++) {
      Serial.print(payload.raw_data[i], HEX);
      Serial.print(" ");
    }
    Serial.println("Valori aggiornati:");
    Serial.print("dato1 = ");
    Serial.println(payload.myData.dato1);
    Serial.print("dato2 = ");
    Serial.println(payload.myData.dato2);
    Serial.print("dato3 = ");
    Serial.println(payload.myData.dato3);
    Serial.print("dato4 = ");
    Serial.println(payload.myData.dato4);
  }

  static unsigned long sendTime;
  if (millis() - sendTime > 10000) {
    sendTime = millis();
    txData(payload.raw_data);
  }

}

//CRC-8 - algoritmo basato sulle formule di CRC-8 di Dallas/Maxim
//codice pubblicato sotto licenza GNU GPL 3.0
byte CRC8(const byte *data, size_t len) {
  byte crc = 0x00;
  while (len--) {
    byte extract = *data++;
    for (byte tempI = 8; tempI; tempI--) {
      byte sum = (crc ^ extract) & 0x01;
      crc >>= 1;
      if (sum) {
        crc ^= 0x8C;
      }
      extract >>= 1;
    }
  }
  return crc;
}

Ciao @cotestatnt ,
ti ringrazio per questo super lavoro, mi sono preso un giorno per provare a caricare il codice e a studiarmelo un po’.
Essendo alle prime armi ammeto che non mi è tutto chiaro al 100%.
Ho comunque capito l’aspetto dello start, del controllo, dello stop, della verifica se il codice è completo.
Non mi è però chiaro un aspetto: questo codice va caricato identico su entrambe le schede in modo che possano comunicare a vicenda?
Inoltre, come faccio a testare inviando qualcosa per poi riceverla dall’altra parte?

Grazie infinite

Ciao @fratt,
durante le mie ricerche mi era capitato di imbattermi anche nella tua discussione che mi hai linkato.
Sarebbe certo molto utile se potessi postare il codice che utilizzi tu, così posso confrontalo con quello di @cotestatnt e soprattutto potrebbe essere utile a chi si imbatte in questa discussione con le mie stesse domande e difficoltà.

Grazie mille super gentile

Appena riesco ad accedere il pc posto tutto (spero stasera ma non garantisco).
Però @cotestatnt su questi argomenti è molto ferrato… Mi sa che è un confronto impari…

Per me ogni aiuto e ogni spunto è utile per capire il funzionamento di questo argomento, che al momento non mi è ancora chiarissimo, nonostante il vostro fondamentale aiuto.
I forum servono per condividere e aiutarsi non per vedere chi è più bravo, altrimenti io sarei già ultimo ahah! :rofl:

Si dovrebbe andare, ma come ti ho detto non l’ho provato.
Per fare dei test, potresti usare una software serial tra i due dispositivi così ti rimangono libere le due porte COM associate alle USB per il monitor seriale in modo da poter vedere se funziona come previsto.
Domani se ho tempo ci provo anche io.

Ni… Io come già detto in questi casi userei un approccio di più alto livello a scapito dell’efficienza proprio per semplificarmi la vita tra le altre cose :sweat_smile:

Purtroppo non so fare quanto mi hai suggerito, quindi non riesco a chiudere il cerchio su come mandare nel concreto quei 4 dati da una scheda all’altra e visualizzare il risultato ricevuto.

C’è anche Serial .readBytes(buffer, length), con cui è possible leggere un intero array di byte. Un unione o una struct possono essere considerati array di byte, (byte*)&union

@begins per quello ti rimando al reference per la software serial

Sono riuscito a provare lo sketch solo adesso e funziona più o meno come previsto.
Più o meno perché come diceva Guglielmo nel post di @fratt, a causa delle diverse piattaforme AVR-ESP8266 i tipi di dati int e long non hanno la stessa dimensione in byte.
Per fare in modo che lo spazio occupato dalle variabili coincida tra le due mcu ho ridefinito la struct usando gli alias int16_t e int32_t che sono pensati proprio per definire una variabile int (o unsigned int uintxx_t) di lunghezza esplicita (8, 16, 32, 64).

Ecco l’esempio aggiornato:

#include <SoftwareSerial.h>
 
/* 
 * RX is digital pin 6 (connect to TX of other device)
 * TX is digital pin 7 (connect to RX of other device)
*/

SoftwareSerial mcuSerial(6, 7); // RX, TX
// Sull'altro dispositivo i pin vanno invertiti (RX = 7, TX = 6);

// Impostiamo la prima mcu come TX e l'altra come RX (-> false)
#define THIS_IS_TX true

#define START  0x02  // Corrisponde al codice ascii STX (start of trasmission)
#define STOP   0x03  // Corrisponde al codice ascii ETX (end of trasmission)

// struttura dati
typedef struct
{
  int16_t dato1;  // 2 byte
  int16_t dato2;  // 2 byte
  int32_t dato3; // 4 byte
  int32_t dato4; // 4 byte  => totale 12 byte
} Dati_S;

#define PAYLOAD_LEN  12   
union {
  Dati_S myData;
  byte raw_data[PAYLOAD_LEN];  // raw_data condivide la stessa area di memoria di myData
} payload;

// Questa funzione prende come parametro un puntatore ad un array di byte che sarà quello definito nella union
// e stampa sulla seriale il contenuto "incapsulato" in un telegramma [START][DATA_Byte]*N [CRC8][STOP]
void txData(const byte *data) {
  byte crc = CRC8(data, PAYLOAD_LEN);
  mcuSerial.write(START);
  mcuSerial.write(data, PAYLOAD_LEN);
  mcuSerial.write(crc);
  mcuSerial.write(STOP);
}

// Questa funzione prende come parametro il puntatore ad un array di byte.
// Quando viene ricevuto un messaggio coerente allo schema definito, 
// copia il contenuto del messaggio nell'array di byte passato come parametro.
bool rxData(byte *data) {
  static byte rx_buffer[PAYLOAD_LEN + 2];  // Lunghezza payload +CRC +STOP
  static int pos = -1;
  while (mcuSerial.available()) {
    byte b = mcuSerial.read();

    // Se pos > -1 significa che è arrivato il byte START 
    // e che quindi devo accumulare i byte ricevuti dalla seriale nel buffer locale
    if (pos > -1) {
      rx_buffer[pos++] = b;
    }

    // Se c'è un errore e non arriva il byte di STOP, tralascio il messaggio
    if (pos > PAYLOAD_LEN + 2) {
        // Errore in ricezione (troppi byte ricevuti)
        Serial.println("Il numero dei byte ricevuti non corrisponde alla lunghezza prevista");
        pos = -1;              // Pronto per nuova ricezione
        return false;
    }

    // E' arrivato il byte di START e non sto ricevendo dati, inizializzo l'indice del buffer di ricezione a 0
    // e cancello il contenuto del buffer di ricezione (non è strettamente necessario);
    if (b == START && pos < 0) {
      pos = 0;
      memset(rx_buffer, '\0', sizeof(rx_buffer));   // Reset del buffer locale
    }

    // E' arrivato il byte di STOP esattamente quando era atteso
    if ((b == STOP) && (pos == PAYLOAD_LEN + 2)) {
      byte crc_calcolato = CRC8(rx_buffer, PAYLOAD_LEN);
      byte crc_ricevuto = rx_buffer[pos - 2];
      Serial.print("\nCRC calcolato: 0x");
      Serial.println(crc_calcolato, HEX);

      Serial.print("CRC ricevuto: 0x");
      Serial.println(crc_ricevuto, HEX);

      if (crc_calcolato == crc_ricevuto ) {
        // Il messaggio è stato ricevuto correttamente
        memcpy(data, rx_buffer, PAYLOAD_LEN);        
        pos = -1;              // Pronto per nuova ricezione
        return true;
      }
      else {
        // Errore in ricezione.
        Serial.println("Dati tricevuti non coerenti");
        pos = -1;              // Pronto per nuova ricezione
        return false;
      }
    }
  }
  return false;
}


void setup() {
  Serial.begin(115200);
  Serial.println("\nTEST invio struct");
  mcuSerial.begin(9600);

  // Se questo è il device che trasmette, inizializziamo payload con dei valori
  if(THIS_IS_TX) {
    payload.myData.dato1 = 0;
    payload.myData.dato2 = 696;
    payload.myData.dato3 = 99999999;
    payload.myData.dato4 = -10237;
    txData(payload.raw_data);
  }
}

void loop() {
  bool res = rxData(payload.raw_data);
  if (res) {
    Serial.print("Messaggio ricevuto correttamente. RAW byte: ");
    for (uint8_t i = 0; i < PAYLOAD_LEN; i++) {
      Serial.print(payload.raw_data[i], HEX);
      Serial.print(" ");
    }
    Serial.println("Valori aggiornati:");
    Serial.print("dato1 = ");
    Serial.println(payload.myData.dato1);
    Serial.print("dato2 = ");
    Serial.println(payload.myData.dato2);
    Serial.print("dato3 = ");
    Serial.println(payload.myData.dato3);
    Serial.print("dato4 = ");
    Serial.println(payload.myData.dato4);
  }

  // Se questo è il device che trasmette, inviamo il payload attuale ogni 5s
  static unsigned long sendTime;
  if (millis() - sendTime > 5000 && THIS_IS_TX) {
    Serial.println("Invio dati alla seconda MCU");
    sendTime = millis();
    payload.myData.dato1++ ;    // Incremento questa variabile di 1
    payload.myData.dato3--;     // Decremento questa variabile di 1
    txData(payload.raw_data);
  }

}

//CRC-8 - algoritmo basato sulle formule di CRC-8 di Dallas/Maxim
//codice pubblicato sotto licenza GNU GPL 3.0
byte CRC8(const byte *data, size_t len) {
  byte crc = 0x00;
  while (len--) {
    byte extract = *data++;
    for (byte tempI = 8; tempI; tempI--) {
      byte sum = (crc ^ extract) & 0x01;
      crc >>= 1;
      if (sum) {
        crc ^= 0x8C;
      }
      extract >>= 1;
    }
  }
  return crc;
}
2 Likes

Riguardo “l’impacchettamento” dati citato da @gpb01 io avevo capito che a seconda della piattaforma potrebbe anche succedere che i dati vengono “allineati” al tipo che occupa più spazio, con l’inserimento di campi fittizi di riempimento. Per questo, visto che non avevo problemi di spazio e visto che mi servivano solo valori interi, ho usato per tutti i campi il tipo uint32_t.
Comunque stasera posto un esempio di come ho fatto io (ieri sera mi sono addormentato prima di riuscire ad accendere il pc…).

Si anche io avevo capito la stessa cosa, ma in questo caso usando gli alias ha funzionato, quindi probabilmente non viene eseguito il padding (o viene eseguito allo stesso modo per le due piattaforme)

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.

2 Likes

Vi ringrazio tantissimo, ho provato i vostri codici e funzionano alla grande.
Devo ora con molta calma leggerli e studiarmeli bene per comprenderli a pieno ma mi avete dato un aiuto immenso.
Spero che questa discussione possa tornare utile anche a molti altri che avranno la necessità di far comunicare due schede tramite seriale.

Grazie infinite!!! :star_struck:

This topic was automatically closed 120 days after the last reply. New replies are no longer allowed.