Protocollo ESP32-NOW Unidirezionale (Parte 2)
Introduzione
Nella prima parte dell’articolo (link) ho descritto le caratteristiche principali del protocollo ESP32-NOW e le modalità di comunicazione mentre in questo articolo, analizzeremo il codice per la realizzazione del protocollo unidirezionale.
Mac Address
L’Initiator per istaurare una comunicazione deve inviare l’indirizzo MAC del dispositivo con il quale vuole comunicare, con questa procedura si identifica il destinatario del messaggio.
Gli indirizzi MAC, detti anche indirizzi fisici, sono codici binari univoci registrati nel dispositivo dal produttore.
L’indirizzo MAC è formato da 48 bit (6 byte) suddivisi in 6 gruppi da 8 bit, esempio:
E’ evidente che per iniziare una comunicazione l’Initiator deve conoscere a priori gli indirizzi MAC di tutti i dispositivi presenti nella rete.
Prima scrivere il codice applicativo, dobbiamo censire tutti gli indirizzi MAC dei dispositivi. Proprio per questo scopo riporto di seguito il codice per leggere gli indirizzi MAC dalle boards ESP32.
#include <WiFi.h> void setup(){ Serial.begin(115200); Serial.println(); Serial.print("ESP Board MAC Address: "); Serial.println(WiFi.macAddress()); } void loop(){ }
L’indirizzo MAC, sarà stampato a monitor, consiglio di prendere nota per poi inserirlo nel codice che verrà descritto nel prossimo paragrafo.
Comunicazione unidirezionale
Vorrei partire dal protocollo di comunicazione più semplice, cioè la comunicazione unidirezionale, dove c’è un solo Initiator e Responder.
Su ogni modulo (Initiator e Responder) dobbiamo caricare il codice corrispondente per lo svolgimento della propria funzionalità.
Codice per Initiator
Analizziamo per prima il codice dell’Initiator, il sistema che ha il compito di iniziare la comunicazione.
#include <esp_now.h> #include <WiFi.h> // memorizziamo in una variabile l'indirizzo MAC del Responder uint8_t IndirizzoMAC[] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; // Struttura del messaggio da inviare typedef struct StrutturaMessaggio { char a[32]; int b; float c; bool d; } StrutturaMessaggio; // Salviamo la struttura nella variabile InvioDati StrutturaMessaggio InvioDati; // Creiamo una variabile peerInfo per memorizzare le informazioni sulla connessione esp_now_peer_info_t peerInfo; // Questa e' la funzione di callback void OnDataSent(const uint8_t *mac_addr, esp_now_send_status_t status) { Serial.print("\r\nLo stato dell'ultimo pacchetto spedito:\t"); Serial.println(status == ESP_NOW_SEND_SUCCESS ? " Pacchetto Consegnato con Successo" : "Consegna del pacchetto Fallita"); } void setup() { // Inizializzazione della seriale Serial.begin(115200); // Impostiamo il WI-FI come una stazione WiFi.mode(WIFI_STA); // Inizializzazione ESP-NOW if (esp_now_init() != ESP_OK) { Serial.println("Errore sull'Inizializzazione ESP-NOW"); return; } // Questa funzione verra' chiamata ad ogni invio dei dati esp_now_register_send_cb(OnDataSent); // configuramo il peer memcpy(peerInfo.peer_addr, IndirizzoMAC, 6); peerInfo.channel = 0; peerInfo.encrypt = false; // Indirizzo peer if (esp_now_add_peer(&peerInfo) != ESP_OK){ Serial.println("fallita la connessione peer"); return; } } void loop() { // Set values to send strcpy(InvioDati.a, "questo e' un messaggio prova"); InvioDati.b = random(1,50); InvioDati.c = 8.66; InvioDati.d = false; // Invio del messaggio sulla rete ESP32-NOW esp_err_t result = esp_now_send( IndirizzoMAC, (uint8_t *) &InvioDati, sizeof(InvioDati)); if (result == ESP_OK) { Serial.println("Invio dati con successo"); } else { Serial.println("Errore i dati non sono stati inviati"); } delay(2000); }
Come prima istruzione inseriamo le due librerie per il protocollo ESP32-NOW e per la gestione della periferica WI-FI.
#include <esp_now.h> #include <WiFi.h>
Dobbiamo inserire l’indirizzo MAC della board con cui vogliamo dialogare come da istruzioni indicate nel precedente paragrafo.
uint8_t IndirizzoMAC[] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
Creiamo una struttura di dati con tutte le possibili combinazioni dei tipi che vogliamo inviare durante la comunicazione.
typedef struct StrutturaMessaggio { char a[32]; int b; float c; bool d; } StrutturaMessaggio;
Rinominiamo la struttura con InvioDati.
StrutturaMessaggio InvioDati;
Creamo una variabile di tipo “peerInfo”, per memorizzare informazioni sulla connessione.
esp_now_peer_info_t peerInfo;
Creiamo una funzione di callback, che verrà eseguita ad ogni invio del messaggio. Lo scopo della funzione è quello di stampare a monitor il corretto o un errore d’invio del messaggio.
void DatiSpediti(const uint8_t *mac_addr, esp_now_send_status_t status) { Serial.print("\r\nLo stato dell'ultimo pacchetto spedito:\t"); Serial.println(status == ESP_NOW_SEND_SUCCESS ? " Pacchetto Consegnato con Successo" : "Consegna del pacchetto Fallita"); }
Inizializziamo i parametri del protocollo ESP32-NOW, se questa istruzione verrà eseguita correttamente, l’esecuzione del codice passa all’istruzione successiva.
if (esp_now_init() != ESP_OK) { Serial.println("Errore sull'Inizializzazione ESP-NOW"); return; }
Questa funzione verrà invocata ad ogni invio di un messaggio.
esp_now_register_send_cb(DatiSpediti);
Configuriamo la modalità di comunicazione e l’indirizzo MAC del dispositivo con cui connettersi, infatti, nelle istruzioni successive se esegue un accoppiamento.
memcpy(peerInfo.peer_addr, IndirizzoMAC, 6); peerInfo.channel = 0; peerInfo.encrypt = false;
Ovviamente, questo è solo un esempio dimostrativo su come usare il protocollo ESP32-NOW, pertanto nelle istruzioni successive caricheremo dei dati senza significato nella struttura precedentemente definita, ma nel vostro sistema potete, per esempio inserire il valore letto da un sensore, lo stato di un’uscita ecc..
strcpy(InvioDati.a, "questo e' un messaggio prova"); InvioDati.b = random(1,50); InvioDati.c = 8.66; InvioDati.d = false;
Con questa istruzione, attiviamo il processo di comunicazione , inviamo i dati memorizzati nella struttura InvioDati, e registriamo nella variabile risultato l’esito della trasmissione.
esp_err_t risultato = esp_now_send( IndirizzoMAC, (uint8_t *) &InvioDati, sizeof(InvioDati));
Stampiamo a monitor l’esito della trasmissione.
if (risultato == ESP_OK) { Serial.println("Invio dati con successo"); } else { Serial.println("Errore i dati non sono stati inviati"); } delay(2000);
Codice Responder
Le parti software simili all’ esempio descritto in precedenza non verranno descritte in questo paragrafo.
#include <esp_now.h> #include <WiFi.h> // Structure example to receive data // Must match the sender structure typedef struct StrutturaMessaggio { char a[32]; int b; float c; bool d; } StrutturaMessaggio; // Salviamo la struttura nella variabile ingressoDati StrutturaMessaggio ingressoDati; // Questa e' la funzione di callback void datiRicevuti(const uint8_t * mac, const uint8_t *incomingData, int len) { memcpy(&ingressoDati, incomingData, sizeof(ingressoDati)); Serial.print("Bytes received: "); Serial.println(len); Serial.print("Char: "); Serial.println(ingressoDati.a); Serial.print("Int: "); Serial.println(ingressoDati.b); Serial.print("Float: "); Serial.println(ingressoDati.c); Serial.print("Bool: "); Serial.println(ingressoDati.d); Serial.println(); } void setup() { // Inizializzazione della seriale Serial.begin(115200); // Impostiamo il WI-FI come una stazione WiFi.mode(WIFI_STA); // Inizializzazione ESP-NOW if (esp_now_init() != ESP_OK) { Serial.println("Error initializing ESP-NOW"); return; } // Questa funzione verra' chiamata ad ogni invio dei dati esp_now_register_recv_cb(datiRicevuti); } void loop() { }
Questa è la funzione di callback che viene chiamata ogni volta che c’è un dato nel registro di ricezione. Tutti dati presenti nel registro d’ingresso *incomingData poi sono copiati nella struttura definita nella parte inziale dello sketch ingressoDati.
void datiRicevuti(const uint8_t * mac, const uint8_t *incomingData, int len) { memcpy(&ingressoDati, incomingData, sizeof(ingressoDati)); Serial.print("Bytes received: "); Serial.println(len); Serial.print("Char: "); Serial.println(ingressoDati.a); Serial.print("Int: "); Serial.println(ingressoDati.b); Serial.print("Float: "); Serial.println(ingressoDati.c); Serial.print("Bool: "); Serial.println(ingressoDati.d); Serial.println(); }
Chiama la funzione di callback quando un dato è stato ricevuto.
esp_now_register_recv_cb(datiRicevuti);