3.3.7
This commit is contained in:
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
Reference in New Issue
Block a user