Einführung in das Protokoll ESP-NOW

von renax
veröffentlicht am 28.07.2024 aktualisiert am 28.07.2024

Stell dir mal vor, deine ESP-Mikrocontroller kommunizieren blitzschnell und zuverlässig miteinander, ganz ohne eine Internetverbindung, Bluetooth oder Kabel. Genau das kann das ESP-NOW-Protokoll. Das Protokoll dient dem Datenaustausch zwischen ESP-Mikrocontrollern. Dabei ist es auch völlig egal, ob es ein ESP8266 Mikrocontroller oder ein ESP32 Mikrocontroller ist, denn mit dem Protokoll ist es möglich, mit bis zu 20 Mikrocontroller zu kommunizieren. ESP-NOW ist sehr flexibel, da jeder Teilnehmer des Protokolls als Transmitter, Receiver oder Transceiver agieren kann.

Zum Kapitel springen Was ist ESP-NOW?

ESP-NOW ist ein von Espressif entwickeltes Kommunikationsprotokoll, welches mehreren ESP-Mikrocontrollern ermöglicht miteinander zu kommunizieren ohne eine Internetverbindung herzustellen - Denn das Protokoll läuft auf der zweiten Schicht - Der Sicherungsschicht des 7- Schichten OSI-Modells und reduziert die Kommunikation um 5 Schichten, was eine schnelle Antwort und Verarbeitung ermöglicht und zugleich noch viel Strom spart. Die eindeutige MAC-Adresse eines Mikrocontrollers wird zur Identifizierung und Adressierung der Geräte verwendet und vor der Kommunikation müssen die Geräte miteinander gepaired werden, wie es bei Bluetooth der Fall ist.

Ein weiterer Vorteil von ESP-NOW ist die Möglichkeit, empfangene Nachrichten zu bestätigen. Ähnliche Funktionen finden sich normalerweise ab der Transportschicht (Schicht 4) des 7-Schichten OSI-Modells, insbesondere bei der Verwendung von TCP.

Zum Kapitel springen Hallo Welt

Zum Kapitel springen MAC-Adresse

Bevor wir mit der Kommunikation anfangen, benötigen wir jeweils die MAC-Adressen der Controller. Die ESP8266WiFi Bibliothek hat eine WiFi.macAddress() Funktion, welche ein einfaches Auslesen der MAC-Adresse ermöglicht.

#include <ESP8266WiFi.h>

void setup(){

   Serial.begin( 115200 );
   
   Serial.print( "MAC-Adresse: " ); 
   Serial.println( WiFi.macAddress() );
   
}
void loop(){}
MAC-Adresse: 24:0A:C4:00:01:00

Wundere dich nicht, falls die ersten drei Bytes deiner MAC-Adresse anders aussehen als oben im Bild, denn Espressif hat fast 200 unterschiedliche OUI für seine MAC-Adressen registriert. Die restlichen Bytes der MAC-Adresse sind einzigartig und können somit auch vom Bild abweichen. Auf MAC Address Lookup kannst du sehen, welche OUI zu Espressif gehören.

Zum Kapitel springen ESP8266 als Sender

Für eine Kommunikation werden immer zwei Parteien benötigt. Einmal ein Sender und ein dazugehöriger Empfänger. Wir starten ganz untypischer weise einfach mal mit einem Sender. Folgende Zeilen Code verwandeln den ESP8266 in einen Sender via ESP-NOW.

Produktempfehlungen und -suche in Verbindung mit dem Amazon Partnerprogramm:

¹ Angaben ohne Gewähr. Bei einem Kauf über den Link erhalten wir eine Provision.

// ESP-NOW - ESP8266 Sender Template
// https://makesmart.net/blog/read/einfuehrung-in-das-protokoll-esp-now

// Bibliotheken
#include <ESP8266WiFi.h>
#include <espnow.h>

// Empfänger MAC-Adresse, jedes Byte der Adresse im HEX Format;
// receiverMac        = 24:0A:C4:00:01:00 
uint8_t receiverMac[] = { 0x24, 0x0A, 0xC4, 0x00, 0x01, 0x00 }; 

// Die Nachricht, die gesendet werden soll
const char message[]    = "makesmart ESP-NOW";

// Callback Funktion, um sicherzustellen, dass die Daten angekommen sind
void dataSentCallback( uint8_t *mac_addr, uint8_t status ){
  bool received = !status;
  Serial.println( received ? "Nachricht angekommen" : "Nachricht nicht angekommen" );
} 

