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

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

Using ESP-NOW and Wi-Fi Simultaneously

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:

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 using ESP8266 NodeMCU boards

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

Arduino IDE

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

The ESP8266 sender board will send temperature and humidity readings from a BME280 sensor.

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

Search for “adafruit bme280 ” on the Search box and install the library.

To use the BME280 library, you also need to install the Adafruit_Sensor library. Follow the next steps to install the library in your Arduino IDE:

Go to Sketch Include Library > Manage Libraries and type “Adafruit Unified Sensor” in the search box. Scroll all the way down to find the library and install it.

To learn more about the BME280 temperature, humidity and pressure sensor, read our guide: ESP8266 with BME280 using Arduino IDE (Pressure, Temperature, Humidity).

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:

You can use the preceding links or go directly to 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 ESP8266 MAC Address).

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

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

ESP8266 Receiver (ESP-NOW + Web Server)

The ESP8266 NodeMCU 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.

How the Code Works

First, include the necessary libraries.

The 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 ESP8266 can connect to your local network.

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.

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

Create a JSON variable called board.

Create an Async Web Server on port 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.

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.

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

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

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

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

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

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:

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.

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.

Then, add the event listener for “new_readings”.

When the ESP8266 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.

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. We also update the date and time the readings were received by calling the getDateTime() JavaScript function.


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

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

Initialize ESP-NOW.

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

Handle Requests

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

Server Event Source

Set up the event source on the server.

Finally, start the server.


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

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

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

ESP8266 Sender Circuit

The ESP8266 sender boards are connected to a BME280 sensor. Wire the sensor to the default ESP8266 I2C pins:

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

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.

How the Code Works

Start by importing the required libraries:

Set Board ID

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

Create an Adafruit_BME280 object called bme.

Receiver’s MAC Address

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

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.

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

Timer Interval

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

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

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:

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

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

Initialize BME280 Sensor

The initBME() function initializes the BME280 sensor.

Reading Temperature

The readTemperature() function reads and returns the temperature from the BME280 sensor.

Reading Humidity

The readHumidity() function reads and returns the humidity from the BME280 sensor.

Note: to learn more about getting temperature and humidity from the BME280 sensor, read: ESP8266 with BME280 using Arduino IDE (Pressure, Temperature, Humidity).

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.


Initialize the Serial Monitor.

Initialize the BME280 sensor:

Set the ESP8266 as a Wi-Fi station.

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

Initialize ESP-NOW.

Set the ESP8266 role:

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.

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.


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

Send ESP-NOW Message

Finally, send the message structure via ESP-NOW.

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

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

It should load the temperature, humidity and the last time the readings were updated on the web page for each board. Upon receiving a new packet, your web page updates automatically without refreshing the web page.

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) using the ESP8266 NodeMCU board. We have a similar project using the ESP32:

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 have a dedicated guide to Server-Sent Events:

If you like ESP8266, you might consider enrolling in our eBook “Home Automation using ESP8266“. You can also access our free ESP8266 resources here.

Thanks for reading.

This content was originally published here.