ESP32: ESP-NOW Web Server Sensor Dashboard (ESP-NOW + Wi-Fi)

In this project, you’ll learn how to host an ESP32 web server and use ESP-NOW communication protocol at the same time. You can have several ESP32 boards sending sensor readings via ESP-NOW to one ESP32 receiver that displays all readings on a web server. The boards will be programmed using Arduino IDE.

ESP32: ESP-NOW Web Server Sensor Dashboard using Arduino IDE (ESP-NOW and Wi-Fi simultaneously)

We have other guides related to ESP-NOW that you might be interested in:

Watch the Video Tutorial

Note: we’ve updated this tutorial with a better method to use ESP-NOW and Wi-Fi simultaneously. The video doesn’t use this current method. You can still watch the video to see how everything works, but we recommend that you take a look at the written article.

Using ESP-NOW and Wi-Fi Simultaneously

Using ESP-NOW and Wi-Fi Simultaneously: ESP-NOW Receiver Web Server and ESP-NOW Sender boards

There are a few things you need to take into account if you want to use Wi-Fi to host a web server and use ESP-NOW simultaneously to receive sensor readings from other boards:

  • The ESP32 sender boards must use the same Wi-Fi channel of the receiver board.
  • The Wi-Fi channel of the receiver board is automatically assigned by your Wi-Fi router.
  • The Wi-Fi mode of the receiver board must be access point and station (WIFI_AP_STA).
  • You can set up the same Wi-Fi channel manually, or you can add a simple spinet of code on the sender to set its Wi-Fi channel to the same of the receiver board.

Project Overview

The following diagram shows a high-level overview of the project we’ll build.

ESP-NOW Receiver Web Server and ESP32 boards sending temperature humidity readings with ESP-NOW
  • There are two ESP32 sender boards that send DHT22 temperature and humidity readings via ESP-NOW to one ESP32 receiver board (ESP-NOW many to one configuration);
  • The ESP32 receiver board receives the packets and displays the readings on a web server;
  • The web server is updated automatically every time it receives a new reading using Server-Sent Events (SSE).

Prerequisites

Before proceeding with this project, make sure you check the following prerequisites.

Arduino IDE

We’ll program the ESP32 boards using Arduino IDE, so before proceeding with this tutorial, make sure you have the ESP32 board installed in your Arduino IDE.

DHT Libraries

The ESP32 sender board will send temperature and humidity readings from a DHT22 sensor.

To read from the DHT sensor, we’ll use the DHT library from Adafruit. To use this library you also need to install the Adafruit Unified Sensor library. Follow the next steps to install those libraries.

1. Open your Arduino IDE and go to Sketch Include Library > Manage Libraries. The Library Manager should open.

2. Search for “DHT” on the Search box and install the DHT library from Adafruit.

Installing Adafruit DHT library

3. After installing the DHT library from Adafruit, type “Adafruit Unified Sensor” in the search box. Scroll all the way down to find the library and install it.

Installing Adafruit Unified Sensor driver library

After installing the libraries, restart your Arduino IDE.

To learn more about the DHT11 or DHT22 temperature sensor, read our guide: ESP32 with DHT11/DHT22 Temperature and Humidity Sensor using Arduino IDE.

Async Web Server Libraries

To build the web server you need to install the following libraries:

These libraries aren’t available to install through the Arduino Library Manager, so you need to copy the library files to the Arduino Installation Libraries folder. Alternatively, in your Arduino IDE, you can go to Sketch Include Library > Add .zip Library and select the libraries you’ve just downloaded.

Arduino_JSON Library

You need to install the Arduino_JSON library. You can install this library in the Arduino IDE Library Manager. Just go to Sketch Include Library > Manage Libraries and search for the library name as follows:

Install Arduino JSON library Arduino IDE

Parts Required

To follow this tutorial, you need multiple ESP32 boards. We’ll use three ESP32 boards. You also need:

You can use the preceding links or go directly to MakerAdvisor.com/tools to find all the parts for your projects at the best price!

Getting the Receiver Board MAC Address

To send messages via ESP-NOW, you need to know the receiver board’s MAC address. Each board has a unique MAC address (learn how to Get and Change the ESP32 MAC Address).

Upload the following code to your ESP32 receiver board to get its MAC address.

// Complete Instructions to Get and Change ESP MAC Address: https://RandomNerdTutorials.com/get-change-esp32-esp8266-mac-address-arduino/

#ifdef ESP32
  #include <WiFi.h>
#else
  #include <ESP8266WiFi.h>
#endif

void setup(){
  Serial.begin(115200);
  Serial.println();
  Serial.print("ESP Board MAC Address:  ");
  Serial.println(WiFi.macAddress());
}
 
void loop(){

}

View raw code

After uploading the code, press the RST/EN button, and the MAC address should be displayed on the Serial Monitor.

ESP32 board MAC Address with Arduino IDE Serial Monitor

ESP32 Receiver (ESP-NOW + Web Server)

The ESP32 receiver board receives the packets from the sender boards and hosts a web server to display the latest received readings.

Upload the following code to your receiver board – the code is prepared to receive readings from two different boards.

/*
  Rui Santos
  Complete project details at https://RandomNerdTutorials.com/esp32-esp-now-wi-fi-web-server/
  
  Permission is hereby granted, free of charge, to any person obtaining a copy
  of this software and associated documentation files.
  
  The above copyright notice and this permission notice shall be included in all
  copies or substantial portions of the Software.
*/

#include <esp_now.h>
#include <WiFi.h>
#include "ESPAsyncWebServer.h"
#include <Arduino_JSON.h>

// Replace with your network credentials (STATION)
const char* ssid = "REPLACE_WITH_YOUR_SSID";
const char* password = "REPLACE_WITH_YOUR_PASSWORD";

// Structure example to receive data
// Must match the sender structure
typedef struct struct_message {
  int id;
  float temp;
  float hum;
  unsigned int readingId;
} struct_message;

struct_message incomingReadings;

JSONVar board;

AsyncWebServer server(80);
AsyncEventSource events("/events");

// callback function that will be executed when data is received
void OnDataRecv(const uint8_t * mac_addr, const uint8_t *incomingData, int len) { 
  // Copies the sender mac address to a string
  char macStr[18];
  Serial.print("Packet received from: ");
  snprintf(macStr, sizeof(macStr), "%02x:%02x:%02x:%02x:%02x:%02x",
           mac_addr[0], mac_addr[1], mac_addr[2], mac_addr[3], mac_addr[4], mac_addr[5]);
  Serial.println(macStr);
  memcpy(&incomingReadings, incomingData, sizeof(incomingReadings));
  
  board["id"] = incomingReadings.id;
  board["temperature"] = incomingReadings.temp;
  board["humidity"] = incomingReadings.hum;
  board["readingId"] = String(incomingReadings.readingId);
  String jsonString = JSON.stringify(board);
  events.send(jsonString.c_str(), "new_readings", millis());
  
  Serial.printf("Board ID %u: %u bytes\n", incomingReadings.id, len);
  Serial.printf("t value: %4.2f \n", incomingReadings.temp);
  Serial.printf("h value: %4.2f \n", incomingReadings.hum);
  Serial.printf("readingID value: %d \n", incomingReadings.readingId);
  Serial.println();
}