void setup() {

  Serial.begin( 115200 );

  // Controller in den Stationmode versetzen, damit keine Störungen entstehen
  WiFi.mode( WIFI_STA ); 

  // Initialisierung von ESP-NOW
  esp_now_init();
  // Rolle setzen - ROLE_CONTROLLER
  esp_now_set_self_role( ESP_NOW_ROLE_CONTROLLER );
  // Callbackfunktion einbinden
  esp_now_register_send_cb( dataSentCallback ); 
  // Pairing mit dem Empfänger - Die Nachricht wird nur an diesen Empfänger gesendet
  esp_now_add_peer(receiverMac, ESP_NOW_ROLE_SLAVE, 0, NULL, 0); 

}

void loop() {

  // Nachricht wird jede Sekunde gesendet
  esp_now_send( receiverMac, (uint8_t *)&message, sizeof(message) );
  delay( 1000 );

}

Das Array receiverMac[] repräsentiert die MAC-Adresse unseres Empfänger-ESPs, welche wir vorher ermittelt haben.
Aus der Adresse 24:0A:C4:00:01:00 wird 0x24, 0x0A, 0xC4, 0x00, 0x01, 0x00, da die ESP-Now Bibliothek MAC-Adressen in Byte-Array Form erwartet.

Danach deklarieren wir eine kurze Nachricht in einem Char-Array, damit unsere Nachricht als Bytestream behandelt wird.

uint8_t receiverMac[]   = { 0x24, 0x0A, 0xC4, 0x00, 0x01, 0x00 }; 
const char message[]    = "makesmart ESP-NOW";

Die Methode dataSentCallback() erwartet die MAC-Adresse des Empfängers und den Übertragungsstatus als Parameter. Sie wird aufgerufen, wenn der Empfänger den Empfang der Nachricht bestätigt.

void dataSentCallback(uint8_t *mac_addr, uint8_t status){
  Serial.println(!status ? "Nachricht angekommen" : "Nachricht nicht angekommen");
}

In ESP-NOW gibt es verschiedene Rollen, die einem Gerät zugewiesen werden können. Diese Rollen bestimmen, wie ein Gerät in einem ESP-NOW Netzwerk agiert. Neben ESP_NOW_ROLE_CONTROLLER gäbe es noch weitere Rollen.

// Rolle setzen - ROLE_CONTROLLER
esp_now_set_self_role( ESP_NOW_ROLE_CONTROLLER );

ESP_NOW_ROLE_CONTROLLER
Diese Rolle bezeichnet ein Gerät, das primär Befehle an andere Geräte sendet. Ein Controller kann auch Antworten oder Daten von diesen Geräten empfangen.

ESP_NOW_ROLE_SLAVE
Ein Gerät mit dieser Rolle empfängt hauptsächlich Befehle vom Controller und reagiert darauf. Ein Slave kann ebenfalls Nachrichten oder Daten an den Controller senden.

ESP_NOW_ROLE_COMBO
Ein Gerät in dieser Rolle kann sowohl als Controller als auch als Slave agieren. Es kann Nachrichten senden und empfangen, sowohl an Controller als auch an andere Slaves.

Anschließend wird mithilfe der esp_now_add_peer()-Funktion ein neues Gerät verbunden und einige Argumente wie die MAC-Adresse des Empfängers und die Rolle festgelegt. Kommunikationskanal, Pointer auf einen Key für eine verschlüsselte Kommunikation und die Länge des Keys würden hier den Rahmen sprengen und deshalb lass die Werte so wie sie hier im Code stehen um Fehler zu vermeiden.

Im Loop senden wir die Nachricht mit der Funktion esp_now_send() im Sekundentakt.

void loop() {

  // Nachricht wird jede Sekunde gesendet
  esp_now_send( receiverMac, (uint8_t *)&message, sizeof(message) );
  delay( 1000 );

}

Dieser Code Sendet in Dauerschleife eine Nachricht mit makesmart ESP-NOW an die MAC-Adresse 24:0A:C4:00:01:00.

Die Nachricht darf maximal 250 Bytes groß sein und muss vor dem Senden explizit in uint8_t umgewandelt werden.

Zum Kapitel springen ESP8266 als Empfänger

Eine Nachricht ins Nichts zu verschicken macht wenig Sinn und deshalb schauen wir uns jetzt an, wie wir einen zweiten ESP8266 als Empfänger initialiseren können. Dieser ESP hat die MAC-Adresse 24:0A:C4:00:01:00 und soll die Nachrichten empfangen.

// Code für den Empfänegr-Controller
// ESP-NOW - ESP8266 Empfänger Template
// https://makesmart.net/blog/read/einfuehrung-in-das-protokoll-esp-now

// Bibliotheken
#include <ESP8266WiFi.h>
#include <espnow.h>

