Direkte Kommunikation mit ESP-NOW

Statt der Kommunikation über ein verbundenes WLAN-Netzwerk können Module des Typ ESP8266, ESP32, ESP32-S und ESP32-C auch direkt miteinander Daten austauschen. Dies geschieht mittels einem von Espressif entwickelten Protokoll namens "ESP-NOW". Hierbei wird kein Router benötigt, denn die Kommunikation erfolgt über die MAC-Adressen der jeweils beteiligten Module.
Der Verbindungsaufbau zwischen den Modulen erfolgt durch das vereinfachte Protokoll deutlich schneller und reduziert damit auch den Stromverbrauch der Module. Allerdings ist Datenmenge pro Paket ist auf 250 Bytes begrenzt. Ein Master kann mit bis zu 20 Slaves gepaart werden. Will man das ESP-Now Netzwerk verschlüsseln (ECDH und AES128-CCM), so reduziert sich die maximale Anzahl der Slaves auf 10.
Es gibt die Übertragungsmöglichkeiten als "unicast" und "broadcast", somit ist sowohl eine 1:1 als auch eine 1:n-Kommunikation möglich.

Verwendete Bauteile

Ermitteln der MAC-Adresse

Da die Identifizierung der Module in ESP-NOW über die MAC-Adresse funktioniert, muss man diese zunächst von den beteiligten Modulen ermitteln und am besten separat notieren.
Die folgenden Programme ermitteln die MAC-Adresse des angeschlossenen Moduls und schreiben diese auf die serielle Konsole. Die Ausgabe sollte beispielsweise so aussehen:
MAC Address: 8C:9E:1F:E9:F5:50

Sketch für ESP-32/ESP-32 CAM

#include "WiFi.h"

// enable for ESP32-CAM
// #include "soc/rtc_cntl_reg.h"

void setup()
{
    // Disable brownout detector (enable for ESP32-CAM)
    // WRITE_PERI_REG(RTC_CNTL_BROWN_OUT_REG, 0);

    Serial.begin(115200);

    WiFi.mode(WIFI_MODE_STA); // "station mode"
    Serial.print("MAC Address: ");
    Serial.println(WiFi.macAddress());
}

void loop() {}

Sketch für ESP8266 (ESP-01/D1 Mini)

#include <ESP8266WiFi.h>

void setup()
{
    Serial.begin(9600);
}

void loop()
{
    Serial.print("MAC Address: ");
    Serial.println(WiFi.macAddress());
    delay(5000);
}

Unidirektionale Kommunikation

Im folgenden Versuch werden zwei ESP32-Module verwendet, um eine unidirektionale Kommunikation mit dem ESP-NOW-Protokoll aufzubauen. Dabei fungiert Modul #1 als Sender und Modul #2 als Empfänger. Für diesen Test werden verschiedene Datentypen verschickt (Char, Int, Float und Bool).

Kommunikation in eine Richtung mit ESP-NOW
Abb.: Kommunikation in eine Richtung mit ESP-NOW

Sketch für Sender-Modul

#include <esp_now.h>
#include <WiFi.h>

// custom MAC address of the responder module
uint8_t responderAddress[] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};

struct messageStruct {
    char myString[32];
    int myInt;
    float myFloat;
    bool myBool;
};

messageStruct messageData;
esp_now_peer_info_t peerInfo;

void onDataSent(const uint8_t *macAddress, esp_now_send_status_t sendStatus)
{
    Serial.print((int)macAddress + ": ");
    if (sendStatus == ESP_NOW_SEND_SUCCESS) {
        Serial.println("sent");
    } else {
        Serial.println("ERROR: sent failed!");
    }
}

void setup()
{
    Serial.begin(115200);
    WiFi.mode(WIFI_STA);

    if (esp_now_init() != ESP_OK) {
        Serial.println("Error init ESP-NOW");
        return;
    }

    esp_now_register_send_cb(onDataSent);

    memcpy(peerInfo.peer_addr, responderAddress, 6);
    peerInfo.channel = 0;
    peerInfo.encrypt = false;

    if (esp_now_add_peer(&peerInfo) != ESP_OK) {
        Serial.println("Failed to add peer");
        return;
    }
}

void loop()
{
    strcpy(messageData.myString, "This is a ESP-NOW sending test");
    messageData.myInt = random(1, 100);
    messageData.myFloat = messageData.myInt * 2.41;
    messageData.myBool = (bool)random(0, 2);

    esp_err_t result = esp_now_send(responderAddress, (uint8_t *) &messageData, sizeof(messageData));
    if (result != ESP_OK) {
        Serial.println("Sending error");
    }

    delay(5000);
}

Sketch für Empfänger-Modul

#include <esp_now.h>
#include <WiFi.h>

struct messageStruct {
    char myString[32];
    int myInt;
    float myFloat;
    bool myBool;
};

messageStruct messageData;

void onDataReceive(const uint8_t *macAddress, const uint8_t *incomingData, int dataLength)
{
    memcpy(&messageData, incomingData, sizeof(messageData));
    Serial.println("Data received: " + String(dataLength));
    Serial.println("String: " + String(messageData.myString));
    Serial.println("Int: " + String(messageData.myInt));
    Serial.println("Float: " + String(messageData.myFloat));
    Serial.println("Bool: " + String(messageData.myBool));
    Serial.println("----------------------------");
}

void setup()
{
    Serial.begin(115200);
    WiFi.mode(WIFI_STA);

    if (esp_now_init() != ESP_OK) {
        Serial.println("Error init ESP-NOW");
        return;
    }

    esp_now_register_recv_cb(onDataReceive);
}

void loop() {}

Wenn beide Sketches auf jeweils ein ESP32-Modul geladen wurden, dann sollte in der seriellen Konsole des Empfängers folgende Ausgabe der empfangenden Daten erscheinen:

Ausgabe der empfangenen Werte auf der seriellen Konsole
Abb.: Ausgabe der empfangenen Werte auf der seriellen Konsole

Weiterführende Links

zurück