const char index_html[] PROGMEM = R"rawliteral(
<!DOCTYPE HTML><html>
<head>
  <title>ESP-NOW DASHBOARD</title>
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.7.2/css/all.css" integrity="sha384-fnmOCqbTlWIlj8LyTjo7mOUStjsKC4pOpQbqyi7RrhN7udi9RwhKkMHpvLbHG9Sr" crossorigin="anonymous">
  <link rel="icon" href="data:,">
  <style>
    html {font-family: Arial; display: inline-block; text-align: center;}
    p {  font-size: 1.2rem;}
    body {  margin: 0;}
    .topnav { overflow: hidden; background-color: #2f4468; color: white; font-size: 1.7rem; }
    .content { padding: 20px; }
    .card { background-color: white; box-shadow: 2px 2px 12px 1px rgba(140,140,140,.5); }
    .cards { max-width: 700px; margin: 0 auto; display: grid; grid-gap: 2rem; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); }
    .reading { font-size: 2.8rem; }
    .packet { color: #bebebe; }
    .card.temperature { color: #fd7e14; }
    .card.humidity { color: #1b78e2; }
  </style>
</head>
<body>
  <div class="topnav">
    <h3>ESP-NOW DASHBOARD</h3>
  </div>
  <div class="content">
    <div class="cards">
      <div class="card temperature">
        <h4><i class="fas fa-thermometer-half"></i> BOARD #1 - TEMPERATURE</h4><p><span class="reading"><span id="t1"></span> &deg;C</span></p><p class="packet">Reading ID: <span id="rt1"></span></p>
      </div>
      <div class="card humidity">
        <h4><i class="fas fa-tint"></i> BOARD #1 - HUMIDITY</h4><p><span class="reading"><span id="h1"></span> &percnt;</span></p><p class="packet">Reading ID: <span id="rh1"></span></p>
      </div>
      <div class="card temperature">
        <h4><i class="fas fa-thermometer-half"></i> BOARD #2 - TEMPERATURE</h4><p><span class="reading"><span id="t2"></span> &deg;C</span></p><p class="packet">Reading ID: <span id="rt2"></span></p>
      </div>
      <div class="card humidity">
        <h4><i class="fas fa-tint"></i> BOARD #2 - HUMIDITY</h4><p><span class="reading"><span id="h2"></span> &percnt;</span></p><p class="packet">Reading ID: <span id="rh2"></span></p>
      </div>
    </div>
  </div>
<script>
if (!!window.EventSource) {
 var source = new EventSource('/events');
 
 source.addEventListener('open', function(e) {
  console.log("Events Connected");
 }, false);
 source.addEventListener('error', function(e) {
  if (e.target.readyState != EventSource.OPEN) {
    console.log("Events Disconnected");
  }
 }, false);
 
 source.addEventListener('message', function(e) {
  console.log("message", e.data);
 }, false);
 
 source.addEventListener('new_readings', function(e) {
  console.log("new_readings", e.data);
  var obj = JSON.parse(e.data);
  document.getElementById("t"+obj.id).innerHTML = obj.temperature.toFixed(2);
  document.getElementById("h"+obj.id).innerHTML = obj.humidity.toFixed(2);
  document.getElementById("rt"+obj.id).innerHTML = obj.readingId;
  document.getElementById("rh"+obj.id).innerHTML = obj.readingId;
 }, false);
}
</script>
</body>
</html>)rawliteral";

void setup() {
  // Initialize Serial Monitor
  Serial.begin(115200);

  // Set the device as a Station and Soft Access Point simultaneously
  WiFi.mode(WIFI_AP_STA);
  
  // Set device as a Wi-Fi Station
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
    delay(1000);
    Serial.println("Setting as a Wi-Fi Station..");
  }
  Serial.print("Station IP Address: ");
  Serial.println(WiFi.localIP());
  Serial.print("Wi-Fi Channel: ");
  Serial.println(WiFi.channel());

  // 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 recv CB to
  // get recv packer info
  esp_now_register_recv_cb(OnDataRecv);

  server.on("/", HTTP_GET, [](AsyncWebServerRequest *request){
    request->send_P(200, "text/html", index_html);
  });
   
  events.onConnect([](AsyncEventSourceClient *client){
    if(client->lastId()){
      Serial.printf("Client reconnected! Last message ID that it got is: %u\n", client->lastId());
    }
    // send event with message "hello!", id current millis
    // and set reconnect delay to 1 second
    client->send("hello!", NULL, millis(), 10000);
  });
  server.addHandler(&events);
  server.begin();
}
 
void loop() {
  static unsigned long lastEventTime = millis();
  static const unsigned long EVENT_INTERVAL_MS = 5000;
  if ((millis() - lastEventTime) > EVENT_INTERVAL_MS) {
    events.send("ping",NULL,millis());
    lastEventTime = millis();
  }
}

View raw code

How the Code Works

First, include the necessary libraries.

#include <esp_now.h>
#include <WiFi.h>
#include "ESPAsyncWebServer.h"
#include <Arduino_JSON.h>

The Arduino_JSON library is needed because we’ll create a JSON variable with the data received from each board. This JSON variable will be used to send all the needed information to the web page as you’ll see later in this project.

Insert your network credentials on the following lines so that the ESP32 can connect to your local network.

const char* ssid = "REPLACE_WITH_YOUR_SSID";
const char* password = "REPLACE_WITH_YOUR_PASSWORD";

Data Structure

Then, create a structure that contains the data we’ll receive. We called this structure struct_message and it contains the board ID, temperature and humidity readings, and the reading ID.

typedef struct struct_message {
    int id;
    float temp;
    float hum;
    int readingId;
} struct_message;

Create a new variable of type struct_message that is called incomingReadings that will store the variables’ values.

struct_message incomingReadings;

Create a JSON variable called board.

JSONVar board;

Create an Async Web Server on port 80.

AsyncWebServer server(80);

Create Event Source

To automatically display the information on the web server when a new reading arrives, we’ll use Server-Sent Events (SSE).

The following line creates a new event source on /events.

AsyncEventSource events("/events");

Server-Sent Events allow a web page (client) to get updates from a server. We’ll use this to automatically display new readings on the web server page when a new ESP-NOW packet arrives.

Important: Server-sent events are not supported on Internet Explorer.

OnDataRecv() function

The OnDataRecv() function will be executed when you receive a new ESP-NOW packet.

void OnDataRecv(const uint8_t * mac_addr, const uint8_t *incomingData, int len) {

Inside that function, print the sender’s MAC address:

// Copies the sender mac address to a string
char macStr[18];
Serial.print("Packet received from: ");
snprintf(macStr, sizeof(macStr), "%02x:%02x:%02x:%02x:%02x:%02x",
         mac_addr[0], mac_addr[1], mac_addr[2], mac_addr[3], mac_addr[4], mac_addr[5]);
Serial.println(macStr);

Copy the information in the incomingData variable into the incomingReadings structure variable.

memcpy(&incomingReadings, incomingData, sizeof(incomingReadings));

Then, create a JSON String variable with the received information (jsonString variable):

board["id"] = incomingReadings.id;
board["temperature"] = incomingReadings.temp;
board["humidity"] = incomingReadings.hum;
board["readingId"] = String(incomingReadings.readingId);
String jsonString = JSON.stringify(board);

Here’s an example of how the jsonString variable may look like after receiving the readings:

board = {
  "id": "1",
  "temperature": "24.32",
  "humidity" = "65.85",
  "readingId" = "2"
}

After gathering all the received data on the jsonString variable, send that information to the browser as an event (“new_readings”).

events.send(jsonString.c_str(), "new_readings", millis());

Later, we’ll see how to handle these events on the client side.

Finally, print the received information on the Arduino IDE Serial Monitor for debugging purposes:

Serial.printf("Board ID %u: %u bytes\n", incomingReadings.id, len);
Serial.printf("t value: %4.2f \n", incomingReadings.temp);
Serial.printf("h value: %4.2f \n", incomingReadings.hum);
Serial.printf("readingID value: %d \n", incomingReadings.readingId);
Serial.println();

Building the Web Page

The index_html variable contains all the HTML, CSS and JavaScript to build the web page. We won’t go into details on how the HTML and CSS works. We’ll just take a look at how to handle the events sent by the server.

Handle Events

Create a new EventSource object and specify the URL of the page sending the updates. In our case, it’s /events.

if (!!window.EventSource) {
 var source = new EventSource('/events');

Once you’ve instantiated an event source, you can start listening for messages from the server with addEventListener().

These are the default event listeners, as shown here in the AsyncWebServer documentation.

source.addEventListener('open', function(e) {
  console.log("Events Connected");
}, false);
source.addEventListener('error', function(e) {
 if (e.target.readyState != EventSource.OPEN) {
   console.log("Events Disconnected");
 }
}, false);

source.addEventListener('message', function(e) {
 console.log("message", e.data);
}, false);

Then, add the event listener for “new_readings”.

source.addEventListener('new_readings', function(e) {

When the ESP32 receives a new packet, it sends a JSON string with the readings as an event (“new_readings”) to the client. The following lines handle what happens when the browser receives that event.

console.log("new_readings", e.data);
var obj = JSON.parse(e.data);
document.getElementById("t"+obj.id).innerHTML = obj.temperature.toFixed(2);
document.getElementById("h"+obj.id).innerHTML = obj.humidity.toFixed(2);
document.getElementById("rt"+obj.id).innerHTML = obj.readingId;
document.getElementById("rh"+obj.id).innerHTML = obj.readingId;

Basically, print the new readings on the browser console, and put the received data into the elements with the corresponding id on the web page.

setup()

In the setup(), set the ESP32 receiver as an access point and Wi-Fi station:

WiFi.mode(WIFI_AP_STA);

The following lines connect the ESP32 to your local network and print the IP address and the Wi-Fi channel:

// Set device as a Wi-Fi Station
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
  delay(1000);
  Serial.println("Setting as a Wi-Fi Station..");
}
Serial.print("Station IP Address: ");
Serial.println(WiFi.localIP());
Serial.print("Wi-Fi Channel: ");
Serial.println(WiFi.channel());

Initialize ESP-NOW.

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

Register for the OnDataRecv callback function, so that it is executed when a new ESP-NOW packet arrives.

esp_now_register_recv_cb(OnDataRecv);

Handle Requests

When you access the ESP32 IP address on the root / URL, send the text that is stored on the index_html variable to build the web page.

server.on("/", HTTP_GET, [](AsyncWebServerRequest *request){
  request->send_P(200, "text/html", index_html);
});

Server Event Source

Set up the event source on the server.

events.onConnect([](AsyncEventSourceClient *client){
  if(client->lastId()){
    Serial.printf("Client reconnected! Last message ID that it got is: %u\n", client->lastId());
  }
  // send event with message "hello!", id current millis
  // and set reconnect delay to 1 second
  client->send("hello!", NULL, millis(), 10000);
 );
  server.addHandler(&events);

Finally, start the server.

server.begin();

loop()

In the loop(), send a ping every 5 seconds. This is used to check on the client side, if the server is still running.

static unsigned long lastEventTime = millis();
static const unsigned long EVENT_INTERVAL_MS = 5000;
if ((millis() - lastEventTime) > EVENT_INTERVAL_MS) {
  events.send("ping",NULL,millis());
  lastEventTime = millis();
}

The following diagram summarizes how the Server-sent Events work on this project and how it updates the values without refreshing the web page.

ESP32 ESP-NOW Web Server Sensor Dashboard Project Overview

After uploading the code to the receiver board, press the on-board EN/RST button. The ESP32 IP address should be printed on the Serial Monitor as well as the Wi-Fi channel.

ESP-NOW get ESP32 IP address and Wi-Fi channel

ESP32 Sender Circuit

The ESP32 sender boards are connected to a DHT22 temperature and humidity sensor. The data pin is connected to GPIO 4. You can choose any other suitable GPIO (read ESP32 Pinout Guide). Follow the next schematic diagram to wire the circuit.

Wiring ESP32 to DHT22 temperature and humidity sensor

ESP32 Sender Code (ESP-NOW)

Each sender board will send a structure via ESP-NOW that contains the board ID (so that you can identify which board sent the readings), the temperature, the humidity, and the reading ID. The reading ID is an int number to know how many messages were sent.

ESP32 Sender Receiver Board with ESP-NOW using Arduino IDE

Upload the following code to each of your sender boards. Don’t forget to increment the id number for each sender board and insert your SSID in the WIFI_SSID variable.

/*
  Rui Santos
  Complete project details at https://RandomNerdTutorials.com/esp32-esp-now-wi-fi-web-server/
  
  Permission is hereby granted, free of charge, to any person obtaining a copy
  of this software and associated documentation files.
  
  The above copyright notice and this permission notice shall be included in all
  copies or substantial portions of the Software.
*/

#include <esp_now.h>
#include <esp_wifi.h>
#include <WiFi.h>
#include <Adafruit_Sensor.h>
#include <DHT.h>

// Set your Board ID (ESP32 Sender #1 = BOARD_ID 1, ESP32 Sender #2 = BOARD_ID 2, etc)
#define BOARD_ID 1

// Digital pin connected to the DHT sensor
#define DHTPIN 4  

// Uncomment the type of sensor in use:
//#define DHTTYPE    DHT11     // DHT 11
#define DHTTYPE    DHT22     // DHT 22 (AM2302)
//#define DHTTYPE    DHT21     // DHT 21 (AM2301)

DHT dht(DHTPIN, DHTTYPE);

//MAC Address of the receiver 
uint8_t broadcastAddress[] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};

//Structure example to send data
//Must match the receiver structure
typedef struct struct_message {
    int id;
    float temp;
    float hum;
    int readingId;
} struct_message;

//Create a struct_message called myData
struct_message myData;

unsigned long previousMillis = 0;   // Stores last time temperature was published
const long interval = 10000;        // Interval at which to publish sensor readings

unsigned int readingId = 0;

// Insert your SSID
constexpr char WIFI_SSID[] = "REPLACE_WITH_YOUR_SSID";

int32_t getWiFiChannel(const char *ssid) {
  if (int32_t n = WiFi.scanNetworks()) {
      for (uint8_t i=0; i<n; i++) {
          if (!strcmp(ssid, WiFi.SSID(i).c_str())) {
              return WiFi.channel(i);
          }
      }
  }
  return 0;
}

float readDHTTemperature() {
  // Sensor readings may also be up to 2 seconds 'old' (its a very slow sensor)
  // Read temperature as Celsius (the default)
  float t = dht.readTemperature();
  // Read temperature as Fahrenheit (isFahrenheit = true)
  //float t = dht.readTemperature(true);
  // Check if any reads failed and exit early (to try again).
  if (isnan(t)) {    
    Serial.println("Failed to read from DHT sensor!");
    return 0;
  }
  else {
    Serial.println(t);
    return t;
  }
}

float readDHTHumidity() {
  // Sensor readings may also be up to 2 seconds 'old' (its a very slow sensor)
  float h = dht.readHumidity();
  if (isnan(h)) {
    Serial.println("Failed to read from DHT sensor!");
    return 0;
  }
  else {
    Serial.println(h);
    return h;
  }
}

// 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");
  Serial.println(status == ESP_NOW_SEND_SUCCESS ? "Delivery Success" : "Delivery Fail");
}
 
void setup() {
  //Init Serial Monitor
  Serial.begin(115200);

  dht.begin();
 
  // Set device as a Wi-Fi Station and set channel
  WiFi.mode(WIFI_STA);

  int32_t channel = getWiFiChannel(WIFI_SSID);

  WiFi.printDiag(Serial); // Uncomment to verify channel number before
  esp_wifi_set_promiscuous(true);
  esp_wifi_set_channel(channel, WIFI_SECOND_CHAN_NONE);
  esp_wifi_set_promiscuous(false);
  WiFi.printDiag(Serial); // Uncomment to verify channel change after

  //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);
  
  //Register peer
  esp_now_peer_info_t peerInfo;
  memcpy(peerInfo.peer_addr, broadcastAddress, 6);
  peerInfo.encrypt = false;
  
  //Add peer        
  if (esp_now_add_peer(&peerInfo) != ESP_OK){
    Serial.println("Failed to add peer");
    return;
  }
}
 
void loop() {
  unsigned long currentMillis = millis();
  if (currentMillis - previousMillis >= interval) {
    // Save the last time a new reading was published
    previousMillis = currentMillis;
    //Set values to send
    myData.id = BOARD_ID;
    myData.temp = readDHTTemperature();
    myData.hum = readDHTHumidity();
    myData.readingId = readingId++;
     
    //Send message via ESP-NOW
    esp_err_t result = esp_now_send(broadcastAddress, (uint8_t *) &myData, sizeof(myData));
    if (result == ESP_OK) {
      Serial.println("Sent with success");
    }
    else {
      Serial.println("Error sending the data");
    }
  }
}

View raw code

How the Code Works

Start by importing the required libraries:

#include <esp_now.h>
#include <esp_wifi.h>
#include <WiFi.h>
#include <Adafruit_Sensor.h>
#include <DHT.h>

Set Board ID

Define the ESP32 sender board ID, for example set BOARD_ID 1 for ESP32 Sender #1, etc…

// Set your Board ID (ESP32 Sender #1 = BOARD_ID 1, ESP32 Sender #2 = BOARD_ID 2, etc)
#define BOARD_ID 1

DHT Sensor

Define the pin the DHT sensor is connected to. In our example, it is connected to GPIO 4.

#define DHTPIN 4

Select the DHT sensor type you’re using. We’re using the DHT22.

// Uncomment the type of sensor in use:
//#define DHTTYPE    DHT11     // DHT 11
#define DHTTYPE    DHT22     // DHT 22 (AM2302)
//#define DHTTYPE    DHT21     // DHT 21 (AM2301)

Create a DHT object on the pin and type defined earlier.

DHT dht(DHTPIN, DHTTYPE);

Receiver’s MAC Address

Insert the receiver’s MAC address on the next line (for example):

uint8_t broadcastAddress[] = {0x30, 0xAE, 0xA4, 0x15, 0xC7, 0xFC};

Data Structure

Then, create a structure that contains the data we want to send. The struct_message contains the board ID, temperature reading, humidity reading, and the reading ID.

typedef struct struct_message {
    int id;
    float temp;
    float hum;
    int readingId;
} struct_message;

Create a new variable of type struct_message that is called myData that stores the variables’ values.

struct_message myData;

Timer Interval

Create some auxiliary timer variables to publish the readings every 10 seconds. You can change the delay time on the interval variable.

unsigned long previousMillis = 0;  // Stores last time temperature was published
const long interval = 10000;       // Interval at which to publish sensor readings

Initialize the readingId variable – it keeps track of the number of readings sent.

unsigned int readingId = 0;

Changing Wi-Fi channel

Now, we’ll get the receiver’s Wi-Fi channel. This is useful because it allows us to automatically assign the same Wi-Fi channel to the sender board.

To do that, you must insert your SSID in the following line:

constexpr char WIFI_SSID[] = "REPLACE_WITH_YOUR_SSID";

Then, the getWiFiChannel() function scans for your network and gets its channel.

int32_t getWiFiChannel(const char *ssid) {
  if (int32_t n = WiFi.scanNetworks()) {
    for (uint8_t i=0; i<n; i++) {
      if (!strcmp(ssid, WiFi.SSID(i).c_str())) {
        return WiFi.channel(i);
      }
    }
  }
  return 0;
}

This snippet of code was proposed by Stephane (one of our readers). You can see his complete example here.

Reading Temperature

The readDHTTemperature() function reads and returns the temperature from the DHT sensor. In case it is not able to get temperature readings, it returns 0.

float readDHTTemperature() {
  // Sensor readings may also be up to 2 seconds 'old' (its a very slow sensor)
  // Read temperature as Celsius (the default)
  float t = dht.readTemperature();
  // Read temperature as Fahrenheit (isFahrenheit = true)
  //float t = dht.readTemperature(true);
  // Check if any reads failed and exit early (to try again).
  if (isnan(t)) {
    Serial.println("Failed to read from DHT sensor!");
    return 0;
  }
  else {
    Serial.println(t);
    return t;
  }
}

Reading Humidity

The readDHTHumidity() function reads and returns the humidity from the DHT sensor. In case it is not able to get humidity readings, it returns 0.

float readDHTHumidity() {
  // Sensor readings may also be up to 2 seconds 'old' (its a very slow sensor)
  float h = dht.readHumidity();
  if (isnan(h)) {
    Serial.println("Failed to read from DHT sensor!");
    return 0;
  }
  else {
    Serial.println(h);
    return h;
  }
}

Note: to learn more about getting temperature and humidity from the DHT22 or DHT11 sensors, read: ESP32 with DHT11/DHT22 Temperature and Humidity Sensor using Arduino IDE.

OnDataSent Callback Function

The OnDataSent() callback function will be executed when a message is sent. In this case, this function prints if the message was successfully delivered or not.

void OnDataSent(const uint8_t *mac_addr, esp_now_send_status_t status) {
  Serial.print("\r\nLast Packet Send Status:\t");
  Serial.println(status == ESP_NOW_SEND_SUCCESS ? "Delivery Success" : "Delivery Fail");
}

setup()

Initialize the Serial Monitor.

Serial.begin(115200);

Set the ESP32 as a Wi-Fi station.

WiFi.mode(WIFI_STA);

Set its channel to match the receiver’s Wi-Fi channel:

int32_t channel = getWiFiChannel(WIFI_SSID);

WiFi.printDiag(Serial); // Uncomment to verify channel number before
esp_wifi_set_promiscuous(true);
esp_wifi_set_channel(channel, WIFI_SECOND_CHAN_NONE);
esp_wifi_set_promiscuous(false);
WiFi.printDiag(Serial); // Uncomment to verify channel change after

Initialize ESP-NOW.

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

After successfully initializing ESP-NOW, register the callback function that will be called when a message is sent. In this case, register for the OnDataSent() function created previously.

esp_now_register_send_cb(OnDataSent);

Add peer

To send data to another board (the receiver), you need to pair it as a peer. The following lines register and add the receiver as a peer.

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

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

loop()

In the loop(), check if it is time to get and send new readings.

unsigned long currentMillis = millis();
if (currentMillis - previousMillis >= interval) {
  // Save the last time a new reading was published
  previousMillis = currentMillis;

Send ESP-NOW Message

Finally, send the message structure via ESP-NOW.

// Send message via ESP-NOW
esp_err_t result = esp_now_send(broadcastAddress, (uint8_t *) &myData, sizeof(myData));
if (result == ESP_OK) {
  Serial.println("Sent with success");
}
else {
  Serial.println("Error sending the data");
}

Recommended reading: Getting Started with ESP-NOW (ESP32 with Arduino IDE)

Upload the code to your sender boards. You should notice that the boards change their Wi-Fi channel to the channel of the receiver board. In our case, the boards changed their Wi-Fi channel number to 6.

Demonstration

After uploading the code to all the boards and if everything is going as expected, the ESP32 receiver board should start receiving sensor readings from the other boards.

ESP32 ESP-NOW Web Server Sensor Dashboard ESP-NOW and Wi-Fi Demonstration Sensor Readings

Open a browser on your local network and type the ESP32 IP address.

ESP32 ESP-NOW Web Server Sensor Dashboard Web Browser

It should load the temperature, humidity, and reading IDs for each board. Upon receiving a new packet, your web page updates automatically without refreshing the web page.

ESP32 ESP-NOW Web Server Sensor Dashboard Mobile Responsive

Wrapping Up

In this tutorial you’ve learned how to use ESP-NOW and Wi-Fi to set up a web server to receive ESP-NOW packets from multiple boards (many-to-one configuration).

Additionally, you also used Server-Sent Events to automatically update the web page every time a new packet is received without refreshing the web page.

We hope you like this project. Other projects/tutorials you may like:

Learn more about the ESP32 with our resources:

We would like to thank our readers that helped us improve this tutorial, namely Stéphane Calderoni and Lee Davidson for their valuable inputs. Thanks you.



Learn how to build a home automation system and we’ll cover the following main subjects: Node-RED, Node-RED Dashboard, Raspberry Pi, ESP32, ESP8266, MQTT, and InfluxDB database DOWNLOAD »
Learn how to build a home automation system and we’ll cover the following main subjects: Node-RED, Node-RED Dashboard, Raspberry Pi, ESP32, ESP8266, MQTT, and InfluxDB database DOWNLOAD »

Enjoyed this project? Stay updated by subscribing our newsletter!

234 thoughts on “ESP32: ESP-NOW Web Server Sensor Dashboard (ESP-NOW + Wi-Fi)”

  1. Hi Rui & Sara,
    Many thanks, RNT team, for another great tutorial, as usual!…
    It will be just wonderful if (as a great complement to this one) you could publish a follow-up tutorial describing the “other-way-around” dashboard CONTROL and REMOTE operation of an ESP-NOW network comprising a master and a multiple-slaved ESP32s (…perhaps maybe just by somehow “joining” and “tweaking” this one with another one of your most popular previous one: the “One-to-Many” ESP-NOW tutorial…??).
    Looking forward to hear from you soon!
    Kind regards from South Africa,
    Daniel.

    Reply
  2. Will this receive ESPNow from esp8266s?

    And how do you add more ‘senders’? I hope that when I can read this on a full size screen I find this last question to be a stupid one

    I am finding your post amazing as training aids, thank you

    Reply
    • Hi Clive.
      Yes, this can receive packets via ESP-NOW from ESP8266. You can see in this example a sender code for the ESP8266: https://randomnerdtutorials.com/esp-now-esp8266-nodemcu-arduino-ide/
      To add more senders, you just need to copy the sender code to the other boards you want to add, and then, prepare the receiver to receive more packets. You also need to change the HTML text to display more readings.
      It may help taking a look at this tutorial: https://randomnerdtutorials.com/esp-now-many-to-one-esp32/
      Regards,
      Sara

      Reply
      • Hello Sara,

        I have attempted to use an esp32 as a master – connected to a router for displaying the data on a web page, and an esp8266 as the slave device which gathers the data. The data exchange is via esp_now. The 32 master requests a data packet from the 8266 which triggers the 8266 to send the data to the master. The logic works perfectly until the esp32 master is connected to the router. After this connection the esp master can send it’s request to the 8266. When the 8266 receives the request, the transmission of the data fails. Have you a working example of an esp master connected to a router and 2 way communication with an 8266?

        With thanks for your great tutorials,
        Glen

        Reply
        • Hi Glen.
          This project worked fine for me using only ESP32 boards.
          I haven’t tried it with ESP8266 😐
          Regards,
          Sara

          Reply
          • Multiple ESP-01 Sending Data to on ESP32 Web Dashboard is a major application one should look into.

            Especially with ESP-MESH and ESP-NOW together a WebDasboard in HighCharts on Board (without WebServer)

        • I think the problem is with the channel. Once you connect to the router it messes things up. I got a ESP8266 sender and a ESP32 receiver. The sender logs temperature every hour and send it to the receiver, the receiver sends it over the internet to a server. What I ended up doing was:

          ESP32 receives data
          Save data on EEPROM
          Restart the ESP32
          Check if there is data to send
          Send it to the server
          Remove data and restart again

          Shitty solution but it worked

          Reply
  3. Rui,
    This gets more and more interesting all the time!
    I need to get more ESP32, ESP8266 and WeMos D1 minis to keep up with all the projects that I need to make.
    I completed and installed 2 room controllers that use ESP NOW, and they work awesome.
    I also used the same ESP NOW technique with 3 WeMos D1 Minis working with two way communications, that update from any location by using hard buttons. the updated status is then visible on SSD1306 displays as well.
    This is prompting me to make a mobile version, that I can carry with me to select audio sources when i am outside, or away from the wall controllers.
    I never dreamed I would be able to create useful controls with low cost, and ease of programming…thanks to yours and Sarah’s coding skills and lessons!
    Thank you!!

    Is the facebook page a place that projects can be shared?

    Reply
  4. Hello Rui and Sara,

    I follow offen your website and try to build some projects from your site.
    Now i see that you are using esp_now.h file, if i use that in my code it gives an error.
    Witch library contains esp_now.h file? hope you can help me, ik tried to understand it.
    i want to understand esp now beceause i want to make a project to control a servo with a joystick over wifi.
    I like your site!!
    Thanks so far.
    Regards Remon
    From the Netherlands

    Reply
    • Hi.
      That library is installed automatically when you install the ESP32 boards.
      Simply select an ESP32 board in the Boards menu before uploading or compiling the code.
      If you’re using an ESP8266, this code is not compatible. It uses the espnow.h library and uses slightly different methods.
      Regards,
      Sara

      Reply
  5. Hi Rui and Sara,
    Yet another excellent tutorial giving many techniques for my own projects.
    One question: How can I put an ESP32 into “Promiscuous” mode?

    IE the ESP32 version of the popular ESP8266 code ..

    wifi_set_opmode(STATION_MODE);
    wifi_promiscuous_enable(1);

    Many thanks
    David

    Reply
  6. Hi Sara and Rui,
    Another great project. Is there a simple way to log the collected data from the units in a CSV-file to be imported in Excel?

    Reply
  7. Hi Sara and Rui
    Is it possible to use ESP NOW as a mesh like features.? Crude way could be to insert all the esp MAC as receivers MAC in the program. Am I right ? Or can you suggest a solution?

    Reply
    • Hi.
      Yes, you can add more boards sending data to each other. I think you can have a maximum of 20 added peers.
      Regards,
      Sara

      Reply
      • Hi Sara,
        The number of paired is 14 max, I found this info here:
        demo-dijiudu.readthedocs.io/en/latest/api-reference/wifi/esp_now.html#add-paired-device
        ” The range of the channel of paired device is from 0 to 14″…
        Regards

        Reply
        • Hi.
          I think that refers to the number of wi-fi channels.
          Here we are referring to 20 ESP-NOW peers.
          See page 4: espressif.com/sites/default/files/documentation/esp-now_user_guide_en.pdf
          Regards,
          Sara

          Reply
          • Hi Sara,

            “you can add more boards sending data to each other. You can have a maximum of 20 added peers”

            Please let me know which statement below is correct base on the statement above:

            One esp32 can send data to maximum of 20 esp32.
            One esp32 can receive data from maximum of 20 esp32.
            One eps32 can send and receive data to maximum of 20 esp32.
            One eps32 can send only to maximum 20 esp32 but can receive from limitless number of esp32.

          • Hi.
            I’m not sure. The sentence on the documentation can be interpreted in different ways.
            But I think the last statement makes more sense.
            Regards,
            Sara

  8. Great project, to change the DTH22 out for a DS18B20 would you just change all reference to DTH to DS18B20 and include DS18B20 library. I am a 70yr old Newbie, but really facinates me. have the project working in its present form.

    Reply
  9. Hola Rui & Sara !

    Thanks again for this tutorial. I really appreciate following-up your posts!

    Rui wrote: “We’re not sure if there’s a better way to use ESP-NOW and Wi-Fi simultaneously. This solution worked reliably for us.”

    Your example shows Access Point Connexion as mandatory in order to create a connexion between the sender (Slave) and the receiver (Master). This reduces the communication range between the two boards (distance).

    I guess the Master should be an Access Point to communicate with another ESP32 as a Web server in order to maximize the ESP-Now functionality. Otherwise I dont’ see why using ESP-NOW data exchange with the master (i.e through Access point). Wi-Fi should be just fine.

    My new project under progress is: ESP32-Sender (slave) to a ESP32-Receiver (master) using ESP-Now (only) for communicating, and having more than 150 ft of distance range between the two boards. The ESP32 Master is than communicating to another ESP32 which is used a gateway. The connexion between the ESP32 Master and the ESP32 Gateway will be either through Serial or Wi-Fi. The ESP32 gateway is then communicating using MQTT to the a Web Server.

    What do you think?

    Merci,

    Reply
    • Hi.
      That’s a great project and I think you can easily have a distance of more than 150 ft.
      Good luck with your projects 😀
      Regards,
      Sara

      Reply
    • this is almost exactly what I am working towards- have you got Serial (hardware Serial1?) to work for communication to the gateway yet?

      Reply
    • Hi, great job again, thanks RNT Team!
      I like this solution, because the peer to peer communication between the ESP is not “sniffable” on the WIFI and is a more secured way to transmit data: you will not sniff them easily as UDP and TCP packets.
      Now if you need to access occasionnaly to your webserver, WIFI current consommation is greater than ESPNow current consommation, so with few data sent by other boards, you will get a longer battery life-time than using WIFI and MQTT.
      By sending data every 10 or more minutes, and using DeepSleep, it looks more interresting than WIFI, don’t you think so?
      So ESP-Now looks safer and more interesting for battery life-time.
      If you need long range, use Lora or change your antennas.
      I’m going to try this solution for my home hand-made alarm with multiple zones.
      Is MQTT usable with ESPNow?
      Regards
      Sébastien

      Reply
      • Hi.
        Yes. You can use MQTT and ESPNow at the same time.
        However, keep in mind that you need to connect to Wi-Fi to use MQTT to connect to your broker.
        Regards,
        Sara

        Reply
        • Hi Sara

          This was an awsome post , as you said I would like to get date through ESP-NOW and then publish with MQTT, but I can’t :C. Any suggestion for the configuration?

          Reply
  10. Thank you for the tutorial! Great write up as always. However, It seems this has the same problem as my previous code. I am trying to have the ‘master’ be able to send and receive ESPNow data as well as connect to wifi to send data. It seems if you start wifi, once you register a peer it’ll kill ESPNow.

    It seems like its either or… Wifi AP OR ESPNow sending, but not both. Let me know what you think.

    Reply
    • Hi Tom.
      This project works fine for us.
      We don’t have that problem.
      However, I haven’t tried sending data from the main ESP32 that is acting as a web server.
      I plan to try that out in the future.
      Regards,
      Sara

      Reply
      • Thank you Sara. Do you know a way I may be able to trigger a ‘slave’ esp32 over serial to start an function, instead of needing the ‘master’ to trigger it over ESPNow? This would solve my issue of needing both ESPNow sending AND WiFi.

        Reply
    • Hi Tom,

      You’re right. That was my point. The example uses Access Point and ESP-now at the same for the sender to the receiver. This is not appropritate.
      Senders should just use ESP-Now only. Fore the receiver (Master), I use it as a gateway. I connect this one using a serial connexion to another ESP32. This last one is then an Access Point to the Web network .
      For the serial connexion :
      #define RXD2 16 // should connect to Tx of ESP32-Now Receiver (Master)
      #define TXD2 17 // should connect to Rx of ESP32-Now Receiver (Master)

      In Setup ()
      Serial2.begin(9600, SERIAL_8N1, RXD2, TXD2);

      So when the ESP32-Now Receiver (Master) output
      Serial.printf(“Board ID %u: %u bytes\n”, incomingReadings.id, len);
      Serial.printf(“t value: %4.2f \n”, incomingReadings.temp);
      Serial.printf(“h value: %4.2f \n”, incomingReadings.hum);
      Serial.printf(“readingID value: %d \n”, incomingReadings.readingId);
      Serial.println();

      then the ESP-32 (Gateway) receives the info (using serial2) and could sent it to the Web using Wi-Fi connexion.

      In other words, only the last ESP32 (Gateway) should be connected using Wi-Fi, but all the others should just use ESP-Now.

      Hope this can help

      Reply
      • Hi.
        Thank you for sharing your solution.
        It is a great idea. Can you share your code with us? We would like to write a tutorial about that in the future, if you don’t mind us sharing.
        Contact us using our contact form: https://randomnerdtutorials.com/support/
        You can write “ESP-NOW Wi-Fi Serial” on the subject.
        Regards,
        Sara

        Reply
        • Here’s the code for the Serial transfer between the last ESP32 Gateway and the ESP32 Master which receives data from esp32 sender through ESP-Now protocol. Bt the way , my tests do not conclude ESP-Now as a very solid communication protocol. Very unstable and not very performant. I’m am abandonning this approach… I guess LoRaWAN will be evaluate as a better protocol. To follow…

          /*
          * There are three serial ports on the ESP known as U0UXD, U1UXD and U2UXD.
          *
          * U0UXD is used to communicate with the ESP32 for programming and during reset/boot.
          * U1UXD is unused and can be used for your projects. Some boards use this port for SPI Flash access though
          * U2UXD is unused and can be used for your projects.
          *
          * This programm extract info from ESP32 Serial connexion
          * Message info received is under the following structure :
          * Temp_Ext:11.30 Humi_Ext:3277.30 Soil_Jar:23.34 Watr_Lev:12.18 #
          *
          * each value is corresping to a different sensor sensor input (ex. DHT11, Soil Moisture, Water Level(HC-SR04) ,…)
          * ref. to void extractDisplay_transf() function below
          */

          #define DEBUG 1 //after you no longer need the debug code, then comment out this #define DEBUG 1 line
          #define VERSION 2.5

          #define RXD2 16 // should connect to Tx of ESp32 d1 (TX) ESP32-Now Master
          #define TXD2 17 // should connect to Rx of Esp32 d3 (RX) ESP32-Now Master

          String message_esp32;

          int Board_Id; // Sender ESP32-Now Board ID
          int incomingMessage_Id = 0; // Message Id

          float Temp_Ext = 0.8; // Exterior Temperature (DHT sensor) 0.8 value is a trace-point for validation
          float Humi_Ext = 0.8; // Exterior Humidity (DHT sensor)
          float Moisture = 0.8; // Soil Moisture value (garden sensor)
          float Water_Level = 0.8; // Water Level value (conteiner of water HC-SR04 sensor)

          void setup() {
          // Note the format for setting a serial port is as follows: Serial2.begin(baud-rate, protocol, RX pin, TX pin);
          Serial.begin(115200);
          //Serial1.begin(9600, SERIAL_8N1, RXD2, TXD2);
          Serial2.begin(9600, SERIAL_8N1, RXD2, TXD2); // Serial2 is not Rx, Tx of the usual Serial pin, but from GPIO 16 and 17
          Serial.println(“Serial Txd is on pin: “+String(TXD2));
          Serial.println(“Serial Rxd is on pin: “+String(RXD2));
          }

          void loop() { //Choose Serial1 or Serial2 as required
          while (Serial2.available()) {
          int result;
          char sub[100];

          delay(10);
          message_esp32 = Serial2.readString() ; // read the message as it comes (lapse_temps du Master)
          // message type ex: Temp_Ext:11.30 Humi_Ext:3277.30 Soil_Jar:0.00 Watr_Lev:0.00 #
          extractDisplay_transf(message_esp32);

          #ifdef DEBUG
          Serial.println(message_esp32);
          Serial.println(“Final Results”);
          Serial.println(Board_Id);
          Serial.println(Temp_Ext);
          Serial.println(Humi_Ext);
          Serial.println(Moisture);
          Serial.println(Water_Level);
          #endif

          }
          }

          // Extractinf value from a message like : Temp_Ext:11.30 Humi_Ext:3277.30 Soil_Jar:0.00 Watr_Lev:0.00 #
          //
          void extractDisplay_transf(String string_tmp){
          String str_val1; // Board_Id
          String str_val2; // Temp_Ext (temperature ext. )
          String str_val3; // Humi_Ext (humudity ext. )
          String str_val4; // Soil_Jar (soil moisture jardin/garden)
          String str_val5; // Watr_Lev (Water Level)

          int firstValue_pos = string_tmp.indexOf(‘:’);
          int secondValue_pos = string_tmp.indexOf(‘:’, firstValue_pos + 1 );
          int thirdValue_pos = string_tmp.indexOf(‘:’, secondValue_pos + 1);
          int fourthValue_pos = string_tmp.indexOf(‘:’, thirdValue_pos + 1);
          int fifthValue_pos = string_tmp.indexOf(‘:’, fourthValue_pos + 1);
          int secondMessage_pos = string_tmp.indexOf(‘ ‘);
          int thirdMessage_pos = string_tmp.indexOf(‘ ‘, secondValue_pos + 1);
          int fourthMessage_pos = string_tmp.indexOf(‘ ‘, thirdValue_pos + 1);
          int fifthMessage_pos = string_tmp.indexOf(‘ ‘, fourthValue_pos + 1);
          int endMessage_pos = string_tmp.lastIndexOf(‘#’); // symbol for the ending message

          str_val1 = string_tmp.substring(firstValue_pos + 1, secondMessage_pos);
          Board_Id = str_val1.toFloat();

          str_val2 = string_tmp.substring(secondValue_pos + 1, thirdMessage_pos);
          Temp_Ext = str_val2.toFloat();

          str_val3 = string_tmp.substring(thirdValue_pos + 1, fourthMessage_pos);
          Humi_Ext = str_val3.toFloat();

          str_val4 = string_tmp.substring(fourthValue_pos + 1, fifthMessage_pos);
          Moisture = str_val4.toFloat();

          str_val5 = string_tmp.substring(fifthValue_pos + 1, endMessage_pos);
          Water_Level = str_val5.toFloat();
          }

          Reply
      • Yup, serial connection was the key. My layout is a bit different but so far its working…

        I now have my ‘master’ signalling the first slave to GO over serial after it receives a trigger JSON from WiFi. Once slave1 is done is sends its packet over ESPNow to slave2 which triggers and does the same. slave3, once complete with its game, then sends all three slave responses back to the master over ESPNow. The master then authenticates with oAuth and sends the complete data over JSON (and WiFi)

        Master is currently using Wifi, ESPNow (receive ONLY), and serial
        slave1 is serial and ESPNow (send and receive)
        slave 2\3 utilizing just ESPNow (send and receive)

        Thanks for the push in the right direction Francois!

        Reply
  11. Hi,
    I wonder – could you give me a pointer to how I get more boxes (cards) in the grid rows (I really need the grid to be a 5 rows x 3 columns) as I just can’t seem to get it. Also I am struggling with changing the justification within a box.
    Great project
    Dave

    Reply
  12. Hello, have you considered using some framework like Mustache ( https://mustache.github.io/ ) to dinamically generate the dashboard on the web server?

    I am not sure whether it may work nicely (if you keep all JSON separated, probably it will only display the data from the last JSON received), or if this may create more headaches in manading the data flow…but it could help to design a dashboard where the number of sender is not necessarily known in advance, or make it seamless to add one more sender board.

    I’m currently using Mustache on a different type of project (web based), and I found it quite handy…I’ll try to see how it works next time I’ll be on a ESP32 based project with a web view!

    Reply
  13. Hi,
    perhaps you might be able to help – In the temperature box, how might I have the title centred (as is) and have the data and labels left/right aligned?
    I’ve been banging my head against the wall for a day now and can’t get it right.
    Thanks

    Reply
  14. Really good tutorials! thanks! Sort of side question, but how can you have separate files for the webserver and the sensor’s in the same project? It seems crazy to try to have multiple projects for each of the code basis, and I’m working to strip out most of the webserver const code into an include file. I’ve never really built anything other than in the main.cpp, so now I’m suddenly wondering could you have multiple object files that “make” or compile to different uploads?

    Reply
      • Thanks! I actually figured out how to have the const char* const for the web page in a .h, but I’m going to look at the CSS/SPIFFS version you posted too!

        But…what I’m really curious about, is how to have multiple compiled objects/code for different ESP32’s all in one project. I mean, I would like to have sender.cpp and receiver.cpp and compile them separate/upload to different esp32’s, but still be in the same project. I was hoping it would compile each target based on what was active and upload that, but I’m guessing that isn’t really possible (from what I read, the Arduino IDE links all .cpp files to main.cpp, or tries to). Even in VSC/PlatformIO, it doesn’t allow me to use separately.

        Thanks!
        Jeff

        Reply
  15. I found this to be one of the most educational experiences I have had from RNT. I suggest that once people get the code running as given to try adding another measurement; in my case I needed a battery monitor. That really got me into the code as I had to make changes to the sender and receiver, use a little JSON, html, CSS, and trial and error; but in the end I was really happy to get it working. What a blast! Thank you!

    Reply
  16. This is really a wonderful project.
    Please guide me with a link or pointer to have Two Way communication in my Esp-Now web server to control a relay in one node, based on sensor reading on the other node

    Reply
  17. would it be possible to use ESP-Now to connect to a remote esp32 hooked up to an 8 channel relay switch but control it from the master(two way comm)? also also a sensor hut that would send data(bme280, ir data, etc), one way to the same master?

    Reply
  18. I see in the example that each ‘Sender’ is using similar sensors as inputs so the same message structure is acceptable as follows:

    typedef struct struct_message {
    int id;
    float temp;
    float hum;
    int readingId;
    } struct_message;

    Question is: what if one of the ‘Senders’ uses entirely different sensor mix, can this new Sender have it’s own message structure format with different mix of value types e.g. float temp1 and also a float temp2.
    If this was the case would it be simply that in the ‘Receiver Code’ we would need to define both message structures ?

    Reply
    • I suspect that it may be OK to have different message structure in each sender as long as both are defined in the common receiver. BUT as an alternative I just have one comprehensive message structure in all devices and this works OK. See my struct message used below.

      In addition I have one of my Senders also sending values out to Bluetooth so I can monitor temperatures on my smartphone or other BT device.

      typedef struct struct_message { // all int store a 2-byte value.
      int id;
      float A11; // 1st A/I from Sender # 1 for BMP280 ‘C’ SupplyAir
      float A12; // 2nd A/I from Sender # 1 for BMP280 ‘S/A Air Pressure’
      float A13; // 3rd A/I from Sender # 1 for DS18B20 #1 RtnAir
      float A14; // 4th A/I from Sender # 1 for DS18B20 #2 Supply Air
      int D11; // 1st D/I from Sender # 1 for Spare D/I #1
      int D12; // 2nd D/I from Sender # 1 for Spare D/I #2
      float A21; // 1st A/I from Sender # 2 for DHT22 ‘C’
      float A22; // 2nd A/I from Sender # 2 for DHT22 ‘RH’
      float A23; // 3rd A/I from Sender # 2 for LDR
      float A24; // 4th A/I from Sender # 2 for Spare A/I – maybe Batt Volts
      float A25; // 5th A/I from Sender # 2 for Spare A/I – maybe Batt Amps
      int D21; // 1st D/I from Sender # 2 for Spare D/I #1 – maybe gate status
      int D22; // 2nd D/I from Sender # 2 for Spare D/I #2
      } struct_message;

      Reply
  19. Please disregard my last message, I figured it out – the router was on a 5GHz channel instead of a 2.4GHz one. fixed!

    Reply
  20. Hey everybody,

    I don’t want to sound like a killjoy, but in my opinion the implementation of the ESP-NOW protocol needs to be fully reviewed in this tutorial. Indeed, ESP-NOW is a connectionless protocol! And that’s not at all what is implemented here. I myself experimented a couple of things and finally found a solution. But it doesn’t satisfy me. If you have any ideas on the subject, you might be able to come and contribute to the discussion I’ve started on this subject:

    https://rntlab.com/question/esp-now-gateway-wifi_mode_sta-with-a-wifi-router/

    I think François has taken note of the problem and his idea to isolate the ESP-NOW network by linking the receiver to another ESP32 (by serial link) which is connected to the Internet (as a station on a WiFi router), is an excellent idea.

    Nevertheless this solution allows to bypass the problem but not to solve it 😉

    Reply
    • Steph,

      At https://rntlab.com/question/esp-now-gateway-wifi_mode_sta-with-a-wifi-router/, you said, “I’ve done a lot of research myself as well… and I’ve come across these proposals as well. Unfortunately, none of them work with the current version of the arduino-esp32 core.” I’m not a member of RNT Lab, so I cannot contribute over there.

      It is not impossible to set a channel other than channel 1 on ESP-NOW senders. After some experimentation, I’ve found that the concepts presented in the example linked to by Steven just prior to your post do indeed work. You just need to #include <esp_wifi.h>. Is that not part of the arduino-esp32 core?

      The project presented in this post can be easily converted to use purely ESP-NOW between the sender and receiver with just a few simple changes.

      Let’s start with the receiver code. (Note: Line numbers are current line numbers before any edits.)

      Lines 21-26 and 157-158 are not needed. There is no need to configure and start an Access Point.
      Line 144 IS needed as is. Though we do not need a ‘hot’ AP, we do need to enable the AP interface else communication will be unreliable.

      Done there. Let’s go to the sender code. (Again, current line numbers.)

      #include <esp_wifi.h>
      Lines 17-19 are not needed. There is no reason to connect to an AP that isn’t there.
      At line 38, set the channel number (CHAN_AP) to the SAME channel number as the receiver’s WiFi channel number. It must be the same else communication will be unreliable.
      Since we are not connecting to an AP, lines 99-105 are also not needed. But we need to do something else here. So, replace those lines with:
      // Set device as a Wi-Fi Station & set channel
      WiFi.mode(WIFI_STA);
      //WiFi.printDiag(Serial); // Uncomment to verify channel number before
      esp_wifi_set_promiscuous(true);
      esp_wifi_set_channel(CHAN_AP, WIFI_SECOND_CHAN_NONE);
      esp_wifi_set_promiscuous(false);
      //WiFi.printDiag(Serial); // Uncomment to verify channel change after

      Unless I’ve overlooked some of the edits I did to make it work, that should be it.

      Keep in mind that if you set the peer_addr (or in this case broadcastAddress at line 35) to FF:FF:FF:FF:FF:FF, then any device on the channel will receive it. If you wish to specifically target the receiver, you should set it to the WiFi MAC address of the receiver. You probably already know, but others may not, that you can get the address of the receiver by adding “Serial.println(WiFi.macAddress());” to the receiver code after setting the WiFi mode.

      Reply
      • Hey Lee,

        Sorry for my previous answer, which I accidentally took out of the thread.

        I was finally able to test the solution proposed on the Espressif forum (which you mentioned in your comment)… and it works!!!

        I was sure I had tested it, but it probably escaped me while I was trying to change my code all the time.

        Anyway, thanks a lot to you and Steve for bringing this solution to my attention.

        I’ve posted a complete sample code on GitHub so that it can be used by the whole RNT community. And I encourage Sara and Rui to quickly modify their tutorial to propose a correct implementation of the ESP-NOW protocol.

        Thanks again!

        Reply
  21. Thank you Lee for your input.

    I don’t rely on the code in this tutorial. I developed my own project.
    However, I’ll know what you’re referring to with your line numbers.

    Nevertheless… the one and only thing that I think is important here (and that I thought I had tested without success on my side) is the use of the following directive:

    esp_wifi_set_promiscuous(flag); // true / false

    I will try again as soon as possible (I need to finish another project first).
    And I will come back to you to let you know the result.

    Thank you for taking the time to study the problem.
    Best regards.
    Steph

    Reply
  22. The statement
    server.on(“/”, HTTP_GET, [](AsyncWebServerRequest *request)
    {
    request->send_P(200, “text/html”, index_html);
    });
    sends fixed “const char index_html” content to the client when a request is received. How could I modify the statement to call a function that would compose a dynamic response, i.e., containing a series of
    server.sendContent(“text “);
    server.sendContent(value);
    statements or something similar?

    Reply
  23. Hello, thank you so much for the project. The receiver code works perfectly fine. But I am getting an error in the sender code in this line “peerInfo.channel = CHAN_AP;”

    ‘CHAN_AP’ was not declared in this scope.

    Thank you

    Reply
  24. Hi Sara and Rui,

    Are you planning to add a tutorial on showing how to use encryption when sending/receiving messages?
    Would like to add an additional level of security on top of my application, as such, message encryption solution that ESP-NOW provides would be be perfect.

    Thanks!

    Reply
      • Hello Sara,

        i’m also interested in such a tutorial.

        I have written some short code and it seems to work, but i’m not really sure. If you are interested i could send you a copy for review and basis for a new tutorial.

        It’s heavily based on your ESP-NOW getting started guide.

        I’m looking forward for your answer!

        Best Regards
        Maik

        Reply
  25. Hi guys,

    Thanks for another useful tutorial. However I’m a bit confused with this one.
    Should the senders boards also have access to the same WiFi network as the receiver one? I had the impression that we could run ESP NOW without WiFi access, but it seems to me that in this tutorial, the senders are also checking for the same SSID as the receiver board. Will this work if the sender board is outside the reach of the router?

    Reply
    • Hi Nuno,

      ESP32s can communicate without any connection thanks to the ESP-NOW protocol, and no WiFi network is required for this. In this tutorial ESP32s actually communicate with the ESP-NOW connectionless protocol.

      Nevertheless, here the ESP32 that receives the measured data from the sensors also exposes a web server to display this data and make it remotely accessible from a simple smartphone or PC. It is precisely for this reason that the receiving node is connected to a WiFi router, to share this data with the entire local network. In this particular context, in order for ESP-NOW communications to continue working, it is imperative that the channel used is the same as the one imposed by the WiFi router to which the receiving ESP32 is connected. Indeed, it must be able to simultaneously ensure ESP-NOW communications and TCP/IP communications exchanged with the clients of the local network. Its communication radio interface must therefore be configured to use the same frequency channel for this purpose.

      As such, the sender nodes must necessarily use the same channel. This is one of the difficulties that must be taken into account and which is explained here. For this reason, they scan WiFi networks within range to identify the one to which the receiving node is connected and at the same time determine the channel they need to use to communicate with it.

      I hope I have been able to clarify things for you.
      Regards,
      Steph

      Reply
      • Hi Steph,

        Thanks for your thorough reply, I think it addressed all my doubts. I guess my thought process was: if ESP-NOW works without WiFi, why should the multiple sender boards be also connected to the same WiFi network as the receiver, but it is clear now.
        I’m raising these questions due to a project I have in hands, but we can use this one as example. I want to have an ESP32 doing some sensor readings and feeding them to a MQQT broker. However, the ESP + sensor is to be deployed in a location in my house that isn’t covered by WiFi (outside router range). That’s why I was thinking of using two ESP32 boards, one next to the router, handling the MQQT communications and receiving sensor data by ESP-NOW from the other board. But I guess that won’t work, from what I got from your reply, right? If it can’t sender board can’t see the same network, then it won’t be able to handle ESP-NOW since it won’t be able to find the same channel. Right?

        If I may ask, do you think in this scenario, communications handled by LoRa would be better? I don’t have line of sight between router and location where the sender ESP32 is to be deployed, but it should work better than regular WiFi/ESP-NOW for indoor operation, no?

        Thanks again Steph,
        Cheers,
        Nuno

        Reply
        • Hi Nuno,

          For your project, the receiving node must of course be within range of the WiFi network broadcast by the router… but not necessarily placed right next to it. This allows you to extend the range of the sensor network. Indeed, these sensors can then be located out of range of the WiFi router, as long as they remain within range of the ESP32 which will receive the measurement data.

          The easiest way is to set the communication channel of the router (for example by configuring it to use channel 1) and make sure it remains constant. In this case, it is sufficient that all the ESP32s use channel 1 and the problem is solved. You can of course choose the channel that suits you best…

          In the event that you can’t set the router’s channel, because your neighbors generate too much interference, and you prefer your router to choose the channel that seems the least congested automatically, then you can consider the following methodology:

          You initialize all the nodes of your sensor network on a common channel (e.g. channel 1): the ESP32 senders (which broadcast the data from their sensors) and the ESP32 receiver, in charge of collecting the measurement data. The latter will be considered as the gateway between the ESP-NOW network and the WiFi network.
          The ESP32 gateway scans the WiFi networks within range and identifies the WiFi network in your house with its SSID, and at the same time determines the channel imposed by the router… but without connecting!
          Then it communicates the channel number via ESP-NOW to all the ESP32 connected to the different sensors. This is quite possible since they are all synchronized on channel 1.
          All the sensor nodes (ESP32s) reset their radio interface configuration to switch to the new channel.
          The ESP32 gateway connects to the WiFi network of the router (It will automatically switch to the same channel as the router).

          There you go! Everyone is now on the same channel as the router.

          You can also use LoRa interfaces, that will work too. But that gives you extra equipment to add to each ESP32. The advantage here is the very important increase of the communication range. LoRa technology will allow you, in a crowded environment (with many obstacles), to make your nodes communicate at several hundred meters away. Whereas ESP-NOW will limit you to a few dozen meters.

          Was I clear enough?
          All the best,
          Steph

          Reply
      • The only issue I have is that when the sender board after the initial setup are moved beyond the reach of the router and I have to reset the slave for any reason which highly plausible during power loss or whatever, then the sender wifi chanel scanning snippet will not work so I have to bring all boards again within the proximity of the router and repeat the set up process.

        I will have to experement with the slave board connected to another board via hardwired seial communication to obtain more reliable set up

        But I have to say that I learned quite a bit from following this tutorial and discussion

        Philip

        Reply
        • Hi Philippe,

          I have experimented with a much simpler way of doing this, which does not require additional controllers, nor does it require bringing all the sender modules closer to the router in order to scan the available WiFi networks again. I intend to publish my code as soon as time allows me to comment it a bit, so if you are interested, I can give you the link here. I guess it will be of interest to other readers who have encountered the same problem.

          Steph

          Reply
  26. Hi Sara

    Thank you. I will look into it.
    Also, do you have any tutorial that demonstrates how to transfer sd card data to a computer using ESP32?

    Reply
  27. Hi Sara.
    I have been going through examples with one ESP32 doing a NOW with another. In the Struct using floats I keep getting a 0 or garbage numbers. I have to change from float to a uint32_t to get any information thru. I did the examples that you provided with the master/slave example using a BME280 sensor with no luck. I also found that if I put an int value in the struct as the ID then followed by the floats I just get the ID and nothing of the other values.

    Do you have any idea as to what my issue could be? I seem to have had this issue using the NLF24 radios. Something with struct?

    thanks for any help on this.
    Love the stuff you guys do.

    Reply
  28. Any idea how I could swap the webserver on the receiver for a httpclient call ? I want to push the value to an external webserver. I have tried many things, but having the httpclient call in the datareceive function is not working. If I set a flag try the httpclient call in the loop, the board core dumps. Anything you might suggest would be great.

    Reply
    • Hi,

      It’s not possible to make an httpClient call using the wifi object in the callback function for esp-now received data. It gives errors on dns or connect operations, probably because the radio is busy/locked at that moment.

      You can post the data to a Queue (https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/system/freertos.html?highlight=queue#queue-api) and then check periodically from the main loop if there are pending messages on the queue and then send them using an HtttpClient.

      Or a more simpler approach using your flag and global static variables. This should work, your coredump is probably because you try to access memory allocated for the callback that is not there after it finishes. If you copy the data, you should have no problem sending it later.

      Reply
  29. Good day and Season’s greetings, Rui & Sara!!…
    One quick enquiry: which ones ESP-NOW functions one might need to add to the “master” device of this great tutorial, in order for the ESP-NOW ESP32s “slaves” devices to gain output control from the Dashboard?…that enhancement will give the whole project a fantastic “both-ways” functionality!!…
    Thanks again for your continuous efforts and sharing your knowledge with this ever expanding group of ESP32 fans, worldwide!!

    Reply
    • Hi.
      In that case, first you need to add some controls to the web server, like buttons, for example.
      When you click the buttons, the ESP32 should receive a request by the client, so that it knows the button was clicked.
      When that happens, it should send a message to the right board with the state of the GPIO.
      The board should interpret that message and control the GPIOs accordingly.
      That is feasible, but I didn’t had the time to develop that yey.
      I hope this helps.
      Regards,
      Sara

      Reply
      • Thanks for your prompt response to my enquiry, Sara!…I’ll take your hints and I will try to implement the cellphone Dashboard ‘remote-control’ WiFi/ESP-NOW control functionality I’m after. Hope that you and Rui will have some time to publish a tutorial in the near future complementing this great Dashboard remote monitoring one!…All the best wishes for 2021 for all the dedicated members of the RNT team!

        Reply
  30. I’m not able to get it working and really fail to see where things go wrong. I have a kind if mixed setup, 2 esp01’s sending dht data to a ttgo esp32. Works fine using the ‘standard’ many to one approach, both esp01s can communicate with the esp32 and the results are written on the ttgo screen. So basic setup is ok.

    Yet, whenever I add the script that should deal with the channel part the communication is lost between all. I assume part of the reason might be my main router is a meshrouter, so could it be the script works fine as long as you have just one router / network available but get’s confused when there’s plenty of choice?

    In the original setup you clearly stated the esp-now part had to use a different channel, yet now it seems to force the same channel, is this correct or should I be using different channels on both?

    Reply
  31. Is there a way how to avoid WiFi.scanNetworks()? I want to make the sender run on battery, but WiFi.scanNetworks() takes 2 seconds to finish, which is too much when the rest of the code takes about 120 milliseconds. I don’t mind setting everything static but was unsuccessful to make it work.

    Reply
  32. I build this project with a ESP8266 as Sender and a ESP32 as Receiver, just from your sources. But it won’t work. I can open the website, but there is no information from the Sender. When I look to the Senders output it says: Last Packet Send Status: Delivery fail. The channels are the same. Do you have any idea what goes wrong?

    Reply
    • Hi.
      It is very difficult to troubleshoot without further information.
      Just check the receiver’s MAC address.
      Also try using the FF broadcasting MAC address and see if that works.
      Regards,
      Sara

      Reply
  33. Hello Sara,
    thanks for the reply. I am sorry I was’n clear enough. I build the ESP_NOW and WiFi project from your examples on the site. Only with a ESP8266 as Sender and a ESP32 as receiver. Exactly as you describes. The only changes were of course my network credentials and the MAC address of the receiver.
    But anyway, later I found your discussion with Michel Tremblay on the LAB site. He had the same problem. With this information I solved my problem by only changed the MAC address of the receiver with his soft MAC address. No changes in the ESP_now_peer( etc) function were necessary. So it was not necessary to use the channel in this function.
    It now works fine. But I still don’t know what a FF broadcasting MAC address is. Could not find a clear explanation on the internet. I even did not know there was something like a soft MAC address for a device.
    Finally, I learned a lot from your website explanations; thank you very much!
    Regards, Bernard

    Reply
  34. Thank you so much for all the information.
    I am a newbie (-:
    I would love if you can upload a code of example of how to upload the reading of the sensor to thingspeak.
    Thanks a Lot
    Avner

    Reply
  35. Hello Sara,
    Which is the best way to proceed? I have completed the tutorial on ESP-NOW ESP32 2-way communication and got my project working to the point that I could split mode code to work as required. I have also done the tutorial on ESP32 BLE and created an MIT App to communicate using serial. So far so good! I also started to do this project but blew up a module and waiting for a replacement.
    Image link – (https://drive.google.com/file/d/1NWuy5ZtgthAQKFJZXCMCQMP_JOrDTrgr/view?usp=sharing)

    Ideally, I would like to control things using an App rather than the IP address as in the ESP-NOW Server example, though that does look good. I would also like the project to be completely independent, so that 10 users next to each other don’t get interference.
    There are so many protocols on the ESP32 that it’s driving me mad, any advice would be extremely appreciated.

    Reply
  36. Hello Sara,
    First of all, congrats for all these work. Your articles are from now on my reference manual for using ESP-NOW on my Wemos devices. One doubt I have is that I don´t find the article where ESP8266 (not ESP32) is working with ESP-NOW and Wi-Fi simultaneously. Is it that because there is no way to put them working together in the ESP8266? I hope the answer to this question is that you are working on it…
    Regards,
    Ander.

    Reply
  37. Hi Sara, Rui,
    Greetings from Australia 🙂

    I am an avid, long time reader of your work at RNT and have learnt a great deal about this magnificent esp32 MCU; Thank you so much!

    This TERRIFIC project has given me pause to post my first question. I apologise in advance for the lengthy post.

    BACKGROUND:
    Based broadly on RNT work, I have successfully built a project based on 2 x esp32-devkit-d1 30 pin boards for an ESP-NOW + BME280 project. My LiPo/solar-charger based sender is mounted in an outdoor plastic, 55cm wingspan ‘flying hawk’ which has short ws2812b strips on it’s wings and a BM280 mounted on its’ undercarriage. Though this 30-pin board in the sender will probably swap out for a D1 Mini.

    My indoor receiver currently has a basic ssd-1306 display… as does the sender while on the development bench .
    — I used Rui’s little “ESP32 Solar Powered Circuit” with the TP4056 to which I added a tiny step-up booster to get 3.3v up to static 5v for the LED strips.
    — a later SW version may even vary the WS2812 palette triggered by current temperature.

    It is this last point that finally gets me to ask my question regarding OTA for occasional SW updates to the sender.

    BTW, though I have on another project, I am not wanting to use WebServer pages for this. That’s saying something for a long-retired ex pro IT / WebDev guy ! ;).

    I have read broadly on this (ESP-NOW + WiFi) issue and see mixed messages regarding ESP-NOW and WiFi being mutually exclusive for OTA.

    QUESTION:
    So, my question is this: Will the [WiFi.mode(WIFI_AP_STA);] method above work for OTA and ESP-NOW?
    — For example, I have seen on arduino.stackexchange.com (77344)

    [quote] “Another method that works if you don’t mind having both at the same time:
    Instead of WiFi.begin(ssid, password); I use WiFi.softAp(ssid,password,channel); for both Sender and Receiver.”[/quote]
    … in addition to a C++ method for switching WiFi ON/OFF, based on the receiver sending a boolean switch to the sender (same post).

    Any and all suggestions to help clear my confusion – and save me countless hours – will be gratefully received.

    Thanks again and regards,
    Chris

    Reply
    • Hi Chris.

      Thanks for following our work 🙂

      I haven’t experimented with OTA with this project.
      But you can try our new OTA project and try to integrate it with the ESP-NOW web server and see if it works as expected.
      It is straightforward to use. So, in under 5 minutes, you’ll have OTA in your project, and you’ll be able to test if it will work.
      Here’s the tutorial: https://randomnerdtutorials.com/esp32-ota-over-the-air-arduino/
      Then, let me know your findings.
      Regards,
      Sara

      Reply
      • Thanks Sara,
        I tried the AsyncElegantOTA library – found it be terrific.
        — I got your demo working in minutes.

        A small heads-up… at first it wasn’t working… but then I realised…

        I had a Firefox upgrade and forgot about the [about:config] trick for getting rid of their default setting of putting www in front of URL’s which causes the URL to fail.
        — That is, manual IP addresses etc.
        — I tried three browsers – but only tweaked FF.

        In case you’re not a FF (Win10) user and others who are, might not be aware…
        :: To stop Firefox from automatically adding www in front of URLs
        :: address bar: > about:config
        :: search for [browser.fixup.alternate.prefix]
        :: —– delete the www, (or set to false) … restart FF.

        I hope this helps others.

        Chris

        Reply
  38. Hi Sara

    This project is top notch!!!

    does the master-receiver have the ability to send back info to the slave/s via esp-now?

    have you got a project for this?

    thanks

    Nick

    Reply
  39. Hi,

    I was wondering if this will also work with an ESP32-CAM instead of a temperature sensor? So it sends the data from the camera to an ESP32-WROOM-32U/D via ESPNOW and the WROOM sends it via wifi to the network.

    Is this possible? How do I implement this if it is possible?

    Thanks in advance

    Greetings Alex

    Reply
  40. Good Afternoon Sara,

    It’s possible not use a wifi router? just connect the computer directly to the receiver board wifi?

    Thanks in advance.

    Reply
  41. Hi sara,
    Thank you very much for this great tutorial!
    Using WiFi.scanNetworks to get the wifi channel work fine as long as the transmitter is in the wifi coverage area .
    Here is a code that works fast and reliable if not. (testing all wifi channels)
    Hi everybody from Belgium

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

    // softAPMacAdress of receiver here
    uint8_t broadcastAddress[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00};

    // Structure example to send data
    // Must match the receiver structure
    typedef struct struct_message
    { String texte;
    }struct_message;
    // Create a struct_message called myData
    struct_message myData;

    boolean recuConfirm=false;
    boolean envoiOk=false;
    boolean ok=false;

    void OnDataSent(uint8_t *mac_addr, uint8_t sendStatus) {

    if(sendStatus==0){
    recuConfirm=true;
    envoiOk=true;
    }
    else {
    recuConfirm=true;
    envoiOk=false;
    }
    }

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

    myData.texte = “Test”;

    //scan wifi channels

    for (int32_t can=1;can<14;can++){
    wifi_promiscuous_enable(1);
    wifi_set_channel(can);
    wifi_promiscuous_enable(0);

    // Try init ESP-NOW
    if (esp_now_init() == 0) {
    esp_now_set_self_role(ESP_NOW_ROLE_CONTROLLER);
    esp_now_register_send_cb(OnDataSent);
    esp_now_add_peer(broadcastAddress, ESP_NOW_ROLE_SLAVE,1, NULL, 0);
    esp_now_send(broadcastAddress, (uint8_t *) &myData, sizeof(myData));
    if( confirme()){ //Yess..found it
    Serial.print(“channel:”);
    Serial.print(can);
    ok=true;
    break;
    }
    }
    delay(20); //don’t remove, needed.
    esp_now_deinit();
    }
    if (ok) {
    myData.texte =”Hello World”;

    // Send message via ESP-NOW
    esp_now_send(broadcastAddress, (uint8_t *) &myData, sizeof(myData));
    if (confirme())Serial.println(“Sending ok”);
    }
    else Serial.println(“Sorry, not found”);

    }

    void loop() {
    }

    boolean confirme(){ // wait sending information
    unsigned long troplong;
    troplong =millis()+1000;
    // wait confirmation (from OnDataSent)
    while (millis()<troplong&&recuConfirm==false)delay(10);
    if (millis()>troplong) return false; //si + 3sec ->false
    else return envoiOk;

    }

    Reply
  42. Hi,
    wifi channel can be changed independent of ESP-Now and must not before initializing ESP-Now.
    So the simplest way for the sender to find the right channel is to change channel if sending is not successfull and try again:
    ….
    boolean isSent; //sending successfull?
    // Callback when data is sent
    void onDataSent(const uint8_t *mac_addr, esp_now_send_status_t sendStatus) {
    isSent = (sendStatus == 0) ? true : false;
    }
    void loop() {
    ….
    esp_now_send(broadcastAddress, (uint8_t *) &myData, sizeof(myData));
    delay(1000); //may be less
    if (!isSent) {
    for(int cnl=1; cnl<14; cnl++){
    esp_wifi_set_promiscuous(true);
    esp_wifi_set_channel(cnl, WIFI_SECOND_CHAN_NONE);
    esp_wifi_set_promiscuous(false);
    esp_now_send(broadcastAddress, (uint8_t *) &myData, sizeof(myData));
    delay(1000); //may be less
    if (isSent) break;
    }
    }
    }
    Tested. Works @me.

    Reply
    • This works great for me and I think it is the best and the fastest way to connect to the receiver. A delay of 100ms instead of 1000ms works well for me. It works even better if the last successful channel is stored in the RTC memory which I did try successfully.

      Reply
    • Olá. Não sei.
      Tudo vai depender do projecto.
      Mas a fonte oficial do espressif menciona o seguinte:
      “ESP-NOW technology also has the following limitations:

      Limited encrypted peers. 10 encrypted peers at the most are supported in Station mode; 6 at the most in SoftAP or SoftAP + Station mode;
      Multiple unencrypted peers are supported, however, their total number should be less than 20, including encrypted peers;
      Payload is limited to 250 bytes.”

      Nunca experimentei com um número tão elevado de dispositivos. Por isso, não posso dar a certeza.

      Cumprimentos.
      Sara Santos

      Reply
  43. Congratulations for this great tutorial and video. Excelent quality.
    Just a little comment, to avoid two resistors use you could activate PULL-UP in GPIO4 of ESP32. Just to include this sentence in slave setup() void:
    pinMode(DHTPIN, INPUT_PULLUP);
    It is same effect thank resistor, but inside ESP32, that allow this function in almost all GPIO.
    Gracias,
    Oscar

    Reply
  44. Hi. Is there a way to put the device’s network into both STA and AP using MicroPython? I have a device that is able to do both separately but not in the same code. I have the device receive a message via ESP-Now and then I want it to text me that it received it. Is that possible or is there another way? Thanks

    Reply
  45. Ola, preciso de uma ajuda.

    Estou querendo inserir um valor numa chave dentro do preferences ao clicar num icone no html, dessa forma:
    server.on(“/ativaMR1”, HTTP_GET, [](AsyncWebServerRequest *request){ request->send(200, “text/html”); jso.putString(“MR1”, MR1ativar); Serial.println(); Serial.println(“SOLICITAÇÃO OK: ATIVAR (MR1)”); Serial.println();});

    Ele nao esta atualizando a chave dessa forma… pde me ajudar?

    att

    Reply
  46. 15:8:21.389 -> ets Jun 8 2016 00:22:57
    15:8:21.390 ->
    15:8:21.390 -> rst:0x1 (POWERON_RESET),boot:0x13 (SPI_FAST_FLASH_BOOT)
    15:8:21.391 -> configsip: 0, SPIWP:0xee
    15:8:21.401 -> clk_drv:0x00,q_drv:0x00,d_drv:0x00,cs0_drv:0x00,hd_drv:0x00,wp_drv:0x00
    15:8:21.427 -> mode:DIO, clock div:1
    15:8:21.434 -> load:0x3fff0018,len:4
    15:8:21.441 -> load:0x3fff001c,len:1044
    15:8:21.447 -> load:0x40078000,len:10124
    15:8:21.454 -> load:0x40080400,len:5856
    15:8:21.470 -> entry 0x400806a8
    15:8:21.476 -> Setting as a Wi-Fi Station..
    15:8:22.833 -> Setting as a Wi-Fi Station..
    15:8:23.836 -> Setting as a Wi-Fi Station..
    15:8:24.833 -> Setting as a Wi-Fi Station..
    15:8:25.834 -> Setting as a Wi-Fi Station..
    15:8:26.893 -> Setting as a Wi-Fi Station..
    15:8:27.831 -> Setting as a Wi-Fi Station..
    15:8:28.830 -> Setting as a Wi-Fi Station..
    15:8:29.832 -> Setting as a Wi-Fi Station..
    15:8:30.827 -> Setting as a Wi-Fi Station..
    15:8:31.828 -> Setting as a Wi-Fi Station..
    15:8:32.828 -> Setting as a Wi-Fi Station..
    … … ….

    The Wifi doesn’t even start. Haven’t done any special changes to the code.

    Reply
    • Hi.
      Double-check the MAC address of the receiver and the distance between the boards.
      To test, you can try it with the broadcast MAC address:
      uint8_t broadcastAddress[] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
      Regards,
      Sara

      Reply
  47. Good day,

    I would like to ask if the IP address that has been given by the Receiver can be change by the user? For example the given IP address is 192.168.45.232, can I change it to 192.168.54.232?

    Reply
  48. Hi Sara,

    Is the given IP address of the receiver can be change? For example the given IP addess is 192.168.45.232, can I change it to 192.168.53.232? And can you also tell me more about the given IP address? Thank you.

    Reply
  49. High, great example, but I’m not getting it. I’m attempting to do this in the esp mesh and am having a hard time putting the piees together since I’m just using the hello from node x. my main page is this:
    //Async webserver
    server.on(“/”, HTTP_GET, [](AsyncWebServerRequest *request){
    request->send(200, “text/html”, ”

    Text to Broadcast

    “);
    if (request->hasArg(“BROADCAST”)){
    String msg = request->arg(“BROADCAST”);
    mesh.sendBroadcast(msg);
    }
    });

    server.begin();

    So I can send a message from the web sever to the nodes and verify they receive it just as the nodes send a message to the server and it receives it on the web page, not on the serial interface.. The nodes send ID and a simple message of hello from node ID I think once I can do this and figure it out I’ll be good to go for the rest of the program. Please help! I use to be a VB6 programmer but this just doesn’t seem to click for my on how the events pull everything to gether.

    Reply
  50. Hello and thank you both for all your hard work. I have been looking for a clearer understanding of ESP-NOW and using wifi also… my question is will the wifi still work correctly with my ESP32-CAM board as a TX & RX setup?

    Reply
  51. Hello, thank you for your efforts. I am trying to use this example as a groundwork for receiving ESP-NOW packets and retransmitting them over WIFI as MQTT. Is this something that you have tried from a single ESP32 receiving ESP-NOW and transmitting MQTT to a PC? I have tried many channel combinations and configurations but it seems that every time I connect WIFI the ESP-NOW communcations stop, any Ideas?

    Reply
    • Hi,
      I have done this but there is a problem.
      The mac address of the receiving unit changes when you turn on the WIFI and enable it as a Access point. The actual mac address can be found from Serial.print(WiFi.softAPmacAddress());
      In my case it was one digit greater than the normal mac address
      Hope this helps

      Reply
  52. Hi Sara,
    This is a great tutorial for receiving ESP-Now data on the same ESP32 that acts as a web server. But how about transmitting ESP-Now + webserver on the same ESP32? I can’t find anything online about it – does it mean it is not possible?

    In a post from June 2, 2020 at 10:14 am you say “I haven’t tried sending data from the main ESP32 that is acting as a web server. I plan to try that out in the future.”
    – Did you ever try it? 🙂

    Reply
      • I guess you are right, I can try it at some point and get back to you. I’m assuming I can then skip the whole part of syncing the WIFI channel across all boards? Because the ESP-now sender is now the webserver, and the receivers won’t need to know about its channel, no?

        Reply
  53. hello everybody,

    I implemented the project and it works perfectly right away. The extension to more ESPs also works without any problems.

    For the implementation in my own project, I have considerable problems with the website, whose logic and structure I do not understand properly.

    I don’t just want to equip each individual ESP with a DHT, I want to use all IOs of the ESP for different alarm contacts.
    To do this, I just want to change the telegram to
    Board ID
    description (of alarm line)
    value
    ReadingID

    Changing the structure is not the problem. But I can’t change the website on the receiver.
    I just want to play back a list of alert points.

    Can someone help me with that?

    Reply
  54. Thank you both for this smashing write up.

    After some changes I was able to adapt your code for my weather station.

    I’m now able to display windspeed (from an anemometer), wind direction (from a weather vane), indoor and outdoor temperatures (from x2 DS18B20-type thermometers) on 4 “cards” in a browser.

    Moreover I also use the data to wirelessly drive a 21 module (1 metre) long LED matrix display to display the above information and locally at the main weather station head end on a smaller 6 module long LED matrix display for house keeping the station.

    A further module also transmits this data over LoRa for added telemetry flexibility.

    I look forward to adapting your other code project to be able to plot wind gusts over a time axis.

    Reply
  55. hi that is a great tutorial. but is it possible to publish these sensor data using mqtt. i actually want to send this that to Home Assistant which have mqtt broker addon to listen to mqtt messages. can you guys make a tutorial on espnow to mqtt gateway or give any suggestion.
    i really appreciate your help.
    thank you

    Reply
  56. Hi Author,
    Great tutorial. I’m currently have my own hosting and database. How can I send the data to the database at my hosting site? Thanks

    Reply
  57. Hi,
    I have to ESP32 talking with each other using ESP-Now and one of the ESP32 acts as an Webserver. Quite similar to the described tutorial here.
    But unfortunatley the function WiFi.localIP() delivers only and all the times 0.0.0.0.
    Could you solve this issue. This may very helpful to find the webpage with non-static IP-Addresses.

    Reply
    • Problem solved: The correct IP is available round about 2,5 seconds after establishing of the WiFi connection.
      So the best way is to ask in a loop for a valid IP address and then continue with normal work.

      Reply
  58. Where to find this specific esp_now.h library for downloading or cloning? On github.com there is only a idf version of it at
    esp-idf/components/esp_wifi/include/esp_now.h, whwere zou can not cloe it.

    Reply
  59. do you have the code that enable the ESP32 to read from the sensors and sent the values to a web page and bring me her ip adress

    Reply
  60. how to upload sensor values received by master esp to thingspeak colud using wifi. Here slave esp will be connected to the sensor and send data to master esp using espnow BLE comminucation

    Reply
  61. Hello Sara thank you for great tutorial, but i have problem my device cannot peer to peer each other. Here’s the message I got
    Mode: STA
    Channel: 1
    SSID (0):
    Passphrase (0):
    BSSID set: 0
    Mode: STA
    Channel: 6
    SSID (0):
    Passphrase (0):
    BSSID set: 0
    E (2302) ESPNOW: Peer interface is invalid
    Failed to add peer
    28.80
    49.00
    Error sending the data

    is there any solution? Thank you

    Reply
  62. I have been successful in using both ESP32s and ESP8266s is in a many-to
    -many ESPNOW broadcasting network and still provide a coded WiFi connection. I use the connection mostly for a simple web console for debug display and user control, but a more elaborate Dashboard is certainly doable. I’ve only tried up tp nine ESPs ‘cause I ran out a of available ESPs laying about the house. And it is blindingly fast. I call it ENNN for ESP Now Node Network.

    Greg

    Reply
  63. I built a weather station based on this project.
    It works great and displays data on a LCD display, and saves the data to an SD card.
    I can connect to the IP address of my board from any computer or phone in the house and see the values.
    I sent the IP address to a friend who lives about a mile away.
    He said he got this message: ERR_CONNECTION_TIMED_OUT
    Is there any way to speed the connection up?
    Thanks.

    Reply
  64. Please how conversion my MAC adress: “e8:db:84:9e:55:0f”
    to command:
    snprintf(macStr, sizeof(macStr), “%02x:%02x:%02x:%02x:%02x:%02x” ???

    Reply
  65. Hi, first of all, thanks for the tutorial.
    I am new on coding, and i’m trying to send more data (temperature and humidity) from a DHT11 connected on the server ESP32 board, the one that receives temperature and humidity from other ESP32, connected via ESP NOW, like the tutorial.
    How do i send the data read directly on the ESP32 that are connected on the DHT11, with the values received from the others boards, to the web?
    I have to insert more lines on de JSON string (board)?

    Thank you.

    Reply
  66. Hi,

    thanks for the good example. I was fiddling around trying to get ESP NOW sensors to speak with a HomeSpan / HomeKit bridge (server) and the channel settings in your code was the right hint! Nowhere else mentioned…
    I’m still looking for a better way to find out the right channel for communicating with the slave, may be by trying with trail and error: Sending a ‘Ping’ packet over every channel and waiting for slave respond with a ‘Pong’.
    Cheers
    Bernd

    Reply
  67. Hello Sarah,

    Thank you for this fantastic example. I would like to implement a similar setup but using a websocket server instead which acts as ESP Now receiver AND sender to multiple sensor boards. I need to read sensor data from several boards AND also want to send instructions to the sensor boards (switch heating on/off for example). So that I am able to read and control the boards via internet. Is that possible?

    Best regards
    Juergen

    Reply
  68. Obviously people are getting this to work.
    I am having issues with the library side.
    #include “ESPAsyncWebServer.h” means the library is local. It says to install in the arduino/library folder.

    are we to strip out the one .H file?
    Any reason for using a local library file rather than using the main library folder?

    Reply
    • I got this working. Seems there were some breaking changes in the updated libraries. Nothing to do with this code. Google any errors you get, and the changes will be shown.
      Changes to the library are required.

      Reply
  69. Has anyone got this to work as a softaccess point rather than connecting to an access point?
    I want this to be stand alone.
    I have cut and pasted items, but cant get this to work as a soft access point server.

    Reply
  70. Sara,
    I’m testing this circuit but it’s not working the web communication doesn’t appear at all on the computer or smartphone just the screen but the temperature and humidity values don’t change. I noticed that in ESPnow communication the channel does not change from 1 to 6 as shown. Could you or someone help me?

    Reply
  71. Newbie question:
    Can someone please explain why the receiver has to be configured in station + AP mode? It’s not obvious to me what part of the AP mode is being used.
    Thanks.

    Reply
  72. Hi,
    Thank you very much for this valuable demo, Can I stream video from multi esp32 cameras by using their unique Mac Address to one client such web browser or Mobile App ? If yes, can you navigate me to an example?

    Reply
  73. Best regards to all the staff and thanks for this project which I found very interesting.
    I state that I use a wemos mini as a receiver, two esp32 as transmitters. I modified the code to adapt it to the SHTC3 sensors, instead of the DHT11. It all works wonderfully well. However, after leaving it to work all day and night, I encountered a problem in the morning: there was no data on the web server. The serial monitor of the receiver was empty while that of the transmitters sent but the sending failed. I restarted the 3 cards and everything started working again, starting again with “Reading ID: 1, 2, etc.. The next morning it was blocked again: Receiver monitor empty. I wanted to do an analysis: I restarted “only” the receiver. After a few seconds, his serial monitor showed: “Client reconnected! Last message ID that it got is: 56819757”. Then I also restarted the two transmitters and everything started working again. My question: What do you think?

    Reply
      • Here, it stopped. Now, the receiver’s serial no longer receives anything. Posting the last lines of the log until the break:

        Packet received from: 9c:9c:1f:c4:bd:38
        Board ID 2: 16 bytes
        t-value: 14.76
        h value: 54.49
        readingID value: 2146

        Packet received from: c8:c9:a3:c6:c6:bc
        Board ID 1: 16 bytes
        t-value: 15.35
        h value: 43.81
        readingID value: 2153

        Packet received from: 9c:9c:1f:c4:bd:38
        Board ID 2: 16 bytes
        t-value: 14.77
        h value: 54.59
        readingID value: 2147

        dhcps: send_nak>>udp_sendto result 0
        dhcps: send_nak>>udp_sendto result 0

        Subsequently I will “only” reset the receiving card to check if and what it receives…

        Reply
  74. For the moment this is the log after the reset:
    ets Jun 8 2016 00:22:57

    rst:0x1 (POWERON_RESET),boot:0x13 (SPI_FAST_FLASH_BOOT)
    configsip:0, SPIWP:0xee
    clk_drv:0x00,q_drv:0x00,d_drv:0x00,cs0_drv:0x00,hd_drv:0x00,wp_drv:0x00
    mode:DIO, clock div:1
    load:0x3fff0018,len:4
    load:0x3fff001c,len:1044
    load:0x40078000,len:10124
    load:0x40080400,len:5856
    entry 0x400806a8
    Setting as a Wi-Fi Station..
    Setting as a Wi-Fi Station..
    Setting as a Wi-Fi Station..
    Station IP Address: 192.168.1.71
    WiFi Channels: 11
    2023-03-16 16:25:37 –> Clients reconnected! Last message ID that it got is: 24661261

    Reply
  75. I’m getting the following from my board 1. It is reading the DHT22 just fine and displaying the readings. But it can’t send to the reciever. The reciever is working fine because I can bring up the webpage just fine.
    Anyone have any thoughts.

    10:21:42.510 -> Mode: STA
    10:21:42.510 -> Channel: 2
    10:21:42.510 -> SSID (13): mynetwork
    10:21:42.510 -> Passphrase (8): mypassword
    10:21:42.510 -> BSSID set: 0
    10:21:42.510 -> E (159) ESPNOW: Peer interface is invalid

    Reply
    • I had the same error on all 3 senders … you must initialize the memory first.

      try this …
      In the sender sketch

      add this line of code > memset(&peerInfo, 0, sizeof(peerInfo));
      right above this line of code > memcpy(peerInfo.peer_addr, broadcastAddress, 6);

      Regards,

      Reply
  76. Hi Sara,

    Thanks for the tutorial.
    I have setup communication between two ESp32 S3 boards. and I am sending an array of size 10 from one to another:

    uint8_t Test[10] = {0,1,2,3,4,5,6,7,8,9};

    I do not get any error but sometimes I get garbage output on the receiver board as follows:

    values1 = 1 hex: 0x68,0xB6,0xB3,0x36,0xAA,0x7C data: 2,0,1,3,4,5,6,7,8,9
    values1 = 1 hex: 0x68,0xB6,0xB3,0x36,0xAA,0x7C data: 2,0,1,3,4,5,6,7,8,9
    values1 = 1 hex: 0x68,0xB6,0xB3,0x36,0xAA,0x7C data: 2,0,1,3,4,5,6,7,8,9
    values1 = 1 hex: 0x68,0xB6,0xB3,0x36,0xAA,0x7C data: 72,96,108,3,1,1,45,26,45,64
    values1 = 1 hex: 0x68,0xB6,0xB3,0x36,0xAA,0x7C data: 2,0,1,3,4,5,6,7,8,9
    values1 = 1 hex: 0x68,0xB6,0xB3,0x36,0xAA,0x7C data: 2,0,1,3,4,5,6,7,8,9
    values1 = 1 hex: 0x68,0xB6,0xB3,0x36,0xAA,0x7C data: 2,0,1,3,4,5,6,7,8,9
    values1 = 1 hex: 0x68,0xB6,0xB3,0x36,0xAA,0x7C data: 2,0,1,3,4,5,6,7,8,9

    I really can not find any source helping me with this error.
    Any thoughts on this issue?

    Thanks

    Reply
  77. Hi Sara,
    Im using esp-now two way communication between a master and a slave. The slave reads from a dht22, sends the readings to the master that hosts a web server and displays those readings. From this server a user can chose the threshold value and a checkbox and those data are being sent back to the slave and control a fan using a bi-stable relay. My problem is on the slave part of the code. Im using deep sleep mode to preserve power. The data are being sent and received successfully, but every time the esp goes to sleep the fan closes not because the gpio doesn power the relay (again im using a bi-stable relay), but because the memory erases the incoming data and the if statement doesnt know what to do. My solution was to use light sleep so that the core and memory stay active. The relay now works fine, but temp and humidity readings are not being sent for a reason and the web server displays nothing. Its quite intresting the fact that the data are being sent with success using deep sleep but without success when i use light sleep, considering that the only thing i change in the code is one line. Do you have anything in mind? Below is the void loop where i think is the problem. Inside the setup i use the command esp_sleep_enable_timer_wakeup(30 * 1000000); to wake up after 30 seconds.
    Thanks in advance!
    void loop() {
    unsigned long currentMillis = millis();
    if (currentMillis – previousMillis >= interval) {
    // Save the last time a new reading was published
    previousMillis = currentMillis;
    //Set values to send
    myData.id = BOARD_ID;
    myData.tempc = readDHTTemperature();
    myData.tempf = readDHTTemperatureF();
    myData.hum = readDHTHumidity();
    myData.volts = readBatteryVolts();
    myData.perc = readBatterypercentage();
    myData.drst = digitalRead(DOOR_SENSOR_PIN);

    //Send message via ESP-NOW
    esp_err_t result = esp_now_send(broadcastAddress, (uint8_t *) &myData, sizeof(myData));
    if (result == ESP_OK) {
    Serial.println("Sent with success");
    }
    else {
    Serial.println("Error sending the data");}

    float temperature = dht.readTemperature();; // read temperature in Celsius

    doorState = digitalRead(DOOR_SENSOR_PIN); // read state

    if (doorState == LOW) {
    Serial.println(“The door is closed!”);
    if (isnan (temperature)) {
    Serial. println (“Failed to read from DHT sensor!”); }
    else{
    if (temperature > incomingReadings.thres && incomingReadings.checkbox == “true” &&
    !triggerActive){
    Serial.println (“Turn the freezer unit on”);
    triggerActive = true;
    digitalWrite (26, LOW); //turn on
    digitalWrite (25,HIGH);
    delay(10);
    digitalWrite (25,LOW);
    }
    else if (temperature < incomingReadings.thres && incomingReadings.checkbox == “true” && triggerActive) {
    Serial.println (“Turn the freezer unit off”);
    triggerActive = false;
    digitalWrite (26, HIGH);
    delay(10);
    digitalWrite (26,LOW);
    digitalWrite (25,LOW);}}}//turn off

    if (doorState == HIGH) {
    esp_sleep_enable_ext0_wakeup(GPIO_NUM_33, HIGH);
    triggerActive = false;
    digitalWrite(26, HIGH);
    delay(10);
    digitalWrite(26, LOW);
    digitalWrite(25, LOW);
    }
    delay(1000);
    //esp_deep_sleep_start();
    esp_light_sleep_start();
    }
    }

    Reply
    • Hi.
      When the ESP32 goes to sleep, it doesn’t run the rest of the code.
      Then, when it wakes up, it starts running the code from the start.
      So, any code that you add after the instruction to deep sleep will not run.
      Regards,
      Sara

      Reply
  78. Dear Sara and Rui ;

    I hope this message finds you well. I am reaching out to seek your guidance and expertise regarding a technical issue I am facing with my smart radar project implementation. I would greatly appreciate any assistance or insights you can provide to help me overcome this challenge.

    The objective of my project is to develop a radar system using an ultrasonic sensor, ESP32 development module, ESP32-CAM module, and ESP-NOW communication protocol. The system is designed to detect objects within a specific range, capture images using the ESP32-CAM module, and display the collected data on a web server for visualization.

    While I have successfully implemented the ultrasonic sensor and established communication between the ESP32 modules, I am encountering two main problems:

    1-Camera Image Capture: The ESP32 module successfully sends the distance data to the ESP32-CAM module. However, the camera does not capture any images when instructed to do so. I have verified the camera module’s connections and confirmed that it is functional. I suspect that there might be an issue with the code responsible for triggering the image capture.

    2-Web Server Display: Despite the successful transmission of data from the ESP32-CAM module to the web server, the collected readings are not being displayed on the web page. I have double-checked the web server configuration and ensured that it is running correctly. However, there might be a code-related issue that prevents the data from being properly rendered on the web page.

    I kindly request your assistance in resolving these issues. It would be immensely helpful if you could provide guidance on how to modify the code to ensure proper image capture and data display on the web server. Additionally, any insights or suggestions you may have to troubleshoot these problems would be highly appreciated.

    If you require any specific details, such as the code snippets or circuit diagram, please let me know, and I will gladly provide them to assist in troubleshooting.

    Thank you in advance for your valuable support. Your expertise and guidance will undoubtedly contribute to the successful completion of this project. I look forward to your response and the opportunity to further discuss and resolve these technical challenges.

    Best regards,

    Reply
    • Hi.
      First, you need to adress one problem at a time.
      Does the ESP32 get the sensor data from the ESP32-CAM? How are you sending the data to the web server?
      Regards,
      Sara

      Reply
      • Dear Sara,

        Thank you for your prompt response. I apologize for any confusion caused. To clarify, in my project, the ESP32-CAM module serves as the receiver, while the ESP32 module functions as the sender.

        Currently, I have successfully implemented the ESP-NOW protocol to establish communication between the ESP32 (sender) and ESP32-CAM (receiver). The ESP32 module is able to send the data to the ESP32-CAM module without any issues.

        However, the problem I am facing is that the data received by the ESP32-CAM is not being displayed on the web server. I have utilized the code you provided above , and i modifie it so that it works using one sender board.

        Regarding the second question, I am using the code you provided, which includes the implementation of the ESP-NOW protocol for communication between the ESP32 and ESP32-CAM.

        Thank you for your response. If it would be helpful, I can provide you with the code I have implemented for the project. Please let me know if you would like me to send it to you.

        I appreciate your willingness to assist me in resolving the issue.

        Best regards,

        Reply
        • Hi.
          Have you checked that you have the right BOARD ID set up?
          In the tutorial we have the id for two boards. If you’re just using one board you should have the right names on the web page too. did you check that?
          Regards,
          Sara

          Reply
  79. This is a great project. My chicken coop is out of range of my router, but with ESP-Now and Wi-Fi, I can make a connection from the coop to the house. So I highly modified the webpage and sender code to read 2 DS18B20s, send over C and F readings, display them on the webpage, and all is good! Well…almost! (esp32 core V1.0.6 is good.)
    Today I started playing with a newly arrived ESP32-S3 dev board, and found I needed to update the esp32 Arduino core to use it. (V2.0.10) Later, returning to my coop project, I made minor cleanups in the code and tried to recompile. Uh Oh! Broke It! (Many words that don’t apply here!)
    (Small portion of verbose output!) Error:

    Linking everything together…
    /home/dave/.arduino15/packages/esp32/tools/xtensa-esp32-elf-gcc/esp-2021r2-patch5-8.4.0/bin/xtensa-esp32-elf-g++ -Wl,–Map=/tmp/arduino_build_743492/esp32_ORIG_Receiver_ESPNOW_DHT_WebServer.ino.map -L/home/dave/.arduino15/packages/esp32/hardware/esp32/2.0.10/tools/sdk/esp32/lib -L/home/dave/.arduino15/packages/esp32/hardware/esp32/2.0.10/tools/sdk/esp32/ld -L/home/dave/.arduino15/packages/esp32/hardware/esp32/2.0.10/tools/sdk/esp32/qio_qspi -T esp32.rom.redefined.ld -T memory.ld -T sections.ld -T esp32.rom.ld -T esp32.rom.api.ld -T esp32.rom.libgcc.ld -T esp32.rom.newlib-data.ld -T esp32.rom.syscalls.ld -T esp32.peripherals.ld -mlongcalls -Wno-frame-address -Wl,–cref -Wl,–gc-sections -fno-rtti -fno-lto -Wl,–wrap=esp_log_write -Wl,–wrap=esp_log_writev -Wl,–wrap=log_printf -u ld_include_hli_vectors_bt -u _Z5setupv -u _Z4loopv -u esp_app_desc -u pthread_include_pthread_impl -u pthread_include_pthread_cond_impl -u pthread_include_pthread_local_storage_impl -u pthread_include_pthread_rwlock_impl -u include_esp_phy_override -u ld_include_highint_hdl -u start_app -u start_app_other_cores -u __ubsan_include -Wl,–wrap=longjmp -u __assert_func -u vfs_include_syscalls_impl -Wl,–undefined=uxTopUsedPriority -u app_main -u newlib_include_heap_impl -u newlib_include_syscalls_impl -u newlib_include_pthread_impl -u newlib_include_assert_impl -u __cxa_guard_dummy -DESP32 -DCORE_DEBUG_LEVEL=0 -DARDUINO_RUNNING_CORE=1 -DARDUINO_EVENT_RUNNING_CORE=1 -DARDUINO_USB_CDC_ON_BOOT=0 -Wl,–start-group /tmp/arduino_build_743492/sketch/esp32_ORIG_Receiver_ESPNOW_DHT_WebServer.ino.cpp.o /tmp/arduino_build_743492/libraries/WiFi/WiFi.cpp.o /tmp/arduino_build_743492/libraries/WiFi/WiFiAP.cpp.o /tmp/arduino_build_743492/libraries/WiFi/WiFiClient.cpp.o /tmp/arduino_build_743492/libraries/WiFi/WiFiGeneric.cpp.o /tmp/arduino_build_743492/libraries/WiFi/WiFiMulti.cpp.o /tmp/arduino_build_743492/libraries/WiFi/WiFiSTA.cpp.o /tmp/arduino_build_743492/libraries/WiFi/WiFiScan.cpp.o /tmp/arduino_build_743492/libraries/WiFi/WiFiServer.cpp.o /tmp/arduino_build_743492/libraries/WiFi/WiFiUdp.cpp.o /tmp/arduino_build_743492/libraries/ESPAsyncWebServer/AsyncEventSource.cpp.o /tmp/arduino_build_743492/libraries/ESPAsyncWebServer/AsyncWebSocket.cpp.o /tmp/arduino_build_743492/libraries/ESPAsyncWebServer/SPIFFSEditor.cpp.o /tmp/arduino_build_743492/libraries/ESPAsyncWebServer/WebAuthentication.cpp.o /tmp/arduino_build_743492/libraries/ESPAsyncWebServer/WebHandlers.cpp.o /tmp/arduino_build_743492/libraries/ESPAsyncWebServer/WebRequest.cpp.o /tmp/arduino_build_743492/libraries/ESPAsyncWebServer/WebResponses.cpp.o /tmp/arduino_build_743492/libraries/ESPAsyncWebServer/WebServer.cpp.o /tmp/arduino_build_743492/libraries/FS/FS.cpp.o /tmp/arduino_build_743492/libraries/FS/vfs_api.cpp.o /tmp/arduino_build_743492/libraries/AsyncTCP/AsyncTCP.cpp.o /tmp/arduino_build_743492/libraries/Arduino_JSON/JSON.cpp.o /tmp/arduino_build_743492/libraries/Arduino_JSON/JSONVar.cpp.o /tmp/arduino_build_743492/libraries/Arduino_JSON/cjson/cJSON.c.o /tmp/arduino_build_743492/core/core.a -lesp_ringbuf -lefuse -lesp_ipc -ldriver -lesp_pm -lmbedtls -lapp_update -lbootloader_support -lspi_flash -lnvs_flash -lpthread -lesp_gdbstub -lespcoredump -lesp_phy -lesp_system -lesp_rom -lhal -lvfs -lesp_eth -ltcpip_adapter -lesp_netif -lesp_event -lwpa_supplicant -lesp_wifi -lconsole -llwip -llog -lheap -lsoc -lesp_hw_support -lxtensa -lesp_common -lesp_timer -lfreertos -lnewlib -lcxx -lapp_trace -lasio -lbt -lcbor -lunity -lcmock -lcoap -lnghttp -lesp-tls -lesp_adc_cal -lesp_hid -ltcp_transport -lesp_http_client -lesp_http_server -lesp_https_ota -lesp_https_server -lesp_lcd -lprotobuf-c -lprotocomm -lmdns -lesp_local_ctrl -lsdmmc -lesp_serial_slave_link -lesp_websocket_client -lexpat -lwear_levelling -lfatfs -lfreemodbus -ljsmn -ljson -llibsodium -lmqtt -lopenssl -lperfmon -lspiffs -lulp -lwifi_provisioning -lrmaker_common -lesp_diagnostics -lrtc_store -lesp_insights -ljson_parser -ljson_generator -lesp_schedule -lespressif__esp_secure_cert_mgr -lesp_rainmaker -lgpio_button -lqrcode -lws2812_led -lesp32-camera -lesp_littlefs -lespressif__esp-dsp -lfb_gfx -lasio -lcmock -lunity -lcoap -lesp_lcd -lesp_websocket_client -lexpat -lfreemodbus -ljsmn -llibsodium -lperfmon -lesp_adc_cal -lesp_hid -lfatfs -lwear_levelling -lopenssl -lspiffs -lesp_insights -lcbor -lesp_diagnostics -lrtc_store -lesp_rainmaker -lesp_local_ctrl -lesp_https_server -lwifi_provisioning -lprotocomm -lbt -lbtdm_app -lprotobuf-c -lmdns -ljson -ljson_parser -ljson_generator -lesp_schedule -lespressif__esp_secure_cert_mgr -lqrcode -lrmaker_common -lmqtt -lcat_face_detect -lhuman_face_detect -lcolor_detect -lmfn -ldl -lesp_ringbuf -lefuse -lesp_ipc -ldriver -lesp_pm -lmbedtls -lapp_update -lbootloader_support -lspi_flash -lnvs_flash -lpthread -lesp_gdbstub -lespcoredump -lesp_phy -lesp_system -lesp_rom -lhal -lvfs -lesp_eth -ltcpip_adapter -lesp_netif -lesp_event -lwpa_supplicant -lesp_wifi -lconsole -llwip -llog -lheap -lsoc -lesp_hw_support -lxtensa -lesp_common -lesp_timer -lfreertos -lnewlib -lcxx -lapp_trace -lnghttp -lesp-tls -ltcp_transport -lesp_http_client -lesp_http_server -lesp_https_ota -lsdmmc -lesp_serial_slave_link -lulp -lmbedtls_2 -lmbedcrypto -lmbedx509 -lcoexist -lcore -lespnow -lmesh -lnet80211 -lpp -lsmartconfig -lwapi -lesp_ringbuf -lefuse -lesp_ipc -ldriver -lesp_pm -lmbedtls -lapp_update -lbootloader_support -lspi_flash -lnvs_flash -lpthread -lesp_gdbstub -lespcoredump -lesp_phy -lesp_system -lesp_rom -lhal -lvfs -lesp_eth -ltcpip_adapter -lesp_netif -lesp_event -lwpa_supplicant -lesp_wifi -lconsole -llwip -llog -lheap -lsoc -lesp_hw_support -lxtensa -lesp_common -lesp_timer -lfreertos -lnewlib -lcxx -lapp_trace -lnghttp -lesp-tls -ltcp_transport -lesp_http_client -lesp_http_server -lesp_https_ota -lsdmmc -lesp_serial_slave_link -lulp -lmbedtls_2 -lmbedcrypto -lmbedx509 -lcoexist -lcore -lespnow -lmesh -lnet80211 -lpp -lsmartconfig -lwapi -lesp_ringbuf -lefuse -lesp_ipc -ldriver -lesp_pm -lmbedtls -lapp_update -lbootloader_support -lspi_flash -lnvs_flash -lpthread -lesp_gdbstub -lespcoredump -lesp_phy -lesp_system -lesp_rom -lhal -lvfs -lesp_eth -ltcpip_adapter -lesp_netif -lesp_event -lwpa_supplicant -lesp_wifi -lconsole -llwip -llog -lheap -lsoc -lesp_hw_support -lxtensa -lesp_common -lesp_timer -lfreertos -lnewlib -lcxx -lapp_trace -lnghttp -lesp-tls -ltcp_transport -lesp_http_client -lesp_http_server -lesp_https_ota -lsdmmc -lesp_serial_slave_link -lulp -lmbedtls_2 -lmbedcrypto -lmbedx509 -lcoexist -lcore -lespnow -lmesh -lnet80211 -lpp -lsmartconfig -lwapi -lesp_ringbuf -lefuse -lesp_ipc -ldriver -lesp_pm -lmbedtls -lapp_update -lbootloader_support -lspi_flash -lnvs_flash -lpthread -lesp_gdbstub -lespcoredump -lesp_phy -lesp_system -lesp_rom -lhal -lvfs -lesp_eth -ltcpip_adapter -lesp_netif -lesp_event -lwpa_supplicant -lesp_wifi -lconsole -llwip -llog -lheap -lsoc -lesp_hw_support -lxtensa -lesp_common -lesp_timer -lfreertos -lnewlib -lcxx -lapp_trace -lnghttp -lesp-tls -ltcp_transport -lesp_http_client -lesp_http_server -lesp_https_ota -lsdmmc -lesp_serial_slave_link -lulp -lmbedtls_2 -lmbedcrypto -lmbedx509 -lcoexist -lcore -lespnow -lmesh -lnet80211 -lpp -lsmartconfig -lwapi -lesp_ringbuf -lefuse -lesp_ipc -ldriver -lesp_pm -lmbedtls -lapp_update -lbootloader_support -lspi_flash -lnvs_flash -lpthread -lesp_gdbstub -lespcoredump -lesp_phy -lesp_system -lesp_rom -lhal -lvfs -lesp_eth -ltcpip_adapter -lesp_netif -lesp_event -lwpa_supplicant -lesp_wifi -lconsole -llwip -llog -lheap -lsoc -lesp_hw_support -lxtensa -lesp_common -lesp_timer -lfreertos -lnewlib -lcxx -lapp_trace -lnghttp -lesp-tls -ltcp_transport -lesp_http_client -lesp_http_server -lesp_https_ota -lsdmmc -lesp_serial_slave_link -lulp -lmbedtls_2 -lmbedcrypto -lmbedx509 -lcoexist -lcore -lespnow -lmesh -lnet80211 -lpp -lsmartconfig -lwapi -lphy -lrtc -lesp_phy -lphy -lrtc -lesp_phy -lphy -lrtc -lxt_hal -lm -lnewlib -lstdc++ -lpthread -lgcc -lcxx -lapp_trace -lgcov -lapp_trace -lgcov -lc -Wl,–end-group -Wl,-EL -o /tmp/arduino_build_743492/esp32_ORIG_Receiver_ESPNOW_DHT_WebServer.ino.elf
    Multiple libraries were found for “WiFi.h”
    Used: /home/dave/.arduino15/packages/esp32/hardware/esp32/2.0.10/libraries/WiFi
    Not used: /home/dave/arduino-1.8.19/libraries/WiFi
    /home/dave/.arduino15/packages/esp32/tools/xtensa-esp32-elf-gcc/esp-2021r2-patch5-8.4.0/bin/../lib/gcc/xtensa-esp32-elf/8.4.0/../../../../xtensa-esp32-elf/bin/ld: /tmp/arduino_build_743492/libraries/ESPAsyncWebServer/WebAuthentication.cpp.o:(.literal._ZL6getMD5PhtPc+0x4): undefined reference to mbedtls_md5_starts'
    /home/dave/.arduino15/packages/esp32/tools/xtensa-esp32-elf-gcc/esp-2021r2-patch5-8.4.0/bin/../lib/gcc/xtensa-esp32-elf/8.4.0/../../../../xtensa-esp32-elf/bin/ld: /tmp/arduino_build_743492/libraries/ESPAsyncWebServer/WebAuthentication.cpp.o: in function
    getMD5(unsigned char*, unsigned short, char*)’:
    /home/dave/Arduino/libraries/ESPAsyncWebServer/src/WebAuthentication.cpp:73: undefined reference to `mbedtls_md5_starts’
    collect2: error: ld returned 1 exit status
    Using library WiFi at version 2.0.0 in folder: /home/dave/.arduino15/packages/esp32/hardware/esp32/2.0.10/libraries/WiFi
    Using library ESPAsyncWebServer at version 1.2.3 in folder: /home/dave/Arduino/libraries/ESPAsyncWebServer
    Using library FS at version 2.0.0 in folder: /home/dave/.arduino15/packages/esp32/hardware/esp32/2.0.10/libraries/FS
    Using library AsyncTCP at version 1.1.4 in folder: /home/dave/Arduino/libraries/AsyncTCP
    Using library Arduino_JSON at version 0.2.0 in folder: /home/dave/Arduino/libraries/Arduino_JSON
    exit status 1
    Error compiling for board ESP32 Dev Module.”

    Reverted back to 1.0.6, and all is fine.
    (Yes, I changed the board selection as required for the board I was working with…in this case a generic ESP32 Dev Module)
    So, can two core libraries exist on the same system? (Incidentally, I tried it in Arduino 2.1.1 with the same results.) Or, can this project code be changed to allow it to compile with the new core?
    Best Always,
    Thanks, Dave

    Reply
    • Maybe the issues is with a library. I did see an error
      undefined reference to `mbedtls_md5_starts’ where the error started

      Reply
    • Hi.
      I think you can just have one core for the ESP32 on Arduino IDE at a time.
      Are you using Arduino IDE or VS Code?
      Regards,
      Sara

      Reply
      • In Arduino IDE v1.8.19:
        It seems it was indeed an Async library. I updated AsyncTCP and ESPAsyncWebServer, then updated the ESP32 core and compiled without issue.
        Thanks for Your and Terry’s help! I never would’ve thought the libraries were an issue as they never flagged as update-able. But of course that too makes sense since they are not “Arduino” libraries. (hindsight is 20-20!)
        Again, Thanks for Your Support Folks!
        Dave

        Reply
  80. E (6162) ESPNOW: Peer interface is invalid
    Failed to add peer
    I’m getting this error. what could be the problem? I’m using different IMU sensor

    Reply
  81. Hello, amazing tutorial. everything work fine but i have one question as i was also trying to upload the reading to firebase. i also followed your firebase data logging tutorial. but i am still unable to figure out how can separated that data and stored separately to firebase as id 1 and id 2. i tried to used if statement but still i am able to get data for only one esp. so any suggestions please?

    Reply
  82. E (6159) ESPNOW: Peer interface is invalid
    Failed to add peer
    Error sending the data

    I’m getting this error.What could be the problem? Help me out

    Reply
  83. Hi Sara. I have successfully implimented a webserver project based on one of a mixture of tutorials in your e-book about webservers. I have a device acting as a server and it’s all good.
    I parkerd that code, copied to a new version am now integrating ESP-NOW that reads RFID cards from an ‘outstation’. That also works and the data is happily forwarded to my telegram bot via the existing webserver/bridge device . However the webserver no longer works, as in the webpage does not connect/display data anymore, however ESP-NOW is OK. There seems to be a lot of chatter about WiFi channels. My existing device is connetred to channel 6, as is the ESPO-NOW sender. As I said, my webserver is no longer connectiong to the client. Any thoughts?

    Reply
  84. how can i connect three esp32 together, where the middle one act as a repeater to farward the packet or data from the esp32 (with the sensor reading) to the esp32 (with the webserver)…is this possible

    Reply
  85. Hi

    Could this code be modified to receive data from another device via ESP-NOW and then post the data to Firebase?
    Thanks

    Reply
  86. Excellent project but one observation which might be helpful to others….

    In the Sender code I found that I had to move the ” esp_now_peer_info_t peerInfo; ” statement to the global definitions area.
    Leaving it in Setup resulted in comm failure between the Sender and Receiver.

    Don’t know why that happens but have noticed it in other projects.

    Regards

    Reply
  87. Hello,
    thanks for this interesting tutorial, I would like to test it.
    Unfortunately I get the following error (m1 mea, Arduino 2.3.0 and MBA intel, Arduino 1.8: Receiver_ESPNow_Webserver_t01.ino:9:

    /Users/…/Library/Arduino15/packages/esp32/tools/esp32-arduino-libs/idf-release_v5.1-3662303f31/esp32/include/esp_wifi/include/esp_now.h:156:54: note: initializing argument 1 of ‘esp_err_t esp_now_register_recv_cb(esp_now_recv_cb_t)’
    156 | esp_err_t esp_now_register_recv_cb(esp_now_recv_cb_t cb);
    | ~~~~~~~~~~~~~~~~~~^~
    exit status 1
    Compilation error: invalid conversion from ‘void ()(uint8_t, uint8_t*, uint8_t)’ {aka ‘void ()(unsigned char, unsigned char*, unsigned char)’} to ‘esp_now_recv_cb_t’ {aka ‘void ()(const esp_now_recv_info, const unsigned char*, int)’} [-fpermissive]

    Did anyone else have this compiling error?
    Thanks in advance,
    regards,
    Tobias

    Reply
  88. Thanks, great project that I can use to upgrade my heating system. Unfortunately my esp32 dev. kit v1keeps rebooting after uploading the server code. Repeated this with same result. Any idea why?

    Reply
  89. Could this be updated to have the esp32 web server as as an AP? I know this uses the asyncweb server, and ties in the slaves for ESP-Now; while you have another article showing the way you would do a web server /AP combination but it is not using the asyncweb server.

    Reply

Leave a Comment

Download Our Free eBooks and Resources

Get instant access to our FREE eBooks, Resources, and Exclusive Electronics Projects by entering your email address below.