This commit is contained in:
2026-05-22 21:52:50 +03:00
commit be7c60e4dd
1854 changed files with 583428 additions and 0 deletions
@@ -0,0 +1,106 @@
/*
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 broadcast peer object
ESP_NOW_Broadcast_Peer broadcast_peer(ESPNOW_WIFI_CHANNEL, WIFI_IF_STA, nullptr);
/* Main */
void setup() {
Serial.begin(115200);
// Initialize the Wi-Fi module
WiFi.mode(WIFI_STA);
WiFi.setChannel(ESPNOW_WIFI_CHANNEL);
while (!WiFi.STA.started()) {
delay(100);
}
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);
// 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.printf("ESP-NOW version: %d, max data length: %d\n", ESP_NOW.getVersion(), ESP_NOW.getMaxDataLen());
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);
}
@@ -0,0 +1,2 @@
requires:
- CONFIG_SOC_WIFI_SUPPORTED=y
@@ -0,0 +1,129 @@
/*
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
// Note: Using pointers instead of objects to prevent dangling pointers when the vector reallocates
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 = new ESP_NOW_Peer_Class(info->src_addr, ESPNOW_WIFI_CHANNEL, WIFI_IF_STA, nullptr);
if (!new_master->add_peer()) {
Serial.println("Failed to register the new master");
delete new_master;
return;
}
masters.push_back(new_master);
Serial.printf("Successfully registered master " MACSTR " (total masters: %zu)\n", MAC2STR(new_master->addr()), masters.size());
} 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);
// Initialize the Wi-Fi module
WiFi.mode(WIFI_STA);
WiFi.setChannel(ESPNOW_WIFI_CHANNEL);
while (!WiFi.STA.started()) {
delay(100);
}
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 ESP-NOW protocol
if (!ESP_NOW.begin()) {
Serial.println("Failed to initialize ESP-NOW");
Serial.println("Reeboting in 5 seconds...");
delay(5000);
ESP.restart();
}
Serial.printf("ESP-NOW version: %d, max data length: %d\n", ESP_NOW.getVersion(), ESP_NOW.getMaxDataLen());
// Register the new peer callback
ESP_NOW.onNewPeer(register_new_master, nullptr);
Serial.println("Setup complete. Waiting for a master to broadcast a message...");
}
void loop() {
// Print debug information every 10 seconds
static unsigned long last_debug = 0;
if (millis() - last_debug > 10000) {
last_debug = millis();
Serial.printf("Registered masters: %zu\n", masters.size());
for (size_t i = 0; i < masters.size(); i++) {
if (masters[i]) {
Serial.printf(" Master %zu: " MACSTR "\n", i, MAC2STR(masters[i]->addr()));
}
}
}
delay(100);
}
@@ -0,0 +1,2 @@
requires:
- CONFIG_SOC_WIFI_SUPPORTED=y
@@ -0,0 +1,381 @@
/*
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 <esp_mac.h> // For the MAC2STR and MACSTR macros
#include <vector>
#include <new> //std::nothrow
/* 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 payload is 250 bytes (ESP_NOW_MAX_DATA_LEN) for ESP-NOW v1.0.
// For ESP-NOW v2.0, the maximum size of the payload is 1470 bytes (ESP_NOW_MAX_DATA_LEN_V2).
// You can use ESP_NOW.getMaxDataLen() after calling ESP_NOW.begin() to get the maximum size
// of the data that can be sent.
// Read about the compatibility between ESP-NOW v1.0 and v2.0 in the ESP-IDF documentation:
// https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/network/esp_now.html#frame-format
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<uint32_t> 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 == nullptr || 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_i("Broadcast message reported as sent %s", success ? "successfully" : "unsuccessfully");
} else {
log_i("Unicast message reported as sent %s to peer " MACSTR, success ? "successfully" : "unsuccessfully", MAC2STR(addr()));
}
}
};
/* Peers */
// Create a vector to store the peer pointers
std::vector<ESP_NOW_Network_Peer *> peers;
// Register the broadcast peer (no encryption support for the broadcast address)
ESP_NOW_Network_Peer broadcast_peer(ESP_NOW.BROADCAST_ADDR, 0, nullptr);
// Pointer to the peer that is the master
ESP_NOW_Network_Peer *master_peer = nullptr;
/* 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 (std::nothrow) 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);
// Initialize the Wi-Fi module
WiFi.mode(WIFI_STA);
WiFi.setChannel(ESPNOW_WIFI_CHANNEL);
while (!WiFi.STA.started()) {
delay(100);
}
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);
// 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();
}
Serial.printf("ESP-NOW version: %d, max data length: %d\n", ESP_NOW.getVersion(), ESP_NOW.getMaxDataLen());
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, nullptr);
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);
}
@@ -0,0 +1,2 @@
requires:
- CONFIG_SOC_WIFI_SUPPORTED=y
@@ -0,0 +1,84 @@
/*
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);
while (!(WiFi.STA.started() || WiFi.AP.started())) {
delay(100);
}
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.printf("ESP-NOW version: %d, max data length: %d\n", ESP_NOW.getVersion(), ESP_NOW.getMaxDataLen());
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);
}
@@ -0,0 +1,2 @@
requires:
- CONFIG_SOC_WIFI_SUPPORTED=y
+9
View File
@@ -0,0 +1,9 @@
name=ESP_NOW
version=3.3.7
author=me-no-dev
maintainer=P-R-O-C-H-Y
sentence=Library for ESP_NOW
paragraph=Supports ESP32 Arduino platforms.
category=Communication
url=https://github.com/espressif/arduino-esp32/
architectures=esp32
+552
View File
@@ -0,0 +1,552 @@
#include "sdkconfig.h"
#if CONFIG_ESP_WIFI_REMOTE_ENABLED
#warning "ESP-NOW is only supported in SoCs with native Wi-Fi support"
#else
#include "ESP32_NOW.h"
#include <string.h>
#include "esp_system.h"
#include "esp32-hal.h"
#include "esp_wifi.h"
#ifndef ESP_NOW_MAX_DATA_LEN_V2
#define ESP_NOW_MAX_DATA_LEN_V2 1470
#endif
static void (*new_cb)(const esp_now_recv_info_t *info, const uint8_t *data, int len, void *arg) = nullptr;
static void *new_arg = nullptr; // * tx_arg = nullptr, * rx_arg = nullptr,
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_rate_config_t *rate_config, ESP_NOW_Peer *_peer = nullptr
) {
log_i("Adding peer " 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 != nullptr;
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 != nullptr) {
for (uint8_t i = 0; i < ESP_NOW_MAX_TOTAL_PEER_NUM; i++) {
if (_esp_now_peers[i] == nullptr) {
_esp_now_peers[i] = _peer;
if (_esp_now_has_begun && rate_config != nullptr) {
log_i("ESP-NOW already running. Setting PHY rate for peer " MACSTR, MAC2STR(_peer->addr()));
result = esp_now_set_peer_rate_config(_peer->addr(), rate_config);
if (result != ESP_OK) {
log_w("Could not set the ESP-NOW PHY rate for peer " MACSTR, MAC2STR(_peer->addr()));
}
}
return ESP_OK;
}
}
log_e("Library Peer list full");
return ESP_FAIL;
}
} 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("ESP-NOW Peer list full");
} else if (result == ESP_ERR_ESPNOW_NO_MEM) {
log_e("Out of memory");
} else if (result == ESP_ERR_ESPNOW_EXIST) {
log_e("Peer already exists");
}
return result;
}
static esp_err_t _esp_now_del_peer(const uint8_t *mac_addr) {
esp_err_t result = esp_now_del_peer(mac_addr);
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_NOT_FOUND) {
log_e("Peer Not Found");
}
for (uint8_t i = 0; i < ESP_NOW_MAX_TOTAL_PEER_NUM; i++) {
if (_esp_now_peers[i] != nullptr && memcmp(mac_addr, _esp_now_peers[i]->addr(), ESP_NOW_ETH_ALEN) == 0) {
_esp_now_peers[i] = nullptr;
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 != nullptr;
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 length : %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 != nullptr) {
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; i < ESP_NOW_MAX_TOTAL_PEER_NUM; i++) {
if (_esp_now_peers[i] != nullptr) {
log_v("Checking peer " MACSTR, MAC2STR(_esp_now_peers[i]->addr()));
}
if (_esp_now_peers[i] != nullptr && 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;
}
}
}
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 5, 0)
static void _esp_now_tx_cb(const esp_now_send_info_t *tx_info, esp_now_send_status_t status) {
const uint8_t *mac_addr = tx_info->des_addr;
#else
static void _esp_now_tx_cb(const uint8_t *mac_addr, esp_now_send_status_t status) {
#endif
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; i < ESP_NOW_MAX_TOTAL_PEER_NUM; i++) {
if (_esp_now_peers[i] != nullptr && memcmp(mac_addr, _esp_now_peers[i]->addr(), ESP_NOW_ETH_ALEN) == 0) {
_esp_now_peers[i]->onSent(status == ESP_NOW_SEND_SUCCESS);
return;
}
}
}
esp_err_t _esp_now_set_all_peers_rate() {
for (uint8_t i = 0; i < ESP_NOW_MAX_TOTAL_PEER_NUM; i++) {
if (_esp_now_peers[i] != nullptr) {
log_v("Setting PHY rate for peer " MACSTR, MAC2STR(_esp_now_peers[i]->addr()));
esp_now_rate_config_t rate = _esp_now_peers[i]->getRate();
esp_err_t err = esp_now_set_peer_rate_config(_esp_now_peers[i]->addr(), &rate);
if (err != ESP_OK) {
log_e("Failed to set rate for peer " MACSTR, MAC2STR(_esp_now_peers[i]->addr()));
return err;
}
}
}
return ESP_OK;
}
ESP_NOW_Class::ESP_NOW_Class() {
max_data_len = 0;
version = 0;
}
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;
}
// Unfortunately we can't get the ESP-NOW version before initializing the Wi-Fi
uint32_t esp_now_version;
err = esp_now_get_version(&esp_now_version);
if (err != ESP_OK) {
log_w("esp_now_get_version failed! Assuming ESP-NOW v1.0");
esp_now_version = 1;
}
if (esp_now_version == 1) {
max_data_len = ESP_NOW_MAX_DATA_LEN;
} else {
max_data_len = ESP_NOW_MAX_DATA_LEN_V2;
}
version = esp_now_version;
log_i("ESP-NOW version: %lu, max_data_len: %lu", version, max_data_len);
_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;
}
// Set the peers PHY rate after initializing ESP-NOW.
err = _esp_now_set_all_peers_rate();
if (err != ESP_OK) {
log_e("Failed to set PHY rate for peers! 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
for (uint8_t i = 0; i < ESP_NOW_MAX_TOTAL_PEER_NUM; i++) {
if (_esp_now_peers[i] != nullptr) {
removePeer(*_esp_now_peers[i]);
}
}
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;
//clear the peer list
memset(_esp_now_peers, 0, sizeof(ESP_NOW_Peer *) * ESP_NOW_MAX_TOTAL_PEER_NUM);
return true;
}
int ESP_NOW_Class::getTotalPeerCount() const {
if (!_esp_now_has_begun) {
log_e("ESP-NOW not initialized");
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() const {
if (!_esp_now_has_begun) {
log_e("ESP-NOW not initialized");
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::getMaxDataLen() const {
if (max_data_len == 0) {
log_e("ESP-NOW not initialized. Please call begin() first to get the max data length.");
return -1;
}
return max_data_len;
}
int ESP_NOW_Class::getVersion() const {
if (version == 0) {
log_e("ESP-NOW not initialized. Please call begin() first to get the version.");
return -1;
}
return version;
}
int ESP_NOW_Class::availableForWrite() {
int available = getMaxDataLen();
if (available < 0) {
return 0;
}
return available;
}
size_t ESP_NOW_Class::write(const uint8_t *data, size_t len) {
if (!_esp_now_has_begun) {
log_e("ESP-NOW not initialized. Please call begin() first to send data.");
return 0;
}
if (len > max_data_len) {
len = max_data_len;
}
esp_err_t result = esp_now_send(nullptr, 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;
}
bool ESP_NOW_Class::removePeer(ESP_NOW_Peer &peer) {
return peer.remove();
}
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, esp_now_rate_config_t *rate_config) {
added = false;
if (mac_addr) {
memcpy(mac, mac_addr, 6);
}
chan = channel;
ifc = iface;
encrypt = lmk != nullptr;
if (encrypt) {
memcpy(key, lmk, 16);
}
if (rate_config) {
rate = *rate_config;
} else {
rate = DEFAULT_ESPNOW_RATE_CONFIG;
}
}
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 : nullptr, &rate, this) != ESP_OK) {
log_e("Failed to add peer " MACSTR, MAC2STR(mac));
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_i("Removing peer - " MACSTR, MAC2STR(mac));
esp_err_t err = _esp_now_del_peer(mac);
if (err == ESP_OK) {
added = false;
log_i("Peer removed - " MACSTR, MAC2STR(mac));
return true;
}
log_e("Failed to remove peer " MACSTR, MAC2STR(mac));
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;
}
log_e("Peer already added and ESP-NOW is already running. Cannot change the MAC address.");
log_e("Please call addr() before adding the peer or before starting ESP-NOW.");
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 : nullptr) == 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 : nullptr) == ESP_OK;
}
/**
* @brief Set the rate configuration for the peer.
*
* @param rate_config Pointer to the rate configuration to set. Nullptr to reset to default rate configuration.
* @return true if the rate configuration was set successfully, false otherwise.
*/
bool ESP_NOW_Peer::setRate(const esp_now_rate_config_t *rate_config) {
if (added && _esp_now_has_begun) {
log_e("Peer already added and ESP-NOW is already running. Cannot set rate configuration.");
log_e("Please call setRate() before adding the peer or before starting ESP-NOW.");
return false;
}
if (rate_config == nullptr) {
log_i("Resetting rate configuration to default.");
rate = DEFAULT_ESPNOW_RATE_CONFIG;
} else {
rate = *rate_config;
}
return true;
}
/**
* @brief Get the rate configuration for the peer.
*
* @return esp_now_rate_config_t The rate configuration for the peer.
*/
esp_now_rate_config_t ESP_NOW_Peer::getRate() const {
return rate;
}
bool ESP_NOW_Peer::isEncrypted() const {
return encrypt;
}
bool ESP_NOW_Peer::setKey(const uint8_t *lmk) {
encrypt = lmk != nullptr;
if (encrypt) {
memcpy(key, lmk, 16);
}
if (!_esp_now_has_begun || !added) {
return true;
}
return _esp_now_modify_peer(mac, chan, ifc, encrypt ? key : nullptr) == ESP_OK;
}
size_t ESP_NOW_Peer::send(const uint8_t *data, int len) {
log_v(MACSTR ", data length %d", MAC2STR(mac), len);
if (!_esp_now_has_begun || !added) {
log_e("Peer not added.");
return 0;
}
int max_data_len = ESP_NOW.getMaxDataLen();
if (max_data_len < 0) {
log_e("Error getting max data length.");
return 0;
}
if (len > max_data_len) {
len = 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;
}
#endif
+112
View File
@@ -0,0 +1,112 @@
#pragma once
#include "sdkconfig.h"
#if CONFIG_ESP_WIFI_REMOTE_ENABLED
#warning "ESP-NOW is only supported in SoCs with native Wi-Fi support"
#else
#include "esp_wifi_types.h"
#include "Print.h"
#include "esp_now.h"
#include "esp32-hal-log.h"
#include "esp_mac.h"
// clang-format off
#define DEFAULT_ESPNOW_RATE_CONFIG { \
.phymode = WIFI_PHY_MODE_11G, \
.rate = WIFI_PHY_RATE_1M_L, \
.ersu = false, \
.dcm = false \
}
// clang-format on
class ESP_NOW_Peer; //forward declaration for friend function
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 = nullptr /* 16 bytes */);
bool end();
int getTotalPeerCount() const;
int getEncryptedPeerCount() const;
int getMaxDataLen() const;
int getVersion() const;
int availableForWrite();
// You can directly send data to all peers without broadcasting using ESP_NOW.write(data, len)
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);
bool removePeer(ESP_NOW_Peer &peer);
protected:
size_t max_data_len;
uint32_t version;
};
class ESP_NOW_Peer {
private:
uint8_t mac[6];
uint8_t chan;
wifi_interface_t ifc;
esp_now_rate_config_t rate;
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 = nullptr,
esp_now_rate_config_t *rate_config = nullptr
);
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 setRate(const esp_now_rate_config_t *rate_config);
esp_now_rate_config_t getRate() const;
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 transmission to peer " MACSTR " %s", MAC2STR(mac), success ? "successful" : "failed");
}
friend bool ESP_NOW_Class::removePeer(ESP_NOW_Peer &);
};
#if !defined(NO_GLOBAL_INSTANCES) && !defined(NO_GLOBAL_ESP_NOW)
extern ESP_NOW_Class ESP_NOW;
#endif
#endif
+302
View File
@@ -0,0 +1,302 @@
#include "sdkconfig.h"
#if CONFIG_ESP_WIFI_REMOTE_ENABLED
#warning "ESP-NOW is only supported in SoCs with native Wi-Fi support"
#else
#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, bool remove_on_fail)
: ESP_NOW_Peer(mac_addr, channel, iface, lmk) {
tx_ring_buf = nullptr;
rx_queue = nullptr;
tx_sem = nullptr;
queued_size = 0;
queued_buff = nullptr;
resend_count = 0;
_remove_on_fail = remove_on_fail;
}
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 = nullptr;
}
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 = nullptr;
}
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 == nullptr) {
tx_sem = xSemaphoreCreateBinary();
//xSemaphoreTake(tx_sem, 0);
xSemaphoreGive(tx_sem);
}
size_t buf_size = 0;
if (ESP_NOW.getVersion() == 2) {
// ESP-NOW v2.0 has a larger maximum data length, so we need to increase the buffer sizes
// to hold around 3-4 packets
buf_size = setRxBufferSize(4096);
buf_size &= setTxBufferSize(4096);
} else {
// ESP-NOW v1.0 has a smaller maximum data length, so we can use the default buffer sizes
// to hold around 3-4 packets
buf_size = setRxBufferSize(1024);
buf_size &= setTxBufferSize(1024);
}
if (buf_size == 0) {
log_e("Failed to set buffer size");
return false;
}
return true;
}
void ESP_NOW_Serial_Class::end() {
remove();
setRxBufferSize(0);
setTxBufferSize(0);
if (tx_sem != nullptr) {
vSemaphoreDelete(tx_sem);
tx_sem = nullptr;
}
}
//Stream
int ESP_NOW_Serial_Class::available(void) {
if (rx_queue == nullptr) {
return 0;
}
return uxQueueMessagesWaiting(rx_queue);
}
int ESP_NOW_Serial_Class::peek(void) {
if (rx_queue == nullptr) {
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 == nullptr) {
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 == nullptr) {
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 == nullptr) {
return;
}
UBaseType_t uxItemsWaiting = 0;
vRingbufferGetInfo(tx_ring_buf, nullptr, nullptr, nullptr, nullptr, &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, nullptr, nullptr, nullptr, nullptr, &uxItemsWaiting);
}
}
//RX callback
void ESP_NOW_Serial_Class::onReceive(const uint8_t *data, size_t len, bool broadcast) {
if (rx_queue == nullptr) {
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() {
if (tx_ring_buf == nullptr) {
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 = nullptr;
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 == nullptr) {
queued_buff = (uint8_t *)xRingbufferReceiveUpTo(tx_ring_buf, &queued_size, 0, ESP_NOW.getMaxDataLen());
} else {
log_d(MACSTR " : PREVIOUS", MAC2STR(addr()));
}
if (queued_buff != nullptr) {
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 == nullptr || tx_ring_buf == nullptr || !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 == nullptr || tx_ring_buf == nullptr || !added) {
return;
}
if (success) {
vRingbufferReturnItem(tx_ring_buf, queued_buff);
queued_buff = nullptr;
//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 = nullptr;
log_e(MACSTR " : RE-SEND_MAX[%u]", MAC2STR(addr()), resend_count);
//if we are not able to send the data and remove_on_fail is set, remove the peer
if (_remove_on_fail) {
xSemaphoreGive(tx_sem);
end();
return;
}
//log_d(MACSTR ": NEXT", MAC2STR(addr()));
checkForTxData();
}
}
}
#endif
+59
View File
@@ -0,0 +1,59 @@
#pragma once
#include "sdkconfig.h"
#if CONFIG_ESP_WIFI_REMOTE_ENABLED
#warning "ESP-NOW is only supported in SoCs with native Wi-Fi support"
#else
#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 _remove_on_fail;
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 = nullptr, bool remove_on_fail = false
);
~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);
};
#endif