Scrivi lo sketch per il tuo device DHT11

In questo tutorial realizzerai il firmware del tuo dispositivo LoRaWan: un device equipaggiato con un sensore DHT11.



Lo sketch Arduino mostrato nel video è il seguente:

/*
  Lettura del sensore DHT11
  Trasmissione dati su rete LoRaWan The Things Network  

  Testato su scheda Arduino MKR WAN 1310 
*/

#include <MKRWAN.h>
#include <DHT.h>

#define JOIN_RETRY_INTERVAL 5000

#define DHTPIN 2     // Pin Digitale connesso al sensore
#define DHTTYPE DHT11   

/*
   Tabella per la decodifica dei parametri di trasmissione

   DataRate  	SF 	BandWidth 	bit/s
	    0      	12 	   125 	     250
	    1   	 	11 	   125       440
	    2 	  	10 	   125 	     980
	    3 	     9 	   125 	   1.760
	    4 	 	   8 	   125 	   3.125
	    5 	  	 7 	   125 	   5.470
	    6 	 	   7 	   250 	  11.000

*/
int tabellaDataRate[][3] = {12,125,250,
                           11,125,440,
                           10,125,980,
                            9,125,1760,
                            8,125,3125,
                            7,125,5470,
                            7,250,11000};


// Impostazione delle credenziali per l'accesso all'Applicazione The things Network (TTN)
// Verificare i valori con quelli riportati nella console TTN
String appEui = "0000000000000000" ;                       
String appKey = "B0999FF0161529F7EFBBE9A3CEA17ACD"; 

LoRaModem modem; // Dichiarazione dell'oggetto per la gestione della trasmissione con protocollo LoRa

DHT dht(DHTPIN, DHTTYPE); // Dichiarazione oggetto per il controllo sensore


void setup() {

  Serial.begin(9600); // Inizializzo la comunicazione seriale con il PC-IDE Arduino
  while(!Serial);  // attendo finché la connessione seriale non è disponibile
  
  Serial.println("");
  Serial.println("===========================================================================");
  Serial.println("");
  Serial.println("Trasmissione dati DHT11 su rete LoRaWan TTN  - v.1.0 by Chirale");
  Serial.println("");
  Serial.println("===========================================================================");
  Serial.println("");
  Serial.println("                          INIZIALIZZAZIONE");
  Serial.println("");
  // inizializzo il sensore
  dht.begin();
  // Tento la connessione LoRaWan
  if (!modem.begin(EU868)) {
    Serial.println("Inizializzazione del modem LoRa non riuscita!");
    Serial.println("Esecuzione del programma terminata.");
    while (1) {} // blocco l'esecuzione del programma con un loop infinito
  };
  Serial.print("Modem LoRa inizializzato.");
  Serial.print("Versione del modem LoRa: ");
  Serial.println(modem.version());
  Serial.print("DeviceEUI: ");  
  Serial.println(modem.deviceEUI()); 
  Serial.println("");   
  Serial.println("Connessione alla rete LoRaWan...");
  
  while(!modem.joinOTAA(appEui, appKey)) {
    Serial.println("Connessione alla rete LoRaWan non riuscita!");
    Serial.println("Attendo....");
    delay(JOIN_RETRY_INTERVAL);
    Serial.println("Riprovo la connessione alla rete LoRaWan");
  }

  Serial.println("Connessione LoRaWan OK.");
  delay(5000); // Attendo un tempo sufficente per consentire al modem di ricevere eventuali messaggi di downlink

}

///-----------------------------------------------------------

