Realizza uno sketch per verificare la comunicazione LoRaWan

Dopo aver effettuato la registrazione del tuo Arduino MKR WAN 1310 nell’applicazione che hai definito su The Things Stack, puoi verificare la correttezza della configurazione provando ad effettuare il Join alla rete LoRaWan e a trasmettere qualche pacchetto dati di prova.

In questo tutorial vedrai come effettuare questo test.



Il codice dello sketch Arduino che ti permette di effettuare la prova generale di connessione alla rete e trasmissione è riportato di seguito.

Il valore di AppEUI è già valorizzato con una stringa di zeri, mentre il valore della variabile globale AppKey deve essere aggiornato con il valore corretto recuperato dalla console TTS.

Non dimenticare quest’ultimo passaggio prima di compilare lo sketch, altrimenti la connessione con la rete LoRa non sarà possibile.

/*
  Test del protocollo di comunicazione e definizione del payload
  Trasmissione dati su rete LoRaWan The Things Network  

  Testato su scheda Arduino MKR WAN 1310 
*/

#include <MKRWAN.h>
#define JOIN_RETRY_INTERVAL 5000

/*
   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 = "xxxxxxxxxxxxxxxxxxxxxxxxx"; // ATTENZIONE!!! inserire al posto delle xxx la appKey generata sull'applicazione TTS 

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

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("Tester di Comunicazione su rete LoRaWan TTN  - v.1.0 by Chirale");
  Serial.println("");
  Serial.println("===========================================================================");
  Serial.println("");
  Serial.println("                          INIZIALIZZAZIONE");
  Serial.println("");
  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() {
  
  Serial.println("");
  Serial.println("===========================================================================");
  Serial.println("Inizio il ciclo di verifica parametri rete, preparazione pacchetti e invio."); 
  Serial.println("---------------------------------------------------------------------------");
  Serial.println("");
  Serial.println("                          PRIMO PACCHETTO DATI");
  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("");
  
  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

  uint8_t buf[] = {0X0A, 0X0B}; // Inizializzo un array di byte contenente i due valori esadecimali 0A e 0B (10 e 11 in decimale)
  modem.write(buf,2); // Aggiungo l'array di lunghezza due byte al pacchetto
 
  Serial.print("Trasmetto su rete LoRaWan il messaggio con payload= ");
  Serial.print(buf[0],HEX);
  Serial.print(buf[1],HEX);
  Serial.print(" e indicatore fport= ");
  Serial.println(fport);
  Serial.println("");
  int esito = modem.endPacket();
  
  Serial.print("Esito invio: ");
  Serial.println(esito);
  
  Serial.println("");

  delay(10000); // attendo 10 secondi per rispettare la fair use policy
  Serial.println("---------------------------------------------------------------------------");
  Serial.println("");
  Serial.println("                          SECONDO PACCHETTO DATI");
  Serial.println("");
  Serial.println("Recupero dal Modem LoRa i parametri di rete...");
  Serial.println("");
  adr = modem.getADR(); // Recupero dal modem il valore del flag ADR
  dataRate = modem.getDataRate(); // Recupero il valore dataRate impostato sul modem
  sf = tabellaDataRate[dataRate][0]; // Leggo dalla tabella il valore Spreading Factor corrispondente
  bw = tabellaDataRate[dataRate][1]; // Leggo dalla tabella il valore di Ampiezza di Banda corrispondente
  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("");
  
  fport = 2; // Imposto a 2 la variabile fport in modo da inviare un secondo pacchetto con diverso fport
  modem.setPort(fport); // Imposto il parametro fport ad 1

  modem.beginPacket(); // Preparo il pacchetto di dati

  buf[0] = 0X0C;
  buf[1] = 0X0D; // modifico l'array di byte inserendo i due valori esadecimali 0C e 0D (12 e 13 in decimale)
  modem.write(buf,2); // Aggiungo l'array di lunghezza due byte al pacchetto
 
  Serial.print("Trasmetto su rete LoRaWan il messaggio con payload= ");
  Serial.print(buf[0],HEX);
  Serial.print(buf[1],HEX);
  Serial.print(" e indicatore fport= ");
  Serial.println(fport);
  Serial.println("");
  esito = modem.endPacket();
  
  Serial.print("Esito invio: ");
  Serial.println(esito);

  Serial.println("");
  Serial.println("---------------------------------------------------------------------------");
  Serial.println("");
  Serial.println("In attesa del prossimo ciclo di lettura e invio...");

  // 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("");

}


L’analisi dettagliata di questo sketch è interessante per comprendere il meccanismo di funzionamento e utilizzo della librearia MKRWAN di Arduino.

#include <MKRWAN.h>
#define JOIN_RETRY_INTERVAL 5000

Queste due righe contengono la direttiva al compilatore per caricare il file contenente le definizioni relative alla libreria MKRWAN e la definizione di una macro, utile per parametrizzare l’intervallo di tempo, in millisecondi, in cui il nostro programma rimarrà inattivo prima di tentare nuovamente una operazione di join alla rete.

Se un tentativo di join fallisce i motivi possono essere diversi, ma in tutti i casi è bene lasciar passare un tempo di diversi secondi, ancora meglio se di diversi minuti, prima di riprovare. La banda radio usata da LoRa non deve mai essere sovrautilizzata.

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

In questa istruzione è stata dichiarata e inizializzata una variabile, chiamata tabellaDataRate, che è stata definita come un array bidimensionale di numeri interi. La doppia coppia di parentesi quadre dichiara la nostra intenzione di usare la variabile come un array a due dimensione, interpretabili come righe e colonne. Il primo indice posto tra parentesi quadre indica l’indice di riga, il secondo indice indica l’indice di colonna. Nella istruzione di dichiarazione, poiché l’array bidimensionale viene anche inizializzato con dei valori, il primo indice non è riportato, poiché viene automaticamente calcolato dalla inizializzazione. Il secondo indice è 3, questo vuol dire che il nostro array avrà tre colonne. Poiché dopo l’operatore ‘=’ inseriamo una sequenza di 21 valori, il compilatore è in grado di capire che il nostro array avrà 7 righe (7 righe X 3 colonne = 21 valori).

Ricorda che in fase di dichiarazione di un array, tra parentesi quadre deve essere indicata la dimensione, mentre quando useremo l’array nel codice, l’indice indicherà la specifica riga o colonna e in questo caso il valore partirà da zero. Ad esempio, per indicare il valore contenuto nella terza riga e seconda colonna scriveremo tabellaDataRate[2][1].

Questa tabella ci servirà per ricavare facilmente i valori dello Spreading Factor, larghezza di banda e velocità in b/s una volta conosciuto il valore numerico del parametro Data Rate. Poiché il DR LoRaWan varia da 0 a 6, il suo valore potrà essere usato come indice di riga nella tabella.

String appEui = "0000000000000000" ;                       
String appKey = "xxxxxxxxxxxxxxxxxxxxxx"; 

Queste due variabili globali di tipo stringa, sono usate per parametrizzare i due valori che serviranno in fase di autenticazione (join) alla rete.

La variabile appEUI è già valorizzata con la stringa di zeri che abbiamo definito nella nostra applicazione TTS in fase di registrazione del device, mentre la variabile appKey deve essere valorizzata con il valore ricavato dalla console TTS, come mostrato nel video. La sequenza di ‘x’ deve essere quindi sostituita con il valore effettivo di appKey.

LoRaModem modem;

Con questa istruzione dichiariamo una variabile globale, più precisamente un oggetto, che abbiamo chiamato modem, appartenente alla classe LoRaModem definita nella libreria MKRWAN, che costituirà in nostro riferimento per parlare con il modem LoRa a bordo della scheda Arduino.

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("Tester di Comunicazione su rete LoRaWan TTN  - v.1.0 by Chirale");
  Serial.println("");
  Serial.println("===========================================================================");
  Serial.println("");
  Serial.println("                          INIZIALIZZAZIONE");
  Serial.println("");

Questa prima parte della funzione setup inizializza la comunicazione seriale e stampa sul monitor seriale il messaggio di benvenuto.

Nota che dopo la Serial.begin è presente l’istruzione while(!Serial); che blocca in un loop infinito l’esecuzione del programma finché non apriamo il monitor seriale.

Non è la prima volta che usiamo questo escamotage. Purtroppo, sulle schede MKR l’esecuzione della funzione setup avviene in modo molto veloce appena alimentiamo la scheda e questo non ci da il tempo di aprire il monitor seriale per visualizzare i messaggi. Tramite questa istruzione, invece, abbiamo tutto il tempo di aprire il monitor. Il programma resta fermo finché non lo facciamo.

Ricorda che questo rende impossibile avviare lo sketch senza collegare la scheda Arduino al PC. Prima di installare in esercizio il prototipo questa istruzione andrebbe eliminata o sostituita con un delay di alcuni secondi.

 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
  };

Questa istruzione avvia il modem LoRa passando come configurazione l’identificativo della banda radio utilizzabile in Europa, cioè EU868 (868 Mhz). La chiamata modem.begin è inserita nel blocco test di una istruzione if, in modo da verificare quale sia il valore restituito. L’espressione !modem.begin() viene valutata come valore logico ‘vero’ quando modem.begin() restituisce il valore logico ‘falso’. Se il valore restituito dalla begin è ‘falso’, vuol dire che l’avvio del modem è fallito e quindi è inutile proseguire.

Nel corpo della if viene stampato il messaggio di errore e bloccato il programma tramite una istruzione che genera 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("");   

Se il programma non viene bloccato, vuol dire che l’inizializzazione del modem LoRa è andata a buon fine e quindi la funzione setup prosegue inviando alcuni messaggi al monitor seriale.

In particolare, come già visto in un precedente tutorial, vengono ricavati e stampati i valori relativi alla versione del firmware del modem e il valore del DeviceEUI.

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
}

Nell’ultima parte della funzione setup viene tentata l’operazione di join alla rete LoRaWan attraverso la chiamata alla funzione modem.joinOTAA a cui vengono passati i due parametri appEUI e appKey.

Come si intuisce dal nome della funzione, il metodo utilizzato è OTAA, cioè Over The Air Activation. LoRaWan prevede due diversi meccanismi di autenticazione e attivazione iniziale dei device. L’OTAA è il più sicuro e quello raccomandato.

Quando abbiamo registrato il device nella applicazione TTS il device è stato anche registrato dalla piattaforma TTS in un apposito server che gestisce l’attivazione e l’autenticazione dei dispositivi.

Il meccanismo OTAA prevede che il device si identifichi attraverso il suo DeviceEUI e trasmetta le credenziali appEUI (chiamato anche joinEUI) e appKey. Se queste credenziali sono corrette, il server di autenticazione genera le chiavi di crittografia e abilita il device. Le chiavi di crittografia sono valide solo da questo momento in poi e non saranno più valide se il device effettua un nuovo join alla rete, con conseguente generazione di nuove chiavi. In questo modo la sicurezza del sistema è più robusta.

L’altro metodo, denominato ABP (Abreast-Based Provisioning) è sconsigliato poiché prevede l’uso di chiavi di crittografia fisse memorizzate nel device e nel server TTS.

La funzione modem.joinOTAA() restituisce il valore logico ‘vero’ se l’operazione ha avuto successo e la sessione LoRaWan può considerarsi avviata. Se il valore restituito è ‘falso’, il tentativo non è andato a buon fine. Anche in questo caso è opportuno che il programma verifichi e gestisca l’esito di questa operazione.

Per questo motivo abbiamo inserito la chiamata a modem.joinOTAA all’interno del blocco di test di una istruzione condizionale.

In questo caso, però, abbiamo usato l’istruzione while e non l’istruzione if, poiché se il tentativo non è andato a buon fine, non è detto che dobbiamo interrompere definitivamente l’esecuzione del programma. Il problema potrebbe essere solo temporaneo e quindi tramite la while generiamo un ciclo che riprova ad effettuare la connessione ad intervalli regolari. La lunghezza di questi intervalli è regolata dal parametro JOIN_RETRY_INTERVAL.

Parleremo fra poco di cosa fare se il join continua a fallire.

void loop() {
  
  Serial.println("");
  Serial.println("===========================================================================");
  Serial.println("Inizio il ciclo di verifica parametri rete, preparazione pacchetti e invio."); 
  Serial.println("---------------------------------------------------------------------------");
  Serial.println("");
  Serial.println("                          PRIMO PACCHETTO DATI");
  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("");
  

Se l’operazione di join va a buon fine, la funzione setup termina e viene chiamata la funzione loop.

Nella prima parte viene stampato un messaggio informativo e vengono recuperati i due parametri ADR e DataRate, tramite chiamate alle due corrispondenti funzioni dell’oggetto modem.

Se ADR, che sta per Automatic Data Rate, è pari ad 1 vuol dire che è abilitata la definizione dinamica del data rate da parte della rete.

Quando la sessione LoRaWan è impostata in questo modo, il server TTS rileva la qualità del segnale relativo ai messaggi ricevuti e se tale qualità è elevata, manda un messaggio in downlink al device chiedendo di innalzare il data rate per le successive trasmissioni.

Se la qualità è bassa, viene chiesto al device di abbassare il data rate.

La variabile dataRate viene quindi utilizzata come indice di riga nell’array tabellaDataRate per recuperare i tre valori Spreading Factor, larghezza di banda (Band Width) e velocità in b/s e valorizzare le tre corrispondenti variabili sf, bw e bs.

  uint8_t fport = 1; // Imposto a 1 la variabile fport
  modem.setPort(fport); // Imposto il parametro fport ad 1

Queste due righe impostano il valore del campo fport, previsto dal formato di pacchetto LoRaWan, al valore 1. Come hai appreso nel tutorial dedicato al protocollo LoRaWan, il progettista ha a disposizione questo campo pari ad un byte per inserire un valore numerico, compreso tra 0 e 254 (0x00 – 0xFF in esadecimale) per contrassegnare il campo payload.

Di solito questo parametro viene utilizzato per indicare il tipo di payload che si sta trasmettendo. Applicazioni complesse possono prevedere che uno stesso device trasmetta pacchetti di dati (payload) che contengono informazioni diverse o che abbiano significati diversi. Il campo fport può essere utilmente utilizzato per contrassegnare il payload senza dover sprecare un byte all’interno dello spazio dedicato al payload.

La prima riga dichiara e inizializza al valore 1 la variabile che abbiamo chiamato fport. Nota che il tipo di dato utilizzato è uint8_t. Questo tipo di dato è di fatto equivalente al tipo di dato chiamato più semplicemente ‘byte‘.

Quando si lavora nella costruzione di pacchetti di dati e si manipolano numeri a livello di bit, i progettisti preferiscono utilizzare i tipi di dato come uint8_t che specificano senza ambiguità il formato delle variabili e dei registri di memoria. uint8_t, vuol dire numero intero, senza segno, rappresentato con 8 bit. Il tipo byte potrebbe essere ambiguo e non essere rappresentato sempre nello stesso modo su microcontrollori differenti.

modem.beginPacket();

Con questa istruzione chiediamo al modem di preparare un buffer di trasmissione, inizialmente vuoto, in cui andremo a scrivere successivamente i bit che costituiscono il payload.

  uint8_t buf[] = {0X0A, 0X0B}; // Inizializzo un array di byte contenente i due valori esadecimali 0A e 0B (10 e 11 in decimale)
  modem.write(buf,2);

Nella prima riga di questo blocco viene dichiarata e inizializzata una variabile, chiamata buf, definita come array di valori di tipo uint8_t e valorizzata con i due valori 0x0A e 0x0D, che in decimale corrispondono a 10 e 11.

L’istruzione successiva scrive i due valori, lunghi esattamente un byte ciascuno nel buffer di trasmissione. La funzione modem.write() ha due possibili forme. Se non viene specificato il secondo parametro, il primo parametro deve essere lungo esattamente un byte, nel nostro caso usiamo la forma estesa e come secondo parametro specifichiamo la lunghezza dell’array fornito come primo parametro.

  Serial.print("Trasmetto su rete LoRaWan il messaggio con payload= ");
  Serial.print(buf[0],HEX);
  Serial.print(buf[1],HEX);
  Serial.print(" e indicatore fport= ");
  Serial.println(fport);
  Serial.println("");
  int esito = modem.endPacket();
  
  Serial.print("Esito invio: ");
  Serial.println(esito);
  
  Serial.println("");

In questo blocco vengono inviati alcuni messaggi informativi sul monitor seriale e con l’istruzione modem.endPacket() viene conclusa l’operazione di preparazione del pacchetto e ne viene richiesta la trasmissione.

Il valore restituito dalla funzione modem.endPacket() è il numero di byte che compongono il payload. Nel nostro codice abbiamo definito una variabile di tipo intero, chiamata esito, che raccoglie questo valore che viene quindi stampato sul monitor seriale.

  delay(10000); // attendo 10 secondi per rispettare la fair use policy
  Serial.println("---------------------------------------------------------------------------");
  Serial.println("");
  Serial.println("                          SECONDO PACCHETTO DATI");
  Serial.println("");
  Serial.println("Recupero dal Modem LoRa i parametri di rete...");
  Serial.println("");
  adr = modem.getADR(); // Recupero dal modem il valore del flag ADR
  dataRate = modem.getDataRate(); // Recupero il valore dataRate impostato sul modem
  sf = tabellaDataRate[dataRate][0]; // Leggo dalla tabella il valore Spreading Factor corrispondente
  bw = tabellaDataRate[dataRate][1]; // Leggo dalla tabella il valore di Ampiezza di Banda corrispondente
  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("");
  
  fport = 2; // Imposto a 2 la variabile fport in modo da inviare un secondo pacchetto con diverso fport
  modem.setPort(fport); // Imposto il parametro fport ad 1

  modem.beginPacket(); // Preparo il pacchetto di dati

  buf[0] = 0X0C;
  buf[1] = 0X0D; // modifico l'array di byte inserendo i due valori esadecimali 0C e 0D (12 e 13 in decimale)
  modem.write(buf,2); // Aggiungo l'array di lunghezza due byte al pacchetto
 
  Serial.print("Trasmetto su rete LoRaWan il messaggio con payload= ");
  Serial.print(buf[0],HEX);
  Serial.print(buf[1],HEX);
  Serial.print(" e indicatore fport= ");
  Serial.println(fport);
  Serial.println("");
  esito = modem.endPacket();
  
  Serial.print("Esito invio: ");
  Serial.println(esito);

  Serial.println("");

Questo blocco di codice è del tutto analogo a quanto visto sopra.

Dopo un tempo di attesa di 10 secondi (inserito per fare i bravi ragazzi mantenendo basso il tasso di utilizzo della banda radio), viene valorizzato il parametro fport a 2 e viene preparato e spedito un nuovo payload, sempre di due soli byte, contenente i valori 0x0c e 0x0D, corrispondenti ai valori decimali 12 e 13.

In questo modo, sulla console TTS vedremo transitare due messaggi con diverso valore di fport e con diverso contenuto.

I valori trasmessi non hanno ovviamente alcun significato. Avremmo potuto trasmettere una qualunque altra sequenza di numeri casuali. Lo scopo è testare il funzionamento della nostra configurazione.

Serial.println("---------------------------------------------------------------------------");
  Serial.println("");
  Serial.println("In attesa del prossimo ciclo di lettura e invio...");

  // 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("");

}

La funzione loop termina con questo blocco di codice, che implementa un semplice count down che viene visualizzato sul monitor seriale.

Il suo scopo è semplicemente quello di far passare del tempo, dando comunque un segno di vita, prima di terminare la chiamata corrente di loop e avviare un nuovo ciclo di trasmissione.

Che fare se il join alla rete non riesce?

Per poter effettuare il test indicato in questo tutorial, il tuo Arduino deve trovarsi in una zona coperta da un gateway LoRaWan collegato a The Things Stack Sandbox.

Sul sito The Things Network puoi verificare la copertura nella tua zona, ma tieni presente che anche se il gateway è riportato nella mappa, non è garantito che sia effettivamente sempre attivo.

La versione sanbox del sistema è gratuita e non offre garanzia di servizio.

Se l’operazione di join continua ripetutamente a fallire, la prima cosa da fare è verificare se sulla console TTS compaiono dei messaggi nella sezione live data del tuo device o della tua applicazione.

Se ad ogni tentativo di join corrisponde un messaggio di errore, probabilmente non hai correttamente configurato le credenziali di autenticazione nello sketch. Controlla quindi il valore inserito nelle variabili globali appKey e appEUI.

Se sulla console tutto tace, è probabile che i tuoi messaggi non vengano ricevuti da nessun gateway.

Verifica che l’antenna sia correttamente collegata alla scheda Arduino, prova ad avvicinarti ad una finestra, oppure colloca il tutto all’esterno, se possibile.

La soluzione definitiva è quella di acquistare un gateway LoRaWan e attivarlo registrandolo sulla tua console TTS. L’ultimo tutorial di questa missione fornisce indicazioni utili per percorrere questa strada.