Ricevere e salvare dati via seriale da una union

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