void loop() {

  // Definisco la Union per convertire float in array di byte
  union floatToBytes {
  float val;
  byte bytes[4];
  } converter;
  
  
  Serial.println("");
  Serial.println("===========================================================================");
  Serial.println("Inizio il ciclo di verifica parametri rete, lettura sensore, preparazione pacchetti e invio."); 
  Serial.println("---------------------------------------------------------------------------");
  Serial.println("");
  
  Serial.println("Recupero dal Modem LoRa i parametri di rete...");
  Serial.println("");
  int adr = modem.getADR();
  int dataRate = modem.getDataRate(); // Recupero il valore dataRate impostato sul modem
  int sf = tabellaDataRate[dataRate][0]; // Leggo dalla tabella il valore Spreading Factor corrispondente
  int bw = tabellaDataRate[dataRate][1]; // Leggo dalla tabella il valore di Ampiezza di Banda corrispondente
  int bs = tabellaDataRate[dataRate][2]; // Leggo dalla tabella il valore del troughput corrispondente
  Serial.print("Adaptive Data Rate ADR = ");
  Serial.println(adr);
  Serial.print("dataRate = ");
  Serial.println(dataRate);
  Serial.print("Spreading Factor (SF) = ");
  Serial.println(sf);
  Serial.print("Band Widht = ");
  Serial.println(bw);
  Serial.print("Velocità (bit/s) = ");
  Serial.println(bs);
  Serial.println("");
  Serial.println("");
  Serial.println("Leggo i valori dal sensore...");
  float umidita = dht.readHumidity();
  // leggo la temperatura in gradi Celsius (è il default)
  float temperatura = dht.readTemperature();

  // Verifico se le letture sono andate a buon fine
  // in caso negativo termino la funzione loop.
  if (isnan(umidita) || isnan(temperatura)) {
    Serial.println("Errore lettura sensore!");
    delay(5000); // attendo 5 secondi
    return;
  }

  // Calcolo l'indice di calore in gradi Celsius (isFahreheit = false perché il default sarebbe in gradi Farenheit)
  float hic = dht.computeHeatIndex(temperatura, umidita, false);
  Serial.println("Lettura del sensore effettuata.");
  Serial.println("");
  Serial.print("Umidità: ");
  Serial.print(umidita);
  Serial.print("%  Temperatura: ");
  Serial.print(temperatura);
  Serial.print("°C  Indice di Calore: ");
  Serial.print(hic);
  Serial.println("°C ");

  uint8_t fport = 1; // Imposto a 1 la variabile fport
  modem.setPort(fport); // Imposto il parametro fport ad 1
  modem.beginPacket(); // Preparo il pacchetto di dati
  
  // Scrivo i dati nel pacchetto come gruppi di 4 byte
  converter.val = temperatura;
  modem.write(converter.bytes, 4);
  converter.val = umidita;
  modem.write(converter.bytes, 4);
  converter.val = hic;
  modem.write(converter.bytes, 4);
 
  Serial.println("Trasmetto su rete LoRaWan il messaggio...");
  Serial.println("");
  int esito = modem.endPacket();
  
  Serial.print("Esito invio: ");
  Serial.println(esito);
  
  Serial.println("");

  

  // Count down per lasciar trascorrere un tempo sufficiente a rispettare la Fair Use Policy di TTN
  for (int i=10; i>0; i--) {
    Serial.print("-");
    Serial.print(i);
    Serial.print(" ");    
    delay(10000);
  }
  Serial.println("");

}





Lo sketch ricalca lo schema che hai analizzato in dettaglio nel tutorial in cui hai effettuato il test di connessione. e trasmissione alla rete LoRaWan e include gli elementi già visti durante il test del sensore DHT11..

L’unico elemento di novità è la preparazione del pacchetto dati, cioè del payload, che contiene i tre valori letti dal sensore e organizzati nel modo che abbiamo visto nel tutorial dedicato al progetto del formato del payload.

Il punto di partenza sono tre numeri memorizzati in altrettante variabili di tipo float.

La funzione modem.write() consente di scrivere nel payload dati contenuti in variabili di tipo array di byte (o array di uint8_t che nel caso di Arduino è la stessa cosa).

Il problema è quindi quello di trasformare un tipo di dato float in un tipo di dato array di byte senza modificare la posizione dei bit.

Abbiamo visto nel tutorial precedente che un float è rappresentato in Arduino tramite 4 byte consecutivi.

Su Internet puoi trovare diverse soluzioni al problema. Nel nostro caso abbiamo scelto una delle soluzioni che ci sembra più semplice ed elegante. Abbiamo usato una union.

La union è un tipo di dato composito, definito nel linguaggio C/C++ che consente di interpretare una stessa area di memoria secondo diversi tipi di dato.

Per prima cosa abbiamo quindi definito una variabile di tipo union che abbiamo chiamato sibillinamente converter.

union floatToBytes {
  float val;
  byte bytes[4];
  } converter;

floatToBytes è il nome del nuovo tipo di dato che abbiamo creato. Sempre nella stessa istruzione abbiamo dichiarato la variabile converter.

Questa union raggruppa due possibili modi di interpretare un area di memoria di 4 byte: come float oppure come array di byte di dimensione 4. I due modi sono riferiti ai due campi della union che abbiamo chiamato val e bytes.

Quando dichiariamo la variabile converter, il microcontrollore allocherà un’area di 4 byte che sarà referenziata dal nome converter.

Quando nel codice scriveremo converter.var, è come se indicassimo una variabile di tipo float, mentre quando scriveremo converter.bytes indicheremo una variabile array di 4 byte.

A questo punto il problema è risolto.

Se ad esempio vogliamo inserire nel payload i 4 byte della variabile temperatura, basterà usare la seguente sequenza di istruzioni.

  converter.val = temperatura;
  modem.write(converter.bytes, 4);

Nella prima riga copiamo il contenuto della variabile temperatura nella variabile referenziata come converter.val. Entrambe le variabili sono di tipo float e tutto avviene regolarmente.

Nella seconda riga passiamo alla funzione modem.write, che si aspetta come primo parametro un array di byte (la cui lunghezza è specificata dal secondo parametro), converter.bytes che è correttamente un oggetto di tipo array di bytes. Sappiamo che la lunghezza dell’array è 4 bytes, per cui abbiamo indicato 4 come secondo parametro.

Semplice ed elegante.