{"id":17655,"date":"2023-03-31T19:04:43","date_gmt":"2023-03-31T19:04:43","guid":{"rendered":"https:\/\/wolles-elektronikkiste.de\/?p=17655"},"modified":"2025-09-14T19:27:13","modified_gmt":"2025-09-14T19:27:13","slug":"esp-now","status":"publish","type":"post","link":"https:\/\/wolles-elektronikkiste.de\/en\/esp-now","title":{"rendered":"ESP-NOW"},"content":{"rendered":"\n<h2 class=\"wp-block-heading\">About this Post<\/h2>\n\n<p><strong>Update July 19, 2024: The board package for the ESP32 has a new version of the ESP-NOW library from version 3.0.0, which is not downward compatible. I have adapted the sketches accordingly. The ESP8266 sketches further down in the article still work as they are (please inform me if this should be the case anymore one day!).  <br><\/strong><\/p>\n\n<p>In my article <a href=\"https:\/\/wolles-elektronikkiste.de\/en\/using-wifi-with-the-esp8266-and-esp32\" target=\"_blank\" rel=\"noopener\">WLAN with ESP8266 and ESP32<\/a>, I showed how to let ESP boards communicate with each other via WiFi and how to control them via the browser of your PC or smartphone. However, if you only want to exchange data between two or more ESP boards, there is a simpler method called ESP-NOW.<\/p>\n<p>I will explain step by step how to use your ESP32 or ESP8266 based boards with ESP-NOW as a receiver, transmitter or transceiver. I primarily use the ESP32 as a demonstration object. Finally, I will also show you how to modify the code for ESP8266 boards.<\/p>\n<p>These are the topics I will discuss:<\/p>\n\n<ul>\n<li><a href=\"#intro\">Introduction \/ preparations (ESP32 and ESP8266)<\/a>\n<ul>\n<li><a href=\"#find_out_mac\">Getting the MAC address<\/a><\/li>\n<li><a href=\"#change_mac\">change MAC address<\/a><\/li>\n<\/ul>\n<\/li>\n<li><a href=\"#function_overview\">Overview of the most important ESP-NOW functions (ESP32 and ESP8266)<\/a><\/li>\n<li><a href=\"#1_transm_1_recv_minimum\">One transmitter, one receiver &#8211; bare minimum (ESP32)<\/a><\/li>\n<li><a href=\"#1_transm_1_recv_advanced\">One transmitter, one receiver &#8211; advanced (ESP32)<\/a><\/li>\n<li><a href=\"#multi_transm_1_recv\">Several transmitters, one receiver (ESP32)<\/a><\/li>\n<li><a href=\"#1_transm_multi_recv\">One transmitter, multiple receivers (ESP32)<\/a><\/li>\n<li><a href=\"#transceiver\">ESP boards as transceivers (ESP32)<\/a><\/li>\n<li><a href=\"#new_functions_v3\">New functions from board package version 3.0.0<\/a><\/li>\n<li><a href=\"#translate_to_esp8266\">&#8220;Translation&#8221; of the sketches for the ESP8266<\/a><\/li>\n<li><a href=\"#mix_esp32_esp8266\">Mixing ESP32 and ESP826 boards<\/a><\/li>\n<li><a href=\"#change_channel\">Changing channels (ESP32 and ESP8266)<\/a><\/li>\n<li><a href=\"#range\">Range<\/a><\/li>\n<\/ul>\n\n<h2 class=\"wp-block-heading\" id=\"intro\">Introduction \/ preparations (ESP32 and ESP8266)<\/h2>\n\n<p>ESP-NOW is a protocol that allows you to exchange messages of up to 250 bytes between up to twenty ESP32 or ESP8266 boards. ESP-NOW is very flexible. It allows you to set up any board as a transmitter, receiver or transceiver and send messages to single or multiple members of your network. It is also possible to mix ESP32 and ESP8266 boards without any problems.<\/p>\n<p>To use ESP-NOW, you do not need to install any additional software. The required libraries are part of the &#8220;standard equipment&#8221; of the ESP32 and ESP8266 packages. &nbsp;<\/p>\n\n<h3 class=\"wp-block-heading\" id=\"find_out_mac\">Getting the MAC address<\/h3>\n\n<p>The members of a network must be clearly identifiable. ESP-NOW uses the <a href=\"https:\/\/en.wikipedia.org\/wiki\/MAC_address\" target=\"_blank\" rel=\"noopener\">MAC address<\/a> of your ESP boards, which you can read out and format with the following sketch:<\/p>\n<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-group=\"get_mac_address.ino\" data-enlighter-title=\"get_mac_address.ino\">#include \"WiFi.h\"\n\/\/#include &lt;ESP8266WiFi.h&gt; \/\/ for ESP8266 boards\n\nvoid setup(){\n    Serial.begin(115200);\n    delay(1000); \/\/ uncomment if your serial monitor is empty\n    WiFi.mode(WIFI_STA); \n    while (!(WiFi.STA.started())) { \/\/ comment the while loop for ESP8266\n        delay(10);\n    }\n    \/\/ delay(1000); \/\/ uncomment for ESP8266\n    Serial.print(\"MAC-Address: \");\n    String mac = WiFi.macAddress();\n    Serial.println(mac);\n    \n    Serial.print(\"Formated: \");\n    Serial.print(\"{\");\n    int index = 0;\n    for(int i=0; i&lt;6; i++){\n        Serial.print(\"0x\");\n        Serial.print(mac.substring(index, index+2));\n        if(i&lt;5){\n            Serial.print(\", \");\n        }\n        index += 3;\n    }\n    Serial.println(\"}\");\n}\n \nvoid loop(){}<\/pre>\n<p>\n\n<p>The explicit setting of the Wi-Fi mode (line 7) is required from board package version 3.0.0 in order to determine the MAC address.<\/p>\n<p>And here is the output:<\/p>\n\n<figure class=\"wp-block-image size-full\"><a href=\"https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2024\/07\/output_esp32_get_mac_address_sta.png\"><img loading=\"lazy\" decoding=\"async\" width=\"382\" height=\"36\" src=\"https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2024\/07\/output_esp32_get_mac_address_sta.png\" alt=\"Output get_mac_address.ino\" class=\"wp-image-21626\" srcset=\"https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2024\/07\/output_esp32_get_mac_address_sta.png 382w, https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2024\/07\/output_esp32_get_mac_address_sta-300x28.png 300w\" sizes=\"auto, (max-width: 382px) 100vw, 382px\" \/><\/a><figcaption class=\"wp-element-caption\">Output get_mac_address.ino<\/figcaption><\/figure>\n\n<h4 class=\"wp-block-heading\">A few explanations about the sketch<\/h4>\n\n<p>To use the WiFi functions, you have to include <code>WiFi.h<\/code> for ESP32-based boards. For ESP8266-based boards, include <code>ESP8266WiFi.h<\/code>.<\/p>\n<p>The MAC address consists of six bytes. Usually, the bytes are specified in the hexadecimal system, separated by colons. E.g. <code>94:3C:C6:33:68:98<\/code>.<\/p>\n<p>So, you have to find out the MAC address of every board that should participate in your ESP-NOW network and write it down. It is recommended to label the boards in some way to avoid confusion.<\/p>\n<p><em>Note: If you load your sketches on ESP boards, then it may happen that the first<\/em> <code>Serial.print()<\/code> <em>instructions are &#8220;swallowed&#8221; during the first program run. A<\/em> <code>while(!Serial)<\/code> <em>after<\/em> <code>Serial.begin()<\/code>, <em>which is recommended for certain boards, does not eliminate the problem (if you should have it). That&#8217;s why you&#8217;ll find a commented out delay() in my sketches, which you can uncomment if necessary.&nbsp;<\/em><\/p>\n\n<h3 class=\"wp-block-heading\" id=\"change_mac\">change MAC address<\/h3>\n\n<p>If it is too tedious for you to read out all MAC addresses, then you can simply set a new one. The following two sketches show how to do this for the ESP32 and the ESP8266.<\/p>\n\n<h4 class=\"wp-block-heading\">ESP32<\/h4>\n<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-group=\"change_mac_address.ino\" data-enlighter-title=\"change_mac_address.ino\">#include &lt;WiFi.h&gt;\n#include &lt;esp_wifi.h&gt;\n\nconst uint8_t newMacAddress[] =  {0xC8, 0xC9, 0xA3, 0xC6, 0xFE, 0x54}; \/\/ customize as you wish  \nuint8_t requestedNewMacAddress[6] = {0,0,0,0,0,0};\n\nvoid setup(){\n    Serial.begin(115200);\n    delay(1000); \n    WiFi.mode(WIFI_STA);\n    while (!(WiFi.STA.started())) {\n        delay(100);\n    }\n    Serial.print(\"Default ESP Board MAC Address: \");\n    Serial.println(WiFi.macAddress());\n       \n    esp_wifi_set_mac(WIFI_IF_STA, newMacAddress);\n    esp_wifi_get_mac(WIFI_IF_STA, requestedNewMacAddress);\n    \n    Serial.print(\"New ESP Board MAC Address:     \");\n    esp_wifi_get_mac(WIFI_IF_STA, requestedNewMacAddress);\n    for (int i=0; i&lt;6; i++) {\n        Serial.print(requestedNewMacAddress[i],HEX);\n        if (i&lt;5) {\n            Serial.print(\":\");\n        }\n    }   \n}\n \nvoid loop(){}<\/pre>\n<p>\n\n<p>And here is the output:<\/p>\n\n<figure class=\"wp-block-image size-full\"><a href=\"https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2024\/07\/output_change_mac_address.png\"><img loading=\"lazy\" decoding=\"async\" width=\"540\" height=\"41\" src=\"https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2024\/07\/output_change_mac_address.png\" alt=\"Output change_mac_address.ino\" class=\"wp-image-21761\" srcset=\"https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2024\/07\/output_change_mac_address.png 540w, https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2024\/07\/output_change_mac_address-300x23.png 300w\" sizes=\"auto, (max-width: 540px) 100vw, 540px\" \/><\/a><figcaption class=\"wp-element-caption\">Output change_mac_address.ino<\/figcaption><\/figure>\n\n<p>A few explanations:<\/p>\n<ul>\n<li>We use a function from the <a href=\"https:\/\/docs.espressif.com\/projects\/esp-idf\/en\/latest\/esp32\/api-reference\/index.html\" target=\"_blank\" rel=\"noopener\">ESP API<\/a> (Application Programming Interface) in this example. You can recognize these functions by the fact that they start with &#8220;esp_&#8221;. To access them, you need to include the appropriate libraries. Here, it is <code>esp_wifi.h<\/code>.<\/li>\n<li>The MAC address is defined as an array (<code>uint8_t<\/code>).<\/li>\n<li>An ESP card can either be integrated into a network (station mode) or serve as an access point itself (access point mode). We set the station mode for ESP-NOW using <code>WiFi.mode(WIFI_STA)<\/code>.<\/li>\n<li>With <code>esp_wifi_set_mac()<\/code> you set the MAC address. The function expects two arguments. The first is the interface used. Since we are working in Station mode, we select the station interface ( = WIFI_IF_STA). The second argument is the new MAC address. <br><ul>\n<li>You can find the documentation for the <code>esp_wifi_set_mac()<\/code> function <a href=\"https:\/\/docs.espressif.com\/projects\/esp-idf\/en\/latest\/esp32\/api-reference\/network\/esp_wifi.html#_CPPv416esp_wifi_set_mac16wifi_interface_tAL6E_K7uint8_t\" target=\"_blank\" rel=\"noopener\">here<\/a>.<\/li>\n<\/ul>\n<\/li>\n<li>Bit 0 of the first byte of the MAC address must be 0, or in simpler terms: the first byte must be an even number. Otherwise, as far as I know, there are no other restrictions on your choice.<\/li>\n<li>The change of the MAC address by <code>esp_wifi_set_mac()<\/code> is not permanent, i.e. the ESP &#8220;forgets&#8221; the setting at a reset.<\/li>\n<li>Since the update to package 3.x, <code>WiFi.macAddress()<\/code> always returns the hardware MAC address. To check this, take the inconvenient detour via <code>esp_wifi_get_mac()<\/code>. <\/li>\n<\/ul>\n<p>I will change the MAC address only in one of the example sketches. If you want to use this option in the other examples, you have to extend the sketches accordingly.<\/p>\n\n<h4 class=\"wp-block-heading\">ESP8266<\/h4>\n\n<p>And this is how the counterpart for the ESP8266 looks like:<\/p>\n<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\">#include &lt;ESP8266WiFi.h&gt;\n\nuint8_t newMacAddress[] = {0x94, 0x3C, 0xC6, 0x33, 0x68, 0x01}; \/\/ customize as you wish\n\nvoid setup(){\n      Serial.begin(115200);\n      \/\/ delay(1000); \/\/ uncomment if your serial monitor is empty\n      Serial.print(\"Default ESP Board MAC Address: \");\n      Serial.println(WiFi.macAddress());\n      \n      WiFi.mode(WIFI_STA);\n      wifi_set_macaddr(STATION_IF, newMacAddress);\n      \n      Serial.print(\"New ESP Board MAC Address:     \");\n      Serial.println(WiFi.macAddress());     \n}\n \nvoid loop(){}<\/pre>\n<p>\n\n<h2 class=\"wp-block-heading\" id=\"function_overview\">Overview of the most important ESP-NOW functions (ESP32 and ESP8266)<\/h2>\n\n<p>To let your boards communicate via ESP-NOW, you only need a few functions, which I have summarized here in an overview:<\/p>\n\n<figure class=\"wp-block-image size-full\"><a href=\"https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2024\/07\/esp_now_functions.png\"><img decoding=\"async\" src=\"https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2024\/07\/esp_now_functions.png\" alt=\"The most important ESP-NOW functions at a glance\" class=\"wp-image-21700\"\/><\/a><figcaption class=\"wp-element-caption\">The most important ESP-NOW functions at a glance<\/figcaption><\/figure>\n\n<p>The function <code>esp_now_register_recv_cb()<\/code> is the main reason why the board package version 3.x is not backwards compatible. More precisely, it is the change of the first function parameter from <code>uint8_t * macAddr<\/code> to <code>const esp_now_recv_info* info<\/code>. <code>esp_now_recv_info<\/code> is a structure with the following public elements: <\/p>\n<ul>\n<li><code>uint8_t *src_addr<\/code>i.e. the MAC address of the transmitter<\/li>\n<li><code>uint8_t *des_addr<\/code>i.e. the MAC address of the recipient<\/li>\n<li><code>wifi_pkt_rx_ctrl_t *rx_ctrl<\/code>this is RX control information<\/li>\n<\/ul>\n\n<p>A complete list of the functions can be found <a href=\"https:\/\/docs.espressif.com\/projects\/esp-idf\/en\/latest\/esp32\/api-reference\/network\/esp_now.html#api-reference\" target=\"_blank\" rel=\"noopener\">here<\/a> in the API documentation for ESP-NOW.<\/p>\n\n<h2 class=\"wp-block-heading\" id=\"1_transm_1_recv_minimum\">One transmitter, one receiver &#8211; bare minimum (ESP32)<\/h2>\n\n<p>Since ESP-NOW can be a little confusing for beginners, we&#8217;ll start with a minimal example and then build on that. In this example, one board takes the role of the transmitter, the other serves as the receiver. The message consists of text only. <\/p>\n<p>And again the hint: <strong>The code is ESP32 specific<\/strong>. I will come to the transfer to the ESP8266 at the end of the article.<\/p>\n\n<h3 class=\"wp-block-heading\">Transmitter<\/h3>\n<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"cpp\" data-enlighter-group=\"transmitter_bare_minimum.ino\" data-enlighter-title=\"transmitter_bare_minimum.ino\">#include &lt;esp_now.h&gt;\n#include &lt;WiFi.h&gt;\n\nuint8_t receiverAddress[] = {0x94, 0x3C, 0xC6, 0x33, 0x68, 0x98};\nesp_now_peer_info_t peerInfo;\n\nvoid setup(){\n    Serial.begin(115200);\n    \/\/ delay(1000); \/\/ uncomment if your serial monitor is empty\n    WiFi.mode(WIFI_STA);\n    \n    esp_now_init();\n    \n    memcpy(peerInfo.peer_addr, receiverAddress, 6);\n    peerInfo.channel = 0;\n    peerInfo.encrypt = false;\n\n    esp_now_add_peer(&amp;peerInfo);\n}\n \nvoid loop(){\n    char message[] = \"Hi, this is a message from the transmitting ESP\";\n    esp_now_send(receiverAddress, (uint8_t *) message, sizeof(message)-1); \/\/ -1 to not send the NULL terminator\n    delay(5000);\n}<\/pre>\n<p>\n\n<p>Explanations:<\/p>\n<ul>\n<li>With <code>#include&lt;esp_now.h&gt;<\/code> we integrate the ESP-NOW library.<\/li>\n<li><code>peerInfo<\/code> contains information about the module we want to communicate with. This is a structure of type <code>esp_now_peer_info_t<\/code>. The documentation can be found <a href=\"https:\/\/docs.espressif.com\/projects\/esp-idf\/en\/latest\/esp32\/api-reference\/network\/esp_now.html#_CPPv417esp_now_peer_info\" target=\"_blank\" rel=\"noopener\">here<\/a>. For us, the following elements of the structure are relevant:\n<ul>\n<li><code>peer_addr<\/code> contains the MAC address of the receiver module.\n<ul>\n<li>An assignment <code>peerInfo.peer_addr = receiverAddress<\/code> is not possible, because arrays cannot be copied so easily. Therefore, the detour via <code>memcpy()<\/code>.<\/li>\n<\/ul>\n<\/li>\n<li><code>channel<\/code> is the <a href=\"https:\/\/de.wikipedia.org\/wiki\/Wireless_Local_Area_Network#Anmerkungen_zu_2,4_GHz\" target=\"_blank\" rel=\"noopener\">WiFi channel<\/a>. You can choose the channels 1 &#8211; 13. With &#8220;0&#8221; the default setting takes effect, namely &#8220;1&#8221;. I will describe how to change the WLAN channel of your module at the end of the article.<\/li>\n<li><code>encrypt<\/code> specifies whether you want to encrypt the message. I am not going into that.<\/li>\n<\/ul>\n<\/li>\n<li><code>esp_now_init()<\/code> initializes ESP-NOW.<\/li>\n<li><code>esp_now_add_peer(&amp;peerInfo)<\/code> adds the ESP module you defined before with <code>peerInfo<\/code> to the network of the current module.&nbsp;<\/li>\n<li><code>esp_now_send()<\/code> sends a message. You pass the MAC address of the recipient, the message and its length to the function. The message is of data type <code>uint8_t<\/code> and is passed as a pointer in <code>esp_now_send()<\/code>. Accordingly, <code>message<\/code> must still be explicitly converted using <code>(uint8_t *)<\/code>.<\/li>\n<\/ul>\n\n<h4 class=\"wp-block-heading\">Broadcasting<\/h4>\n\n<p>If you don&#8217;t want to determine MAC addresses, you could use the broadcast MAC address FF:FF:FF:FF:FF:FF:FF instead of individual addresses. All recipients will then receive the message, provided they are set to the same channel. <\/p>\n\n<h3 class=\"wp-block-heading\">Receiver<\/h3>\n<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-group=\"receiver_bare_minimum.ino\" data-enlighter-title=\"receiver_bare_minimum.ino\">#include &lt;esp_now.h&gt;\n#include &lt;WiFi.h&gt;\n\nvoid messageReceived(const esp_now_recv_info *info, const uint8_t* incomingData, int len){\n    for(int i=0; i&lt;len; i++){\n        Serial.print((char)incomingData[i]);\n    }\n    Serial.println();\n}\n\n\nvoid setup(){\n    Serial.begin(115200);\n    \/\/ delay(1000); \/\/ uncomment if your serial monitor is empty\n    WiFi.mode(WIFI_STA);\n    esp_now_init();\n    esp_now_register_recv_cb(messageReceived);\n}\n\nvoid loop(){}<\/pre>\n<p>\n\n<p>Here are some explanations as well:<\/p>\n<ul>\n<li>To receive messages only, you do not need to add the sending module as a &#8220;peer&#8221;.<\/li>\n<li>The only new function is <code>esp_now_register_recv_cb()<\/code>. With this, you register a function (here: messageReceived), which is called automatically when a message is received. The &#8220;cb&#8221; stands for &#8220;call back&#8221;. Since the function is called automatically, <code>loop()<\/code> can be left empty. You know the principle from interrupt service routines.<\/li>\n<li>The parameters of the function to be called are the MAC address of the sender, the message itself and the length of the message.<\/li>\n<li>Since we sent a text, but the received message is of data type <code>uint8_t<\/code>, we have to explicitly convert it back to <code>char<\/code>.&nbsp;<\/li>\n<\/ul>\n\n<h4 class=\"wp-block-heading\">Output on the serial monitor<\/h4>\n\n<p>If everything went well, then the message of the transmitter should be displayed on the serial monitor of the receiver module every five seconds.<\/p>\n\n<figure class=\"wp-block-image size-full\"><a href=\"https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2023\/03\/output_receiver_bare_minimum.png\"><img loading=\"lazy\" decoding=\"async\" width=\"589\" height=\"99\" src=\"https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2023\/03\/output_receiver_bare_minimum.png\" alt=\"\" class=\"wp-image-21663\" srcset=\"https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2023\/03\/output_receiver_bare_minimum.png 589w, https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2023\/03\/output_receiver_bare_minimum-300x50.png 300w\" sizes=\"auto, (max-width: 589px) 100vw, 589px\" \/><\/a><figcaption class=\"wp-element-caption\">Output of receiver_bare_minimum.ino<\/figcaption><\/figure>\n\n<h2 class=\"wp-block-heading\" id=\"1_transm_1_recv_advanced\">One transmitter, one receiver &#8211; advanced (ESP32)<\/h2>\n\n<p>The &#8220;Bare Minimum&#8221; sketches are not particularly convenient so far. There are no error messages if something goes wrong, no send confirmation, and no display of the MAC address of the transmitter. Also, we have only sent plain text so far. In practice, it is more likely that users will want to transmit data, such as sensor readings. We will now eliminate these deficits.<\/p>\n\n<h3 class=\"wp-block-heading\">Transmitter sketch<\/h3>\n\n<p>First, the transmitter sketch:<\/p>\n<\/p>\n<div class=\"scroll-paragraph\">\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"cpp\" data-enlighter-group=\"transmitter_basic.ino\" data-enlighter-title=\"transmitter_basic.ino\">#include &lt;esp_now.h&gt;\n#include &lt;WiFi.h&gt;\n\nuint8_t receiverAddress[] = {0x94, 0x3C, 0xC6, 0x33, 0x68, 0x98};\nesp_now_peer_info_t peerInfo;\n\ntypedef struct message {\n    char text[64];\n    int intVal;\n    float floatVal;\n} message;\n\nmessage myMessage; \n\nvoid messageSent(const uint8_t *macAddr, esp_now_send_status_t status) {\n    Serial.print(\"Send status: \");\n    if(status == ESP_NOW_SEND_SUCCESS){\n        Serial.println(\"Success\");\n    }\n    else{\n        Serial.println(\"Error\");\n    }\n}\n\nvoid setup(){\n    Serial.begin(115200);\n    \/\/ delay(1000); \/\/\u00a0uncomment\u00a0if\u00a0your\u00a0serial\u00a0monitor\u00a0is\u00a0empty\n    WiFi.mode(WIFI_STA);\n    \n    if (esp_now_init() == ESP_OK) {\n        Serial.println(\"ESPNow Init success\");\n    }\n    else {\n        Serial.println(\"ESPNow Init fail\");\n        return;\n    }\n    \n    esp_now_register_send_cb((esp_now_send_cb_t)messageSent);   \n\n    memcpy(peerInfo.peer_addr, receiverAddress, 6);\n    peerInfo.channel = 0;\n    peerInfo.encrypt = false;\n\n    if (esp_now_add_peer(&amp;peerInfo) != ESP_OK) {\n        Serial.println(\"Failed to add peer\");\n        return;\n    }\n}\n \nvoid loop(){\n    char textMsg[] = \"Hi Receiver, here's my data for you: \";\n    memcpy(&amp;myMessage.text, textMsg, sizeof(textMsg));\n    myMessage.intVal = 4242;\n    myMessage.floatVal = 42.42;\n    esp_err_t result = esp_now_send(receiverAddress, (uint8_t *) &amp;myMessage, sizeof(myMessage));\n    if (result != ESP_OK) {\n        Serial.println(\"Sending error\");\n    }\n    delay(5000);\n}<\/pre>\n<p>\u00a0<\/p>\n<\/div>\n<p>\n\n<h4 class=\"wp-block-heading\">What is different compared to the bare minimum sketch?<\/h4>\n\n<ul>\n<li>As format for our message we use a <a href=\"https:\/\/www.w3schools.com\/cpp\/cpp_structs.asp\" target=\"_blank\" rel=\"noopener\">structure<\/a> (message). This gives us a lot of flexibility in terms of the types of data that are included in the message. In this example, the structure consists of a character array, an integer, and a float value. For the character array, we have to specify the maximum expected length.<\/li>\n<li>Structures like <code>message<\/code> are kind of stripped down classes. <code>message myMessage;<\/code> creates the object <code>myMessage<\/code>.<\/li>\n<li>The return value of <code>esp_now_init()<\/code> tells us whether the process was completed without errors.  <\/li>\n<li>In the same way, we use the return value of <code>esp_now_add_peer()<\/code>. Important: the function does not check whether the peer is really available or reachable. But, for example, you would get an error message if the maximum number of peers is exceeded. For more information, see the <a href=\"https:\/\/docs.espressif.com\/projects\/esp-idf\/en\/latest\/esp32\/api-reference\/network\/esp_now.html#_CPPv416esp_now_add_peerPK19esp_now_peer_info_t\" target=\"_blank\" rel=\"noopener\">API documentation<\/a>.<\/li>\n<li>To copy the array <code>textMsg<\/code> to the array <code>myMessage.text<\/code> you have to use <code>memcpy()<\/code> for the reasons already mentioned before. You can assign the other elements directly.  <\/li>\n<li>With <code>esp_now_register_send_cb((esp_now_send_cb_t)messageSent);<\/code> we register the function <code>messageSent<\/code>, which is called when a message has been sent. Again, the parameters are predefined.<\/li>\n<li>Checking <code>status<\/code> in the function <code>messageSent<\/code> and checking <code>result<\/code> as the return value of <code>esp_now_send()<\/code> may seem like unnecessary duplication, but different criteria are checked in each case. For details &#8211; guess what &#8211; see the API documentation.<\/li>\n<\/ul>\n\n<h3 class=\"wp-block-heading\">Receiver sketch<\/h3>\n\n<p>The receiver sketch can also be made more comfortable:<\/p>\n<\/p>\n<div class=\"scroll-paragraph\">\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-group=\"receiver_basic.ino\" data-enlighter-title=\"receiver_basic.ino\">#include &lt;esp_now.h&gt;\n#include &lt;WiFi.h&gt;\n\ntypedef struct message {\n    char text[64];\n    int intVal;\n    float floatVal;\n} message;\n\nmessage myMessage;\n\nvoid messageReceived(const esp_now_recv_info *info, const uint8_t* incomingData, int len){\n    memcpy(&amp;myMessage, incomingData, sizeof(myMessage));\n    Serial.printf(\"Transmitter MAC Address: %02X:%02X:%02X:%02X:%02X:%02X \\n\\r\", \n            info-&gt;src_addr[0], info-&gt;src_addr[1], info-&gt;src_addr[2], info-&gt;src_addr[3], info-&gt;src_addr[4], info-&gt;src_addr[5]);    \n    Serial.print(\"Message: \");\n    Serial.println(myMessage.text);\n    Serial.print(\"Integer Value: \");\n    Serial.println(myMessage.intVal);\n    Serial.print(\"Float Value: \");\n    Serial.println(myMessage.floatVal);\n    Serial.println();\n}\n\nvoid setup(){\n    Serial.begin(115200);\n    \/\/ delay(1000); \/\/ uncomment if your serial monitor is empty\n    WiFi.mode(WIFI_STA);\n    \n    if (esp_now_init() == ESP_OK) {\n        Serial.println(\"ESPNow Init success\");\n    }\n    else {\n        Serial.println(\"ESPNow Init fail\");\n        return;\n    }\n\n    esp_now_register_recv_cb(messageReceived);\n}\n \nvoid loop(){}<\/pre>\n<p>\u00a0<\/p>\n<\/div>\n<p>\n\n<h4 class=\"wp-block-heading\">What is different here compared to the bare minimum sketch?<\/h4>\n\n<ul>\n<li>We implement the same structure <code>message<\/code> as in the transmitter sketch.<\/li>\n<li>We copy the incoming message to <code>myMessage<\/code> using <code>memcpy()<\/code> and then we can conveniently access the elements of the structure <code>myMessage<\/code>.<\/li>\n<li>We display the MAC address of the transmitter. The formatting (hex numbers, colons) is provided by the <code>printf()<\/code> function. &nbsp;\n<ul>\n<li>The MAC address (<code>src_addr<\/code>) is an element of <code>info<\/code>. Since <code>info<\/code> was passed as a pointer to <code>messageReceived()<\/code>, we must use the arrow operator instead of the dot operator to access <code>src_addr<\/code>. <\/li>\n<\/ul>\n<\/li>\n<\/ul>\n\n<h4 class=\"wp-block-heading\">Output of receiver_basic.ino<\/h4>\n\n<figure class=\"wp-block-image size-full\"><a href=\"https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2023\/03\/output_receiver_basic.png\"><img loading=\"lazy\" decoding=\"async\" width=\"572\" height=\"181\" src=\"https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2023\/03\/output_receiver_basic.png\" alt=\"Output of receiver_basic.ino\" class=\"wp-image-21669\" srcset=\"https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2023\/03\/output_receiver_basic.png 572w, https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2023\/03\/output_receiver_basic-300x95.png 300w\" sizes=\"auto, (max-width: 572px) 100vw, 572px\" \/><\/a><figcaption class=\"wp-element-caption\">Output of receiver_basic.ino<\/figcaption><\/figure>\n\n<h2 class=\"wp-block-heading\" id=\"multi_transm_1_recv\">Several transmitters, one receiver (ESP32)<\/h2>\n\n<p>In practice, one often uses several transmitters and one receiver. A typical application example would be a weather station with sensors at different locations that transmit their data to a central station.<\/p>\n<p>You do not need any additional ESP-NOW functions for this configuration. The only challenge is assigning the incoming data to the transmitters. The obvious solution would be to compare the transmitter MAC address with a list stored in the receiver sketch.  However, an identity check of arrays is quite computationally intensive. Alternatively, the message from the transmitter could contain an identifier. &nbsp;<\/p>\n<p>I decided to use a different method. All ESP modules participating in the network get a new MAC address. The MAC addresses differ only in the last byte, which also serves as numbering. In my example, three transmitters are used, and I have assigned the following MAC addresses for them:<\/p>\n<ul>\n<li>Transmitter <strong>0<\/strong> address = 94:3C:C6:33:68:<strong>00<\/strong>,<\/li>\n<li>Transmitter <strong>1<\/strong> address = 94:3C:C6:33:68:<strong>01<\/strong>,<\/li>\n<li>Transmitter <strong>2<\/strong> address = 94:3C:C6:33:68:<strong>02<\/strong>,<\/li>\n<li>Receiver address = 94:3C:C6:33:68<strong>:05<\/strong><strong> <br><\/strong> <\/li>\n<\/ul>\n\n<h3 class=\"wp-block-heading\">Transmitter sketch example<\/h3>\n\n<p>Here is the sketch for the transmitter 0:<\/p>\n<\/p>\n<div class=\"scroll-paragraph\">\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-group=\"multi_transm_one_recv_transmitter0.ino\" data-enlighter-title=\"multi_transm_one_recv_transmitter0.ino\">#include &lt;esp_now.h&gt;\n#include &lt;esp_wifi.h&gt;\n#include &lt;WiFi.h&gt;\n\nuint8_t receiverAddress[] = {0x94, 0x3C, 0xC6, 0x33, 0x68, 0x05};\nuint8_t myAddress[] = {0x94, 0x3C, 0xC6, 0x33, 0x68, 0x00};\nesp_now_peer_info_t peerInfo;\n\ntypedef struct data {\n    int humidity;\n    float temperature;\n} data;\n\ndata myMessage; \n\nvoid messageSent(const uint8_t *macAddr, esp_now_send_status_t status) {\n    Serial.print(\"Send status: \");\n    if(status == ESP_NOW_SEND_SUCCESS){\n        Serial.println(\"Success\");\n    }\n    else{\n        Serial.println(\"Error\");\n    }\n}\n\nvoid setup(){\n    Serial.begin(115200);\n    delay(1000);\n    WiFi.mode(WIFI_STA);\n    esp_wifi_set_mac(WIFI_IF_STA, myAddress);\n    Serial.print(\"New ESP Board MAC Address:  \");\n    Serial.println(WiFi.macAddress());  \n    \n    if (esp_now_init() == ESP_OK) {\n        Serial.println(\"ESPNow Init success\");\n    }\n    else {\n        Serial.println(\"ESPNow Init fail\");\n        return;\n    }\n    \n    esp_now_register_send_cb((esp_now_send_cb_t)messageSent);   \n\n    memcpy(peerInfo.peer_addr, receiverAddress, 6);\n    peerInfo.channel = 0;\n    peerInfo.encrypt = false;\n\n    if (esp_now_add_peer(&amp;peerInfo) != ESP_OK) {\n        Serial.println(\"Failed to add peer\");\n        return;\n    }\n}\n \nvoid loop(){\n    myMessage.humidity = 42;\n    myMessage.temperature = 16.9;\n    esp_err_t result = esp_now_send(receiverAddress, (uint8_t *) &amp;myMessage, sizeof(myMessage));\n    if (result != ESP_OK) {\n        Serial.println(\"Sending error\");\n    }\n    delay(3000);\n}<\/pre>\n<p>\u00a0<\/p>\n<\/div>\n<p>\n\n<p>As data we send the humidity and the temperature, which we could have determined with a DHT22, for example. The data is &#8220;packed&#8221; in the structure <code>data<\/code>.<\/p>\n\n<h3 class=\"wp-block-heading\">Receiver<\/h3>\n\n<p>On the receiver side, we first copy the incoming messages to the &#8220;auxiliary structure&#8221; <code>stationMsg<\/code> and from there we transfer them to the array <code>weatherStation[]<\/code>. The counter for the elements of the array is simply the last digit of the MAC address, i.e. <code>info-&gt;src_addr[5]<\/code>:<\/p>\n<\/p>\n<div class=\"scroll-paragraph\">\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-group=\"multi_transm_one_recv_receiver.ino\" data-enlighter-title=\"multi_transm_one_recv_receiver.ino\">#include &lt;esp_now.h&gt;\n#include &lt;esp_wifi.h&gt;\n#include &lt;WiFi.h&gt;\n\nuint8_t myAddress[] = {0x94, 0x3C, 0xC6, 0x33, 0x68, 0x05};\n\ntypedef struct data {\n    int humidity;\n    float temperature;\n} data;\n\ndata stationMsg;\ndata weatherStation[3] = {0, 0};\n\nvoid messageReceived(const esp_now_recv_info *info, const uint8_t* incomingData, int len){\n    memcpy(&amp;stationMsg, incomingData, sizeof(stationMsg));\n    weatherStation[info-&gt;src_addr[5]].humidity = stationMsg.humidity;\n    weatherStation[info-&gt;src_addr[5]].temperature = stationMsg.temperature;\n}\n\nvoid setup(){\n    Serial.begin(115200);\n    delay(1000);\n    WiFi.mode(WIFI_STA);\n    esp_wifi_set_mac(WIFI_IF_STA, myAddress);\n    \n    if (esp_now_init() == ESP_OK) {\n        Serial.println(\"ESPNow Init success\");\n    }\n    else {\n        Serial.println(\"ESPNow Init fail\");\n        return;\n    }\n\n    esp_now_register_recv_cb(messageReceived);\n}\n \nvoid loop(){\n    for(int i=0; i&lt;3; i++){\n        Serial.print(\"Weather Station \");\n        Serial.print(i);\n        Serial.println(\":\");\n        Serial.print(\"Humidity [%]    : \");\n        Serial.println(weatherStation[i].humidity);\n        Serial.print(\"Temperature [\u00b0C]: \");\n        Serial.println(weatherStation[i].temperature,1);\n        Serial.println();\n    }\n    Serial.println();\n    delay(5000);   \n}<\/pre>\n<p>\u00a0<\/p>\n<\/div>\n<p>\n\n<p>Here is the output:<\/p>\n\n<figure class=\"wp-block-image size-full\"><a href=\"https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2023\/03\/output_multi_transm_one_recv_receiver.png\"><img loading=\"lazy\" decoding=\"async\" width=\"487\" height=\"201\" src=\"https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2023\/03\/output_multi_transm_one_recv_receiver.png\" alt=\"Output of multi_transm_one_recv_receiver.ino\" class=\"wp-image-21675\" srcset=\"https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2023\/03\/output_multi_transm_one_recv_receiver.png 487w, https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2023\/03\/output_multi_transm_one_recv_receiver-300x124.png 300w\" sizes=\"auto, (max-width: 487px) 100vw, 487px\" \/><\/a><figcaption class=\"wp-element-caption\">Output of multi_transm_one_recv_receiver.ino<\/figcaption><\/figure>\n\n<h2 class=\"wp-block-heading\" id=\"1_transm_multi_recv\">One transmitter, multiple receivers (ESP32)<\/h2>\n\n<p>It is just as easy to network one transmitter and multiple receivers. This configuration could be used for smart home applications, for example. The sender must know all the MAC addresses of the peers, i.e. the receivers, and connect to all of them.<\/p>\n<p>For simplicity and better overview, in my example all receivers get sent the same data types, so we only need to define one structure. If necessary, this could be easily changed.<\/p>\n\n<h3 class=\"wp-block-heading\">Transmitter<\/h3>\n<\/p>\n<div class=\"scroll-paragraph\">\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-group=\"one_transm_multi_recv_transmitter.ino\" data-enlighter-title=\"one_transm_multi_recv_transmitter.ino\">#include &lt;esp_now.h&gt;\n#include &lt;WiFi.h&gt;\n\nuint8_t receiverAddress[3][6] =  {{0xC8, 0xC9, 0xA3, 0xCA, 0x22, 0x70},\n                                  {0xC8, 0xC9, 0xA3, 0xC6, 0xFE, 0x54},\n                                  {0x94, 0xE6, 0x86, 0x0D, 0x7B, 0x80}};\nesp_now_peer_info_t peerInfo[3];\n\ntypedef struct message {\n    char text[32];\n    int intVal;\n    float floatVal;\n} message;\n\nmessage myMessage[3]; \n\nvoid messageSent(const uint8_t *macAddr, esp_now_send_status_t status) {\n    Serial.printf(\"Send status to receiver %02X:%02X:%02X:%02X:%02X:%02X : \", \n            macAddr[0], macAddr[1], macAddr[2], macAddr[3], macAddr[4], macAddr[5]);        \n    if(status == ESP_NOW_SEND_SUCCESS){\n        Serial.println(\"Success\");\n    }\n    else{\n        Serial.println(\"Error\");\n    }\n}\n\nvoid setup(){\n    Serial.begin(115200);\n    \/\/ delay(1000); \/\/ uncomment if your serial monitor is empty\n    WiFi.mode(WIFI_STA);\n    \n    if (esp_now_init() == ESP_OK) {\n        Serial.println(\"ESPNow Init success\");\n    }\n    else {\n        Serial.println(\"ESPNow Init fail\");\n        return;\n    }\n    \n    esp_now_register_send_cb((esp_now_send_cb_t)messageSent);   \n\n    for(int i=0; i&lt;3; i++){\n        memcpy(peerInfo[i].peer_addr, receiverAddress[i], 6);\n        peerInfo[i].channel = 0;\n        peerInfo[i].encrypt = false;\n\n        if (esp_now_add_peer(&amp;peerInfo[i]) != ESP_OK) {\n            Serial.println(\"Failed to add peer\");\n            return;\n        }\n    }\n}\n \nvoid loop(){\n    char textMsg0[] = \"Hi Receiver 0\";\n    memcpy(&amp;myMessage[0].text, textMsg0, sizeof(textMsg0));\n    myMessage[0].intVal = 4242;\n    myMessage[0].floatVal = 42.42;\n    \n    char textMsg1[] = \"Ciao Receiver 1\";\n    memcpy(&amp;myMessage[1].text, textMsg1, sizeof(textMsg1));\n    myMessage[1].intVal = 1234;\n    myMessage[1].floatVal = 12.34;\n    \n    char textMsg2[] = \"Hola Receiver 2\";\n    memcpy(&amp;myMessage[2].text, textMsg2, sizeof(textMsg2));\n    myMessage[2].intVal = 4711;\n    myMessage[2].floatVal = 47.11;\n    \n    for(int i=0; i&lt;3; i++){\n        esp_err_t result = esp_now_send(receiverAddress[i], (uint8_t *) &amp;myMessage[i], sizeof(myMessage[i]));\n        if (result != ESP_OK) {\n            Serial.print(\"Sending error module \");\n            Serial.println(i);\n        }\n    }\n     delay(10000);\n}<\/pre>\n<p>\u00a0<\/p>\n<\/div>\n<p>\n\n<p>I don&#8217;t think the code needs any further explanation, do you?<\/p>\n<p>You could also simplify this sketch a bit by changing the MAC addresses of the receivers so that they only differ in the last byte. You then only need a one-dimensional array for the <code>receiverAddress<\/code>, and if you want to address receiver no. i, then you only have to set the address with <code>receiverAddress[5] = 0x0i<\/code>.<\/p>\n<p>Maybe also useful: If you replace the receiver address in the function <code>esp_now_send()<\/code> by NULL (written exactly like this, not &#8220;0&#8221;), the message will be sent to all registered peers.<\/p>\n\n<h4 class=\"wp-block-heading\">Output<\/h4>\n\n<p>This is what the output looks like on the transmitter side:<\/p>\n\n<figure class=\"wp-block-image size-full\"><a href=\"https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2023\/03\/output_one_transm_multi_recv_transmitter.png\"><img loading=\"lazy\" decoding=\"async\" width=\"709\" height=\"59\" src=\"https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2023\/03\/output_one_transm_multi_recv_transmitter.png\" alt=\"\" class=\"wp-image-21685\" srcset=\"https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2023\/03\/output_one_transm_multi_recv_transmitter.png 709w, https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2023\/03\/output_one_transm_multi_recv_transmitter-300x25.png 300w\" sizes=\"auto, (max-width: 709px) 100vw, 709px\" \/><\/a><figcaption class=\"wp-element-caption\">Output of one_transm_multi_recv_transmitter.ino<\/figcaption><\/figure>\n\n<h3 class=\"wp-block-heading\">Receiver<\/h3>\n\n<p>The sketch for the receivers should also be understandable without further explanation:<\/p>\n<\/p>\n<div class=\"scroll-paragraph\">\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-group=\"one_transm_multi_recv_receiver.ino\" data-enlighter-title=\"one_transm_multi_recv_receiver.ino\">#include &lt;esp_now.h&gt;\n#include &lt;WiFi.h&gt;\n\ntypedef struct message {\n    char text[32];\n    int intVal;\n    float floatVal;\n} message;\n\nmessage myMessage;\n\nvoid messageReceived(const esp_now_recv_info *info, const uint8_t* incomingData, int len){\n    memcpy(&amp;myMessage, incomingData, sizeof(myMessage));\n    Serial.print(\"Message: \");\n    Serial.println(myMessage.text);\n    Serial.print(\"Integer Value: \");\n    Serial.println(myMessage.intVal);\n    Serial.print(\"Float Value: \");\n    Serial.println(myMessage.floatVal);\n    Serial.println();\n}\n\nvoid setup(){\n    Serial.begin(115200);\n    \/\/ delay(1000); \/\/ uncomment if your serial monitor is empty\n    WiFi.mode(WIFI_STA);\n    \n    if (esp_now_init() == ESP_OK) {\n        Serial.println(\"ESPNow Init success\");\n    }\n    else {\n        Serial.println(\"ESPNow Init fail\");\n        return;\n    }\n\n    esp_now_register_recv_cb(messageReceived);\n}\n \nvoid loop(){}<\/pre>\n<p>\u00a0<\/p>\n<\/div>\n<p>\n\n<h4 class=\"wp-block-heading\">Output<\/h4>\n\n<p>And here still exemplarily the output of Receiver 1:<\/p>\n\n<figure class=\"wp-block-image size-full\"><a href=\"https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2023\/03\/output_one_transm_multi_recv_receiver.png\"><img loading=\"lazy\" decoding=\"async\" width=\"435\" height=\"53\" src=\"https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2023\/03\/output_one_transm_multi_recv_receiver.png\" alt=\"Output of one_transm_multi_recv_receiver.ino\" class=\"wp-image-21687\" srcset=\"https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2023\/03\/output_one_transm_multi_recv_receiver.png 435w, https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2023\/03\/output_one_transm_multi_recv_receiver-300x37.png 300w\" sizes=\"auto, (max-width: 435px) 100vw, 435px\" \/><\/a><figcaption class=\"wp-element-caption\">Output of one_transm_multi_recv_receiver.ino<\/figcaption><\/figure>\n\n<h2 class=\"wp-block-heading\" id=\"transceiver\">ESP as transceiver (ESP32)<\/h2>\n\n<p>One setup is still missing, namely the use of ESP boards as transceivers, i.e. as combined transmitters and receivers. The good news is that you don&#8217;t need any new features to do this. You also don&#8217;t have to switch between receive and transmit mode or similar. Just take one of the transmitter sketches and add the elements needed for a receiver (or the other way around).<\/p>\n<p>In my example of this, one transceiver (the &#8220;lead&#8221;) sends data to another transceiver (the &#8220;follower&#8221;) at certain intervals, which thanks the lead transceiver for doing so and reports back its runtime in seconds. Admittedly, it makes little sense, but it is only meant to illustrate the principle!<\/p>\n<p>I named the lead transceiver because it sets the pace in this specific example. Just as well, the two transceivers could send their messages to each other on an equal and event-based basis, e.g. at certain time intervals, when a certain sensor value limit is reached, when a button is pressed, or whatever comes to your mind.<\/p>\n\n<h3 class=\"wp-block-heading\">Sketch for the &#8220;lead transceiver&#8221;<\/h3>\n<\/p>\n<div class=\"scroll-paragraph\">\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-group=\"transceiver_lead.ino\" data-enlighter-title=\"transceiver_lead.ino\">#include &lt;esp_now.h&gt;\n#include &lt;WiFi.h&gt;\n\nuint8_t receiverAddress[] =  {0xC8, 0xC9, 0xA3, 0xCA, 0x22, 0x70};\nesp_now_peer_info_t peerInfo;\n\ntypedef struct messageToBeSent {\n    char text[64];\n    int intVal;\n    float floatVal;\n} messageToBeSent;\n\ntypedef struct receivedMessage {\n    char text[64];\n    long runTime;\n} receivedMessage;\n\nmessageToBeSent myMessageToBeSent; \nreceivedMessage myReceivedMessage; \n\nvoid messageSent(const uint8_t *macAddr, esp_now_send_status_t status) {\n    Serial.print(\"Send status: \");\n    if(status == ESP_NOW_SEND_SUCCESS){\n        Serial.println(\"Success\");\n    }\n    else{\n        Serial.println(\"Error\");\n    }\n}\n\nvoid messageReceived(const esp_now_recv_info *info, const uint8_t* incomingData, int len){\n    memcpy(&amp;myReceivedMessage, incomingData, sizeof(myReceivedMessage));\n    Serial.printf(\"Incoming Message from: %02X:%02X:%02X:%02X:%02X:%02X \\n\\r\", \n            info-&gt;src_addr[0], info-&gt;src_addr[1], info-&gt;src_addr[2], info-&gt;src_addr[3], info-&gt;src_addr[4], info-&gt;src_addr[5]);\n    Serial.print(\"Message: \");\n    Serial.println(myReceivedMessage.text);\n    Serial.print(\"RunTime [s]: \");\n    Serial.println(myReceivedMessage.runTime);\n    Serial.println();\n}\n\nvoid setup(){\n    Serial.begin(115200);\n    \/\/ delay(1000); \/\/ uncomment if your serial monitor is empty\n    WiFi.mode(WIFI_STA);\n    \n    if (esp_now_init() == ESP_OK) {\n        Serial.println(\"ESPNow Init success\");\n    }\n    else {\n        Serial.println(\"ESPNow Init fail\");\n        return;\n    }\n    \n    esp_now_register_send_cb((esp_now_send_cb_t)messageSent);  \n    esp_now_register_recv_cb(messageReceived); \n\n    memcpy(peerInfo.peer_addr, receiverAddress, 6);\n    peerInfo.channel = 0;\n    peerInfo.encrypt = false;\n\n    if (esp_now_add_peer(&amp;peerInfo) != ESP_OK) {\n        Serial.println(\"Failed to add peer\");\n        return;\n    }\n}\n \nvoid loop(){\n    char textMsg[] = \"Hi, here's my data for you: \";\n    memcpy(&amp;myMessageToBeSent.text, textMsg, sizeof(textMsg));\n    myMessageToBeSent.intVal = 4242;\n    myMessageToBeSent.floatVal = 42.42;\n    esp_err_t result = esp_now_send(receiverAddress, (uint8_t *) &amp;myMessageToBeSent, sizeof(myMessageToBeSent));\n    if (result != ESP_OK) {\n        Serial.println(\"Sending error\");\n    }\n    delay(5000);\n}<\/pre>\n<p>\u00a0<\/p>\n<\/div>\n<p>\n\n<p>As you can see &#8211; it&#8217;s just a combination of transmitter and receiver sketches.<\/p>\n\n<h3 class=\"wp-block-heading\">Sketch for the &#8220;Following Transceiver&#8221;<\/h3>\n\n<p>Here, on the follower page, the messages are received and acknowledged with a reply.<\/p>\n<\/p>\n<div class=\"scroll-paragraph\">\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-group=\"transceiver_follower.ino\" data-enlighter-title=\"transceiver_follower.ino\">#include &lt;esp_now.h&gt;\n#include &lt;WiFi.h&gt;\n\nuint8_t receiverAddress[] = {0x94, 0x3C, 0xC6, 0x34, 0xCF, 0xA4};\nesp_now_peer_info_t peerInfo;\n\ntypedef struct messageToBeSent{\n    char text[64];\n    long runTime;\n} messageToBeSent;\n\ntypedef struct receivedMessage {\n    char text[64];\n    int intVal;\n    float floatVal;\n} receivedMessage;\n\nmessageToBeSent myMessageToBeSent; \nreceivedMessage myReceivedMessage; \n\nvoid messageReceived(const esp_now_recv_info *info, const uint8_t* incomingData, int len){\n    memcpy(&amp;myReceivedMessage, incomingData, sizeof(myReceivedMessage));\n    Serial.printf(\"Incoming Message from: %02X:%02X:%02X:%02X:%02X:%02X \\n\\r\", \n                info-&gt;src_addr[0], info-&gt;src_addr[1], info-&gt;src_addr[2], info-&gt;src_addr[3], info-&gt;src_addr[4], info-&gt;src_addr[5]);\n    Serial.print(\"Message: \");\n    Serial.println(myReceivedMessage.text);\n    Serial.print(\"Integer Value: \");\n    Serial.println(myReceivedMessage.intVal);\n    Serial.print(\"Float Value: \");\n    Serial.println(myReceivedMessage.floatVal);\n    Serial.println();\n    Serial.println(\"Sending answer...\");\n    Serial.println(); \n\n    char textMsg[] = \"Thanks for the data!\";\n    memcpy(&amp;myMessageToBeSent.text, textMsg, sizeof(textMsg));\n    myMessageToBeSent.runTime = millis()\/1000;\n    esp_err_t result = esp_now_send(receiverAddress, (uint8_t *) &amp;myMessageToBeSent, sizeof(myMessageToBeSent));\n    if (result != ESP_OK) {\n        Serial.println(\"Sending error\");\n    }    \n}\n\nvoid setup(){\n    Serial.begin(115200);\n    \/\/ delay(1000); \/\/ uncomment if your serial monitor is empty\n    WiFi.mode(WIFI_STA);\n    \n    if (esp_now_init() == ESP_OK) {\n        Serial.println(\"ESPNow Init success\");\n    }\n    else {\n        Serial.println(\"ESPNow Init fail\");\n        return;\n    }\n\n    esp_now_register_recv_cb(messageReceived);\n    \n    memcpy(peerInfo.peer_addr, receiverAddress, 6);\n    peerInfo.channel = 0;\n    peerInfo.encrypt = false;\n\n    if (esp_now_add_peer(&amp;peerInfo) != ESP_OK) {\n        Serial.println(\"Failed to add peer\");\n        return;\n    }\n}\n \nvoid loop(){}<\/pre>\n<p>\u00a0<\/p>\n<\/div>\n<p>\n\n<h3 class=\"wp-block-heading\">Outputs:<\/h3>\n\n<p>Here is the output for the lead:<\/p>\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full\"><a href=\"https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2023\/03\/output_transceiver_lead.png\"><img loading=\"lazy\" decoding=\"async\" width=\"578\" height=\"163\" src=\"https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2023\/03\/output_transceiver_lead.png\" alt=\"\" class=\"wp-image-21693\" srcset=\"https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2023\/03\/output_transceiver_lead.png 578w, https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2023\/03\/output_transceiver_lead-300x85.png 300w, https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2023\/03\/output_transceiver_lead-350x100.png 350w\" sizes=\"auto, (max-width: 578px) 100vw, 578px\" \/><\/a><figcaption class=\"wp-element-caption\">Output of transceiver_lead.ino<\/figcaption><\/figure>\n<\/div>\n<p>And here is the output for the follower:<\/p>\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full\"><a href=\"https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2023\/03\/output_transceiver_follower.png\"><img loading=\"lazy\" decoding=\"async\" width=\"519\" height=\"108\" src=\"https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2023\/03\/output_transceiver_follower.png\" alt=\"\" class=\"wp-image-21695\" srcset=\"https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2023\/03\/output_transceiver_follower.png 519w, https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2023\/03\/output_transceiver_follower-300x62.png 300w\" sizes=\"auto, (max-width: 519px) 100vw, 519px\" \/><\/a><figcaption class=\"wp-element-caption\">Output of transceiver_follower.ino<\/figcaption><\/figure>\n<\/div>\n<p>I will not show an example of networking multiple transceivers. You should be able to deduce this easily from the previous examples.<\/p>\n\n<h2 class=\"wp-block-heading\" id=\"new_functions_v3\">New functions from board package version 3.0.0<\/h2>\n\n<p>The ESP-NOW library from ESP32 board package version 3.0.0 has received some very convenient functions. Try out the example sketches ESP_NOW_Broadcast_Master, ESP_NOW_Broadcast_Slave and ESP_NOW_Networking (File \u2192 Examples \u2192 ESP_NOW). You can use these sketches to set up ESP-NOW networks in which the participants are automatically registered without having to read out MAC addresses. However, these sketches are not necessarily easy to understand for beginners. Perhaps I will write a separate article about this.    <\/p>\n<p>Another new feature is ESP-NOW Serial. This feature is not as convenient, but is extremely easy to use. There is also an example sketch for this. I will report on ESP-NOW Serial in one of my next articles.   <\/p>\n\n<h2 class=\"wp-block-heading\" id=\"translate_to_esp8266\">&#8220;Translation&#8221; of the sketches for the ESP8266<\/h2>\n\n<p>The ESP8266 implementation of ESP-NOW is not very different from the implementation on the ESP32. However, the difference is large enough that unified sketches (via annotations or <code class=\" language-c\"><span class=\"token macro property\"><span class=\"token directive-hash\">#<\/span><span class=\"token directive keyword\">ifdef<\/span> <span class=\"token expression\">ESP32...#else<\/span><\/span><\/code> constructions) would have become confusing.&nbsp;<\/p>\n<p>Here are the main differences:<\/p>\n<ul>\n<li>The names of the libraries to be included.<\/li>\n<li>The parameters of the callback functions.&nbsp;<\/li>\n<li>For the ESP82866, its role as receiver, transmitter or transceiver must be explicitly defined with <code>esp_now_set_self_role()<\/code>.<\/li>\n<li>Return values of some functions.<\/li>\n<li>The first parameter of the function registered with <code>esp_now_register_recv_cb()<\/code> (here: <code>messageReceived()<\/code>) is <code>uint8_t* macAddr<\/code> and not <code>const esp_now_recv_info* info<\/code>.<\/li>\n<\/ul>\n<p>As an example I have &#8220;translated&#8221; the transceiver sketches for the ESP8266.<\/p>\n\n<h3 class=\"wp-block-heading\">Lead<\/h3>\n<\/p>\n<div class=\"scroll-paragraph\">\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-group=\"esp8266_transceiver_lead.ino\" data-enlighter-title=\"esp8266_transceiver_lead.ino\">#include &lt;espnow.h&gt;\n#include \"ESP8266WiFi.h\"\n\nuint8_t receiverAddress[] = {0xA4, 0xCF, 0x12, 0xDF, 0x5D, 0x89};\n\ntypedef struct messageToBeSent {\n    char text[64];\n    int intVal;\n    float floatVal;\n} messageToBeSent;\n\ntypedef struct receivedMessage {\n    char text[64];\n    long runTime;\n} receivedMessage;\n\nmessageToBeSent myMessageToBeSent; \nreceivedMessage myReceivedMessage; \n\nvoid messageSent(uint8_t *macAddr, uint8_t status) {\n    Serial.print(\"Send status: \");\n    if(status == 0){\n        Serial.println(\"Success\");\n    }\n    else{\n        Serial.println(\"Error\");\n    }\n}\n\nvoid messageReceived(uint8_t* macAddr, uint8_t* incomingData, uint8_t len){\n    memcpy(&amp;myReceivedMessage, incomingData, sizeof(myReceivedMessage));\n    Serial.printf(\"Incoming Message from: %02X:%02X:%02X:%02X:%02X:%02X \\n\\r\", \n            macAddr[0], macAddr[1], macAddr[2], macAddr[3], macAddr[4], macAddr[5]);    \n    Serial.print(\"Message: \");\n    Serial.println(myReceivedMessage.text);\n    Serial.print(\"RunTime [s]: \");\n    Serial.println(myReceivedMessage.runTime);\n    Serial.println();\n}\n\nvoid setup(){\n    Serial.begin(115200);\n    \/\/ delay(1000); \/\/\u00a0uncomment\u00a0if\u00a0your\u00a0serial\u00a0monitor\u00a0is\u00a0empty\n    WiFi.mode(WIFI_STA);\n    \n    if (esp_now_init() == 0) {\n        Serial.println(\"ESPNow Init success\");\n    }\n    else {\n        Serial.println(\"ESPNow Init fail\");\n        return;\n    }\n\n    esp_now_set_self_role(ESP_NOW_ROLE_COMBO);\n    uint8_t result = esp_now_add_peer(receiverAddress, ESP_NOW_ROLE_COMBO, 0, NULL, 0);\n    if(result != 0){\n        Serial.println(\"Failed to add peer\");\n    }\n    \n    esp_now_register_send_cb(messageSent);  \n    esp_now_register_recv_cb(messageReceived); \n\n   \n}\n \nvoid loop(){\n    char textMsg[] = \"Hi, here's my data for you: \";\n    memcpy(&amp;myMessageToBeSent.text, textMsg, sizeof(textMsg));\n    myMessageToBeSent.intVal = 4242;\n    myMessageToBeSent.floatVal = 42.42;\n    esp_now_send(receiverAddress, (uint8_t *) &amp;myMessageToBeSent, sizeof(myMessageToBeSent));\n    delay(5000);\n}<\/pre>\n<p>\u00a0<\/p>\n<\/div>\n<p>\n\n<h3 class=\"wp-block-heading\">Followers<\/h3>\n<\/p>\n<div class=\"scroll-paragraph\">\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-group=\"esp8266_transceiver_follower.ino\" data-enlighter-title=\"esp8266_transceiver_follower.ino\">#include &lt;espnow.h&gt;\n#include \"ESP8266WiFi.h\"\n\nuint8_t receiverAddress[] = {0x44, 0x17, 0x93, 0x0E, 0x2E, 0xED};\n\ntypedef struct messageToBeSent{\n    char text[64];\n    long runTime;\n} messageToBeSent;\n\ntypedef struct receivedMessage {\n    char text[64];\n    int intVal;\n    float floatVal;\n} receivedMessage;\n\nmessageToBeSent myMessageToBeSent; \nreceivedMessage myReceivedMessage; \n\nvoid messageReceived(uint8_t* macAddr, uint8_t* incomingData, uint8_t len){\n    memcpy(&amp;myReceivedMessage, incomingData, sizeof(myReceivedMessage));\n    Serial.printf(\"Incoming Message from: %02X:%02X:%02X:%02X:%02X:%02X \\n\\r\", \n            macAddr[0], macAddr[1], macAddr[2], macAddr[3], macAddr[4], macAddr[5]);\n    Serial.print(\"Message: \");\n    Serial.println(myReceivedMessage.text);\n    Serial.print(\"Integer Value: \");\n    Serial.println(myReceivedMessage.intVal);\n    Serial.print(\"Float Value: \");\n    Serial.println(myReceivedMessage.floatVal);\n    Serial.println();\n    Serial.println(\"Sending answer...\");\n    Serial.println(); \n\n    char textMsg[] = \"Thanks for the data!\";\n    memcpy(&amp;myMessageToBeSent.text, textMsg, sizeof(textMsg));\n    myMessageToBeSent.runTime = millis()\/1000;\n    esp_now_send(receiverAddress, (uint8_t *) &amp;myMessageToBeSent, sizeof(myMessageToBeSent));    \n}    \n\nvoid setup(){\n    Serial.begin(115200);\n    \/\/ delay(1000); \/\/\u00a0uncomment\u00a0if\u00a0your\u00a0serial\u00a0monitor\u00a0is\u00a0empty\n    WiFi.mode(WIFI_STA);\n    \n    if (esp_now_init() == 0) {\n        Serial.println(\"ESPNow Init success\");\n    }\n    else {\n        Serial.println(\"ESPNow Init fail\");\n        return;\n    }\n\n    esp_now_set_self_role(ESP_NOW_ROLE_COMBO);\n    uint8_t result = esp_now_add_peer(receiverAddress, ESP_NOW_ROLE_COMBO, 0, NULL, 0);\n    if(result != 0){\n        Serial.println(\"Failed to add peer\");\n    }\n    \n    esp_now_register_recv_cb(messageReceived);\n}\n \nvoid loop(){}<\/pre>\n<p>\u00a0<\/p>\n<\/div>\n<p>\n\n<h2 class=\"wp-block-heading\" id=\"mix_esp32_esp8266\">Mixing ESP32 and ESP826 boards<\/h2>\n\n<p>This is (now?) no longer a problem. Contrary to my previous statements, ESP32 and ESP8266 boards can communicate with each other without any problems. Either something has changed in the ESP-NOW libraries, or I did something stupid in my previous attempts. I have verified this using the transceiver sketches on an ESP32 development board and a Wemos D1 Mini.     <\/p>\n\n<h2 class=\"wp-block-heading\" id=\"change_channel\">Changing channels (ESP32 and ESP8266) <\/h2>\n\n<p>If you have range problems due to interaction with other WiFi networks, then switching to a different channel might help. The following two sketches show how to change the channel for an ESP32 and an ESP8266 board. All participants in a network must of course be set to the same channel.<\/p>\n\n<h3 class=\"wp-block-heading\">ESP32<\/h3>\n<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-group=\"esp32_change_channel.ino\" data-enlighter-title=\"esp32_change_channel.ino\">#include &lt;esp_now.h&gt;\n#include &lt;esp_wifi.h&gt;\n#include &lt;WiFi.h&gt;\n#define CHANNEL 13\n\nvoid setup(){\n    Serial.begin(115200);\n    \/\/ delay(1000); \/\/ uncomment if your serial monitor is empty\n    WiFi.mode(WIFI_STA);\n    Serial.print(\"WiFi-Channel Default: \");\n    Serial.println(WiFi.channel());\n    \n    esp_wifi_set_promiscuous(true);\n    esp_wifi_set_channel(CHANNEL, WIFI_SECOND_CHAN_NONE);\n    esp_wifi_set_promiscuous(false);\n    \n    Serial.print(\"WiFi-Channel Update : \");\n    Serial.println(WiFi.channel());\n}\n     \nvoid loop(){}<\/pre>\n<p>\n\n<h3 class=\"wp-block-heading\">ESP8266<\/h3>\n<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-group=\"esp8266_change_channel.ino\" data-enlighter-title=\"esp8266_change_channel.ino\">#include &lt;espnow.h&gt;\n#include \"ESP8266WiFi.h\"\n#define CHANNEL 13\n\nvoid setup(){\n    Serial.begin(115200);\n    \/\/ delay(1000); \/\/\u00a0uncomment\u00a0if\u00a0your\u00a0serial\u00a0monitor\u00a0is\u00a0empty\n    WiFi.mode(WIFI_STA);\n    Serial.print(\"WiFi-Channel Default: \");\n    Serial.println(WiFi.channel());\n    \n    wifi_promiscuous_enable(true);\n    wifi_set_channel(CHANNEL);\n    wifi_promiscuous_enable(false); \n\n    Serial.print(\"WiFi Channel Update : \");\n    Serial.println(WiFi.channel());\n}\n \nvoid loop(){}<\/pre>\n<p>\n\n<h2 class=\"wp-block-heading\" id=\"range\">Range<\/h2>\n\n<p>If you google for range tests for ESP-NOW, you will come across maximum values between 100 and 250 meters, as long as the tests were conducted outdoors, with a free line of sight and without external antennas. How far you get in your apartment or house depends mainly on the building fabric.<\/p>\n<p>In my own range tests, I used two Wemos D1 Mini Boards and two ESP32-WROOM-32 based development boards. Both pairs achieved identical ranges. In the neighboring room, i.e. through a wall, no messages were lost. One room further on, i.e. through two walls, I only had reception in some areas. This is roughly comparable to the range of my Fritz!Box 7590.<\/p>\n<p>If the range is not sufficient, you could place a module as a repeater between the modules to be connected. Or you can use modules that allow you to connect external antennas.<\/p>\n\n<h2 class=\"wp-block-heading\">Acknowledgement <\/h2>\n\n<p>I have <a href=\"https:\/\/pixabay.com\/de\/users\/maklay62-182851\/?utm_source=link-attribution&amp;utm_medium=referral&amp;utm_campaign=image&amp;utm_content=1432953\" target=\"_blank\" rel=\"noopener\">S K<\/a> on <a href=\"https:\/\/pixabay.com\/de\/\/?utm_source=link-attribution&amp;utm_medium=referral&amp;utm_campaign=image&amp;utm_content=1432953\" target=\"_blank\" rel=\"noopener\">Pixabay<\/a> to thank for the &#8220;NOW note&#8221; on the post image.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>With ESP-NOW you can easily send messages of up to 250 bytes between ESP32 and\/or ESP8266 boards. A step-by-step introduction. <\/p>\n","protected":false},"author":1,"featured_media":17368,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[543,548,1320,570],"tags":[556,2543,2037,2039,2036,2035,1044,655,2038,564,2040,2540,2541,1262],"class_list":["post-17655","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-boards-and-microcontrollers","category-wireless","category-wireless-en-2","category-wlan-en","tag-arduino-en-2","tag-broadcast-en","tag-change-channel","tag-change-mac-address","tag-esp_now_send-en","tag-esp-now-en","tag-esp32-en","tag-esp8266-en","tag-mac-address","tag-range-en","tag-test-en","tag-version-3-0-0-en","tag-version-3-x-en","tag-wemos-d1-mini-en"],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v27.4 - https:\/\/yoast.com\/product\/yoast-seo-wordpress\/ -->\n<title>ESP-NOW &#8226; Wolles Elektronikkiste<\/title>\n<meta name=\"description\" content=\"With ESP-NOW you can easily send messages of up to 250 bytes between ESP32 and ESP8266 boards. A step-by-step introduction.\" \/>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/wolles-elektronikkiste.de\/en\/esp-now\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"ESP-NOW &#8226; Wolles Elektronikkiste\" \/>\n<meta property=\"og:description\" content=\"With ESP-NOW you can easily send messages of up to 250 bytes between ESP32 and ESP8266 boards. A step-by-step introduction.\" \/>\n<meta property=\"og:url\" content=\"https:\/\/wolles-elektronikkiste.de\/en\/esp-now\" \/>\n<meta property=\"og:site_name\" content=\"Wolles Elektronikkiste\" \/>\n<meta property=\"article:published_time\" content=\"2023-03-31T19:04:43+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2025-09-14T19:27:13+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2023\/02\/post_image_esp_now.png\" \/>\n\t<meta property=\"og:image:width\" content=\"1000\" \/>\n\t<meta property=\"og:image:height\" content=\"1000\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/png\" \/>\n<meta name=\"author\" content=\"Wolfgang Ewald\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"Wolfgang Ewald\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"29 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\\\/\\\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\\\/\\\/wolles-elektronikkiste.de\\\/en\\\/esp-now#article\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/wolles-elektronikkiste.de\\\/en\\\/esp-now\"},\"author\":{\"name\":\"Wolfgang Ewald\",\"@id\":\"https:\\\/\\\/wolles-elektronikkiste.de\\\/en#\\\/schema\\\/person\\\/b774e4d64b4766889a2f7c6e5ec85b46\"},\"headline\":\"ESP-NOW\",\"datePublished\":\"2023-03-31T19:04:43+00:00\",\"dateModified\":\"2025-09-14T19:27:13+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\\\/\\\/wolles-elektronikkiste.de\\\/en\\\/esp-now\"},\"wordCount\":3116,\"commentCount\":14,\"publisher\":{\"@id\":\"https:\\\/\\\/wolles-elektronikkiste.de\\\/en#\\\/schema\\\/person\\\/b774e4d64b4766889a2f7c6e5ec85b46\"},\"image\":{\"@id\":\"https:\\\/\\\/wolles-elektronikkiste.de\\\/en\\\/esp-now#primaryimage\"},\"thumbnailUrl\":\"https:\\\/\\\/wolles-elektronikkiste.de\\\/wp-content\\\/uploads\\\/2023\\\/02\\\/post_image_esp_now.png\",\"keywords\":[\"Arduino\",\"broadcast\",\"change channel\",\"change MAC address\",\"esp_now_send\",\"ESP-NOW\",\"ESP32\",\"ESP8266\",\"MAC address\",\"Range\",\"test\",\"version 3.0.0\",\"version 3.x\",\"Wemos D1 Mini\"],\"articleSection\":[\"Boards and Microcontrollers\",\"Wireless\",\"wireless\",\"WLAN\"],\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\\\/\\\/wolles-elektronikkiste.de\\\/en\\\/esp-now#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\\\/\\\/wolles-elektronikkiste.de\\\/en\\\/esp-now\",\"url\":\"https:\\\/\\\/wolles-elektronikkiste.de\\\/en\\\/esp-now\",\"name\":\"ESP-NOW &#8226; Wolles Elektronikkiste\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/wolles-elektronikkiste.de\\\/en#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\\\/\\\/wolles-elektronikkiste.de\\\/en\\\/esp-now#primaryimage\"},\"image\":{\"@id\":\"https:\\\/\\\/wolles-elektronikkiste.de\\\/en\\\/esp-now#primaryimage\"},\"thumbnailUrl\":\"https:\\\/\\\/wolles-elektronikkiste.de\\\/wp-content\\\/uploads\\\/2023\\\/02\\\/post_image_esp_now.png\",\"datePublished\":\"2023-03-31T19:04:43+00:00\",\"dateModified\":\"2025-09-14T19:27:13+00:00\",\"description\":\"With ESP-NOW you can easily send messages of up to 250 bytes between ESP32 and ESP8266 boards. A step-by-step introduction.\",\"breadcrumb\":{\"@id\":\"https:\\\/\\\/wolles-elektronikkiste.de\\\/en\\\/esp-now#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\\\/\\\/wolles-elektronikkiste.de\\\/en\\\/esp-now\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\\\/\\\/wolles-elektronikkiste.de\\\/en\\\/esp-now#primaryimage\",\"url\":\"https:\\\/\\\/wolles-elektronikkiste.de\\\/wp-content\\\/uploads\\\/2023\\\/02\\\/post_image_esp_now.png\",\"contentUrl\":\"https:\\\/\\\/wolles-elektronikkiste.de\\\/wp-content\\\/uploads\\\/2023\\\/02\\\/post_image_esp_now.png\",\"width\":1000,\"height\":1000},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\\\/\\\/wolles-elektronikkiste.de\\\/en\\\/esp-now#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Startseite\",\"item\":\"https:\\\/\\\/wolles-elektronikkiste.de\\\/en\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"ESP-NOW\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\\\/\\\/wolles-elektronikkiste.de\\\/en#website\",\"url\":\"https:\\\/\\\/wolles-elektronikkiste.de\\\/en\",\"name\":\"Wolles Elektronikkiste\",\"description\":\"Die wunderbare Welt der Elektronik\",\"publisher\":{\"@id\":\"https:\\\/\\\/wolles-elektronikkiste.de\\\/en#\\\/schema\\\/person\\\/b774e4d64b4766889a2f7c6e5ec85b46\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\\\/\\\/wolles-elektronikkiste.de\\\/en?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":[\"Person\",\"Organization\"],\"@id\":\"https:\\\/\\\/wolles-elektronikkiste.de\\\/en#\\\/schema\\\/person\\\/b774e4d64b4766889a2f7c6e5ec85b46\",\"name\":\"Wolfgang Ewald\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\\\/\\\/wolles-elektronikkiste.de\\\/wp-content\\\/uploads\\\/2019\\\/03\\\/cropped-Logo-1.png\",\"url\":\"https:\\\/\\\/wolles-elektronikkiste.de\\\/wp-content\\\/uploads\\\/2019\\\/03\\\/cropped-Logo-1.png\",\"contentUrl\":\"https:\\\/\\\/wolles-elektronikkiste.de\\\/wp-content\\\/uploads\\\/2019\\\/03\\\/cropped-Logo-1.png\",\"width\":512,\"height\":512,\"caption\":\"Wolfgang Ewald\"},\"logo\":{\"@id\":\"https:\\\/\\\/wolles-elektronikkiste.de\\\/wp-content\\\/uploads\\\/2019\\\/03\\\/cropped-Logo-1.png\"}}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"ESP-NOW &#8226; Wolles Elektronikkiste","description":"With ESP-NOW you can easily send messages of up to 250 bytes between ESP32 and ESP8266 boards. A step-by-step introduction.","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/wolles-elektronikkiste.de\/en\/esp-now","og_locale":"en_US","og_type":"article","og_title":"ESP-NOW &#8226; Wolles Elektronikkiste","og_description":"With ESP-NOW you can easily send messages of up to 250 bytes between ESP32 and ESP8266 boards. A step-by-step introduction.","og_url":"https:\/\/wolles-elektronikkiste.de\/en\/esp-now","og_site_name":"Wolles Elektronikkiste","article_published_time":"2023-03-31T19:04:43+00:00","article_modified_time":"2025-09-14T19:27:13+00:00","og_image":[{"width":1000,"height":1000,"url":"https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2023\/02\/post_image_esp_now.png","type":"image\/png"}],"author":"Wolfgang Ewald","twitter_card":"summary_large_image","twitter_misc":{"Written by":"Wolfgang Ewald","Est. reading time":"29 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/wolles-elektronikkiste.de\/en\/esp-now#article","isPartOf":{"@id":"https:\/\/wolles-elektronikkiste.de\/en\/esp-now"},"author":{"name":"Wolfgang Ewald","@id":"https:\/\/wolles-elektronikkiste.de\/en#\/schema\/person\/b774e4d64b4766889a2f7c6e5ec85b46"},"headline":"ESP-NOW","datePublished":"2023-03-31T19:04:43+00:00","dateModified":"2025-09-14T19:27:13+00:00","mainEntityOfPage":{"@id":"https:\/\/wolles-elektronikkiste.de\/en\/esp-now"},"wordCount":3116,"commentCount":14,"publisher":{"@id":"https:\/\/wolles-elektronikkiste.de\/en#\/schema\/person\/b774e4d64b4766889a2f7c6e5ec85b46"},"image":{"@id":"https:\/\/wolles-elektronikkiste.de\/en\/esp-now#primaryimage"},"thumbnailUrl":"https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2023\/02\/post_image_esp_now.png","keywords":["Arduino","broadcast","change channel","change MAC address","esp_now_send","ESP-NOW","ESP32","ESP8266","MAC address","Range","test","version 3.0.0","version 3.x","Wemos D1 Mini"],"articleSection":["Boards and Microcontrollers","Wireless","wireless","WLAN"],"inLanguage":"en-US","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/wolles-elektronikkiste.de\/en\/esp-now#respond"]}]},{"@type":"WebPage","@id":"https:\/\/wolles-elektronikkiste.de\/en\/esp-now","url":"https:\/\/wolles-elektronikkiste.de\/en\/esp-now","name":"ESP-NOW &#8226; Wolles Elektronikkiste","isPartOf":{"@id":"https:\/\/wolles-elektronikkiste.de\/en#website"},"primaryImageOfPage":{"@id":"https:\/\/wolles-elektronikkiste.de\/en\/esp-now#primaryimage"},"image":{"@id":"https:\/\/wolles-elektronikkiste.de\/en\/esp-now#primaryimage"},"thumbnailUrl":"https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2023\/02\/post_image_esp_now.png","datePublished":"2023-03-31T19:04:43+00:00","dateModified":"2025-09-14T19:27:13+00:00","description":"With ESP-NOW you can easily send messages of up to 250 bytes between ESP32 and ESP8266 boards. A step-by-step introduction.","breadcrumb":{"@id":"https:\/\/wolles-elektronikkiste.de\/en\/esp-now#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/wolles-elektronikkiste.de\/en\/esp-now"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/wolles-elektronikkiste.de\/en\/esp-now#primaryimage","url":"https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2023\/02\/post_image_esp_now.png","contentUrl":"https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2023\/02\/post_image_esp_now.png","width":1000,"height":1000},{"@type":"BreadcrumbList","@id":"https:\/\/wolles-elektronikkiste.de\/en\/esp-now#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Startseite","item":"https:\/\/wolles-elektronikkiste.de\/en"},{"@type":"ListItem","position":2,"name":"ESP-NOW"}]},{"@type":"WebSite","@id":"https:\/\/wolles-elektronikkiste.de\/en#website","url":"https:\/\/wolles-elektronikkiste.de\/en","name":"Wolles Elektronikkiste","description":"Die wunderbare Welt der Elektronik","publisher":{"@id":"https:\/\/wolles-elektronikkiste.de\/en#\/schema\/person\/b774e4d64b4766889a2f7c6e5ec85b46"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/wolles-elektronikkiste.de\/en?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":["Person","Organization"],"@id":"https:\/\/wolles-elektronikkiste.de\/en#\/schema\/person\/b774e4d64b4766889a2f7c6e5ec85b46","name":"Wolfgang Ewald","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2019\/03\/cropped-Logo-1.png","url":"https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2019\/03\/cropped-Logo-1.png","contentUrl":"https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2019\/03\/cropped-Logo-1.png","width":512,"height":512,"caption":"Wolfgang Ewald"},"logo":{"@id":"https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2019\/03\/cropped-Logo-1.png"}}]}},"_links":{"self":[{"href":"https:\/\/wolles-elektronikkiste.de\/en\/wp-json\/wp\/v2\/posts\/17655","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/wolles-elektronikkiste.de\/en\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/wolles-elektronikkiste.de\/en\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/wolles-elektronikkiste.de\/en\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/wolles-elektronikkiste.de\/en\/wp-json\/wp\/v2\/comments?post=17655"}],"version-history":[{"count":2,"href":"https:\/\/wolles-elektronikkiste.de\/en\/wp-json\/wp\/v2\/posts\/17655\/revisions"}],"predecessor-version":[{"id":24862,"href":"https:\/\/wolles-elektronikkiste.de\/en\/wp-json\/wp\/v2\/posts\/17655\/revisions\/24862"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/wolles-elektronikkiste.de\/en\/wp-json\/wp\/v2\/media\/17368"}],"wp:attachment":[{"href":"https:\/\/wolles-elektronikkiste.de\/en\/wp-json\/wp\/v2\/media?parent=17655"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/wolles-elektronikkiste.de\/en\/wp-json\/wp\/v2\/categories?post=17655"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/wolles-elektronikkiste.de\/en\/wp-json\/wp\/v2\/tags?post=17655"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}