Last Updated: 08/06/2024

#70 ESP-NOW Broadcast Mode

Projects >> #70 ESP-NOW Broadcast Mode

IMPORTANT: ESP32 board library changed in 2024, this has changed some functions in ESP-NOW

Lesson 1 Getting Started

In 2024 the ESP32 library was updated to version 3.0.0 (now at 3.0.1)

Although there are some obvious changes that break code such as the esp_now_register_recv_cb(OnDataRecv);

I discovered other issues that failed silently, setting a custom mac address is one of them.

This video shows how I set a custom mac address using ESP32 board library v3.0.1


Code Download New and old custom mac address code

Lesson 1 Getting Started

In 2024 the ESP32 library was updated to version 3.0.0

This was much more than a bug fix and has changes that will break existing code. As I have discovered this includes ESP-NOW.
I first noticed this with the the line:

esp_now_register_recv_cb(OnDataRecv);

Producing the error:

Compilation error: invalid conversion from 'void (*)(const uint8_t*, const uint8_t*, int)' {aka 'void (*)(const unsigned char*, const unsigned char*, int)'} to 'esp_now_recv_cb_t' {aka 'void (*)(const esp_now_recv_info*, const unsigned char*, int)'} [-fpermissive]

This video shows how I fixed thse issues using the version 3.0.0 library.

Code Download Example receive and transmit code from the video

ESP-NOW Broadcast Mode - Calling All Stations

This tutorial has been split into two parts.

The first section will deal with the basics of ESP-NOW using a pair of ESP-32's broadcasting to each others known MAC addresses.

The 2nd example will uses broadcast mode so does not know the other boards MAC address, but also does not know if the board received a message.

ESP-NOW Broadcast Mode

Hardware Requirements

Although ESP-NOW runs on ESP32 and ESP8266 this tutorial has been coded for the ESP32, I used 2 ESP 32 Dev Modules.

If you have 3 or more boards it is useful for seeing the effects of broadcast mode.

I have provided links to the Random Nerd titorials as they have code for ESP8266. In my final version the code runs on both types of board but that is for another tutorial. I have provided my code for it as example 3 but will run through it in more details in a second video if there is enough interest.

What is ESP-NOW

Traditonal wifi networks send and receive through a router box or access point with devices each being given an IP address either hyard coded or thorugh DHCP.

traditionalwifi


ESP-NOW is able to communicate over the same signal as wifi (frequency and inbuilt hardware) but instead of going thorugh a router box is able to communicate directly with each device. Individual devices are identified by their MAC address rather than an IP address.

ESP-NOW network

Standard ESP-NOW Advantages
No Router Box Removing the router box allows mesh netwrosk to be built of individual ESP-NOW enabled devices to communicate individually or to a number of devices in a single broadcast.
Acknowledgment of Receipt Within the ESP-NOW protocol is the ability to now only send and receive but also receive a message to say if a transmission has been received or not aloowing a message to be resent if required.
Encryption Easy to turn on Eencryption mode

Speed

In my tests transmissions have been faster than going via a router box. I tend to try to "Fail Safe" so I tend to base my calculation on a message being received withing 0.18 seconds. This not only includes the transmission but the processing of the message and then the generation and sending of the response.
also this takes into account that I may have multiple devices sending and receiving from each other at the same tme so is much lower than the actual trasmission speed.
If you have no interest in a responce than a much faster rate of message sending can be used.
As with all wifi the environment can have an effect on signal strength and transmission.
Range Ranges have been reported up to 220 metres, I certainly have had no issues with range around the house. If you work on the range being slightly shorter than a router box (router box should have a better built in antenna) you should be safe.
Interferance For my project I needed to make sure transmission wasn't corrupted by other sources. As such I did tests with conventional Wifi signal being boosted very close to my set up, multiple mobile phones, numerous ESP-NOW devices transmitting and so far have found reliability to be very good.

