@alteriom/painlessmesh 1.7.9 → 1.8.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. package/CHANGELOG.md +118 -2
  2. package/README.md +159 -12
  3. package/docs/BRIDGE_FAILOVER.md +512 -0
  4. package/docs/BRIDGE_HEALTH_MONITORING.md +293 -0
  5. package/docs/CREATE_MISSING_RELEASES.md +321 -0
  6. package/docs/releases/RELEASE_SUMMARY_v1.7.8.md +523 -0
  7. package/docs/releases/RELEASE_SUMMARY_v1.7.9.md +542 -0
  8. package/examples/alteriom/alteriom_sensor_package.hpp +213 -0
  9. package/examples/alteriomSensorNode/alteriom_sensor_package.hpp +1014 -11
  10. package/examples/basic/basic.ino +6 -2
  11. package/examples/bridge/bridge.ino +44 -23
  12. package/examples/bridge/bridge_health_monitoring_example.ino +188 -0
  13. package/examples/bridgeAwareSensorNode/alteriom_sensor_package.hpp +1227 -0
  14. package/examples/bridgeAwareSensorNode/bridgeAwareSensorNode.ino +343 -0
  15. package/examples/bridgeAwareSensorNode/platformio.ini +26 -0
  16. package/examples/bridge_failover/README.md +358 -0
  17. package/examples/bridge_failover/bridge_failover.ino +180 -0
  18. package/examples/bridge_failover/platformio.ini +27 -0
  19. package/examples/diagnosticsExample/diagnosticsExample.ino +171 -0
  20. package/examples/diagnosticsExample/platformio.ini +26 -0
  21. package/examples/multi_bridge/README.md +346 -0
  22. package/examples/multi_bridge/primary_bridge.ino +96 -0
  23. package/examples/multi_bridge/regular_node.ino +141 -0
  24. package/examples/multi_bridge/secondary_bridge.ino +111 -0
  25. package/examples/ntpTimeSyncBridge/alteriom_sensor_package.hpp +1383 -0
  26. package/examples/ntpTimeSyncBridge/ntpTimeSyncBridge.ino +81 -0
  27. package/examples/ntpTimeSyncNode/alteriom_sensor_package.hpp +1383 -0
  28. package/examples/ntpTimeSyncNode/ntpTimeSyncNode.ino +109 -0
  29. package/examples/queued_alarms/README.md +390 -0
  30. package/examples/queued_alarms/queued_alarms.ino +265 -0
  31. package/examples/rtcIntegration/README.md +235 -0
  32. package/examples/rtcIntegration/rtcIntegration.ino +196 -0
  33. package/library.json +1 -1
  34. package/library.properties +1 -1
  35. package/package.json +1 -1
  36. package/src/arduino/wifi.hpp +888 -0
  37. package/src/painlessMeshSTA.cpp +63 -0
  38. package/src/painlessMeshSTA.h +3 -0
  39. package/src/painlessmesh/mesh.hpp +1327 -4
  40. package/src/painlessmesh/message_queue.hpp +368 -0
  41. package/src/painlessmesh/plugin.hpp +69 -0
  42. package/src/painlessmesh/rtc.hpp +203 -0
