Unverified Commit 1a808294 authored by Jan Procházka's avatar Jan Procházka Committed by GitHub

Add ESP-NOW Arduino library (#9395)

* feat(libs): Add ESP-NOW Arduino library

* Update libraries/ESP_NOW/src/ESP32_NOW.cpp
Co-authored-by: default avatarLucas Saavedra Vaz <32426024+lucasssvaz@users.noreply.github.com>

* Update libraries/ESP_NOW/src/ESP32_NOW.cpp
Co-authored-by: default avatarLucas Saavedra Vaz <32426024+lucasssvaz@users.noreply.github.com>

* fix(esp-now): Add check if Wifi is started.

* Fix ESP_NOW_Serial

* Add ESP NOW Serial Example

* Add comment

* Skip esp-now example for esp32h2

* Add broadcast address constant

* Change return value to align with other APIs

* Apply suggested changes

* Improve example

* Fix example

* Improve serial example

* Add argument to receive callback to know if a message was broadcasted

* Update libraries/ESP_NOW/src/ESP32_NOW_Serial.cpp

* Simplify example

* Add broadcast example

* Change comments

* Change comment

* Improve broadcast master example

* Remove examples using IDF's API

* Fix example

* Add network example

* Add skip file

* Add LMK back

* Add logs

* Improve example

* Fix example

* Apply @suglider suggestions from code review
Co-authored-by: default avatarRodrigo Garcia <rodrigo.garcia@espressif.com>

* Add documentation

* fix examples links in docs

* Apply @lucasssvaz suggestions to docs
Co-authored-by: default avatarLucas Saavedra Vaz <32426024+lucasssvaz@users.noreply.github.com>

* Update espnow.rst

* Update examples

* make onSent optional and remove underscores for virtual functions

* Make onRecieve also optional and make constructor protected

---------
Co-authored-by: default avatarLucas Saavedra Vaz <32426024+lucasssvaz@users.noreply.github.com>
Co-authored-by: default avatarme-no-dev <hristo@espressif.com>
Co-authored-by: default avatarRodrigo Garcia <rodrigo.garcia@espressif.com>
parent 6c6666ad
...@@ -83,6 +83,7 @@ set(ARDUINO_ALL_LIBRARIES ...@@ -83,6 +83,7 @@ set(ARDUINO_ALL_LIBRARIES
DNSServer DNSServer
EEPROM EEPROM
ESP_I2S ESP_I2S
ESP_NOW
ESP_SR ESP_SR
ESPmDNS ESPmDNS
Ethernet Ethernet
...@@ -128,6 +129,10 @@ set(ARDUINO_LIBRARY_EEPROM_SRCS libraries/EEPROM/src/EEPROM.cpp) ...@@ -128,6 +129,10 @@ 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_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 set(ARDUINO_LIBRARY_ESP_SR_SRCS
libraries/ESP_SR/src/ESP_SR.cpp libraries/ESP_SR/src/ESP_SR.cpp
libraries/ESP_SR/src/esp32-hal-sr.c) libraries/ESP_SR/src/esp32-hal-sr.c)
......
...@@ -2,29 +2,275 @@ ...@@ -2,29 +2,275 @@
ESP-NOW ESP-NOW
####### #######
ESP-NOW is a fast, connectionless communication technology featuring a short packet transmission. About
ESP-NOW is ideal for smart lights, remote control devices, sensors and other applications. -----
.. 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 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 1. ESP-NOW Broadcast Master Example:
:language: arduino
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 2. ESP-NOW Broadcast Slave Example:
:language: arduino
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
/**
ESPNOW - Basic communication - Master
Date: 26th September 2017
Author: Arvind Ravulavaru <https://github.com/arvindr21>
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 <esp_now.h>
#include <WiFi.h>
#include <esp_wifi.h> // 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);
}
/**
ESPNOW - Basic communication - Slave
Date: 26th September 2017
Author: Arvind Ravulavaru <https://github.com/arvindr21>
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 <esp_now.h>
#include <WiFi.h>
#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
}
/**
ESPNOW - Basic communication - Master
Date: 26th September 2017
Author: Arvind Ravulavaru <https://github.com/arvindr21>
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 <esp_now.h>
#include <WiFi.h>
// 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);
}
/**
ESPNOW - Basic communication - Slave
Date: 26th September 2017
Author: Arvind Ravulavaru <https://github.com/arvindr21>
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 <esp_now.h>
#include <WiFi.h>
#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
}
/*
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 <esp_mac.h> // 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);
}
/*
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 <esp_mac.h> // For the MAC2STR and MACSTR macros
#include <vector>
/* 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<ESP_NOW_Peer_Class> 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);
}
This diff is collapsed.
/*
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);
}
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
This diff is collapsed.
#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;
#include "ESP32_NOW_Serial.h"
#include <string.h>
#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<len; i++){
if(!xQueueSend(rx_queue, data+i, 0)){
log_e("RX Overflow!");
return;
}
}
// Now trigger the ISR to read data from the ring buffer.
if(xSemaphoreTake(tx_sem, 0) == pdTRUE){
checkForTxData();
}
}
//Print
int ESP_NOW_Serial_Class::availableForWrite(){
//return ESP_NOW_MAX_DATA_LEN;
if(tx_ring_buf == NULL){
return 0;
}
return xRingbufferGetCurFreeSize(tx_ring_buf);
}
size_t ESP_NOW_Serial_Class::tryToSend(){
//log_d(MACSTR ": %u", MAC2STR(addr()), queued_size);
size_t sent = send(queued_buff, queued_size);
if(!sent){
//_onSent will not be called anymore
//the data is lost in this case
vRingbufferReturnItem(tx_ring_buf, queued_buff);
queued_buff = NULL;
xSemaphoreGive(tx_sem);
end();
}
return sent;
}
bool ESP_NOW_Serial_Class::checkForTxData(){
//do we have something that failed the last time?
resend_count = 0;
if (queued_buff == NULL) {
queued_buff = (uint8_t *)xRingbufferReceiveUpTo(tx_ring_buf, &queued_size, 0, ESP_NOW_MAX_DATA_LEN);
} else {
log_d(MACSTR" : PREVIOUS", MAC2STR(addr()));
}
if (queued_buff != NULL) {
return tryToSend() > 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);
}
}
}
#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);
};
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment