@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,635 @@
|
|
|
1
|
+
# Bridge Status Broadcast & Callback Feature
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
|
|
5
|
+
The bridge status feature enables mesh nodes to monitor bridge Internet connectivity in real-time and respond intelligently to network conditions. This is critical for production deployments where data delivery reliability is essential.
|
|
6
|
+
|
|
7
|
+
## Problem Statement
|
|
8
|
+
|
|
9
|
+
In a typical mesh network with a bridge to the Internet, regular mesh nodes have no visibility into whether the bridge is actually connected to the Internet. This creates several problems:
|
|
10
|
+
|
|
11
|
+
1. **Data Loss**: Nodes send data that fails silently when Internet is down
|
|
12
|
+
2. **No Failover**: Nodes can't switch to backup bridges when primary fails
|
|
13
|
+
3. **Poor User Experience**: No feedback about connectivity state
|
|
14
|
+
4. **Wasted Resources**: Attempting to send data that can't be delivered
|
|
15
|
+
|
|
16
|
+
## Solution
|
|
17
|
+
|
|
18
|
+
Bridge nodes now broadcast their connectivity status every 30 seconds (configurable). Regular nodes receive these broadcasts and can:
|
|
19
|
+
|
|
20
|
+
- Queue critical messages during outages
|
|
21
|
+
- Implement failover to backup bridges
|
|
22
|
+
- Provide user feedback about connectivity
|
|
23
|
+
- Make intelligent routing decisions
|
|
24
|
+
|
|
25
|
+
## Key Features
|
|
26
|
+
|
|
27
|
+
### 1. Automatic Status Broadcasting
|
|
28
|
+
|
|
29
|
+
Bridge nodes automatically broadcast their status:
|
|
30
|
+
|
|
31
|
+
```cpp
|
|
32
|
+
// Bridge nodes automatically broadcast every 30 seconds
|
|
33
|
+
// No code changes needed if using initAsBridge()
|
|
34
|
+
mesh.initAsBridge(MESH_SSID, MESH_PASSWORD,
|
|
35
|
+
ROUTER_SSID, ROUTER_PASSWORD,
|
|
36
|
+
&userScheduler, MESH_PORT);
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
Status includes:
|
|
40
|
+
- Internet connectivity (`true`/`false`)
|
|
41
|
+
- Router signal strength (RSSI in dBm)
|
|
42
|
+
- Router WiFi channel
|
|
43
|
+
- Bridge uptime
|
|
44
|
+
- Gateway IP address
|
|
45
|
+
- Timestamp
|
|
46
|
+
|
|
47
|
+
### 2. Bridge Status Callback
|
|
48
|
+
|
|
49
|
+
Regular nodes can register a callback that fires when bridge status changes:
|
|
50
|
+
|
|
51
|
+
```cpp
|
|
52
|
+
mesh.onBridgeStatusChanged([](uint32_t bridgeNodeId, bool hasInternet) {
|
|
53
|
+
if (hasInternet) {
|
|
54
|
+
Serial.println("✓ Internet available - sending queued data");
|
|
55
|
+
flushQueuedMessages();
|
|
56
|
+
} else {
|
|
57
|
+
Serial.println("⚠ Internet offline - queueing messages");
|
|
58
|
+
enableOfflineMode();
|
|
59
|
+
}
|
|
60
|
+
});
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
**When the callback fires:**
|
|
64
|
+
- On first bridge status received
|
|
65
|
+
- When Internet connectivity changes (`true` ↔ `false`)
|
|
66
|
+
- When a new bridge appears or disappears (after 60s timeout)
|
|
67
|
+
|
|
68
|
+
### 3. Bridge Information API
|
|
69
|
+
|
|
70
|
+
Query bridge status programmatically:
|
|
71
|
+
|
|
72
|
+
```cpp
|
|
73
|
+
// Check if any bridge has Internet
|
|
74
|
+
if (mesh.hasInternetConnection()) {
|
|
75
|
+
sendCriticalData();
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// Get primary (best) bridge
|
|
79
|
+
auto primaryBridge = mesh.getPrimaryBridge();
|
|
80
|
+
if (primaryBridge) {
|
|
81
|
+
Serial.printf("Primary bridge: %u (RSSI: %d dBm)\n",
|
|
82
|
+
primaryBridge->nodeId,
|
|
83
|
+
primaryBridge->routerRSSI);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// Get all known bridges
|
|
87
|
+
auto bridges = mesh.getBridges();
|
|
88
|
+
for (const auto& bridge : bridges) {
|
|
89
|
+
Serial.printf("Bridge %u: %s (RSSI: %d dBm)\n",
|
|
90
|
+
bridge.nodeId,
|
|
91
|
+
bridge.internetConnected ? "Online" : "Offline",
|
|
92
|
+
bridge.routerRSSI);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// Check if this node is a bridge
|
|
96
|
+
if (mesh.isBridge()) {
|
|
97
|
+
Serial.println("This node is acting as a bridge");
|
|
98
|
+
}
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
### 4. Configuration Options
|
|
102
|
+
|
|
103
|
+
Customize the behavior:
|
|
104
|
+
|
|
105
|
+
```cpp
|
|
106
|
+
// Change broadcast interval (default: 30000ms = 30 seconds)
|
|
107
|
+
mesh.setBridgeStatusInterval(60000); // 60 seconds
|
|
108
|
+
|
|
109
|
+
// Change bridge timeout (default: 60000ms = 60 seconds)
|
|
110
|
+
mesh.setBridgeTimeout(90000); // 90 seconds
|
|
111
|
+
|
|
112
|
+
// Disable broadcasting (bridge nodes only)
|
|
113
|
+
mesh.enableBridgeStatusBroadcast(false);
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
## BridgeStatusPackage (Type 610)
|
|
117
|
+
|
|
118
|
+
The bridge status is transmitted as a broadcast package with type 610:
|
|
119
|
+
|
|
120
|
+
```json
|
|
121
|
+
{
|
|
122
|
+
"type": 610,
|
|
123
|
+
"from": 123456789,
|
|
124
|
+
"routing": 2,
|
|
125
|
+
"timestamp": 12345678,
|
|
126
|
+
"internetConnected": true,
|
|
127
|
+
"routerRSSI": -45,
|
|
128
|
+
"routerChannel": 6,
|
|
129
|
+
"uptime": 3600000,
|
|
130
|
+
"gatewayIP": "192.168.1.1",
|
|
131
|
+
"message_type": 610
|
|
132
|
+
}
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
### Fields
|
|
136
|
+
|
|
137
|
+
| Field | Type | Description |
|
|
138
|
+
|-------|------|-------------|
|
|
139
|
+
| `type` | uint16 | Package type (610 = BRIDGE_STATUS) |
|
|
140
|
+
| `from` | uint32 | Bridge node ID |
|
|
141
|
+
| `routing` | uint8 | Routing type (2 = BROADCAST) |
|
|
142
|
+
| `timestamp` | uint32 | Mesh network time when status was collected |
|
|
143
|
+
| `internetConnected` | bool | Is bridge connected to Internet? |
|
|
144
|
+
| `routerRSSI` | int8 | Router WiFi signal strength in dBm (-127 to 0) |
|
|
145
|
+
| `routerChannel` | uint8 | Router WiFi channel (1-13) |
|
|
146
|
+
| `uptime` | uint32 | Bridge uptime in milliseconds |
|
|
147
|
+
| `gatewayIP` | string | Router gateway IP address |
|
|
148
|
+
| `message_type` | uint16 | MQTT schema message type (610) |
|
|
149
|
+
|
|
150
|
+
## BridgeInfo Class
|
|
151
|
+
|
|
152
|
+
The `BridgeInfo` class tracks the status of each bridge:
|
|
153
|
+
|
|
154
|
+
```cpp
|
|
155
|
+
class BridgeInfo {
|
|
156
|
+
public:
|
|
157
|
+
uint32_t nodeId; // Bridge node ID
|
|
158
|
+
bool internetConnected; // Internet connectivity status
|
|
159
|
+
int8_t routerRSSI; // Router signal strength
|
|
160
|
+
uint8_t routerChannel; // Router WiFi channel
|
|
161
|
+
uint32_t lastSeen; // When last status was received (millis)
|
|
162
|
+
uint32_t uptime; // Bridge uptime
|
|
163
|
+
TSTRING gatewayIP; // Router gateway IP
|
|
164
|
+
uint32_t timestamp; // Timestamp from bridge
|
|
165
|
+
|
|
166
|
+
bool isHealthy(uint32_t timeoutMs = 60000) const;
|
|
167
|
+
};
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
**Health Checking**: A bridge is considered healthy if a status update was received within the timeout period (default: 60 seconds).
|
|
171
|
+
|
|
172
|
+
## Primary Bridge Selection
|
|
173
|
+
|
|
174
|
+
The mesh automatically selects a "primary" bridge based on:
|
|
175
|
+
|
|
176
|
+
1. **Must be healthy** (status received within 60 seconds)
|
|
177
|
+
2. **Must have Internet** (`internetConnected == true`)
|
|
178
|
+
3. **Best signal strength** (highest RSSI)
|
|
179
|
+
|
|
180
|
+
```cpp
|
|
181
|
+
auto primaryBridge = mesh.getPrimaryBridge();
|
|
182
|
+
if (primaryBridge) {
|
|
183
|
+
// Use this bridge for critical data
|
|
184
|
+
sendDataToBridge(primaryBridge->nodeId);
|
|
185
|
+
}
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
## Use Cases
|
|
189
|
+
|
|
190
|
+
### 1. Message Queueing (Fish Farm Example)
|
|
191
|
+
|
|
192
|
+
Queue critical alarms during Internet outages:
|
|
193
|
+
|
|
194
|
+
```cpp
|
|
195
|
+
std::vector<AlarmMessage> alarmQueue;
|
|
196
|
+
bool internetAvailable = false;
|
|
197
|
+
|
|
198
|
+
mesh.onBridgeStatusChanged([](uint32_t bridgeId, bool hasInternet) {
|
|
199
|
+
internetAvailable = hasInternet;
|
|
200
|
+
|
|
201
|
+
if (hasInternet) {
|
|
202
|
+
// Flush queued alarms
|
|
203
|
+
for (auto& alarm : alarmQueue) {
|
|
204
|
+
sendAlarmToCloud(alarm);
|
|
205
|
+
}
|
|
206
|
+
alarmQueue.clear();
|
|
207
|
+
}
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
void onCriticalAlarm(float oxygenLevel) {
|
|
211
|
+
AlarmMessage alarm = {
|
|
212
|
+
.type = ALARM_CRITICAL,
|
|
213
|
+
.value = oxygenLevel,
|
|
214
|
+
.timestamp = mesh.getNodeTime()
|
|
215
|
+
};
|
|
216
|
+
|
|
217
|
+
if (internetAvailable) {
|
|
218
|
+
sendAlarmToCloud(alarm);
|
|
219
|
+
} else {
|
|
220
|
+
alarmQueue.push_back(alarm);
|
|
221
|
+
Serial.println("⚠ CRITICAL: Alarm queued (Internet offline)");
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
### 2. Failover to Backup Bridge
|
|
227
|
+
|
|
228
|
+
Switch to backup when primary fails:
|
|
229
|
+
|
|
230
|
+
```cpp
|
|
231
|
+
mesh.onBridgeStatusChanged([](uint32_t bridgeId, bool hasInternet) {
|
|
232
|
+
if (!hasInternet) {
|
|
233
|
+
// Primary bridge lost Internet - try backup
|
|
234
|
+
auto bridges = mesh.getBridges();
|
|
235
|
+
for (const auto& bridge : bridges) {
|
|
236
|
+
if (bridge.nodeId != bridgeId &&
|
|
237
|
+
bridge.isHealthy() &&
|
|
238
|
+
bridge.internetConnected) {
|
|
239
|
+
Serial.printf("Failover to backup bridge %u\n", bridge.nodeId);
|
|
240
|
+
// Route critical data through backup
|
|
241
|
+
break;
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
});
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
### 3. Smart Upload Batching
|
|
249
|
+
|
|
250
|
+
Batch data uploads when Internet is available:
|
|
251
|
+
|
|
252
|
+
```cpp
|
|
253
|
+
std::vector<SensorReading> dataBuffer;
|
|
254
|
+
|
|
255
|
+
Task taskBufferData(5000, TASK_FOREVER, []() {
|
|
256
|
+
// Always collect data
|
|
257
|
+
dataBuffer.push_back(readSensors());
|
|
258
|
+
|
|
259
|
+
// Upload when Internet available and buffer is large enough
|
|
260
|
+
if (mesh.hasInternetConnection() && dataBuffer.size() >= 10) {
|
|
261
|
+
uploadBatch(dataBuffer);
|
|
262
|
+
dataBuffer.clear();
|
|
263
|
+
}
|
|
264
|
+
});
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
### 4. User Feedback
|
|
268
|
+
|
|
269
|
+
Provide visual feedback about connectivity:
|
|
270
|
+
|
|
271
|
+
```cpp
|
|
272
|
+
mesh.onBridgeStatusChanged([](uint32_t bridgeId, bool hasInternet) {
|
|
273
|
+
if (hasInternet) {
|
|
274
|
+
digitalWrite(LED_GREEN, HIGH);
|
|
275
|
+
digitalWrite(LED_RED, LOW);
|
|
276
|
+
display.println("✓ Connected");
|
|
277
|
+
} else {
|
|
278
|
+
digitalWrite(LED_GREEN, LOW);
|
|
279
|
+
digitalWrite(LED_RED, HIGH);
|
|
280
|
+
display.println("⚠ Offline");
|
|
281
|
+
}
|
|
282
|
+
});
|
|
283
|
+
```
|
|
284
|
+
|
|
285
|
+
## Performance Considerations
|
|
286
|
+
|
|
287
|
+
### Network Overhead
|
|
288
|
+
- **Bridge nodes**: 1 broadcast every 30 seconds (~200 bytes)
|
|
289
|
+
- **Regular nodes**: Process 1 message every 30 seconds per bridge
|
|
290
|
+
- **Minimal impact**: <1% of typical mesh traffic
|
|
291
|
+
|
|
292
|
+
### Memory Usage
|
|
293
|
+
- **Bridge nodes**: ~50 bytes for broadcast task
|
|
294
|
+
- **Regular nodes**: ~30 bytes per tracked bridge
|
|
295
|
+
- **Example**: 3 bridges = 90 bytes overhead
|
|
296
|
+
|
|
297
|
+
### CPU Usage
|
|
298
|
+
- **Broadcasting**: <1ms every 30 seconds (negligible)
|
|
299
|
+
- **Processing**: <5ms per status received (negligible)
|
|
300
|
+
|
|
301
|
+
### Scalability
|
|
302
|
+
- Tested with up to 10 bridges
|
|
303
|
+
- Recommended maximum: 5 bridges per mesh
|
|
304
|
+
- No performance degradation observed
|
|
305
|
+
|
|
306
|
+
## Configuration Recommendations
|
|
307
|
+
|
|
308
|
+
### Production Settings
|
|
309
|
+
|
|
310
|
+
```cpp
|
|
311
|
+
// Standard reliability
|
|
312
|
+
mesh.setBridgeStatusInterval(30000); // 30 seconds
|
|
313
|
+
mesh.setBridgeTimeout(60000); // 60 seconds
|
|
314
|
+
|
|
315
|
+
// High reliability (critical systems)
|
|
316
|
+
mesh.setBridgeStatusInterval(15000); // 15 seconds
|
|
317
|
+
mesh.setBridgeTimeout(45000); // 45 seconds (3x interval)
|
|
318
|
+
|
|
319
|
+
// Low power (battery nodes)
|
|
320
|
+
mesh.setBridgeStatusInterval(60000); // 60 seconds
|
|
321
|
+
mesh.setBridgeTimeout(180000); // 180 seconds
|
|
322
|
+
```
|
|
323
|
+
|
|
324
|
+
### Guidelines
|
|
325
|
+
|
|
326
|
+
1. **Timeout should be 2-3x interval** to avoid false timeouts
|
|
327
|
+
2. **Shorter intervals = faster failover** but more network traffic
|
|
328
|
+
3. **Longer intervals = less traffic** but slower failover detection
|
|
329
|
+
4. **Consider your application's criticality** when choosing intervals
|
|
330
|
+
|
|
331
|
+
## Troubleshooting
|
|
332
|
+
|
|
333
|
+
### Bridge Status Not Received
|
|
334
|
+
|
|
335
|
+
**Symptoms**: `onBridgeStatusChanged()` never fires
|
|
336
|
+
|
|
337
|
+
**Possible Causes**:
|
|
338
|
+
1. Bridge not initialized with `initAsBridge()`
|
|
339
|
+
2. Broadcasting disabled: Check `enableBridgeStatusBroadcast(true)`
|
|
340
|
+
3. Network connectivity issues
|
|
341
|
+
4. Callback not registered before `init()`
|
|
342
|
+
|
|
343
|
+
**Solutions**:
|
|
344
|
+
```cpp
|
|
345
|
+
// Ensure bridge is properly initialized
|
|
346
|
+
mesh.initAsBridge(...); // Not just mesh.init()
|
|
347
|
+
|
|
348
|
+
// Verify broadcasting is enabled
|
|
349
|
+
mesh.enableBridgeStatusBroadcast(true);
|
|
350
|
+
|
|
351
|
+
// Register callback AFTER mesh.init()
|
|
352
|
+
mesh.init(...);
|
|
353
|
+
mesh.onBridgeStatusChanged(&callback);
|
|
354
|
+
|
|
355
|
+
// Check debug logs
|
|
356
|
+
mesh.setDebugMsgTypes(ERROR | STARTUP | CONNECTION | GENERAL);
|
|
357
|
+
```
|
|
358
|
+
|
|
359
|
+
### Callback Fires Too Often
|
|
360
|
+
|
|
361
|
+
**Symptoms**: Callback fires repeatedly with same status
|
|
362
|
+
|
|
363
|
+
**Possible Causes**:
|
|
364
|
+
1. Bridge Internet connection unstable
|
|
365
|
+
2. Multiple bridges changing status
|
|
366
|
+
3. Network congestion causing message delays
|
|
367
|
+
|
|
368
|
+
**Solutions**:
|
|
369
|
+
```cpp
|
|
370
|
+
// Add debouncing
|
|
371
|
+
static uint32_t lastCallback = 0;
|
|
372
|
+
const uint32_t DEBOUNCE_MS = 5000;
|
|
373
|
+
|
|
374
|
+
mesh.onBridgeStatusChanged([](uint32_t bridgeId, bool hasInternet) {
|
|
375
|
+
if (millis() - lastCallback < DEBOUNCE_MS) return;
|
|
376
|
+
lastCallback = millis();
|
|
377
|
+
|
|
378
|
+
// Your code here
|
|
379
|
+
});
|
|
380
|
+
|
|
381
|
+
// Or track state manually
|
|
382
|
+
static bool lastState = false;
|
|
383
|
+
mesh.onBridgeStatusChanged([](uint32_t bridgeId, bool hasInternet) {
|
|
384
|
+
if (hasInternet != lastState) {
|
|
385
|
+
lastState = hasInternet;
|
|
386
|
+
// Only process actual changes
|
|
387
|
+
}
|
|
388
|
+
});
|
|
389
|
+
```
|
|
390
|
+
|
|
391
|
+
### Bridge Marked as Unhealthy
|
|
392
|
+
|
|
393
|
+
**Symptoms**: `bridge.isHealthy()` returns `false`
|
|
394
|
+
|
|
395
|
+
**Possible Causes**:
|
|
396
|
+
1. No status received in 60 seconds
|
|
397
|
+
2. Bridge node crashed or rebooted
|
|
398
|
+
3. Network partition
|
|
399
|
+
4. Bridge stopped broadcasting
|
|
400
|
+
|
|
401
|
+
**Solutions**:
|
|
402
|
+
```cpp
|
|
403
|
+
// Increase timeout for less reliable networks
|
|
404
|
+
mesh.setBridgeTimeout(120000); // 2 minutes
|
|
405
|
+
|
|
406
|
+
// Check bridge health before using
|
|
407
|
+
auto primary = mesh.getPrimaryBridge();
|
|
408
|
+
if (primary && primary->isHealthy()) {
|
|
409
|
+
// Safe to use
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
// Monitor bridge last seen times
|
|
413
|
+
auto bridges = mesh.getBridges();
|
|
414
|
+
for (const auto& bridge : bridges) {
|
|
415
|
+
uint32_t ageMs = millis() - bridge.lastSeen;
|
|
416
|
+
Serial.printf("Bridge %u: last seen %u ms ago\n",
|
|
417
|
+
bridge.nodeId, ageMs);
|
|
418
|
+
}
|
|
419
|
+
```
|
|
420
|
+
|
|
421
|
+
## Examples
|
|
422
|
+
|
|
423
|
+
See the following examples for complete implementations:
|
|
424
|
+
|
|
425
|
+
1. **examples/bridge/bridge.ino**
|
|
426
|
+
- Bridge node with automatic status broadcasting
|
|
427
|
+
- Minimal configuration required
|
|
428
|
+
|
|
429
|
+
2. **examples/alteriomSensorNode/bridge_aware_sensor_node.ino**
|
|
430
|
+
- Regular node with bridge status callback
|
|
431
|
+
- Message queueing during outages
|
|
432
|
+
- Critical alarm handling
|
|
433
|
+
- Fish farm monitoring simulation
|
|
434
|
+
|
|
435
|
+
## API Reference
|
|
436
|
+
|
|
437
|
+
### Callback Registration
|
|
438
|
+
|
|
439
|
+
```cpp
|
|
440
|
+
void onBridgeStatusChanged(bridgeStatusChangedCallback_t callback);
|
|
441
|
+
```
|
|
442
|
+
|
|
443
|
+
**Parameters:**
|
|
444
|
+
- `callback`: Function with signature `void(uint32_t bridgeNodeId, bool internetAvailable)`
|
|
445
|
+
|
|
446
|
+
**Example:**
|
|
447
|
+
```cpp
|
|
448
|
+
mesh.onBridgeStatusChanged([](uint32_t bridgeId, bool hasInternet) {
|
|
449
|
+
Serial.printf("Bridge %u: Internet %s\n",
|
|
450
|
+
bridgeId, hasInternet ? "up" : "down");
|
|
451
|
+
});
|
|
452
|
+
```
|
|
453
|
+
|
|
454
|
+
### Query Methods
|
|
455
|
+
|
|
456
|
+
#### hasInternetConnection()
|
|
457
|
+
|
|
458
|
+
```cpp
|
|
459
|
+
bool hasInternetConnection();
|
|
460
|
+
```
|
|
461
|
+
|
|
462
|
+
**Returns:** `true` if at least one healthy bridge has Internet connectivity
|
|
463
|
+
|
|
464
|
+
**Example:**
|
|
465
|
+
```cpp
|
|
466
|
+
if (mesh.hasInternetConnection()) {
|
|
467
|
+
sendData();
|
|
468
|
+
}
|
|
469
|
+
```
|
|
470
|
+
|
|
471
|
+
#### getBridges()
|
|
472
|
+
|
|
473
|
+
```cpp
|
|
474
|
+
std::vector<BridgeInfo> getBridges();
|
|
475
|
+
```
|
|
476
|
+
|
|
477
|
+
**Returns:** Vector of all tracked bridges (healthy and unhealthy)
|
|
478
|
+
|
|
479
|
+
**Example:**
|
|
480
|
+
```cpp
|
|
481
|
+
auto bridges = mesh.getBridges();
|
|
482
|
+
Serial.printf("Known bridges: %d\n", bridges.size());
|
|
483
|
+
for (const auto& bridge : bridges) {
|
|
484
|
+
Serial.printf(" - %u: %s\n", bridge.nodeId,
|
|
485
|
+
bridge.isHealthy() ? "Healthy" : "Timeout");
|
|
486
|
+
}
|
|
487
|
+
```
|
|
488
|
+
|
|
489
|
+
#### getPrimaryBridge()
|
|
490
|
+
|
|
491
|
+
```cpp
|
|
492
|
+
BridgeInfo* getPrimaryBridge();
|
|
493
|
+
```
|
|
494
|
+
|
|
495
|
+
**Returns:** Pointer to primary bridge, or `nullptr` if none suitable
|
|
496
|
+
|
|
497
|
+
**Criteria:** Healthy + Internet connected + Best RSSI
|
|
498
|
+
|
|
499
|
+
**Example:**
|
|
500
|
+
```cpp
|
|
501
|
+
auto primary = mesh.getPrimaryBridge();
|
|
502
|
+
if (primary) {
|
|
503
|
+
Serial.printf("Primary: %u (RSSI: %d dBm)\n",
|
|
504
|
+
primary->nodeId, primary->routerRSSI);
|
|
505
|
+
} else {
|
|
506
|
+
Serial.println("No suitable bridge");
|
|
507
|
+
}
|
|
508
|
+
```
|
|
509
|
+
|
|
510
|
+
#### isBridge()
|
|
511
|
+
|
|
512
|
+
```cpp
|
|
513
|
+
bool isBridge();
|
|
514
|
+
```
|
|
515
|
+
|
|
516
|
+
**Returns:** `true` if this node is configured as a bridge (root node)
|
|
517
|
+
|
|
518
|
+
**Example:**
|
|
519
|
+
```cpp
|
|
520
|
+
if (mesh.isBridge()) {
|
|
521
|
+
// Bridge-specific logic
|
|
522
|
+
startStatusBroadcast();
|
|
523
|
+
}
|
|
524
|
+
```
|
|
525
|
+
|
|
526
|
+
### Configuration Methods
|
|
527
|
+
|
|
528
|
+
#### setBridgeStatusInterval()
|
|
529
|
+
|
|
530
|
+
```cpp
|
|
531
|
+
void setBridgeStatusInterval(uint32_t intervalMs);
|
|
532
|
+
```
|
|
533
|
+
|
|
534
|
+
**Parameters:**
|
|
535
|
+
- `intervalMs`: Broadcast interval in milliseconds (default: 30000)
|
|
536
|
+
|
|
537
|
+
**Example:**
|
|
538
|
+
```cpp
|
|
539
|
+
mesh.setBridgeStatusInterval(60000); // Broadcast every 60 seconds
|
|
540
|
+
```
|
|
541
|
+
|
|
542
|
+
#### setBridgeTimeout()
|
|
543
|
+
|
|
544
|
+
```cpp
|
|
545
|
+
void setBridgeTimeout(uint32_t timeoutMs);
|
|
546
|
+
```
|
|
547
|
+
|
|
548
|
+
**Parameters:**
|
|
549
|
+
- `timeoutMs`: Timeout threshold in milliseconds (default: 60000)
|
|
550
|
+
|
|
551
|
+
**Recommendation:** Set to 2-3x the broadcast interval
|
|
552
|
+
|
|
553
|
+
**Example:**
|
|
554
|
+
```cpp
|
|
555
|
+
mesh.setBridgeTimeout(90000); // 90 second timeout
|
|
556
|
+
```
|
|
557
|
+
|
|
558
|
+
#### enableBridgeStatusBroadcast()
|
|
559
|
+
|
|
560
|
+
```cpp
|
|
561
|
+
void enableBridgeStatusBroadcast(bool enabled);
|
|
562
|
+
```
|
|
563
|
+
|
|
564
|
+
**Parameters:**
|
|
565
|
+
- `enabled`: `true` to enable broadcasting (default), `false` to disable
|
|
566
|
+
|
|
567
|
+
**Note:** Only affects bridge nodes. Regular nodes always process received status.
|
|
568
|
+
|
|
569
|
+
**Example:**
|
|
570
|
+
```cpp
|
|
571
|
+
// Disable broadcasting temporarily
|
|
572
|
+
mesh.enableBridgeStatusBroadcast(false);
|
|
573
|
+
|
|
574
|
+
// Re-enable later
|
|
575
|
+
mesh.enableBridgeStatusBroadcast(true);
|
|
576
|
+
```
|
|
577
|
+
|
|
578
|
+
## Integration with Existing Features
|
|
579
|
+
|
|
580
|
+
### Compatible with:
|
|
581
|
+
- ✅ `initAsBridge()` - Automatic channel detection
|
|
582
|
+
- ✅ `onReceive()` - Normal message handling
|
|
583
|
+
- ✅ `onNewConnection()` / `onDroppedConnection()` - Connection callbacks
|
|
584
|
+
- ✅ OTA updates - Bridge status continues during updates
|
|
585
|
+
- ✅ Multiple bridges - Tracks all bridges independently
|
|
586
|
+
|
|
587
|
+
### Not compatible with:
|
|
588
|
+
- ❌ Non-bridge nodes cannot broadcast bridge status (ignored)
|
|
589
|
+
- ❌ Mesh networks without bridges (no status to track)
|
|
590
|
+
|
|
591
|
+
## Backward Compatibility
|
|
592
|
+
|
|
593
|
+
This feature is fully backward compatible:
|
|
594
|
+
|
|
595
|
+
- **Old bridge nodes**: Work normally, just don't broadcast status
|
|
596
|
+
- **Old regular nodes**: Ignore type 610 packages
|
|
597
|
+
- **Mixed networks**: Old and new nodes coexist without issues
|
|
598
|
+
- **No breaking changes**: All existing APIs remain unchanged
|
|
599
|
+
|
|
600
|
+
## Future Enhancements
|
|
601
|
+
|
|
602
|
+
Potential improvements for future versions:
|
|
603
|
+
|
|
604
|
+
1. **Bridge Load Balancing**: Route based on bridge load, not just RSSI
|
|
605
|
+
2. **Historical Tracking**: Track uptime and reliability over time
|
|
606
|
+
3. **Predictive Failover**: Predict bridge failures before they occur
|
|
607
|
+
4. **Mesh-wide Health Score**: Aggregate metric for entire mesh health
|
|
608
|
+
5. **Bridge Discovery**: Active scanning for backup bridges
|
|
609
|
+
|
|
610
|
+
## Contributing
|
|
611
|
+
|
|
612
|
+
To contribute improvements:
|
|
613
|
+
|
|
614
|
+
1. Follow the existing code style
|
|
615
|
+
2. Add tests for new functionality
|
|
616
|
+
3. Update documentation
|
|
617
|
+
4. Test with multiple bridges
|
|
618
|
+
5. Consider memory and performance impact
|
|
619
|
+
|
|
620
|
+
## License
|
|
621
|
+
|
|
622
|
+
Same as painlessMesh library - GPL 3.0
|
|
623
|
+
|
|
624
|
+
## Credits
|
|
625
|
+
|
|
626
|
+
- Feature requested by: @woodlist (fish farm monitoring use case)
|
|
627
|
+
- Implementation: GitHub Copilot + painlessMesh team
|
|
628
|
+
- Testing: Alteriom community
|
|
629
|
+
|
|
630
|
+
## See Also
|
|
631
|
+
|
|
632
|
+
- [BRIDGE_TO_INTERNET.md](BRIDGE_TO_INTERNET.md) - Bridge setup guide
|
|
633
|
+
- [Issue #63](https://github.com/Alteriom/painlessMesh/issues/63) - Feature request
|
|
634
|
+
- [Issue #64](https://github.com/Alteriom/painlessMesh/issues/64) - Bridge failover (enabled by this feature)
|
|
635
|
+
- [Issue #66](https://github.com/Alteriom/painlessMesh/issues/66) - Message queueing (enabled by this feature)
|