@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,263 +1,263 @@
1
- #ifndef _PAINLESS_MESH_NTP_HPP_
2
- #define _PAINLESS_MESH_NTP_HPP_
3
-
4
- #ifndef TIME_SYNC_INTERVAL
5
- #define TIME_SYNC_INTERVAL 1 * TASK_MINUTE // Time resync period
6
- #endif
7
-
8
- #ifndef TIME_SYNC_ACCURACY
9
- #define TIME_SYNC_ACCURACY 5000 // Minimum time sync accuracy (5ms
10
- #endif
11
-
12
- #ifndef MIN_LARGE_OFFSET_STEP
13
- #define MIN_LARGE_OFFSET_STEP 500 // Minimum offset size before softening
14
- #endif
15
-
16
- #ifndef MAX_CPU_TRIP_RATIO
17
- #define MAX_CPU_TRIP_RATIO \
18
- 85 // Maximum ratio between remote cpu time and trip time for NTP calc to be
19
- // reasonable
20
- #endif
21
-
22
- #include "Arduino.h"
23
-
24
- #include "painlessmesh/callback.hpp"
25
- #include "painlessmesh/logger.hpp"
26
- #include "painlessmesh/router.hpp"
27
-
28
- extern painlessmesh::logger::LogClass Log;
29
-
30
- namespace painlessmesh {
31
- namespace ntp {
32
-
33
- class MeshTime {
34
- public:
35
- /** Returns the mesh time in microsecond precision.
36
- *
37
- * Time rolls over every 71 minutes.
38
- *
39
- * Nodes try to keep a common time base synchronizing to each other using [an
40
- * SNTP based
41
- * protocol](https://gitlab.com/painlessMesh/painlessMesh/wikis/mesh-protocol#time-sync)
42
- */
43
- uint32_t getNodeTime() { return micros() + timeOffset; }
44
-
45
- protected:
46
- uint32_t timeOffset = 0;
47
- };
48
-
49
- /**
50
- * Calculate the offset of the local clock using the ntp algorithm
51
- *
52
- * See ntp overview for more information
53
- */
54
- inline int32_t clockOffset(uint32_t time0, uint32_t time1, uint32_t time2,
55
- uint32_t time3) {
56
- uint32_t offset =
57
- ((int32_t)(time1 - time0) / 2) + ((int32_t)(time2 - time3) / 2);
58
-
59
- // Take small steps to avoid over correction
60
- int32_t signedOffset = (int32_t)offset;
61
- if (abs(signedOffset) < MIN_LARGE_OFFSET_STEP && abs(signedOffset) > 4)
62
- signedOffset = signedOffset / 4;
63
- // Discard outliers with excessive trip to cpu ratios
64
- if ((time3 - time0) > (time2 - time1) * MAX_CPU_TRIP_RATIO) signedOffset = 0;
65
- return signedOffset;
66
- }
67
-
68
- /**
69
- * Calculate the time it took to get reply from other node
70
- *
71
- * See ntp algorithm for more information
72
- */
73
- inline int32_t tripDelay(uint32_t time0, uint32_t time1, uint32_t time2,
74
- uint32_t time3) {
75
- return ((time3 - time0) - (time2 - time1)) / 2;
76
- }
77
-
78
- inline bool adopt(protocol::NodeTree mesh, protocol::NodeTree connection) {
79
- auto mySubCount =
80
- layout::size(layout::excludeRoute(std::move(mesh), connection.nodeId));
81
- auto remoteSubCount = layout::size(connection);
82
- if (mySubCount > remoteSubCount) return false;
83
- if (mySubCount == remoteSubCount) {
84
- if (connection.nodeId == 0)
85
- Log(logger::ERROR, "Adopt called on uninitialized connection\n");
86
- // TODO: there is a change here that a middle node also lower is than the
87
- // two others and will start switching between both. Maybe should do it
88
- // randomly instead?
89
- return mesh.nodeId < connection.nodeId;
90
- }
91
- return true;
92
- }
93
-
94
- template <class T>
95
- void initTimeSync(protocol::NodeTree mesh, std::shared_ptr<T> connection,
96
- uint32_t nodeTime) {
97
- using namespace painlessmesh::logger;
98
- painlessmesh::protocol::TimeSync timeSync;
99
- if (adopt(mesh, (*connection))) {
100
- timeSync = painlessmesh::protocol::TimeSync(mesh.nodeId, connection->nodeId,
101
- nodeTime);
102
- Log(S_TIME, "initTimeSync(): Requesting time from %u\n",
103
- connection->nodeId);
104
- } else {
105
- timeSync =
106
- painlessmesh::protocol::TimeSync(mesh.nodeId, connection->nodeId);
107
- Log(S_TIME, "initTimeSync(): Requesting %u to adopt our time\n",
108
- connection->nodeId);
109
- }
110
-
111
- router::send<protocol::TimeSync, T>(timeSync, connection, true);
112
- }
113
-
114
- template <class T, class U>
115
- void handleTimeSync(T& mesh, painlessmesh::protocol::TimeSync timeSync,
116
- std::shared_ptr<U> conn, uint32_t receivedAt) {
117
- switch (timeSync.msg.type) {
118
- case (painlessmesh::protocol::TIME_SYNC_ERROR):
119
- Log(logger::ERROR,
120
- "handleTimeSync(): Received time sync error. Restarting time "
121
- "sync.\n");
122
- conn->timeSyncTask.forceNextIteration();
123
- break;
124
- case (painlessmesh::protocol::TIME_SYNC_REQUEST): // Other party request me
125
- // to ask it for time
126
- Log(logger::S_TIME,
127
- "handleTimeSync(): Received requesto to start TimeSync with "
128
- "node: %u\n",
129
- conn->nodeId);
130
- timeSync.reply(mesh.getNodeTime());
131
- router::send<painlessmesh::protocol::TimeSync>(timeSync, conn, true);
132
- break;
133
-
134
- case (painlessmesh::protocol::TIME_REQUEST):
135
- timeSync.reply(receivedAt, mesh.getNodeTime());
136
- router::send<painlessmesh::protocol::TimeSync>(timeSync, conn, true);
137
-
138
- Log(logger::S_TIME,
139
- "handleTimeSync(): timeSyncStatus with %u completed\n", conn->nodeId);
140
-
141
- // After response is sent I assume sync is completed
142
- conn->timeSyncTask.delay(TIME_SYNC_INTERVAL);
143
- break;
144
-
145
- case (painlessmesh::protocol::TIME_REPLY): {
146
- Log(logger::S_TIME,
147
- "handleTimeSync(): %u adopting TIME_RESPONSE from %u\n", mesh.nodeId,
148
- conn->nodeId);
149
- int32_t offset = painlessmesh::ntp::clockOffset(
150
- timeSync.msg.t0, timeSync.msg.t1, timeSync.msg.t2, receivedAt);
151
- mesh.timeOffset += offset; // Accumulate offset
152
-
153
- // flag all connections for re-timeSync
154
- if (mesh.nodeTimeAdjustedCallback) {
155
- mesh.nodeTimeAdjustedCallback(offset);
156
- }
157
-
158
- if ((offset < TIME_SYNC_ACCURACY) && (offset > -TIME_SYNC_ACCURACY) &&
159
- (offset != 0)) {
160
- // mark complete only if offset was less than 10 ms
161
- conn->timeSyncTask.delay(TIME_SYNC_INTERVAL);
162
- Log(logger::S_TIME,
163
- "handleTimeSync(): timeSyncStatus with %u completed\n",
164
- conn->nodeId);
165
-
166
- // Time has changed, update other nodes
167
- for (auto&& connection : mesh.subs) {
168
- if (connection->nodeId != conn->nodeId) { // exclude this connection
169
- connection->timeSyncTask.forceNextIteration();
170
- Log(logger::S_TIME,
171
- "handleTimeSync(): timeSyncStatus with %u brought forward\n",
172
- connection->nodeId);
173
- }
174
- }
175
- } else {
176
- // Iterate sync procedure if accuracy was not enough
177
- conn->timeSyncTask.delay(200 * TASK_MILLISECOND); // Small delay
178
- Log(logger::S_TIME,
179
- "handleTimeSync(): timeSyncStatus with %u needs further tries\n",
180
- conn->nodeId);
181
- }
182
- break;
183
- }
184
- default:
185
- Log(logger::ERROR, "handleTimeSync(): unkown type %u, %u\n",
186
- timeSync.msg.type, painlessmesh::protocol::TIME_SYNC_REQUEST);
187
- break;
188
- }
189
- Log(logger::S_TIME, "handleTimeSync(): ----------------------------------\n");
190
- }
191
-
192
- template <class T, class U>
193
- void handleTimeDelay(T& mesh, painlessmesh::protocol::TimeDelay timeDelay,
194
- std::shared_ptr<U> conn, uint32_t receivedAt) {
195
- Log(logger::S_TIME, "handleTimeDelay(): from %u in timestamp\n",
196
- timeDelay.from);
197
-
198
- switch (timeDelay.msg.type) {
199
- case (painlessmesh::protocol::TIME_SYNC_ERROR):
200
- Log(logger::ERROR,
201
- "handleTimeDelay(): Error in requesting time delay. Please try "
202
- "again.\n");
203
- break;
204
-
205
- case (painlessmesh::protocol::TIME_REQUEST):
206
- // conn->timeSyncStatus == IN_PROGRESS;
207
- Log(logger::S_TIME, "handleTimeDelay(): TIME REQUEST received.\n");
208
-
209
- // Build time response
210
- timeDelay.reply(receivedAt, mesh.getNodeTime());
211
- router::send<protocol::TimeDelay, U>(timeDelay, conn);
212
- break;
213
-
214
- case (painlessmesh::protocol::TIME_REPLY): {
215
- Log(logger::S_TIME, "handleTimeDelay(): TIME RESPONSE received.\n");
216
- int32_t delay = painlessmesh::ntp::tripDelay(
217
- timeDelay.msg.t0, timeDelay.msg.t1, timeDelay.msg.t2, receivedAt);
218
- Log(logger::S_TIME, "handleTimeDelay(): Delay is %d\n", delay);
219
-
220
- // conn->timeSyncStatus == COMPLETE;
221
-
222
- if (mesh.nodeDelayReceivedCallback)
223
- mesh.nodeDelayReceivedCallback(timeDelay.from, delay);
224
- } break;
225
-
226
- default:
227
- Log(logger::ERROR,
228
- "handleTimeDelay(): Unknown timeSyncMessageType received. Ignoring "
229
- "for now.\n");
230
- }
231
-
232
- Log(logger::S_TIME, "handleTimeSync(): ----------------------------------\n");
233
- }
234
-
235
- template <class T, typename U>
236
- callback::MeshPackageCallbackList<U> addPackageCallback(
237
- callback::MeshPackageCallbackList<U>&& callbackList, T& mesh) {
238
- // TimeSync
239
- callbackList.onPackage(
240
- protocol::TIME_SYNC,
241
- [&mesh](protocol::Variant& variant, std::shared_ptr<U> connection,
242
- uint32_t receivedAt) {
243
- auto timeSync = variant.to<protocol::TimeSync>();
244
- handleTimeSync<T, U>(mesh, timeSync, connection, receivedAt);
245
- return false;
246
- });
247
-
248
- // TimeDelay
249
- callbackList.onPackage(
250
- protocol::TIME_DELAY,
251
- [&mesh](protocol::Variant& variant, std::shared_ptr<U> connection,
252
- uint32_t receivedAt) {
253
- auto timeDelay = variant.to<protocol::TimeDelay>();
254
- handleTimeDelay<T, U>(mesh, timeDelay, connection, receivedAt);
255
- return false;
256
- });
257
-
258
- return callbackList;
259
- }
260
-
261
- } // namespace ntp
262
- } // namespace painlessmesh
263
- #endif
1
+ #ifndef _PAINLESS_MESH_NTP_HPP_
2
+ #define _PAINLESS_MESH_NTP_HPP_
3
+
4
+ #ifndef TIME_SYNC_INTERVAL
5
+ #define TIME_SYNC_INTERVAL 1 * TASK_MINUTE // Time resync period
6
+ #endif
7
+
8
+ #ifndef TIME_SYNC_ACCURACY
9
+ #define TIME_SYNC_ACCURACY 5000 // Minimum time sync accuracy (5ms
10
+ #endif
11
+
12
+ #ifndef MIN_LARGE_OFFSET_STEP
13
+ #define MIN_LARGE_OFFSET_STEP 500 // Minimum offset size before softening
14
+ #endif
15
+
16
+ #ifndef MAX_CPU_TRIP_RATIO
17
+ #define MAX_CPU_TRIP_RATIO \
18
+ 85 // Maximum ratio between remote cpu time and trip time for NTP calc to be
19
+ // reasonable
20
+ #endif
21
+
22
+ #include "Arduino.h"
23
+
24
+ #include "painlessmesh/callback.hpp"
25
+ #include "painlessmesh/logger.hpp"
26
+ #include "painlessmesh/router.hpp"
27
+
28
+ extern painlessmesh::logger::LogClass Log;
29
+
30
+ namespace painlessmesh {
31
+ namespace ntp {
32
+
33
+ class MeshTime {
34
+ public:
35
+ /** Returns the mesh time in microsecond precision.
36
+ *
37
+ * Time rolls over every 71 minutes.
38
+ *
39
+ * Nodes try to keep a common time base synchronizing to each other using [an
40
+ * SNTP based
41
+ * protocol](https://gitlab.com/painlessMesh/painlessMesh/wikis/mesh-protocol#time-sync)
42
+ */
43
+ uint32_t getNodeTime() { return micros() + timeOffset; }
44
+
45
+ protected:
46
+ uint32_t timeOffset = 0;
47
+ };
48
+
49
+ /**
50
+ * Calculate the offset of the local clock using the ntp algorithm
51
+ *
52
+ * See ntp overview for more information
53
+ */
54
+ inline int32_t clockOffset(uint32_t time0, uint32_t time1, uint32_t time2,
55
+ uint32_t time3) {
56
+ uint32_t offset =
57
+ ((int32_t)(time1 - time0) / 2) + ((int32_t)(time2 - time3) / 2);
58
+
59
+ // Take small steps to avoid over correction
60
+ int32_t signedOffset = (int32_t)offset;
61
+ if (abs(signedOffset) < MIN_LARGE_OFFSET_STEP && abs(signedOffset) > 4)
62
+ signedOffset = signedOffset / 4;
63
+ // Discard outliers with excessive trip to cpu ratios
64
+ if ((time3 - time0) > (time2 - time1) * MAX_CPU_TRIP_RATIO) signedOffset = 0;
65
+ return signedOffset;
66
+ }
67
+
68
+ /**
69
+ * Calculate the time it took to get reply from other node
70
+ *
71
+ * See ntp algorithm for more information
72
+ */
73
+ inline int32_t tripDelay(uint32_t time0, uint32_t time1, uint32_t time2,
74
+ uint32_t time3) {
75
+ return ((time3 - time0) - (time2 - time1)) / 2;
76
+ }
77
+
78
+ inline bool adopt(protocol::NodeTree mesh, protocol::NodeTree connection) {
79
+ auto mySubCount =
80
+ layout::size(layout::excludeRoute(std::move(mesh), connection.nodeId));
81
+ auto remoteSubCount = layout::size(connection);
82
+ if (mySubCount > remoteSubCount) return false;
83
+ if (mySubCount == remoteSubCount) {
84
+ if (connection.nodeId == 0)
85
+ Log(logger::ERROR, "Adopt called on uninitialized connection\n");
86
+ // TODO: there is a change here that a middle node also lower is than the
87
+ // two others and will start switching between both. Maybe should do it
88
+ // randomly instead?
89
+ return mesh.nodeId < connection.nodeId;
90
+ }
91
+ return true;
92
+ }
93
+
94
+ template <class T>
95
+ void initTimeSync(protocol::NodeTree mesh, std::shared_ptr<T> connection,
96
+ uint32_t nodeTime) {
97
+ using namespace painlessmesh::logger;
98
+ painlessmesh::protocol::TimeSync timeSync;
99
+ if (adopt(mesh, (*connection))) {
100
+ timeSync = painlessmesh::protocol::TimeSync(mesh.nodeId, connection->nodeId,
101
+ nodeTime);
102
+ Log(S_TIME, "initTimeSync(): Requesting time from %u\n",
103
+ connection->nodeId);
104
+ } else {
105
+ timeSync =
106
+ painlessmesh::protocol::TimeSync(mesh.nodeId, connection->nodeId);
107
+ Log(S_TIME, "initTimeSync(): Requesting %u to adopt our time\n",
108
+ connection->nodeId);
109
+ }
110
+
111
+ router::send<protocol::TimeSync, T>(timeSync, connection, true);
112
+ }
113
+
114
+ template <class T, class U>
115
+ void handleTimeSync(T& mesh, painlessmesh::protocol::TimeSync timeSync,
116
+ std::shared_ptr<U> conn, uint32_t receivedAt) {
117
+ switch (timeSync.msg.type) {
118
+ case (painlessmesh::protocol::TIME_SYNC_ERROR):
119
+ Log(logger::ERROR,
120
+ "handleTimeSync(): Received time sync error. Restarting time "
121
+ "sync.\n");
122
+ conn->timeSyncTask.forceNextIteration();
123
+ break;
124
+ case (painlessmesh::protocol::TIME_SYNC_REQUEST): // Other party request me
125
+ // to ask it for time
126
+ Log(logger::S_TIME,
127
+ "handleTimeSync(): Received requesto to start TimeSync with "
128
+ "node: %u\n",
129
+ conn->nodeId);
130
+ timeSync.reply(mesh.getNodeTime());
131
+ router::send<painlessmesh::protocol::TimeSync>(timeSync, conn, true);
132
+ break;
133
+
134
+ case (painlessmesh::protocol::TIME_REQUEST):
135
+ timeSync.reply(receivedAt, mesh.getNodeTime());
136
+ router::send<painlessmesh::protocol::TimeSync>(timeSync, conn, true);
137
+
138
+ Log(logger::S_TIME,
139
+ "handleTimeSync(): timeSyncStatus with %u completed\n", conn->nodeId);
140
+
141
+ // After response is sent I assume sync is completed
142
+ conn->timeSyncTask.delay(TIME_SYNC_INTERVAL);
143
+ break;
144
+
145
+ case (painlessmesh::protocol::TIME_REPLY): {
146
+ Log(logger::S_TIME,
147
+ "handleTimeSync(): %u adopting TIME_RESPONSE from %u\n", mesh.nodeId,
148
+ conn->nodeId);
149
+ int32_t offset = painlessmesh::ntp::clockOffset(
150
+ timeSync.msg.t0, timeSync.msg.t1, timeSync.msg.t2, receivedAt);
151
+ mesh.timeOffset += offset; // Accumulate offset
152
+
153
+ // flag all connections for re-timeSync
154
+ if (mesh.nodeTimeAdjustedCallback) {
155
+ mesh.nodeTimeAdjustedCallback(offset);
156
+ }
157
+
158
+ if ((offset < TIME_SYNC_ACCURACY) && (offset > -TIME_SYNC_ACCURACY) &&
159
+ (offset != 0)) {
160
+ // mark complete only if offset was less than 10 ms
161
+ conn->timeSyncTask.delay(TIME_SYNC_INTERVAL);
162
+ Log(logger::S_TIME,
163
+ "handleTimeSync(): timeSyncStatus with %u completed\n",
164
+ conn->nodeId);
165
+
166
+ // Time has changed, update other nodes
167
+ for (auto&& connection : mesh.subs) {
168
+ if (connection->nodeId != conn->nodeId) { // exclude this connection
169
+ connection->timeSyncTask.forceNextIteration();
170
+ Log(logger::S_TIME,
171
+ "handleTimeSync(): timeSyncStatus with %u brought forward\n",
172
+ connection->nodeId);
173
+ }
174
+ }
175
+ } else {
176
+ // Iterate sync procedure if accuracy was not enough
177
+ conn->timeSyncTask.delay(200 * TASK_MILLISECOND); // Small delay
178
+ Log(logger::S_TIME,
179
+ "handleTimeSync(): timeSyncStatus with %u needs further tries\n",
180
+ conn->nodeId);
181
+ }
182
+ break;
183
+ }
184
+ default:
185
+ Log(logger::ERROR, "handleTimeSync(): unkown type %u, %u\n",
186
+ timeSync.msg.type, painlessmesh::protocol::TIME_SYNC_REQUEST);
187
+ break;
188
+ }
189
+ Log(logger::S_TIME, "handleTimeSync(): ----------------------------------\n");
190
+ }
191
+
192
+ template <class T, class U>
193
+ void handleTimeDelay(T& mesh, painlessmesh::protocol::TimeDelay timeDelay,
194
+ std::shared_ptr<U> conn, uint32_t receivedAt) {
195
+ Log(logger::S_TIME, "handleTimeDelay(): from %u in timestamp\n",
196
+ timeDelay.from);
197
+
198
+ switch (timeDelay.msg.type) {
199
+ case (painlessmesh::protocol::TIME_SYNC_ERROR):
200
+ Log(logger::ERROR,
201
+ "handleTimeDelay(): Error in requesting time delay. Please try "
202
+ "again.\n");
203
+ break;
204
+
205
+ case (painlessmesh::protocol::TIME_REQUEST):
206
+ // conn->timeSyncStatus == IN_PROGRESS;
207
+ Log(logger::S_TIME, "handleTimeDelay(): TIME REQUEST received.\n");
208
+
209
+ // Build time response
210
+ timeDelay.reply(receivedAt, mesh.getNodeTime());
211
+ router::send<protocol::TimeDelay, U>(timeDelay, conn);
212
+ break;
213
+
214
+ case (painlessmesh::protocol::TIME_REPLY): {
215
+ Log(logger::S_TIME, "handleTimeDelay(): TIME RESPONSE received.\n");
216
+ int32_t delay = painlessmesh::ntp::tripDelay(
217
+ timeDelay.msg.t0, timeDelay.msg.t1, timeDelay.msg.t2, receivedAt);
218
+ Log(logger::S_TIME, "handleTimeDelay(): Delay is %d\n", delay);
219
+
220
+ // conn->timeSyncStatus == COMPLETE;
221
+
222
+ if (mesh.nodeDelayReceivedCallback)
223
+ mesh.nodeDelayReceivedCallback(timeDelay.from, delay);
224
+ } break;
225
+
226
+ default:
227
+ Log(logger::ERROR,
228
+ "handleTimeDelay(): Unknown timeSyncMessageType received. Ignoring "
229
+ "for now.\n");
230
+ }
231
+
232
+ Log(logger::S_TIME, "handleTimeSync(): ----------------------------------\n");
233
+ }
234
+
235
+ template <class T, typename U>
236
+ callback::MeshPackageCallbackList<U> addPackageCallback(
237
+ callback::MeshPackageCallbackList<U>&& callbackList, T& mesh) {
238
+ // TimeSync
239
+ callbackList.onPackage(
240
+ protocol::TIME_SYNC,
241
+ [&mesh](protocol::Variant& variant, std::shared_ptr<U> connection,
242
+ uint32_t receivedAt) {
243
+ auto timeSync = variant.to<protocol::TimeSync>();
244
+ handleTimeSync<T, U>(mesh, timeSync, connection, receivedAt);
245
+ return false;
246
+ });
247
+
248
+ // TimeDelay
249
+ callbackList.onPackage(
250
+ protocol::TIME_DELAY,
251
+ [&mesh](protocol::Variant& variant, std::shared_ptr<U> connection,
252
+ uint32_t receivedAt) {
253
+ auto timeDelay = variant.to<protocol::TimeDelay>();
254
+ handleTimeDelay<T, U>(mesh, timeDelay, connection, receivedAt);
255
+ return false;
256
+ });
257
+
258
+ return callbackList;
259
+ }
260
+
261
+ } // namespace ntp
262
+ } // namespace painlessmesh
263
+ #endif