diff --git a/CMakeLists.txt b/CMakeLists.txt index 9aa2b55e2a9..cc294494f43 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -83,6 +83,7 @@ set(ARDUINO_ALL_LIBRARIES DNSServer EEPROM ESP_I2S + ESP_NOW ESP_SR ESPmDNS Ethernet @@ -127,6 +128,10 @@ set(ARDUINO_LIBRARY_DNSServer_SRCS libraries/DNSServer/src/DNSServer.cpp) set(ARDUINO_LIBRARY_EEPROM_SRCS libraries/EEPROM/src/EEPROM.cpp) set(ARDUINO_LIBRARY_ESP_I2S_SRCS libraries/ESP_I2S/src/ESP_I2S.cpp) + +set(ARDUINO_LIBRARY_ESP_NOW_SRCS + libraries/ESP_NOW/src/ESP32_NOW.cpp + libraries/ESP_NOW/src/ESP32_NOW_Serial.cpp) set(ARDUINO_LIBRARY_ESP_SR_SRCS libraries/ESP_SR/src/ESP_SR.cpp diff --git a/docs/en/api/espnow.rst b/docs/en/api/espnow.rst index e6c5328c188..48472881599 100644 --- a/docs/en/api/espnow.rst +++ b/docs/en/api/espnow.rst @@ -2,29 +2,275 @@ ESP-NOW ####### -ESP-NOW is a fast, connectionless communication technology featuring a short packet transmission. -ESP-NOW is ideal for smart lights, remote control devices, sensors and other applications. +About +----- -.. note:: This is a work in progress project and this section is still missing. If you want to contribute, please see the `Contributions Guide <../contributing.html>`_. +ESP-NOW is a communication protocol designed for low-power, low-latency, and high-throughput communication between ESP32 devices without the need for an access point (AP). +It is ideal for scenarios where devices need to communicate directly with each other in a local network. +ESP-NOW can be used for smart lights, remote control devices, sensors and many other applications. + +This library provides an easy-to-use interface for setting up ESP-NOW communication, adding and removing peers, and sending and receiving data packets. + +Arduino-ESP32 ESP-NOW API +------------------------- + +ESP-NOW Class +************* + +The `ESP_NOW_Class` is the main class used for managing ESP-NOW communication. + +begin +^^^^^ + +Initialize the ESP-NOW communication. This function must be called before using any other ESP-NOW functionalities. + +.. code-block:: cpp + + bool begin(const uint8_t *pmk = NULL); + +* ``pmk``: Optional. Pass the pairwise master key (PMK) if encryption is enabled. + +Returns ``true`` if initialization is successful, ``false`` otherwise. + +end +^^^ + +End the ESP-NOW communication. This function releases all resources used by the ESP-NOW library. + +.. code-block:: cpp + + bool end(); + +Returns ``true`` if the operation is successful, ``false`` otherwise. + +getTotalPeerCount +^^^^^^^^^^^^^^^^^ + +Get the total number of peers currently added. + +.. code-block:: cpp + + int getTotalPeerCount(); + +Returns the total number of peers, or ``-1`` if an error occurs. + +getEncryptedPeerCount +^^^^^^^^^^^^^^^^^^^^^ + +Get the number of peers using encryption. + +.. code-block:: cpp + + int getEncryptedPeerCount(); + +Returns the number of peers using encryption, or ``-1`` if an error occurs. + +onNewPeer +^^^^^^^^^ + +You can register a callback function to handle incoming data from new peers using the `onNewPeer` function. + +.. code-block:: cpp + + void onNewPeer(void (*cb)(const esp_now_recv_info_t *info, const uint8_t *data, int len, void *arg), void *arg); + +* ``cb``: Pointer to the callback function. +* ``arg``: Optional. Pointer to user-defined argument to be passed to the callback function. + +``cb`` function signature: + +.. code-block:: cpp + + void cb(const esp_now_recv_info_t *info, const uint8_t *data, int len, void *arg); + +``info``: Information about the received packet. +``data``: Pointer to the received data. +``len``: Length of the received data. +``arg``: User-defined argument passed to the callback function. + +ESP-NOW Peer Class +****************** + +The `ESP_NOW_Peer` class represents a peer device in the ESP-NOW network. It is an abstract class that must be inherited by a child class that properly handles the peer connections and implements the `_onReceive` and `_onSent` methods. + +Constructor +^^^^^^^^^^^ + +Create an instance of the `ESP_NOW_Peer` class. + +.. code-block:: cpp + + ESP_NOW_Peer(const uint8_t *mac_addr, uint8_t channel, wifi_interface_t iface, const uint8_t *lmk); + +* ``mac_addr``: MAC address of the peer device. +* ``channel``: Communication channel. +* ``iface``: WiFi interface. +* ``lmk``: Optional. Pass the local master key (LMK) if encryption is enabled. + +add +^^^ + +Add the peer to the ESP-NOW network. + +.. code-block:: cpp + + bool add(); + +Returns ``true`` if the peer is added successfully, ``false`` otherwise. + +remove +^^^^^^ + +Remove the peer from the ESP-NOW network. + +.. code-block:: cpp + + bool remove(); + +Returns ``true`` if the peer is removed successfully, ``false`` otherwise. + +send +^^^^ + +Send data to the peer. + +.. code-block:: cpp + + size_t send(const uint8_t *data, int len); + +* ``data``: Pointer to the data to be sent. +* ``len``: Length of the data in bytes. + +Returns the number of bytes sent, or ``0`` if an error occurs. + +addr +^^^^ + +Get the MAC address of the peer. + +.. code-block:: cpp + + const uint8_t * addr() const; + +Returns a pointer to the MAC address. + +addr +^^^^ + +Set the MAC address of the peer. + +.. code-block:: cpp + + void addr(const uint8_t *mac_addr); + +* ``mac_addr``: MAC address of the peer. + +getChannel +^^^^^^^^^^ + +Get the communication channel of the peer. + +.. code-block:: cpp + + uint8_t getChannel() const; + +Returns the communication channel. + +setChannel +^^^^^^^^^^ + +Set the communication channel of the peer. + +.. code-block:: cpp + + void setChannel(uint8_t channel); + +* ``channel``: Communication channel. + +getInterface +^^^^^^^^^^^^ + +Get the WiFi interface of the peer. + +.. code-block:: cpp + + wifi_interface_t getInterface() const; + +Returns the WiFi interface. + +setInterface +^^^^^^^^^^^^ + +Set the WiFi interface of the peer. + +.. code-block:: cpp + + void setInterface(wifi_interface_t iface); + +* ``iface``: WiFi interface. + +isEncrypted +^^^^^^^^^^^ + +Check if the peer is using encryption. + +.. code-block:: cpp + + bool isEncrypted() const; + +Returns ``true`` if the peer is using encryption, ``false`` otherwise. + +setKey +^^^^^^ + +Set the local master key (LMK) for the peer. + +.. code-block:: cpp + + void setKey(const uint8_t *lmk); + +* ``lmk``: Local master key. + +onReceive +^^^^^^^^^^ + +Callback function to handle incoming data from the peer. This is a virtual method can be implemented by the upper class for custom handling. + +.. code-block:: cpp + + void onReceive(const uint8_t *data, int len, bool broadcast); + +* ``data``: Pointer to the received data. +* ``len``: Length of the received data. +* ``broadcast``: ``true`` if the data is broadcasted, ``false`` otherwise. + +onSent +^^^^^^^ + +Callback function to handle the completion of sending data to the peer. This is a virtual method can be implemented by the upper class for custom handling. + +.. code-block:: cpp + + void onSent(bool success); + +* ``success``: ``true`` if the data is sent successfully, ``false`` otherwise. Examples -------- -ESP-NOW Master -************** +Set of 2 examples of the ESP-NOW library to send and receive data using broadcast messages between multiple ESP32 devices (multiple masters, multiple slaves). -.. literalinclude:: ../../../libraries/ESP32/examples/ESPNow/ESPNow_Basic_Master/ESPNow_Basic_Master.ino - :language: arduino +1. ESP-NOW Broadcast Master Example: -ESP-NOW Slave -************* +.. literalinclude:: ../../../libraries/ESP_NOW/examples/ESP_NOW_Broadcast_Master/ESP_NOW_Broadcast_Master.ino + :language: cpp -.. literalinclude:: ../../../libraries/ESP32/examples/ESPNow/ESPNow_Basic_Slave/ESPNow_Basic_Slave.ino - :language: arduino +2. ESP-NOW Broadcast Slave Example: -Resources ---------- +.. literalinclude:: ../../../libraries/ESP_NOW/examples/ESP_NOW_Broadcast_Slave/ESP_NOW_Broadcast_Slave.ino + :language: cpp -* `ESP-NOW`_ (User Guide) +Example of the ESP-NOW Serial library to send and receive data as a stream between 2 ESP32 devices using the serial monitor: -.. _ESP-NOW: https://www.espressif.com/sites/default/files/documentation/esp-now_user_guide_en.pdf +.. literalinclude:: ../../../libraries/ESP_NOW/examples/ESP_NOW_Serial/ESP_NOW_Serial.ino + :language: cpp diff --git a/libraries/ESP32/examples/ESPNow/ESPNow_Basic_Master/ESPNow_Basic_Master.ino b/libraries/ESP32/examples/ESPNow/ESPNow_Basic_Master/ESPNow_Basic_Master.ino deleted file mode 100644 index b2e6b35506c..00000000000 --- a/libraries/ESP32/examples/ESPNow/ESPNow_Basic_Master/ESPNow_Basic_Master.ino +++ /dev/null @@ -1,262 +0,0 @@ -/** - ESPNOW - Basic communication - Master - Date: 26th September 2017 - Author: Arvind Ravulavaru - Purpose: ESPNow Communication between a Master ESP32 and a Slave ESP32 - Description: This sketch consists of the code for the Master module. - Resources: (A bit outdated) - a. https://espressif.com/sites/default/files/documentation/esp-now_user_guide_en.pdf - b. http://www.esploradores.com/practica-6-conexion-esp-now/ - - << This Device Master >> - - Flow: Master - Step 1 : ESPNow Init on Master and set it in STA mode - Step 2 : Start scanning for Slave ESP32 (we have added a prefix of `slave` to the SSID of slave for an easy setup) - Step 3 : Once found, add Slave as peer - Step 4 : Register for send callback - Step 5 : Start Transmitting data from Master to Slave - - Flow: Slave - Step 1 : ESPNow Init on Slave - Step 2 : Update the SSID of Slave with a prefix of `slave` - Step 3 : Set Slave in AP mode - Step 4 : Register for receive callback and wait for data - Step 5 : Once data arrives, print it in the serial monitor - - Note: Master and Slave have been defined to easily understand the setup. - Based on the ESPNOW API, there is no concept of Master and Slave. - Any devices can act as master or salve. -*/ - -#include -#include -#include // only for esp_wifi_set_channel() - -// Global copy of slave -esp_now_peer_info_t slave; -#define CHANNEL 1 -#define PRINTSCANRESULTS 0 -#define DELETEBEFOREPAIR 0 - -// Init ESP Now with fallback -void InitESPNow() { - WiFi.disconnect(); - if (esp_now_init() == ESP_OK) { - Serial.println("ESPNow Init Success"); - } - else { - Serial.println("ESPNow Init Failed"); - // Retry InitESPNow, add a counte and then restart? - // InitESPNow(); - // or Simply Restart - ESP.restart(); - } -} - -// Scan for slaves in AP mode -void ScanForSlave() { - int16_t scanResults = WiFi.scanNetworks(false, false, false, 300, CHANNEL); // Scan only on one channel - // reset on each scan - bool slaveFound = 0; - memset(&slave, 0, sizeof(slave)); - - Serial.println(""); - if (scanResults == 0) { - Serial.println("No WiFi devices in AP Mode found"); - } else { - Serial.print("Found "); Serial.print(scanResults); Serial.println(" devices "); - for (int i = 0; i < scanResults; ++i) { - // Print SSID and RSSI for each device found - String SSID = WiFi.SSID(i); - int32_t RSSI = WiFi.RSSI(i); - String BSSIDstr = WiFi.BSSIDstr(i); - - if (PRINTSCANRESULTS) { - Serial.print(i + 1); - Serial.print(": "); - Serial.print(SSID); - Serial.print(" ("); - Serial.print(RSSI); - Serial.print(")"); - Serial.println(""); - } - delay(10); - // Check if the current device starts with `Slave` - if (SSID.indexOf("Slave") == 0) { - // SSID of interest - Serial.println("Found a Slave."); - Serial.print(i + 1); Serial.print(": "); Serial.print(SSID); Serial.print(" ["); Serial.print(BSSIDstr); Serial.print("]"); Serial.print(" ("); Serial.print(RSSI); Serial.print(")"); Serial.println(""); - // Get BSSID => Mac Address of the Slave - int mac[6]; - if ( 6 == sscanf(BSSIDstr.c_str(), "%x:%x:%x:%x:%x:%x", &mac[0], &mac[1], &mac[2], &mac[3], &mac[4], &mac[5] ) ) { - for (int ii = 0; ii < 6; ++ii ) { - slave.peer_addr[ii] = (uint8_t) mac[ii]; - } - } - - slave.channel = CHANNEL; // pick a channel - slave.encrypt = 0; // no encryption - - slaveFound = 1; - // we are planning to have only one slave in this example; - // Hence, break after we find one, to be a bit efficient - break; - } - } - } - - if (slaveFound) { - Serial.println("Slave Found, processing.."); - } else { - Serial.println("Slave Not Found, trying again."); - } - - // clean up ram - WiFi.scanDelete(); -} - -// Check if the slave is already paired with the master. -// If not, pair the slave with master -bool manageSlave() { - if (slave.channel == CHANNEL) { - if (DELETEBEFOREPAIR) { - deletePeer(); - } - - Serial.print("Slave Status: "); - // check if the peer exists - bool exists = esp_now_is_peer_exist(slave.peer_addr); - if ( exists) { - // Slave already paired. - Serial.println("Already Paired"); - return true; - } else { - // Slave not paired, attempt pair - esp_err_t addStatus = esp_now_add_peer(&slave); - if (addStatus == ESP_OK) { - // Pair success - Serial.println("Pair success"); - return true; - } else if (addStatus == ESP_ERR_ESPNOW_NOT_INIT) { - // How did we get so far!! - Serial.println("ESPNOW Not Init"); - return false; - } else if (addStatus == ESP_ERR_ESPNOW_ARG) { - Serial.println("Invalid Argument"); - return false; - } else if (addStatus == ESP_ERR_ESPNOW_FULL) { - Serial.println("Peer list full"); - return false; - } else if (addStatus == ESP_ERR_ESPNOW_NO_MEM) { - Serial.println("Out of memory"); - return false; - } else if (addStatus == ESP_ERR_ESPNOW_EXIST) { - Serial.println("Peer Exists"); - return true; - } else { - Serial.println("Not sure what happened"); - return false; - } - } - } else { - // No slave found to process - Serial.println("No Slave found to process"); - return false; - } -} - -void deletePeer() { - esp_err_t delStatus = esp_now_del_peer(slave.peer_addr); - Serial.print("Slave Delete Status: "); - if (delStatus == ESP_OK) { - // Delete success - Serial.println("Success"); - } else if (delStatus == ESP_ERR_ESPNOW_NOT_INIT) { - // How did we get so far!! - Serial.println("ESPNOW Not Init"); - } else if (delStatus == ESP_ERR_ESPNOW_ARG) { - Serial.println("Invalid Argument"); - } else if (delStatus == ESP_ERR_ESPNOW_NOT_FOUND) { - Serial.println("Peer not found."); - } else { - Serial.println("Not sure what happened"); - } -} - -uint8_t data = 0; -// send data -void sendData() { - data++; - const uint8_t *peer_addr = slave.peer_addr; - Serial.print("Sending: "); Serial.println(data); - esp_err_t result = esp_now_send(peer_addr, &data, sizeof(data)); - Serial.print("Send Status: "); - if (result == ESP_OK) { - Serial.println("Success"); - } else if (result == ESP_ERR_ESPNOW_NOT_INIT) { - // How did we get so far!! - Serial.println("ESPNOW not Init."); - } else if (result == ESP_ERR_ESPNOW_ARG) { - Serial.println("Invalid Argument"); - } else if (result == ESP_ERR_ESPNOW_INTERNAL) { - Serial.println("Internal Error"); - } else if (result == ESP_ERR_ESPNOW_NO_MEM) { - Serial.println("ESP_ERR_ESPNOW_NO_MEM"); - } else if (result == ESP_ERR_ESPNOW_NOT_FOUND) { - Serial.println("Peer not found."); - } else { - Serial.println("Not sure what happened"); - } -} - -// callback when data is sent from Master to Slave -void OnDataSent(const uint8_t *mac_addr, esp_now_send_status_t status) { - char macStr[18]; - snprintf(macStr, sizeof(macStr), "%02x:%02x:%02x:%02x:%02x:%02x", - mac_addr[0], mac_addr[1], mac_addr[2], mac_addr[3], mac_addr[4], mac_addr[5]); - Serial.print("Last Packet Sent to: "); Serial.println(macStr); - Serial.print("Last Packet Send Status: "); Serial.println(status == ESP_NOW_SEND_SUCCESS ? "Delivery Success" : "Delivery Fail"); -} - -void setup() { - Serial.begin(115200); - //Set device in STA mode to begin with - WiFi.mode(WIFI_STA); - esp_wifi_set_channel(CHANNEL, WIFI_SECOND_CHAN_NONE); - Serial.println("ESPNow/Basic/Master Example"); - // This is the mac address of the Master in Station Mode - Serial.print("STA MAC: "); Serial.println(WiFi.macAddress()); - Serial.print("STA CHANNEL "); Serial.println(WiFi.channel()); - // Init ESPNow with a fallback logic - InitESPNow(); - // Once ESPNow is successfully Init, we will register for Send CB to - // get the status of Trasnmitted packet - esp_now_register_send_cb(OnDataSent); -} - -void loop() { - // In the loop we scan for slave - ScanForSlave(); - // If Slave is found, it would be populate in `slave` variable - // We will check if `slave` is defined and then we proceed further - if (slave.channel == CHANNEL) { // check if slave channel is defined - // `slave` is defined - // Add slave as peer if it has not been added already - bool isPaired = manageSlave(); - if (isPaired) { - // pair success or already paired - // Send data to device - sendData(); - } else { - // slave pair failed - Serial.println("Slave pair failed!"); - } - } - else { - // No slave found to process - } - - // wait for 3seconds to run the logic again - delay(3000); -} diff --git a/libraries/ESP32/examples/ESPNow/ESPNow_Basic_Slave/ESPNow_Basic_Slave.ino b/libraries/ESP32/examples/ESPNow/ESPNow_Basic_Slave/ESPNow_Basic_Slave.ino deleted file mode 100644 index 50711b18fd5..00000000000 --- a/libraries/ESP32/examples/ESPNow/ESPNow_Basic_Slave/ESPNow_Basic_Slave.ino +++ /dev/null @@ -1,92 +0,0 @@ -/** - ESPNOW - Basic communication - Slave - Date: 26th September 2017 - Author: Arvind Ravulavaru - Purpose: ESPNow Communication between a Master ESP32 and a Slave ESP32 - Description: This sketch consists of the code for the Slave module. - Resources: (A bit outdated) - a. https://espressif.com/sites/default/files/documentation/esp-now_user_guide_en.pdf - b. http://www.esploradores.com/practica-6-conexion-esp-now/ - - << This Device Slave >> - - Flow: Master - Step 1 : ESPNow Init on Master and set it in STA mode - Step 2 : Start scanning for Slave ESP32 (we have added a prefix of `slave` to the SSID of slave for an easy setup) - Step 3 : Once found, add Slave as peer - Step 4 : Register for send callback - Step 5 : Start Transmitting data from Master to Slave - - Flow: Slave - Step 1 : ESPNow Init on Slave - Step 2 : Update the SSID of Slave with a prefix of `slave` - Step 3 : Set Slave in AP mode - Step 4 : Register for receive callback and wait for data - Step 5 : Once data arrives, print it in the serial monitor - - Note: Master and Slave have been defined to easily understand the setup. - Based on the ESPNOW API, there is no concept of Master and Slave. - Any devices can act as master or salve. -*/ - -#include -#include - -#define CHANNEL 1 - -// Init ESP Now with fallback -void InitESPNow() { - WiFi.disconnect(); - if (esp_now_init() == ESP_OK) { - Serial.println("ESPNow Init Success"); - } - else { - Serial.println("ESPNow Init Failed"); - // Retry InitESPNow, add a counte and then restart? - // InitESPNow(); - // or Simply Restart - ESP.restart(); - } -} - -// config AP SSID -void configDeviceAP() { - const char *SSID = "Slave_1"; - bool result = WiFi.softAP(SSID, "Slave_1_Password", CHANNEL, 0); - if (!result) { - Serial.println("AP Config failed."); - } else { - Serial.println("AP Config Success. Broadcasting with AP: " + String(SSID)); - Serial.print("AP CHANNEL "); Serial.println(WiFi.channel()); - } -} - -void setup() { - Serial.begin(115200); - Serial.println("ESPNow/Basic/Slave Example"); - //Set device in AP mode to begin with - WiFi.mode(WIFI_AP); - // configure device AP mode - configDeviceAP(); - // This is the mac address of the Slave in AP Mode - Serial.print("AP MAC: "); Serial.println(WiFi.softAPmacAddress()); - // Init ESPNow with a fallback logic - InitESPNow(); - // Once ESPNow is successfully Init, we will register for recv CB to - // get recv packer info. - esp_now_register_recv_cb(OnDataRecv); -} - -// callback when data is recv from Master -void OnDataRecv(const esp_now_recv_info_t * info, const uint8_t *data, int data_len) { - char macStr[18]; - snprintf(macStr, sizeof(macStr), "%02x:%02x:%02x:%02x:%02x:%02x", - info->src_addr[0], info->src_addr[1], info->src_addr[2], info->src_addr[3], info->src_addr[4], info->src_addr[5]); - Serial.print("Last Packet Recv from: "); Serial.println(macStr); - Serial.print("Last Packet Recv Data: "); Serial.println(*data); - Serial.println(""); -} - -void loop() { - // Chill -} diff --git a/libraries/ESP32/examples/ESPNow/ESPNow_MultiSlave_Master/ESPNow_MultiSlave_Master.ino b/libraries/ESP32/examples/ESPNow/ESPNow_MultiSlave_Master/ESPNow_MultiSlave_Master.ino deleted file mode 100644 index 6e212dd1121..00000000000 --- a/libraries/ESP32/examples/ESPNow/ESPNow_MultiSlave_Master/ESPNow_MultiSlave_Master.ino +++ /dev/null @@ -1,244 +0,0 @@ -/** - ESPNOW - Basic communication - Master - Date: 26th September 2017 - Author: Arvind Ravulavaru - Purpose: ESPNow Communication between a Master ESP32 and multiple ESP32 Slaves - Description: This sketch consists of the code for the Master module. - Resources: (A bit outdated) - a. https://espressif.com/sites/default/files/documentation/esp-now_user_guide_en.pdf - b. http://www.esploradores.com/practica-6-conexion-esp-now/ - - << This Device Master >> - - Flow: Master - Step 1 : ESPNow Init on Master and set it in STA mode - Step 2 : Start scanning for Slave ESP32 (we have added a prefix of `slave` to the SSID of slave for an easy setup) - Step 3 : Once found, add Slave as peer - Step 4 : Register for send callback - Step 5 : Start Transmitting data from Master to Slave(s) - - Flow: Slave - Step 1 : ESPNow Init on Slave - Step 2 : Update the SSID of Slave with a prefix of `slave` - Step 3 : Set Slave in AP mode - Step 4 : Register for receive callback and wait for data - Step 5 : Once data arrives, print it in the serial monitor - - Note: Master and Slave have been defined to easily understand the setup. - Based on the ESPNOW API, there is no concept of Master and Slave. - Any devices can act as master or salve. - - - // Sample Serial log with 1 master & 2 slaves - Found 12 devices - 1: Slave:24:0A:C4:81:CF:A4 [24:0A:C4:81:CF:A5] (-44) - 3: Slave:30:AE:A4:02:6D:CC [30:AE:A4:02:6D:CD] (-55) - 2 Slave(s) found, processing.. - Processing: 24:A:C4:81:CF:A5 Status: Already Paired - Processing: 30:AE:A4:2:6D:CD Status: Already Paired - Sending: 9 - Send Status: Success - Last Packet Sent to: 24:0a:c4:81:cf:a5 - Last Packet Send Status: Delivery Success - Send Status: Success - Last Packet Sent to: 30:ae:a4:02:6d:cd - Last Packet Send Status: Delivery Success - -*/ - -#include -#include - -// Global copy of slave -#define NUMSLAVES 20 -esp_now_peer_info_t slaves[NUMSLAVES] = {}; -int SlaveCnt = 0; - -#define CHANNEL 3 -#define PRINTSCANRESULTS 0 - -// Init ESP Now with fallback -void InitESPNow() { - WiFi.disconnect(); - if (esp_now_init() == ESP_OK) { - Serial.println("ESPNow Init Success"); - } - else { - Serial.println("ESPNow Init Failed"); - // Retry InitESPNow, add a counte and then restart? - // InitESPNow(); - // or Simply Restart - ESP.restart(); - } -} - -// Scan for slaves in AP mode -void ScanForSlave() { - int8_t scanResults = WiFi.scanNetworks(); - //reset slaves - memset(slaves, 0, sizeof(slaves)); - SlaveCnt = 0; - Serial.println(""); - if (scanResults == 0) { - Serial.println("No WiFi devices in AP Mode found"); - } else { - Serial.print("Found "); Serial.print(scanResults); Serial.println(" devices "); - for (int i = 0; i < scanResults; ++i) { - // Print SSID and RSSI for each device found - String SSID = WiFi.SSID(i); - int32_t RSSI = WiFi.RSSI(i); - String BSSIDstr = WiFi.BSSIDstr(i); - - if (PRINTSCANRESULTS) { - Serial.print(i + 1); Serial.print(": "); Serial.print(SSID); Serial.print(" ["); Serial.print(BSSIDstr); Serial.print("]"); Serial.print(" ("); Serial.print(RSSI); Serial.print(")"); Serial.println(""); - } - delay(10); - // Check if the current device starts with `Slave` - if (SSID.indexOf("Slave") == 0) { - // SSID of interest - Serial.print(i + 1); Serial.print(": "); Serial.print(SSID); Serial.print(" ["); Serial.print(BSSIDstr); Serial.print("]"); Serial.print(" ("); Serial.print(RSSI); Serial.print(")"); Serial.println(""); - // Get BSSID => Mac Address of the Slave - int mac[6]; - - if ( 6 == sscanf(BSSIDstr.c_str(), "%x:%x:%x:%x:%x:%x", &mac[0], &mac[1], &mac[2], &mac[3], &mac[4], &mac[5] ) ) { - for (int ii = 0; ii < 6; ++ii ) { - slaves[SlaveCnt].peer_addr[ii] = (uint8_t) mac[ii]; - } - } - slaves[SlaveCnt].channel = CHANNEL; // pick a channel - slaves[SlaveCnt].encrypt = 0; // no encryption - SlaveCnt++; - } - } - } - - if (SlaveCnt > 0) { - Serial.print(SlaveCnt); Serial.println(" Slave(s) found, processing.."); - } else { - Serial.println("No Slave Found, trying again."); - } - - // clean up ram - WiFi.scanDelete(); -} - -// Check if the slave is already paired with the master. -// If not, pair the slave with master -void manageSlave() { - if (SlaveCnt > 0) { - for (int i = 0; i < SlaveCnt; i++) { - Serial.print("Processing: "); - for (int ii = 0; ii < 6; ++ii ) { - Serial.print((uint8_t) slaves[i].peer_addr[ii], HEX); - if (ii != 5) Serial.print(":"); - } - Serial.print(" Status: "); - // check if the peer exists - bool exists = esp_now_is_peer_exist(slaves[i].peer_addr); - if (exists) { - // Slave already paired. - Serial.println("Already Paired"); - } else { - // Slave not paired, attempt pair - esp_err_t addStatus = esp_now_add_peer(&slaves[i]); - if (addStatus == ESP_OK) { - // Pair success - Serial.println("Pair success"); - } else if (addStatus == ESP_ERR_ESPNOW_NOT_INIT) { - // How did we get so far!! - Serial.println("ESPNOW Not Init"); - } else if (addStatus == ESP_ERR_ESPNOW_ARG) { - Serial.println("Add Peer - Invalid Argument"); - } else if (addStatus == ESP_ERR_ESPNOW_FULL) { - Serial.println("Peer list full"); - } else if (addStatus == ESP_ERR_ESPNOW_NO_MEM) { - Serial.println("Out of memory"); - } else if (addStatus == ESP_ERR_ESPNOW_EXIST) { - Serial.println("Peer Exists"); - } else { - Serial.println("Not sure what happened"); - } - delay(100); - } - } - } else { - // No slave found to process - Serial.println("No Slave found to process"); - } -} - - -uint8_t data = 0; -// send data -void sendData() { - data++; - for (int i = 0; i < SlaveCnt; i++) { - const uint8_t *peer_addr = slaves[i].peer_addr; - if (i == 0) { // print only for first slave - Serial.print("Sending: "); - Serial.println(data); - } - esp_err_t result = esp_now_send(peer_addr, &data, sizeof(data)); - Serial.print("Send Status: "); - if (result == ESP_OK) { - Serial.println("Success"); - } else if (result == ESP_ERR_ESPNOW_NOT_INIT) { - // How did we get so far!! - Serial.println("ESPNOW not Init."); - } else if (result == ESP_ERR_ESPNOW_ARG) { - Serial.println("Invalid Argument"); - } else if (result == ESP_ERR_ESPNOW_INTERNAL) { - Serial.println("Internal Error"); - } else if (result == ESP_ERR_ESPNOW_NO_MEM) { - Serial.println("ESP_ERR_ESPNOW_NO_MEM"); - } else if (result == ESP_ERR_ESPNOW_NOT_FOUND) { - Serial.println("Peer not found."); - } else { - Serial.println("Not sure what happened"); - } - delay(100); - } -} - -// callback when data is sent from Master to Slave -void OnDataSent(const uint8_t *mac_addr, esp_now_send_status_t status) { - char macStr[18]; - snprintf(macStr, sizeof(macStr), "%02x:%02x:%02x:%02x:%02x:%02x", - mac_addr[0], mac_addr[1], mac_addr[2], mac_addr[3], mac_addr[4], mac_addr[5]); - Serial.print("Last Packet Sent to: "); Serial.println(macStr); - Serial.print("Last Packet Send Status: "); Serial.println(status == ESP_NOW_SEND_SUCCESS ? "Delivery Success" : "Delivery Fail"); -} - -void setup() { - Serial.begin(115200); - //Set device in STA mode to begin with - WiFi.mode(WIFI_STA); - Serial.println("ESPNow/Multi-Slave/Master Example"); - // This is the mac address of the Master in Station Mode - Serial.print("STA MAC: "); Serial.println(WiFi.macAddress()); - // Init ESPNow with a fallback logic - InitESPNow(); - // Once ESPNow is successfully Init, we will register for Send CB to - // get the status of Trasnmitted packet - esp_now_register_send_cb(OnDataSent); -} - -void loop() { - // In the loop we scan for slave - ScanForSlave(); - // If Slave is found, it would be populate in `slave` variable - // We will check if `slave` is defined and then we proceed further - if (SlaveCnt > 0) { // check if slave channel is defined - // `slave` is defined - // Add slave as peer if it has not been added already - manageSlave(); - // pair success or already paired - // Send data to device - sendData(); - } else { - // No slave found to process - } - - // wait for 3seconds to run the logic again - delay(1000); -} diff --git a/libraries/ESP32/examples/ESPNow/ESPNow_MultiSlave_Slave/ESPNow_MultiSlave_Slave.ino b/libraries/ESP32/examples/ESPNow/ESPNow_MultiSlave_Slave/ESPNow_MultiSlave_Slave.ino deleted file mode 100644 index ad3b94037c3..00000000000 --- a/libraries/ESP32/examples/ESPNow/ESPNow_MultiSlave_Slave/ESPNow_MultiSlave_Slave.ino +++ /dev/null @@ -1,94 +0,0 @@ -/** - ESPNOW - Basic communication - Slave - Date: 26th September 2017 - Author: Arvind Ravulavaru - Purpose: ESPNow Communication between a Master ESP32 and multiple ESP32 Slaves - Description: This sketch consists of the code for the Slave module. - Resources: (A bit outdated) - a. https://espressif.com/sites/default/files/documentation/esp-now_user_guide_en.pdf - b. http://www.esploradores.com/practica-6-conexion-esp-now/ - - << This Device Slave >> - - Flow: Master - Step 1 : ESPNow Init on Master and set it in STA mode - Step 2 : Start scanning for Slave ESP32 (we have added a prefix of `slave` to the SSID of slave for an easy setup) - Step 3 : Once found, add Slave as peer - Step 4 : Register for send callback - Step 5 : Start Transmitting data from Master to Slave(s) - - Flow: Slave - Step 1 : ESPNow Init on Slave - Step 2 : Update the SSID of Slave with a prefix of `slave` - Step 3 : Set Slave in AP mode - Step 4 : Register for receive callback and wait for data - Step 5 : Once data arrives, print it in the serial monitor - - Note: Master and Slave have been defined to easily understand the setup. - Based on the ESPNOW API, there is no concept of Master and Slave. - Any devices can act as master or salve. -*/ - -#include -#include - -#define CHANNEL 1 - -// Init ESP Now with fallback -void InitESPNow() { - WiFi.disconnect(); - if (esp_now_init() == ESP_OK) { - Serial.println("ESPNow Init Success"); - } - else { - Serial.println("ESPNow Init Failed"); - // Retry InitESPNow, add a counte and then restart? - // InitESPNow(); - // or Simply Restart - ESP.restart(); - } -} - -// config AP SSID -void configDeviceAP() { - String Prefix = "Slave:"; - String Mac = WiFi.macAddress(); - String SSID = Prefix + Mac; - String Password = "123456789"; - bool result = WiFi.softAP(SSID.c_str(), Password.c_str(), CHANNEL, 0); - if (!result) { - Serial.println("AP Config failed."); - } else { - Serial.println("AP Config Success. Broadcasting with AP: " + String(SSID)); - } -} - -void setup() { - Serial.begin(115200); - Serial.println("ESPNow/Basic/Slave Example"); - //Set device in AP mode to begin with - WiFi.mode(WIFI_AP); - // configure device AP mode - configDeviceAP(); - // This is the mac address of the Slave in AP Mode - Serial.print("AP MAC: "); Serial.println(WiFi.softAPmacAddress()); - // Init ESPNow with a fallback logic - InitESPNow(); - // Once ESPNow is successfully Init, we will register for recv CB to - // get recv packer info. - esp_now_register_recv_cb(OnDataRecv); -} - -// callback when data is recv from Master -void OnDataRecv(const esp_now_recv_info_t * info, const uint8_t *data, int data_len) { - char macStr[18]; - snprintf(macStr, sizeof(macStr), "%02x:%02x:%02x:%02x:%02x:%02x", - info->src_addr[0], info->src_addr[1], info->src_addr[2], info->src_addr[3], info->src_addr[4], info->src_addr[5]); - Serial.print("Last Packet Recv from: "); Serial.println(macStr); - Serial.print("Last Packet Recv Data: "); Serial.println(*data); - Serial.println(""); -} - -void loop() { - // Chill -} diff --git a/libraries/ESP32/examples/ESPNow/ESPNow_Basic_Master/.skip.esp32h2 b/libraries/ESP_NOW/examples/ESP_NOW_Broadcast_Master/.skip.esp32h2 similarity index 100% rename from libraries/ESP32/examples/ESPNow/ESPNow_Basic_Master/.skip.esp32h2 rename to libraries/ESP_NOW/examples/ESP_NOW_Broadcast_Master/.skip.esp32h2 diff --git a/libraries/ESP_NOW/examples/ESP_NOW_Broadcast_Master/ESP_NOW_Broadcast_Master.ino b/libraries/ESP_NOW/examples/ESP_NOW_Broadcast_Master/ESP_NOW_Broadcast_Master.ino new file mode 100644 index 00000000000..e3e9e62386d --- /dev/null +++ b/libraries/ESP_NOW/examples/ESP_NOW_Broadcast_Master/ESP_NOW_Broadcast_Master.ino @@ -0,0 +1,103 @@ +/* + ESP-NOW Broadcast Master + Lucas Saavedra Vaz - 2024 + + This sketch demonstrates how to broadcast messages to all devices within the ESP-NOW network. + This example is intended to be used with the ESP-NOW Broadcast Slave example. + + The master device will broadcast a message every 5 seconds to all devices within the network. + This will be done using by registering a peer object with the broadcast address. + + The slave devices will receive the broadcasted messages and print them to the Serial Monitor. +*/ + +#include "ESP32_NOW.h" +#include "WiFi.h" + +#include // For the MAC2STR and MACSTR macros + +/* Definitions */ + +#define ESPNOW_WIFI_CHANNEL 6 + +/* Classes */ + +// Creating a new class that inherits from the ESP_NOW_Peer class is required. + +class ESP_NOW_Broadcast_Peer : public ESP_NOW_Peer { +public: + // Constructor of the class using the broadcast address + ESP_NOW_Broadcast_Peer(uint8_t channel, wifi_interface_t iface, const uint8_t *lmk) + : ESP_NOW_Peer(ESP_NOW.BROADCAST_ADDR, channel, iface, lmk) {} + + // Destructor of the class + ~ESP_NOW_Broadcast_Peer() { + remove(); + } + + // Function to properly initialize the ESP-NOW and register the broadcast peer + bool begin() { + if (!ESP_NOW.begin() || !add()) { + log_e("Failed to initialize ESP-NOW or register the broadcast peer"); + return false; + } + return true; + } + + // Function to send a message to all devices within the network + bool send_message(const uint8_t *data, size_t len) { + if (!send(data, len)) { + log_e("Failed to broadcast message"); + return false; + } + return true; + } +}; + +/* Global Variables */ + +uint32_t msg_count = 0; + +// Create a boradcast peer object +ESP_NOW_Broadcast_Peer broadcast_peer(ESPNOW_WIFI_CHANNEL, WIFI_IF_STA, NULL); + +/* Main */ + +void setup() { + Serial.begin(115200); + while (!Serial) delay(10); + + Serial.println("ESP-NOW Example - Broadcast Master"); + Serial.println("Wi-Fi parameters:"); + Serial.println(" Mode: STA"); + Serial.println(" MAC Address: " + WiFi.macAddress()); + Serial.printf(" Channel: %d\n", ESPNOW_WIFI_CHANNEL); + + // Initialize the Wi-Fi module + WiFi.mode(WIFI_STA); + WiFi.setChannel(ESPNOW_WIFI_CHANNEL); + + // Register the broadcast peer + if (!broadcast_peer.begin()) { + Serial.println("Failed to initialize broadcast peer"); + Serial.println("Reebooting in 5 seconds..."); + delay(5000); + ESP.restart(); + } + + Serial.println("Setup complete. Broadcasting messages every 5 seconds."); +} + +void loop() { + // Broadcast a message to all devices within the network + char data[32]; + snprintf(data, sizeof(data), "Hello, World! #%lu", msg_count++); + + Serial.printf("Broadcasting message: %s\n", data); + + if (!broadcast_peer.send_message((uint8_t *)data, sizeof(data))) { + Serial.println("Failed to broadcast message"); + } + + delay(5000); +} diff --git a/libraries/ESP32/examples/ESPNow/ESPNow_Basic_Slave/.skip.esp32h2 b/libraries/ESP_NOW/examples/ESP_NOW_Broadcast_Slave/.skip.esp32h2 similarity index 100% rename from libraries/ESP32/examples/ESPNow/ESPNow_Basic_Slave/.skip.esp32h2 rename to libraries/ESP_NOW/examples/ESP_NOW_Broadcast_Slave/.skip.esp32h2 diff --git a/libraries/ESP_NOW/examples/ESP_NOW_Broadcast_Slave/ESP_NOW_Broadcast_Slave.ino b/libraries/ESP_NOW/examples/ESP_NOW_Broadcast_Slave/ESP_NOW_Broadcast_Slave.ino new file mode 100644 index 00000000000..ac553bdceff --- /dev/null +++ b/libraries/ESP_NOW/examples/ESP_NOW_Broadcast_Slave/ESP_NOW_Broadcast_Slave.ino @@ -0,0 +1,114 @@ +/* + ESP-NOW Broadcast Slave + Lucas Saavedra Vaz - 2024 + + This sketch demonstrates how to receive broadcast messages from a master device using the ESP-NOW protocol. + + The master device will broadcast a message every 5 seconds to all devices within the network. + + The slave devices will receive the broadcasted messages. If they are not from a known master, they will be registered as a new master + using a callback function. +*/ + +#include "ESP32_NOW.h" +#include "WiFi.h" + +#include // For the MAC2STR and MACSTR macros + +#include + +/* Definitions */ + +#define ESPNOW_WIFI_CHANNEL 6 + +/* Classes */ + +// Creating a new class that inherits from the ESP_NOW_Peer class is required. + +class ESP_NOW_Peer_Class : public ESP_NOW_Peer { +public: + // Constructor of the class + ESP_NOW_Peer_Class(const uint8_t *mac_addr, + uint8_t channel, + wifi_interface_t iface, + const uint8_t *lmk) : ESP_NOW_Peer(mac_addr, channel, iface, lmk) {} + + // Destructor of the class + ~ESP_NOW_Peer_Class() {} + + // Function to register the master peer + bool add_peer() { + if (!add()) { + log_e("Failed to register the broadcast peer"); + return false; + } + return true; + } + + // Function to print the received messages from the master + void _onReceive(const uint8_t *data, size_t len, bool broadcast) { + Serial.printf("Received a message from master " MACSTR " (%s)\n", MAC2STR(addr()), broadcast ? "broadcast" : "unicast"); + Serial.printf(" Message: %s\n", (char *)data); + } +}; + +/* Global Variables */ + +// List of all the masters. It will be populated when a new master is registered +std::vector masters; + +/* Callbacks */ + +// Callback called when an unknown peer sends a message +void register_new_master(const esp_now_recv_info_t *info, const uint8_t *data, int len, void *arg) { + if (memcmp(info->des_addr, ESP_NOW.BROADCAST_ADDR, 6) == 0) { + Serial.printf("Unknown peer " MACSTR " sent a broadcast message\n", MAC2STR(info->src_addr)); + Serial.println("Registering the peer as a master"); + + ESP_NOW_Peer_Class new_master(info->src_addr, ESPNOW_WIFI_CHANNEL, WIFI_IF_STA, NULL); + + masters.push_back(new_master); + if (!masters.back().add_peer()) { + Serial.println("Failed to register the new master"); + return; + } + } else { + // The slave will only receive broadcast messages + log_v("Received a unicast message from " MACSTR, MAC2STR(info->src_addr)); + log_v("Igorning the message"); + } +} + +/* Main */ + +void setup() { + Serial.begin(115200); + while (!Serial) delay(10); + + Serial.println("ESP-NOW Example - Broadcast Slave"); + Serial.println("Wi-Fi parameters:"); + Serial.println(" Mode: STA"); + Serial.println(" MAC Address: " + WiFi.macAddress()); + Serial.printf(" Channel: %d\n", ESPNOW_WIFI_CHANNEL); + + // Initialize the Wi-Fi module + WiFi.mode(WIFI_STA); + WiFi.setChannel(ESPNOW_WIFI_CHANNEL); + + // Initialize the ESP-NOW protocol + if (!ESP_NOW.begin()) { + Serial.println("Failed to initialize ESP-NOW"); + Serial.println("Reeboting in 5 seconds..."); + delay(5000); + ESP.restart(); + } + + // Register the new peer callback + ESP_NOW.onNewPeer(register_new_master, NULL); + + Serial.println("Setup complete. Waiting for a master to broadcast a message..."); +} + +void loop() { + delay(1000); +} diff --git a/libraries/ESP32/examples/ESPNow/ESPNow_MultiSlave_Master/.skip.esp32h2 b/libraries/ESP_NOW/examples/ESP_NOW_Network/.skip.esp32h2 similarity index 100% rename from libraries/ESP32/examples/ESPNow/ESPNow_MultiSlave_Master/.skip.esp32h2 rename to libraries/ESP_NOW/examples/ESP_NOW_Network/.skip.esp32h2 diff --git a/libraries/ESP_NOW/examples/ESP_NOW_Network/ESP_NOW_Network.ino b/libraries/ESP_NOW/examples/ESP_NOW_Network/ESP_NOW_Network.ino new file mode 100644 index 00000000000..2c72921ea2b --- /dev/null +++ b/libraries/ESP_NOW/examples/ESP_NOW_Network/ESP_NOW_Network.ino @@ -0,0 +1,369 @@ +/* + ESP-NOW Network Example + Lucas Saavedra Vaz - 2024 + + This example is based on the ESP-NOW example from the ESP-IDF framework. + + The aim of this example is to demonstrate how to create a network of devices using the ESP-NOW protocol. + The slave devices will broadcast random data to the master device every 5 seconds and from time to time + they will ping the other slave devices with a "Hello!" message. + + The master device will receive the data from the slave devices and print it to the Serial Monitor. From time + to time, the master device will calculate the average of the priorities of the slave devices and send it to + all the slave devices. + + Each device will have a priority that will be used to decide which device will be the master. + The device with the highest priority will be the master. + + Flow: + 1. Each device will generate a priority based on its MAC address. + 2. The devices will broadcast their priority on the network. + 3. The devices will listen to the broadcast messages and register the priorities of the other devices. + 4. After all devices have been registered, the device with the highest priority will be the master. + 5. The slave devices will send random data to the master every 5 seconds. + - Every "REPORT_INTERVAL" messages, the slaves will send a message to the other slaves. + 6. The master device will calculate the average of the data and send it to the slave devices every "REPORT_INTERVAL" messages. + +*/ + +#include "ESP32_NOW.h" +#include "WiFi.h" + +#include // For the MAC2STR and MACSTR macros + +#include + +/* Definitions */ + +// Wi-Fi interface to be used by the ESP-NOW protocol +#define ESPNOW_WIFI_IFACE WIFI_IF_STA + +// Channel to be used by the ESP-NOW protocol +#define ESPNOW_WIFI_CHANNEL 4 + +// Delay between sending messages +#define ESPNOW_SEND_INTERVAL_MS 5000 + +// Number of peers to wait for (excluding this device) +#define ESPNOW_PEER_COUNT 2 + +// Report to other devices every 5 messages +#define REPORT_INTERVAL 5 + +/* + ESP-NOW uses the CCMP method, which is described in IEEE Std. 802.11-2012, to protect the vendor-specific action frame. + The Wi-Fi device maintains a Primary Master Key (PMK) and several Local Master Keys (LMK). + The lengths of both PMK and LMK need to be 16 bytes. + + PMK is used to encrypt LMK with the AES-128 algorithm. If PMK is not set, a default PMK will be used. + + LMK of the paired device is used to encrypt the vendor-specific action frame with the CCMP method. + The maximum number of different LMKs is six. If the LMK of the paired device is not set, the vendor-specific + action frame will not be encrypted. + + Encrypting multicast (broadcast address) vendor-specific action frame is not supported. + + PMK needs to be the same for all devices in the network. LMK only needs to be the same between paired devices. +*/ + +// Primary Master Key (PMK) and Local Master Key (LMK) +#define ESPNOW_EXAMPLE_PMK "pmk1234567890123" +#define ESPNOW_EXAMPLE_LMK "lmk1234567890123" + +/* Structs */ + +// The following struct is used to send data to the peer device. +// We use the attribute "packed" to ensure that the struct is not padded (all data +// is contiguous in the memory and without gaps). +// The maximum size of the complete message is 250 bytes (ESP_NOW_MAX_DATA_LEN). + +typedef struct { + uint32_t count; + uint32_t priority; + uint32_t data; + bool ready; + char str[7]; +} __attribute__((packed)) esp_now_data_t; + +/* Global Variables */ + +uint32_t self_priority = 0; // Priority of this device +uint8_t current_peer_count = 0; // Number of peers that have been found +bool device_is_master = false; // Flag to indicate if this device is the master +bool master_decided = false; // Flag to indicate if the master has been decided +uint32_t sent_msg_count = 0; // Counter for the messages sent. Only starts counting after all peers have been found +uint32_t recv_msg_count = 0; // Counter for the messages received. Only starts counting after all peers have been found +esp_now_data_t new_msg; // Message that will be sent to the peers +std::vector last_data(5); // Vector that will store the last 5 data received + +/* Classes */ + +// We need to create a class that inherits from ESP_NOW_Peer and implement the _onReceive and _onSent methods. +// This class will be used to store the priority of the device and to send messages to the peers. +// For more information about the ESP_NOW_Peer class, see the ESP_NOW_Peer class in the ESP32_NOW.h file. + +class ESP_NOW_Network_Peer : public ESP_NOW_Peer { +public: + uint32_t priority; + bool peer_is_master = false; + bool peer_ready = false; + + ESP_NOW_Network_Peer(const uint8_t *mac_addr, uint32_t priority = 0, const uint8_t *lmk = (const uint8_t *)ESPNOW_EXAMPLE_LMK) + : ESP_NOW_Peer(mac_addr, ESPNOW_WIFI_CHANNEL, ESPNOW_WIFI_IFACE, lmk) + , priority(priority) {} + + ~ESP_NOW_Network_Peer() {} + + bool begin() { + // In this example the ESP-NOW protocol will already be initialized as we require it to receive broadcast messages. + if (!add()) { + log_e("Failed to initialize ESP-NOW or register the peer"); + return false; + } + return true; + } + + bool send_message(const uint8_t *data, size_t len) { + if (data == NULL || len == 0) { + log_e("Data to be sent is NULL or has a length of 0"); + return false; + } + + // Call the parent class method to send the data + return send(data, len); + } + + void onReceive(const uint8_t *data, size_t len, bool broadcast) { + esp_now_data_t *msg = (esp_now_data_t *)data; + + if (peer_ready == false && msg->ready == true) { + Serial.printf("Peer " MACSTR " reported ready\n", MAC2STR(addr())); + peer_ready = true; + } + + if (!broadcast) { + recv_msg_count++; + if (device_is_master) { + Serial.printf("Received a message from peer " MACSTR "\n", MAC2STR(addr())); + Serial.printf(" Count: %lu\n", msg->count); + Serial.printf(" Random data: %lu\n", msg->data); + last_data.push_back(msg->data); + last_data.erase(last_data.begin()); + } else if (peer_is_master) { + Serial.println("Received a message from the master"); + Serial.printf(" Average data: %lu\n", msg->data); + } + else { + Serial.printf("Peer " MACSTR " says: %s\n", MAC2STR(addr()), msg->str); + } + } + } + + void onSent(bool success) { + bool broadcast = memcmp(addr(), ESP_NOW.BROADCAST_ADDR, ESP_NOW_ETH_ALEN) == 0; + if (broadcast) { + log_v("Broadcast message reported as sent %s", success ? "successfully" : "unsuccessfully"); + } + else { + log_v("Unicast message reported as sent %s to peer " MACSTR, success ? "successfully" : "unsuccessfully", MAC2STR(addr())); + } + } +}; + +/* Peers */ + +std::vector peers; // Create a vector to store the peer pointers +ESP_NOW_Network_Peer broadcast_peer(ESP_NOW.BROADCAST_ADDR, 0, NULL); // Register the broadcast peer (no encryption support for the broadcast address) +ESP_NOW_Network_Peer *master_peer = nullptr; // Pointer to peer that is the master + +/* Helper functions */ + +// Function to reboot the device +void fail_reboot() { + Serial.println("Rebooting in 5 seconds..."); + delay(5000); + ESP.restart(); +} + +// Function to check which device has the highest priority +uint32_t check_highest_priority() { + uint32_t highest_priority = 0; + for (auto &peer : peers) { + if (peer->priority > highest_priority) { + highest_priority = peer->priority; + } + } + return std::max(highest_priority, self_priority); +} + +// Function to calculate the average of the data received +uint32_t calc_average() { + uint32_t avg = 0; + for (auto &d : last_data) { + avg += d; + } + avg /= last_data.size(); + return avg; +} + +// Function to check if all peers are ready +bool check_all_peers_ready() { + for (auto &peer : peers) { + if (!peer->peer_ready) { + return false; + } + } + return true; +} + +/* Callbacks */ + +// Callback called when a new peer is found +void register_new_peer(const esp_now_recv_info_t *info, const uint8_t *data, int len, void *arg) { + esp_now_data_t *msg = (esp_now_data_t *)data; + int priority = msg->priority; + + if (priority == self_priority) { + Serial.println("ERROR! Device has the same priority as this device. Unsupported behavior."); + fail_reboot(); + } + + if (current_peer_count < ESPNOW_PEER_COUNT) { + Serial.printf("New peer found: " MACSTR " with priority %d\n", MAC2STR(info->src_addr), priority); + ESP_NOW_Network_Peer *new_peer = new ESP_NOW_Network_Peer(info->src_addr, priority); + if (new_peer == nullptr || !new_peer->begin()) { + Serial.println("Failed to create or register the new peer"); + delete new_peer; + return; + } + peers.push_back(new_peer); + current_peer_count++; + if (current_peer_count == ESPNOW_PEER_COUNT) { + Serial.println("All peers have been found"); + new_msg.ready = true; + } + } +} + +/* Main */ + +void setup() { + uint8_t self_mac[6]; + + Serial.begin(115200); + while (!Serial) delay(10); + + Serial.println("ESP-NOW Network Example"); + Serial.println("Wi-Fi parameters:"); + Serial.println(" Mode: STA"); + Serial.println(" MAC Address: " + WiFi.macAddress()); + Serial.printf(" Channel: %d\n", ESPNOW_WIFI_CHANNEL); + + // Initialize the Wi-Fi module + WiFi.mode(WIFI_STA); + WiFi.setChannel(ESPNOW_WIFI_CHANNEL); + + // Generate yhis device's priority based on the 3 last bytes of the MAC address + WiFi.macAddress(self_mac); + self_priority = self_mac[3] << 16 | self_mac[4] << 8 | self_mac[5]; + Serial.printf("This device's priority: %lu\n", self_priority); + + // Initialize the ESP-NOW protocol + if (!ESP_NOW.begin((const uint8_t *)ESPNOW_EXAMPLE_PMK)) { + Serial.println("Failed to initialize ESP-NOW"); + fail_reboot(); + } + + if (!broadcast_peer.begin()) { + Serial.println("Failed to initialize broadcast peer"); + fail_reboot(); + } + + // Register the callback to be called when a new peer is found + ESP_NOW.onNewPeer(register_new_peer, NULL); + + Serial.println("Setup complete. Broadcasting own priority to find the master..."); + memset(&new_msg, 0, sizeof(new_msg)); + strncpy(new_msg.str, "Hello!", sizeof(new_msg.str)); + new_msg.priority = self_priority; +} + +void loop() { + if (!master_decided) { + // Broadcast the priority to find the master + if (!broadcast_peer.send_message((const uint8_t *)&new_msg, sizeof(new_msg))) { + Serial.println("Failed to broadcast message"); + } + + // Check if all peers have been found + if (current_peer_count == ESPNOW_PEER_COUNT) { + // Wait until all peers are ready + if (check_all_peers_ready()) { + Serial.println("All peers are ready"); + // Check which device has the highest priority + master_decided = true; + uint32_t highest_priority = check_highest_priority(); + if (highest_priority == self_priority) { + device_is_master = true; + Serial.println("This device is the master"); + } else { + for (int i = 0; i < ESPNOW_PEER_COUNT; i++) { + if (peers[i]->priority == highest_priority) { + peers[i]->peer_is_master = true; + master_peer = peers[i]; + Serial.printf("Peer " MACSTR " is the master with priority %lu\n", MAC2STR(peers[i]->addr()), highest_priority); + break; + } + } + } + Serial.println("The master has been decided"); + } else { + Serial.println("Waiting for all peers to be ready..."); + } + } + } else { + if (!device_is_master) { + // Send a message to the master + new_msg.count = sent_msg_count + 1; + new_msg.data = random(10000); + if (!master_peer->send_message((const uint8_t *)&new_msg, sizeof(new_msg))) { + Serial.println("Failed to send message to the master"); + } else { + Serial.printf("Sent message to the master. Count: %lu, Data: %lu\n", new_msg.count, new_msg.data); + sent_msg_count++; + } + + // Check if it is time to report to peers + if (sent_msg_count % REPORT_INTERVAL == 0) { + // Send a message to the peers + for (auto &peer : peers) { + if (!peer->peer_is_master) { + if (!peer->send_message((const uint8_t *)&new_msg, sizeof(new_msg))) { + Serial.printf("Failed to send message to peer " MACSTR "\n", MAC2STR(peer->addr())); + } else { + Serial.printf("Sent message \"%s\" to peer " MACSTR "\n", new_msg.str, MAC2STR(peer->addr())); + } + } + } + } + } else { + // Check if it is time to report to peers + if (recv_msg_count % REPORT_INTERVAL == 0) { + // Report average data to the peers + uint32_t avg = calc_average(); + new_msg.data = avg; + for (auto &peer : peers) { + new_msg.count = sent_msg_count + 1; + if (!peer->send_message((const uint8_t *)&new_msg, sizeof(new_msg))) { + Serial.printf("Failed to send message to peer " MACSTR "\n", MAC2STR(peer->addr())); + } else { + Serial.printf("Sent message to peer " MACSTR ". Recv: %lu, Sent: %lu, Avg: %lu\n", MAC2STR(peer->addr()), recv_msg_count, new_msg.count, new_msg.data); + sent_msg_count++; + } + } + } + } + } + + delay(ESPNOW_SEND_INTERVAL_MS); +} diff --git a/libraries/ESP32/examples/ESPNow/ESPNow_MultiSlave_Slave/.skip.esp32h2 b/libraries/ESP_NOW/examples/ESP_NOW_Serial/.skip.esp32h2 similarity index 100% rename from libraries/ESP32/examples/ESPNow/ESPNow_MultiSlave_Slave/.skip.esp32h2 rename to libraries/ESP_NOW/examples/ESP_NOW_Serial/.skip.esp32h2 diff --git a/libraries/ESP_NOW/examples/ESP_NOW_Serial/ESP_NOW_Serial.ino b/libraries/ESP_NOW/examples/ESP_NOW_Serial/ESP_NOW_Serial.ino new file mode 100644 index 00000000000..8e7b2041a2d --- /dev/null +++ b/libraries/ESP_NOW/examples/ESP_NOW_Serial/ESP_NOW_Serial.ino @@ -0,0 +1,82 @@ +/* + ESP-NOW Serial Example - Unicast transmission + Lucas Saavedra Vaz - 2024 + Send data between two ESP32s using the ESP-NOW protocol in one-to-one (unicast) configuration. + Note that different MAC addresses are used for different interfaces. + The devices can be in different modes (AP or Station) and still communicate using ESP-NOW. + The only requirement is that the devices are on the same Wi-Fi channel. + Set the peer MAC address according to the device that will receive the data. + + Example setup: + - Device 1: AP mode with MAC address F6:12:FA:42:B6:E8 + Peer MAC address set to the Station MAC address of Device 2 (F4:12:FA:40:64:4C) + - Device 2: Station mode with MAC address F4:12:FA:40:64:4C + Peer MAC address set to the AP MAC address of Device 1 (F6:12:FA:42:B6:E8) + + The device running this sketch will also receive and print data from any device that has its MAC address set as the peer MAC address. + To properly visualize the data being sent, set the line ending in the Serial Monitor to "Both NL & CR". +*/ + +#include "ESP32_NOW_Serial.h" +#include "MacAddress.h" +#include "WiFi.h" + +#include "esp_wifi.h" + +// 0: AP mode, 1: Station mode +#define ESPNOW_WIFI_MODE_STATION 1 + +// Channel to be used by the ESP-NOW protocol +#define ESPNOW_WIFI_CHANNEL 1 + +#if ESPNOW_WIFI_MODE_STATION // ESP-NOW using WiFi Station mode + #define ESPNOW_WIFI_MODE WIFI_STA // WiFi Mode + #define ESPNOW_WIFI_IF WIFI_IF_STA // WiFi Interface +#else // ESP-NOW using WiFi AP mode + #define ESPNOW_WIFI_MODE WIFI_AP // WiFi Mode + #define ESPNOW_WIFI_IF WIFI_IF_AP // WiFi Interface +#endif + +// Set the MAC address of the device that will receive the data +// For example: F4:12:FA:40:64:4C +const MacAddress peer_mac({0xF4, 0x12, 0xFA, 0x40, 0x64, 0x4C}); + +ESP_NOW_Serial_Class NowSerial(peer_mac, ESPNOW_WIFI_CHANNEL, ESPNOW_WIFI_IF); + +void setup() { + Serial.begin(115200); + + Serial.print("WiFi Mode: "); + Serial.println(ESPNOW_WIFI_MODE == WIFI_AP ? "AP" : "Station"); + WiFi.mode(ESPNOW_WIFI_MODE); + + Serial.print("Channel: "); + Serial.println(ESPNOW_WIFI_CHANNEL); + WiFi.setChannel(ESPNOW_WIFI_CHANNEL, WIFI_SECOND_CHAN_NONE); + + Serial.print("MAC Address: "); + Serial.println(ESPNOW_WIFI_MODE == WIFI_AP ? WiFi.softAPmacAddress() : WiFi.macAddress()); + + // Start the ESP-NOW communication + Serial.println("ESP-NOW communication starting..."); + NowSerial.begin(115200); + Serial.println("You can now send data to the peer device using the Serial Monitor.\n"); +} + +void loop() { + while (NowSerial.available()) + { + Serial.write(NowSerial.read()); + } + + while (Serial.available() && NowSerial.availableForWrite()) + { + if (NowSerial.write(Serial.read()) <= 0) + { + Serial.println("Failed to send data"); + break; + } + } + + delay(1); +} diff --git a/libraries/ESP_NOW/library.properties b/libraries/ESP_NOW/library.properties new file mode 100755 index 00000000000..e07c55d5aa2 --- /dev/null +++ b/libraries/ESP_NOW/library.properties @@ -0,0 +1,9 @@ +name=ESP_NOW +version=1.0.0 +author=me-no-dev +maintainer=P-R-O-C-H-Y +sentence=Library for ESP_NOW +paragraph=Supports ESP32 Arduino platforms. +category=Sensor +url=https://github.com/espressif/arduino-esp32/ +architectures=esp32 \ No newline at end of file diff --git a/libraries/ESP_NOW/src/ESP32_NOW.cpp b/libraries/ESP_NOW/src/ESP32_NOW.cpp new file mode 100644 index 00000000000..3e2329e7a8c --- /dev/null +++ b/libraries/ESP_NOW/src/ESP32_NOW.cpp @@ -0,0 +1,399 @@ +#include "ESP32_NOW.h" +#include +#include "esp_system.h" +#include "esp32-hal.h" +#include "esp_wifi.h" + +static void (*new_cb)(const esp_now_recv_info_t *info, const uint8_t * data, int len, void * arg) = NULL; +static void * new_arg = NULL;// * tx_arg = NULL, * rx_arg = NULL, +static bool _esp_now_has_begun = false; +static ESP_NOW_Peer * _esp_now_peers[ESP_NOW_MAX_TOTAL_PEER_NUM]; + +static esp_err_t _esp_now_add_peer(const uint8_t *mac_addr, uint8_t channel, wifi_interface_t iface, const uint8_t *lmk, ESP_NOW_Peer * _peer=NULL){ + log_v(MACSTR, MAC2STR(mac_addr)); + if (esp_now_is_peer_exist(mac_addr)) { + log_e("Peer Already Exists"); + return ESP_ERR_ESPNOW_EXIST; + } + + esp_now_peer_info_t peer; + memset(&peer, 0, sizeof(esp_now_peer_info_t)); + memcpy(peer.peer_addr, mac_addr, ESP_NOW_ETH_ALEN); + peer.channel = channel; + peer.ifidx = iface; + peer.encrypt = lmk != NULL; + if(lmk){ + memcpy(peer.lmk, lmk, ESP_NOW_KEY_LEN); + } + + esp_err_t result = esp_now_add_peer(&peer); + if(result == ESP_OK){ + if(_peer != NULL){ + for(uint8_t i=0; iaddr(), ESP_NOW_ETH_ALEN) == 0){ + _esp_now_peers[i] = NULL; + break; + } + } + return result; +} + +static esp_err_t _esp_now_modify_peer(const uint8_t *mac_addr, uint8_t channel, wifi_interface_t iface, const uint8_t *lmk){ + log_v(MACSTR, MAC2STR(mac_addr)); + if (!esp_now_is_peer_exist(mac_addr)) { + log_e("Peer not found"); + return ESP_ERR_ESPNOW_NOT_FOUND; + } + + esp_now_peer_info_t peer; + memset(&peer, 0, sizeof(esp_now_peer_info_t)); + memcpy(peer.peer_addr, mac_addr, ESP_NOW_ETH_ALEN); + peer.channel = channel; + peer.ifidx = iface; + peer.encrypt = lmk != NULL; + if(lmk){ + memcpy(peer.lmk, lmk, ESP_NOW_KEY_LEN); + } + + esp_err_t result = esp_now_mod_peer(&peer); + if (result == ESP_OK) { + return result; + } else if (result == ESP_ERR_ESPNOW_NOT_INIT) { + log_e("ESPNOW Not Init"); + } else if (result == ESP_ERR_ESPNOW_ARG) { + log_e("Invalid Argument"); + } else if (result == ESP_ERR_ESPNOW_FULL) { + log_e("Peer list full"); + } else if (result == ESP_ERR_ESPNOW_NO_MEM) { + log_e("Out of memory"); + } + return result; +} + +static void _esp_now_rx_cb(const esp_now_recv_info_t *info, const uint8_t *data, int len){ + bool broadcast = memcmp(info->des_addr, ESP_NOW.BROADCAST_ADDR, ESP_NOW_ETH_ALEN) == 0; + log_v("%s from " MACSTR ", data lenght : %u", broadcast ? "Broadcast" : "Unicast", MAC2STR(info->src_addr), len); + log_buf_v(data, len); + if(!esp_now_is_peer_exist(info->src_addr) && new_cb != NULL){ + log_v("Calling new_cb, peer not found."); + new_cb(info, data, len, new_arg); + return; + } + //find the peer and call it's callback + for(uint8_t i=0; iaddr())); + } + if(_esp_now_peers[i] != NULL && memcmp(info->src_addr, _esp_now_peers[i]->addr(), ESP_NOW_ETH_ALEN) == 0){ + log_v("Calling onReceive"); + _esp_now_peers[i]->onReceive(data, len, broadcast); + return; + } + } +} + +static void _esp_now_tx_cb(const uint8_t *mac_addr, esp_now_send_status_t status) { + log_v(MACSTR" : %s", MAC2STR(mac_addr), (status == ESP_NOW_SEND_SUCCESS)?"SUCCESS":"FAILED"); + //find the peer and call it's callback + for(uint8_t i=0; iaddr(), ESP_NOW_ETH_ALEN) == 0){ + _esp_now_peers[i]->onSent(status == ESP_NOW_SEND_SUCCESS); + return; + } + } +} + +ESP_NOW_Class::ESP_NOW_Class(){} + +ESP_NOW_Class::~ESP_NOW_Class(){} + +bool ESP_NOW_Class::begin(const uint8_t *pmk){ + if(_esp_now_has_begun){ + return true; + } + + esp_err_t err = esp_wifi_start(); + if (err != ESP_OK) { + log_e("WiFi not started! 0x%x)", err); + return false; + } + + _esp_now_has_begun = true; + + memset(_esp_now_peers, 0, sizeof(ESP_NOW_Peer *) * ESP_NOW_MAX_TOTAL_PEER_NUM); + + err = esp_now_init(); + if(err != ESP_OK){ + log_e("esp_now_init failed! 0x%x", err); + _esp_now_has_begun = false; + return false; + } + + if(pmk){ + err = esp_now_set_pmk(pmk); + if(err != ESP_OK){ + log_e("esp_now_set_pmk failed! 0x%x", err); + _esp_now_has_begun = false; + return false; + } + } + + err = esp_now_register_recv_cb(_esp_now_rx_cb); + if(err != ESP_OK){ + log_e("esp_now_register_recv_cb failed! 0x%x", err); + _esp_now_has_begun = false; + return false; + } + + err = esp_now_register_send_cb(_esp_now_tx_cb); + if(err != ESP_OK){ + log_e("esp_now_register_send_cb failed! 0x%x", err); + _esp_now_has_begun = false; + return false; + } + return true; +} + +bool ESP_NOW_Class::end(){ + if(!_esp_now_has_begun){ + return true; + } + //remove all peers? + esp_err_t err = esp_now_deinit(); + if(err != ESP_OK){ + log_e("esp_now_deinit failed! 0x%x", err); + return false; + } + _esp_now_has_begun = false; + memset(_esp_now_peers, 0, sizeof(ESP_NOW_Peer *) * ESP_NOW_MAX_TOTAL_PEER_NUM); + return true; +} + +int ESP_NOW_Class::getTotalPeerCount(){ + if(!_esp_now_has_begun){ + return -1; + } + esp_now_peer_num_t num; + esp_err_t err = esp_now_get_peer_num(&num); + if(err != ESP_OK){ + log_e("esp_now_get_peer_num failed! 0x%x", err); + return -1; + } + return num.total_num; +} + +int ESP_NOW_Class::getEncryptedPeerCount(){ + if(!_esp_now_has_begun){ + return -1; + } + esp_now_peer_num_t num; + esp_err_t err = esp_now_get_peer_num(&num); + if(err != ESP_OK){ + log_e("esp_now_get_peer_num failed! 0x%x", err); + return -1; + } + return num.encrypt_num; +} + +int ESP_NOW_Class::availableForWrite(){ + return ESP_NOW_MAX_DATA_LEN; +} + +size_t ESP_NOW_Class::write(const uint8_t * data, size_t len){ + if(!_esp_now_has_begun){ + return 0; + } + if(len > ESP_NOW_MAX_DATA_LEN){ + len = ESP_NOW_MAX_DATA_LEN; + } + esp_err_t result = esp_now_send(NULL, data, len); + if (result == ESP_OK) { + return len; + } else if (result == ESP_ERR_ESPNOW_NOT_INIT) { + log_e("ESPNOW not init."); + } else if (result == ESP_ERR_ESPNOW_ARG) { + log_e("Invalid argument"); + } else if (result == ESP_ERR_ESPNOW_INTERNAL) { + log_e("Internal Error"); + } else if (result == ESP_ERR_ESPNOW_NO_MEM) { + log_e("Our of memory"); + } else if (result == ESP_ERR_ESPNOW_NOT_FOUND) { + log_e("Peer not found."); + } else if (result == ESP_ERR_ESPNOW_IF) { + log_e("Interface does not match."); + } + return 0; +} + +void ESP_NOW_Class::onNewPeer(void (*cb)(const esp_now_recv_info_t *info, const uint8_t * data, int len, void * arg), void * arg){ + new_cb = cb; + new_arg = arg; +} + +ESP_NOW_Class ESP_NOW; + +/* + * + * Inheritable Peer Class + * +*/ + +ESP_NOW_Peer::ESP_NOW_Peer(const uint8_t *mac_addr, uint8_t channel, wifi_interface_t iface, const uint8_t *lmk){ + added = false; + if(mac_addr){ + memcpy(mac, mac_addr,6); + } + chan = channel; + ifc = iface; + encrypt = lmk != NULL; + if(encrypt){ + memcpy(key, lmk, 16); + } +} + +bool ESP_NOW_Peer::add(){ + if(!_esp_now_has_begun){ + return false; + } + if(added){ + return true; + } + if(_esp_now_add_peer(mac, chan, ifc, encrypt?key:NULL, this) != ESP_OK){ + return false; + } + log_v("Peer added - " MACSTR, MAC2STR(mac)); + added = true; + return true; +} + +bool ESP_NOW_Peer::remove(){ + if(!_esp_now_has_begun){ + return false; + } + if(!added){ + return true; + } + log_v("Peer removed - " MACSTR, MAC2STR(mac)); + esp_err_t err = _esp_now_del_peer(mac); + if(err == ESP_OK){ + added = false; + return true; + } + return false; +} + +const uint8_t * ESP_NOW_Peer::addr() const{ + return mac; +} + +bool ESP_NOW_Peer::addr(const uint8_t *mac_addr){ + if(!_esp_now_has_begun || !added){ + memcpy(mac, mac_addr,6); + return true; + } + return false; +} + +uint8_t ESP_NOW_Peer::getChannel() const{ + return chan; +} + +bool ESP_NOW_Peer::setChannel(uint8_t channel){ + chan = channel; + if(!_esp_now_has_begun || !added){ + return true; + } + return _esp_now_modify_peer(mac, chan, ifc, encrypt?key:NULL) == ESP_OK; +} + +wifi_interface_t ESP_NOW_Peer::getInterface() const{ + return ifc; +} + +bool ESP_NOW_Peer::setInterface(wifi_interface_t iface){ + ifc = iface; + if(!_esp_now_has_begun || !added){ + return true; + } + return _esp_now_modify_peer(mac, chan, ifc, encrypt?key:NULL) == ESP_OK; +} + +bool ESP_NOW_Peer::isEncrypted() const{ + return encrypt; +} + +bool ESP_NOW_Peer::setKey(const uint8_t *lmk){ + encrypt = lmk != NULL; + if(encrypt){ + memcpy(key, lmk, 16); + } + if(!_esp_now_has_begun || !added){ + return true; + } + return _esp_now_modify_peer(mac, chan, ifc, encrypt?key:NULL) == ESP_OK; +} + +size_t ESP_NOW_Peer::send(const uint8_t * data, int len){ + log_v(MACSTR", data lenght %d", MAC2STR(mac), len); + if(!_esp_now_has_begun || !added){ + log_e("Peer not added."); + return 0; + } + if(len > ESP_NOW_MAX_DATA_LEN){ + len = ESP_NOW_MAX_DATA_LEN; + } + esp_err_t result = esp_now_send(mac, data, len); + if (result == ESP_OK) { + return len; + } else if (result == ESP_ERR_ESPNOW_NOT_INIT) { + log_e("ESPNOW not init."); + } else if (result == ESP_ERR_ESPNOW_ARG) { + log_e("Invalid argument"); + } else if (result == ESP_ERR_ESPNOW_INTERNAL) { + log_e("Internal Error"); + } else if (result == ESP_ERR_ESPNOW_NO_MEM) { + log_e("Our of memory"); + } else if (result == ESP_ERR_ESPNOW_NOT_FOUND) { + log_e("Peer not found."); + } else if (result == ESP_ERR_ESPNOW_IF) { + log_e("Interface does not match."); + } + return 0; +} + +ESP_NOW_Peer::operator bool() const +{ + return added; +} diff --git a/libraries/ESP_NOW/src/ESP32_NOW.h b/libraries/ESP_NOW/src/ESP32_NOW.h new file mode 100644 index 00000000000..5ad26df574f --- /dev/null +++ b/libraries/ESP_NOW/src/ESP32_NOW.h @@ -0,0 +1,71 @@ +#pragma once + +#include "esp_wifi_types.h" +#include "Print.h" +#include "esp_now.h" +#include "esp32-hal-log.h" +#include "esp_mac.h" + +class ESP_NOW_Peer { +private: + uint8_t mac[6]; + uint8_t chan; + wifi_interface_t ifc; + bool encrypt; + uint8_t key[16]; + +protected: + bool added; + bool add(); + bool remove(); + size_t send(const uint8_t * data, int len); + + ESP_NOW_Peer(const uint8_t *mac_addr, uint8_t channel=0, wifi_interface_t iface=WIFI_IF_AP, const uint8_t *lmk=NULL); + +public: + virtual ~ESP_NOW_Peer() {} + + const uint8_t * addr() const; + bool addr(const uint8_t *mac_addr); + + uint8_t getChannel() const; + bool setChannel(uint8_t channel); + + wifi_interface_t getInterface() const; + bool setInterface(wifi_interface_t iface); + + bool isEncrypted() const; + bool setKey(const uint8_t *lmk); + + operator bool() const; + + //optional callbacks to be implemented by the upper class + virtual void onReceive(const uint8_t * data, size_t len, bool broadcast) { + log_i("Received %d bytes from " MACSTR " %s", len, MAC2STR(mac), broadcast ? "broadcast" : ""); + } + virtual void onSent(bool success) { log_i("Message reported as sent %s", success ? "successfully" : "unsuccessfully"); } +}; + +class ESP_NOW_Class : public Print { +public: + const uint8_t BROADCAST_ADDR[6] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; + + ESP_NOW_Class(); + ~ESP_NOW_Class(); + + bool begin(const uint8_t *pmk=NULL /* 16 bytes */); + bool end(); + + int getTotalPeerCount(); + int getEncryptedPeerCount(); + + int availableForWrite(); + size_t write(const uint8_t * data, size_t len); + size_t write(uint8_t data){ return write(&data, 1); } + + void onNewPeer(void (*cb)(const esp_now_recv_info_t *info, const uint8_t * data, int len, void * arg), void * arg); + +}; + +extern ESP_NOW_Class ESP_NOW; + diff --git a/libraries/ESP_NOW/src/ESP32_NOW_Serial.cpp b/libraries/ESP_NOW/src/ESP32_NOW_Serial.cpp new file mode 100644 index 00000000000..b562b893258 --- /dev/null +++ b/libraries/ESP_NOW/src/ESP32_NOW_Serial.cpp @@ -0,0 +1,272 @@ +#include "ESP32_NOW_Serial.h" +#include +#include "esp_now.h" +#include "esp_system.h" +#include "esp32-hal.h" +#include "esp_mac.h" + +/* + * + * Serial Port Implementation Class + * +*/ + +ESP_NOW_Serial_Class::ESP_NOW_Serial_Class(const uint8_t *mac_addr, uint8_t channel, wifi_interface_t iface, const uint8_t *lmk) +: ESP_NOW_Peer(mac_addr, channel, iface, lmk){ + tx_ring_buf = NULL; + rx_queue = NULL; + tx_sem = NULL; + queued_size = 0; + queued_buff = NULL; + resend_count = 0; +} + +ESP_NOW_Serial_Class::~ESP_NOW_Serial_Class(){ + end(); +} + +size_t ESP_NOW_Serial_Class::setTxBufferSize(size_t tx_queue_len){ + if(tx_ring_buf){ + vRingbufferDelete(tx_ring_buf); + tx_ring_buf = NULL; + } + if(!tx_queue_len){ + return 0; + } + tx_ring_buf = xRingbufferCreate(tx_queue_len, RINGBUF_TYPE_BYTEBUF); + if(!tx_ring_buf){ + return 0; + } + return tx_queue_len; +} + +size_t ESP_NOW_Serial_Class::setRxBufferSize(size_t rx_queue_len){ + if(rx_queue){ + vQueueDelete(rx_queue); + rx_queue = NULL; + } + if(!rx_queue_len){ + return 0; + } + rx_queue = xQueueCreate(rx_queue_len, sizeof(uint8_t)); + if(!rx_queue){ + return 0; + } + return rx_queue_len; +} + +bool ESP_NOW_Serial_Class::begin(unsigned long baud){ + if(!ESP_NOW.begin() || !add()){ + return false; + } + if(tx_sem == NULL){ + tx_sem = xSemaphoreCreateBinary(); + //xSemaphoreTake(tx_sem, 0); + xSemaphoreGive(tx_sem); + } + setRxBufferSize(1024);//default if not preset + setTxBufferSize(1024);//default if not preset + return true; +} + +void ESP_NOW_Serial_Class::end(){ + remove(); + setRxBufferSize(0); + setTxBufferSize(0); + if (tx_sem != NULL) { + vSemaphoreDelete(tx_sem); + tx_sem = NULL; + } +} + +//Stream +int ESP_NOW_Serial_Class::available(void){ + if(rx_queue == NULL){ + return 0; + } + return uxQueueMessagesWaiting(rx_queue); +} + +int ESP_NOW_Serial_Class::peek(void){ + if(rx_queue == NULL){ + return -1; + } + uint8_t c; + if(xQueuePeek(rx_queue, &c, 0)) { + return c; + } + return -1; +} + +int ESP_NOW_Serial_Class::read(void){ + if(rx_queue == NULL){ + return -1; + } + uint8_t c = 0; + if(xQueueReceive(rx_queue, &c, 0)) { + return c; + } + return -1; +} + +size_t ESP_NOW_Serial_Class::read(uint8_t *buffer, size_t size){ + if(rx_queue == NULL){ + return -1; + } + uint8_t c = 0; + size_t count = 0; + while(count < size && xQueueReceive(rx_queue, &c, 0)){ + buffer[count++] = c; + } + return count; +} + +void ESP_NOW_Serial_Class::flush(){ + if(tx_ring_buf == NULL){ + return; + } + UBaseType_t uxItemsWaiting = 0; + vRingbufferGetInfo(tx_ring_buf, NULL, NULL, NULL, NULL, &uxItemsWaiting); + if(uxItemsWaiting){ + // Now trigger the ISR to read data from the ring buffer. + if(xSemaphoreTake(tx_sem, 0) == pdTRUE){ + checkForTxData(); + } + } + while(uxItemsWaiting){ + delay(5); + vRingbufferGetInfo(tx_ring_buf, NULL, NULL, NULL, NULL, &uxItemsWaiting); + } +} + +//RX callback +void ESP_NOW_Serial_Class::onReceive(const uint8_t * data, size_t len, bool broadcast){ + if(rx_queue == NULL){ + return; + } + for(uint32_t i=0; i 0; + } + //log_d(MACSTR ": EMPTY", MAC2STR(addr())); + xSemaphoreGive(tx_sem); + return false; +} + +size_t ESP_NOW_Serial_Class::write(const uint8_t *buffer, size_t size, uint32_t timeout){ + log_v(MACSTR", size %u", MAC2STR(addr()), size); + if(tx_sem == NULL || tx_ring_buf == NULL || !added){ + return 0; + } + size_t space = availableForWrite(); + size_t left = size; + + if(space){ + if(left < space){ + space = left; + } + if(xRingbufferSend(tx_ring_buf, (void*) (buffer), space, 0) == pdTRUE){ + buffer += space; + left -= space; + if(xSemaphoreTake(tx_sem, 0) == pdTRUE){ + if(checkForTxData()){ + if(!left){ + //we are done + return size; + } + } else { + //send failed + return 0; + } + } + } else { + log_e("RingbufferFastSend Failed"); + return 0; + } + } else if(xSemaphoreTake(tx_sem, timeout) == pdTRUE){ + checkForTxData(); + } + // Blocking method, Sending data to ringbuffer, and handle the data in ISR. + if(xRingbufferSend(tx_ring_buf, (void*) (buffer), left, timeout / portTICK_PERIOD_MS) != pdTRUE){ + log_e("RingbufferSend Failed"); + return size - left; + } + // Now trigger the ISR to read data from the ring buffer. + if(xSemaphoreTake(tx_sem, 0) == pdTRUE){ + checkForTxData(); + } + + return size; +} +//TX Done Callback +void ESP_NOW_Serial_Class::onSent(bool success){ + log_v(MACSTR" : %s", MAC2STR(addr()), success?"OK":"FAIL"); + if(tx_sem == NULL || tx_ring_buf == NULL || !added){ + return; + } + if(success){ + vRingbufferReturnItem(tx_ring_buf, queued_buff); + queued_buff = NULL; + //send next packet? + //log_d(MACSTR ": NEXT", MAC2STR(addr())); + checkForTxData(); + } else { + //send failed + //resend + if(resend_count < 5){ + resend_count++; + //log_d(MACSTR ": RE-SENDING[%u]", MAC2STR(addr()), resend_count); + tryToSend(); + } else { + //resend limit reached + //the data is lost in this case + vRingbufferReturnItem(tx_ring_buf, queued_buff); + queued_buff = NULL; + xSemaphoreGive(tx_sem); + end(); + log_e(MACSTR" : RE-SEND_MAX[%u]", MAC2STR(addr()), resend_count); + } + } +} diff --git a/libraries/ESP_NOW/src/ESP32_NOW_Serial.h b/libraries/ESP_NOW/src/ESP32_NOW_Serial.h new file mode 100644 index 00000000000..a2f97948682 --- /dev/null +++ b/libraries/ESP_NOW/src/ESP32_NOW_Serial.h @@ -0,0 +1,46 @@ +#pragma once + +#include "esp_wifi_types.h" +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "freertos/queue.h" +#include "freertos/semphr.h" +#include "freertos/ringbuf.h" +#include "Stream.h" +#include "ESP32_NOW.h" + +class ESP_NOW_Serial_Class : public Stream, public ESP_NOW_Peer { +private: + RingbufHandle_t tx_ring_buf; + QueueHandle_t rx_queue; + SemaphoreHandle_t tx_sem; + size_t queued_size; + uint8_t *queued_buff; + size_t resend_count; + + bool checkForTxData(); + size_t tryToSend(); +public: + ESP_NOW_Serial_Class(const uint8_t *mac_addr, uint8_t channel, wifi_interface_t iface=WIFI_IF_AP, const uint8_t *lmk=NULL); + ~ESP_NOW_Serial_Class(); + size_t setRxBufferSize(size_t); + size_t setTxBufferSize(size_t); + bool begin(unsigned long baud=0); + void end(); + //Stream + int available(); + int read(); + size_t read(uint8_t *buffer, size_t size); + int peek(); + void flush(); + //Print + int availableForWrite(); + size_t write(const uint8_t *buffer, size_t size, uint32_t timeout_ms); + size_t write(const uint8_t *buffer, size_t size){ return write(buffer, size, 1000); } + size_t write(uint8_t data){ return write(&data, 1); } + //ESP_NOW_Peer + void onReceive(const uint8_t * data, size_t len, bool broadcast); + void onSent(bool success); +}; + +