@alteriom/painlessmesh 1.8.0 → 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.
@@ -0,0 +1,111 @@
1
+ //************************************************************
2
+ // Multi-Bridge Example: Secondary Bridge Node
3
+ //
4
+ // This example demonstrates a secondary bridge (priority 5) in a
5
+ // multi-bridge deployment. This provides hot standby redundancy
6
+ // and can handle load if the primary bridge is busy.
7
+ //
8
+ // Features demonstrated:
9
+ // - Secondary bridge with medium priority
10
+ // - Hot standby mode (always ready)
11
+ // - Automatic failover if primary fails
12
+ // - Load balancing support
13
+ //************************************************************
14
+
15
+ #include "painlessMesh.h"
16
+
17
+ #define MESH_PREFIX "MultiBridgeMesh"
18
+ #define MESH_PASSWORD "meshpassword"
19
+ #define MESH_PORT 5555
20
+
21
+ // Secondary router connection (backup Internet or different ISP)
22
+ #define ROUTER_SSID "SecondaryRouter"
23
+ #define ROUTER_PASSWORD "routerpass2"
24
+
25
+ Scheduler userScheduler;
26
+ painlessMesh mesh;
27
+
28
+ // Task to display bridge coordination status
29
+ Task taskBridgeStatus(10000, TASK_FOREVER, [](){
30
+ Serial.println("\n=== Secondary Bridge Status ===");
31
+ Serial.printf("Node ID: %u\n", mesh.getNodeId());
32
+ Serial.printf("Connected Nodes: %d\n", mesh.getNodeList().size());
33
+
34
+ // Show all active bridges in the mesh
35
+ auto activeBridges = mesh.getActiveBridges();
36
+ Serial.printf("Active Bridges: %d\n", activeBridges.size());
37
+ for (auto bridgeId : activeBridges) {
38
+ Serial.printf(" - Bridge: %u%s\n", bridgeId,
39
+ (bridgeId == mesh.getNodeId()) ? " (ME)" : "");
40
+ }
41
+
42
+ // Show recommended bridge for next message
43
+ uint32_t recommended = mesh.getRecommendedBridge();
44
+ Serial.printf("Recommended Bridge: %u\n", recommended);
45
+
46
+ if (recommended == mesh.getNodeId()) {
47
+ Serial.println("⚠️ I AM THE ACTIVE BRIDGE (primary likely failed!)");
48
+ } else {
49
+ Serial.println("✓ Standby mode - primary bridge is active");
50
+ }
51
+ Serial.println("=============================\n");
52
+ });
53
+
54
+ void receivedCallback(uint32_t from, String& msg) {
55
+ Serial.printf("Received from %u: %s\n", from, msg.c_str());
56
+ }
57
+
58
+ void newConnectionCallback(uint32_t nodeId) {
59
+ Serial.printf("New Connection, nodeId = %u\n", nodeId);
60
+ }
61
+
62
+ void changedConnectionCallback() {
63
+ Serial.printf("Changed connections. Node count: %d\n", mesh.getNodeList().size());
64
+ }
65
+
66
+ void bridgeStatusCallback(uint32_t bridgeNodeId, bool hasInternet) {
67
+ if (hasInternet) {
68
+ Serial.printf("✓ Bridge %u: Internet connected\n", bridgeNodeId);
69
+ } else {
70
+ Serial.printf("⚠️ Bridge %u: Internet OFFLINE\n", bridgeNodeId);
71
+ }
72
+ }
73
+
74
+ void setup() {
75
+ Serial.begin(115200);
76
+ delay(2000);
77
+
78
+ Serial.println("\n\n=== Multi-Bridge: SECONDARY BRIDGE ===\n");
79
+
80
+ // Initialize mesh as secondary bridge with priority 5
81
+ mesh.setDebugMsgTypes(ERROR | STARTUP | CONNECTION);
82
+
83
+ // Enable multi-bridge coordination mode
84
+ mesh.enableMultiBridge(true);
85
+
86
+ // Set bridge selection strategy (PRIORITY_BASED is default)
87
+ mesh.setBridgeSelectionStrategy(painlessMesh::PRIORITY_BASED);
88
+
89
+ // Initialize as bridge with priority 5 (secondary)
90
+ mesh.initAsBridge(MESH_PREFIX, MESH_PASSWORD,
91
+ ROUTER_SSID, ROUTER_PASSWORD,
92
+ &userScheduler, MESH_PORT, 5); // Priority 5 = Secondary
93
+
94
+ mesh.onReceive(&receivedCallback);
95
+ mesh.onNewConnection(&newConnectionCallback);
96
+ mesh.onChangedConnections(&changedConnectionCallback);
97
+ mesh.onBridgeStatusChanged(&bridgeStatusCallback);
98
+
99
+ // Add status reporting task
100
+ userScheduler.addTask(taskBridgeStatus);
101
+ taskBridgeStatus.enable();
102
+
103
+ Serial.println("\n=== Secondary Bridge Ready ===");
104
+ Serial.println("This node is the SECONDARY bridge (Priority 5)");
105
+ Serial.println("It operates in hot standby mode");
106
+ Serial.println("Will take over if primary bridge fails\n");
107
+ }
108
+
109
+ void loop() {
110
+ mesh.update();
111
+ }
@@ -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