@@ -0,0 +1,109 @@
1
+ /**
2
+ * @file ntpTimeSyncNode.ino
3
+ * @brief Example: Regular mesh node receiving NTP time from bridge
4
+ *
5
+ * This example demonstrates a regular mesh node that:
6
+ * 1. Connects to the mesh network
7
+ * 2. Listens for NTP time broadcasts from bridge nodes
8
+ * 3. Updates local time when NTP sync is received
9
+ * 4. Optionally syncs RTC module if available
10
+ *
11
+ * No Internet connection needed - time is received from bridge!
12
+ */
13
+
14
+ #include "painlessMesh.h"
15
+ #include "examples/alteriom/alteriom_sensor_package.hpp"
16
+
17
+ // Mesh configuration
18
+ #define MESH_PREFIX "AlteriomMesh"
19
+ #define MESH_PASSWORD "your_mesh_password"
20
+ #define MESH_PORT 5555
21
+
22
+ Scheduler userScheduler;
23
+ painlessMesh mesh;
24
+
25
+ using namespace alteriom;
26
+
27
+ // Track last time sync
28
+ uint32_t lastNTPSync = 0;
29
+ uint32_t ntpSyncCount = 0;
30
+
31
+ // Received callback - handle incoming messages
32
+ void receivedCallback(uint32_t from, String& msg) {
33
+ // Parse JSON message
34
+ DynamicJsonDocument doc(1024);
35
+ DeserializationError error = deserializeJson(doc, msg);
36
+
37
+ if (error) {
38
+ Serial.printf("JSON parse error: %s\n", error.c_str());
39
+ return;
40
+ }
41
+
42
+ JsonObject obj = doc.as<JsonObject>();
43
+ uint16_t msgType = obj["type"];
44
+
45
+ // Check if this is an NTP time sync message
46
+ if (msgType == 614) {
47
+ // Deserialize NTP time sync package
48
+ auto pkg = NTPTimeSyncPackage(obj);
49
+
50
+ Serial.printf("\n=== NTP Time Sync Received ===\n");
51
+ Serial.printf("From: %u\n", from);
52
+ Serial.printf("NTP Time: %u\n", pkg.ntpTime);
53
+ Serial.printf("Accuracy: %ums\n", pkg.accuracy);
54
+ Serial.printf("Source: %s\n", pkg.source.c_str());
55
+ Serial.printf("Timestamp: %u\n", pkg.timestamp);
56
+ Serial.println("=============================\n");
57
+
58
+ // Update mesh time (this is application-specific)
59
+ // In a real implementation, you would:
60
+ // 1. Verify the sender is a bridge node
61
+ // 2. Apply the time with mesh.setTimeFromNTP(pkg.ntpTime)
62
+ // 3. Update RTC if available
63
+
64
+ lastNTPSync = millis();
65
+ ntpSyncCount++;
66
+
67
+ Serial.printf("Time sync applied! Total syncs: %u\n", ntpSyncCount);
68
+ }
69
+ }
70
+
71
+ // Status task - periodic status updates
72
+ Task taskStatus(30000, TASK_FOREVER, [](){
73
+ uint32_t timeSinceSync = (millis() - lastNTPSync) / 1000;
74
+
75
+ Serial.printf("\n--- Node Status ---\n");
76
+ Serial.printf("Node ID: %u\n", mesh.getNodeId());
77
+ Serial.printf("Connections: %d\n", mesh.getNodeList().size());
78
+ Serial.printf("NTP Syncs: %u\n", ntpSyncCount);
79
+
80
+ if (ntpSyncCount > 0) {
81
+ Serial.printf("Last sync: %u seconds ago\n", timeSinceSync);
82
+ } else {
83
+ Serial.println("Waiting for first NTP sync...");
84
+ }
85
+
86
+ Serial.println("------------------\n");
87
+ });
88
+
89
+ void setup() {
90
+ Serial.begin(115200);
91
+
92
+ // Initialize mesh
93
+ mesh.setDebugMsgTypes(ERROR | STARTUP | CONNECTION);
94
+ mesh.init(MESH_PREFIX, MESH_PASSWORD, &userScheduler, MESH_PORT);
95
+
96
+ // Set callbacks
97
+ mesh.onReceive(&receivedCallback);
98
+
99
+ // Add status task
100
+ userScheduler.addTask(taskStatus);
101
+ taskStatus.enable();
102
+
103
+ Serial.println("Regular mesh node initialized");
104
+ Serial.println("Listening for NTP time broadcasts...");
105
+ }
106
+
107
+ void loop() {
108
+ mesh.update();
109
+ }
@@ -0,0 +1,390 @@
1
+ # Queued Alarms Example
2
+
3
+ ## Overview
4
+
5
+ This example demonstrates **priority-based message queueing** for critical IoT systems that cannot afford to lose data during Internet outages. It's designed for the fish farm dissolved oxygen (O2) monitoring use case described in [Issue #66](https://github.com/Alteriom/painlessMesh/issues/66).
6
+
7
+ ## Problem Statement
8
+
9
+ In production IoT systems like fish farm monitoring, **critical alarms must never be lost**. When the bridge node loses Internet connectivity:
10
+
11
+ - ❌ **Without queueing**: Critical O2 alarms are lost → fish die
12
+ - ✅ **With queueing**: Alarms are queued and delivered when connection restored → supervisor notified, fish saved
13
+
14
+ ## Features
15
+
16
+ ### ✅ Priority-Based Queueing
17
+
18
+ - **CRITICAL** (Priority 0): Life-safety alarms - never dropped
19
+ - **HIGH** (Priority 1): Important warnings - preserved up to 80% capacity
20
+ - **NORMAL** (Priority 2): Regular data - preserved up to 60% capacity
21
+ - **LOW** (Priority 3): Telemetry - dropped first when queue full
22
+
23
+ ### ✅ Automatic Queue Management
24
+
25
+ - Queues messages when Internet unavailable
26
+ - Flushes queue when Internet restored
27
+ - Prunes old messages (configurable age)
28
+ - Monitors queue health with callbacks
29
+
30
+ ### ✅ Production Ready
31
+
32
+ - Survives Internet outages (queuing)
33
+ - Handles queue overflow intelligently
34
+ - Provides queue statistics
35
+ - Retry logic with attempt tracking
36
+
37
+ ## Hardware Requirements
38
+
39
+ - **ESP32** or **ESP8266**
40
+ - At least 2 nodes (1 bridge + 1 sensor node)
41
+ - Bridge node needs WiFi router access
42
+
43
+ ## Configuration
44
+
45
+ ### 1. Mesh Network Settings
46
+
47
+ ```cpp
48
+ #define MESH_PREFIX "FishFarmMesh"
49
+ #define MESH_PASSWORD "somethingSneaky"
50
+ #define MESH_PORT 5555
51
+ ```
52
+
53
+ ### 2. Router Credentials (Bridge Node)
54
+
55
+ ```cpp
56
+ #define ROUTER_SSID "YourWiFiSSID"
57
+ #define ROUTER_PASSWORD "YourWiFiPassword"
58
+ ```
59
+
60
+ Enable in setup for bridge node:
61
+ ```cpp
62
+ mesh.stationManual(ROUTER_SSID, ROUTER_PASSWORD);
63
+ ```
64
+
65
+ ### 3. Sensor Thresholds
66
+
67
+ Adjust for your sensors (dissolved oxygen in mg/L):
68
+
69
+ ```cpp
70
+ #define CRITICAL_O2_THRESHOLD 3.0 // Life-critical
71
+ #define WARNING_O2_THRESHOLD 5.0 // Warning level
72
+ ```
73
+
74
+ ### 4. Queue Configuration
75
+
76
+ ```cpp
77
+ #define MAX_QUEUE_SIZE 500 // Max messages
78
+ #define QUEUE_PRUNE_AGE (24 * 60 * 60 * 1000) // 24 hours
79
+ ```
80
+
81
+ ## How It Works
82
+
83
+ ### Normal Operation (Internet Available)
84
+
85
+ ```
86
+ Sensor → Mesh → Bridge → Internet → Cloud/MQTT
87
+ ```
88
+
89
+ Messages sent immediately, no queueing.
90
+
91
+ ### Offline Mode (No Internet)
92
+
93
+ ```
94
+ Sensor → Mesh → Bridge → Queue (Priority-based)
95
+ ↓
96
+ [CRITICAL never dropped]
97
+ [LOW dropped first]
98
+ ```
99
+
100
+ Messages queued with priority, delivered when Internet restored.
101
+
102
+ ### Internet Restored
103
+
104
+ ```
105
+ Queue → Flush → MQTT/HTTP → Cloud
106
+ ↓
107
+ Remove on success
108
+ Retry on failure (max 3 attempts)
109
+ ```
110
+
111
+ ## Usage
112
+
113
+ ### 1. Flash Bridge Node
114
+
115
+ 1. Uncomment these lines in `setup()`:
116
+ ```cpp
117
+ mesh.stationManual(ROUTER_SSID, ROUTER_PASSWORD);
118
+ mesh.setHostname("FishFarmBridge");
119
+ ```
120
+ 2. Upload to ESP32/ESP8266 with router access
121
+ 3. Bridge connects to router and provides Internet to mesh
122
+
123
+ ### 2. Flash Sensor Nodes
124
+
125
+ 1. Leave router lines commented
126
+ 2. Upload to sensor node ESP32/ESP8266
127
+ 3. Node joins mesh and monitors sensors
128
+
129
+ ### 3. Monitor Serial Output
130
+
131
+ **Normal operation:**
132
+ ```
133
+ ✅ ONLINE MODE - Internet restored
134
+ 📊 Telemetry: 7.32 mg/L
135
+ 📊 Telemetry: 6.85 mg/L
136
+ ```
137
+
138
+ **Internet lost:**
139
+ ```
140
+ ⚠️ OFFLINE MODE ACTIVATED
141
+ Queue size: 0 messages
142
+ 📊 Telemetry: 6.42 mg/L - queued #1
143
+ [Queue: 1 messages]
144
+ ```
145
+
146
+ **Critical alarm (offline):**
147
+ ```
148
+ 🚨 CRITICAL O2 ALARM: 2.87 mg/L - QUEUED #5
149
+ [Queue: 5 messages (1 CRITICAL)]
150
+ ```
151
+
152
+ **Internet restored:**
153
+ ```
154
+ ✅ ONLINE MODE - Internet restored
155
+ Flushing 5 queued messages...
156
+ Sending queued message #1 (priority=3, attempts=0)
157
+ Sending queued message #5 (priority=0, attempts=0)
158
+ ✅ Queue flushed (5 messages sent)
159
+ ```
160
+
161
+ ## Queue States
162
+
163
+ The example monitors queue health:
164
+
165
+ - **EMPTY**: No messages queued
166
+ - **NORMAL**: Queue has space available
167
+ - **75% FULL**: Warning - queue reaching capacity
168
+ - **FULL**: Queue full - dropping LOW priority messages
169
+
170
+ Example output:
171
+ ```
172
+ ⚠️ Queue 75% full (375 messages)
173
+ 🚨 Queue FULL (500 messages) - dropping LOW priority
174
+ ```
175
+
176
+ ## Testing Without Hardware
177
+
178
+ ### Simulate Internet Loss
179
+
180
+ In real deployment, Internet loss is automatic. For testing, you can:
181
+
182
+ 1. **Disconnect router**: Physically disconnect Ethernet/WAN
183
+ 2. **Block MAC address**: Router settings → Block bridge MAC
184
+ 3. **Power cycle router**: Turn off router
185
+ 4. **Modify code**: Add test button to toggle `offlineMode`
186
+
187
+ ### Verify Queue Behavior
188
+
189
+ 1. Start with Internet connected
190
+ 2. Cause Internet loss (any method above)
191
+ 3. Wait for critical alarms to queue
192
+ 4. Restore Internet
193
+ 5. Verify messages are flushed
194
+
195
+ Expected sequence:
196
+ ```
197
+ ✅ Online → ⚠️ Offline (queueing) → ✅ Online (flush queue)
198
+ ```
199
+
200
+ ## Integration with Cloud Services
201
+
202
+ ### MQTT Example
203
+
204
+ Replace simulated sending with MQTT:
205
+
206
+ ```cpp
207
+ #include <PubSubClient.h>
208
+
209
+ WiFiClient wifiClient;
210
+ PubSubClient mqttClient(wifiClient);
211
+
212
+ // In setup()
213
+ mqttClient.setServer("mqtt.example.com", 1883);
214
+
215
+ // In bridgeStatusCallback()
216
+ for (auto& msg : messages) {
217
+ bool sent = mqttClient.publish(
218
+ msg.destination.c_str(), // Topic from queueMessage()
219
+ msg.payload.c_str()
220
+ );
221
+
222
+ if (sent) {
223
+ mesh.removeQueuedMessage(msg.id);
224
+ } else {
225
+ mesh.incrementQueuedMessageAttempts(msg.id);
226
+ }
227
+ }
228
+ ```
229
+
230
+ ### HTTP Example
231
+
232
+ Replace simulated sending with HTTP POST:
233
+
234
+ ```cpp
235
+ #include <HTTPClient.h>
236
+
237
+ HTTPClient http;
238
+
239
+ for (auto& msg : messages) {
240
+ http.begin(msg.destination); // URL from queueMessage()
241
+ http.addHeader("Content-Type", "application/json");
242
+
243
+ int httpCode = http.POST(msg.payload);
244
+ bool sent = (httpCode == 200 || httpCode == 201);
245
+
246
+ if (sent) {
247
+ mesh.removeQueuedMessage(msg.id);
248
+ } else {
249
+ mesh.incrementQueuedMessageAttempts(msg.id);
250
+ }
251
+
252
+ http.end();
253
+ }
254
+ ```
255
+
256
+ ## Message Format
257
+
258
+ Example JSON message structure:
259
+
260
+ ### Critical Alarm
261
+ ```json
262
+ {
263
+ "type": "CRITICAL_ALARM",
264
+ "sensor": "O2",
265
+ "value": 2.87,
266
+ "threshold": 3.0,
267
+ "tankId": "TANK_A",
268
+ "nodeId": 123456789,
269
+ "timestamp": 1234567890
270
+ }
271
+ ```
272
+
273
+ ### Warning
274
+ ```json
275
+ {
276
+ "type": "WARNING",
277
+ "sensor": "O2",
278
+ "value": 4.5,
279
+ "threshold": 5.0,
280
+ "nodeId": 123456789
281
+ }
282
+ ```
283
+
284
+ ### Telemetry
285
+ ```json
286
+ {
287
+ "sensor": "O2",
288
+ "value": 7.32,
289
+ "nodeId": 123456789
290
+ }
291
+ ```
292
+
293
+ ## API Reference
294
+
295
+ ### Enable Queue
296
+
297
+ ```cpp
298
+ mesh.enableMessageQueue(true, MAX_QUEUE_SIZE);
299
+ ```
300
+
301
+ ### Queue Message
302
+
303
+ ```cpp
304
+ uint32_t msgId = mesh.queueMessage(
305
+ payload, // Message content
306
+ destination, // Cloud endpoint/topic
307
+ PRIORITY_CRITICAL // Priority level
308
+ );
309
+ ```
310
+
311
+ ### Flush Queue
312
+
313
+ ```cpp
314
+ auto messages = mesh.flushMessageQueue();
315
+ for (auto& msg : messages) {
316
+ if (sendToCloud(msg)) {
317
+ mesh.removeQueuedMessage(msg.id);
318
+ }
319
+ }
320
+ ```
321
+
322
+ ### Query Queue
323
+
324
+ ```cpp
325
+ uint32_t total = mesh.getQueuedMessageCount();
326
+ uint32_t critical = mesh.getQueuedMessageCount(PRIORITY_CRITICAL);
327
+ ```
328
+
329
+ ### Callbacks
330
+
331
+ ```cpp
332
+ mesh.onBridgeStatusChanged([](uint32_t bridgeId, bool hasInternet) {
333
+ // Handle Internet connectivity change
334
+ });
335
+
336
+ mesh.onQueueStateChanged([](QueueState state, uint32_t count) {
337
+ // Handle queue state change (EMPTY, NORMAL, 75%, FULL)
338
+ });
339
+ ```
340
+
341
+ ## Troubleshooting
342
+
343
+ ### Queue Always Full
344
+
345
+ - Increase `MAX_QUEUE_SIZE`
346
+ - Decrease message frequency
347
+ - Lower message priorities
348
+ - Reduce `QUEUE_PRUNE_AGE`
349
+
350
+ ### Messages Not Flushing
351
+
352
+ - Check `bridgeStatusCallback()` is called
353
+ - Verify Internet connectivity with `mesh.hasInternetConnection()`
354
+ - Check MQTT/HTTP sending code
355
+ - Monitor serial for errors
356
+
357
+ ### High Memory Usage
358
+
359
+ - Reduce `MAX_QUEUE_SIZE`
360
+ - Enable aggressive pruning
361
+ - Use shorter message payloads
362
+ - Monitor with `ESP.getFreeHeap()`
363
+
364
+ ## Performance
365
+
366
+ ### Memory Usage
367
+
368
+ | Queue Size | RAM Usage (approx) |
369
+ |------------|-------------------|
370
+ | 100 | ~20 KB |
371
+ | 500 | ~100 KB |
372
+ | 1000 | ~200 KB |
373
+
374
+ **ESP32**: Can handle 1000+ messages
375
+ **ESP8266**: Recommend ≤500 messages
376
+
377
+ ### Throughput
378
+
379
+ - **Queue**: ~1000 msg/sec
380
+ - **Flush**: Limited by MQTT/HTTP send rate (~10-50 msg/sec)
381
+
382
+ ## Related Documentation
383
+
384
+ - [Issue #66: Message Queueing Feature](https://github.com/Alteriom/painlessMesh/issues/66)
385
+ - [Issue #63: Bridge Status Broadcast](https://github.com/Alteriom/painlessMesh/issues/63)
386
+ - [painlessMesh Documentation](https://gitlab.com/painlessMesh/painlessMesh)
387
+
388
+ ## License
389
+
390
+ MIT License - See repository LICENSE file