// Callback Funktion, um empfangene Daten zu vearbeiten
void receivedDataCallback( uint8_t *mac, uint8_t *incomingData, uint8_t len ){

  char incomingChar[ len + 1 ];
  memcpy( incomingChar, incomingData, len );
  incomingChar[len] = '\0';


  // Nachricht `message` erhalten
  String message    = String(incomingChar);
  
  // Ausgabe über den seriellen Monitor
  Serial.println("Erhalten: " + String(message));

}


void setup() {

  // Initialisierung des seriellen Monitors zur Ausgabe
  Serial.begin( 115200 ); 

  // Controller in den Stationmode versetzen, damit keine Störungen entstehen
  WiFi.mode( WIFI_STA );
  
  // Initialisierung von ESP-NOW
  esp_now_init();
  // Rolle setzen - ROLE_SLAVE_CONTROLLER
  esp_now_set_self_role(ESP_NOW_ROLE_SLAVE);

  // Callbackfunktion einbinden
  esp_now_register_recv_cb(receivedDataCallback);


}


void loop() {

}

Die Callback Funktion receivedDataCallback wandelt die eingehenden Daten unserer Übertragung in einen String um und gibt sie über den Seriellen Monitor aus.
Die Funktion benötigt die MAC-Adresse des Senders, die eingehenden Daten und die Länge der Daten als Parameter, da diese durch die Bibliothek vorgegeben werden.

Zuerst erstellen wir ein char-Array (wie im Sender-Code) und geben ihm die Größe der eingehenden Nachricht len + 1 Byte extra; Dieses Byte dient als Puffer für den Null-Terminator. Dieses wandeln wir zu einem String um und geben es im seriellen Monitor aus.

// Callback Funktion, um empfangene Daten zu vearbeiten
void receivedDataCallback( uint8_t *mac, uint8_t *incomingData, uint8_t len ){

  // Erstelle ein Char-Array mit der Länge der empfangenen Daten plus eins für das Nullzeichen
  char incomingChar[ len + 1 ];
  // Kopiere die empfangenen Daten in das Char-Array
  memcpy( incomingChar, incomingData, len );
  // Füge ein Nullzeichen am Ende des Char-Arrays hinzu, um es als String zu terminieren
  incomingChar[len] = '\0';


  // Nachricht `message` erhalten
  String message    = String(incomingChar);
  
  // Ausgabe über den seriellen Monitor
  Serial.println("Erhalten: " + String(message));

}

Im Setup geben wir dem ESP die Rolle ESP_NOW_ROLE_SLAVE, da dieser hauptsächlich als Empfänger fungiert. Danach registrieren wir wie auch beim Sender Unsere Callback-Funktion. Es kann verwirrend sein, dass unser Sender als Controller und unser Empfänger als Slave bezeichnet wird. In der ESP-NOW Terminologie bedeutet "Controller" einfach, dass das Gerät primär Befehle sendet, während "Slave" bedeutet, dass das Gerät primär Befehle empfängt. Dies steht im Gegensatz zu herkömmlichen Server-Client-Beziehungen.

esp_now_set_self_role( ESP_NOW_ROLE_SLAVE );
esp_now_register_recv_cb( receivedDataCallback );

Der Loop bleibt leer, da wir diesen nicht benötigen. Die Callback-Funktion wird beim Start registriert anschließend haben wir keine sich wiederholenden Aufgaben für den ESP8266.

Produktempfehlungen und -suche in Verbindung mit dem Amazon Partnerprogramm:

¹ Angaben ohne Gewähr. Bei einem Kauf über den Link erhalten wir eine Provision.

Et voilà. Siehe da, im seriellen Monitor finden wir nun die Nachricht Erhalten: Hello World im Sekundentakt. Wir haben eine erfolgreiche Kommunikation getätig. Mit diesen beiden Codes in Kombination könnten wir theoretisch jetzt ein N:1 Netzwerk aufbauen. Dazu einfach weitere Controller wie in ESP8266 als Sender einbinden.

Zum Kapitel springen Vorteile mit ESP-NOW

  • Niedrige Latenz
    ESP-NOW ermöglicht eine nahezu sofortige Kommunikation, da die Daten ohne die üblichen WLAN-Verbindungsprozesse gesendet werden. Ideal für Echtzeitanwendungen!
  • Geringer Stromverbrauch
    Da ESP-NOW ohne den Overhead von WLAN-Protokollen funktioniert, kann es den Energieverbrauch erheblich reduzieren. Von daher ist es von Vorteil für batteriebetriebene Projekte vorallem in Kombination mit Deep-Sleep.
  • Einfach zu implementieren
    Da die API keine komplexen Codes benötigt, kann ESP-NOW mit ein paar Einschränkungen einfach in bestehende Projekte implementiert werden. Oben war der Code doch schon ziemlich verständlich, oder nicht? 😉
  • Unabhängigkeit
    ESP-NOW ist unabhängig von irgendwelchen WLAN-Access Points und dadurch können Geräte kommunizieren, die evtl. nicht vom Netz gedeckt werden. Es müssen aber die Beschränkungen bezüglich der Reichweite des jeweiligen Controllers beachtet werden, da das Signal nur so weit reicht wie die Antenne es zulässt.
  • Hohe Anzahl an Peers
    ESP-NOW ermöglicht die Kommunikation mit einer relativ großen Anzahl von Peers (bis zu 20). Durch die hohe Anzahl an Peers lässt sich durch geschickte Programmierung ein Mesh-Netzwerk aufbauen, da jeder Controller als Empfänger und Sender funktionieren kann. So ähnlich wie bei Zigbee.

