@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.
- package/CHANGELOG.md +118 -2
- package/README.md +159 -12
- package/docs/BRIDGE_FAILOVER.md +512 -0
- package/docs/BRIDGE_HEALTH_MONITORING.md +293 -0
- package/docs/CREATE_MISSING_RELEASES.md +321 -0
- package/docs/releases/RELEASE_SUMMARY_v1.7.8.md +523 -0
- package/docs/releases/RELEASE_SUMMARY_v1.7.9.md +542 -0
- package/examples/alteriom/alteriom_sensor_package.hpp +213 -0
- package/examples/alteriomSensorNode/alteriom_sensor_package.hpp +1014 -11
- package/examples/basic/basic.ino +6 -2
- package/examples/bridge/bridge.ino +44 -23
- package/examples/bridge/bridge_health_monitoring_example.ino +188 -0
- package/examples/bridgeAwareSensorNode/alteriom_sensor_package.hpp +1227 -0
- package/examples/bridgeAwareSensorNode/bridgeAwareSensorNode.ino +343 -0
- package/examples/bridgeAwareSensorNode/platformio.ini +26 -0
- package/examples/bridge_failover/README.md +358 -0
- package/examples/bridge_failover/bridge_failover.ino +180 -0
- package/examples/bridge_failover/platformio.ini +27 -0
- package/examples/diagnosticsExample/diagnosticsExample.ino +171 -0
- package/examples/diagnosticsExample/platformio.ini +26 -0
- package/examples/multi_bridge/README.md +346 -0
- package/examples/multi_bridge/primary_bridge.ino +96 -0
- package/examples/multi_bridge/regular_node.ino +141 -0
- package/examples/multi_bridge/secondary_bridge.ino +111 -0
- package/examples/ntpTimeSyncBridge/alteriom_sensor_package.hpp +1383 -0
- package/examples/ntpTimeSyncBridge/ntpTimeSyncBridge.ino +81 -0
- package/examples/ntpTimeSyncNode/alteriom_sensor_package.hpp +1383 -0
- package/examples/ntpTimeSyncNode/ntpTimeSyncNode.ino +109 -0
- package/examples/queued_alarms/README.md +390 -0
- package/examples/queued_alarms/queued_alarms.ino +265 -0
- package/examples/rtcIntegration/README.md +235 -0
- package/examples/rtcIntegration/rtcIntegration.ino +196 -0
- package/library.json +1 -1
- package/library.properties +1 -1
- package/package.json +1 -1
- package/src/arduino/wifi.hpp +888 -0
- package/src/painlessMeshSTA.cpp +63 -0
- package/src/painlessMeshSTA.h +3 -0
- package/src/painlessmesh/mesh.hpp +1327 -4
- package/src/painlessmesh/message_queue.hpp +368 -0
- package/src/painlessmesh/plugin.hpp +69 -0
- 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
|