@alteriom/painlessmesh 1.6.1 → 1.7.3

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 (152) hide show
  1. package/CHANGELOG.md +435 -144
  2. package/LICENSE +674 -674
  3. package/README.md +491 -434
  4. package/RELEASE_GUIDE.md +504 -418
  5. package/docs/DOCUMENTATION_MIGRATION_PLAN.md +175 -175
  6. package/docs/IMPLEMENTATION_PLAN_MESH_TOPOLOGY.md +1062 -0
  7. package/docs/MESH_TOPOLOGY_GUIDE.md +992 -0
  8. package/docs/MESH_TOPOLOGY_PROGRESS.md +422 -0
  9. package/docs/MQTT_BRIDGE_COMMANDS.md +894 -0
  10. package/docs/MQTT_BRIDGE_IMPLEMENTATION_SUMMARY.md +324 -0
  11. package/docs/MQTT_COMMAND_SCHEMA_PROPOSAL.md +576 -0
  12. package/docs/MQTT_SCHEMA_COMPLIANCE.md +285 -0
  13. package/docs/MQTT_SCHEMA_PROPOSALS.md +446 -0
  14. package/docs/MQTT_SCHEMA_REVIEW.md +690 -0
  15. package/docs/OTA_COMMANDS_REFERENCE.md +554 -0
  16. package/docs/PHASE1_GUIDE.md +349 -0
  17. package/docs/PHASE2_GUIDE.md +543 -0
  18. package/docs/README.md +130 -71
  19. package/docs/SCHEMA_VALIDATION_CHECKLIST.md +222 -0
  20. package/docs/alteriom/overview.md +507 -507
  21. package/docs/api/core-api.md +606 -606
  22. package/docs/architecture/mesh-architecture.md +378 -378
  23. package/docs/architecture/plugin-system.md +516 -516
  24. package/docs/archive/DOCUSAURUS_DEPLOYMENT.md +166 -0
  25. package/docs/archive/FEATURE_PROPOSALS.md +337 -0
  26. package/docs/archive/LIBRARY_JSON_FIX.md +98 -0
  27. package/docs/archive/LIBRARY_STRUCTURE_FIX.md +215 -0
  28. package/docs/archive/PHASE1_IMPLEMENTATION.md +325 -0
  29. package/docs/archive/PHASE2_IMPLEMENTATION.md +567 -0
  30. package/docs/archive/RELEASE_SUMMARY.md +173 -0
  31. package/docs/archive/SCONS_BUILD_FIX.md +313 -0
  32. package/docs/archive/TRIGGER_RELEASE.md +280 -0
  33. package/docs/archive/VECTOR_INCLUDE_FIX.md +129 -0
  34. package/docs/archive/ota-and-status-enhancements.md +911 -0
  35. package/docs/archive/ota-status-architecture-diagrams.md +658 -0
  36. package/docs/archive/ota-status-quick-reference.md +284 -0
  37. package/docs/development/ARDUINO_COMPLIANCE_SUMMARY.md +71 -0
  38. package/docs/development/CODE_REFACTORING_RECOMMENDATIONS.md +1011 -0
  39. package/docs/development/DOCKER_TESTING.md +196 -0
  40. package/docs/development/PLATFORMIO_USAGE.md +180 -0
  41. package/docs/development/TESTING_SUMMARY.md +126 -0
  42. package/docs/development/contributing.md +301 -0
  43. package/docs/development/documentation.md +583 -0
  44. package/docs/getting-started/first-mesh.md +409 -409
  45. package/docs/getting-started/installation.md +274 -274
  46. package/docs/getting-started/quickstart.md +157 -157
  47. package/docs/improvements/FUTURE_PROPOSALS.md +1016 -0
  48. package/docs/improvements/IMPLEMENTATION_HISTORY.md +1091 -0
  49. package/docs/improvements/OTA_STATUS_ENHANCEMENTS.md +709 -0
  50. package/docs/improvements/README.md +212 -69
  51. package/docs/platformio-publishing.md +255 -0
  52. package/docs/platformio-setup-summary.md +121 -0
  53. package/docs/releases/FEATURE_HISTORY.md +543 -0
  54. package/docs/releases/PATCH_v1.7.3.md +262 -0
  55. package/docs/releases/PHASE1_SUMMARY.md +246 -0
  56. package/docs/releases/PHASE2_SUMMARY.md +499 -0
  57. package/docs/releases/RELEASE_NOTES_1.7.0.md +539 -0
  58. package/docs/troubleshooting/common-issues.md +520 -520
  59. package/docs/troubleshooting/debugging.md +455 -0
  60. package/docs/troubleshooting/faq.md +472 -472
  61. package/docs/tutorials/basic-examples.md +717 -717
  62. package/docs/wiki/API-Reference.md +245 -245
  63. package/docs/wiki/Complete-Documentation.md +122 -122
  64. package/examples/alteriom/README.md +139 -81
  65. package/examples/alteriom/alteriom.ino +186 -185
  66. package/examples/alteriom/alteriom_sensor_package.hpp +240 -127
  67. package/examples/alteriom/platformio.ini +24 -24
  68. package/examples/alteriomImproved/alteriom_sensor_package.hpp +224 -0
  69. package/examples/{alteriom → alteriomImproved}/improved_sensor_node.ino +245 -245
  70. package/examples/alteriomImproved/platformio.ini +25 -0
  71. package/examples/alteriomPhase1/alteriom_sensor_package.hpp +224 -0
  72. package/examples/alteriomPhase1/phase1_features.ino +242 -0
  73. package/examples/alteriomPhase1/platformio.ini +25 -0
  74. package/examples/alteriomPhase2/alteriom_sensor_package.hpp +224 -0
  75. package/examples/alteriomPhase2/phase2_features.ino +186 -0
  76. package/examples/alteriomPhase2/platformio.ini +25 -0
  77. package/examples/{alteriom → alteriomSensorNode}/alteriom_sensor_node.ino +183 -183
  78. package/examples/alteriomSensorNode/alteriom_sensor_package.hpp +224 -0
  79. package/examples/alteriomSensorNode/platformio.ini +25 -0
  80. package/examples/basic/basic.ino +66 -66
  81. package/examples/basic/platformio.ini +25 -25
  82. package/examples/bridge/bridge.ino +51 -51
  83. package/examples/bridge/mesh_event_publisher.hpp +253 -0
  84. package/examples/bridge/mesh_topology_reporter.hpp +303 -0
  85. package/examples/bridge/mqtt_command_bridge.hpp +459 -0
  86. package/examples/bridge/mqtt_status_bridge.hpp +519 -0
  87. package/examples/bridge/platformio.ini +25 -25
  88. package/examples/echoNode/echoNode.ino +33 -33
  89. package/examples/echoNode/platformio.ini +25 -25
  90. package/examples/logClient/logClient.ino +109 -109
  91. package/examples/logClient/platformio.ini +25 -25
  92. package/examples/logServer/logServer.ino +81 -81
  93. package/examples/logServer/platformio.ini +25 -25
  94. package/examples/meshCommandNode/alteriom_sensor_package.hpp +235 -0
  95. package/examples/meshCommandNode/meshCommandNode.ino +263 -0
  96. package/examples/meshCommandNode/platformio.ini +25 -0
  97. package/examples/mqttBridge/mqttBridge.ino +118 -118
  98. package/examples/mqttBridge/platformio.ini +26 -26
  99. package/examples/mqttCommandBridge/alteriom_sensor_package.hpp +235 -0
  100. package/examples/mqttCommandBridge/mesh_event_publisher.hpp +253 -0
  101. package/examples/mqttCommandBridge/mesh_topology_reporter.hpp +303 -0
  102. package/examples/mqttCommandBridge/mqttCommandBridge.ino +252 -0
  103. package/examples/mqttCommandBridge/mqtt_command_bridge.hpp +459 -0
  104. package/examples/mqttCommandBridge/platformio.ini +26 -0
  105. package/examples/mqttStatusBridge/mqttStatusBridge.ino +216 -0
  106. package/examples/mqttStatusBridge/mqtt_status_bridge.hpp +522 -0
  107. package/examples/mqttStatusBridge/platformio.ini +26 -0
  108. package/examples/mqttTopologyTest/README.md +467 -0
  109. package/examples/mqttTopologyTest/mqttTopologyTest.ino +748 -0
  110. package/examples/mqttTopologyTest/platformio.ini +26 -0
  111. package/examples/namedMesh/namedMesh.ino +97 -97
  112. package/examples/namedMesh/platformio.ini +25 -25
  113. package/examples/otaReceiver/otaReceiver.ino +79 -79
  114. package/examples/otaReceiver/platformio.ini +25 -25
  115. package/examples/otaSender/otaSender.ino +160 -151
  116. package/examples/otaSender/platformio.ini +25 -25
  117. package/examples/startHere/platformio.ini +25 -25
  118. package/examples/startHere/startHere.ino +159 -159
  119. package/examples/webServer/platformio.ini +27 -27
  120. package/examples/webServer/webServer.ino +89 -89
  121. package/keywords.txt +48 -48
  122. package/library.json +55 -34
  123. package/library.properties +10 -10
  124. package/package.json +86 -78
  125. package/src/AlteriomPainlessMesh.h +97 -97
  126. package/src/arduino/wifi.hpp +365 -365
  127. package/src/boost/asynctcp.hpp +279 -279
  128. package/src/painlessMesh.h +70 -70
  129. package/src/painlessMeshSTA.cpp +236 -236
  130. package/src/painlessMeshSTA.h +58 -58
  131. package/src/painlessTaskOptions.h +4 -4
  132. package/src/painlessmesh/base64.hpp +111 -111
  133. package/src/painlessmesh/buffer.hpp +229 -229
  134. package/src/painlessmesh/callback.hpp +91 -91
  135. package/src/painlessmesh/configuration.hpp +77 -77
  136. package/src/painlessmesh/connection.hpp +192 -192
  137. package/src/painlessmesh/layout.hpp +188 -188
  138. package/src/painlessmesh/logger.hpp +158 -158
  139. package/src/painlessmesh/memory.hpp +119 -119
  140. package/src/painlessmesh/mesh.hpp +761 -560
  141. package/src/painlessmesh/metrics.hpp +322 -322
  142. package/src/painlessmesh/ntp.hpp +263 -263
  143. package/src/painlessmesh/ota.hpp +582 -553
  144. package/src/painlessmesh/plugin.hpp +188 -188
  145. package/src/painlessmesh/protocol.hpp +813 -813
  146. package/src/painlessmesh/router.hpp +338 -322
  147. package/src/painlessmesh/tcp.hpp +71 -71
  148. package/src/painlessmesh/validation.hpp +238 -238
  149. package/src/plugin/performance.hpp +214 -214
  150. package/src/plugin/remote.hpp +64 -64
  151. package/src/scheduler.cpp +10 -10
  152. package/src/wifi.cpp +2 -2
