@alteriom/painlessmesh 1.7.8 → 1.8.0

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 (59) hide show
  1. package/CHANGELOG.md +139 -3
  2. package/README.md +114 -4
  3. package/RELEASE_GUIDE.md +57 -8
  4. package/docs/BRIDGE_FAILOVER.md +512 -0
  5. package/docs/BRIDGE_HEALTH_MONITORING.md +293 -0
  6. package/docs/CREATE_MISSING_RELEASES.md +321 -0
  7. package/docs/README.md +2 -1
  8. package/docs/RELEASE_AGENT_SUMMARY.md +386 -0
  9. package/docs/releases/RELEASE_SUMMARY_v1.7.8.md +523 -0
  10. package/docs/releases/RELEASE_SUMMARY_v1.7.9.md +542 -0
  11. package/docs/troubleshooting/ESP32_C6_COMPATIBILITY.md +157 -0
  12. package/docs/troubleshooting/common-issues.md +28 -0
  13. package/examples/alteriom/alteriom_sensor_package.hpp +233 -1
  14. package/examples/alteriom/platformio.ini +1 -1
  15. package/examples/alteriomImproved/platformio.ini +1 -1
  16. package/examples/alteriomMetricsHealth/metrics_health_node.ino +18 -7
  17. package/examples/alteriomMetricsHealth/platformio.ini +1 -1
  18. package/examples/alteriomPhase1/platformio.ini +1 -1
  19. package/examples/alteriomPhase2/platformio.ini +1 -1
  20. package/examples/alteriomSensorNode/alteriom_sensor_package.hpp +1014 -11
  21. package/examples/alteriomSensorNode/platformio.ini +1 -1
  22. package/examples/basic/basic.ino +6 -2
  23. package/examples/basic/platformio.ini +1 -1
  24. package/examples/bridge/alteriom_sensor_package.hpp +1170 -0
  25. package/examples/bridge/bridge.ino +44 -23
  26. package/examples/bridge/bridge_health_monitoring_example.ino +188 -0
  27. package/examples/bridge/enhanced_mqtt_bridge.hpp +1 -1
  28. package/examples/bridge/mqtt_command_bridge.hpp +2 -2
  29. package/examples/bridge/platformio.ini +2 -1
  30. package/examples/bridgeAwareSensorNode/alteriom_sensor_package.hpp +1227 -0
  31. package/examples/bridgeAwareSensorNode/bridgeAwareSensorNode.ino +343 -0
  32. package/examples/bridgeAwareSensorNode/platformio.ini +26 -0
  33. package/examples/bridge_failover/README.md +358 -0
  34. package/examples/bridge_failover/bridge_failover.ino +180 -0
  35. package/examples/bridge_failover/platformio.ini +27 -0
  36. package/examples/diagnosticsExample/diagnosticsExample.ino +171 -0
  37. package/examples/diagnosticsExample/platformio.ini +26 -0
  38. package/examples/echoNode/platformio.ini +1 -1
  39. package/examples/logClient/platformio.ini +1 -1
  40. package/examples/logServer/platformio.ini +1 -1
  41. package/examples/mqttStatusBridge/platformio.ini +1 -1
  42. package/examples/namedMesh/platformio.ini +1 -1
  43. package/examples/ntpTimeSyncBridge/alteriom_sensor_package.hpp +1383 -0
  44. package/examples/ntpTimeSyncBridge/ntpTimeSyncBridge.ino +81 -0
  45. package/examples/ntpTimeSyncNode/alteriom_sensor_package.hpp +1383 -0
  46. package/examples/ntpTimeSyncNode/ntpTimeSyncNode.ino +109 -0
  47. package/examples/otaReceiver/platformio.ini +1 -1
  48. package/examples/rtcIntegration/README.md +235 -0
  49. package/examples/rtcIntegration/rtcIntegration.ino +196 -0
  50. package/examples/startHere/platformio.ini +1 -1
  51. package/examples/webServer/platformio.ini +1 -1
  52. package/library.json +93 -53
  53. package/library.properties +1 -1
  54. package/package.json +2 -2
  55. package/src/arduino/wifi.hpp +581 -0
  56. package/src/painlessMeshSTA.cpp +68 -0
  57. package/src/painlessMeshSTA.h +3 -0
  58. package/src/painlessmesh/mesh.hpp +1127 -4
  59. package/src/painlessmesh/rtc.hpp +203 -0
