here is a demo-code that demonstrates Tx / Rx with ESP-NOW.
It has a lot of debug-output to analyse if some details are wrong
// keep this variable on top to have an easy to remember place to look up
// which board shall have this code-version /a short name is easier to remember as
// a MAC-Adress
char BoardName[] = "Bonny";
unsigned long SendDataInterval;
int Summand;
// at the end of this file there is an explanation that uses an analogy with
// sending / receiving postal letters to explain the several parts of ESP-NOW
// so if you would like to have an easy to understand overview read this
// explanation first.
#include <WiFi.h>
#include <esp_now.h>
// For sending / receiving ESP-NOW-data on both sides sender and receiver;
// a structured variable has to be defined with the EXACT SAME structure
// the information that shall be sended/received is transmitted bytewise
// the ESP-NOW-functions know nothing about what the bytes mean
// it just transfers a specified number of bytes
// If the structures do not match 100% the data gets MIS-interpreted on the receiver-side
// in this demo-code a structure with an Array Of Char and an integer is used
// it is defined as follows
// ESP-NOW data-structure-definition
typedef struct MyESP_NOW_Data_type {
char MyESP_NOW_MsgStr[128];
int MyESP_NOW_Int;
} MyESP_NOW_Data_type;
// After defining the structure two variables
// one for receiving one for sending data
// this demo wants to demonstrate send AND receive in both directions
MyESP_NOW_Data_type my_received_ESP_NOW_Data;
MyESP_NOW_Data_type my_READYtoSEND_ESP_NOW_Data;
//#############################################################################################
// list of MAC-Adresses of all receivers:
// important note: ESP-NOW sending is based on the RECEIVERS Mac-adress.
// this means for every ESP-modul that shall receive an ESP-NOW-Messages
// you have to execute register a peer in the Setup-function
// Mac-Adress must be stored in an array of uint8_t
uint8_t ESP_NOW_MAC_adrOfRecv[] = {0x24, 0x6F, 0x28, 0x22, 0x62, 0xFC };// Board 0x90 sends to Board 0xFC
//uint8_t ESP_NOW_MAC_adrOfRecv[] = {0x24, 0x6F, 0x28, 0x22, 0xB6, 0x90 }; // Board 0xFC sends to Board 0x90
char MAC_adrOfRecv_as_AoC[18];
char Own_MacAdr_AoC[18]; //suffix _AoC for easier remembering variable-type is ArrayOfChar
char MySelfHeader[] = "Myself: ";
char ReceivedHeader[] = "-------------->";
//##############################################################################################
//ESP_NOW_functions
// callback function that will be executed when data is received
// Parameters:
// mac: mac-adress of sender
// incomingData: the bytes that are received
// NoOfBytesRcv: number of bytes received
//void OnDataRecv(uint8_t * mac, uint8_t *incomingData, uint8_t NoOfBytesRcv) {
void OnDataRecv(const uint8_t * mac, const uint8_t *incomingData, int NoOfBytesRcv) {
char MacAdr[18];
Serial.print(ReceivedHeader);
Serial.print("ESP-NOW-Data received from Board with MAC-Adress#");
Serial.print( strupr(hex02str(mac[0])));
Serial.print(":");
Serial.print( strupr(hex02str(mac[1])));
Serial.print(":");
Serial.print( strupr(hex02str(mac[2])));
Serial.print(":");
Serial.print( strupr(hex02str(mac[3])));
Serial.print(":");
Serial.print( strupr(hex02str(mac[4])));
Serial.print(":");
Serial.print( strupr(hex02str(mac[5])));
Serial.print(":");
Serial.println("#");
// copy data bytewise from variable incomingData to variable my_received_ESP_NOW_Data
memcpy(&my_received_ESP_NOW_Data, incomingData, sizeof(my_received_ESP_NOW_Data));
Serial.print(ReceivedHeader);
Serial.print("No of Bytes received: ");
Serial.println(NoOfBytesRcv);
//these lines must match the variables inside the ESP_NOW-data-structure
Serial.print(ReceivedHeader);
Serial.print("Received Msg: #"); // leading "#"
Serial.print(my_received_ESP_NOW_Data.MyESP_NOW_MsgStr);
Serial.println("#"); // trailing "#" makes it easy to see which bytes where received
Serial.print(ReceivedHeader);
Serial.print("Int: ");
Serial.println(my_received_ESP_NOW_Data.MyESP_NOW_Int);
Serial.println();
}
// callback when data is sent. Gets executed when sending data has finished
void OnDataSent(const uint8_t *mac_addr, esp_now_send_status_t status) {
Serial.println();
Serial.println();
Serial.print(MySelfHeader);
Serial.print(" OnDataSent Send Status: ");
if (status == ESP_NOW_SEND_SUCCESS) {
Serial.println("Success'");
Serial.println();
}
else {
Serial.println("Failed'");
Serial.println();
}
}
// attention! for some unknown strange reasons the variable
// for the peer-info has to be global otherwise you will
// get the error "ESPNOW: Peer Interface is invalid"
esp_now_peer_info_t MyPeerInfo;
void ESP_Now_setup() {
WiFi.mode(WIFI_STA);
Serial.println("WiFi.mode(WIFI_STA); done");
WiFi.disconnect(); // for strange reasons WiFi.disconnect() makes ESP-NOW work
Serial.println("WiFi.disconnect(); done");
// Init ESP-NOW
if (esp_now_init() != ESP_OK) {
Serial.println("Error initializing ESP-NOW");
return;
}
Serial.println("esp_now_init() was successful");
// register callback-function that will be executed each time
// function esp_now_send() has finished
esp_now_register_send_cb(OnDataSent);
Serial.println("esp_now_register_send_cb(OnDataSent); done");
// register callback-function that will be executed each time
// ESP-NOW-Data is received
esp_now_register_recv_cb(OnDataRecv);
Serial.println("esp_now_register_recv_cb(OnDataRecv); done");
// the ESP-NOW-Sender needs to "fill out" a list with informations about each receiver
// this is called peer. Therefore you have to create a variable of type esp_now_peer_info_t
//esp_now_peer_info_t MyPeerInfo;
// then "fill out" peer-data-form
memcpy(MyPeerInfo.peer_addr, ESP_NOW_MAC_adrOfRecv, 6);
MyPeerInfo.channel = 0;
MyPeerInfo.encrypt = false;
// after setting up peer-info, add peer
if (esp_now_add_peer(&MyPeerInfo) != ESP_OK){
Serial.println("Failed to add peer");
return;
}
Serial.println("esp_now_add_peer(&peerInfo) was successful");
// this setup peer-info and add peer has to be repeated for each receiver
// that shall receive from this sender
for (int i = 0; i < 6; i++) {
strcat (MAC_adrOfRecv_as_AoC, hex02str(ESP_NOW_MAC_adrOfRecv[i]) );
if (i < 6) {
strcat (MAC_adrOfRecv_as_AoC, ":" );
}
}
MAC_adrOfRecv_as_AoC[17] = 0;
strupr(MAC_adrOfRecv_as_AoC); // make letters UPPERCASE
Serial.print("MAC-Adress of Receiver is ");
Serial.println(MAC_adrOfRecv_as_AoC);
}
void ESP_NOW_SendData()
{
char AoC[10];
// Set values to send
strcpy(my_READYtoSEND_ESP_NOW_Data.MyESP_NOW_MsgStr, "HI I'M SENDING EVERY ");
itoa(SendDataInterval,AoC,10);
strcat (my_READYtoSEND_ESP_NOW_Data.MyESP_NOW_MsgStr,AoC );
strcat (my_READYtoSEND_ESP_NOW_Data.MyESP_NOW_MsgStr, " Milli-SECONDS COUNTiNG UP ");
itoa(Summand,AoC,10);
strcat (my_READYtoSEND_ESP_NOW_Data.MyESP_NOW_MsgStr,AoC );
my_READYtoSEND_ESP_NOW_Data.MyESP_NOW_Int = my_READYtoSEND_ESP_NOW_Data.MyESP_NOW_Int + Summand;
// Send message via ESP-NOW
esp_now_send(ESP_NOW_MAC_adrOfRecv, (uint8_t *) &my_READYtoSEND_ESP_NOW_Data, sizeof(my_READYtoSEND_ESP_NOW_Data));
Serial.print(MySelfHeader);
Serial.println("esp_now_send(ESP_NOW_MAC_adrOfRecv, (uint8_t *) &my_READYtoSEND_ESP_NOW_Data, sizeof(my_READYtoSEND_ESP_NOW_Data)); done");
Serial.print(MySelfHeader);
Serial.print("I am the board named '");
Serial.print(BoardName);
Serial.print("' with the MAC-Adress ");
Serial.println(Own_MacAdr_AoC);
Serial.print(MySelfHeader);
Serial.print("and I try to send my ESP-NOW-Data to the board with MAC-Adress #");
Serial.print(MAC_adrOfRecv_as_AoC);
Serial.println("#");
// if sending has finished function OnDataSent is called
}
/* nonblocking timing based on millis()
this function returns true each time the TimePeriod has expired and immediately
starts a new TimePeriod. So this function
example-use:
unsigned long myTimer;
if (TimePeriodIsOver( myTimer,2000) ) {
//the code here gets executed only every 2000 Milliseconds
}
*/
boolean TimePeriodIsOver (unsigned long &expireTime, unsigned long TimePeriod) {
unsigned long currentMillis = millis();
if ( currentMillis - expireTime >= TimePeriod )
{
expireTime = currentMillis; // set new expireTime
return true; // more time than TimePeriod) has elapsed since last time if-condition was true
}
else return false; // not expired
}
unsigned long SendDataTimer;
/*
everytime the compiler compiles the file new
the macros named "__FILE__", "__DATE__", "__TIME__"
where replaced with what their names say Filename with path
Date and time of the Computer the IDE is running on
// so by simply starting the device the uploaded code yells filename date and time at the serial interface
*/
void PrintWiFiMacAdress()
{
char HexByteDigits[3];
for (uint8_t i = 0; i < 18; i = i + 1)
{
Own_MacAdr_AoC[i] = WiFi.macAddress()[i];
}
Own_MacAdr_AoC[17] = 0; // zero to terminate the string
Serial.print("ESP32-Board's OWN MAC-Address is: ");
Serial.println(Own_MacAdr_AoC);
Serial.println();
Serial.println("copy the line below and replace the codeline");
Serial.println("uint8_t ESP_NOW_MAC_adrOfRecv[] = { .... };");
Serial.println("inside the code with the copied line from the serial monitor");
Serial.println();
Serial.print("uint8_t ESP_NOW_MAC_adrOfRecv[] = {");
for (uint8_t i = 0; i < 16; i = i + 3)
{
HexByteDigits[0] = Own_MacAdr_AoC[i];
HexByteDigits[1] = Own_MacAdr_AoC[i+1];
HexByteDigits[2] = 0; // zero for terminating the string
Serial.print("0x");
Serial.print(HexByteDigits);
if (i < 14) Serial.print(", ");
}
Serial.println(" };");
Serial.println();
}
char* hex02str(uint8_t b) {
static char str[]="FF"; // RAM für einen 2-Zeichen string reservieren.
snprintf(str,sizeof(str),"%02x",b);
return str;
}
void PrintFileNameDateTime()
{
Serial.print("Code running comes from file ");
Serial.println(__FILE__);
Serial.print(" compiled ");
Serial.print(__DATE__);
Serial.print(" ");
Serial.println(__TIME__);
}
void setup() {
// Init Serial Monitor
Serial.begin(115200);
Serial.println(); // a carriage return to make sure serial-output begins in colum 1 of a new line
PrintFileNameDateTime();
PrintWiFiMacAdress();
ESP_Now_setup();
}
void loop() {
SendDataInterval = 5000;
Summand = 6;
// check if timer-intervall is over
if (TimePeriodIsOver(SendDataTimer,SendDataInterval) )
{
Serial.print(MySelfHeader);
Serial.println("SendData");
ESP_NOW_SendData();
Serial.print(MySelfHeader);
Serial.println("SendData done");
}
}
// explanation how ESP-NOW works:
// sending serial data is as easy as
// Serial.begin(baudrate);
// Serial.print("Hello World");
// This can be coded so easy because a lot of things are fixed
// IO-Pins used by Serial fixed
// Connection to other device: made of wire must not be programmed
// standard serial-connection does not care if receiver is connected
// send data and that's all
// with ESP-NOW more things are NOT fixed = are adjustable and MUST be adjusted
// the receiver is NOT defined by wire but through his MAC-Adress
// Data-transport can do only transporting one or multiple bytes
// That's not a real problem the user can define his own data-pattern (data-structure)
// ESP-NOW offers feedback if a datatransmission was successful or failed
// each ESP-modul can receive data from different senders
// so an information from WHICH sender the data was sended is useful
// this is identified by the senders MAC-Adress
// Analogy sending / receiving postal letters:
// if you want to SEND out a letter to somebody you have to write
// the receivers adress on the envelope.
// similar thing with ESP-NOW: the data is NOT guided by a wire its sended "Up in the air"
// with ESP-NOW this receiver-adress is the MAC-Adress of the receiver
// if the postman brings you a letter you don't have to be at home. He will put the letter
// into your letter-box and you can pick it out later.
// similar thing with ESP-NOW: there is some kind of "letter-box" the received data will be stored into
// the data-receiving into the "letter-box" runs in the backround similar to an interrupt
// The received data will be catched up and must be copied into some kind of "letterbox"
// any transporting service has some kind of procedure if you want to send a package or a letter
// fill out some kind of form (on paper or onnline) print out and glue a sticker
// with the transporting informations on the package/envelope put the package / envelope into the
// "send out" box or bring it to a pickup store
// so there are some things to do:
// - build a special designed "package" with multiple special designed compartments
// where all parts of the data has its own compartment
// defining a new variable type "structure"
// - build a "letter-box" designed expecially for the user-defined package
// and "mount it in front of your house" so it can be easily found by the postman
// - write a list with receiver-adresses and handover this list to ESP-NOW (the "letter-department" of your "company"
// - setup if data shall be encrypted or not
// - setup transmission channel that shall be used for sending the data wireless
// - design your "sending-form"
// these are the reasons why ESP-NOW needs a bit more than just
// Serial.begin(baudrate);
// Serial.print("My Data");
// Your mobile phone has a lot of functions. It took you some time to learn them all
// Same thing with ESP-NOW it will take some time to learn it
// if you want it fully automated without learning anything
// use Alexa, Siri or google assistant instead
best regards Stefan