@@ -1,322 +1,338 @@
1
- #ifndef _PAINLESS_MESH_ROUTER_HPP_
2
- #define _PAINLESS_MESH_ROUTER_HPP_
3
-
4
- #include <algorithm>
5
- #include <memory>
6
-
7
- #include "painlessmesh/callback.hpp"
8
- #include "painlessmesh/layout.hpp"
9
- #include "painlessmesh/logger.hpp"
10
- #include "painlessmesh/protocol.hpp"
11
-
12
- extern painlessmesh::logger::LogClass Log;
13
-
14
- namespace painlessmesh {
15
-
16
- /**
17
- * Helper functions to route messages
18
- */
19
- namespace router {
20
- template <class T>
21
- std::shared_ptr<T> findRoute(layout::Layout<T> tree,
22
- std::function<bool(std::shared_ptr<T>)> func) {
23
- auto route = std::find_if(tree.subs.begin(), tree.subs.end(), func);
24
- if (route == tree.subs.end()) return NULL;
25
- return (*route);
26
- }
27
-
28
- template <class T>
29
- std::shared_ptr<T> findRoute(layout::Layout<T> tree, uint32_t nodeId) {
30
- return findRoute<T>(tree, [nodeId](std::shared_ptr<T> s) {
31
- return layout::contains((*s), nodeId);
32
- });
33
- }
34
-
35
- template <class T, class U>
36
- bool send(T& package, std::shared_ptr<U> conn, bool priority = false) {
37
- painlessmesh::protocol::Variant variant(package);
38
- TSTRING msg;
39
- variant.printTo(msg);
40
- return conn->addMessage(msg, priority);
41
- }
42
-
43
- template <class T, class U>
44
- bool send(T&& package, std::shared_ptr<U> conn, bool priority = false) {
45
- painlessmesh::protocol::Variant variant(package);
46
- TSTRING msg;
47
- variant.printTo(msg);
48
- return conn->addMessage(msg, priority);
49
- }
50
-
51
- template <class U>
52
- bool send(protocol::Variant& variant, std::shared_ptr<U> conn,
53
- bool priority = false) {
54
- TSTRING msg;
55
- variant.printTo(msg);
56
- return conn->addMessage(msg, priority);
57
- }
58
-
59
- template <class U>
60
- bool send(protocol::Variant&& variant, std::shared_ptr<U> conn,
61
- bool priority = false) {
62
- TSTRING msg;
63
- variant.printTo(msg);
64
- return conn->addMessage(msg, priority);
65
- }
66
-
67
- template <class T, class U>
68
- bool send(T& package, layout::Layout<U> layout) {
69
- painlessmesh::protocol::Variant variant(package);
70
- TSTRING msg;
71
- variant.printTo(msg);
72
- auto conn = findRoute<U>(layout, variant.dest());
73
- if (conn) return conn->addMessage(msg);
74
- return false;
75
- }
76
-
77
- template <class U>
78
- bool send(protocol::Variant& variant, layout::Layout<U> layout) {
79
- TSTRING msg;
80
- variant.printTo(msg);
81
- auto conn = findRoute<U>(layout, variant.dest());
82
- if (conn) return conn->addMessage(msg);
83
- return false;
84
- }
85
-
86
- template <class T, class U>
87
- bool send(T&& package, layout::Layout<U> layout) {
88
- painlessmesh::protocol::Variant variant(package);
89
- TSTRING msg;
90
- variant.printTo(msg);
91
- auto conn = findRoute<U>(layout, variant.dest());
92
- if (conn) return conn->addMessage(msg);
93
- return false;
94
- }
95
-
96
- template <class U>
97
- bool send(protocol::Variant&& variant, layout::Layout<U> layout) {
98
- TSTRING msg;
99
- variant.printTo(msg);
100
- auto conn = findRoute<U>(layout, variant.dest());
101
- if (conn) return conn->addMessage(msg);
102
- return false;
103
- }
104
-
105
- template <class T, class U>
106
- size_t broadcast(T& package, layout::Layout<U> layout, uint32_t exclude) {
107
- painlessmesh::protocol::Variant variant(package);
108
- TSTRING msg;
109
- variant.printTo(msg);
110
- size_t i = 0;
111
- for (auto&& conn : layout.subs) {
112
- if (conn->nodeId != 0 && conn->nodeId != exclude) {
113
- auto sent = conn->addMessage(msg);
114
- if (sent) ++i;
115
- }
116
- }
117
- return i;
118
- }
119
-
120
- template <class T, class U>
121
- size_t broadcast(T&& package, layout::Layout<U> layout, uint32_t exclude) {
122
- painlessmesh::protocol::Variant variant(package);
123
- TSTRING msg;
124
- variant.printTo(msg);
125
- size_t i = 0;
126
- for (auto&& conn : layout.subs) {
127
- if (conn->nodeId != 0 && conn->nodeId != exclude) {
128
- auto sent = conn->addMessage(msg);
129
- if (sent) ++i;
130
- }
131
- }
132
- return i;
133
- }
134
-
135
- template <class T>
136
- size_t broadcast(protocol::Variant& variant, layout::Layout<T> layout,
137
- uint32_t exclude) {
138
- TSTRING msg;
139
- variant.printTo(msg);
140
- size_t i = 0;
141
- for (auto&& conn : layout.subs) {
142
- if (conn->nodeId != 0 && conn->nodeId != exclude) {
143
- auto sent = conn->addMessage(msg);
144
- if (sent) ++i;
145
- }
146
- }
147
- return i;
148
- }
149
-
150
- template <class T>
151
- size_t broadcast(protocol::Variant&& variant, layout::Layout<T> layout,
152
- uint32_t exclude) {
153
- TSTRING msg;
154
- variant.printTo(msg);
155
- size_t i = 0;
156
- for (auto&& conn : layout.subs) {
157
- if (conn->nodeId != 0 && conn->nodeId != exclude) {
158
- auto sent = conn->addMessage(msg);
159
- if (sent) ++i;
160
- }
161
- }
162
- return i;
163
- }
164
-
165
- template <class T>
166
- void routePackage(layout::Layout<T> layout, std::shared_ptr<T> connection,
167
- const TSTRING& pkg, callback::MeshPackageCallbackList<T> cbl,
168
- uint32_t receivedAt) {
169
- using namespace logger;
170
- Log(COMMUNICATION, "routePackage(): Recvd from %u: %s\n", connection->nodeId,
171
- pkg.c_str());
172
- #if ARDUINOJSON_VERSION_MAJOR == 7
173
- protocol::Variant variant(pkg);
174
- if (variant.error) {
175
- Log(ERROR,
176
- "routePackage(): parsing failed. err=%u, total_length=%d, data=%s<--\n",
177
- variant.error, pkg.length(), pkg.c_str());
178
- return;
179
- }
180
-
181
- if (variant.routing() == SINGLE && variant.dest() != layout.getNodeId()) {
182
- // Send on without further processing
183
- send<T>(variant, layout);
184
- return;
185
- } else if (variant.routing() == BROADCAST) {
186
- broadcast<T>(variant, layout, connection->nodeId);
187
- }
188
- auto calls = cbl.execute(variant.type(), variant, connection, receivedAt);
189
- if (calls == 0)
190
- Log(DEBUG, "routePackage(): No callbacks executed; %u, %s\n",
191
- variant.type(), pkg.c_str());
192
- #else
193
- static size_t baseCapacity = 512;
194
- // Using a ptr so we can overwrite it if we need to grow capacity.
195
- // Bug in copy constructor with grown capacity can cause segmentation fault
196
- auto variant =
197
- std::make_shared<protocol::Variant>(pkg, pkg.length() + baseCapacity);
198
- while (variant->error == DeserializationError::NoMemory &&
199
- baseCapacity <= 20480) {
200
- // Not enough memory, adapt scaling (variant::capacityScaling) and log the
201
- // new value
202
- Log(DEBUG,
203
- "routePackage(): parsing failed. err=%u, increasing capacity: %u\n",
204
- variant->error, baseCapacity);
205
- baseCapacity += 256;
206
- variant =
207
- std::make_shared<protocol::Variant>(pkg, pkg.length() + baseCapacity);
208
- }
209
- if (variant->error) {
210
- Log(ERROR,
211
- "routePackage(): parsing failed. err=%u, total_length=%d, data=%s<--\n",
212
- variant->error, pkg.length(), pkg.c_str());
213
- return;
214
- }
215
-
216
- if (variant->routing() == SINGLE && variant->dest() != layout.getNodeId()) {
217
- // Send on without further processing
218
- send<T>((*variant), layout);
219
- return;
220
- } else if (variant->routing() == BROADCAST) {
221
- broadcast<T>((*variant), layout, connection->nodeId);
222
- }
223
- auto calls = cbl.execute(variant->type(), (*variant), connection, receivedAt);
224
- if (calls == 0)
225
- Log(DEBUG, "routePackage(): No callbacks executed; %u, %s\n",
226
- variant->type(), pkg.c_str());
227
- #endif
228
- }
229
-
230
- template <class T, class U>
231
- void handleNodeSync(T& mesh, protocol::NodeTree newTree,
232
- std::shared_ptr<U> conn) {
233
- Log(logger::SYNC, "handleNodeSync(): with %u\n", conn->nodeId);
234
-
235
- if (!conn->validSubs(newTree)) {
236
- Log(logger::SYNC, "handleNodeSync(): invalid new connection\n");
237
- Log.remote("Invalid connection to %u\n", conn->nodeId);
238
- conn->close();
239
- return;
240
- }
241
-
242
- if (conn->newConnection) {
243
- auto oldConnection = router::findRoute<U>(mesh, newTree.nodeId);
244
- if (oldConnection) {
245
- Log(logger::SYNC,
246
- "handleNodeSync(): already connected to %u. Closing the new "
247
- "connection \n",
248
- newTree.nodeId);
249
- Log.remote("Already connected to %u\n", newTree.nodeId);
250
- conn->close();
251
- return;
252
- }
253
- auto remoteNodeId = newTree.nodeId;
254
- mesh.addTask([&mesh, remoteNodeId]() {
255
- Log(logger::CONNECTION, "newConnectionTask():\n");
256
- Log(logger::CONNECTION, "newConnectionTask(): adding %u now= %u\n",
257
- remoteNodeId, mesh.getNodeTime());
258
- mesh.newConnectionCallbacks.execute(remoteNodeId);
259
- });
260
-
261
- // Initially interval is every 10 seconds,
262
- // this will slow down to TIME_SYNC_INTERVAL
263
- // after first succesfull sync
264
- // TODO move it to a new connection callback and use initTimeSync from
265
- // ntp.hpp
266
- conn->timeSyncTask.set(10 * TASK_SECOND, TASK_FOREVER, [conn, &mesh]() {
267
- Log(logger::S_TIME, "timeSyncTask(): %u\n", conn->nodeId);
268
- mesh.startTimeSync(conn);
269
- });
270
- mesh.mScheduler->addTask(conn->timeSyncTask);
271
- if (conn->station)
272
- // We are STA, request time immediately
273
- conn->timeSyncTask.enable();
274
- else
275
- // We are the AP, give STA the change to initiate time sync
276
- conn->timeSyncTask.enableDelayed();
277
- conn->newConnection = false;
278
- }
279
-
280
- if (conn->updateSubs(newTree)) {
281
- auto nodeId = newTree.nodeId;
282
- mesh.addTask(
283
- [&mesh, nodeId]() { mesh.changedConnectionCallbacks.execute(nodeId); });
284
- } else {
285
- conn->nodeSyncTask.delay();
286
- mesh.stability += std::min(1000 - mesh.stability, (size_t)25);
287
- }
288
- }
289
-
290
- template <class T, typename U>
291
- callback::MeshPackageCallbackList<U> addPackageCallback(
292
- callback::MeshPackageCallbackList<U>&& callbackList, T& mesh) {
293
- // REQUEST type,
294
- callbackList.onPackage(
295
- protocol::NODE_SYNC_REQUEST,
296
- [&mesh](protocol::Variant& variant, std::shared_ptr<U> connection,
297
- uint32_t receivedAt) {
298
- auto newTree = variant.to<protocol::NodeSyncRequest>();
299
- handleNodeSync<T, U>(mesh, newTree, connection);
300
- send<protocol::NodeSyncReply>(
301
- connection->reply(std::move(mesh.asNodeTree())), connection, true);
302
- return false;
303
- });
304
-
305
- // Reply type just handle it
306
- callbackList.onPackage(
307
- protocol::NODE_SYNC_REPLY,
308
- [&mesh](protocol::Variant& variant, std::shared_ptr<U> connection,
309
- uint32_t receivedAt) {
310
- auto newTree = variant.to<protocol::NodeSyncReply>();
311
- handleNodeSync<T, U>(mesh, newTree, connection);
312
- connection->timeOutTask.disable();
313
- return false;
314
- });
315
-
316
- return callbackList;
317
- }
318
-
319
- } // namespace router
320
- } // namespace painlessmesh
321
-
322
- #endif
1
+ #ifndef _PAINLESS_MESH_ROUTER_HPP_
2
+ #define _PAINLESS_MESH_ROUTER_HPP_
3
+
4
+ #include <algorithm>
5
+ #include <memory>
6
+
7
+ #include "painlessmesh/callback.hpp"
8
+ #include "painlessmesh/layout.hpp"
9
+ #include "painlessmesh/logger.hpp"
10
+ #include "painlessmesh/protocol.hpp"
11
+
12
+ extern painlessmesh::logger::LogClass Log;
13
+
14
+ namespace painlessmesh {
15
+
16
+ /**
17
+ * Helper functions to route messages
18
+ */
19
+ namespace router {
20
+ template <class T>
21
+ std::shared_ptr<T> findRoute(layout::Layout<T> tree,
22
+ std::function<bool(std::shared_ptr<T>)> func) {
23
+ auto route = std::find_if(tree.subs.begin(), tree.subs.end(), func);
24
+ if (route == tree.subs.end()) return NULL;
25
+ return (*route);
26
+ }
27
+
28
+ template <class T>
29
+ std::shared_ptr<T> findRoute(layout::Layout<T> tree, uint32_t nodeId) {
30
+ return findRoute<T>(tree, [nodeId](std::shared_ptr<T> s) {
31
+ return layout::contains((*s), nodeId);
32
+ });
33
+ }
34
+
35
+ template <class T, class U>
36
+ bool send(T& package, std::shared_ptr<U> conn, bool priority = false) {
37
+ painlessmesh::protocol::Variant variant(package);
38
+ TSTRING msg;
39
+ variant.printTo(msg);
40
+ return conn->addMessage(msg, priority);
41
+ }
42
+
43
+ template <class T, class U>
44
+ bool send(T&& package, std::shared_ptr<U> conn, bool priority = false) {
45
+ painlessmesh::protocol::Variant variant(package);
46
+ TSTRING msg;
47
+ variant.printTo(msg);
48
+ return conn->addMessage(msg, priority);
49
+ }
50
+
51
+ template <class U>
52
+ bool send(protocol::Variant& variant, std::shared_ptr<U> conn,
53
+ bool priority = false) {
54
+ TSTRING msg;
55
+ variant.printTo(msg);
56
+ return conn->addMessage(msg, priority);
57
+ }
58
+
59
+ template <class U>
60
+ bool send(protocol::Variant&& variant, std::shared_ptr<U> conn,
61
+ bool priority = false) {
62
+ TSTRING msg;
63
+ variant.printTo(msg);
64
+ return conn->addMessage(msg, priority);
65
+ }
66
+
67
+ template <class T, class U>
68
+ bool send(T& package, layout::Layout<U> layout) {
69
+ painlessmesh::protocol::Variant variant(package);
70
+ TSTRING msg;
71
+ variant.printTo(msg);
72
+ auto conn = findRoute<U>(layout, variant.dest());
73
+ if (conn) return conn->addMessage(msg);
74
+ return false;
75
+ }
76
+
77
+ template <class U>
78
+ bool send(protocol::Variant& variant, layout::Layout<U> layout) {
79
+ TSTRING msg;
80
+ variant.printTo(msg);
81
+ auto conn = findRoute<U>(layout, variant.dest());
82
+ if (conn) return conn->addMessage(msg);
83
+ return false;
84
+ }
85
+
86
+ template <class T, class U>
87
+ bool send(T&& package, layout::Layout<U> layout) {
88
+ painlessmesh::protocol::Variant variant(package);
89
+ TSTRING msg;
90
+ variant.printTo(msg);
91
+ auto conn = findRoute<U>(layout, variant.dest());
92
+ if (conn) return conn->addMessage(msg);
93
+ return false;
94
+ }
95
+
96
+ template <class U>
97
+ bool send(protocol::Variant&& variant, layout::Layout<U> layout) {
98
+ TSTRING msg;
99
+ variant.printTo(msg);
100
+ auto conn = findRoute<U>(layout, variant.dest());
101
+ if (conn) return conn->addMessage(msg);
102
+ return false;
103
+ }
104
+
105
+ template <class T, class U>
106
+ size_t broadcast(T& package, layout::Layout<U> layout, uint32_t exclude) {
107
+ painlessmesh::protocol::Variant variant(package);
108
+ TSTRING msg;
109
+ variant.printTo(msg);
110
+ size_t i = 0;
111
+ for (auto&& conn : layout.subs) {
112
+ if (conn->nodeId != 0 && conn->nodeId != exclude) {
113
+ auto sent = conn->addMessage(msg);
114
+ if (sent) ++i;
115
+ }
116
+ }
117
+ return i;
118
+ }
119
+
120
+ template <class T, class U>
121
+ size_t broadcast(T&& package, layout::Layout<U> layout, uint32_t exclude) {
122
+ painlessmesh::protocol::Variant variant(package);
123
+ TSTRING msg;
124
+ variant.printTo(msg);
125
+ size_t i = 0;
126
+ for (auto&& conn : layout.subs) {
127
+ if (conn->nodeId != 0 && conn->nodeId != exclude) {
128
+ auto sent = conn->addMessage(msg);
129
+ if (sent) ++i;
130
+ }
131
+ }
132
+ return i;
133
+ }
134
+
135
+ template <class T>
136
+ size_t broadcast(protocol::Variant& variant, layout::Layout<T> layout,
137
+ uint32_t exclude) {
138
+ TSTRING msg;
139
+ variant.printTo(msg);
140
+ size_t i = 0;
141
+ for (auto&& conn : layout.subs) {
142
+ if (conn->nodeId != 0 && conn->nodeId != exclude) {
143
+ auto sent = conn->addMessage(msg);
144
+ if (sent) ++i;
145
+ }
146
+ }
147
+ return i;
148
+ }
149
+
150
+ template <class T>
151
+ size_t broadcast(protocol::Variant&& variant, layout::Layout<T> layout,
152
+ uint32_t exclude) {
153
+ TSTRING msg;
154
+ variant.printTo(msg);
155
+ size_t i = 0;
156
+ for (auto&& conn : layout.subs) {
157
+ if (conn->nodeId != 0 && conn->nodeId != exclude) {
158
+ auto sent = conn->addMessage(msg);
159
+ if (sent) ++i;
160
+ }
161
+ }
162
+ return i;
163
+ }
164
+
165
+ template <class T>
166
+ void routePackage(layout::Layout<T> layout, std::shared_ptr<T> connection,
167
+ const TSTRING& pkg, callback::MeshPackageCallbackList<T> cbl,
168
+ uint32_t receivedAt) {
169
+ using namespace logger;
170
+ Log(COMMUNICATION, "routePackage(): Recvd from %u: %s\n", connection->nodeId,
171
+ pkg.c_str());
172
+ #if ARDUINOJSON_VERSION_MAJOR == 7
173
+ protocol::Variant variant(pkg);
174
+ if (variant.error) {
175
+ Log(ERROR,
176
+ "routePackage(): parsing failed. err=%u, total_length=%d, data=%s<--\n",
177
+ variant.error, pkg.length(), pkg.c_str());
178
+ return;
179
+ }
180
+
181
+ if (variant.routing() == SINGLE && variant.dest() != layout.getNodeId()) {
182
+ // Send on without further processing
183
+ send<T>(variant, layout);
184
+ return;
185
+ } else if (variant.routing() == BROADCAST) {
186
+ broadcast<T>(variant, layout, connection->nodeId);
187
+ }
188
+ auto calls = cbl.execute(variant.type(), variant, connection, receivedAt);
189
+ if (calls == 0)
190
+ Log(DEBUG, "routePackage(): No callbacks executed; %u, %s\n",
191
+ variant.type(), pkg.c_str());
192
+ #else
193
+ // Calculate required capacity based on message size and nesting depth
194
+ // Fixed capacity approach to avoid segmentation fault issues with
195
+ // dynamic reallocation (see issue #521 and CODE_REFACTORING_RECOMMENDATIONS.md)
196
+ size_t nestingDepth = std::count(pkg.begin(), pkg.end(), '{') +
197
+ std::count(pkg.begin(), pkg.end(), '[');
198
+
199
+ #if ARDUINOJSON_VERSION_MAJOR >= 7
200
+ // ArduinoJson v7: automatic capacity management, use generous buffer
201
+ size_t calculatedCapacity = pkg.length() + 1024;
202
+ #else
203
+ // ArduinoJson v6: manual capacity calculation required
204
+ // Base capacity: message length + overhead for JSON structure
205
+ // Each nesting level adds overhead for pointers and metadata
206
+ size_t calculatedCapacity = pkg.length() +
207
+ JSON_OBJECT_SIZE(10) * std::max(nestingDepth, size_t(1)) +
208
+ 512; // Additional buffer for strings and padding
209
+ #endif
210
+
211
+ // Cap at 8KB for safety on ESP8266 (which has ~80KB total heap)
212
+ // Messages larger than this should be rejected
213
+ constexpr size_t MAX_MESSAGE_CAPACITY = 8192;
214
+ size_t capacity = std::min(calculatedCapacity, MAX_MESSAGE_CAPACITY);
215
+
216
+ auto variant = std::make_shared<protocol::Variant>(pkg, capacity);
217
+
218
+ if (variant->error) {
219
+ if (variant->error == DeserializationError::NoMemory) {
220
+ Log(ERROR,
221
+ "routePackage(): Message too large. length=%d, calculated_capacity=%u, "
222
+ "nesting_depth=%u. Consider increasing MAX_MESSAGE_CAPACITY if needed.\n",
223
+ pkg.length(), calculatedCapacity, nestingDepth);
224
+ } else {
225
+ Log(ERROR,
226
+ "routePackage(): parsing failed. err=%u, length=%d, data=%s<--\n",
227
+ variant->error, pkg.length(), pkg.c_str());
228
+ }
229
+ return;
230
+ }
231
+
232
+ if (variant->routing() == SINGLE && variant->dest() != layout.getNodeId()) {
233
+ // Send on without further processing
234
+ send<T>((*variant), layout);
235
+ return;
236
+ } else if (variant->routing() == BROADCAST) {
237
+ broadcast<T>((*variant), layout, connection->nodeId);
238
+ }
239
+ auto calls = cbl.execute(variant->type(), (*variant), connection, receivedAt);
240
+ if (calls == 0)
241
+ Log(DEBUG, "routePackage(): No callbacks executed; %u, %s\n",
242
+ variant->type(), pkg.c_str());
243
+ #endif
244
+ }
245
+
246
+ template <class T, class U>
247
+ void handleNodeSync(T& mesh, protocol::NodeTree newTree,
248
+ std::shared_ptr<U> conn) {
249
+ Log(logger::SYNC, "handleNodeSync(): with %u\n", conn->nodeId);
250
+
251
+ if (!conn->validSubs(newTree)) {
252
+ Log(logger::SYNC, "handleNodeSync(): invalid new connection\n");
253
+ Log.remote("Invalid connection to %u\n", conn->nodeId);
254
+ conn->close();
255
+ return;
256
+ }
257
+
258
+ if (conn->newConnection) {
259
+ auto oldConnection = router::findRoute<U>(mesh, newTree.nodeId);
260
+ if (oldConnection) {
261
+ Log(logger::SYNC,
262
+ "handleNodeSync(): already connected to %u. Closing the new "
263
+ "connection \n",
264
+ newTree.nodeId);
265
+ Log.remote("Already connected to %u\n", newTree.nodeId);
266
+ conn->close();
267
+ return;
268
+ }
269
+ auto remoteNodeId = newTree.nodeId;
270
+ mesh.addTask([&mesh, remoteNodeId]() {
271
+ Log(logger::CONNECTION, "newConnectionTask():\n");
272
+ Log(logger::CONNECTION, "newConnectionTask(): adding %u now= %u\n",
273
+ remoteNodeId, mesh.getNodeTime());
274
+ mesh.newConnectionCallbacks.execute(remoteNodeId);
275
+ });
276
+
277
+ // Initially interval is every 10 seconds,
278
+ // this will slow down to TIME_SYNC_INTERVAL
279
+ // after first succesfull sync
280
+ // TODO move it to a new connection callback and use initTimeSync from
281
+ // ntp.hpp
282
+ conn->timeSyncTask.set(10 * TASK_SECOND, TASK_FOREVER, [conn, &mesh]() {
283
+ Log(logger::S_TIME, "timeSyncTask(): %u\n", conn->nodeId);
284
+ mesh.startTimeSync(conn);
285
+ });
286
+ mesh.mScheduler->addTask(conn->timeSyncTask);
287
+ if (conn->station)
288
+ // We are STA, request time immediately
289
+ conn->timeSyncTask.enable();
290
+ else
291
+ // We are the AP, give STA the change to initiate time sync
292
+ conn->timeSyncTask.enableDelayed();
293
+ conn->newConnection = false;
294
+ }
295
+
296
+ if (conn->updateSubs(newTree)) {
297
+ auto nodeId = newTree.nodeId;
298
+ mesh.addTask(
299
+ [&mesh, nodeId]() { mesh.changedConnectionCallbacks.execute(nodeId); });
300
+ } else {
301
+ conn->nodeSyncTask.delay();
302
+ mesh.stability += std::min(1000 - mesh.stability, (size_t)25);
303
+ }
304
+ }
305
+
306
+ template <class T, typename U>
307
+ callback::MeshPackageCallbackList<U> addPackageCallback(
308
+ callback::MeshPackageCallbackList<U>&& callbackList, T& mesh) {
309
+ // REQUEST type,
310
+ callbackList.onPackage(
311
+ protocol::NODE_SYNC_REQUEST,
312
+ [&mesh](protocol::Variant& variant, std::shared_ptr<U> connection,
313
+ uint32_t receivedAt) {
314
+ auto newTree = variant.to<protocol::NodeSyncRequest>();
315
+ handleNodeSync<T, U>(mesh, newTree, connection);
316
+ send<protocol::NodeSyncReply>(
317
+ connection->reply(std::move(mesh.asNodeTree())), connection, true);
318
+ return false;
319
+ });
320
+
321
+ // Reply type just handle it
322
+ callbackList.onPackage(
323
+ protocol::NODE_SYNC_REPLY,
324
+ [&mesh](protocol::Variant& variant, std::shared_ptr<U> connection,
325
+ uint32_t receivedAt) {
326
+ auto newTree = variant.to<protocol::NodeSyncReply>();
327
+ handleNodeSync<T, U>(mesh, newTree, connection);
328
+ connection->timeOutTask.disable();
329
+ return false;
330
+ });
331
+
332
+ return callbackList;
333
+ }
334
+
335
+ } // namespace router
336
+ } // namespace painlessmesh
337
+
338
+ #endif