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

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 receiver board must be set both as a station and as an access point;
  • The ESP32 should use a different Wi-Fi channel for station mode and different channel for the access point;
  • We’ll set channel 2 for the access point. This should be the same Wi-Fi channel defined in the ESP32 sender boards;
  • The sender boards should be connected to the receiver via access point on channel 2.

We’re not sure if there’s a better way to use ESP-NOW and Wi-Fi simultaneously. This solution worked reliably for us. If you have another working solution, you can share it in the comment’s section.

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 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.

/*
  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 <Adafruit_Sensor.h>
#include <DHT.h>

//The gateway access point credentials
const char* ssid = "ESP32-Access-Point";
const char* password = "123456789";

// 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};

//Wi-Fi channel (must match the gateway wi-fi channel as an access point)
#define CHAN_AP 2

//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;

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
  WiFi.mode(WIFI_STA);
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
    delay(1000);
    Serial.println("Connecting to Access Point...");
  }

  //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.channel = CHAN_AP;  
  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 <WiFi.h>
#include <Adafruit_Sensor.h>
#include <DHT.h>

Receiver’s Access Point Credentials

As we’ve seen previously, the sender board needs to connect to the receiver board access point (learn more about setting up an ESP32 Access Point). The receiver board access point credentials are the ones defined in the next lines. If you change the access point credentials, you need to change them for every board.

// The gateway access point credentials
const char* ssid = "ESP32-Access-Point";
const char* password = "123456789";

Note: these are not your local network credentials. These are the receiver board access point credentials.

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};

ESP-NOW Wi-Fi Channel

As mentioned previously, we’ll use Wi-Fi channel 2 to communicate via ESP-NOW.

#define CHAN_AP 2

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;

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 and connect it to the receiver’s access point.

WiFi.mode(WIFI_STA);
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
  delay(1000);
  Serial.println("Connecting to Access Point...");
}

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.channel = CHAN_AP;
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)

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* ssidStation = "REPLACE_WITH_YOUR_ROUTER_SSID";
const char* passwordStation = "REPLACE_WITH_YOUR_ROUTER_PASSWORD";

// ACCESS POINT credentials
const char* ssidAP = "ESP32-Access-Point";
const char* passwordAP = "123456789";

// Wi-Fi channel for the access point (must match the sender channel)
#define CHAN_AP 2

// 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(ssidStation, passwordStation);
  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());

  // Set device as an access point
  WiFi.softAP(ssidAP, passwordAP, CHAN_AP, true);  

  // 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* ssidStation = "REPLACE_WITH_YOUR_SSID";
const char* passwordStation = "REPLACE_WITH_YOUR_PASSWORD";

As explained previously, this board needs to be set as a Wi-Fi station and access point simultaneously. In the following lines insert the credentials for the access point (you can leave them as they are).

const char* ssidAP = "ESP32-Access-Point";
const char* passwordAP = "123456789";

Define the ESP-NOW Wi-Fi channel (channel 2).

#define CHAN_AP 2

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 on 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:

WiFi.begin(ssidStation, passwordStation);
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());

Create an access point with the SSID, password and channel defined earlier.

WiFi.softAP(ssidAP, passwordAP, CHAN_AP, true);

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

Demonstration

After uploading the code to the receiver board, press the on-board EN/RST button. The ESP IP address should be printed on the Serial Monitor.

ESP32 ESP-NOW Web Server Sensor Dashboard Arduino IDE Serial Monitor Demonstration Sensor Readings

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 setup 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:


Learn how to program and build projects with the ESP32 and ESP8266 using MicroPython firmware DOWNLOAD »

Learn how to program and build projects with the ESP32 and ESP8266 using MicroPython firmware DOWNLOAD »


Enjoyed this project? Stay updated by subscribing our weekly newsletter!

55 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
    • Hi Daniel.
      That’s a great idea! Thanks for suggestion.
      We’ll take a look at something like that.
      Regards.
      Sara

      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
  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
  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
  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
  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

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.