@alteriom/painlessmesh 1.6.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +144 -0
- package/LICENSE +674 -0
- package/README.md +434 -0
- package/RELEASE_GUIDE.md +419 -0
- package/docs/DOCUMENTATION_MIGRATION_PLAN.md +176 -0
- package/docs/README.md +71 -0
- package/docs/alteriom/overview.md +508 -0
- package/docs/api/core-api.md +607 -0
- package/docs/architecture/mesh-architecture.md +379 -0
- package/docs/architecture/plugin-system.md +517 -0
- package/docs/getting-started/first-mesh.md +410 -0
- package/docs/getting-started/installation.md +275 -0
- package/docs/getting-started/quickstart.md +158 -0
- package/docs/improvements/README.md +69 -0
- package/docs/troubleshooting/common-issues.md +521 -0
- package/docs/troubleshooting/faq.md +473 -0
- package/docs/tutorials/basic-examples.md +718 -0
- package/docs/wiki/API-Reference.md +246 -0
- package/docs/wiki/Complete-Documentation.md +123 -0
- package/examples/alteriom/README.md +82 -0
- package/examples/alteriom/alteriom.ino +186 -0
- package/examples/alteriom/alteriom_sensor_node.ino +184 -0
- package/examples/alteriom/alteriom_sensor_package.hpp +128 -0
- package/examples/alteriom/improved_sensor_node.ino +246 -0
- package/examples/alteriom/platformio.ini +25 -0
- package/examples/basic/basic.ino +66 -0
- package/examples/basic/platformio.ini +25 -0
- package/examples/bridge/bridge.ino +51 -0
- package/examples/bridge/platformio.ini +25 -0
- package/examples/echoNode/echoNode.ino +33 -0
- package/examples/echoNode/platformio.ini +25 -0
- package/examples/logClient/logClient.ino +109 -0
- package/examples/logClient/platformio.ini +25 -0
- package/examples/logServer/logServer.ino +81 -0
- package/examples/logServer/platformio.ini +25 -0
- package/examples/mqttBridge/mqttBridge.ino +118 -0
- package/examples/mqttBridge/platformio.ini +26 -0
- package/examples/namedMesh/namedMesh.ino +97 -0
- package/examples/namedMesh/platformio.ini +25 -0
- package/examples/otaReceiver/otaReceiver.ino +79 -0
- package/examples/otaReceiver/platformio.ini +25 -0
- package/examples/otaSender/nodemcu32s_connections.JPG +0 -0
- package/examples/otaSender/otaSender.ino +151 -0
- package/examples/otaSender/platformio.ini +25 -0
- package/examples/startHere/platformio.ini +25 -0
- package/examples/startHere/startHere.ino +159 -0
- package/examples/webServer/platformio.ini +27 -0
- package/examples/webServer/webServer.ino +89 -0
- package/keywords.txt +49 -0
- package/library.json +34 -0
- package/library.properties +11 -0
- package/package.json +78 -0
- package/src/AlteriomPainlessMesh.h +98 -0
- package/src/arduino/wifi.hpp +365 -0
- package/src/boost/asynctcp.hpp +279 -0
- package/src/painlessMesh.h +70 -0
- package/src/painlessMeshSTA.cpp +236 -0
- package/src/painlessMeshSTA.h +58 -0
- package/src/painlessTaskOptions.h +4 -0
- package/src/painlessmesh/base64.hpp +111 -0
- package/src/painlessmesh/buffer.hpp +229 -0
- package/src/painlessmesh/callback.hpp +91 -0
- package/src/painlessmesh/configuration.hpp +77 -0
- package/src/painlessmesh/connection.hpp +192 -0
- package/src/painlessmesh/layout.hpp +188 -0
- package/src/painlessmesh/logger.hpp +158 -0
- package/src/painlessmesh/memory.hpp +120 -0
- package/src/painlessmesh/mesh.hpp +560 -0
- package/src/painlessmesh/metrics.hpp +323 -0
- package/src/painlessmesh/ntp.hpp +263 -0
- package/src/painlessmesh/ota.hpp +553 -0
- package/src/painlessmesh/plugin.hpp +188 -0
- package/src/painlessmesh/protocol.hpp +813 -0
- package/src/painlessmesh/router.hpp +322 -0
- package/src/painlessmesh/tcp.hpp +71 -0
- package/src/painlessmesh/validation.hpp +239 -0
- package/src/plugin/performance.hpp +214 -0
- package/src/plugin/remote.hpp +64 -0
- package/src/scheduler.cpp +10 -0
- package/src/wifi.cpp +2 -0
|
@@ -0,0 +1,718 @@
|
|
|
1
|
+
# Basic Examples
|
|
2
|
+
|
|
3
|
+
This tutorial provides a collection of basic painlessMesh examples, from simple message passing to more advanced scenarios. Each example builds upon previous concepts and demonstrates key mesh networking principles.
|
|
4
|
+
|
|
5
|
+
## Example 1: Simple Broadcast
|
|
6
|
+
|
|
7
|
+
The most basic mesh example - nodes that broadcast messages to all other nodes.
|
|
8
|
+
|
|
9
|
+
```cpp
|
|
10
|
+
#include "painlessMesh.h"
|
|
11
|
+
|
|
12
|
+
#define MESH_PREFIX "MyMeshNetwork"
|
|
13
|
+
#define MESH_PASSWORD "secretPassword"
|
|
14
|
+
#define MESH_PORT 5555
|
|
15
|
+
|
|
16
|
+
Scheduler userScheduler;
|
|
17
|
+
painlessMesh mesh;
|
|
18
|
+
|
|
19
|
+
// Send a message every 5 seconds
|
|
20
|
+
Task taskSendMessage(5000, TASK_FOREVER, [](){
|
|
21
|
+
String msg = "Hello from node " + String(mesh.getNodeId());
|
|
22
|
+
mesh.sendBroadcast(msg);
|
|
23
|
+
Serial.printf("Sent: %s\n", msg.c_str());
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
void receivedCallback(uint32_t from, String& msg) {
|
|
27
|
+
Serial.printf("Received from %u: %s\n", from, msg.c_str());
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
void newConnectionCallback(uint32_t nodeId) {
|
|
31
|
+
Serial.printf("New connection: %u\n", nodeId);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
void setup() {
|
|
35
|
+
Serial.begin(115200);
|
|
36
|
+
|
|
37
|
+
mesh.setDebugMsgTypes(ERROR | STARTUP | CONNECTION);
|
|
38
|
+
mesh.init(MESH_PREFIX, MESH_PASSWORD, &userScheduler, MESH_PORT);
|
|
39
|
+
mesh.onReceive(&receivedCallback);
|
|
40
|
+
mesh.onNewConnection(&newConnectionCallback);
|
|
41
|
+
|
|
42
|
+
userScheduler.addTask(taskSendMessage);
|
|
43
|
+
taskSendMessage.enable();
|
|
44
|
+
|
|
45
|
+
Serial.printf("Node ID: %u\n", mesh.getNodeId());
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
void loop() {
|
|
49
|
+
mesh.update();
|
|
50
|
+
}
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
**Key Concepts:**
|
|
54
|
+
- All nodes broadcast the same message type
|
|
55
|
+
- Every node receives every broadcast message
|
|
56
|
+
- Node ID uniquely identifies each device
|
|
57
|
+
|
|
58
|
+
## Example 2: Point-to-Point Messaging
|
|
59
|
+
|
|
60
|
+
Sending messages to specific nodes instead of broadcasting.
|
|
61
|
+
|
|
62
|
+
```cpp
|
|
63
|
+
#include "painlessMesh.h"
|
|
64
|
+
|
|
65
|
+
#define MESH_PREFIX "MyMeshNetwork"
|
|
66
|
+
#define MESH_PASSWORD "secretPassword"
|
|
67
|
+
#define MESH_PORT 5555
|
|
68
|
+
|
|
69
|
+
Scheduler userScheduler;
|
|
70
|
+
painlessMesh mesh;
|
|
71
|
+
|
|
72
|
+
uint32_t targetNode = 0; // Will be set to first discovered node
|
|
73
|
+
|
|
74
|
+
// Send targeted message every 10 seconds
|
|
75
|
+
Task taskSendTargeted(10000, TASK_FOREVER, [](){
|
|
76
|
+
if (targetNode != 0) {
|
|
77
|
+
String msg = "Direct message from " + String(mesh.getNodeId());
|
|
78
|
+
bool sent = mesh.sendSingle(targetNode, msg);
|
|
79
|
+
Serial.printf("Sent to %u: %s [%s]\n", targetNode, msg.c_str(),
|
|
80
|
+
sent ? "OK" : "FAILED");
|
|
81
|
+
}
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
void receivedCallback(uint32_t from, String& msg) {
|
|
85
|
+
Serial.printf("Received from %u: %s\n", from, msg.c_str());
|
|
86
|
+
|
|
87
|
+
// Echo back to sender
|
|
88
|
+
String response = "Echo: " + msg;
|
|
89
|
+
mesh.sendSingle(from, response);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
void newConnectionCallback(uint32_t nodeId) {
|
|
93
|
+
Serial.printf("New connection: %u\n", nodeId);
|
|
94
|
+
|
|
95
|
+
// Set first connected node as target
|
|
96
|
+
if (targetNode == 0) {
|
|
97
|
+
targetNode = nodeId;
|
|
98
|
+
Serial.printf("Set target node: %u\n", targetNode);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
void droppedConnectionCallback(uint32_t nodeId) {
|
|
103
|
+
Serial.printf("Dropped connection: %u\n", nodeId);
|
|
104
|
+
|
|
105
|
+
// Clear target if it disconnected
|
|
106
|
+
if (targetNode == nodeId) {
|
|
107
|
+
targetNode = 0;
|
|
108
|
+
|
|
109
|
+
// Find new target from remaining connections
|
|
110
|
+
auto nodes = mesh.getNodeList();
|
|
111
|
+
if (!nodes.empty()) {
|
|
112
|
+
targetNode = *nodes.begin();
|
|
113
|
+
Serial.printf("New target node: %u\n", targetNode);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
void setup() {
|
|
119
|
+
Serial.begin(115200);
|
|
120
|
+
|
|
121
|
+
mesh.setDebugMsgTypes(ERROR | STARTUP | CONNECTION);
|
|
122
|
+
mesh.init(MESH_PREFIX, MESH_PASSWORD, &userScheduler, MESH_PORT);
|
|
123
|
+
mesh.onReceive(&receivedCallback);
|
|
124
|
+
mesh.onNewConnection(&newConnectionCallback);
|
|
125
|
+
mesh.onDroppedConnection(&droppedConnectionCallback);
|
|
126
|
+
|
|
127
|
+
userScheduler.addTask(taskSendTargeted);
|
|
128
|
+
taskSendTargeted.enable();
|
|
129
|
+
|
|
130
|
+
Serial.printf("Node ID: %u\n", mesh.getNodeId());
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
void loop() {
|
|
134
|
+
mesh.update();
|
|
135
|
+
}
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
**Key Concepts:**
|
|
139
|
+
- Use `sendSingle()` for point-to-point messages
|
|
140
|
+
- Track connected nodes using callbacks
|
|
141
|
+
- Handle node disconnections gracefully
|
|
142
|
+
|
|
143
|
+
## Example 3: JSON Message Format
|
|
144
|
+
|
|
145
|
+
Structured data exchange using JSON messages.
|
|
146
|
+
|
|
147
|
+
```cpp
|
|
148
|
+
#include "painlessMesh.h"
|
|
149
|
+
#include "ArduinoJson.h"
|
|
150
|
+
|
|
151
|
+
#define MESH_PREFIX "MyMeshNetwork"
|
|
152
|
+
#define MESH_PASSWORD "secretPassword"
|
|
153
|
+
#define MESH_PORT 5555
|
|
154
|
+
|
|
155
|
+
Scheduler userScheduler;
|
|
156
|
+
painlessMesh mesh;
|
|
157
|
+
|
|
158
|
+
int messageCounter = 0;
|
|
159
|
+
|
|
160
|
+
// Send structured data every 15 seconds
|
|
161
|
+
Task taskSendData(15000, TASK_FOREVER, [](){
|
|
162
|
+
DynamicJsonDocument doc(200);
|
|
163
|
+
|
|
164
|
+
doc["type"] = "sensor_data";
|
|
165
|
+
doc["nodeId"] = mesh.getNodeId();
|
|
166
|
+
doc["counter"] = messageCounter++;
|
|
167
|
+
doc["temperature"] = random(150, 350) / 10.0; // 15.0 to 35.0
|
|
168
|
+
doc["humidity"] = random(300, 800) / 10.0; // 30.0 to 80.0
|
|
169
|
+
doc["timestamp"] = mesh.getNodeTime();
|
|
170
|
+
|
|
171
|
+
String message;
|
|
172
|
+
serializeJson(doc, message);
|
|
173
|
+
|
|
174
|
+
mesh.sendBroadcast(message);
|
|
175
|
+
Serial.printf("Sent: %s\n", message.c_str());
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
void receivedCallback(uint32_t from, String& msg) {
|
|
179
|
+
Serial.printf("Received from %u: %s\n", from, msg.c_str());
|
|
180
|
+
|
|
181
|
+
// Parse JSON message
|
|
182
|
+
DynamicJsonDocument doc(512);
|
|
183
|
+
DeserializationError error = deserializeJson(doc, msg);
|
|
184
|
+
|
|
185
|
+
if (error) {
|
|
186
|
+
Serial.printf("JSON parsing failed: %s\n", error.c_str());
|
|
187
|
+
return;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
// Process different message types
|
|
191
|
+
String msgType = doc["type"];
|
|
192
|
+
|
|
193
|
+
if (msgType == "sensor_data") {
|
|
194
|
+
uint32_t nodeId = doc["nodeId"];
|
|
195
|
+
float temperature = doc["temperature"];
|
|
196
|
+
float humidity = doc["humidity"];
|
|
197
|
+
uint32_t timestamp = doc["timestamp"];
|
|
198
|
+
|
|
199
|
+
Serial.printf("Sensor data from node %u: T=%.1f°C, H=%.1f%% (age: %u µs)\n",
|
|
200
|
+
nodeId, temperature, humidity,
|
|
201
|
+
mesh.getNodeTime() - timestamp);
|
|
202
|
+
|
|
203
|
+
// Respond with acknowledgment
|
|
204
|
+
sendAcknowledgment(from, doc["counter"]);
|
|
205
|
+
}
|
|
206
|
+
else if (msgType == "acknowledgment") {
|
|
207
|
+
int counter = doc["counter"];
|
|
208
|
+
Serial.printf("Received acknowledgment from %u for message %d\n", from, counter);
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
void sendAcknowledgment(uint32_t targetNode, int counter) {
|
|
213
|
+
DynamicJsonDocument doc(100);
|
|
214
|
+
|
|
215
|
+
doc["type"] = "acknowledgment";
|
|
216
|
+
doc["nodeId"] = mesh.getNodeId();
|
|
217
|
+
doc["counter"] = counter;
|
|
218
|
+
|
|
219
|
+
String message;
|
|
220
|
+
serializeJson(doc, message);
|
|
221
|
+
|
|
222
|
+
mesh.sendSingle(targetNode, message);
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
void setup() {
|
|
226
|
+
Serial.begin(115200);
|
|
227
|
+
|
|
228
|
+
mesh.setDebugMsgTypes(ERROR | STARTUP | CONNECTION);
|
|
229
|
+
mesh.init(MESH_PREFIX, MESH_PASSWORD, &userScheduler, MESH_PORT);
|
|
230
|
+
mesh.onReceive(&receivedCallback);
|
|
231
|
+
|
|
232
|
+
userScheduler.addTask(taskSendData);
|
|
233
|
+
taskSendData.enable();
|
|
234
|
+
|
|
235
|
+
Serial.printf("Node ID: %u\n", mesh.getNodeId());
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
void loop() {
|
|
239
|
+
mesh.update();
|
|
240
|
+
}
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
**Key Concepts:**
|
|
244
|
+
- Use ArduinoJson for structured data
|
|
245
|
+
- Handle JSON parsing errors gracefully
|
|
246
|
+
- Implement message acknowledgments
|
|
247
|
+
- Add timestamps for data age validation
|
|
248
|
+
|
|
249
|
+
## Example 4: Multi-Function Node
|
|
250
|
+
|
|
251
|
+
A node that handles multiple types of messages and functions.
|
|
252
|
+
|
|
253
|
+
```cpp
|
|
254
|
+
#include "painlessMesh.h"
|
|
255
|
+
#include "ArduinoJson.h"
|
|
256
|
+
|
|
257
|
+
#define MESH_PREFIX "MyMeshNetwork"
|
|
258
|
+
#define MESH_PASSWORD "secretPassword"
|
|
259
|
+
#define MESH_PORT 5555
|
|
260
|
+
|
|
261
|
+
// LED pin (built-in LED on most ESP boards)
|
|
262
|
+
#define LED_PIN 2
|
|
263
|
+
|
|
264
|
+
Scheduler userScheduler;
|
|
265
|
+
painlessMesh mesh;
|
|
266
|
+
|
|
267
|
+
bool ledState = false;
|
|
268
|
+
int statusCounter = 0;
|
|
269
|
+
|
|
270
|
+
// Send status every 30 seconds
|
|
271
|
+
Task taskSendStatus(30000, TASK_FOREVER, [](){
|
|
272
|
+
DynamicJsonDocument doc(300);
|
|
273
|
+
|
|
274
|
+
doc["type"] = "status";
|
|
275
|
+
doc["nodeId"] = mesh.getNodeId();
|
|
276
|
+
doc["counter"] = statusCounter++;
|
|
277
|
+
doc["uptime"] = millis() / 1000;
|
|
278
|
+
doc["freeHeap"] = ESP.getFreeHeap();
|
|
279
|
+
doc["connections"] = mesh.getNodeList().size();
|
|
280
|
+
doc["ledState"] = ledState;
|
|
281
|
+
doc["timestamp"] = mesh.getNodeTime();
|
|
282
|
+
|
|
283
|
+
String message;
|
|
284
|
+
serializeJson(doc, message);
|
|
285
|
+
|
|
286
|
+
mesh.sendBroadcast(message);
|
|
287
|
+
Serial.printf("Status sent: uptime=%ds, heap=%d, connections=%d\n",
|
|
288
|
+
(int)(millis()/1000), ESP.getFreeHeap(), mesh.getNodeList().size());
|
|
289
|
+
});
|
|
290
|
+
|
|
291
|
+
// Blink LED every 2 seconds
|
|
292
|
+
Task taskBlinkLED(2000, TASK_FOREVER, [](){
|
|
293
|
+
ledState = !ledState;
|
|
294
|
+
digitalWrite(LED_PIN, ledState ? HIGH : LOW);
|
|
295
|
+
});
|
|
296
|
+
|
|
297
|
+
void receivedCallback(uint32_t from, String& msg) {
|
|
298
|
+
Serial.printf("Received from %u: %s\n", from, msg.c_str());
|
|
299
|
+
|
|
300
|
+
DynamicJsonDocument doc(512);
|
|
301
|
+
DeserializationError error = deserializeJson(doc, msg);
|
|
302
|
+
|
|
303
|
+
if (error) return;
|
|
304
|
+
|
|
305
|
+
String msgType = doc["type"];
|
|
306
|
+
|
|
307
|
+
if (msgType == "status") {
|
|
308
|
+
handleStatusMessage(from, doc);
|
|
309
|
+
}
|
|
310
|
+
else if (msgType == "command") {
|
|
311
|
+
handleCommandMessage(from, doc);
|
|
312
|
+
}
|
|
313
|
+
else if (msgType == "ping") {
|
|
314
|
+
handlePingMessage(from, doc);
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
void handleStatusMessage(uint32_t from, JsonDocument& doc) {
|
|
319
|
+
int uptime = doc["uptime"];
|
|
320
|
+
int freeHeap = doc["freeHeap"];
|
|
321
|
+
int connections = doc["connections"];
|
|
322
|
+
bool remoteLedState = doc["ledState"];
|
|
323
|
+
|
|
324
|
+
Serial.printf("Status from %u: uptime=%ds, heap=%d, LED=%s\n",
|
|
325
|
+
from, uptime, freeHeap, remoteLedState ? "ON" : "OFF");
|
|
326
|
+
|
|
327
|
+
// Check if remote node needs help
|
|
328
|
+
if (freeHeap < 10000) {
|
|
329
|
+
Serial.printf("Warning: Node %u has low memory!\n", from);
|
|
330
|
+
sendHelpOffer(from);
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
void handleCommandMessage(uint32_t from, JsonDocument& doc) {
|
|
335
|
+
String command = doc["command"];
|
|
336
|
+
|
|
337
|
+
Serial.printf("Command from %u: %s\n", from, command.c_str());
|
|
338
|
+
|
|
339
|
+
if (command == "led_on") {
|
|
340
|
+
ledState = true;
|
|
341
|
+
digitalWrite(LED_PIN, HIGH);
|
|
342
|
+
sendCommandResponse(from, "led_on", "OK");
|
|
343
|
+
}
|
|
344
|
+
else if (command == "led_off") {
|
|
345
|
+
ledState = false;
|
|
346
|
+
digitalWrite(LED_PIN, LOW);
|
|
347
|
+
sendCommandResponse(from, "led_off", "OK");
|
|
348
|
+
}
|
|
349
|
+
else if (command == "get_status") {
|
|
350
|
+
// Force immediate status send
|
|
351
|
+
taskSendStatus.forceNextIteration();
|
|
352
|
+
}
|
|
353
|
+
else {
|
|
354
|
+
sendCommandResponse(from, command, "UNKNOWN_COMMAND");
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
void handlePingMessage(uint32_t from, JsonDocument& doc) {
|
|
359
|
+
uint32_t pingTime = doc["timestamp"];
|
|
360
|
+
|
|
361
|
+
// Send pong response
|
|
362
|
+
DynamicJsonDocument response(100);
|
|
363
|
+
response["type"] = "pong";
|
|
364
|
+
response["nodeId"] = mesh.getNodeId();
|
|
365
|
+
response["pingTime"] = pingTime;
|
|
366
|
+
response["pongTime"] = mesh.getNodeTime();
|
|
367
|
+
|
|
368
|
+
String message;
|
|
369
|
+
serializeJson(response, message);
|
|
370
|
+
mesh.sendSingle(from, message);
|
|
371
|
+
|
|
372
|
+
Serial.printf("Responded to ping from %u\n", from);
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
void sendCommandResponse(uint32_t targetNode, String command, String result) {
|
|
376
|
+
DynamicJsonDocument doc(150);
|
|
377
|
+
|
|
378
|
+
doc["type"] = "command_response";
|
|
379
|
+
doc["nodeId"] = mesh.getNodeId();
|
|
380
|
+
doc["command"] = command;
|
|
381
|
+
doc["result"] = result;
|
|
382
|
+
doc["timestamp"] = mesh.getNodeTime();
|
|
383
|
+
|
|
384
|
+
String message;
|
|
385
|
+
serializeJson(doc, message);
|
|
386
|
+
mesh.sendSingle(targetNode, message);
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
void sendHelpOffer(uint32_t targetNode) {
|
|
390
|
+
DynamicJsonDocument doc(100);
|
|
391
|
+
|
|
392
|
+
doc["type"] = "help_offer";
|
|
393
|
+
doc["nodeId"] = mesh.getNodeId();
|
|
394
|
+
doc["message"] = "I can help with processing";
|
|
395
|
+
|
|
396
|
+
String message;
|
|
397
|
+
serializeJson(doc, message);
|
|
398
|
+
mesh.sendSingle(targetNode, message);
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
void newConnectionCallback(uint32_t nodeId) {
|
|
402
|
+
Serial.printf("New connection: %u (total: %d)\n", nodeId, mesh.getNodeList().size());
|
|
403
|
+
|
|
404
|
+
// Send welcome message to new node
|
|
405
|
+
DynamicJsonDocument doc(100);
|
|
406
|
+
doc["type"] = "welcome";
|
|
407
|
+
doc["nodeId"] = mesh.getNodeId();
|
|
408
|
+
doc["message"] = "Welcome to the mesh!";
|
|
409
|
+
|
|
410
|
+
String message;
|
|
411
|
+
serializeJson(doc, message);
|
|
412
|
+
mesh.sendSingle(nodeId, message);
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
void droppedConnectionCallback(uint32_t nodeId) {
|
|
416
|
+
Serial.printf("Lost connection: %u (remaining: %d)\n",
|
|
417
|
+
nodeId, mesh.getNodeList().size());
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
void setup() {
|
|
421
|
+
Serial.begin(115200);
|
|
422
|
+
|
|
423
|
+
pinMode(LED_PIN, OUTPUT);
|
|
424
|
+
digitalWrite(LED_PIN, LOW);
|
|
425
|
+
|
|
426
|
+
mesh.setDebugMsgTypes(ERROR | STARTUP | CONNECTION);
|
|
427
|
+
mesh.init(MESH_PREFIX, MESH_PASSWORD, &userScheduler, MESH_PORT);
|
|
428
|
+
mesh.onReceive(&receivedCallback);
|
|
429
|
+
mesh.onNewConnection(&newConnectionCallback);
|
|
430
|
+
mesh.onDroppedConnection(&droppedConnectionCallback);
|
|
431
|
+
|
|
432
|
+
userScheduler.addTask(taskSendStatus);
|
|
433
|
+
userScheduler.addTask(taskBlinkLED);
|
|
434
|
+
taskSendStatus.enable();
|
|
435
|
+
taskBlinkLED.enable();
|
|
436
|
+
|
|
437
|
+
Serial.printf("Multi-function node started. ID: %u\n", mesh.getNodeId());
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
void loop() {
|
|
441
|
+
mesh.update();
|
|
442
|
+
}
|
|
443
|
+
```
|
|
444
|
+
|
|
445
|
+
**Key Concepts:**
|
|
446
|
+
- Handle multiple message types in one node
|
|
447
|
+
- Implement command/response patterns
|
|
448
|
+
- Monitor network health and offer assistance
|
|
449
|
+
- Combine mesh communication with hardware control
|
|
450
|
+
|
|
451
|
+
## Example 5: Network Discovery and Mapping
|
|
452
|
+
|
|
453
|
+
Discover and map the entire mesh network topology.
|
|
454
|
+
|
|
455
|
+
```cpp
|
|
456
|
+
#include "painlessMesh.h"
|
|
457
|
+
#include "ArduinoJson.h"
|
|
458
|
+
|
|
459
|
+
#define MESH_PREFIX "MyMeshNetwork"
|
|
460
|
+
#define MESH_PASSWORD "secretPassword"
|
|
461
|
+
#define MESH_PORT 5555
|
|
462
|
+
|
|
463
|
+
Scheduler userScheduler;
|
|
464
|
+
painlessMesh mesh;
|
|
465
|
+
|
|
466
|
+
std::map<uint32_t, std::set<uint32_t>> networkTopology;
|
|
467
|
+
std::map<uint32_t, String> nodeInfo;
|
|
468
|
+
|
|
469
|
+
// Request topology information every 60 seconds
|
|
470
|
+
Task taskDiscoverTopology(60000, TASK_FOREVER, [](){
|
|
471
|
+
DynamicJsonDocument doc(100);
|
|
472
|
+
|
|
473
|
+
doc["type"] = "topology_request";
|
|
474
|
+
doc["requesterId"] = mesh.getNodeId();
|
|
475
|
+
doc["timestamp"] = mesh.getNodeTime();
|
|
476
|
+
|
|
477
|
+
String message;
|
|
478
|
+
serializeJson(doc, message);
|
|
479
|
+
|
|
480
|
+
mesh.sendBroadcast(message);
|
|
481
|
+
Serial.println("Sent topology discovery request");
|
|
482
|
+
});
|
|
483
|
+
|
|
484
|
+
// Print network map every 90 seconds
|
|
485
|
+
Task taskPrintNetworkMap(90000, TASK_FOREVER, [](){
|
|
486
|
+
printNetworkTopology();
|
|
487
|
+
});
|
|
488
|
+
|
|
489
|
+
void receivedCallback(uint32_t from, String& msg) {
|
|
490
|
+
DynamicJsonDocument doc(1024);
|
|
491
|
+
DeserializationError error = deserializeJson(doc, msg);
|
|
492
|
+
|
|
493
|
+
if (error) return;
|
|
494
|
+
|
|
495
|
+
String msgType = doc["type"];
|
|
496
|
+
|
|
497
|
+
if (msgType == "topology_request") {
|
|
498
|
+
sendTopologyResponse(from);
|
|
499
|
+
}
|
|
500
|
+
else if (msgType == "topology_response") {
|
|
501
|
+
processTopologyResponse(from, doc);
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
void sendTopologyResponse(uint32_t requester) {
|
|
506
|
+
DynamicJsonDocument doc(512);
|
|
507
|
+
|
|
508
|
+
doc["type"] = "topology_response";
|
|
509
|
+
doc["nodeId"] = mesh.getNodeId();
|
|
510
|
+
doc["requester"] = requester;
|
|
511
|
+
|
|
512
|
+
// Include my connections
|
|
513
|
+
JsonArray connections = doc.createNestedArray("connections");
|
|
514
|
+
auto nodeList = mesh.getNodeList();
|
|
515
|
+
for (uint32_t nodeId : nodeList) {
|
|
516
|
+
connections.add(nodeId);
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
// Include node information
|
|
520
|
+
doc["uptime"] = millis() / 1000;
|
|
521
|
+
doc["freeHeap"] = ESP.getFreeHeap();
|
|
522
|
+
doc["version"] = "1.0";
|
|
523
|
+
|
|
524
|
+
String message;
|
|
525
|
+
serializeJson(doc, message);
|
|
526
|
+
|
|
527
|
+
mesh.sendSingle(requester, message);
|
|
528
|
+
Serial.printf("Sent topology response to %u\n", requester);
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
void processTopologyResponse(uint32_t from, JsonDocument& doc) {
|
|
532
|
+
Serial.printf("Received topology from %u\n", from);
|
|
533
|
+
|
|
534
|
+
// Store node information
|
|
535
|
+
int uptime = doc["uptime"];
|
|
536
|
+
int freeHeap = doc["freeHeap"];
|
|
537
|
+
String version = doc["version"];
|
|
538
|
+
|
|
539
|
+
char info[100];
|
|
540
|
+
snprintf(info, sizeof(info), "uptime:%ds heap:%d ver:%s",
|
|
541
|
+
uptime, freeHeap, version.c_str());
|
|
542
|
+
nodeInfo[from] = String(info);
|
|
543
|
+
|
|
544
|
+
// Store connections
|
|
545
|
+
networkTopology[from].clear();
|
|
546
|
+
JsonArray connections = doc["connections"];
|
|
547
|
+
for (JsonVariant connection : connections) {
|
|
548
|
+
uint32_t connectedNode = connection.as<uint32_t>();
|
|
549
|
+
networkTopology[from].insert(connectedNode);
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
Serial.printf("Node %u has %d connections\n", from, connections.size());
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
void printNetworkTopology() {
|
|
556
|
+
Serial.println("=== Network Topology ===");
|
|
557
|
+
|
|
558
|
+
if (networkTopology.empty()) {
|
|
559
|
+
Serial.println("No topology data available");
|
|
560
|
+
return;
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
// Print each node and its connections
|
|
564
|
+
for (auto& [nodeId, connections] : networkTopology) {
|
|
565
|
+
Serial.printf("Node %u (%s)\n", nodeId,
|
|
566
|
+
nodeInfo.count(nodeId) ? nodeInfo[nodeId].c_str() : "no info");
|
|
567
|
+
|
|
568
|
+
if (connections.empty()) {
|
|
569
|
+
Serial.println(" No connections");
|
|
570
|
+
} else {
|
|
571
|
+
Serial.print(" Connected to: ");
|
|
572
|
+
for (uint32_t connectedNode : connections) {
|
|
573
|
+
Serial.printf("%u ", connectedNode);
|
|
574
|
+
}
|
|
575
|
+
Serial.println();
|
|
576
|
+
}
|
|
577
|
+
}
|
|
578
|
+
|
|
579
|
+
// Calculate network statistics
|
|
580
|
+
int totalNodes = networkTopology.size() + 1; // +1 for this node
|
|
581
|
+
int totalConnections = 0;
|
|
582
|
+
for (auto& [nodeId, connections] : networkTopology) {
|
|
583
|
+
totalConnections += connections.size();
|
|
584
|
+
}
|
|
585
|
+
totalConnections += mesh.getNodeList().size(); // This node's connections
|
|
586
|
+
totalConnections /= 2; // Each connection counted twice
|
|
587
|
+
|
|
588
|
+
Serial.printf("Network Summary: %d nodes, %d connections\n",
|
|
589
|
+
totalNodes, totalConnections);
|
|
590
|
+
Serial.println("========================");
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
void newConnectionCallback(uint32_t nodeId) {
|
|
594
|
+
Serial.printf("New connection: %u\n", nodeId);
|
|
595
|
+
|
|
596
|
+
// Immediate topology request for new node
|
|
597
|
+
DynamicJsonDocument doc(100);
|
|
598
|
+
doc["type"] = "topology_request";
|
|
599
|
+
doc["requesterId"] = mesh.getNodeId();
|
|
600
|
+
doc["timestamp"] = mesh.getNodeTime();
|
|
601
|
+
|
|
602
|
+
String message;
|
|
603
|
+
serializeJson(doc, message);
|
|
604
|
+
mesh.sendSingle(nodeId, message);
|
|
605
|
+
}
|
|
606
|
+
|
|
607
|
+
void droppedConnectionCallback(uint32_t nodeId) {
|
|
608
|
+
Serial.printf("Lost connection: %u\n", nodeId);
|
|
609
|
+
|
|
610
|
+
// Remove from topology map
|
|
611
|
+
networkTopology.erase(nodeId);
|
|
612
|
+
nodeInfo.erase(nodeId);
|
|
613
|
+
|
|
614
|
+
// Also remove from other nodes' connection lists
|
|
615
|
+
for (auto& [otherNode, connections] : networkTopology) {
|
|
616
|
+
connections.erase(nodeId);
|
|
617
|
+
}
|
|
618
|
+
}
|
|
619
|
+
|
|
620
|
+
void setup() {
|
|
621
|
+
Serial.begin(115200);
|
|
622
|
+
|
|
623
|
+
mesh.setDebugMsgTypes(ERROR | STARTUP | CONNECTION);
|
|
624
|
+
mesh.init(MESH_PREFIX, MESH_PASSWORD, &userScheduler, MESH_PORT);
|
|
625
|
+
mesh.onReceive(&receivedCallback);
|
|
626
|
+
mesh.onNewConnection(&newConnectionCallback);
|
|
627
|
+
mesh.onDroppedConnection(&droppedConnectionCallback);
|
|
628
|
+
|
|
629
|
+
userScheduler.addTask(taskDiscoverTopology);
|
|
630
|
+
userScheduler.addTask(taskPrintNetworkMap);
|
|
631
|
+
taskDiscoverTopology.enable();
|
|
632
|
+
taskPrintNetworkMap.enable();
|
|
633
|
+
|
|
634
|
+
Serial.printf("Network discovery node started. ID: %u\n", mesh.getNodeId());
|
|
635
|
+
}
|
|
636
|
+
|
|
637
|
+
void loop() {
|
|
638
|
+
mesh.update();
|
|
639
|
+
}
|
|
640
|
+
```
|
|
641
|
+
|
|
642
|
+
**Key Concepts:**
|
|
643
|
+
- Actively discover network topology
|
|
644
|
+
- Store and analyze network structure
|
|
645
|
+
- Track node information and capabilities
|
|
646
|
+
- Visualize mesh connectivity
|
|
647
|
+
|
|
648
|
+
## Common Patterns and Best Practices
|
|
649
|
+
|
|
650
|
+
### Error Handling
|
|
651
|
+
```cpp
|
|
652
|
+
void receivedCallback(uint32_t from, String& msg) {
|
|
653
|
+
// Always validate JSON parsing
|
|
654
|
+
DynamicJsonDocument doc(512);
|
|
655
|
+
DeserializationError error = deserializeJson(doc, msg);
|
|
656
|
+
|
|
657
|
+
if (error) {
|
|
658
|
+
Serial.printf("JSON error from %u: %s\n", from, error.c_str());
|
|
659
|
+
return; // Don't process invalid JSON
|
|
660
|
+
}
|
|
661
|
+
|
|
662
|
+
// Validate required fields exist
|
|
663
|
+
if (!doc.containsKey("type")) {
|
|
664
|
+
Serial.printf("Missing 'type' field from %u\n", from);
|
|
665
|
+
return;
|
|
666
|
+
}
|
|
667
|
+
|
|
668
|
+
// Process message...
|
|
669
|
+
}
|
|
670
|
+
```
|
|
671
|
+
|
|
672
|
+
### Memory Management
|
|
673
|
+
```cpp
|
|
674
|
+
void sendLargeMessage() {
|
|
675
|
+
// Use appropriate document size
|
|
676
|
+
DynamicJsonDocument doc(1024); // Size for your data
|
|
677
|
+
|
|
678
|
+
// ... populate document
|
|
679
|
+
|
|
680
|
+
String message;
|
|
681
|
+
size_t messageSize = serializeJson(doc, message);
|
|
682
|
+
|
|
683
|
+
if (message.length() > 1000) {
|
|
684
|
+
Serial.println("Warning: Large message may cause issues");
|
|
685
|
+
}
|
|
686
|
+
|
|
687
|
+
mesh.sendBroadcast(message);
|
|
688
|
+
}
|
|
689
|
+
```
|
|
690
|
+
|
|
691
|
+
### Task Management
|
|
692
|
+
```cpp
|
|
693
|
+
void setup() {
|
|
694
|
+
// Create tasks but don't enable immediately
|
|
695
|
+
Task task1(5000, TASK_FOREVER, &function1);
|
|
696
|
+
Task task2(10000, TASK_FOREVER, &function2);
|
|
697
|
+
|
|
698
|
+
userScheduler.addTask(task1);
|
|
699
|
+
userScheduler.addTask(task2);
|
|
700
|
+
|
|
701
|
+
// Enable tasks after mesh is initialized
|
|
702
|
+
mesh.init(MESH_PREFIX, MESH_PASSWORD, &userScheduler, MESH_PORT);
|
|
703
|
+
|
|
704
|
+
task1.enable();
|
|
705
|
+
task2.enable();
|
|
706
|
+
}
|
|
707
|
+
```
|
|
708
|
+
|
|
709
|
+
## Next Steps
|
|
710
|
+
|
|
711
|
+
These examples provide a foundation for building more complex mesh applications. Consider exploring:
|
|
712
|
+
|
|
713
|
+
- [Custom Packages Tutorial](custom-packages.md) for type-safe messaging
|
|
714
|
+
- [Sensor Networks Tutorial](sensor-networks.md) for IoT applications
|
|
715
|
+
- [Alteriom Extensions](../alteriom/overview.md) for production-ready packages
|
|
716
|
+
- [Performance Optimization](../advanced/performance.md) for scaling up
|
|
717
|
+
|
|
718
|
+
Each example can be extended with additional features like data persistence, external connectivity, or advanced routing strategies based on your specific needs.
|