The only issue was a slight sound over my desktop computer speaker when 6 ESP-NOW devices are transmitting right next to it.
Web Connection Although ESP-NOW will not connect directly to the web, you can configure and ESP-NOW device to communicate via ESP-NOW and over a traditional router based network at the same time to get your data online if required.
Standard ESP-NOW Disadvantages
Transmission Size Don't start thinking of transmiitting video. This system allows a packet of data up to 250 bytes to be transmitted.
so it is ideal for sending sensor readings or instructions between devices. Think of it as devices being able to text each other.
Device number limit. With encryption is is not recommened to have more than 7 devices. Without encryption you can have aorund 20 devices.
If you add too many devices later devices do not get a message as I have found out.
Addressing You need to know the MAC address of the device you are sending to. There are ways to interogate this but it gets a bit more complicated. especially when you have multiple devices talking to each other.
This becomes an issue is a board is replaced as the MAC address will be different.
To combat this I always change the MAC address to one I have defined in code.

ESP-NOW broadcast mode

In broadcast mode the units sned out transmissions to a fixed broadcast address that all units listen to. So every unit is always sending out to all the units in range.
At the same time each unit has no idea how many or if any units picked up their broadcast.

Broadcast ESP-NOW Advantages
No limit to items on network By transmitting and receivning from a single address there is now no limit on the the number of units sending and receiving from each other (although the pactic al limits come down to how much data can be recived and processed).
Items can be added at any time As a general broadcast address is being sent to and received from new items can be added to the network without the need to know their MAC address.
No need to keep lists of MAC addresses As a single broadcast address is used there is no need to keep a list of each boards mac addresses.
Encryption Encryption is built into the protocol

Speed

In my tests transmissions have been as fast if not faster than going via a router box. I tend to try to "Fail Safe" so I tend to base my calculation on a message being received withing 0.18 seconds. This not only includes the transmission but the processing of the message and then the generation and sending of the response.
also this takes into account that I may have multiple devices sending and receiving from each other at the same tme so is much lower than the actual trasmission speed.
If you have no interest in a responce than a much faster rate of message sending can be used.
As with all wifi the environment can have an effect on signal strength and transmission.
Range Ranges have been reported up to 220 metres, I certainly have had no issues with range around the house. If you work on the range being slightly shorter than a router box (router box should have a better built in antenna) you should be safe.
Interferance For my project I needed to make sure transmission wasn't corrupted by other sources. As such I did tests with conventional Wifi signal being boosted very close to my set up, multiple mobile phones, numerous ESP-NOW devices transmitting and so far have found reliability to be very good.

The only issue was a slight sound over my desktop computer speaker when 6 ESP-NOW devices are transmitting right next to it.
Web Connection Although ESP-NOW will not connect directly to the web, you can configure and ESP-NOW device to communicate via ESP-NOW and over a traditional router based network at the same time to get your data online if required.
Broadcast ESP-NOW Disadvantages
No encryption No encrytion is used on the network so don't sdtrast transmitting your trade secrets.
Snooping As the transmissions are sent to a stndard bnroadcast address anyone could being a device nearby and start to pick up your data packets so again no ideal for secret data.
Network spamming Just as others can receive your data, because it's a standard broadcast address they could also start transmitting rubbish that then gets picked up on your network.
No built in transmission receipt system There are no built in protocols to track how many if any devices have picked up your transmission or any way to know if they are freindly or unfriendly snooping devices.

 

 

Example 1: ESP-NOW ESP32 example


Click to Download code: ESPNOWESP32v1.ino

Rather than reinventing I have written a code example based on the tutorials at:

Random Nerds Getting Started with ESP-NOW (ESP32 with Arduino IDE)

Random Nerds Getting Started with ESP-NOW (ESP8266 NodeMCU with Arduino IDE)

The code has been written so that it's possible to quickly oload it with minor differences onto a pair of ESP32 boards.

By loading into a pair of Serial monitors it is possible to see the data being transferred backwards and forwards.

NOTE: In this mode the other boards MAC address must be known

 
/* ESPNOWESP32v1
   04/02/2024

*/


#include "esp_now.h"
#include "WiFi.h"

#include "esp_wifi.h" //extra library to change mac address

// REPLACE WITH YOUR RECEIVER MAC Address
//uint8_t broadcastAddress[] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};//All stations broadcast address

uint8_t broadcastAddress[] = {0xE0, 0xE2, 0xE6, 0xD1, 0x0D, 0x01}; //broadcast address
uint8_t CustomMACaddress[] = {0xE0, 0xE2, 0xE6, 0xD1, 0x0D, 0x02}; //Custom MAC address

