Skip to content

Commit 6ad83cf

Browse files
committed
Add network example
1 parent c435a7f commit 6ad83cf

File tree

1 file changed

+371
-0
lines changed

1 file changed

+371
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,371 @@
1+
/*
2+
ESP-NOW Network Example
3+
Lucas Saavedra Vaz - 2024
4+
5+
This example is based on the ESP-NOW example from the ESP-IDF framework.
6+
7+
The aim of this example is to demonstrate how to create a network of devices using the ESP-NOW protocol.
8+
The slave devices will broadcast random data to the master device every 5 seconds and from time to time
9+
they will ping the other slave devices with a "Hello!" message.
10+
11+
The master device will receive the data from the slave devices and print it to the Serial Monitor. From time
12+
to time, the master device will calculate the average of the priorities of the slave devices and send it to
13+
all the slave devices.
14+
15+
Each device will have a priority that will be used to decide which device will be the master.
16+
The device with the highest priority will be the master.
17+
18+
Flow:
19+
1. Each device will generate a priority based on its MAC address.
20+
2. The devices will broadcast their priority on the network.
21+
3. The devices will listen to the broadcast messages and register the priorities of the other devices.
22+
4. After all devices have been registered, the device with the highest priority will be the master.
23+
5. The slave devices will send random data to the master every 5 seconds.
24+
- Every "REPORT_INTERVAL" messages, the slaves will send a message to the other slaves.
25+
6. The master device will calculate the average of the data and send it to the slave devices every "REPORT_INTERVAL" messages.
26+
27+
*/
28+
29+
#include "ESP32_NOW.h"
30+
#include "WiFi.h"
31+
32+
#include <esp_mac.h> // For the MAC2STR and MACSTR macros
33+
34+
#include <algorithm>
35+
#include <vector>
36+
37+
/* Definitions */
38+
39+
// Wi-Fi interface to be used by the ESP-NOW protocol
40+
#define ESPNOW_WIFI_IFACE WIFI_IF_STA
41+
42+
// Channel to be used by the ESP-NOW protocol
43+
#define ESPNOW_WIFI_CHANNEL 4
44+
45+
// Delay between sending messages
46+
#define ESPNOW_SEND_INTERVAL_MS 5000
47+
48+
// Number of peers to wait for (excluding this device)
49+
#define ESPNOW_PEER_COUNT 2
50+
51+
// Report to other devices every 5 messages
52+
#define REPORT_INTERVAL 5
53+
54+
/*
55+
ESP-NOW uses the CCMP method, which is described in IEEE Std. 802.11-2012, to protect the vendor-specific action frame.
56+
The Wi-Fi device maintains a Primary Master Key (PMK) and several Local Master Keys (LMK).
57+
The lengths of both PMK and LMK need to be 16 bytes.
58+
59+
PMK is used to encrypt LMK with the AES-128 algorithm. If PMK is not set, a default PMK will be used.
60+
61+
LMK of the paired device is used to encrypt the vendor-specific action frame with the CCMP method.
62+
The maximum number of different LMKs is six. If the LMK of the paired device is not set, the vendor-specific
63+
action frame will not be encrypted.
64+
65+
Encrypting multicast (broadcast address) vendor-specific action frame is not supported.
66+
67+
PMK needs to be the same for all devices in the network. LMK only needs to be the same between paired devices.
68+
*/
69+
70+
// Primary Master Key (PMK) and Local Master Key (LMK)
71+
#define ESPNOW_EXAMPLE_PMK "pmk1234567890123"
72+
#define ESPNOW_EXAMPLE_LMK "lmk1234567890123"
73+
74+
/* Structs */
75+
76+
// The following struct is used to send data to the peer device.
77+
// We use the attribute "packed" to ensure that the struct is not padded (all data
78+
// is contiguous in the memory and without gaps).
79+
// The maximum size of the complete message is 250 bytes (ESP_NOW_MAX_DATA_LEN).
80+
81+
typedef struct {
82+
uint32_t count;
83+
uint32_t priority;
84+
uint32_t data;
85+
char str[7];
86+
} __attribute__((packed)) esp_now_data_t;
87+
88+
/* Global Variables */
89+
90+
uint32_t self_priority = 0; // Priority of this device
91+
uint8_t current_peer_count = 0; // Number of peers that have been found
92+
uint8_t check_count = 0; // Counter to wait after all peers have been found
93+
bool device_is_master = false; // Flag to indicate if this device is the master
94+
bool master_decided = false; // Flag to indicate if the master has been decided
95+
uint32_t sent_msg_count = 0; // Counter for the messages sent. Only starts counting after all peers have been found
96+
uint32_t recv_msg_count = 0; // Counter for the messages received. Only starts counting after all peers have been found
97+
esp_now_data_t new_msg; // Message that will be sent to the peers
98+
std::vector<uint32_t> last_data(5); // Vector that will store the last 5 data received
99+
100+
/* Classes */
101+
102+
// We need to create a class that inherits from ESP_NOW_Peer to implement the _onReceive and _onSent methods.
103+
// This class will be used to store the priority of the device and to send messages to the peers.
104+
// For more information about the ESP_NOW_Peer class, see the ESP_NOW_Peer class in the ESP32_NOW.h file.
105+
106+
class ESP_NOW_Network_Peer : public ESP_NOW_Peer {
107+
public:
108+
uint32_t priority;
109+
bool peer_is_master = false;
110+
111+
// Revert the lmk later
112+
ESP_NOW_Network_Peer(const uint8_t *mac_addr, uint32_t priority = 0, const uint8_t *lmk = nullptr);
113+
~ESP_NOW_Network_Peer();
114+
115+
bool begin();
116+
bool send_message(const uint8_t *data, size_t len);
117+
118+
// ESP_NOW_Peer interfaces
119+
void _onReceive(const uint8_t *data, size_t len, bool broadcast);
120+
void _onSent(bool success);
121+
};
122+
123+
/* Methods */
124+
125+
ESP_NOW_Network_Peer::ESP_NOW_Network_Peer(const uint8_t *mac_addr, uint32_t priority, const uint8_t *lmk)
126+
: ESP_NOW_Peer(mac_addr, ESPNOW_WIFI_CHANNEL, ESPNOW_WIFI_IFACE, lmk)
127+
, priority(priority) {}
128+
129+
ESP_NOW_Network_Peer::~ESP_NOW_Network_Peer() {}
130+
131+
bool ESP_NOW_Network_Peer::begin() {
132+
// In this example the ESP-NOW protocol will already be initialized as we require it to receive broadcast messages.
133+
if (!add()) {
134+
log_e("Failed to initialize ESP-NOW or register the peer");
135+
return false;
136+
}
137+
return true;
138+
}
139+
140+
bool ESP_NOW_Network_Peer::send_message(const uint8_t *data, size_t len) {
141+
if (data == NULL || len == 0) {
142+
log_e("Data to be sent is NULL or has a length of 0");
143+
return false;
144+
}
145+
146+
// Call the parent class method to send the data
147+
return send(data, len);
148+
}
149+
150+
void ESP_NOW_Network_Peer::_onReceive(const uint8_t *data, size_t len, bool broadcast) {
151+
if (!broadcast) {
152+
esp_now_data_t *msg = (esp_now_data_t *)data;
153+
recv_msg_count++;
154+
if (device_is_master) {
155+
Serial.printf("Received a message from peer " MACSTR "\n", MAC2STR(addr()));
156+
Serial.printf(" Count: %lu\n", msg->count);
157+
Serial.printf(" Random data: %lu\n", msg->data);
158+
last_data.push_back(msg->data);
159+
last_data.erase(last_data.begin());
160+
} else if (peer_is_master) {
161+
Serial.println("Received a message from the master");
162+
Serial.printf(" Average data: %lu\n", msg->data);
163+
}
164+
else {
165+
Serial.printf("Peer " MACSTR " says: %s\n", MAC2STR(addr()), msg->str);
166+
}
167+
}
168+
}
169+
170+
void ESP_NOW_Network_Peer::_onSent(bool success) {
171+
bool broadcast = memcmp(addr(), ESP_NOW.BROADCAST_ADDR, ESP_NOW_ETH_ALEN) == 0;
172+
if (broadcast) {
173+
log_v("Broadcast message reported as sent %s", success ? "successfully" : "unsuccessfully");
174+
}
175+
else {
176+
log_v("Unicast message reported as sent %s to peer " MACSTR, success ? "successfully" : "unsuccessfully", MAC2STR(addr()));
177+
}
178+
}
179+
180+
/* Peers */
181+
182+
std::vector<ESP_NOW_Network_Peer *> peers; // Create a vector to store the peer pointers
183+
ESP_NOW_Network_Peer broadcast_peer(ESP_NOW.BROADCAST_ADDR, 0, NULL); // Register the broadcast peer (no encryption support for the broadcast address)
184+
ESP_NOW_Network_Peer *master_peer = nullptr; // Pointer to peer that is the master
185+
186+
/* Helper functions */
187+
188+
// Function to reboot the device
189+
void fail_reboot() {
190+
Serial.println("Rebooting in 5 seconds...");
191+
delay(5000);
192+
ESP.restart();
193+
}
194+
195+
// Function to check which device has the highest priority
196+
uint32_t check_highest_priority() {
197+
uint32_t highest_priority = 0;
198+
for (auto &peer : peers) {
199+
if (peer->priority > highest_priority) {
200+
highest_priority = peer->priority;
201+
}
202+
}
203+
return std::max(highest_priority, self_priority);
204+
}
205+
206+
// Function to calculate the average of the data received
207+
uint32_t calc_average() {
208+
uint32_t avg = 0;
209+
for (auto &d : last_data) {
210+
avg += d;
211+
}
212+
avg /= last_data.size();
213+
return avg;
214+
}
215+
216+
/* Callbacks */
217+
218+
// Callback called when a new peer is found
219+
void register_new_peer(const esp_now_recv_info_t *info, const uint8_t *data, int len, void *arg) {
220+
esp_now_data_t *msg = (esp_now_data_t *)data;
221+
int priority = msg->priority;
222+
223+
if (priority == self_priority) {
224+
Serial.println("ERROR! Device has the same priority as this device");
225+
fail_reboot();
226+
}
227+
228+
if (current_peer_count < ESPNOW_PEER_COUNT) {
229+
Serial.printf("New peer found: " MACSTR " with priority %d\n", MAC2STR(info->src_addr), priority);
230+
ESP_NOW_Network_Peer *new_peer = new ESP_NOW_Network_Peer(info->src_addr, priority);
231+
if (!new_peer->begin()) {
232+
Serial.println("Failed to create the new peer");
233+
delete new_peer;
234+
return;
235+
}
236+
peers.push_back(new_peer);
237+
if (!peers.back()->begin()) {
238+
Serial.println("Failed to register the new peer");
239+
peers.pop_back();
240+
return;
241+
}
242+
current_peer_count++;
243+
if (current_peer_count == ESPNOW_PEER_COUNT) {
244+
Serial.println("All peers have been found");
245+
Serial.println("Broadcasting the priority 3 more times to ensure that all devices have received it");
246+
}
247+
}
248+
}
249+
250+
/* Main */
251+
252+
void setup() {
253+
uint8_t self_mac[6];
254+
255+
Serial.begin(115200);
256+
while (!Serial) delay(10);
257+
258+
Serial.println("ESP-NOW Network Example");
259+
Serial.println("Wi-Fi parameters:");
260+
Serial.println(" Mode: STA");
261+
Serial.println(" MAC Address: " + WiFi.macAddress());
262+
Serial.printf(" Channel: %d\n", ESPNOW_WIFI_CHANNEL);
263+
264+
// Initialize the Wi-Fi module
265+
WiFi.mode(WIFI_STA);
266+
WiFi.setChannel(ESPNOW_WIFI_CHANNEL);
267+
268+
// Generate yhis device's priority based on the 3 last bytes of the MAC address
269+
WiFi.macAddress(self_mac);
270+
self_priority = self_mac[3] << 16 | self_mac[4] << 8 | self_mac[5];
271+
Serial.printf("This device's priority: %lu\n", self_priority);
272+
273+
// Initialize the ESP-NOW protocol
274+
if (!ESP_NOW.begin((const uint8_t *)ESPNOW_EXAMPLE_PMK)) {
275+
Serial.println("Failed to initialize ESP-NOW");
276+
fail_reboot();
277+
}
278+
279+
if (!broadcast_peer.begin()) {
280+
Serial.println("Failed to initialize broadcast peer");
281+
fail_reboot();
282+
}
283+
284+
// Register the callback to be called when a new peer is found
285+
ESP_NOW.onNewPeer(register_new_peer, NULL);
286+
287+
Serial.println("Setup complete. Broadcasting own priority to find the master...");
288+
memset(&new_msg, 0, sizeof(new_msg));
289+
strncpy(new_msg.str, "Hello!", sizeof(new_msg.str));
290+
new_msg.priority = self_priority;
291+
}
292+
293+
void loop() {
294+
if (!master_decided) {
295+
// Broadcast the priority to find the master
296+
if (!broadcast_peer.send_message((const uint8_t *)&new_msg, sizeof(new_msg))) {
297+
Serial.println("Failed to broadcast message");
298+
}
299+
300+
// Check if all peers have been found
301+
if (current_peer_count == ESPNOW_PEER_COUNT) {
302+
// Transmit the priority 3 more times to ensure that all devices have received it
303+
if (check_count >= 3) {
304+
// Check which device has the highest priority
305+
master_decided = true;
306+
uint32_t highest_priority = check_highest_priority();
307+
if (highest_priority == self_priority) {
308+
device_is_master = true;
309+
Serial.println("This device is the master");
310+
} else {
311+
for (int i = 0; i < ESPNOW_PEER_COUNT; i++) {
312+
if (peers[i]->priority == highest_priority) {
313+
peers[i]->peer_is_master = true;
314+
master_peer = peers[i];
315+
Serial.printf("Peer " MACSTR " is the master with priority %lu\n", MAC2STR(peers[i]->addr()), highest_priority);
316+
break;
317+
}
318+
}
319+
}
320+
Serial.println("The master has been decided");
321+
} else {
322+
Serial.printf("%d...\n", check_count + 1);
323+
check_count++;
324+
}
325+
}
326+
} else {
327+
if (!device_is_master) {
328+
// Send a message to the master
329+
new_msg.count = sent_msg_count + 1;
330+
new_msg.data = random(10000);
331+
if (!master_peer->send_message((const uint8_t *)&new_msg, sizeof(new_msg))) {
332+
Serial.println("Failed to send message to the master");
333+
} else {
334+
Serial.printf("Sent message to the master. Count: %lu, Data: %lu\n", new_msg.count, new_msg.data);
335+
sent_msg_count++;
336+
}
337+
338+
// Check if it is time to report to peers
339+
if (sent_msg_count % REPORT_INTERVAL == 0) {
340+
// Send a message to the peers
341+
for (auto &peer : peers) {
342+
if (!peer->peer_is_master) {
343+
if (!peer->send_message((const uint8_t *)&new_msg, sizeof(new_msg))) {
344+
Serial.printf("Failed to send message to peer " MACSTR "\n", MAC2STR(peer->addr()));
345+
} else {
346+
Serial.printf("Sent message \"%s\" to peer " MACSTR "\n", new_msg.str, MAC2STR(peer->addr()));
347+
}
348+
}
349+
}
350+
}
351+
} else {
352+
// Check if it is time to report to peers
353+
if (recv_msg_count % REPORT_INTERVAL == 0) {
354+
// Report average data to the peers
355+
uint32_t avg = calc_average();
356+
new_msg.count = sent_msg_count + 1;
357+
new_msg.data = avg;
358+
for (auto &peer : peers) {
359+
if (!peer->send_message((const uint8_t *)&new_msg, sizeof(new_msg))) {
360+
Serial.printf("Failed to send message to peer " MACSTR "\n", MAC2STR(peer->addr()));
361+
} else {
362+
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);
363+
sent_msg_count++;
364+
}
365+
}
366+
}
367+
}
368+
}
369+
370+
delay(ESPNOW_SEND_INTERVAL_MS);
371+
}

0 commit comments

Comments
 (0)