Zum Kapitel springen Einschränkungen mit ESP-NOW

  • Geringe Datenmenge möglich: Das Protokoll kann nur Daten mit maximal 250Bytes übertragen und ist somit ungeeignet für die Übertragung von größeren Datenmengen wie z.B. Bilder.
  • Kein WLAN und ESP-NOW gleichzeitig dank 2,4Ghz Band
    Sowohl ESP-NOW als auch WLAN senden ihre Daten über das 2.4GHz Band. Deshalb kann man nicht ohne Weiteres WLAN und ESP-NOW gleichzeitig verwenden; jedoch könnte man es nacheinander:
  1. ESP-Now initialisieren, ..., ESP-NOW beenden
  2. WLAN initialisieren, ..., WLAN beenden
  • weniger Peers mit verschlüsselung: ESP-NOW kann zwar bis zu 20 Peers pairen, aber falls man die Kommunikation innerhalb des ESP-NOW Netzwerks verschlüsseln möchte, reduziert sich die Anzahl möglicher Peers auf 10.

Zum Kapitel springen Schlusswort

ESP-NOW ist schon ein ziemlich cooles Protokoll, wenn man schnell und einfach Daten zwischen anderen ESPs austauschen möchte. Auch der geringe Stromverbrauch durch den Verzicht auf eine konstante WLAN Verbindung machen das Protokoll attraktiv für batteriebetriebene Projekte wie Sensoren oder Aktoren. Für diese reicht eine Payloadgröße von 250 Bytes völlig aus.

Möchte man jedoch größere Datenmengen übertragen sollte man ein alternatives Protokoll in Betracht ziehen.
Schade ist auch, dass man WLAN und ESP-NOW auf einem ESP8266 nicht parallel nutzen kann, um emfangene Daten direkt an einen Webserver weiterzuleiten zu können. Da der ESP32 zwei Kerne zur Verfügung hat, lässt sich das möglicherweise umsetzen, jedoch muss man trotzdem drauf achten, dass ESP-NOW und die WiFi-Verbindung auf dem gleichen Channel ablaufen. Das könnte zu Störungen oder zu einem komplizierterem Entwicklungsprozess führen.

Falls noch Fragen aufkommen, könnt ihr sie mir auch gerne zukommen lassen!

Ich bin oft auf dem makesmart.net-Discord unter dem Namen renax187 erreichbar.

Bis zum nächsten Post!

Zum Kapitel springen Quellen und Dokumentationen

Teile diesen Beitrag



Diese Artikel könnten dich auch interessieren

Mikrocontroller im Detail: Vom Aufbau bis zu den grundlegenden Komponenten

Was steckt in einem Mikrocontroller? Vom fertigen Bauteil über den Prozessor bis hin zum Siliziumchip und den kleinsten Strukturen – wie sind leistungsstarke Technologien für unseren Alltag aufgebaut?

cooper.bin am 04.10.2024

ESP8266 Webserver: GET-Variablen auslesen

GET-Parameter sind Variablen, die über die URL übergeben werden. Diese Variablen können auf einem ESP8266 ausgelesen werden, um bestimmte Aktionen in der Software auszulösen.

cooper.bin am 17.02.2024

ESP8266 - Ein einfacher Webserver mit mDNS

Während Webserver wie Apache2 oder NGNIX auf Rechnern laufen, kann man auch auf einem ESP8266 einen Webserver verwenden. In diesem Tutorial werden wir ein Grundgerüst implementieren.

cooper.bin am 13.02.2024

Datentypen in der Arduino IDE - eine Übersicht

Erfahre alles über Arduino-Datentypen: Von bool, byte, int bis zu speziellen Typen wie void und Zeigern. Ideal für Einsteiger und Fortgeschrittene.

cooper.bin am 16.03.2024

ESP8266 D1 Mini WLAN-Relais mit HTTP API

Mit diesem einfachem Webserver auf dem ESP8266 mit HTTP API kannst du ein Relais smart machen! Steuere das Relais über deinen Webbrowser oder automatisiert durch andere Dienste.

cooper.bin am 20.02.2024