//uint8_t CustomMACaddress[] = {0xE0, 0xE2, 0xE6, 0xD1, 0x0D, 0x01}; //Custom MAC address
//uint8_t broadcastAddress[] = {0xE0, 0xE2, 0xE6, 0xD1, 0x0D, 0x02}; //Broadcast address

// Structure example to send data
// Must match the receiver structure
typedef struct struct_message {
  byte myByte[50];
} struct_message;

// Create a struct_message called myData
struct_message sendData;//data to be sent
struct_message recData;//data received

esp_now_peer_info_t peerInfo;

// callback when data is sent
void OnDataSent(const uint8_t *mac_addr, esp_now_send_status_t status) {
  Serial.print("\r\nLast Packet Send Status:\t");
  //This line demonstrates the data returned
  //Ideally it fails it should really resend the data.

  Serial.println(status == ESP_NOW_SEND_SUCCESS ? "Delivery Success" : "Delivery Fail");
}

// callback function that will be executed when data is received
void OnDataRecv(const uint8_t * mac, const uint8_t *incomingData, int len) {
  memcpy(&recData, incomingData, sizeof(recData));

  //WARNING: The code below is badly written and to demonstrate the onDataRevv function only
  //Any data should be copied out of this function as quickly as possible and processed in the main loop
  //This will allow for the next set of data to arrive
  //Use similar to an interrupot routine
  Serial.print("Bytes received: ");
  Serial.println(len);
  for(int w=0;w < 50;w++){
   Serial.print("byte: ");
   Serial.print(w);
   Serial.print(" = "); 
   Serial.print(recData.myByte[w]);
  }
  Serial.println(" ");
  
}
 
void setup() {
  // Init Serial Monitor
  Serial.begin(115200);
  Serial.println("ESPNOWESP32v1");
  // Set device as a Wi-Fi Station
  WiFi.mode(WIFI_STA);

  Serial.print("Original MAC address: ");
  Serial.println(WiFi.macAddress());//The address the board came with

  esp_wifi_set_mac(WIFI_IF_STA, &CustomMACaddress[0]);
  Serial.print("Custom MAC Address for ESP32:  ");
  Serial.println(WiFi.macAddress());  /*Prints Custom MAC address*/

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

  // Once ESPNow is successfully Init, we will register for Send CB to
  // get the status of Trasnmitted packet
  esp_now_register_send_cb(OnDataSent);

  //on data receive function
  esp_now_register_recv_cb(OnDataRecv);
  
  // Register peer
  memcpy(peerInfo.peer_addr, broadcastAddress, 6);
  peerInfo.channel = 0;  
  peerInfo.encrypt = false;
  
  // Add peer        
  if (esp_now_add_peer(&peerInfo) != ESP_OK){
    Serial.println("Failed to add peer");
    return;
  }
}
 
byte q;//used to store an incrementing byte to demo transmission

void loop() {
  int w; //just used as a counter
  q++;
  // Set values to send
  for(w=0;w < 50;w++){
    //different send data patterns to distinguish between boards
    //sendData.myByte[w] =  q++;//q will overun at 255 and go back to Zero
    sendData.myByte[w] = w + q;//q will overun at 255 and go back to Zero
    Serial.print(sendData.myByte[w]);
    Serial.print(",");
  }
  Serial.println(" ");
  
  
  // Send message via ESP-NOW
  esp_err_t result = esp_now_send(broadcastAddress, (uint8_t *) &sendData, sizeof(sendData));
   
  delay(2000);
}

 

Example 2: ESP-NOW ESP32 Broadcast mode example


Click to Download code: ESPNOWBroadcastESP32v1

Once again the code needs to be uploaded to a pair of boards but start by just uploading it to one.

As you do this you will see the old code fail to deliever because it is targeting a set MAC address while the broadcast delivers because it is sending to all stations.

Once uploaded to both boards they will transmit data to each other but it is no reponse is sent back. We only know the transmission is successfull because of what we see in the Serial Monitor.

If more boards are added they will all receive every transmission.

 

 
/* ESPNOWBroadcastESP32v1
   04/02/2024

   delivery from other board fails because the MAC address has changed
   sending is OK because it's sending to all addresses.

*/


