@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.
Files changed (80) hide show
  1. package/CHANGELOG.md +144 -0
  2. package/LICENSE +674 -0
  3. package/README.md +434 -0
  4. package/RELEASE_GUIDE.md +419 -0
  5. package/docs/DOCUMENTATION_MIGRATION_PLAN.md +176 -0
  6. package/docs/README.md +71 -0
  7. package/docs/alteriom/overview.md +508 -0
  8. package/docs/api/core-api.md +607 -0
  9. package/docs/architecture/mesh-architecture.md +379 -0
  10. package/docs/architecture/plugin-system.md +517 -0
  11. package/docs/getting-started/first-mesh.md +410 -0
  12. package/docs/getting-started/installation.md +275 -0
  13. package/docs/getting-started/quickstart.md +158 -0
  14. package/docs/improvements/README.md +69 -0
  15. package/docs/troubleshooting/common-issues.md +521 -0
  16. package/docs/troubleshooting/faq.md +473 -0
  17. package/docs/tutorials/basic-examples.md +718 -0
  18. package/docs/wiki/API-Reference.md +246 -0
  19. package/docs/wiki/Complete-Documentation.md +123 -0
  20. package/examples/alteriom/README.md +82 -0
  21. package/examples/alteriom/alteriom.ino +186 -0
  22. package/examples/alteriom/alteriom_sensor_node.ino +184 -0
  23. package/examples/alteriom/alteriom_sensor_package.hpp +128 -0
  24. package/examples/alteriom/improved_sensor_node.ino +246 -0
  25. package/examples/alteriom/platformio.ini +25 -0
  26. package/examples/basic/basic.ino +66 -0
  27. package/examples/basic/platformio.ini +25 -0
  28. package/examples/bridge/bridge.ino +51 -0
  29. package/examples/bridge/platformio.ini +25 -0
  30. package/examples/echoNode/echoNode.ino +33 -0
  31. package/examples/echoNode/platformio.ini +25 -0
  32. package/examples/logClient/logClient.ino +109 -0
  33. package/examples/logClient/platformio.ini +25 -0
  34. package/examples/logServer/logServer.ino +81 -0
  35. package/examples/logServer/platformio.ini +25 -0
  36. package/examples/mqttBridge/mqttBridge.ino +118 -0
  37. package/examples/mqttBridge/platformio.ini +26 -0
  38. package/examples/namedMesh/namedMesh.ino +97 -0
  39. package/examples/namedMesh/platformio.ini +25 -0
  40. package/examples/otaReceiver/otaReceiver.ino +79 -0
  41. package/examples/otaReceiver/platformio.ini +25 -0
  42. package/examples/otaSender/nodemcu32s_connections.JPG +0 -0
  43. package/examples/otaSender/otaSender.ino +151 -0
  44. package/examples/otaSender/platformio.ini +25 -0
  45. package/examples/startHere/platformio.ini +25 -0
  46. package/examples/startHere/startHere.ino +159 -0
  47. package/examples/webServer/platformio.ini +27 -0
  48. package/examples/webServer/webServer.ino +89 -0
  49. package/keywords.txt +49 -0
  50. package/library.json +34 -0
  51. package/library.properties +11 -0
  52. package/package.json +78 -0
  53. package/src/AlteriomPainlessMesh.h +98 -0
  54. package/src/arduino/wifi.hpp +365 -0
  55. package/src/boost/asynctcp.hpp +279 -0
  56. package/src/painlessMesh.h +70 -0
  57. package/src/painlessMeshSTA.cpp +236 -0
  58. package/src/painlessMeshSTA.h +58 -0
  59. package/src/painlessTaskOptions.h +4 -0
  60. package/src/painlessmesh/base64.hpp +111 -0
  61. package/src/painlessmesh/buffer.hpp +229 -0
  62. package/src/painlessmesh/callback.hpp +91 -0
  63. package/src/painlessmesh/configuration.hpp +77 -0
  64. package/src/painlessmesh/connection.hpp +192 -0
  65. package/src/painlessmesh/layout.hpp +188 -0
  66. package/src/painlessmesh/logger.hpp +158 -0
  67. package/src/painlessmesh/memory.hpp +120 -0
  68. package/src/painlessmesh/mesh.hpp +560 -0
  69. package/src/painlessmesh/metrics.hpp +323 -0
  70. package/src/painlessmesh/ntp.hpp +263 -0
  71. package/src/painlessmesh/ota.hpp +553 -0
  72. package/src/painlessmesh/plugin.hpp +188 -0
  73. package/src/painlessmesh/protocol.hpp +813 -0
  74. package/src/painlessmesh/router.hpp +322 -0
  75. package/src/painlessmesh/tcp.hpp +71 -0
  76. package/src/painlessmesh/validation.hpp +239 -0
  77. package/src/plugin/performance.hpp +214 -0
  78. package/src/plugin/remote.hpp +64 -0
  79. package/src/scheduler.cpp +10 -0
  80. 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.