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:

Figura 1

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.

Figura 2

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);
Figura 3 (Initiator)
Figura 4 (Responder)

Lascia una risposta

Il tuo indirizzo email non sarà pubblicato. I campi obbligatori sono contrassegnati *

Ti sei iscritto alla newsletter

There was an error while trying to send your request. Please try again.

Quattrodispositivi utilizzerà le informazioni fornite in questo modulo per restare in contatto con te e fornire aggiornamenti del sito