#include "esp_now.h"
#include "WiFi.h"

//no longer needed as wifi address not changed
//#include "esp_wifi.h" //extra library to change mac address

// REPLACE WITH YOUR RECEIVER MAC Address
uint8_t broadcastAddress[] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };  //All stations broadcast address



// Structure example to send data
// Must match the receiver structure
typedef struct struct_message {
  byte myByte[50];
} struct_message;

// Create a struct_message called myData
struct_message sendData;  //data to be sent
struct_message recData;   //data received

esp_now_peer_info_t peerInfo;





// callback function that will be executed when data is received
void OnDataRecv(const uint8_t *mac, const uint8_t *incomingData, int len) {
  memcpy(&recData, incomingData, sizeof(recData));

  //WARNING: The code below is badly written and to demonstrate the onDataRevv function only
  //Any data should be copied out of this function as quickly as possible and processed in the main loop
  //This will allow for the next set of data to arrive
  //Use similar to an interrupt routine
  Serial.print("Bytes received: ");
  Serial.println(len);
  for (int w = 0; w < 50; w++) {
    Serial.print("byte: ");
    Serial.print(w);
    Serial.print(" = ");
    Serial.print(recData.myByte[w]);
  }
  Serial.println(" ");
}

void setup() {
  // Init Serial Monitor
  Serial.begin(115200);
  Serial.println("ESPNOWBroadcastESP32v1");
  // Set device as a Wi-Fi Station
  WiFi.mode(WIFI_STA);



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



  //on data receive function
  esp_now_register_recv_cb(OnDataRecv);

  // Register peer
  memcpy(peerInfo.peer_addr, broadcastAddress, 6);
  //peerInfo.channel = 0;
  peerInfo.channel = 1;
  peerInfo.encrypt = false;

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

byte q;  //used to store an incrementing byte to demo transmission
unsigned long currentMillis;
unsigned long transmitMillis;
int transmitTimer = 2000;

void loop() {
  int w;  //just used as a counter
  currentMillis = millis();
  if (currentMillis - transmitMillis >= transmitTimer) {
    transmitMillis = currentMillis;
    q++;
    // Set values to send
    for (w = 0; w < 50; w++) {
      //different send data patterns to distinguish between boards
      //sendData.myByte[w] =  q++;//q will overun at 255 and go back to Zero
      sendData.myByte[w] = w + q;  //q will overun at 255 and go back to Zero
      Serial.print(sendData.myByte[w]);
      Serial.print(",");
    }
    Serial.println(" ");


    // Send message via ESP-NOW
    esp_err_t result = esp_now_send(broadcastAddress, (uint8_t *)&sendData, sizeof(sendData));
    //Serial.println(result);
  }
}

 

How to use and improve ESP-NOW Broadcast mode.

ESP-NOW broadcast mode is not suitable for everything. If it is mission critical, top secret etc it's not the format I would use. However, there are situation when this sort of set up is ideal and with some fairly simple code we can make the system far more robust.
I will be using this system for multiple direction communication on a model railway example but the solutions could be used in multiple sensor based environments.

Broadcast ESP-NOW Fixes
No encryption It would be possible to use your own encryption/decryption code on your network. There are even AES encypiton libraries available for th Arduino IDE so as long as you keep you packet data after encyption within the 250 byte transmission limit you can still encypt you message.
As I'm sending modfel railway control codes I didn't think I needed that level of encyrption. Also it has an imapct on speed as each message needs to be encrypted and decrypted but the option is available.
Snooping As I'm not sending anyhting sensitive I wasn't worried about someone snooping on my transmissions.
Network spamming This is more of an issue, not that I was worried about deliberate hacking. However, if two layouts at an exhibition used the same system it would be very easy for them to start sneindg data to each other.
As such I chose to create a network Address that gets transmitted as part of every message so that I can check the message was sent off my system.
This could be hacked but why?
Transmission receipt system As well as including a network address in my transmissions I also included the MAC address of the transmitter so that if a receipt is sent the sender will know it is for them. If a receipt is not received the original sender repeats the message at intervals up to 5 times.

The other alternative was to send every message 5 times as standard, this is the type of thing that DCC model railway control does as standard as there is no receipt that a message has been delivered. However as wifi is slower than direct wire based transmission I was concerned that I could start generating huge spikes of netwrk traffic. Hence the solution I chose.
Message Types Each message not only has a network address and sender address but also has a "Type". For the railway this could be a speed control, an accessory command or a sensor reporting back. as each deivice will know what sort of messages it will process it can quickly filter out messages that it does not need to process.

 

Model Railway Requirements

My layout is built on a modular system. This means the order of the boards can be changed and that not all boardws will be in use all the time.
As such my control system needs to be flexible enough to deal with this issue.

I wanted 3 loco controllers, this allows me to use a controller while my grandson has a simpler version while also leaving a spare in reserve. I not only want each controller to be able to send sommands to the DCC-EX controller but I want to be able to swap loco data between controllers in real time.

Accessory controllers on my layout tend to be Touch screens over Neopixels so just touch the route and the track sets itself. These wait for a reply from the accessory decoder before flagging the change on the display. As there are multiple panels all appropriate panels update themselves regardless of which one sent the track control command.

On each baseboard is a board controller. This receives the incoming command to deal with point/turnout control as well as various lighting/animation effects. Some items require responses being snet back, other the unit just processes without response.

Effects controllers are for very complex animations, often including sound that I build on seperate controller boards as often it's just easer to make the stand alone using.

Time control: I also wanted certain animations such as lights to be synched to the layout fast clock, this means lighst switch on and off at appropriate times.

Although I started using conventional DCC as 3 extra boards got added I quickly realised I would pass the limit of ESP-NOW boards and needed to swithc to bradcast mode.

As I was building my system I was also asked to help with a DCC system that needed block detection data sent over ESP-NOW to Loconet and then an analogue layout that required point control from different olocations that was again ideal for ESP-NOW control.

after building these systems I thgouht it would make sense to build a system called NOWRail to act as a communication system for DCC layouts but also able to add DCC functionality for turnouts to Analogue layouts drastically reducing wiring needs.

IOM Railway Control

Programming requirenents and Solution.

ESP-NOW is availale on ESP8288 and ESP32 based boards.Although transmissions can be sent between both types of board the code for ESP-NOW is set up slightly differently on each type of board. I therefore had to add some preprocesor condiional code to make sure the code could run on both types of board.

The next issue was then sending and processing of commands.

Rather than creating and sending messages instantly and then receiving and processing the messages in the same way I decided to use FIFO (First in First Out) buffers to store the messages and then send or process them depending if they are incoming or outgoing messages.

I finished up with 3 buffers, each 256 bytes long with 50 bytes per instruction.

making the buffers with 256 lines meant that the position counter if made one byte in length would just over flow and automatically go back to the beginning of the buffer. It meant that in toatl the buffers took up 38,400 bytes that is not really a problem for even the most basic ESP8266 D1 mini.

The code flow is as follows.

Each unit has to be able to send and receive so all have the same core code system.

Code Flow Diagram

Other Considerations

When I started on this is was just going to be used by me, however as I have helped others with their projects I decided to write the code in a way that other items such as block detectors/loconet and analogue lkayout control could be easiliy added.
This adds to extra thoughts on code layout as how do you allow people to modify/use the code yet work in a way that stops them wrecking the code.

As I now have a working prototype I have decided to write the code as a class/library with functions that are called and examples on how to use each item.

This will be a slower method of writing the code with new things to learn, but should allow lesser skilled programmers to use the code.

Example 3: NOWRailv1

This is the proof of concept code for NOWRailv1. It has been tested on ESP32 and ESP8266 boards with other boards generating background traffic and interferance.

So far it has tested very well and now needs to move onto the next stage.

Although the code is a bit rough you will see the various buffers being written and read from as well as the broadcast transmissions.

The code is in multiple tabs. Moving forwards I will probably write it as a class and maybe even a library for ease of use.

Click to Download code: NOWRailV1.zip

Additional Resources

How To 6 : FIFO (Circular) Buffer 23/12/2023

Random Nerds Getting Started with ESP-NOW (ESP32 with Arduino IDE)

Random Nerds Getting Started with ESP-NOW (ESP8266 NodeMCU with Arduino IDE)

Comments


For help or suggestions on new projects, please email the address in this image: and use #70 ESP-NOW Broadcast Mode as a reference.