{"id":115899,"date":"2022-10-13T14:58:24","date_gmt":"2022-10-13T14:58:24","guid":{"rendered":"https:\/\/randomnerdtutorials.com\/?p=115899"},"modified":"2024-06-12T16:52:01","modified_gmt":"2024-06-12T16:52:01","slug":"esp-now-auto-pairing-esp32-esp8266","status":"publish","type":"post","link":"https:\/\/randomnerdtutorials.com\/esp-now-auto-pairing-esp32-esp8266\/","title":{"rendered":"ESP-NOW: Auto-pairing for ESP32\/ESP8266 with Bidirectional Communication and Web Server"},"content":{"rendered":"\n<p>This guide shows how to build an ESP32 web server and use ESP-NOW communication protocol simultaneously. We&#8217;ll show you how to establish a two-way communication between the master (web server) and slaves, and how to automatically add boards to the network (auto-pairing).<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full\"><img data-recalc-dims=\"1\" fetchpriority=\"high\" decoding=\"async\" width=\"1200\" height=\"675\" src=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2022\/09\/ESP-NOW-Web-Server-auto-pairing.jpg?resize=1200%2C675&#038;quality=100&#038;strip=all&#038;ssl=1\" alt=\"ESP-NOW Auto-pairing for ESP32 ESP8266 with Bidirectional Communication and Web Server\" class=\"wp-image-116327\" srcset=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2022\/09\/ESP-NOW-Web-Server-auto-pairing.jpg?w=1280&amp;quality=100&amp;strip=all&amp;ssl=1 1280w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2022\/09\/ESP-NOW-Web-Server-auto-pairing.jpg?resize=300%2C169&amp;quality=100&amp;strip=all&amp;ssl=1 300w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2022\/09\/ESP-NOW-Web-Server-auto-pairing.jpg?resize=1024%2C576&amp;quality=100&amp;strip=all&amp;ssl=1 1024w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2022\/09\/ESP-NOW-Web-Server-auto-pairing.jpg?resize=768%2C432&amp;quality=100&amp;strip=all&amp;ssl=1 768w\" sizes=\"(max-width: 1200px) 100vw, 1200px\" \/><\/figure><\/div>\n\n\n<p>This tutorial is an improvement of the following: <\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><a href=\"https:\/\/randomnerdtutorials.com\/esp32-esp-now-wi-fi-web-server\/\">ESP32: ESP-NOW Web Server Sensor Dashboard (ESP-NOW + Wi-Fi)<\/a><\/li>\n<\/ul>\n\n\n\n<p>The new version includes:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Two-way communication between the server and the slaves;<\/li>\n\n\n\n<li>Auto-pairing peers\u2014you don&#8217;t need to know any of the boards&#8217; MAC addresses. You don&#8217;t need to add peers manually. You just need to run the codes provided and the boards will be automatically added to the ESP-NOW network.<\/li>\n<\/ul>\n\n\n\n<p>The improvements were suggested by one of our readers (<strong>Jean-Claude Servaye<\/strong>). You can find the original codes on <a href=\"https:\/\/github.com\/Servayejc\" target=\"_blank\" rel=\"noreferrer noopener\">his GitHub page<\/a>.<\/p>\n\n\n\n<p>If you&#8217;re new to ESP-NOW, we recommend getting familiar with ESP-NOW concepts and functions first. Check the following getting started guides:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><a href=\"https:\/\/randomnerdtutorials.com\/esp-now-esp32-arduino-ide\/\">Getting Started with ESP-NOW (ESP32 with Arduino IDE)<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/randomnerdtutorials.com\/esp-now-esp8266-nodemcu-arduino-ide\/\">Getting Started with ESP-NOW (ESP8266 NodeMCU with Arduino IDE)<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/randomnerdtutorials.com\/esp-now-two-way-communication-esp32\/\">ESP-NOW Two-Way Communication Between ESP32 Boards<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/randomnerdtutorials.com\/esp-now-two-way-communication-esp8266-nodemcu\/\">ESP-NOW Two-Way Communication Between ESP8266 NodeMCU Boards<\/a><\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">Using ESP-NOW and Wi-Fi (Web Server) Simultaneously<\/h2>\n\n\n\n<p>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:<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full\"><img data-recalc-dims=\"1\" decoding=\"async\" width=\"902\" height=\"865\" src=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2020\/08\/ESP-NOW-With-Wi-Fi-Web-Server-How-It-Works.png?resize=902%2C865&#038;quality=100&#038;strip=all&#038;ssl=1\" alt=\"ESP-NOW and Wi-Fi simultaneously. The web server and the sender boards must be on the same wi-fi channel.\" class=\"wp-image-98801\" srcset=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2020\/08\/ESP-NOW-With-Wi-Fi-Web-Server-How-It-Works.png?w=902&amp;quality=100&amp;strip=all&amp;ssl=1 902w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2020\/08\/ESP-NOW-With-Wi-Fi-Web-Server-How-It-Works.png?resize=300%2C288&amp;quality=100&amp;strip=all&amp;ssl=1 300w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2020\/08\/ESP-NOW-With-Wi-Fi-Web-Server-How-It-Works.png?resize=768%2C736&amp;quality=100&amp;strip=all&amp;ssl=1 768w\" sizes=\"(max-width: 902px) 100vw, 902px\" \/><\/figure><\/div>\n\n\n<ul class=\"wp-block-list\">\n<li>The ESP32\/ESP8266 sender boards <strong>must use the same Wi-Fi channel<\/strong> as the receiver board (server).<\/li>\n\n\n\n<li>The Wi-Fi channel of the receiver board is automatically assigned by your Wi-Fi router.<\/li>\n\n\n\n<li>The Wi-Fi mode of the receiver board must be access point and station (<span class=\"rnthl rntliteral\">WIFI_AP_STA<\/span>).<\/li>\n\n\n\n<li>You can set up the same Wi-Fi channel manually, but we&#8217;ll do it automatically. The sender will try different Wi-Fi channels until it gets a response from the server.<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">Project Overview<\/h2>\n\n\n\n<p>Here&#8217;s a quick overview of the example we&#8217;ll build:<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full\"><img data-recalc-dims=\"1\" decoding=\"async\" width=\"750\" height=\"421\" src=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2022\/09\/ESP-NOW-Web-Server-Example.png?resize=750%2C421&#038;quality=100&#038;strip=all&#038;ssl=1\" alt=\"ESP-NOW Web Server Example\" class=\"wp-image-115926\" srcset=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2022\/09\/ESP-NOW-Web-Server-Example.png?w=750&amp;quality=100&amp;strip=all&amp;ssl=1 750w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2022\/09\/ESP-NOW-Web-Server-Example.png?resize=300%2C168&amp;quality=100&amp;strip=all&amp;ssl=1 300w\" sizes=\"(max-width: 750px) 100vw, 750px\" \/><\/figure><\/div>\n\n\n<ul class=\"wp-block-list\">\n<li>There are two ESP sender boards (ESP32 or ESP8266) that send readings<strong>*<\/strong> via ESP-NOW to one ESP32 receiver board (<a href=\"https:\/\/randomnerdtutorials.com\/esp-now-many-to-one-esp32\/\">ESP-NOW many to one configuration<\/a>);<\/li>\n\n\n\n<li>The receiver board receives the packets and displays the readings on a web page;<\/li>\n\n\n\n<li>The web page is updated automatically every time it receives a new reading using Server-Sent Events (SSE);<\/li>\n\n\n\n<li>The receiver also sends data to the sender\u2014this is to illustrate how to establish bidirectional communication. As an example, we&#8217;ll send arbitrary values, but you can easily replace them with sensor readings or any other data like threshold values, or commands to turn on\/off GPIOs.<\/li>\n<\/ul>\n\n\n\n<p><strong>*<\/strong>we&#8217;ll send arbitrary temperature and humidity values\u2014we won&#8217;t use an actual sensor. After testing the project and checking that everything is working as expected you can use a sensor of your choice (it doesn&#8217;t have to be temperature or humidity).<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Auto-Pairing<\/h2>\n\n\n\n<p>Here&#8217;s how the auto-pairing with peers (sender(server)\/slave boards) works:<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"750\" height=\"428\" src=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2022\/09\/ESP-NOW-auto-pairing-diagram.png?resize=750%2C428&#038;quality=100&#038;strip=all&#038;ssl=1\" alt=\"ESP-NOW Auto pairing diagram\" class=\"wp-image-116234\" srcset=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2022\/09\/ESP-NOW-auto-pairing-diagram.png?w=750&amp;quality=100&amp;strip=all&amp;ssl=1 750w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2022\/09\/ESP-NOW-auto-pairing-diagram.png?resize=300%2C171&amp;quality=100&amp;strip=all&amp;ssl=1 300w\" sizes=\"(max-width: 750px) 100vw, 750px\" \/><\/figure><\/div>\n\n\n<ul class=\"wp-block-list\">\n<li>The peer sends a message of type <span class=\"rnthl rntliteral\">PAIRING<\/span> to the server (<strong>1<\/strong>) using the broadcast MAC address <span class=\"rnthl rntliteral\">ff:ff:ff:ff:ff:ff<\/span>. When you send data to this MAC address, all ESP-NOW devices receive the message. For the server to receive the message, they need to communicate on the same Wi-Fi channel.<\/li>\n\n\n\n<li>If the peer doesn&#8217;t receive a message from the server, it tries to send the same message on a different Wi-Fi channel. It repeats the process until it gets a message from the server.<\/li>\n\n\n\n<li>The server receives the message and the address of the peer (<strong>2<\/strong>).<\/li>\n\n\n\n<li>The server adds the address of the peer to his peer list (<strong>3<\/strong>).<\/li>\n\n\n\n<li>The server replies to the peer with a message of type <span class=\"rnthl rntliteral\">PAIRING<\/span> with its information (MAC address and channel) (<strong>4<\/strong>).<\/li>\n\n\n\n<li>The peer receives the message and the <span class=\"rnthl rntliteral\">WiFi.macAddress<\/span> of the server (<strong>5<\/strong>).<\/li>\n\n\n\n<li>The peer adds the received address of the server to his peer list (<strong>6<\/strong>).<\/li>\n\n\n\n<li>The peer tries to send a message to the server address but it fails to transmit*.\n<ol class=\"wp-block-list\">\n<li>The peer adds the <span class=\"rnthl rntliteral\">WiFi.softAPmacAddress<\/span> of the server to his peer list.<\/li>\n\n\n\n<li>The peer sends a message to the server <span class=\"rnthl rntliteral\">WiFi.softAPmacAddress<\/span>.<\/li>\n<\/ol>\n<\/li>\n\n\n\n<li>The server receives the message from the peer.<\/li>\n\n\n\n<li>They can now communicate bidirectionally (<strong>6<\/strong>).<\/li>\n<\/ul>\n\n\n\n<p>*ESP32 in <span class=\"rnthl rntliteral\">WIFI_AP_STA<\/span> mode responds with its <span class=\"rnthl rntliteral\">WiFi.macAddress<\/span> but it uses <span class=\"rnthl rntliteral\">WiFi.softAPmacAddress<\/span> to receive from ESP8266 peer. <\/p>\n\n\n\n<p><span class=\"rnthl rntliteral\">WiFi.softAPmacAddress<\/span> is created from <span class=\"rnthl rntliteral\">WiFi.macAddress<\/span> by adding 1 to the last byte\u2014<a href=\"https:\/\/docs.espressif.com\/projects\/esp-idf\/en\/v3.1.7\/api-reference\/system\/base_mac_address.html\" title=\"check the documentation\">check the documentation<\/a>.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Prerequisites<\/h2>\n\n\n\n<p>Before proceeding with this project, make sure you check the following prerequisites.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Arduino IDE<\/h3>\n\n\n\n<p>We\u2019ll program the ESP32 and ESP8266 boards using Arduino IDE, so before proceeding with this tutorial, make sure you have the ESP32 and ESP8266 boards installed in your Arduino IDE.<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><a href=\"https:\/\/randomnerdtutorials.com\/installing-the-esp32-board-in-arduino-ide-windows-instructions\/\">Installing ESP32 Board in Arduino IDE (Windows, Mac OS X, and Linux)<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/randomnerdtutorials.com\/how-to-install-esp8266-board-arduino-ide\/\">Installing ESP8266 Board in Arduino IDE (Windows, Mac OS X, Linux)<\/a><\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">Async Web Server Libraries<\/h3>\n\n\n\n<p>To build the web server you need to install the following libraries:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><a href=\"https:\/\/github.com\/me-no-dev\/ESPAsyncWebServer\" target=\"_blank\" rel=\"noreferrer noopener\">ESPAsyncWebServer<\/a>&nbsp;<\/li>\n\n\n\n<li><a href=\"https:\/\/github.com\/me-no-dev\/AsyncTCP\" target=\"_blank\" rel=\"noreferrer noopener\">AsyncTCP<\/a><\/li>\n<\/ul>\n\n\n\n<p>These libraries aren\u2019t 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&nbsp;<strong>Sketch&nbsp;<\/strong>&gt;&nbsp;<strong>Include Library<\/strong>&nbsp;&gt;&nbsp;<strong>Add .zip Library<\/strong>&nbsp;and select the libraries you\u2019ve just downloaded.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Arduino_JSON Library<\/h3>\n\n\n\n<p>Our examples will use the <a href=\"https:\/\/arduinojson.org\/\" target=\"_blank\" rel=\"noreferrer noopener\">ArduinoJSON library by Benoit Blanchon<\/a> version <strong>7.0.4<\/strong>. You can install this library in the Arduino IDE Library Manager. Just go to&nbsp;<strong>Sketch&nbsp;<\/strong>&gt;&nbsp;<strong>Include Library<\/strong>&nbsp;&gt;&nbsp;<strong>Manage Libraries<\/strong>&nbsp;and search for the library name <span class=\"rnthl rntliteral\">ArduinoJSON<\/span> as follows:<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"749\" height=\"514\" src=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2024\/06\/install-arduinojsonlibrary-arduino-ide-2.png?resize=749%2C514&#038;quality=100&#038;strip=all&#038;ssl=1\" alt=\"Install ArduinoJson library arduino ide 2\" class=\"wp-image-158855\" srcset=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2024\/06\/install-arduinojsonlibrary-arduino-ide-2.png?w=749&amp;quality=100&amp;strip=all&amp;ssl=1 749w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2024\/06\/install-arduinojsonlibrary-arduino-ide-2.png?resize=300%2C206&amp;quality=100&amp;strip=all&amp;ssl=1 300w\" sizes=\"(max-width: 749px) 100vw, 749px\" \/><\/figure><\/div>\n\n\n<h2 class=\"wp-block-heading\">Parts Required<\/h2>\n\n\n\n<p>To test this project, you need at least three ESP boards. One ESP32 board to act as a server and two sender\/slave ESP boards that can be ESP32 or ESP8266.<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><a href=\"https:\/\/makeradvisor.com\/tools\/esp32-dev-board-wi-fi-bluetooth\/\" target=\"_blank\" rel=\"noreferrer noopener\">ESP32<\/a>&nbsp;(read&nbsp;<a href=\"https:\/\/makeradvisor.com\/esp32-development-boards-review-comparison\/\" target=\"_blank\" rel=\"noreferrer noopener\">Best ESP32 development boards<\/a>)<\/li>\n\n\n\n<li><a href=\"https:\/\/makeradvisor.com\/tools\/esp8266-esp-12e-nodemcu-wi-fi-development-board\/\" target=\"_blank\" rel=\"noreferrer noopener\">ESP8266&nbsp;<\/a>(read&nbsp;<a href=\"https:\/\/makeradvisor.com\/tools\/esp8266-esp-12e-nodemcu-wi-fi-development-board\/\" target=\"_blank\" rel=\"noreferrer noopener\">Best ESP8266 development boards<\/a>)<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">ESP32 Server<\/h2>\n\n\n\n<p>Here are the server features:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Pairs automatically with peers (other ESP-NOW boards);<\/li>\n\n\n\n<li>Receives packets from peers;<\/li>\n\n\n\n<li>Hosts a web server to display the latest received packets;<\/li>\n\n\n\n<li>Also sends data back to the other boards (bidirectional communication with peers).<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">ESP32 Server Code<\/h3>\n\n\n\n<p>Upload the following code to your ESP32 board. This can receive data from multiple boards. However, the web page is just prepared to display data from two boards. You can easily modify the web page to accommodate more boards.<\/p>\n\n\n<pre style=\"max-height: 40em; margin-bottom: 20px;\"><code class=\"language-c\">\/*\r\n  Rui Santos &amp; Sara Santos - Random Nerd Tutorials\r\n  Complete project details at https:\/\/RandomNerdTutorials.com\/esp-now-auto-pairing-esp32-esp8266\/\r\n  Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files.\r\n  The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.\r\n  Based on JC Servaye example: https:\/\/github.com\/Servayejc\/esp_now_web_server\/\r\n*\/\r\n#include &lt;esp_now.h&gt;\r\n#include &lt;WiFi.h&gt;\r\n#include &lt;esp_wifi.h&gt;\r\n#include &quot;ESPAsyncWebServer.h&quot;\r\n#include &quot;AsyncTCP.h&quot;\r\n#include &lt;ArduinoJson.h&gt;\r\n\r\n\/\/ Replace with your network credentials (STATION)\r\nconst char* ssid = &quot;REPLACE_WITH_YOUR_SSID&quot;;\r\nconst char* password = &quot;REPLACE_WITH_YOUR_PASSWORD&quot;;\r\n\r\nesp_now_peer_info_t slave;\r\nint chan; \r\n\r\nenum MessageType {PAIRING, DATA,};\r\nMessageType messageType;\r\n\r\nint counter = 0;\r\n\r\nuint8_t clientMacAddress[6];\r\n\r\n\/\/ Structure example to receive data\r\n\/\/ Must match the sender structure\r\ntypedef struct struct_message {\r\n  uint8_t msgType;\r\n  uint8_t id;\r\n  float temp;\r\n  float hum;\r\n  unsigned int readingId;\r\n} struct_message;\r\n\r\ntypedef struct struct_pairing {       \/\/ new structure for pairing\r\n    uint8_t msgType;\r\n    uint8_t id;\r\n    uint8_t macAddr[6];\r\n    uint8_t channel;\r\n} struct_pairing;\r\n\r\nstruct_message incomingReadings;\r\nstruct_message outgoingSetpoints;\r\nstruct_pairing pairingData;\r\n\r\nAsyncWebServer server(80);\r\nAsyncEventSource events(&quot;\/events&quot;);\r\n\r\nconst char index_html[] PROGMEM = R&quot;rawliteral(\r\n&lt;!DOCTYPE HTML&gt;&lt;html&gt;\r\n&lt;head&gt;\r\n  &lt;title&gt;ESP-NOW DASHBOARD&lt;\/title&gt;\r\n  &lt;meta name=&quot;viewport&quot; content=&quot;width=device-width, initial-scale=1&quot;&gt;\r\n  &lt;link rel=&quot;stylesheet&quot; href=&quot;https:\/\/use.fontawesome.com\/releases\/v5.7.2\/css\/all.css&quot; integrity=&quot;sha384-fnmOCqbTlWIlj8LyTjo7mOUStjsKC4pOpQbqyi7RrhN7udi9RwhKkMHpvLbHG9Sr&quot; crossorigin=&quot;anonymous&quot;&gt;\r\n  &lt;link rel=&quot;icon&quot; href=&quot;data:,&quot;&gt;\r\n  &lt;style&gt;\r\n    html {font-family: Arial; display: inline-block; text-align: center;}\r\n    p {  font-size: 1.2rem;}\r\n    body {  margin: 0;}\r\n    .topnav { overflow: hidden; background-color: #2f4468; color: white; font-size: 1.7rem; }\r\n    .content { padding: 20px; }\r\n    .card { background-color: white; box-shadow: 2px 2px 12px 1px rgba(140,140,140,.5); }\r\n    .cards { max-width: 700px; margin: 0 auto; display: grid; grid-gap: 2rem; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); }\r\n    .reading { font-size: 2.8rem; }\r\n    .packet { color: #bebebe; }\r\n    .card.temperature { color: #fd7e14; }\r\n    .card.humidity { color: #1b78e2; }\r\n  &lt;\/style&gt;\r\n&lt;\/head&gt;\r\n&lt;body&gt;\r\n  &lt;div class=&quot;topnav&quot;&gt;\r\n    &lt;h3&gt;ESP-NOW DASHBOARD&lt;\/h3&gt;\r\n  &lt;\/div&gt;\r\n  &lt;div class=&quot;content&quot;&gt;\r\n    &lt;div class=&quot;cards&quot;&gt;\r\n      &lt;div class=&quot;card temperature&quot;&gt;\r\n        &lt;h4&gt;&lt;i class=&quot;fas fa-thermometer-half&quot;&gt;&lt;\/i&gt; BOARD #1 - TEMPERATURE&lt;\/h4&gt;&lt;p&gt;&lt;span class=&quot;reading&quot;&gt;&lt;span id=&quot;t1&quot;&gt;&lt;\/span&gt; &amp;deg;C&lt;\/span&gt;&lt;\/p&gt;&lt;p class=&quot;packet&quot;&gt;Reading ID: &lt;span id=&quot;rt1&quot;&gt;&lt;\/span&gt;&lt;\/p&gt;\r\n      &lt;\/div&gt;\r\n      &lt;div class=&quot;card humidity&quot;&gt;\r\n        &lt;h4&gt;&lt;i class=&quot;fas fa-tint&quot;&gt;&lt;\/i&gt; BOARD #1 - HUMIDITY&lt;\/h4&gt;&lt;p&gt;&lt;span class=&quot;reading&quot;&gt;&lt;span id=&quot;h1&quot;&gt;&lt;\/span&gt; &amp;percnt;&lt;\/span&gt;&lt;\/p&gt;&lt;p class=&quot;packet&quot;&gt;Reading ID: &lt;span id=&quot;rh1&quot;&gt;&lt;\/span&gt;&lt;\/p&gt;\r\n      &lt;\/div&gt;\r\n      &lt;div class=&quot;card temperature&quot;&gt;\r\n        &lt;h4&gt;&lt;i class=&quot;fas fa-thermometer-half&quot;&gt;&lt;\/i&gt; BOARD #2 - TEMPERATURE&lt;\/h4&gt;&lt;p&gt;&lt;span class=&quot;reading&quot;&gt;&lt;span id=&quot;t2&quot;&gt;&lt;\/span&gt; &amp;deg;C&lt;\/span&gt;&lt;\/p&gt;&lt;p class=&quot;packet&quot;&gt;Reading ID: &lt;span id=&quot;rt2&quot;&gt;&lt;\/span&gt;&lt;\/p&gt;\r\n      &lt;\/div&gt;\r\n      &lt;div class=&quot;card humidity&quot;&gt;\r\n        &lt;h4&gt;&lt;i class=&quot;fas fa-tint&quot;&gt;&lt;\/i&gt; BOARD #2 - HUMIDITY&lt;\/h4&gt;&lt;p&gt;&lt;span class=&quot;reading&quot;&gt;&lt;span id=&quot;h2&quot;&gt;&lt;\/span&gt; &amp;percnt;&lt;\/span&gt;&lt;\/p&gt;&lt;p class=&quot;packet&quot;&gt;Reading ID: &lt;span id=&quot;rh2&quot;&gt;&lt;\/span&gt;&lt;\/p&gt;\r\n      &lt;\/div&gt;\r\n    &lt;\/div&gt;\r\n  &lt;\/div&gt;\r\n&lt;script&gt;\r\nif (!!window.EventSource) {\r\n var source = new EventSource('\/events');\r\n \r\n source.addEventListener('open', function(e) {\r\n  console.log(&quot;Events Connected&quot;);\r\n }, false);\r\n source.addEventListener('error', function(e) {\r\n  if (e.target.readyState != EventSource.OPEN) {\r\n    console.log(&quot;Events Disconnected&quot;);\r\n  }\r\n }, false);\r\n \r\n source.addEventListener('message', function(e) {\r\n  console.log(&quot;message&quot;, e.data);\r\n }, false);\r\n \r\n source.addEventListener('new_readings', function(e) {\r\n  console.log(&quot;new_readings&quot;, e.data);\r\n  var obj = JSON.parse(e.data);\r\n  document.getElementById(&quot;t&quot;+obj.id).innerHTML = obj.temperature.toFixed(2);\r\n  document.getElementById(&quot;h&quot;+obj.id).innerHTML = obj.humidity.toFixed(2);\r\n  document.getElementById(&quot;rt&quot;+obj.id).innerHTML = obj.readingId;\r\n  document.getElementById(&quot;rh&quot;+obj.id).innerHTML = obj.readingId;\r\n }, false);\r\n}\r\n&lt;\/script&gt;\r\n&lt;\/body&gt;\r\n&lt;\/html&gt;)rawliteral&quot;;\r\n\r\nvoid readMacAddress(){\r\n  uint8_t baseMac[6];\r\n  esp_err_t ret = esp_wifi_get_mac(WIFI_IF_STA, baseMac);\r\n  if (ret == ESP_OK) {\r\n    Serial.printf(&quot;%02x:%02x:%02x:%02x:%02x:%02x\\n&quot;,\r\n                  baseMac[0], baseMac[1], baseMac[2],\r\n                  baseMac[3], baseMac[4], baseMac[5]);\r\n  } else {\r\n    Serial.println(&quot;Failed to read MAC address&quot;);\r\n  }\r\n}\r\n\r\nvoid readDataToSend() {\r\n  outgoingSetpoints.msgType = DATA;\r\n  outgoingSetpoints.id = 0;\r\n  outgoingSetpoints.temp = random(0, 40);\r\n  outgoingSetpoints.hum = random(0, 100);\r\n  outgoingSetpoints.readingId = counter++;\r\n}\r\n\r\n\/\/ ---------------------------- esp_ now -------------------------\r\nvoid printMAC(const uint8_t * mac_addr){\r\n  char macStr[18];\r\n  snprintf(macStr, sizeof(macStr), &quot;%02x:%02x:%02x:%02x:%02x:%02x&quot;,\r\n           mac_addr[0], mac_addr[1], mac_addr[2], mac_addr[3], mac_addr[4], mac_addr[5]);\r\n  Serial.print(macStr);\r\n}\r\n\r\nbool addPeer(const uint8_t *peer_addr) {      \/\/ add pairing\r\n  memset(&amp;slave, 0, sizeof(slave));\r\n  const esp_now_peer_info_t *peer = &amp;slave;\r\n  memcpy(slave.peer_addr, peer_addr, 6);\r\n  \r\n  slave.channel = chan; \/\/ pick a channel\r\n  slave.encrypt = 0; \/\/ no encryption\r\n  \/\/ check if the peer exists\r\n  bool exists = esp_now_is_peer_exist(slave.peer_addr);\r\n  if (exists) {\r\n    \/\/ Slave already paired.\r\n    Serial.println(&quot;Already Paired&quot;);\r\n    return true;\r\n  }\r\n  else {\r\n    esp_err_t addStatus = esp_now_add_peer(peer);\r\n    if (addStatus == ESP_OK) {\r\n      \/\/ Pair success\r\n      Serial.println(&quot;Pair success&quot;);\r\n      return true;\r\n    }\r\n    else \r\n    {\r\n      Serial.println(&quot;Pair failed&quot;);\r\n      return false;\r\n    }\r\n  }\r\n} \r\n\r\n\/\/ callback when data is sent\r\nvoid OnDataSent(const uint8_t *mac_addr, esp_now_send_status_t status) {\r\n  Serial.print(&quot;Last Packet Send Status: &quot;);\r\n  Serial.print(status == ESP_NOW_SEND_SUCCESS ? &quot;Delivery Success to &quot; : &quot;Delivery Fail to &quot;);\r\n  printMAC(mac_addr);\r\n  Serial.println();\r\n}\r\n\r\nvoid OnDataRecv(const uint8_t * mac_addr, const uint8_t *incomingData, int len) { \r\n  Serial.print(len);\r\n  Serial.println(&quot; bytes of new data received.&quot;);\r\n  StaticJsonDocument&lt;1000&gt; root;\r\n  String payload;\r\n  uint8_t type = incomingData[0];       \/\/ first message byte is the type of message \r\n  switch (type) {\r\n  case DATA :                           \/\/ the message is data type\r\n    memcpy(&amp;incomingReadings, incomingData, sizeof(incomingReadings));\r\n    \/\/ create a JSON document with received data and send it by event to the web page\r\n    root[&quot;id&quot;] = incomingReadings.id;\r\n    root[&quot;temperature&quot;] = incomingReadings.temp;\r\n    root[&quot;humidity&quot;] = incomingReadings.hum;\r\n    root[&quot;readingId&quot;] = String(incomingReadings.readingId);\r\n    serializeJson(root, payload);\r\n    Serial.print(&quot;event send :&quot;);\r\n    serializeJson(root, Serial);\r\n    events.send(payload.c_str(), &quot;new_readings&quot;, millis());\r\n    Serial.println();\r\n    break;\r\n  \r\n  case PAIRING:                            \/\/ the message is a pairing request \r\n    memcpy(&amp;pairingData, incomingData, sizeof(pairingData));\r\n    Serial.println(pairingData.msgType);\r\n    Serial.println(pairingData.id);\r\n    Serial.print(&quot;Pairing request from MAC Address: &quot;);\r\n    printMAC(pairingData.macAddr);\r\n    Serial.print(&quot; on channel &quot;);\r\n    Serial.println(pairingData.channel);\r\n\r\n    clientMacAddress[0] = pairingData.macAddr[0];\r\n    clientMacAddress[1] = pairingData.macAddr[1];\r\n    clientMacAddress[2] = pairingData.macAddr[2];\r\n    clientMacAddress[3] = pairingData.macAddr[3];\r\n    clientMacAddress[4] = pairingData.macAddr[4];\r\n    clientMacAddress[5] = pairingData.macAddr[5];\r\n\r\n    if (pairingData.id &gt; 0) {     \/\/ do not replay to server itself\r\n      if (pairingData.msgType == PAIRING) { \r\n        pairingData.id = 0;       \/\/ 0 is server\r\n        \/\/ Server is in AP_STA mode: peers need to send data to server soft AP MAC address \r\n        WiFi.softAPmacAddress(pairingData.macAddr);\r\n        Serial.print(&quot;Pairing MAC Address: &quot;);\r\n        printMAC(clientMacAddress);\r\n        pairingData.channel = chan;\r\n        Serial.println(&quot; send response&quot;);\r\n        esp_err_t result = esp_now_send(clientMacAddress, (uint8_t *) &amp;pairingData, sizeof(pairingData));\r\n        addPeer(clientMacAddress);\r\n      }  \r\n    }  \r\n    break; \r\n  }\r\n}\r\n\r\nvoid initESP_NOW(){\r\n    \/\/ Init ESP-NOW\r\n    if (esp_now_init() != ESP_OK) {\r\n      Serial.println(&quot;Error initializing ESP-NOW&quot;);\r\n      return;\r\n    }\r\n    esp_now_register_send_cb(OnDataSent);\r\n    esp_now_register_recv_cb(esp_now_recv_cb_t(OnDataRecv));\r\n} \r\n\r\nvoid setup() {\r\n  \/\/ Initialize Serial Monitor\r\n  Serial.begin(115200);\r\n\r\n  WiFi.mode(WIFI_STA);\r\n  WiFi.STA.begin();\r\n  Serial.print(&quot;Server MAC Address: &quot;);\r\n  readMacAddress();\r\n\r\n  \/\/ Set the device as a Station and Soft Access Point simultaneously\r\n  WiFi.mode(WIFI_AP_STA);\r\n  \/\/ Set device as a Wi-Fi Station\r\n  WiFi.begin(ssid, password);\r\n  while (WiFi.status() != WL_CONNECTED) {\r\n    delay(1000);\r\n    Serial.println(&quot;Setting as a Wi-Fi Station..&quot;);\r\n  }\r\n\r\n  Serial.print(&quot;Server SOFT AP MAC Address:  &quot;);\r\n  Serial.println(WiFi.softAPmacAddress());\r\n\r\n  chan = WiFi.channel();\r\n  Serial.print(&quot;Station IP Address: &quot;);\r\n  Serial.println(WiFi.localIP());\r\n  Serial.print(&quot;Wi-Fi Channel: &quot;);\r\n  Serial.println(WiFi.channel());\r\n\r\n  initESP_NOW();\r\n  \r\n  \/\/ Start Web server\r\n  server.on(&quot;\/&quot;, HTTP_GET, [](AsyncWebServerRequest *request){\r\n    request-&gt;send(200, &quot;text\/html&quot;, index_html);\r\n  });\r\n  \r\n  \/\/ Events \r\n  events.onConnect([](AsyncEventSourceClient *client){\r\n    if(client-&gt;lastId()){\r\n      Serial.printf(&quot;Client reconnected! Last message ID that it got is: %u\\n&quot;, client-&gt;lastId());\r\n    }\r\n    \/\/ send event with message &quot;hello!&quot;, id current millis\r\n    \/\/ and set reconnect delay to 1 second\r\n    client-&gt;send(&quot;hello!&quot;, NULL, millis(), 10000);\r\n  });\r\n  server.addHandler(&amp;events);\r\n  \/\/ start server\r\n  server.begin();\r\n}\r\n\r\nvoid loop() {\r\n  static unsigned long lastEventTime = millis();\r\n  static const unsigned long EVENT_INTERVAL_MS = 5000;\r\n  if ((millis() - lastEventTime) &gt; EVENT_INTERVAL_MS) {\r\n    events.send(&quot;ping&quot;, NULL, millis());\r\n    lastEventTime = millis();\r\n    readDataToSend();\r\n    esp_now_send(NULL, (uint8_t *) &amp;outgoingSetpoints, sizeof(outgoingSetpoints));\r\n  }\r\n}\r\n<\/code><\/pre>\n\t<p style=\"text-align:center\"><a class=\"rntwhite\" href=\"https:\/\/github.com\/RuiSantosdotme\/Random-Nerd-Tutorials\/raw\/master\/Projects\/ESP\/ESP_NOW\/ESP_NOW_Auto_Pairing\/ESP32_Server.ino\" target=\"_blank\">View raw code<\/a><\/p>\n\n\n\n<h3 class=\"wp-block-heading\">How the Code Works<\/h3>\n\n\n\n<p>We already explained how the server code works in great detail in a <a href=\"https:\/\/randomnerdtutorials.com\/esp32-esp-now-wi-fi-web-server\/\">previous project<\/a>. So, we&#8217;ll just take a look at the relevant parts for auto-pairing.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\">Message Types<\/h4>\n\n\n\n<p>The server and senders can exchange two types of messages: messages with pairing data with MAC address, channel, and board id, and messages with the actual data like sensor readings.<\/p>\n\n\n\n<p>So, we create an enumerated type that holds the possible incoming message types (<span class=\"rnthl rntliteral\">PAIRING<\/span> and <span class=\"rnthl rntliteral\">DATA<\/span>).<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>enum MessageType {PAIRING, DATA,};<\/code><\/pre>\n\n\n\n<p>&#8220;<em>An enumerated type is a data type (usually user-defined) consisting of a set of named constants called enumerators. The act of creating an enumerated type defines an enumeration. When an identifier such as a variable is declared having an enumerated type, the variable can be assigned any of the enumerators as a value<\/em>&#8220;. Source: https:\/\/playground.arduino.cc\/Code\/Enum\/<\/p>\n\n\n\n<p>After that, we create a variable of that type we&#8217;ve just created called <span class=\"rnthl rntliteral\">messageType<\/span>. Remember that this variable can only have two possible values: <span class=\"rnthl rntliteral\">PAIRING<\/span> or <span class=\"rnthl rntliteral\">DATA<\/span>.<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>MessageType messageType;<\/code><\/pre>\n\n\n\n<h4 class=\"wp-block-heading\">Data Structure<\/h4>\n\n\n\n<p>Create a structure that will contain the data we&#8217;ll receive. We called this structure <span class=\"rnthl rntliteral\">struct_message<\/span> and it contains the message type (so that we know if we received a message with data or with peer info), board ID, temperature and humidity readings, and the reading ID.<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>typedef struct struct_message {\n  uint8_t msgType;\n  uint8_t id;\n  float temp;\n  float hum;\n  unsigned int readingId;\n} struct_message;<\/code><\/pre>\n\n\n\n<p>We also need another structure to contain the peer information for pairing the peer. We call this structure <span class=\"rnthl rntliteral\">struct_pairing<\/span>. This structure will contain the message type, board id, mac address of the sender board, and Wi-Fi channel.<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>typedef struct struct_pairing {       \/\/ new structure for pairing\n    uint8_t msgType;\n    uint8_t id;\n    uint8_t macAddr&#091;6];\n    uint8_t channel;\n} struct_pairing;<\/code><\/pre>\n\n\n\n<p>We create two variables of type <span class=\"rnthl rntliteral\">struct_message<\/span>, one called <span class=\"rnthl rntliteral\">incomingReadings<\/span> that will store the readings coming from the slaves, and another called <span class=\"rnthl rntliteral\">outgoingSetpoints<\/span> that will hold the data to send to the slaves.<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>struct_message incomingReadings;\nstruct_message outgoingSetpoints;<\/code><\/pre>\n\n\n\n<p>We also create a variable of type <span class=\"rnthl rntliteral\">struct_pairing<\/span> to hold the peer information.<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>struct_pairing pairingData;<\/code><\/pre>\n\n\n\n<h4 class=\"wp-block-heading\">readDataToSend() Function<\/h4>\n\n\n\n<p>The <span class=\"rnthl rntliteral\">readDataToSend()<\/span> should be used to get data from whichever sensor you&#8217;re using and put them on the associated structure to be sent to the slave boards.<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>void readDataToSend() {\n  outgoingSetpoints.msgType = DATA;\n  outgoingSetpoints.id = 0;\n  outgoingSetpoints.temp = random(0, 40);\n  outgoingSetpoints.hum = random(0, 100);\n  outgoingSetpoints.readingId = counter++;\n}<\/code><\/pre>\n\n\n\n<p>The <span class=\"rnthl rntliteral\">msgType<\/span> should be <span class=\"rnthl rntliteral\">DATA<\/span>. The <span class=\"rnthl rntliteral\">id<\/span> corresponds to the board id (we&#8217;re setting the server board ID to 0, the others boards should have id=1, 2, 3, and so on). Finally, <span class=\"rnthl rntliteral\">temp<\/span> and <span class=\"rnthl rntliteral\">hum<\/span> hold the sensor readings. In this case, we&#8217;re setting them to random values. You should replace that with the correct functions to get data from your sensor. Every time we send a new set of readings, we increase the <span class=\"rnthl rntliteral\">counter<\/span> variable.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\">Adding a Peer<\/h4>\n\n\n\n<p>We create a function called <span class=\"rnthl rntliteral\">addPeer()<\/span> that will return a boolean variable (either <span class=\"rnthl rntliteral\">true<\/span> or <span class=\"rnthl rntliteral\">false<\/span>) that indicates whether the pairing process was successful or not. This function tries to add peers. It will be called later when the board receives a message of type <span class=\"rnthl rntliteral\">PAIRING<\/span>. If the peer is already on the list of peers, it returns <span class=\"rnthl rntliteral\">true<\/span>. It also returns <span class=\"rnthl rntliteral\">true<\/span> if the peer is successfully added. It returns <span class=\"rnthl rntliteral\">false<\/span>, if it fails to add the peer to the list.<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>bool addPeer(const uint8_t *peer_addr) {      \/\/ add pairing\n  memset(&amp;slave, 0, sizeof(slave));\n  const esp_now_peer_info_t *peer = &amp;slave;\n  memcpy(slave.peer_addr, peer_addr, 6);\n  \n  slave.channel = chan; \/\/ pick a channel\n  slave.encrypt = 0; \/\/ no encryption\n  \/\/ check if the peer exists\n  bool exists = esp_now_is_peer_exist(slave.peer_addr);\n  if (exists) {\n    \/\/ Slave already paired.\n    Serial.println(\"Already Paired\");\n    return true;\n  }\n  else {\n    esp_err_t addStatus = esp_now_add_peer(peer);\n    if (addStatus == ESP_OK) {\n      \/\/ Pair success\n      Serial.println(\"Pair success\");\n      return true;\n    }\n    else \n    {\n      Serial.println(\"Pair failed\");\n      return false;\n    }\n  }\n} <\/code><\/pre>\n\n\n\n<h4 class=\"wp-block-heading\">Receiving and Handling ESP-NOW Messages<\/h4>\n\n\n\n<p>The <span class=\"rnthl rntliteral\">OnDataRecv()<\/span> function will be executed when you receive a new ESP-NOW packet.<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>void OnDataRecv(const uint8_t * mac_addr, const uint8_t *incomingData, int len) {<\/code><\/pre>\n\n\n\n<p>Inside that function, print the length of the message.<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>Serial.print(len);\nSerial.print(\" bytes of data received from : \");<\/code><\/pre>\n\n\n\n<p>Previously, we&#8217;ve seen that we can receive two types of messages: <span class=\"rnthl rntliteral\">PAIRING<\/span> and <span class=\"rnthl rntliteral\">DATA<\/span>. So, we must handle the message content differently depending on the type of message. We can get the type of message as follows:<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>uint8_t type = incomingData&#091;0];       \/\/ first message byte is the type of message<\/code><\/pre>\n\n\n\n<p>Then, we&#8217;ll run different codes depending if the message is of type <span class=\"rnthl rntliteral\">DATA<\/span> or <span class=\"rnthl rntliteral\">PAIRING<\/span>.<\/p>\n\n\n\n<p>If it is of type <span class=\"rnthl rntliteral\">DATA<\/span>, copy the information in the <span class=\"rnthl rntliteral\">incomingData<\/span> variable into the <span class=\"rnthl rntliteral\">incomingReadings<\/span> structure variable.<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>memcpy(&amp;incomingReadings, incomingData, sizeof(incomingReadings));<\/code><\/pre>\n\n\n\n<p>Then, create a JSON document with the received information (<span class=\"rnthl rntliteral\">root<\/span>):<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>\/\/ create a JSON document with received data and send it by event to the web page\nroot&#091;\"id\"] = incomingReadings.id;\nroot&#091;\"temperature\"] = incomingReadings.temp;\nroot&#091;\"humidity\"] = incomingReadings.hum;\nroot&#091;\"readingId\"] = String(incomingReadings.readingId);<\/code><\/pre>\n\n\n\n<p>Convert the JSON document to a string (<span class=\"rnthl rntliteral\">payload<\/span>):<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>serializeJson(root, payload);<\/code><\/pre>\n\n\n\n<p>After gathering all the received data on the <span class=\"rnthl rntliteral\">payload<\/span> variable, send that information to the browser as an event (<span class=\"rnthl rntliteral\">&#8220;new_readings&#8221;<\/span>).<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>events.send(payload.c_str(), \"new_readings\", millis());<\/code><\/pre>\n\n\n\n<p>We&#8217;ve seen <a href=\"https:\/\/randomnerdtutorials.com\/esp32-esp-now-wi-fi-web-server\/\">on a previous project<\/a> how to handle these events on the client side. <\/p>\n\n\n\n<p>If the message is of type <span class=\"rnthl rntliteral\">PAIRING<\/span>, it contains the peer information.<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>case PAIRING:                            \/\/ the message is a pairing request<\/code><\/pre>\n\n\n\n<p>We save the received data in the <span class=\"rnthl rntliteral\">incomingData<\/span> variable and print the details on the Serial Monitor.<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>memcpy(&amp;pairingData, incomingData, sizeof(pairingData));\nSerial.println(pairingData.msgType);\nSerial.println(pairingData.id);\nSerial.print(\"Pairing request from MAC Address: \");\nprintMAC(pairingData.macAddr);\nSerial.print(\" on channel \");\nSerial.println(pairingData.channel);<\/code><\/pre>\n\n\n\n<p>The server responds back with its MAC address (in access point mode) and channel, so that the peer knows it sent the information using the right channel and can add the server as peer.<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>if (pairingData.id > 0) {     \/\/ do not replay to server itself\n  if (pairingData.msgType == PAIRING) { \n    pairingData.id = 0;       \/\/ 0 is server\n    \/\/ Server is in AP_STA mode: peers need to send data to server soft AP MAC address \n    WiFi.softAPmacAddress(pairingData.macAddr);\n    Serial.print(\"Pairing MAC Address: \");\n    printMAC(clientMacAddress);\n    pairingData.channel = chan;\n    Serial.println(\" send response\");\n    esp_err_t result = esp_now_send(clientMacAddress, (uint8_t *) &amp;pairingData, sizeof(pairingData));<\/code><\/pre>\n\n\n\n<p>Finally, the server adds the sender to its peer list using the <span class=\"rnthl rntliteral\">addPeer()<\/span> function we created previously.<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>addPeer(clientMacAddress);<\/code><\/pre>\n\n\n\n<h4 class=\"wp-block-heading\">Initialize ESP-NOW<\/h4>\n\n\n\n<p>The <span class=\"rnthl rntliteral\">initESP_NOW()<\/span> function intializes ESP-NOW and registers the callback functions for when data is sent and received.<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>void initESP_NOW(){\n    \/\/ Init ESP-NOW\n    if (esp_now_init() != ESP_OK) {\n      Serial.println(\"Error initializing ESP-NOW\");\n      return;\n    }\n    esp_now_register_send_cb(OnDataSent);\n    esp_now_register_recv_cb(esp_now_recv_cb_t(OnDataRecv));\n}<\/code><\/pre>\n\n\n\n<h4 class=\"wp-block-heading\">setup()<\/h4>\n\n\n\n<p>In the <span class=\"rnthl rntliteral\">setup()<\/span>, print the board MAC address:<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>readMacAddress();<\/code><\/pre>\n\n\n\n<p>Set the ESP32 receiver as station and soft access point simultaneously:<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>WiFi.mode(WIFI_AP_STA);<\/code><\/pre>\n\n\n\n<p>The following lines connect the ESP32 to your local network and print the IP address and the Wi-Fi channel:<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>WiFi.begin(ssid, password);\nwhile (WiFi.status() != WL_CONNECTED) {\n    delay(1000);\n    Serial.println(\"Setting as a Wi-Fi Station..\");\n}<\/code><\/pre>\n\n\n\n<p>Print the board MAC address in access point mode, which is different than the MAC address on station mode.<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>Serial.print(\"Server SOFT AP MAC Address:  \");\nSerial.println(WiFi.softAPmacAddress());<\/code><\/pre>\n\n\n\n<p>Get the board Wi-Fi channel and print it in the Serial Monitor.<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>chan = WiFi.channel();\nSerial.print(\"Station IP Address: \");\nSerial.println(WiFi.localIP());\nSerial.print(\"Wi-Fi Channel: \");\nSerial.println(WiFi.channel());<\/code><\/pre>\n\n\n\n<p>Initialize ESP-NOW by calling the <span class=\"rnthl rntliteral\">initESP_NOW()<\/span> function we created previously.<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>initESP_NOW();<\/code><\/pre>\n\n\n\n<h4 class=\"wp-block-heading\">Send Data Messages to the Sender Boards<\/h4>\n\n\n\n<p>In the <span class=\"rnthl rntliteral\">loop()<\/span>, every 5 seconds (<span class=\"rnthl rntliteral\">EVENT_INTERVAL_MS<\/span>) get data from a sensor or sample data by calling the <span class=\"rnthl rntliteral\">readDataToSend()<\/span> function. It adds new data to the <span class=\"rnthl rntliteral\">outgoingSetpoints<\/span> structure.<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>readDataToSend();<\/code><\/pre>\n\n\n\n<p>Finally, send that data to all registered peers.<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>esp_now_send(NULL, (uint8_t *) &amp;outgoingSetpoints, sizeof(outgoingSetpoints));<\/code><\/pre>\n\n\n\n<p>That&#8217;s pretty much how the server code works when it comes to handling ESP-NOW messages and automatically adding peers.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Testing the Server<\/h3>\n\n\n\n<p>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.<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"832\" height=\"267\" src=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2024\/06\/ESP32-Server-ESP-NOW-Protocol-Autopairing-Arduino-IDE-Serial-Demonstration.png?resize=832%2C267&#038;quality=100&#038;strip=all&#038;ssl=1\" alt=\"ESP32 Server ESP-NOW Protocol Autopairing Arduino IDE Serial Demonstration\" class=\"wp-image-158848\" srcset=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2024\/06\/ESP32-Server-ESP-NOW-Protocol-Autopairing-Arduino-IDE-Serial-Demonstration.png?w=832&amp;quality=100&amp;strip=all&amp;ssl=1 832w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2024\/06\/ESP32-Server-ESP-NOW-Protocol-Autopairing-Arduino-IDE-Serial-Demonstration.png?resize=300%2C96&amp;quality=100&amp;strip=all&amp;ssl=1 300w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2024\/06\/ESP32-Server-ESP-NOW-Protocol-Autopairing-Arduino-IDE-Serial-Demonstration.png?resize=768%2C246&amp;quality=100&amp;strip=all&amp;ssl=1 768w\" sizes=\"(max-width: 832px) 100vw, 832px\" \/><\/figure><\/div>\n\n\n<p>You can access the web server on the board&#8217;s IP address. At the moment, there won&#8217;t be any data displayed because we haven&#8217;t prepared the sender boards yet. Let the server board run the code.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">ESP32\/ESP8266 Sender<\/h2>\n\n\n\n<p>Here are the sender board features:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Pairs automatically with server;<\/li>\n\n\n\n<li>Sends packets with sensor readings to server;<\/li>\n\n\n\n<li>Also receives data from the server (bidirectional communication).<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">Auto-Pairing<\/h3>\n\n\n\n<p>Here&#8217;s how the auto-pairing with the server works:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>The sender doesn&#8217;t have access to the router;<\/li>\n\n\n\n<li>The sender doesn&#8217;t know the server&#8217;s MAC address;<\/li>\n\n\n\n<li>The server must be running for this to work (with the previous code);<\/li>\n\n\n\n<li>The sender sets esp now on channel 1;<\/li>\n\n\n\n<li>The server adds an entry with the broadcast address to its peer list;<\/li>\n\n\n\n<li>The sender sends a <span class=\"rnthl rntliteral\">PAIRING<\/span> message request in broadcast mode:\n<ul class=\"wp-block-list\">\n<li>If the server receives the message we are on the correct channel:<\/li>\n\n\n\n<li>The server adds the received MAC to his peer list (previous section);<\/li>\n\n\n\n<li>The server replies to the MAC address with a message containing his channel number and MAC address (previous section);<\/li>\n\n\n\n<li>The sender replaces the broadcast address with the server address in his peer list.<\/li>\n<\/ul>\n<\/li>\n\n\n\n<li>else\n<ul class=\"wp-block-list\">\n<li>The sender repeats the process on the next channel.<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n\n\n\n<p><span class=\"rnthl rntliteral\">WiFi.softAPmacAddress<\/span> is created from <span class=\"rnthl rntliteral\">WiFi.macAddress<\/span> by adding 1 to the last byte\u2014<a href=\"https:\/\/docs.espressif.com\/projects\/esp-idf\/en\/v3.1.7\/api-reference\/system\/base_mac_address.html\" title=\"check the documentation\">check the documentation<\/a>.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">ESP32 Sender Code<\/h3>\n\n\n\n<p>Upload the following code to your ESP32 board. <\/p>\n\n\n<pre style=\"max-height: 40em; margin-bottom: 20px;\"><code class=\"language-c\">\/*\r\n  Rui Santos &amp; Sara Santos - Random Nerd Tutorials\r\n  Complete project details at https:\/\/RandomNerdTutorials.com\/esp-now-auto-pairing-esp32-esp8266\/\r\n  Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files.\r\n  The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. \r\n  Based on JC Servaye example: https:\/\/github.com\/Servayejc\/esp_now_sender\/\r\n*\/\r\n#include &lt;Arduino.h&gt;\r\n#include &lt;esp_now.h&gt;\r\n#include &lt;esp_wifi.h&gt;\r\n#include &lt;WiFi.h&gt;\r\n#include &lt;EEPROM.h&gt;\r\n\r\n\/\/ Set your Board and Server ID \r\n#define BOARD_ID 1\r\n#define MAX_CHANNEL 13  \/\/ 11 in North America or 13 in Europe\r\n\r\nuint8_t serverAddress[] = {0xFF,0xFF,0xFF,0xFF,0xFF,0xFF};\r\nuint8_t clientMacAddress[6];\r\n\r\n\/\/ Structure to send data\r\n\/\/ Must match the receiver structure\r\n\/\/ Structure example to receive data\r\n\/\/ Must match the sender structure\r\ntypedef struct struct_message {\r\n  uint8_t msgType;\r\n  uint8_t id;\r\n  float temp;\r\n  float hum;\r\n  unsigned int readingId;\r\n} struct_message;\r\n\r\ntypedef struct struct_pairing {       \/\/ new structure for pairing\r\n    uint8_t msgType;\r\n    uint8_t id;\r\n    uint8_t macAddr[6];\r\n    uint8_t channel;\r\n} struct_pairing;\r\n\r\nesp_now_peer_info_t peer;\r\n\r\n\/\/ Create 2 struct_message \r\nstruct_message myData;  \/\/ data to send\r\nstruct_message inData;  \/\/ data received\r\nstruct_pairing pairingData;\r\n\r\nenum PairingStatus {NOT_PAIRED, PAIR_REQUEST, PAIR_REQUESTED, PAIR_PAIRED,};\r\nPairingStatus pairingStatus = NOT_PAIRED;\r\n\r\nenum MessageType {PAIRING, DATA,};\r\nMessageType messageType;\r\n\r\n#ifdef SAVE_CHANNEL\r\n  int lastChannel;\r\n#endif  \r\nint channel = 1;\r\n \r\n\/\/ simulate temperature and humidity data\r\nfloat t = 0;\r\nfloat h = 0;\r\n\r\nunsigned long currentMillis = millis();\r\nunsigned long previousMillis = 0;   \/\/ Stores last time temperature was published\r\nconst long interval = 10000;        \/\/ Interval at which to publish sensor readings\r\nunsigned long start;                \/\/ used to measure Pairing time\r\nunsigned int readingId = 0;   \r\n\r\nvoid readGetMacAddress(){\r\n  uint8_t baseMac[6];\r\n  esp_err_t ret = esp_wifi_get_mac(WIFI_IF_STA, baseMac);\r\n  if (ret == ESP_OK) {\r\n    Serial.printf(&quot;%02x:%02x:%02x:%02x:%02x:%02x\\n&quot;,\r\n                  baseMac[0], baseMac[1], baseMac[2],\r\n                  baseMac[3], baseMac[4], baseMac[5]);\r\n  } else {\r\n    Serial.println(&quot;Failed to read MAC address&quot;);\r\n  }\r\n  clientMacAddress[0] = baseMac[0];\r\n  clientMacAddress[1] = baseMac[1];\r\n  clientMacAddress[2] = baseMac[2];\r\n  clientMacAddress[3] = baseMac[3];\r\n  clientMacAddress[4] = baseMac[4];\r\n  clientMacAddress[5] = baseMac[5];\r\n}\r\n\r\n\/\/ simulate temperature reading\r\nfloat readDHTTemperature() {\r\n  t = random(0,40);\r\n  return t;\r\n}\r\n\r\n\/\/ simulate humidity reading\r\nfloat readDHTHumidity() {\r\n  h = random(0,100);\r\n  return h;\r\n}\r\n\r\nvoid addPeer(const uint8_t * mac_addr, uint8_t chan){\r\n  ESP_ERROR_CHECK(esp_wifi_set_channel(chan ,WIFI_SECOND_CHAN_NONE));\r\n  esp_now_del_peer(mac_addr);\r\n  memset(&amp;peer, 0, sizeof(esp_now_peer_info_t));\r\n  peer.channel = chan;\r\n  peer.encrypt = false;\r\n  memcpy(peer.peer_addr, mac_addr, sizeof(uint8_t[6]));\r\n  if (esp_now_add_peer(&amp;peer) != ESP_OK){\r\n    Serial.println(&quot;Failed to add peer&quot;);\r\n    return;\r\n  }\r\n  memcpy(serverAddress, mac_addr, sizeof(uint8_t[6]));\r\n}\r\n\r\nvoid printMAC(const uint8_t * mac_addr){\r\n  char macStr[18];\r\n  snprintf(macStr, sizeof(macStr), &quot;%02x:%02x:%02x:%02x:%02x:%02x&quot;,\r\n           mac_addr[0], mac_addr[1], mac_addr[2], mac_addr[3], mac_addr[4], mac_addr[5]);\r\n  Serial.print(macStr);\r\n}\r\n\r\nvoid OnDataSent(const uint8_t *mac_addr, esp_now_send_status_t status) {\r\n  Serial.print(&quot;\\r\\nLast Packet Send Status:\\t&quot;);\r\n  Serial.println(status == ESP_NOW_SEND_SUCCESS ? &quot;Delivery Success&quot; : &quot;Delivery Fail&quot;);\r\n}\r\n\r\nvoid OnDataRecv(const uint8_t * mac_addr, const uint8_t *incomingData, int len) { \r\n  Serial.print(&quot;Packet received with &quot;);\r\n  Serial.print(&quot;data size = &quot;);\r\n  Serial.println(sizeof(incomingData));\r\n  uint8_t type = incomingData[0];\r\n  switch (type) {\r\n  case DATA :      \/\/ we received data from server\r\n    memcpy(&amp;inData, incomingData, sizeof(inData));\r\n    Serial.print(&quot;ID  = &quot;);\r\n    Serial.println(inData.id);\r\n    Serial.print(&quot;Setpoint temp = &quot;);\r\n    Serial.println(inData.temp);\r\n    Serial.print(&quot;SetPoint humidity = &quot;);\r\n    Serial.println(inData.hum);\r\n    Serial.print(&quot;reading Id  = &quot;);\r\n    Serial.println(inData.readingId);\r\n\r\n    if (inData.readingId % 2 == 1){\r\n      digitalWrite(LED_BUILTIN, LOW);\r\n    } else { \r\n      digitalWrite(LED_BUILTIN, HIGH);\r\n    }\r\n    break;\r\n\r\n  case PAIRING:    \/\/ we received pairing data from server\r\n    memcpy(&amp;pairingData, incomingData, sizeof(pairingData));\r\n    if (pairingData.id == 0) {              \/\/ the message comes from server\r\n      Serial.print(&quot;Pairing done for MAC Address: &quot;);\r\n      printMAC(pairingData.macAddr);\r\n      Serial.print(&quot; on channel &quot; );\r\n      Serial.print(pairingData.channel);    \/\/ channel used by the server\r\n      Serial.print(&quot; in &quot;);\r\n      Serial.print(millis()-start);\r\n      Serial.println(&quot;ms&quot;);\r\n      addPeer(pairingData.macAddr, pairingData.channel); \/\/ add the server  to the peer list \r\n      #ifdef SAVE_CHANNEL\r\n        lastChannel = pairingData.channel;\r\n        EEPROM.write(0, pairingData.channel);\r\n        EEPROM.commit();\r\n      #endif  \r\n      pairingStatus = PAIR_PAIRED;             \/\/ set the pairing status\r\n    }\r\n    break;\r\n  }  \r\n}\r\n\r\nPairingStatus autoPairing(){\r\n  switch(pairingStatus) {\r\n    case PAIR_REQUEST:\r\n      Serial.print(&quot;Pairing request on channel &quot;  );\r\n      Serial.println(channel);\r\n\r\n      \/\/ set WiFi channel   \r\n      ESP_ERROR_CHECK(esp_wifi_set_channel(channel,  WIFI_SECOND_CHAN_NONE));\r\n      if (esp_now_init() != ESP_OK) {\r\n        Serial.println(&quot;Error initializing ESP-NOW&quot;);\r\n      }\r\n\r\n      \/\/ set callback routines\r\n      esp_now_register_send_cb(OnDataSent);\r\n      esp_now_register_recv_cb(esp_now_recv_cb_t(OnDataRecv));\r\n    \r\n      \/\/ set pairing data to send to the server\r\n      pairingData.msgType = PAIRING;\r\n      pairingData.id = BOARD_ID;     \r\n      pairingData.channel = channel;\r\n      pairingData.macAddr[0] = clientMacAddress[0];\r\n      pairingData.macAddr[1] = clientMacAddress[1];\r\n      pairingData.macAddr[2] = clientMacAddress[2];\r\n      pairingData.macAddr[3] = clientMacAddress[3];\r\n      pairingData.macAddr[4] = clientMacAddress[4];\r\n      pairingData.macAddr[5] = clientMacAddress[5];\r\n\r\n      \/\/ add peer and send request\r\n      addPeer(serverAddress, channel);\r\n      esp_now_send(serverAddress, (uint8_t *) &amp;pairingData, sizeof(pairingData));\r\n      previousMillis = millis();\r\n      pairingStatus = PAIR_REQUESTED;\r\n      break;\r\n\r\n    case PAIR_REQUESTED:\r\n      \/\/ time out to allow receiving response from server\r\n      currentMillis = millis();\r\n      if(currentMillis - previousMillis &gt; 1000) {\r\n        previousMillis = currentMillis;\r\n        \/\/ time out expired,  try next channel\r\n        channel ++;\r\n        if (channel &gt; MAX_CHANNEL){\r\n          channel = 1;\r\n        }   \r\n        pairingStatus = PAIR_REQUEST;\r\n      }\r\n    break;\r\n\r\n    case PAIR_PAIRED:\r\n      \/\/ nothing to do here \r\n    break;\r\n  }\r\n  return pairingStatus;\r\n}  \r\n\r\nvoid setup() {\r\n  Serial.begin(115200);\r\n  Serial.println();\r\n  pinMode(LED_BUILTIN, OUTPUT);\r\n  \r\n  WiFi.mode(WIFI_STA);\r\n  WiFi.STA.begin();\r\n  Serial.print(&quot;Client Board MAC Address:  &quot;);\r\n  readGetMacAddress();\r\n  WiFi.disconnect();\r\n  start = millis();\r\n\r\n  #ifdef SAVE_CHANNEL \r\n    EEPROM.begin(10);\r\n    lastChannel = EEPROM.read(0);\r\n    Serial.println(lastChannel);\r\n    if (lastChannel &gt;= 1 &amp;&amp; lastChannel &lt;= MAX_CHANNEL) {\r\n      channel = lastChannel; \r\n    }\r\n    Serial.println(channel);\r\n  #endif  \r\n  pairingStatus = PAIR_REQUEST;\r\n}  \r\n\r\nvoid loop() {\r\n  if (autoPairing() == PAIR_PAIRED) {\r\n    unsigned long currentMillis = millis();\r\n    if (currentMillis - previousMillis &gt;= interval) {\r\n      \/\/ Save the last time a new reading was published\r\n      previousMillis = currentMillis;\r\n      \/\/Set values to send\r\n      myData.msgType = DATA;\r\n      myData.id = BOARD_ID;\r\n      myData.temp = readDHTTemperature();\r\n      myData.hum = readDHTHumidity();\r\n      myData.readingId = readingId++;\r\n      esp_err_t result = esp_now_send(serverAddress, (uint8_t *) &amp;myData, sizeof(myData));\r\n    }\r\n  }\r\n}\r\n<\/code><\/pre>\n\t<p style=\"text-align:center\"><a class=\"rntwhite\" href=\"https:\/\/github.com\/RuiSantosdotme\/Random-Nerd-Tutorials\/raw\/master\/Projects\/ESP\/ESP_NOW\/ESP_NOW_Auto_Pairing\/ESP32_Sender.ino\" target=\"_blank\">View raw code<\/a><\/p>\n\n\n\n<h3 class=\"wp-block-heading\">ESP8266 Sender Code<\/h3>\n\n\n\n<p>If you&#8217;re using ESP8266 boards, use the following code instead. It&#8217;s similar to the previous code but uses the ESP8266-specific ESP-NOW functions.<\/p>\n\n\n<pre style=\"max-height: 40em; margin-bottom: 20px;\"><code class=\"language-c\">\/*\r\n  Rui Santos &amp; Sara Santos - Random Nerd Tutorials\r\n  Complete project details at https:\/\/RandomNerdTutorials.com\/esp-now-auto-pairing-esp32-esp8266\/\r\n  Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files.\r\n  The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.\r\n  Based on JC Servaye example: https:\/\/https:\/\/github.com\/Servayejc\/esp8266_espnow\r\n*\/\r\n#include &lt;ESP8266WiFi.h&gt;\r\n#include &lt;espnow.h&gt;\r\n\r\nuint8_t channel = 1;\r\nint readingId = 0;\r\nint id = 2;\r\n\r\n#define MAX_CHANNEL 13  \/\/ 11 in North America or 13 in Europe\r\n\r\nunsigned long currentMillis = millis(); \r\nunsigned long lastTime = 0;  \r\nunsigned long timerDelay = 2000;  \/\/ send readings timer\r\n\r\nuint8_t broadcastAddressX[] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};\r\n\r\nuint8_t clientMacAddress[6];\r\n\r\nenum PairingStatus {PAIR_REQUEST, PAIR_REQUESTED, PAIR_PAIRED, };\r\nPairingStatus pairingStatus = PAIR_REQUEST;\r\n\r\nenum MessageType {PAIRING, DATA,};\r\nMessageType messageType;\r\n\r\n\/\/ Define variables to store DHT readings to be sent\r\nfloat temperature;\r\nfloat humidity;\r\n\r\n\/\/ Define variables to store incoming readings\r\nfloat incomingTemp;\r\nfloat incomingHum;\r\nint incomingReadingsId;\r\n\r\n\/\/ Updates DHT readings every 10 seconds\r\n\/\/const long interval = 10000; \r\nunsigned long previousMillis = 0;    \/\/ will store last time DHT was updated \r\n\r\n\/\/Structure example to send data\r\n\/\/Must match the receiver structure\r\ntypedef struct struct_message {\r\n  uint8_t msgType;\r\n  uint8_t id;\r\n  float temp;\r\n  float hum;\r\n  unsigned int readingId;\r\n} struct_message;\r\n\r\ntypedef struct struct_pairing {       \/\/ new structure for pairing\r\n    uint8_t msgType;\r\n    uint8_t id;\r\n    uint8_t macAddr[6];\r\n    uint8_t channel;\r\n} struct_pairing;\r\n\r\n\/\/ Create a struct_message called myData\r\nstruct_message myData;\r\nstruct_message incomingReadings;\r\nstruct_pairing pairingData;\r\n\r\n#define BOARD_ID 2\r\nunsigned long start;\r\n\r\n\/\/ Callback when data is sent\r\nvoid OnDataSent(uint8_t *mac_addr, uint8_t sendStatus) {\r\n  Serial.print(&quot;Last Packet Send Status: &quot;);\r\n  if (sendStatus == 0){\r\n    Serial.println(&quot;Delivery success&quot;);\r\n  }\r\n  else{\r\n    Serial.println(&quot;Delivery fail&quot;);\r\n  }\r\n}\r\n\r\nvoid printMAC(const uint8_t * mac_addr){\r\n  char macStr[18];\r\n  snprintf(macStr, sizeof(macStr), &quot;%02x:%02x:%02x:%02x:%02x:%02x&quot;,\r\n           mac_addr[0], mac_addr[1], mac_addr[2], mac_addr[3], mac_addr[4], mac_addr[5]);\r\n  Serial.print(macStr);\r\n}\r\n\r\nvoid printIncomingReadings(){\r\n  \/\/ Display Readings in Serial Monitor\r\n  Serial.println(&quot;INCOMING READINGS&quot;);\r\n  Serial.print(&quot;Temperature: &quot;);\r\n  Serial.print(incomingTemp);\r\n  Serial.println(&quot; \u00baC&quot;);\r\n  Serial.print(&quot;Humidity: &quot;);\r\n  Serial.print(incomingHum);\r\n  Serial.println(&quot; %&quot;);\r\n  Serial.print(&quot;Led: &quot;);\r\n  Serial.print(incomingReadingsId);\r\n}\r\n\r\n\/\/ Callback when data is received\r\nvoid OnDataRecv(uint8_t * mac, uint8_t *incomingData, uint8_t len) {\r\n  Serial.print(&quot;Size of message : &quot;);\r\n  Serial.print(len);\r\n  Serial.print(&quot; from &quot;);\r\n  printMAC(mac);\r\n  Serial.println();\r\n  uint8_t type = incomingData[0];\r\n  switch (type) {\r\n  case DATA :  \r\n    memcpy(&amp;incomingReadings, incomingData, sizeof(incomingReadings));\r\n    Serial.print(len);\r\n    Serial.print(&quot; Data bytes received from: &quot;);\r\n    printMAC(mac);\r\n    Serial.println();\r\n    incomingTemp = incomingReadings.temp;\r\n    incomingHum = incomingReadings.hum;\r\n    printIncomingReadings();\r\n    \r\n    if (incomingReadings.readingId % 2 == 1){\r\n      digitalWrite(LED_BUILTIN, LOW);\r\n    } else { \r\n      digitalWrite(LED_BUILTIN, HIGH);\r\n    }\r\n    break;\r\n\r\n  case PAIRING:\r\n    memcpy(&amp;pairingData, incomingData, sizeof(pairingData));\r\n    if (pairingData.id == 0) {                \/\/ the message comes from server\r\n      Serial.print(&quot;Pairing done for &quot;);\r\n      printMAC(pairingData.macAddr);\r\n      Serial.print(&quot; on channel &quot; );\r\n      Serial.print(pairingData.channel);    \/\/ channel used by the server\r\n      Serial.print(&quot; in &quot;);\r\n      Serial.print(millis()-start);\r\n      Serial.println(&quot;ms&quot;);\r\n      \/\/esp_now_del_peer(pairingData.macAddr);\r\n      \/\/esp_now_del_peer(mac);\r\n      esp_now_add_peer(pairingData.macAddr, ESP_NOW_ROLE_COMBO, pairingData.channel, NULL, 0); \/\/ add the server to the peer list \r\n      pairingStatus = PAIR_PAIRED ;            \/\/ set the pairing status\r\n    }\r\n    break;\r\n  }  \r\n}\r\n\r\nvoid getReadings(){\r\n  \/\/ Read Temperature\r\n  temperature = 22.5;\r\n  humidity = 55.5;\r\n}\r\n\r\nvoid readGetMacAddress(){\r\n  String val = WiFi.macAddress();\r\n  Serial.println(val);\r\n  char* endPtr; \r\n  clientMacAddress[0] = strtol(val.c_str(), &amp;endPtr, 16); \/\/ read the first starting at the beginning of the buffer. this initializes endPtr as a pointer to the ':' after the first number \r\n  for (int i = 1;  (*endPtr) &amp;&amp; (i &lt; 6); i++) {\r\n    clientMacAddress[i] = strtol(endPtr + 1, &amp;endPtr, 16); \/\/ using +1 for the pointer as we want to skip the ':'\r\n  }\r\n\r\n  for (int i = 0; i &lt; 6; i++) {\r\n    Serial.print(clientMacAddress[i], HEX);\r\n    if (i != 5) Serial.print(F(&quot;:&quot;));\r\n  }\r\n}\r\n\r\nPairingStatus autoPairing(){\r\n  switch(pairingStatus) {\r\n  case PAIR_REQUEST:\r\n    Serial.print(&quot;Pairing request on channel &quot;  );\r\n    Serial.println(channel);\r\n  \r\n    \/\/ clean esp now\r\n    esp_now_deinit();\r\n    WiFi.mode(WIFI_STA);\r\n    \/\/ set WiFi channel   \r\n    wifi_promiscuous_enable(1);\r\n    wifi_set_channel(channel);\r\n    wifi_promiscuous_enable(0);\r\n    \/\/WiFi.printDiag(Serial);\r\n    WiFi.disconnect();\r\n\r\n    \/\/ Init ESP-NOW\r\n    if (esp_now_init() != 0) {\r\n      Serial.println(&quot;Error initializing ESP-NOW&quot;);\r\n    }\r\n    esp_now_set_self_role(ESP_NOW_ROLE_COMBO);\r\n    \/\/ set callback routines\r\n    esp_now_register_send_cb(OnDataSent);\r\n    esp_now_register_recv_cb(OnDataRecv);\r\n    \r\n    \/\/ set pairing data to send to the server\r\n    pairingData.id = BOARD_ID;     \r\n    pairingData.channel = channel;\r\n    pairingData.macAddr[0] = clientMacAddress[0];\r\n    pairingData.macAddr[1] = clientMacAddress[1];\r\n    pairingData.macAddr[2] = clientMacAddress[2];\r\n    pairingData.macAddr[3] = clientMacAddress[3];\r\n    pairingData.macAddr[4] = clientMacAddress[4];\r\n    pairingData.macAddr[5] = clientMacAddress[5];\r\n    previousMillis = millis();\r\n    \/\/ add peer and send request\r\n    Serial.println(esp_now_send(broadcastAddressX, (uint8_t *) &amp;pairingData, sizeof(pairingData)));\r\n    pairingStatus = PAIR_REQUESTED;\r\n    break;\r\n\r\n  case PAIR_REQUESTED:\r\n    \/\/ time out to allow receiving response from server\r\n    currentMillis = millis();\r\n    if(currentMillis - previousMillis &gt; 1000) {\r\n      previousMillis = currentMillis;\r\n      \/\/ time out expired,  try next channel\r\n      channel ++;\r\n      if (channel &gt; MAX_CHANNEL) {\r\n        channel = 0;\r\n      }\r\n      pairingStatus = PAIR_REQUEST; \r\n    }\r\n    break;\r\n\r\n  case PAIR_PAIRED:\r\n    \/\/Serial.println(&quot;Paired!&quot;);\r\n    break;\r\n  }\r\n  return pairingStatus;\r\n} \r\n\r\n\r\n\r\nvoid setup() {\r\n  \/\/ Init Serial Monitor\r\n  Serial.begin(74880);\r\n  pinMode(LED_BUILTIN, OUTPUT);\r\n  \/\/ Init DHT sensor\r\n \r\n  \/\/ Set device as a Wi-Fi Station\r\n  WiFi.mode(WIFI_STA);\r\n  readGetMacAddress();\r\n  \/\/Serial.println(WiFi.macAddress());\r\n  WiFi.disconnect();\r\n\r\n  \/\/ Init ESP-NOW\r\n  if (esp_now_init() != 0) {\r\n    Serial.println(&quot;Error initializing ESP-NOW&quot;);\r\n    return;\r\n  }\r\n\r\n  \/\/ Set ESP-NOW Role\r\n  esp_now_set_self_role(ESP_NOW_ROLE_COMBO);\r\n    \r\n  \/\/ Register for a callback function that will be called when data is received\r\n  esp_now_register_recv_cb(OnDataRecv);\r\n  esp_now_register_send_cb(OnDataSent);\r\n\r\n  pairingData.id = 2;\r\n}\r\n \r\nvoid loop() { \r\n  if (autoPairing() == PAIR_PAIRED) { \r\n    static unsigned long lastEventTime = millis();\r\n    static const unsigned long EVENT_INTERVAL_MS = 10000;\r\n    if ((millis() - lastEventTime) &gt; EVENT_INTERVAL_MS) {\r\n      Serial.print(&quot;.&quot;);\r\n      getReadings();\r\n\r\n      \/\/Set values to send\r\n      myData.msgType = DATA;\r\n      myData.id = 2;\r\n      myData.temp = temperature;\r\n      myData.hum = humidity;\r\n      myData.readingId = readingId ++;\r\n      \r\n      \/\/ Send message via ESP-NOW to all peers \r\n      esp_now_send(pairingData.macAddr, (uint8_t *) &amp;myData, sizeof(myData));\r\n      lastEventTime = millis();\r\n    }\r\n  }\r\n}\r\n<\/code><\/pre>\n\t<p style=\"text-align:center\"><a class=\"rntwhite\" href=\"https:\/\/github.com\/RuiSantosdotme\/Random-Nerd-Tutorials\/raw\/master\/Projects\/ESP\/ESP_NOW\/ESP_NOW_Auto_Pairing\/ESP8266_Sender.ino\" target=\"_blank\">View raw code<\/a><\/p>\n\n\n\n<h3 class=\"wp-block-heading\">How the Code Works<\/h3>\n\n\n\n<p>The ESP32 and ESP8266 are slightly different when it comes to the ESP-NOW-specific functions. But they are structured similarly. So, we&#8217;ll just take a look at the ESP32 code.<\/p>\n\n\n\n<p>We&#8217;ll take a look at the relevant sections that handle auto-pairing with the server. The rest of the code was already explained in great detail in a <a href=\"https:\/\/randomnerdtutorials.com\/esp32-esp-now-wi-fi-web-server\/\">previous project<\/a>.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\">Set Board ID<\/h4>\n\n\n\n<p>Define the sender board ID. Each board should have a different id so that the server knows who sent the message. Board id 0 is reserved for the server, so you should start numbering your sender boards at 1.<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>\/\/ Set your Board ID (ESP32 Sender #1 = BOARD_ID 1, ESP32 Sender #2 = BOARD_ID 2, etc)\n#define BOARD_ID 1<\/code><\/pre>\n\n\n\n<h4 class=\"wp-block-heading\">Define the maximum number of channels<\/h4>\n\n\n\n<p>The sender will loop through different Wi-Fi channels until it finds the server. So, set the maximum number of channels.<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>#define MAX_CHANNEL 11  \/\/ for North America \/\/ 13 in Europe<\/code><\/pre>\n\n\n\n<h4 class=\"wp-block-heading\">Server&#8217;s MAC Address<\/h4>\n\n\n\n<p>The sender board doesn&#8217;t know the server MAC address. So, we&#8217;ll start by sending a message to the broadcast MAC address FF:FF:FF:FF:FF:FF on different channels. When we send messages to this MAC address, all ESP-NOW devices receive this message. Then, the server will respond back with its actual MAC address when we find the right Wi-Fi channel.<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>uint8_t serverAddress&#091;] = {0xFF,0xFF,0xFF,0xFF,0xFF,0xFF};<\/code><\/pre>\n\n\n\n<h4 class=\"wp-block-heading\">Data Structure<\/h4>\n\n\n\n<p>Similarly to the server code, we create two structures.One to receive actual data and another to receive details for pairing.<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>\/\/Structure to send data\n\/\/Must match the receiver structure\n\/\/ Structure example to receive data\n\/\/ Must match the sender structure\ntypedef struct struct_message {\n  uint8_t msgType;\n  uint8_t id;\n  float temp;\n  float hum;\n  unsigned int readingId;\n} struct_message;\n\ntypedef struct struct_pairing {       \/\/ new structure for pairing\n    uint8_t msgType;\n    uint8_t id;\n    uint8_t macAddr&#091;6];\n    uint8_t channel;\n} struct_pairing;\n\n\/\/Create 2 struct_message \nstruct_message myData;  \/\/ data to send\nstruct_message inData;  \/\/ data received\nstruct_pairing pairingData;<\/code><\/pre>\n\n\n\n<h4 class=\"wp-block-heading\">Pairing Statues<\/h4>\n\n\n\n<p>Then, we create an enumeration type called <span class=\"rnthl rntliteral\">ParingStatus<\/span> that can have the following values: <span class=\"rnthl rntliteral\">NOT_PAIRED<\/span>, <span class=\"rnthl rntliteral\">PAIR_REQUEST<\/span>, <span class=\"rnthl rntliteral\">PAIR_REQUESTED<\/span>, and <span class=\"rnthl rntliteral\">PAIR_PAIRED<\/span>. This will help us following the pairing status situation.<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>enum PairingStatus {NOT_PAIRED, PAIR_REQUEST, PAIR_REQUESTED, PAIR_PAIRED,};<\/code><\/pre>\n\n\n\n<p>We create a variable of that type called <span class=\"rnthl rntliteral\">pairingStatus<\/span>. When the board first starts, it&#8217;s not paired, so it&#8217;s set to <span class=\"rnthl rntliteral\">NOT_PAIRED<\/span>.<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>PairingStatus pairingStatus = NOT_PAIRED;<\/code><\/pre>\n\n\n\n<h4 class=\"wp-block-heading\">Message Types<\/h4>\n\n\n\n<p>As we did in the server, we also create a <span class=\"rnthl rntliteral\">MessageType<\/span> so that we know if we received a pairing message or a message with data.<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>enum MessageType {PAIRING, DATA,};\nMessageType messageType;<\/code><\/pre>\n\n\n\n<h4 class=\"wp-block-heading\">Adding a Peer<\/h4>\n\n\n\n<p>This function adds a new peer to the list. It accepts as arguments the peer MAC address and channel.<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>void addPeer(const uint8_t * mac_addr, uint8_t chan){\n  esp_now_peer_info_t peer;\n  ESP_ERROR_CHECK(esp_wifi_set_channel(chan ,WIFI_SECOND_CHAN_NONE));\n  esp_now_del_peer(mac_addr);\n  memset(&amp;peer, 0, sizeof(esp_now_peer_info_t));\n  peer.channel = chan;\n  peer.encrypt = false;\n  memcpy(peer.peer_addr, mac_addr, sizeof(uint8_t&#091;6]));\n  if (esp_now_add_peer(&amp;peer) != ESP_OK){\n    Serial.println(\"Failed to add peer\");\n    return;\n  }\n  memcpy(serverAddress, mac_addr, sizeof(uint8_t&#091;6]));\n}<\/code><\/pre>\n\n\n\n<h4 class=\"wp-block-heading\">Receiving and Handling ESP-NOW Messages<\/h4>\n\n\n\n<p>The <span class=\"rnthl rntliteral\">OnDataRecv()<\/span> function will be executed when you receive a new ESP-NOW packet.<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>void OnDataRecv(const uint8_t * mac_addr, const uint8_t *incomingData, int len) {<\/code><\/pre>\n\n\n\n<p>Inside that function, print the length of the message:<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>Serial.print(\"Packet received with \");\nSerial.print(\"data size = \");\nSerial.println(sizeof(incomingData));<\/code><\/pre>\n\n\n\n<p>Previously, we&#8217;ve seen that we can receive two types of messages: <span class=\"rnthl rntliteral\">PAIRING<\/span> and <span class=\"rnthl rntliteral\">DATA<\/span>. So, we must handle the message content differently depending on the type of message. We can get the type of message as follows:<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>uint8_t type = incomingData&#091;0];       \/\/ first message byte is the type of message<\/code><\/pre>\n\n\n\n<p>Then, we&#8217;ll run different codes depending if the message is of type <span class=\"rnthl rntliteral\">DATA<\/span> or <span class=\"rnthl rntliteral\">PAIRING<\/span>.<\/p>\n\n\n\n<p>If it is of type <span class=\"rnthl rntliteral\">DATA<\/span>, copy the information in the <span class=\"rnthl rntliteral\">incomingData<\/span> variable into the <span class=\"rnthl rntliteral\">inData<\/span> structure variable.<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>memcpy(&amp;inData, incomingData, sizeof(inData));<\/code><\/pre>\n\n\n\n<p>Then, we simply print the received data on the Serial Monitor. You can do any other tasks with the received data that might be useful for your project.<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>Serial.print(\"ID  = \");\nSerial.println(inData.id);\nSerial.print(\"Setpoint temp = \");\nSerial.println(inData.temp);\nSerial.print(\"SetPoint humidity = \");\nSerial.println(inData.hum);\nSerial.print(\"reading Id  = \");\nSerial.println(inData.readingId);<\/code><\/pre>\n\n\n\n<p>In this case, we blink the built-in LED whenever the reading ID is an odd number, but you can perform any other tasks depending on the received data.<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>if (incomingReadings.readingId % 2 == 1){\n  digitalWrite(LED_BUILTIN, LOW);\n} else { \n  digitalWrite(LED_BUILTIN, HIGH);\n}\nbreak;<\/code><\/pre>\n\n\n\n<p>If the message is of type <span class=\"rnthl rntliteral\">PAIRING<\/span>, first we check if the received message is from the server and not from another sender board. We know that because the <span class=\"rnthl rntliteral\">id<\/span> variable for the server is <span class=\"rnthl rntliteral\">0<\/span>.<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>case PAIRING:    \/\/ we received pairing data from server\n  memcpy(&amp;pairingData, incomingData, sizeof(pairingData));\n  if (pairingData.id == 0) {              \/\/ the message comes from server<\/code><\/pre>\n\n\n\n<p>Then, we print the MAC address and channel. This information is sent by the server. <\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>Serial.print(\"Pairing done for MAC Address: \");\n printMAC(pairingData.macAddr);\n Serial.print(\" on channel \" );\n Serial.print(pairingData.channel);    \/\/ channel used by the server<\/code><\/pre>\n\n\n\n<p>So, now that we know the server details, we can call the <span class=\"rnthl rntliteral\">addPeer()<\/span> function and pass as arguments the server MAC address and channel to add the server to the peer list.<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>addPeer(pairingData.macAddr, pairingData.channel); \/\/ add the server  to the peer list <\/code><\/pre>\n\n\n\n<p>If the pairing is successful, we change the <span class=\"rnthl rntliteral\">pairingStatus<\/span> to <span class=\"rnthl rntliteral\">PAIR_PAIRED<\/span>.<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>pairingStatus = PAIR_PAIRED;             \/\/ set the pairing status<\/code><\/pre>\n\n\n\n<h4 class=\"wp-block-heading\">Auto Pairing<\/h4>\n\n\n\n<p>The <span class=\"rnthl rntliteral\">autoPairing()<\/span> function returns the pairing status.<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>PairingStatus autoPairing(){<\/code><\/pre>\n\n\n\n<p>We can have different scenarios. If it is of type <span class=\"rnthl rntliteral\">PAIR_REQUEST<\/span>, it will set up the ESP-NOW callback functions and send the first message of type <span class=\"rnthl rntliteral\">PAIRING<\/span> to the broadcast address on a predefined channel (starting at 1). After that, we change the pairing status to <span class=\"rnthl rntliteral\">PAIR_REQUESTED<\/span> (it means we&#8217;ve already sent a request).<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>case PAIR_REQUEST:\n  Serial.print(\"Pairing request on channel \"  );\n  Serial.println(channel);\n\n  \/\/ set WiFi channel   \n  ESP_ERROR_CHECK(esp_wifi_set_channel(channel,  WIFI_SECOND_CHAN_NONE));\n  if (esp_now_init() != ESP_OK) {\n    Serial.println(\"Error initializing ESP-NOW\");\n  }\n\n  \/\/ set callback routines\n  esp_now_register_send_cb(OnDataSent);\n  esp_now_register_recv_cb(esp_now_recv_cb_t(OnDataRecv));\n    \n  \/\/ set pairing data to send to the server\n  pairingData.msgType = PAIRING;\n  pairingData.id = BOARD_ID;     \n  pairingData.channel = channel;\n  pairingData.macAddr&#091;0] = clientMacAddress&#091;0];\n  pairingData.macAddr&#091;1] = clientMacAddress&#091;1];\n  pairingData.macAddr&#091;2] = clientMacAddress&#091;2];\n  pairingData.macAddr&#091;3] = clientMacAddress&#091;3];\n  pairingData.macAddr&#091;4] = clientMacAddress&#091;4];\n  pairingData.macAddr&#091;5] = clientMacAddress&#091;5];\n\n  \/\/ add peer and send request\n  addPeer(serverAddress, channel);\n  esp_now_send(serverAddress, (uint8_t *) &amp;pairingData, sizeof(pairingData));\n  previousMillis = millis();\n  pairingStatus = PAIR_REQUESTED;\n  break;<\/code><\/pre>\n\n\n\n<p>After sending a pairing message, we wait some time to see if we get a message from the server. If we don&#8217;t, we try on the next Wi-Fi channel and change the <span class=\"rnthl rntliteral\">pairingStatus<\/span> to <span class=\"rnthl rntliteral\">PAIR_REQUEST<\/span> again, so that the board sends a new request on a different Wi-Fi channel.<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>case PAIR_REQUESTED:\n  \/\/ time out to allow receiving response from server\n  currentMillis = millis();\n  if(currentMillis - previousMillis > 250) {\n    previousMillis = currentMillis;\n    \/\/ time out expired,  try next channel\n    channel ++;\n    if (channel > MAX_CHANNEL){\n     channel = 1;\n    }   \n    pairingStatus = PAIR_REQUEST;\n  }\n  break;<\/code><\/pre>\n\n\n\n<p>If the <span class=\"rnthl rntliteral\">pairingStatus<\/span> is <span class=\"rnthl rntliteral\">PAIR_PAIRED<\/span>, meaning we&#8217;re already paired with the server, we don&#8217;t need to do anything.<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>case PAIR_PAIRED:\n  \/\/ nothing to do here \n  break;<\/code><\/pre>\n\n\n\n<p>Finally, return the <span class=\"rnthl rntliteral\">pairingStatus<\/span>.<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>return pairingStatus;<\/code><\/pre>\n\n\n\n<h4 class=\"wp-block-heading\">setup()<\/h4>\n\n\n\n<p>In the <span class=\"rnthl rntliteral\">setup()<\/span>, set the <span class=\"rnthl rntliteral\">pairingStatus<\/span> to <span class=\"rnthl rntliteral\">PAIR_REQUEST<\/span>.<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>pairingStatus = PAIR_REQUEST;<\/code><\/pre>\n\n\n\n<h4 class=\"wp-block-heading\">loop()<\/h4>\n\n\n\n<p>In the <span class=\"rnthl rntliteral\">loop()<\/span>, check if the board is paired with the server before doing anything else.<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>if (autoPairing() == PAIR_PAIRED) {<\/code><\/pre>\n\n\n\n<p>This will run the <span class=\"rnthl rntliteral\">autoPairing()<\/span> function and handle the auto-pairing with the server. When the board is paired with the sender (<span class=\"rnthl rntliteral\">PAIR_PAIRED<\/span>), we can communicate with the server to exchange data with messages of type <span class=\"rnthl rntliteral\">DATA<\/span>.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\">Sending Messages to the Server<\/h4>\n\n\n\n<p>In this case, we&#8217;re sending arbitrary temperature and humidity values, but you can exchange any other data with the server.<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>unsigned long currentMillis = millis();\nif (currentMillis - previousMillis &gt;= interval) {\n  \/\/ Save the last time a new reading was published\n  previousMillis = currentMillis;\n  \/\/Set values to send\n  myData.msgType = DATA;\n  myData.id = BOARD_ID;\n  myData.temp = readDHTTemperature();\n  myData.hum = readDHTHumidity();\n  myData.readingId = readingId++;\n  esp_err_t result = esp_now_send(serverAddress, (uint8_t *) &amp;myData, sizeof(myData));\n}<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Testing the Sender Boards<\/h3>\n\n\n\n<p>Now, you can test the sender boards. We recommend opening a serial communication with the server on another software like PuTTY for example so that you can see what&#8217;s going on on the server and sender simultaneously.<\/p>\n\n\n\n<p>After having the server running, you can upload the sender code to the other boards.<\/p>\n\n\n\n<p>After uploading the code, open the Serial Monitor at a baud rate of 115200 and press the RST button so that the board starts running the code.<\/p>\n\n\n\n<p>This is what the sender should return.<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"832\" height=\"879\" src=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2024\/06\/ESP32-Client-ESP-NOW-Protocol-Autopairing-Arduino-IDE-Serial-Demonstration.png?resize=832%2C879&#038;quality=100&#038;strip=all&#038;ssl=1\" alt=\"ESP32 Client ESP-NOW Protocol Autopairing Arduino IDE Serial Demonstration\" class=\"wp-image-158847\" srcset=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2024\/06\/ESP32-Client-ESP-NOW-Protocol-Autopairing-Arduino-IDE-Serial-Demonstration.png?w=832&amp;quality=100&amp;strip=all&amp;ssl=1 832w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2024\/06\/ESP32-Client-ESP-NOW-Protocol-Autopairing-Arduino-IDE-Serial-Demonstration.png?resize=284%2C300&amp;quality=100&amp;strip=all&amp;ssl=1 284w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2024\/06\/ESP32-Client-ESP-NOW-Protocol-Autopairing-Arduino-IDE-Serial-Demonstration.png?resize=768%2C811&amp;quality=100&amp;strip=all&amp;ssl=1 768w\" sizes=\"(max-width: 832px) 100vw, 832px\" \/><\/figure><\/div>\n\n\n<p>As you can see, first, it sends a pairing request using different channels until it gets a response from the server. In this case, it is using channel 7.<\/p>\n\n\n\n<p>After that, we start receiving messages from the server. We also send messages to the server.<\/p>\n\n\n\n<p>On the server side, this is what happens:<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"827\" height=\"735\" src=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2024\/06\/ESP32-Server-ESP-NOW-Protocol-Autopairing-Arduino-IDE-Serial-Monitor-Receive-Data-Demonstration.png?resize=827%2C735&#038;quality=100&#038;strip=all&#038;ssl=1\" alt=\"ESP32 Server ESP-NOW Protocol Autopairing Arduino IDE Serial Monitor Receive Data Demonstration\" class=\"wp-image-158853\" srcset=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2024\/06\/ESP32-Server-ESP-NOW-Protocol-Autopairing-Arduino-IDE-Serial-Monitor-Receive-Data-Demonstration.png?w=827&amp;quality=100&amp;strip=all&amp;ssl=1 827w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2024\/06\/ESP32-Server-ESP-NOW-Protocol-Autopairing-Arduino-IDE-Serial-Monitor-Receive-Data-Demonstration.png?resize=300%2C267&amp;quality=100&amp;strip=all&amp;ssl=1 300w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2024\/06\/ESP32-Server-ESP-NOW-Protocol-Autopairing-Arduino-IDE-Serial-Monitor-Receive-Data-Demonstration.png?resize=768%2C683&amp;quality=100&amp;strip=all&amp;ssl=1 768w\" sizes=\"(max-width: 827px) 100vw, 827px\" \/><\/figure><\/div>\n\n\n<p>The server receives a pairing request from the sender. It will pair with the sender. In my case, it was already paired because I had run this code before. After data, we start sending and receiving data.<\/p>\n\n\n\n<p>You can upload the sender code to multiple boards and they will all automatically pair with the server. The sender boards can be ESP32 or ESP8266 boards. Make sure you use the right code for the board you&#8217;re using. <\/p>\n\n\n\n<p>Now, you can go to the server&#8217;s IP address to see the readings from the sender boards displayed on the dashboard. The web page is prepared to display readings from two boards. If you want to display more readings you need to modify the web page.<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"750\" height=\"540\" src=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2022\/09\/ESP-NOW-Web-Server-dashboard.png?resize=750%2C540&#038;quality=100&#038;strip=all&#038;ssl=1\" alt=\"ESP-NOW web server dashboard\" class=\"wp-image-116315\" srcset=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2022\/09\/ESP-NOW-Web-Server-dashboard.png?w=750&amp;quality=100&amp;strip=all&amp;ssl=1 750w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2022\/09\/ESP-NOW-Web-Server-dashboard.png?resize=300%2C216&amp;quality=100&amp;strip=all&amp;ssl=1 300w\" sizes=\"(max-width: 750px) 100vw, 750px\" \/><\/figure><\/div>\n\n\n<h2 class=\"wp-block-heading\">Wrapping Up<\/h2>\n\n\n\n<p>In this tutorial, we&#8217;ve shown you how you can build a ESP-NOW Web Server, pair with peers automatically and establish a two-way communication between server and senders boards.<\/p>\n\n\n\n<p>You can adapt the parts of code that deal with auto-pairing and use them in your ESP-NOW examples.<\/p>\n\n\n\n<p>We would like to thank <strong>Jean-Claude Servaye<\/strong> for sharing his ESP-NOW auto-pairing code sketches with us. We only made a few modifications to the sketches. You can find the original codes on <a href=\"https:\/\/github.com\/Servayejc\" target=\"_blank\" rel=\"noreferrer noopener\">his GitHub page<\/a>.<\/p>\n\n\n\n<p>You may also like:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><a href=\"https:\/\/randomnerdtutorials.com\/?s=ESP-NOW\">More ESP-NOW examples&#8230;<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/randomnerdtutorials.com\/learn-esp32-with-arduino-ide\/\">Learn ESP32 with Arduino IDE ebook<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/randomnerdtutorials.com\/home-automation-using-esp8266\/\">Home Automation using ESP8266 ebook<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/randomnerdtutorials.com\/build-web-servers-esp32-esp8266-ebook\/\">Build Web Server with ESP32 and ESP8266 ebook<\/a><\/li>\n<\/ul>\n\n\n\n<p>Thanks for reading.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>This guide shows how to build an ESP32 web server and use ESP-NOW communication protocol simultaneously. We&#8217;ll show you how to establish a two-way communication between the master (web server) &#8230; <\/p>\n<p class=\"read-more-container\"><a title=\"ESP-NOW: Auto-pairing for ESP32\/ESP8266 with Bidirectional Communication and Web Server\" class=\"read-more button\" href=\"https:\/\/randomnerdtutorials.com\/esp-now-auto-pairing-esp32-esp8266\/#more-115899\" aria-label=\"Read more about ESP-NOW: Auto-pairing for ESP32\/ESP8266 with Bidirectional Communication and Web Server\">CONTINUE READING \u00bb<\/a><\/p>\n","protected":false},"author":5,"featured_media":116327,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"jetpack_post_was_ever_published":false,"_jetpack_newsletter_access":"","_jetpack_dont_email_post_to_subs":false,"_jetpack_newsletter_tier_id":0,"_jetpack_memberships_contains_paywalled_content":false,"_jetpack_memberships_contains_paid_content":false,"footnotes":""},"categories":[281,276,277,299,264],"tags":[],"class_list":["post-115899","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-esp32-project","category-esp32","category-esp32-arduino-ide","category-0-esp32","category-project"],"aioseo_notices":[],"jetpack_featured_media_url":"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2022\/09\/ESP-NOW-Web-Server-auto-pairing.jpg?fit=1280%2C720&quality=100&strip=all&ssl=1","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/randomnerdtutorials.com\/wp-json\/wp\/v2\/posts\/115899","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/randomnerdtutorials.com\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/randomnerdtutorials.com\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/randomnerdtutorials.com\/wp-json\/wp\/v2\/users\/5"}],"replies":[{"embeddable":true,"href":"https:\/\/randomnerdtutorials.com\/wp-json\/wp\/v2\/comments?post=115899"}],"version-history":[{"count":33,"href":"https:\/\/randomnerdtutorials.com\/wp-json\/wp\/v2\/posts\/115899\/revisions"}],"predecessor-version":[{"id":158858,"href":"https:\/\/randomnerdtutorials.com\/wp-json\/wp\/v2\/posts\/115899\/revisions\/158858"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/randomnerdtutorials.com\/wp-json\/wp\/v2\/media\/116327"}],"wp:attachment":[{"href":"https:\/\/randomnerdtutorials.com\/wp-json\/wp\/v2\/media?parent=115899"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/randomnerdtutorials.com\/wp-json\/wp\/v2\/categories?post=115899"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/randomnerdtutorials.com\/wp-json\/wp\/v2\/tags?post=115899"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}