@@ -0,0 +1,512 @@
1
+ # Bridge Failover with RSSI-Based Election
2
+
3
+ ## Overview
4
+
5
+ Bridge failover enables painlessMesh networks to automatically recover from bridge node failures by electing a new bridge based on router signal strength (RSSI). This ensures continuous Internet connectivity for critical applications like fish farm monitoring, industrial IoT, and smart building systems.
6
+
7
+ ## Problem Statement
8
+
9
+ In a typical mesh network, the bridge node connecting to the Internet represents a single point of failure:
10
+
11
+ - **Bridge goes offline**: Entire mesh loses Internet access
12
+ - **Bridge loses Internet**: Gateway unavailable for data upload
13
+ - **Manual recovery**: Requires human intervention to restore connectivity
14
+
15
+ Without automatic failover, critical systems can miss alarms, lose sensor data, or fail to respond to urgent conditions.
16
+
17
+ ## Solution: Distributed Bridge Election
18
+
19
+ painlessMesh implements a distributed consensus protocol that:
20
+
21
+ 1. **Detects failures**: Monitors bridge heartbeats (Type 610 status broadcasts)
22
+ 2. **Triggers elections**: Starts election when primary bridge fails
23
+ 3. **Selects winner**: Deterministically chooses node with best router signal
24
+ 4. **Promotes bridge**: Winner automatically becomes new bridge
25
+ 5. **Announces takeover**: Informs mesh of new bridge
26
+
27
+ ## Architecture
28
+
29
+ ### Message Types
30
+
31
+ #### Type 610: BRIDGE_STATUS (Existing)
32
+ Bridge nodes broadcast their status every 30 seconds:
33
+ ```json
34
+ {
35
+ "type": 610,
36
+ "from": 1234567890,
37
+ "routing": 2,
38
+ "internetConnected": true,
39
+ "routerRSSI": -42,
40
+ "routerChannel": 6,
41
+ "uptime": 3600000,
42
+ "gatewayIP": "192.168.1.1",
43
+ "timestamp": 1609459200
44
+ }
45
+ ```
46
+
47
+ #### Type 611: BRIDGE_ELECTION (New)
48
+ Candidates broadcast their router signal strength:
49
+ ```json
50
+ {
51
+ "type": 611,
52
+ "from": 2886734890,
53
+ "routing": 2,
54
+ "routerRSSI": -35,
55
+ "uptime": 3600000,
56
+ "freeMemory": 150000,
57
+ "timestamp": 1609459300,
58
+ "routerSSID": "MyRouter"
59
+ }
60
+ ```
61
+
62
+ #### Type 612: BRIDGE_TAKEOVER (New)
63
+ Winner announces bridge role assumption:
64
+ ```json
65
+ {
66
+ "type": 612,
67
+ "from": 2886734890,
68
+ "routing": 2,
69
+ "previousBridge": 1234567890,
70
+ "reason": "Election winner - best router signal",
71
+ "routerRSSI": -35,
72
+ "timestamp": 1609459400
73
+ }
74
+ ```
75
+
76
+ ### Election Protocol
77
+
78
+ ```
79
+ ┌──────────────────────────────────────────────────────┐
80
+ │ NORMAL OPERATION │
81
+ │ Bridge broadcasts status every 30s (Type 610) │
82
+ └──────────────────────────────────────────────────────┘
83
+
84
+ Bridge fails
85
+ (60s timeout)
86
+
87
+ ┌──────────────────────────────────────────────────────┐
88
+ │ ELECTION PHASE (5 seconds) │
89
+ │ 1. Each node scans for router RSSI │
90
+ │ 2. Broadcasts candidacy (Type 611) │
91
+ │ 3. Collects all candidates │
92
+ └──────────────────────────────────────────────────────┘
93
+
94
+ ┌──────────────────────────────────────────────────────┐
95
+ │ EVALUATION PHASE (Instant) │
96
+ │ All nodes independently evaluate candidates using: │
97
+ │ 1. Best RSSI wins │
98
+ │ 2. Tiebreaker: Highest uptime │
99
+ │ 3. Tiebreaker: Most free memory │
100
+ │ 4. Tiebreaker: Lowest node ID │
101
+ └──────────────────────────────────────────────────────┘
102
+
103
+ ┌──────────────────────────────────────────────────────┐
104
+ │ PROMOTION PHASE (5 seconds) │
105
+ │ Winner: │
106
+ │ 1. Calls initAsBridge() │
107
+ │ 2. Connects to router │
108
+ │ 3. Broadcasts takeover (Type 612) │
109
+ └──────────────────────────────────────────────────────┘
110
+
111
+ ┌──────────────────────────────────────────────────────┐
112
+ │ NEW NORMAL OPERATION │
113
+ │ New bridge broadcasts status (Type 610) │
114
+ └──────────────────────────────────────────────────────┘
115
+ ```
116
+
117
+ ### Winner Selection Algorithm
118
+
119
+ All nodes execute identical deterministic evaluation:
120
+
121
+ ```cpp
122
+ BridgeCandidate* winner = nullptr;
123
+ int8_t bestRSSI = -127;
124
+
125
+ for (auto& candidate : candidates) {
126
+ if (candidate.routerRSSI > bestRSSI) {
127
+ // Better signal strength
128
+ bestRSSI = candidate.routerRSSI;
129
+ winner = &candidate;
130
+ } else if (candidate.routerRSSI == bestRSSI) {
131
+ // Tiebreaker 1: Higher uptime (more stable)
132
+ if (candidate.uptime > winner->uptime) {
133
+ winner = &candidate;
134
+ } else if (candidate.uptime == winner->uptime) {
135
+ // Tiebreaker 2: More memory (more capable)
136
+ if (candidate.freeMemory > winner->freeMemory) {
137
+ winner = &candidate;
138
+ } else if (candidate.freeMemory == winner->freeMemory) {
139
+ // Tiebreaker 3: Lower node ID (deterministic)
140
+ if (candidate.nodeId < winner->nodeId) {
141
+ winner = &candidate;
142
+ }
143
+ }
144
+ }
145
+ }
146
+ }
147
+ ```
148
+
149
+ ## API Reference
150
+
151
+ ### Configuration
152
+
153
+ ```cpp
154
+ // Set router credentials (required for election participation)
155
+ mesh.setRouterCredentials(ROUTER_SSID, ROUTER_PASSWORD);
156
+
157
+ // Enable automatic failover (default: enabled)
158
+ mesh.enableBridgeFailover(true);
159
+
160
+ // Set election timeout in milliseconds (default: 5000)
161
+ mesh.setElectionTimeout(5000);
162
+
163
+ // Set bridge timeout for failure detection (default: 60000)
164
+ mesh.setBridgeTimeout(60000);
165
+
166
+ // Set bridge status broadcast interval (default: 30000)
167
+ mesh.setBridgeStatusInterval(30000);
168
+ ```
169
+
170
+ ### Callbacks
171
+
172
+ ```cpp
173
+ // Called when bridge status changes
174
+ void onBridgeStatusChanged(uint32_t bridgeNodeId, bool hasInternet) {
175
+ if (!hasInternet) {
176
+ Serial.println("Bridge lost Internet - election may start");
177
+ }
178
+ }
179
+
180
+ // Called when this node's role changes
181
+ void onBridgeRoleChanged(bool isBridge, String reason) {
182
+ if (isBridge) {
183
+ Serial.printf("Promoted to bridge: %s\n", reason.c_str());
184
+ }
185
+ }
186
+
187
+ // Register callbacks
188
+ mesh.onBridgeStatusChanged(&onBridgeStatusChanged);
189
+ mesh.onBridgeRoleChanged(&onBridgeRoleChanged);
190
+ ```
191
+
192
+ ### Status Methods
193
+
194
+ ```cpp
195
+ // Check if this node is a bridge
196
+ bool isBridge = mesh.isBridge();
197
+
198
+ // Check if any bridge has Internet connectivity
199
+ bool hasInternet = mesh.hasInternetConnection();
200
+
201
+ // Get primary (best) bridge
202
+ BridgeInfo* primary = mesh.getPrimaryBridge();
203
+ if (primary) {
204
+ Serial.printf("Primary bridge: %u (RSSI: %d dBm)\n",
205
+ primary->nodeId, primary->routerRSSI);
206
+ }
207
+
208
+ // Get all known bridges
209
+ std::vector<BridgeInfo> bridges = mesh.getBridges();
210
+ for (const auto& bridge : bridges) {
211
+ Serial.printf("Bridge %u: Internet=%s, RSSI=%d\n",
212
+ bridge.nodeId,
213
+ bridge.internetConnected ? "YES" : "NO",
214
+ bridge.routerRSSI);
215
+ }
216
+ ```
217
+
218
+ ## Usage Example
219
+
220
+ ### Basic Setup
221
+
222
+ ```cpp
223
+ #include "painlessMesh.h"
224
+
225
+ #define MESH_PREFIX "MyMesh"
226
+ #define MESH_PASSWORD "password"
227
+ #define ROUTER_SSID "MyRouter"
228
+ #define ROUTER_PASSWORD "routerpass"
229
+
230
+ painlessMesh mesh;
231
+
232
+ void setup() {
233
+ // Initialize as regular node
234
+ mesh.init(MESH_PREFIX, MESH_PASSWORD, &userScheduler);
235
+
236
+ // Enable automatic failover
237
+ mesh.setRouterCredentials(ROUTER_SSID, ROUTER_PASSWORD);
238
+ mesh.enableBridgeFailover(true);
239
+
240
+ // Register callbacks
241
+ mesh.onBridgeStatusChanged(&bridgeStatusCallback);
242
+ mesh.onBridgeRoleChanged(&bridgeRoleCallback);
243
+ }
244
+ ```
245
+
246
+ ### Initial Bridge Setup
247
+
248
+ ```cpp
249
+ void setup() {
250
+ // Initialize as bridge with automatic channel detection
251
+ mesh.initAsBridge(MESH_PREFIX, MESH_PASSWORD,
252
+ ROUTER_SSID, ROUTER_PASSWORD,
253
+ &userScheduler);
254
+ }
255
+ ```
256
+
257
+ See `examples/bridge_failover/` for complete working example.
258
+
259
+ ## Failure Scenarios
260
+
261
+ ### Scenario 1: Bridge Goes Offline
262
+
263
+ **Timeline:**
264
+ - T+0s: Bridge node powers off
265
+ - T+30s: Last status broadcast expires
266
+ - T+60s: Nodes detect failure (timeout threshold)
267
+ - T+62s: Election starts (2s coordination delay)
268
+ - T+67s: Election completes (5s collection window)
269
+ - T+72s: Winner promoted to bridge (5s promotion)
270
+
271
+ **Total Failover Time:** ~70 seconds
272
+
273
+ ### Scenario 2: Bridge Loses Internet
274
+
275
+ **Timeline:**
276
+ - T+0s: Router Internet connection fails
277
+ - T+30s: Bridge broadcasts `internetConnected: false`
278
+ - T+30s: Nodes receive status, consider election
279
+ - T+32s: Election starts (if no recovery detected)
280
+ - T+37s: Election completes
281
+ - T+42s: Winner promoted to bridge
282
+
283
+ **Total Failover Time:** ~42 seconds
284
+
285
+ ### Scenario 3: Sequential Bridge Failures
286
+
287
+ **Timeline:**
288
+ - T+0s: Primary bridge fails, election starts
289
+ - T+70s: Node A wins, becomes bridge
290
+ - T+120s: Node A also fails
291
+ - T+180s: Failure detected (60s timeout)
292
+ - T+250s: New election completes, Node B becomes bridge
293
+
294
+ **Recovery:** Continues indefinitely until stable bridge found
295
+
296
+ ## Performance Characteristics
297
+
298
+ ### Timing
299
+ - **Failure detection:** 60 seconds (configurable)
300
+ - **Election duration:** 5 seconds (configurable)
301
+ - **Promotion delay:** 5 seconds (WiFi reconnection)
302
+ - **Total failover:** 60-70 seconds typical
303
+
304
+ ### Network Overhead
305
+ - **Status broadcasts:** 256 bytes per bridge every 30s
306
+ - **Election messages:** 256 bytes per candidate (one-time)
307
+ - **Takeover announcement:** 256 bytes (one-time)
308
+
309
+ ### Memory Usage
310
+ - **Per candidate:** ~12 bytes during election
311
+ - **Bridge tracking:** ~48 bytes per bridge
312
+ - **State machine:** ~100 bytes
313
+
314
+ ### Scalability
315
+ - **Tested:** Up to 10 nodes
316
+ - **Theoretical:** 50+ nodes (limited by election timeout)
317
+ - **Recommended:** 5-10 nodes per mesh
318
+
319
+ ## Edge Cases and Prevention
320
+
321
+ ### Split-Brain Prevention
322
+
323
+ **Problem:** Multiple nodes promote themselves simultaneously
324
+
325
+ **Solution:**
326
+ - State machine prevents concurrent elections
327
+ - Deterministic evaluation ensures consensus
328
+ - All nodes reach same conclusion independently
329
+
330
+ ### Rapid Failover Prevention
331
+
332
+ **Problem:** Bridge roles oscillate rapidly
333
+
334
+ **Solution:**
335
+ - Minimum 60 seconds between role changes
336
+ - RSSI hysteresis (winner must be significantly better)
337
+ - Uptime tiebreaker favors stable nodes
338
+
339
+ ### Phantom Election Prevention
340
+
341
+ **Problem:** Election starts when bridge is healthy
342
+
343
+ **Solution:**
344
+ - Only trigger on confirmed failure (60s no heartbeat)
345
+ - Check `hasInternetConnection()` before starting
346
+ - Verify router is visible before participating
347
+
348
+ ### Router Visibility Issues
349
+
350
+ **Problem:** Candidate can't see router during scan
351
+
352
+ **Solution:**
353
+ - Return RSSI=0 if router not found
354
+ - Candidate excluded from winner consideration
355
+ - Election continues with remaining candidates
356
+
357
+ ## Troubleshooting
358
+
359
+ ### Elections Don't Start
360
+
361
+ **Check:**
362
+ - Router credentials configured: `setRouterCredentials()`
363
+ - Failover enabled: `enableBridgeFailover(true)`
364
+ - Bridge timeout exceeded (60 seconds)
365
+ - At least one node can see router
366
+
367
+ **Debug:**
368
+ ```cpp
369
+ Serial.printf("Credentials: %s\n", routerCredentialsConfigured ? "YES" : "NO");
370
+ Serial.printf("Failover: %s\n", bridgeFailoverEnabled ? "ON" : "OFF");
371
+ Serial.printf("Last bridge seen: %u ms ago\n", millis() - lastBridgeSeen);
372
+ ```
373
+
374
+ ### Wrong Node Wins Election
375
+
376
+ **Check:**
377
+ - RSSI measurement accuracy (WiFi scan)
378
+ - Router placement and interference
379
+ - Tiebreaker criteria (uptime, memory, node ID)
380
+
381
+ **Debug:**
382
+ ```cpp
383
+ Serial.printf("My RSSI: %d dBm\n", scanRouterSignalStrength(ROUTER_SSID));
384
+ Serial.printf("My uptime: %u ms\n", millis());
385
+ Serial.printf("My free memory: %u bytes\n", ESP.getFreeHeap());
386
+ ```
387
+
388
+ ### Bridge Promotion Fails
389
+
390
+ **Check:**
391
+ - Router password correct
392
+ - Router channel compatible
393
+ - Node can reach router physically
394
+ - Sufficient memory for bridge mode
395
+
396
+ **Debug:**
397
+ ```cpp
398
+ Serial.printf("WiFi status: %d\n", WiFi.status());
399
+ Serial.printf("Router channel: %d\n", WiFi.channel());
400
+ Serial.printf("Free heap: %u bytes\n", ESP.getFreeHeap());
401
+ ```
402
+
403
+ ## Security Considerations
404
+
405
+ ### Router Credentials
406
+
407
+ **Risk:** Router password stored in node memory
408
+
409
+ **Mitigation:**
410
+ - Use separate guest network for mesh bridges
411
+ - Limit router permissions (no admin access)
412
+ - Consider WPA2-Enterprise for stronger security
413
+
414
+ ### Rogue Bridge Prevention
415
+
416
+ **Risk:** Malicious node claims bridge role with fake RSSI
417
+
418
+ **Mitigation:**
419
+ - Physical security of mesh nodes
420
+ - Verify bridge Internet connectivity post-election
421
+ - Monitor bridge status broadcasts for anomalies
422
+
423
+ ### Denial of Service
424
+
425
+ **Risk:** Attacker triggers repeated elections
426
+
427
+ **Mitigation:**
428
+ - Minimum 60s between role changes
429
+ - Rate limiting on election start triggers
430
+ - Monitor for excessive election activity
431
+
432
+ ## Best Practices
433
+
434
+ ### 1. Configure Redundancy
435
+ ```cpp
436
+ // At least 2 nodes with router credentials
437
+ mesh.setRouterCredentials(ROUTER_SSID, ROUTER_PASSWORD);
438
+ ```
439
+
440
+ ### 2. Monitor Bridge Health
441
+ ```cpp
442
+ void loop() {
443
+ if (!mesh.hasInternetConnection()) {
444
+ // Queue critical data locally
445
+ queueMessage(msg);
446
+ }
447
+ }
448
+ ```
449
+
450
+ ### 3. Log Election Activity
451
+ ```cpp
452
+ void onBridgeRoleChanged(bool isBridge, String reason) {
453
+ logToSD(millis(), isBridge, reason); // Audit trail
454
+ }
455
+ ```
456
+
457
+ ### 4. Test Failover Regularly
458
+ ```cpp
459
+ // Scheduled failover test (monthly)
460
+ if (shouldTestFailover()) {
461
+ // Temporarily disable primary bridge
462
+ testBridgeFailover();
463
+ }
464
+ ```
465
+
466
+ ### 5. Optimize Router Placement
467
+ - Position router centrally in mesh coverage area
468
+ - Minimize physical obstructions
469
+ - Avoid interference from other 2.4GHz devices
470
+
471
+ ## Comparison with Alternatives
472
+
473
+ ### Manual Failover
474
+ - **Pro:** Full control, predictable behavior
475
+ - **Con:** Requires human intervention, slow recovery
476
+
477
+ ### Pre-designated Backup
478
+ - **Pro:** Fast failover to known secondary
479
+ - **Con:** Backup may have poor signal, not optimal
480
+
481
+ ### RSSI-Based Election (painlessMesh)
482
+ - **Pro:** Automatic, optimal selection, distributed
483
+ - **Con:** 60-70s failover time, requires router visibility
484
+
485
+ ## Future Enhancements
486
+
487
+ ### Potential Improvements
488
+ 1. **Faster failover:** Reduce detection timeout to 30s
489
+ 2. **Predictive failover:** Detect degrading bridges before failure
490
+ 3. **Multi-router support:** Failover between different routers
491
+ 4. **Quality of Service:** Prioritize critical traffic during failover
492
+ 5. **Geographic awareness:** Consider physical location in selection
493
+
494
+ ### Experimental Features
495
+ - Load-based selection (choose least-loaded node)
496
+ - Battery-aware (exclude low-battery nodes)
497
+ - Historical reliability (favor nodes with uptime track record)
498
+
499
+ ## References
500
+
501
+ - Issue: Alteriom/painlessMesh#XX (Bridge Failover Request)
502
+ - Related: Alteriom/painlessMesh#63 (Bridge Status Broadcast)
503
+ - Related: Alteriom/painlessMesh#59 (initAsBridge Method)
504
+ - Example: `examples/bridge_failover/`
505
+ - Test: `test/catch/catch_alteriom_packages.cpp`
506
+
507
+ ## Credits
508
+
509
+ - **Requested by:** @woodlist (fish farm alarm system)
510
+ - **Implemented:** painlessMesh v1.8.0
511
+ - **Consensus model:** Based on Raft algorithm principles
512
+ - **RSSI selection:** Adapted from WiFi mesh best practices