@alteriom/painlessmesh 1.8.2 → 1.8.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.
- package/CHANGELOG.md +32 -0
- package/README.md +62 -11
- package/RELEASE_GUIDE.md +57 -16
- package/docs/ARDUINO_LIBRARY_MANAGER_SUBMISSION.md +331 -0
- package/docs/features/DIAGNOSTICS_API.md +534 -0
- package/docs/getting-started/arduino-manual-install.md +313 -0
- package/docs/implementation/BRIDGE_ARCHITECTURE_IMPLEMENTATION.md +340 -0
- package/docs/implementation/BRIDGE_HEALTH_MONITORING_IMPLEMENTATION.md +213 -0
- package/docs/implementation/BRIDGE_STATUS_FEATURE.md +635 -0
- package/docs/implementation/DIAGNOSTICS_API_IMPLEMENTATION.md +232 -0
- package/docs/implementation/IMPLEMENTATION_COMPLETE.md +228 -0
- package/docs/implementation/IMPLEMENTATION_NTP_TIME_SYNC.md +325 -0
- package/docs/implementation/IMPLEMENTATION_SUMMARY.md +316 -0
- package/docs/implementation/MESSAGE_QUEUE_IMPLEMENTATION.md +405 -0
- package/docs/implementation/MULTI_BRIDGE_IMPLEMENTATION.md +520 -0
- package/docs/implementation/NTP_TIME_SYNC_FEATURE.md +392 -0
- package/docs/internal/CUSTOM_AGENT_ANALYSIS.md +391 -0
- package/docs/internal/ISSUE_65_VERIFICATION.md +947 -0
- package/docs/internal/ISSUE_66_CLOSURE.md +249 -0
- package/docs/internal/ISSUE_66_STATUS.md +316 -0
- package/docs/internal/PR_SUMMARY.md +315 -0
- package/docs/internal/REVIEW_SUMMARY.md +332 -0
- package/docs/releases/PUBLISH_v1.8.0_INSTRUCTIONS.md +163 -0
- package/docs/releases/QUICK_START_RELEASES.md +113 -0
- package/docs/releases/RELEASE_CHECKLIST_v1.8.0.md +331 -0
- package/docs/releases/RELEASE_CHECKLIST_v1.8.2.md +309 -0
- package/docs/releases/RELEASE_NOTES_v1.8.0.md +685 -0
- package/docs/releases/RELEASE_NOTES_v1.8.1.md +221 -0
- package/docs/releases/RELEASE_NOTES_v1.8.2.md +421 -0
- package/docs/releases/RELEASE_NOTES_v1.8.3.md +292 -0
- package/docs/troubleshooting/ARDUINO_IDE_VERSION_FIX_SUMMARY.md +229 -0
- package/docs/troubleshooting/ARDUINO_LIBRARY_NAME_FIX.md +197 -0
- package/docs/troubleshooting/NPM_PUBLISHING_ISSUE_SUMMARY.md +110 -0
- package/docs/troubleshooting/station-reconnection-issues.md +172 -0
- package/examples/priority/README.md +274 -0
- package/examples/priority/priority_basic_example.ino +115 -0
- package/examples/priority/priority_with_queue.ino +249 -0
- package/examples/routing_demo/README.md +172 -0
- package/examples/routing_demo/routing_demo.ino +102 -0
- package/library.json +1 -1
- package/library.properties +3 -3
- package/package.json +1 -1
- package/src/arduino/wifi.hpp +49 -16
- package/src/painlessMesh.h +15 -0
- package/src/painlessMeshSTA.cpp +7 -1
- package/src/painlessmesh/buffer.hpp +218 -37
- package/src/painlessmesh/connection.hpp +21 -1
- package/src/painlessmesh/mesh.hpp +253 -19
- package/src/painlessmesh/router.hpp +31 -0
|
@@ -0,0 +1,392 @@
|
|
|
1
|
+
# NTP Time Synchronization Feature
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
|
|
5
|
+
The NTP Time Synchronization feature enables bridge nodes with Internet connectivity to distribute authoritative NTP time to all nodes in the mesh network. This eliminates the need for each node to query NTP servers individually, saving bandwidth, power, and reducing network congestion.
|
|
6
|
+
|
|
7
|
+
## Type ID: 614 (TIME_SYNC_NTP)
|
|
8
|
+
|
|
9
|
+
## Architecture
|
|
10
|
+
|
|
11
|
+
```
|
|
12
|
+
Internet
|
|
13
|
+
|
|
|
14
|
+
| NTP Query
|
|
15
|
+
v
|
|
16
|
+
Bridge Node ---------> Regular Node 1
|
|
17
|
+
| |
|
|
18
|
+
| Broadcast | Update Time
|
|
19
|
+
| Type 614 | Sync RTC
|
|
20
|
+
| |
|
|
21
|
+
+----------------> Regular Node 2
|
|
22
|
+
| |
|
|
23
|
+
+----------------> Regular Node 3
|
|
24
|
+
|
|
|
25
|
+
v
|
|
26
|
+
Application Code
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## Package Structure
|
|
30
|
+
|
|
31
|
+
### NTPTimeSyncPackage (Type 614)
|
|
32
|
+
|
|
33
|
+
```cpp
|
|
34
|
+
class NTPTimeSyncPackage : public painlessmesh::plugin::BroadcastPackage {
|
|
35
|
+
public:
|
|
36
|
+
uint32_t ntpTime = 0; // Unix timestamp from NTP server (seconds)
|
|
37
|
+
uint16_t accuracy = 0; // Milliseconds uncertainty/precision
|
|
38
|
+
TSTRING source = ""; // NTP server source (e.g., "pool.ntp.org")
|
|
39
|
+
uint32_t timestamp = 0; // Collection timestamp (millis())
|
|
40
|
+
uint16_t messageType = 614; // MQTT Schema message_type
|
|
41
|
+
};
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
### JSON Format
|
|
45
|
+
|
|
46
|
+
```json
|
|
47
|
+
{
|
|
48
|
+
"type": 614,
|
|
49
|
+
"from": 123456,
|
|
50
|
+
"routing": 2,
|
|
51
|
+
"ntpTime": 1699564800,
|
|
52
|
+
"accuracy": 50,
|
|
53
|
+
"source": "pool.ntp.org",
|
|
54
|
+
"timestamp": 12345678,
|
|
55
|
+
"message_type": 614
|
|
56
|
+
}
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
## Implementation Guide
|
|
60
|
+
|
|
61
|
+
### Bridge Node (Sender)
|
|
62
|
+
|
|
63
|
+
The bridge node queries NTP and broadcasts time to the mesh:
|
|
64
|
+
|
|
65
|
+
```cpp
|
|
66
|
+
#include "painlessMesh.h"
|
|
67
|
+
#include "examples/alteriom/alteriom_sensor_package.hpp"
|
|
68
|
+
|
|
69
|
+
using namespace alteriom;
|
|
70
|
+
|
|
71
|
+
painlessMesh mesh;
|
|
72
|
+
|
|
73
|
+
// Periodic task to broadcast NTP time
|
|
74
|
+
Task taskBroadcastNTP(60000, TASK_FOREVER, [](){
|
|
75
|
+
auto pkg = NTPTimeSyncPackage();
|
|
76
|
+
pkg.from = mesh.getNodeId();
|
|
77
|
+
pkg.ntpTime = mesh.getNodeTime() / 1000000; // Convert to seconds
|
|
78
|
+
pkg.accuracy = 50; // 50ms uncertainty
|
|
79
|
+
pkg.source = "pool.ntp.org";
|
|
80
|
+
pkg.timestamp = millis();
|
|
81
|
+
|
|
82
|
+
mesh.sendBroadcast(pkg.toJson());
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
void setup() {
|
|
86
|
+
mesh.init(MESH_PREFIX, MESH_PASSWORD, &userScheduler, MESH_PORT);
|
|
87
|
+
mesh.stationManual(ROUTER_SSID, ROUTER_PASSWORD); // Bridge mode
|
|
88
|
+
|
|
89
|
+
userScheduler.addTask(taskBroadcastNTP);
|
|
90
|
+
taskBroadcastNTP.enable();
|
|
91
|
+
}
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
### Regular Node (Receiver)
|
|
95
|
+
|
|
96
|
+
Regular nodes receive and apply NTP time:
|
|
97
|
+
|
|
98
|
+
```cpp
|
|
99
|
+
#include "painlessMesh.h"
|
|
100
|
+
#include "examples/alteriom/alteriom_sensor_package.hpp"
|
|
101
|
+
|
|
102
|
+
using namespace alteriom;
|
|
103
|
+
|
|
104
|
+
void receivedCallback(uint32_t from, String& msg) {
|
|
105
|
+
DynamicJsonDocument doc(1024);
|
|
106
|
+
deserializeJson(doc, msg);
|
|
107
|
+
JsonObject obj = doc.as<JsonObject>();
|
|
108
|
+
|
|
109
|
+
if (obj["type"] == 614) {
|
|
110
|
+
auto pkg = NTPTimeSyncPackage(obj);
|
|
111
|
+
|
|
112
|
+
// Verify sender is a bridge (optional but recommended)
|
|
113
|
+
if (isBridgeNode(from)) {
|
|
114
|
+
// Apply time synchronization
|
|
115
|
+
mesh.setTimeFromNTP(pkg.ntpTime);
|
|
116
|
+
|
|
117
|
+
// Optional: Sync RTC module if available
|
|
118
|
+
#ifdef HAS_RTC
|
|
119
|
+
rtc.setTime(pkg.ntpTime);
|
|
120
|
+
#endif
|
|
121
|
+
|
|
122
|
+
Serial.printf("Time synced from %s (±%ums)\n",
|
|
123
|
+
pkg.source.c_str(), pkg.accuracy);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
void setup() {
|
|
129
|
+
mesh.init(MESH_PREFIX, MESH_PASSWORD, &userScheduler, MESH_PORT);
|
|
130
|
+
mesh.onReceive(&receivedCallback);
|
|
131
|
+
}
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
## Field Details
|
|
135
|
+
|
|
136
|
+
### ntpTime (uint32_t)
|
|
137
|
+
- Unix timestamp in seconds since epoch (1970-01-01 00:00:00 UTC)
|
|
138
|
+
- Range: 0 to 4,294,967,295 (year 2106)
|
|
139
|
+
- Example: 1699564800 = 2023-11-09 20:00:00 UTC
|
|
140
|
+
|
|
141
|
+
### accuracy (uint16_t)
|
|
142
|
+
- Time uncertainty in milliseconds
|
|
143
|
+
- Range: 0 to 65,535ms (0 to 65.5 seconds)
|
|
144
|
+
- Typical values:
|
|
145
|
+
- 10-50ms: Good NTP connection
|
|
146
|
+
- 50-200ms: Average NTP connection
|
|
147
|
+
- 200-1000ms: Poor/distant NTP server
|
|
148
|
+
- 1000-5000ms: Very poor connection or local time source
|
|
149
|
+
|
|
150
|
+
### source (TSTRING)
|
|
151
|
+
- NTP server hostname or IP address
|
|
152
|
+
- Examples:
|
|
153
|
+
- "pool.ntp.org"
|
|
154
|
+
- "time.google.com"
|
|
155
|
+
- "time.nist.gov"
|
|
156
|
+
- "192.168.1.1" (local router)
|
|
157
|
+
- Empty string if no NTP available
|
|
158
|
+
|
|
159
|
+
### timestamp (uint32_t)
|
|
160
|
+
- When the time was collected/broadcast (millis())
|
|
161
|
+
- Used for calculating staleness of time data
|
|
162
|
+
- Wraps around every 49.7 days
|
|
163
|
+
|
|
164
|
+
## Best Practices
|
|
165
|
+
|
|
166
|
+
### For Bridge Nodes
|
|
167
|
+
|
|
168
|
+
1. **Update Frequency**: Broadcast every 30-60 seconds
|
|
169
|
+
- Too frequent: Wastes bandwidth
|
|
170
|
+
- Too infrequent: Nodes may drift
|
|
171
|
+
|
|
172
|
+
2. **NTP Query Strategy**: Query NTP less frequently than broadcast
|
|
173
|
+
- Query NTP every 5-15 minutes
|
|
174
|
+
- Cache and reuse recent NTP time
|
|
175
|
+
- Update accuracy field based on staleness
|
|
176
|
+
|
|
177
|
+
3. **Error Handling**: Handle NTP failures gracefully
|
|
178
|
+
- Stop broadcasting if NTP unavailable for >15 minutes
|
|
179
|
+
- Or increase accuracy value to indicate uncertainty
|
|
180
|
+
|
|
181
|
+
### For Regular Nodes
|
|
182
|
+
|
|
183
|
+
1. **Validation**: Verify sender is bridge before applying time
|
|
184
|
+
```cpp
|
|
185
|
+
bool isBridgeNode(uint32_t nodeId) {
|
|
186
|
+
// Check if node is in bridge list
|
|
187
|
+
// Or check if node has Internet connectivity flag
|
|
188
|
+
}
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
2. **Staleness Check**: Don't apply very old time data
|
|
192
|
+
```cpp
|
|
193
|
+
uint32_t age = millis() - pkg.timestamp;
|
|
194
|
+
if (age > 120000) { // Older than 2 minutes
|
|
195
|
+
Serial.println("Time data too old, ignoring");
|
|
196
|
+
return;
|
|
197
|
+
}
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
3. **Accuracy Threshold**: Only apply if accuracy is acceptable
|
|
201
|
+
```cpp
|
|
202
|
+
if (pkg.accuracy > 1000) { // Worse than 1 second
|
|
203
|
+
Serial.println("Time accuracy too poor, ignoring");
|
|
204
|
+
return;
|
|
205
|
+
}
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
4. **RTC Integration**: Sync RTC for offline operation
|
|
209
|
+
```cpp
|
|
210
|
+
if (rtcEnabled && pkg.accuracy < 500) {
|
|
211
|
+
rtc.setTime(pkg.ntpTime);
|
|
212
|
+
Serial.println("RTC synced with NTP time");
|
|
213
|
+
}
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
## Benefits
|
|
217
|
+
|
|
218
|
+
### Network Efficiency
|
|
219
|
+
- **Reduced NTP queries**: Only bridge queries NTP, not every node
|
|
220
|
+
- **Lower bandwidth**: One NTP query serves entire mesh
|
|
221
|
+
- **Less congestion**: Fewer nodes competing for Internet access
|
|
222
|
+
|
|
223
|
+
### Power Savings
|
|
224
|
+
- **No WiFi switching**: Nodes stay on mesh, don't need router connection
|
|
225
|
+
- **Lower power**: No NTP protocol overhead per node
|
|
226
|
+
- **Longer battery life**: Especially important for sensor nodes
|
|
227
|
+
|
|
228
|
+
### Improved Accuracy
|
|
229
|
+
- **Authoritative source**: Bridge has better NTP access than distant nodes
|
|
230
|
+
- **Lower latency**: Mesh broadcast faster than Internet NTP
|
|
231
|
+
- **Better synchronization**: All nodes sync to same time source
|
|
232
|
+
|
|
233
|
+
### Offline Operation
|
|
234
|
+
- **RTC sync**: Nodes can sync RTC modules for offline timekeeping
|
|
235
|
+
- **Time continuity**: Time available even when bridge loses Internet
|
|
236
|
+
- **Graceful degradation**: Mesh continues with cached time
|
|
237
|
+
|
|
238
|
+
## Security Considerations
|
|
239
|
+
|
|
240
|
+
### Trust and Validation
|
|
241
|
+
|
|
242
|
+
1. **Bridge Authentication**: Verify time source is trusted bridge
|
|
243
|
+
- Use bridge node whitelist
|
|
244
|
+
- Check sender's bridge status flag
|
|
245
|
+
- Validate against multiple bridges if available
|
|
246
|
+
|
|
247
|
+
2. **Replay Attack Prevention**: Check timestamp freshness
|
|
248
|
+
```cpp
|
|
249
|
+
static uint32_t lastTimestamp = 0;
|
|
250
|
+
if (pkg.timestamp <= lastTimestamp) {
|
|
251
|
+
// Potential replay attack or clock rollback
|
|
252
|
+
return;
|
|
253
|
+
}
|
|
254
|
+
lastTimestamp = pkg.timestamp;
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
3. **Sanity Checks**: Validate time is reasonable
|
|
258
|
+
```cpp
|
|
259
|
+
const uint32_t MIN_TIME = 1609459200; // 2021-01-01
|
|
260
|
+
const uint32_t MAX_TIME = 2147483647; // 2038-01-19
|
|
261
|
+
|
|
262
|
+
if (pkg.ntpTime < MIN_TIME || pkg.ntpTime > MAX_TIME) {
|
|
263
|
+
Serial.println("Time out of valid range");
|
|
264
|
+
return;
|
|
265
|
+
}
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
4. **Large Jump Detection**: Reject suspicious time changes
|
|
269
|
+
```cpp
|
|
270
|
+
uint32_t currentTime = getCurrentTime();
|
|
271
|
+
int32_t timeDelta = pkg.ntpTime - currentTime;
|
|
272
|
+
|
|
273
|
+
if (abs(timeDelta) > 86400) { // More than 1 day
|
|
274
|
+
Serial.println("Time change too large, manual intervention needed");
|
|
275
|
+
return;
|
|
276
|
+
}
|
|
277
|
+
```
|
|
278
|
+
|
|
279
|
+
## Example Applications
|
|
280
|
+
|
|
281
|
+
### Sensor Network
|
|
282
|
+
- All sensors use synchronized timestamps
|
|
283
|
+
- Data correlation across nodes is accurate
|
|
284
|
+
- Event ordering is consistent
|
|
285
|
+
|
|
286
|
+
### Coordinated Actions
|
|
287
|
+
- Multiple nodes can trigger actions at specific times
|
|
288
|
+
- Light shows with precise timing
|
|
289
|
+
- Scheduled operations across mesh
|
|
290
|
+
|
|
291
|
+
### Data Logging
|
|
292
|
+
- Consistent timestamps for all log entries
|
|
293
|
+
- Time-series data from multiple sources can be merged
|
|
294
|
+
- Historical analysis is accurate
|
|
295
|
+
|
|
296
|
+
### Security Systems
|
|
297
|
+
- Event timestamps are reliable for audit logs
|
|
298
|
+
- Video/sensor correlation across devices
|
|
299
|
+
- Alarm scheduling with accurate time
|
|
300
|
+
|
|
301
|
+
## Testing
|
|
302
|
+
|
|
303
|
+
### Unit Tests
|
|
304
|
+
|
|
305
|
+
The feature includes comprehensive unit tests in `test/catch/catch_alteriom_packages.cpp`:
|
|
306
|
+
|
|
307
|
+
- Basic serialization/deserialization
|
|
308
|
+
- JSON field validation
|
|
309
|
+
- Different NTP sources
|
|
310
|
+
- Edge cases (0 values, max values)
|
|
311
|
+
- Long source hostnames
|
|
312
|
+
- High accuracy values
|
|
313
|
+
|
|
314
|
+
Run tests:
|
|
315
|
+
```bash
|
|
316
|
+
cd painlessMesh
|
|
317
|
+
cmake -G Ninja .
|
|
318
|
+
ninja
|
|
319
|
+
./bin/catch_alteriom_packages
|
|
320
|
+
```
|
|
321
|
+
|
|
322
|
+
### Integration Testing
|
|
323
|
+
|
|
324
|
+
Test with example sketches:
|
|
325
|
+
|
|
326
|
+
1. Upload `ntpTimeSyncBridge.ino` to bridge node with Internet
|
|
327
|
+
2. Upload `ntpTimeSyncNode.ino` to regular mesh nodes
|
|
328
|
+
3. Monitor serial output to verify:
|
|
329
|
+
- Bridge broadcasts NTP time
|
|
330
|
+
- Nodes receive and apply time
|
|
331
|
+
- Timestamps are consistent
|
|
332
|
+
|
|
333
|
+
## Troubleshooting
|
|
334
|
+
|
|
335
|
+
### Bridge Not Broadcasting
|
|
336
|
+
|
|
337
|
+
**Symptoms**: No NTP broadcasts seen by nodes
|
|
338
|
+
|
|
339
|
+
**Solutions**:
|
|
340
|
+
1. Check Internet connectivity: `ping 8.8.8.8`
|
|
341
|
+
2. Verify NTP server is reachable
|
|
342
|
+
3. Check broadcast task is enabled
|
|
343
|
+
4. Increase debug level: `mesh.setDebugMsgTypes(ERROR | STARTUP | CONNECTION)`
|
|
344
|
+
|
|
345
|
+
### Nodes Not Receiving Time
|
|
346
|
+
|
|
347
|
+
**Symptoms**: Nodes connected but not syncing time
|
|
348
|
+
|
|
349
|
+
**Solutions**:
|
|
350
|
+
1. Verify receiver callback is registered: `mesh.onReceive(&receivedCallback)`
|
|
351
|
+
2. Check message type parsing: `obj["type"] == 614`
|
|
352
|
+
3. Ensure JSON buffer is large enough: `DynamicJsonDocument doc(1024)`
|
|
353
|
+
4. Monitor for JSON parse errors
|
|
354
|
+
|
|
355
|
+
### Poor Time Accuracy
|
|
356
|
+
|
|
357
|
+
**Symptoms**: High accuracy values (>500ms)
|
|
358
|
+
|
|
359
|
+
**Solutions**:
|
|
360
|
+
1. Use closer NTP server (local or regional)
|
|
361
|
+
2. Check network latency to NTP server
|
|
362
|
+
3. Reduce NTP query frequency
|
|
363
|
+
4. Consider using multiple NTP sources and averaging
|
|
364
|
+
|
|
365
|
+
### Time Drift
|
|
366
|
+
|
|
367
|
+
**Symptoms**: Time gradually becomes inaccurate
|
|
368
|
+
|
|
369
|
+
**Solutions**:
|
|
370
|
+
1. Increase broadcast frequency (30-60 seconds)
|
|
371
|
+
2. Verify NTP queries are successful
|
|
372
|
+
3. Check for mesh network stability issues
|
|
373
|
+
4. Use RTC module for drift compensation
|
|
374
|
+
|
|
375
|
+
## Related Features
|
|
376
|
+
|
|
377
|
+
- **Bridge Status (Type 610)**: Indicates bridge connectivity status
|
|
378
|
+
- **RTC Integration**: Offline timekeeping when NTP unavailable
|
|
379
|
+
- **Mesh Time Sync**: Built-in mesh time synchronization protocol
|
|
380
|
+
|
|
381
|
+
## Version History
|
|
382
|
+
|
|
383
|
+
- **v1.8.1**: Initial implementation of NTP time sync feature
|
|
384
|
+
- Type ID 614 allocated for TIME_SYNC_NTP
|
|
385
|
+
- Examples and documentation added
|
|
386
|
+
|
|
387
|
+
## References
|
|
388
|
+
|
|
389
|
+
- Issue: Enhancement: Bridge-to-Mesh NTP Time Distribution
|
|
390
|
+
- Examples: `examples/ntpTimeSyncBridge/` and `examples/ntpTimeSyncNode/`
|
|
391
|
+
- Tests: `test/catch/catch_alteriom_packages.cpp`
|
|
392
|
+
- Package: `examples/alteriom/alteriom_sensor_package.hpp`
|