@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,171 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* painlessMesh Diagnostics API Example
|
|
3
|
+
*
|
|
4
|
+
* This example demonstrates the enhanced diagnostics API for bridge operations.
|
|
5
|
+
* It shows how to:
|
|
6
|
+
* - Enable diagnostics tracking
|
|
7
|
+
* - Get bridge status information
|
|
8
|
+
* - Monitor election history
|
|
9
|
+
* - Test bridge connectivity
|
|
10
|
+
* - Generate diagnostic reports
|
|
11
|
+
* - Export topology for visualization
|
|
12
|
+
*
|
|
13
|
+
* Hardware: ESP32 or ESP8266
|
|
14
|
+
*
|
|
15
|
+
* Usage:
|
|
16
|
+
* 1. Upload to multiple devices
|
|
17
|
+
* 2. Configure one device as a bridge (see bridgeExample)
|
|
18
|
+
* 3. Regular nodes will use this diagnostics API to monitor mesh health
|
|
19
|
+
* 4. Open Serial Monitor to see diagnostic information
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
#include "painlessMesh.h"
|
|
23
|
+
|
|
24
|
+
#define MESH_PREFIX "DiagnosticsMesh"
|
|
25
|
+
#define MESH_PASSWORD "diagnostic123"
|
|
26
|
+
#define MESH_PORT 5555
|
|
27
|
+
|
|
28
|
+
Scheduler userScheduler;
|
|
29
|
+
painlessMesh mesh;
|
|
30
|
+
|
|
31
|
+
// Task to periodically print diagnostics
|
|
32
|
+
Task taskPrintDiagnostics(30000, TASK_FOREVER, []() {
|
|
33
|
+
Serial.println("\n" + mesh.getDiagnosticReport());
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
// Task to test bridge connectivity
|
|
37
|
+
Task taskTestBridge(60000, TASK_FOREVER, []() {
|
|
38
|
+
Serial.println("\n=== Testing Bridge Connectivity ===");
|
|
39
|
+
auto result = mesh.testBridgeConnectivity();
|
|
40
|
+
|
|
41
|
+
if (result.success) {
|
|
42
|
+
Serial.printf("✓ Bridge test PASSED: %s\n", result.message.c_str());
|
|
43
|
+
Serial.printf(" Latency: %u ms\n", result.latencyMs);
|
|
44
|
+
Serial.printf(" Internet available: %s\n", result.internetReachable ? "Yes" : "No");
|
|
45
|
+
} else {
|
|
46
|
+
Serial.printf("✗ Bridge test FAILED: %s\n", result.message.c_str());
|
|
47
|
+
}
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
// Task to monitor bridge status
|
|
51
|
+
Task taskMonitorBridge(15000, TASK_FOREVER, []() {
|
|
52
|
+
auto status = mesh.getBridgeStatus();
|
|
53
|
+
|
|
54
|
+
Serial.println("\n=== Bridge Status ===");
|
|
55
|
+
Serial.printf("Node ID: %u\n", mesh.getNodeId());
|
|
56
|
+
Serial.printf("Role: %s\n", status.role.c_str());
|
|
57
|
+
Serial.printf("Is Bridge: %s\n", status.isBridge ? "Yes" : "No");
|
|
58
|
+
Serial.printf("Internet Connected: %s\n", status.internetConnected ? "Yes" : "No");
|
|
59
|
+
|
|
60
|
+
if (status.bridgeNodeId != 0) {
|
|
61
|
+
Serial.printf("Bridge Node: %u (RSSI: %d dBm)\n",
|
|
62
|
+
status.bridgeNodeId, status.bridgeRSSI);
|
|
63
|
+
Serial.printf("Time since bridge change: %u seconds\n",
|
|
64
|
+
status.timeSinceBridgeChange / 1000);
|
|
65
|
+
} else {
|
|
66
|
+
Serial.println("No bridge available");
|
|
67
|
+
}
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
// Task to show election history
|
|
71
|
+
Task taskShowElections(120000, TASK_FOREVER, []() {
|
|
72
|
+
auto history = mesh.getElectionHistory();
|
|
73
|
+
|
|
74
|
+
if (history.size() > 0) {
|
|
75
|
+
Serial.println("\n=== Election History ===");
|
|
76
|
+
for (const auto& election : history) {
|
|
77
|
+
Serial.printf("Winner: %u (RSSI: %d dBm, %u candidates) - %s\n",
|
|
78
|
+
election.winnerNodeId, election.winnerRSSI,
|
|
79
|
+
election.candidateCount, election.reason.c_str());
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
// Task to export topology for visualization
|
|
85
|
+
Task taskExportTopology(300000, TASK_FOREVER, []() {
|
|
86
|
+
Serial.println("\n=== Mesh Topology (GraphViz DOT format) ===");
|
|
87
|
+
Serial.println(mesh.exportTopologyDOT());
|
|
88
|
+
Serial.println("Copy above to http://www.webgraphviz.com/ to visualize");
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
void receivedCallback(uint32_t from, String &msg) {
|
|
92
|
+
Serial.printf("Received from %u: %s\n", from, msg.c_str());
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
void newConnectionCallback(uint32_t nodeId) {
|
|
96
|
+
Serial.printf("New Connection: %u\n", nodeId);
|
|
97
|
+
|
|
98
|
+
// Get last bridge change when new node connects
|
|
99
|
+
auto event = mesh.getLastBridgeChange();
|
|
100
|
+
if (event.timestamp > 0) {
|
|
101
|
+
Serial.printf("Last bridge change: %u -> %u (%s)\n",
|
|
102
|
+
event.oldBridgeId, event.newBridgeId, event.reason.c_str());
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
void changedConnectionCallback() {
|
|
107
|
+
Serial.println("Connections changed");
|
|
108
|
+
Serial.printf("Mesh nodes: %d\n", mesh.getNodeList().size());
|
|
109
|
+
|
|
110
|
+
// Show Internet path for this node
|
|
111
|
+
auto path = mesh.getInternetPath(mesh.getNodeId());
|
|
112
|
+
if (path.size() > 0) {
|
|
113
|
+
Serial.print("Path to Internet: ");
|
|
114
|
+
for (auto nodeId : path) {
|
|
115
|
+
Serial.printf("%u -> ", nodeId);
|
|
116
|
+
}
|
|
117
|
+
Serial.println("Internet");
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
void bridgeStatusChangedCallback(uint32_t bridgeNodeId, bool hasInternet) {
|
|
122
|
+
Serial.printf("\n!!! Bridge Status Changed !!!\n");
|
|
123
|
+
Serial.printf("Bridge %u Internet: %s\n",
|
|
124
|
+
bridgeNodeId, hasInternet ? "Connected" : "Disconnected");
|
|
125
|
+
|
|
126
|
+
// Run immediate connectivity test
|
|
127
|
+
auto result = mesh.testBridgeConnectivity();
|
|
128
|
+
Serial.printf("Connectivity test: %s\n", result.message.c_str());
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
void setup() {
|
|
132
|
+
Serial.begin(115200);
|
|
133
|
+
|
|
134
|
+
Serial.println("\n\n=== painlessMesh Diagnostics Example ===");
|
|
135
|
+
Serial.println("Initializing mesh network...");
|
|
136
|
+
|
|
137
|
+
// Initialize mesh
|
|
138
|
+
mesh.setDebugMsgTypes(ERROR | STARTUP | CONNECTION);
|
|
139
|
+
mesh.init(MESH_PREFIX, MESH_PASSWORD, &userScheduler, MESH_PORT);
|
|
140
|
+
|
|
141
|
+
// Enable diagnostics tracking
|
|
142
|
+
Serial.println("Enabling diagnostics...");
|
|
143
|
+
mesh.enableDiagnostics(true);
|
|
144
|
+
|
|
145
|
+
// Setup callbacks
|
|
146
|
+
mesh.onReceive(&receivedCallback);
|
|
147
|
+
mesh.onNewConnection(&newConnectionCallback);
|
|
148
|
+
mesh.onChangedConnections(&changedConnectionCallback);
|
|
149
|
+
mesh.onBridgeStatusChanged(&bridgeStatusChangedCallback);
|
|
150
|
+
|
|
151
|
+
// Add diagnostic tasks
|
|
152
|
+
userScheduler.addTask(taskPrintDiagnostics);
|
|
153
|
+
userScheduler.addTask(taskTestBridge);
|
|
154
|
+
userScheduler.addTask(taskMonitorBridge);
|
|
155
|
+
userScheduler.addTask(taskShowElections);
|
|
156
|
+
userScheduler.addTask(taskExportTopology);
|
|
157
|
+
|
|
158
|
+
// Enable tasks
|
|
159
|
+
taskPrintDiagnostics.enable();
|
|
160
|
+
taskTestBridge.enable();
|
|
161
|
+
taskMonitorBridge.enable();
|
|
162
|
+
taskShowElections.enable();
|
|
163
|
+
taskExportTopology.enable();
|
|
164
|
+
|
|
165
|
+
Serial.println("Setup complete!");
|
|
166
|
+
Serial.printf("Node ID: %u\n", mesh.getNodeId());
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
void loop() {
|
|
170
|
+
mesh.update();
|
|
171
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
[platformio]
|
|
2
|
+
src_dir = .
|
|
3
|
+
|
|
4
|
+
[env]
|
|
5
|
+
lib_deps =
|
|
6
|
+
bblanchon/ArduinoJson
|
|
7
|
+
arkhipenko/TaskScheduler
|
|
8
|
+
|
|
9
|
+
lib_ldf_mode = deep+
|
|
10
|
+
[env:esp8266]
|
|
11
|
+
platform = espressif8266
|
|
12
|
+
board = nodemcuv2
|
|
13
|
+
framework = arduino
|
|
14
|
+
lib_extra_dirs = ../../
|
|
15
|
+
lib_deps =
|
|
16
|
+
${env.lib_deps} ; Inherit common dependencies
|
|
17
|
+
esp32async/ESPAsyncTCP@^2.0.0 ; Only for ESP8266
|
|
18
|
+
|
|
19
|
+
[env:esp32]
|
|
20
|
+
platform = espressif32
|
|
21
|
+
board = esp32dev
|
|
22
|
+
framework = arduino
|
|
23
|
+
lib_extra_dirs = ../../
|
|
24
|
+
lib_deps =
|
|
25
|
+
${env.lib_deps} ; Inherit common dependencies
|
|
26
|
+
esp32async/AsyncTCP
|
|
@@ -0,0 +1,346 @@
|
|
|
1
|
+
# Multi-Bridge Example
|
|
2
|
+
|
|
3
|
+
This example demonstrates advanced multi-bridge coordination for high-availability mesh networks with multiple simultaneous Internet gateways.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
In a multi-bridge deployment, multiple nodes act as bridges to the Internet simultaneously. This provides:
|
|
8
|
+
|
|
9
|
+
- **High Availability**: Zero downtime during failover
|
|
10
|
+
- **Load Balancing**: Distribute traffic across multiple uplinks
|
|
11
|
+
- **Geographic Distribution**: Bridges in different locations
|
|
12
|
+
- **Redundancy**: Multiple paths to Internet
|
|
13
|
+
|
|
14
|
+
## Architecture
|
|
15
|
+
|
|
16
|
+
```
|
|
17
|
+
Internet Connection A → Primary Bridge (Priority 10)
|
|
18
|
+
↓
|
|
19
|
+
Mesh Network
|
|
20
|
+
↓
|
|
21
|
+
Internet Connection B → Secondary Bridge (Priority 5)
|
|
22
|
+
↓
|
|
23
|
+
Regular Nodes
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## Files
|
|
27
|
+
|
|
28
|
+
- **primary_bridge.ino** - Primary bridge with highest priority (10)
|
|
29
|
+
- **secondary_bridge.ino** - Secondary bridge for redundancy (priority 5)
|
|
30
|
+
- **regular_node.ino** - Regular mesh node that uses bridges
|
|
31
|
+
|
|
32
|
+
## How It Works
|
|
33
|
+
|
|
34
|
+
### Bridge Priority
|
|
35
|
+
|
|
36
|
+
Bridges are assigned priorities (1-10):
|
|
37
|
+
- **10 = Primary**: Handles all traffic when available
|
|
38
|
+
- **5 = Secondary**: Hot standby, takes over if primary fails
|
|
39
|
+
- **1 = Standby**: Only used if all higher priority bridges fail
|
|
40
|
+
|
|
41
|
+
### Bridge Selection Strategies
|
|
42
|
+
|
|
43
|
+
1. **PRIORITY_BASED** (default): Always use highest priority available bridge
|
|
44
|
+
2. **ROUND_ROBIN**: Distribute load evenly across all bridges
|
|
45
|
+
3. **BEST_SIGNAL**: Use bridge with best WiFi signal strength
|
|
46
|
+
|
|
47
|
+
### Coordination Protocol
|
|
48
|
+
|
|
49
|
+
Bridges exchange coordination messages (Type 613) containing:
|
|
50
|
+
- Priority level
|
|
51
|
+
- Current role (primary/secondary/standby)
|
|
52
|
+
- Load percentage
|
|
53
|
+
- List of known peer bridges
|
|
54
|
+
|
|
55
|
+
Regular nodes track all bridges and automatically select the best one based on the configured strategy.
|
|
56
|
+
|
|
57
|
+
## Setup Instructions
|
|
58
|
+
|
|
59
|
+
### Hardware Required
|
|
60
|
+
|
|
61
|
+
- 3+ ESP32 or ESP8266 devices
|
|
62
|
+
- 1-2 WiFi routers with Internet connection
|
|
63
|
+
- USB cables for programming
|
|
64
|
+
|
|
65
|
+
### Configuration
|
|
66
|
+
|
|
67
|
+
#### Primary Bridge
|
|
68
|
+
1. Flash `primary_bridge.ino` to first ESP32
|
|
69
|
+
2. Configure router credentials:
|
|
70
|
+
```cpp
|
|
71
|
+
#define ROUTER_SSID "YourPrimaryRouter"
|
|
72
|
+
#define ROUTER_PASSWORD "routerpass"
|
|
73
|
+
```
|
|
74
|
+
3. Priority is set to 10 (highest)
|
|
75
|
+
|
|
76
|
+
#### Secondary Bridge
|
|
77
|
+
1. Flash `secondary_bridge.ino` to second ESP32
|
|
78
|
+
2. Configure router credentials (can be same or different router):
|
|
79
|
+
```cpp
|
|
80
|
+
#define ROUTER_SSID "YourSecondaryRouter"
|
|
81
|
+
#define ROUTER_PASSWORD "routerpass2"
|
|
82
|
+
```
|
|
83
|
+
3. Priority is set to 5 (secondary)
|
|
84
|
+
|
|
85
|
+
#### Regular Nodes
|
|
86
|
+
1. Flash `regular_node.ino` to remaining ESP32 devices
|
|
87
|
+
2. No router credentials needed
|
|
88
|
+
3. They automatically discover and use bridges
|
|
89
|
+
|
|
90
|
+
### Testing
|
|
91
|
+
|
|
92
|
+
#### Normal Operation
|
|
93
|
+
1. Power on primary bridge - should connect to router
|
|
94
|
+
2. Power on secondary bridge - should connect to router
|
|
95
|
+
3. Power on regular nodes - should join mesh
|
|
96
|
+
4. Regular nodes should prefer primary bridge (priority 10)
|
|
97
|
+
|
|
98
|
+
#### Failover Test
|
|
99
|
+
1. Disconnect primary bridge from power
|
|
100
|
+
2. Regular nodes should detect loss within 60 seconds
|
|
101
|
+
3. Regular nodes should automatically switch to secondary bridge
|
|
102
|
+
4. No data loss or interruption
|
|
103
|
+
|
|
104
|
+
#### Load Balancing Test (Round-Robin)
|
|
105
|
+
1. On regular nodes, change strategy:
|
|
106
|
+
```cpp
|
|
107
|
+
mesh.setBridgeSelectionStrategy(painlessMesh::ROUND_ROBIN);
|
|
108
|
+
```
|
|
109
|
+
2. Messages should alternate between bridges
|
|
110
|
+
|
|
111
|
+
## Bridge Selection Strategies
|
|
112
|
+
|
|
113
|
+
### Priority-Based (Default)
|
|
114
|
+
|
|
115
|
+
Best for most deployments. Uses highest priority bridge available.
|
|
116
|
+
|
|
117
|
+
```cpp
|
|
118
|
+
mesh.setBridgeSelectionStrategy(painlessMesh::PRIORITY_BASED);
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
**When to use:**
|
|
122
|
+
- Production systems needing predictable routing
|
|
123
|
+
- When you have primary and backup Internet connections
|
|
124
|
+
- Clear preference for one connection over another
|
|
125
|
+
|
|
126
|
+
### Round-Robin Load Balancing
|
|
127
|
+
|
|
128
|
+
Distributes traffic evenly across all bridges.
|
|
129
|
+
|
|
130
|
+
```cpp
|
|
131
|
+
mesh.setBridgeSelectionStrategy(painlessMesh::ROUND_ROBIN);
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
**When to use:**
|
|
135
|
+
- High traffic scenarios
|
|
136
|
+
- Multiple equal-quality Internet connections
|
|
137
|
+
- Load distribution is important
|
|
138
|
+
|
|
139
|
+
### Best Signal
|
|
140
|
+
|
|
141
|
+
Always uses bridge with strongest WiFi signal.
|
|
142
|
+
|
|
143
|
+
```cpp
|
|
144
|
+
mesh.setBridgeSelectionStrategy(painlessMesh::BEST_SIGNAL);
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
**When to use:**
|
|
148
|
+
- Large mesh networks
|
|
149
|
+
- Nodes move around
|
|
150
|
+
- Signal strength affects performance significantly
|
|
151
|
+
|
|
152
|
+
## API Reference
|
|
153
|
+
|
|
154
|
+
### Multi-Bridge Configuration
|
|
155
|
+
|
|
156
|
+
```cpp
|
|
157
|
+
// Enable multi-bridge mode
|
|
158
|
+
mesh.enableMultiBridge(true);
|
|
159
|
+
|
|
160
|
+
// Set selection strategy
|
|
161
|
+
mesh.setBridgeSelectionStrategy(painlessMesh::PRIORITY_BASED);
|
|
162
|
+
mesh.setBridgeSelectionStrategy(painlessMesh::ROUND_ROBIN);
|
|
163
|
+
mesh.setBridgeSelectionStrategy(painlessMesh::BEST_SIGNAL);
|
|
164
|
+
|
|
165
|
+
// Set max concurrent bridges (default: 2, max: 5)
|
|
166
|
+
mesh.setMaxBridges(3);
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
### Bridge Initialization
|
|
170
|
+
|
|
171
|
+
```cpp
|
|
172
|
+
// Initialize as bridge with priority
|
|
173
|
+
mesh.initAsBridge(meshSSID, meshPassword,
|
|
174
|
+
routerSSID, routerPassword,
|
|
175
|
+
&scheduler, port, priority);
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
### Bridge Discovery
|
|
179
|
+
|
|
180
|
+
```cpp
|
|
181
|
+
// Get list of active bridge node IDs
|
|
182
|
+
std::vector<uint32_t> bridges = mesh.getActiveBridges();
|
|
183
|
+
|
|
184
|
+
// Get recommended bridge for next message
|
|
185
|
+
uint32_t bridgeId = mesh.getRecommendedBridge();
|
|
186
|
+
|
|
187
|
+
// Manually select bridge for next transmission
|
|
188
|
+
mesh.selectBridge(bridgeId);
|
|
189
|
+
|
|
190
|
+
// Check if multi-bridge is enabled
|
|
191
|
+
bool enabled = mesh.isMultiBridgeEnabled();
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
## Expected Output
|
|
195
|
+
|
|
196
|
+
### Primary Bridge
|
|
197
|
+
```
|
|
198
|
+
=== Multi-Bridge: PRIMARY BRIDGE ===
|
|
199
|
+
|
|
200
|
+
=== Bridge Mode Initialization (Priority: 10, Role: primary) ===
|
|
201
|
+
✓ Router connected on channel 6
|
|
202
|
+
✓ Router IP: 192.168.1.100
|
|
203
|
+
Bridge coordination enabled (priority: 10, role: primary)
|
|
204
|
+
|
|
205
|
+
=== Primary Bridge Status ===
|
|
206
|
+
Node ID: 123456
|
|
207
|
+
Connected Nodes: 3
|
|
208
|
+
Active Bridges: 2
|
|
209
|
+
- Bridge: 123456 (ME)
|
|
210
|
+
- Bridge: 789012
|
|
211
|
+
Recommended Bridge: 123456
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
### Secondary Bridge
|
|
215
|
+
```
|
|
216
|
+
=== Multi-Bridge: SECONDARY BRIDGE ===
|
|
217
|
+
|
|
218
|
+
=== Bridge Mode Initialization (Priority: 5, Role: secondary) ===
|
|
219
|
+
✓ Router connected on channel 6
|
|
220
|
+
✓ Router IP: 192.168.1.101
|
|
221
|
+
Bridge coordination enabled (priority: 5, role: secondary)
|
|
222
|
+
|
|
223
|
+
=== Secondary Bridge Status ===
|
|
224
|
+
Node ID: 789012
|
|
225
|
+
Connected Nodes: 3
|
|
226
|
+
Active Bridges: 2
|
|
227
|
+
- Bridge: 123456
|
|
228
|
+
- Bridge: 789012 (ME)
|
|
229
|
+
Recommended Bridge: 123456
|
|
230
|
+
✓ Standby mode - primary bridge is active
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
### Regular Node
|
|
234
|
+
```
|
|
235
|
+
=== Multi-Bridge: REGULAR NODE ===
|
|
236
|
+
|
|
237
|
+
=== Network Status ===
|
|
238
|
+
Node ID: 456789
|
|
239
|
+
Connected Nodes: 4
|
|
240
|
+
Active Bridges: 2
|
|
241
|
+
- Bridge: 123456
|
|
242
|
+
- Bridge: 789012
|
|
243
|
+
Internet Available: YES
|
|
244
|
+
|
|
245
|
+
--- Sending Sensor Data ---
|
|
246
|
+
Recommended Bridge: 123456
|
|
247
|
+
Message: Temperature: 22.3°C, Humidity: 45.2%
|
|
248
|
+
✓ Sent to bridge
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
## Troubleshooting
|
|
252
|
+
|
|
253
|
+
### No Bridges Found
|
|
254
|
+
|
|
255
|
+
**Symptoms**: `Active Bridges: 0`
|
|
256
|
+
|
|
257
|
+
**Solutions**:
|
|
258
|
+
- Verify bridge nodes are powered on
|
|
259
|
+
- Check router credentials are correct
|
|
260
|
+
- Ensure bridges successfully connected to router
|
|
261
|
+
- Check all devices use same MESH_PREFIX and MESH_PASSWORD
|
|
262
|
+
|
|
263
|
+
### Bridges Not Coordinating
|
|
264
|
+
|
|
265
|
+
**Symptoms**: Bridges don't see each other
|
|
266
|
+
|
|
267
|
+
**Solutions**:
|
|
268
|
+
- Verify `enableMultiBridge(true)` is called on all bridges
|
|
269
|
+
- Check mesh connectivity between bridges
|
|
270
|
+
- Look for "Bridge coordination" messages in logs
|
|
271
|
+
- Ensure bridges are on same mesh network
|
|
272
|
+
|
|
273
|
+
### Failover Not Working
|
|
274
|
+
|
|
275
|
+
**Symptoms**: No automatic switch to secondary
|
|
276
|
+
|
|
277
|
+
**Solutions**:
|
|
278
|
+
- Check `mesh.onBridgeStatusChanged()` callback is registered
|
|
279
|
+
- Verify secondary bridge has Internet connection
|
|
280
|
+
- Ensure bridge status broadcasts are enabled
|
|
281
|
+
- Check 60-second timeout hasn't been exceeded
|
|
282
|
+
|
|
283
|
+
### Wrong Bridge Selected
|
|
284
|
+
|
|
285
|
+
**Symptoms**: Secondary used instead of primary
|
|
286
|
+
|
|
287
|
+
**Solutions**:
|
|
288
|
+
- Verify priorities are correct (primary=10, secondary=5)
|
|
289
|
+
- Check strategy is PRIORITY_BASED
|
|
290
|
+
- Ensure primary bridge has Internet connection
|
|
291
|
+
- Look for priority values in coordination messages
|
|
292
|
+
|
|
293
|
+
## Advanced Configuration
|
|
294
|
+
|
|
295
|
+
### Three or More Bridges
|
|
296
|
+
|
|
297
|
+
```cpp
|
|
298
|
+
// Bridge 1: Primary (priority 10)
|
|
299
|
+
mesh.initAsBridge(ssid, pass, router1, pass1, &sched, port, 10);
|
|
300
|
+
|
|
301
|
+
// Bridge 2: Secondary (priority 7)
|
|
302
|
+
mesh.initAsBridge(ssid, pass, router2, pass2, &sched, port, 7);
|
|
303
|
+
|
|
304
|
+
// Bridge 3: Tertiary (priority 3)
|
|
305
|
+
mesh.initAsBridge(ssid, pass, router3, pass3, &sched, port, 3);
|
|
306
|
+
```
|
|
307
|
+
|
|
308
|
+
### Geographic Distribution
|
|
309
|
+
|
|
310
|
+
Use when mesh spans multiple buildings:
|
|
311
|
+
|
|
312
|
+
```
|
|
313
|
+
Building A: Bridge1 (priority 10) → Internet A
|
|
314
|
+
Building B: Bridge2 (priority 10) → Internet B
|
|
315
|
+
```
|
|
316
|
+
|
|
317
|
+
Both bridges have same priority, nodes use closest one (BEST_SIGNAL strategy).
|
|
318
|
+
|
|
319
|
+
### Traffic Shaping (Future)
|
|
320
|
+
|
|
321
|
+
Route different message types to different bridges:
|
|
322
|
+
|
|
323
|
+
```cpp
|
|
324
|
+
// Coming in future release
|
|
325
|
+
mesh.setBridgeSelectionStrategy(mesh.TRAFFIC_TYPE);
|
|
326
|
+
mesh.routeTrafficType(ALARM_MESSAGE, bridge1);
|
|
327
|
+
mesh.routeTrafficType(SENSOR_DATA, bridge2);
|
|
328
|
+
```
|
|
329
|
+
|
|
330
|
+
## Dependencies
|
|
331
|
+
|
|
332
|
+
- Issue #63: Bridge Status Broadcast ✅ (IMPLEMENTED)
|
|
333
|
+
- Issue #64: Bridge Failover ✅ (IMPLEMENTED)
|
|
334
|
+
- Issue #65: Multi-Bridge Coordination ✅ (THIS FEATURE)
|
|
335
|
+
|
|
336
|
+
## Related Examples
|
|
337
|
+
|
|
338
|
+
- `examples/bridge/` - Basic single bridge setup
|
|
339
|
+
- `examples/bridge_failover/` - Single bridge with automatic failover
|
|
340
|
+
- `examples/bridgeAwareSensorNode/` - Node that checks bridge status
|
|
341
|
+
|
|
342
|
+
## Further Reading
|
|
343
|
+
|
|
344
|
+
- [Bridge Architecture Documentation](../../BRIDGE_TO_INTERNET.md)
|
|
345
|
+
- [Bridge Health Monitoring](../../BRIDGE_HEALTH_MONITORING_IMPLEMENTATION.md)
|
|
346
|
+
- [painlessMesh Wiki](https://gitlab.com/painlessMesh/painlessMesh/-/wikis/home)
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
//************************************************************
|
|
2
|
+
// Multi-Bridge Example: Primary Bridge Node
|
|
3
|
+
//
|
|
4
|
+
// This example demonstrates a primary bridge (priority 10) in a
|
|
5
|
+
// multi-bridge deployment. Use with secondary_bridge.ino for
|
|
6
|
+
// redundancy and load balancing.
|
|
7
|
+
//
|
|
8
|
+
// Features demonstrated:
|
|
9
|
+
// - Primary bridge with highest priority
|
|
10
|
+
// - Multi-bridge coordination
|
|
11
|
+
// - Load reporting and bridge discovery
|
|
12
|
+
// - Automatic role management
|
|
13
|
+
//************************************************************
|
|
14
|
+
|
|
15
|
+
#include "painlessMesh.h"
|
|
16
|
+
|
|
17
|
+
#define MESH_PREFIX "MultiBridgeMesh"
|
|
18
|
+
#define MESH_PASSWORD "meshpassword"
|
|
19
|
+
#define MESH_PORT 5555
|
|
20
|
+
|
|
21
|
+
// Primary router connection (high-speed Internet)
|
|
22
|
+
#define ROUTER_SSID "PrimaryRouter"
|
|
23
|
+
#define ROUTER_PASSWORD "routerpass"
|
|
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=== Primary 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
|
+
Serial.println("=============================\n");
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
void receivedCallback(uint32_t from, String& msg) {
|
|
49
|
+
Serial.printf("Received from %u: %s\n", from, msg.c_str());
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
void newConnectionCallback(uint32_t nodeId) {
|
|
53
|
+
Serial.printf("New Connection, nodeId = %u\n", nodeId);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
void changedConnectionCallback() {
|
|
57
|
+
Serial.printf("Changed connections. Node count: %d\n", mesh.getNodeList().size());
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
void setup() {
|
|
61
|
+
Serial.begin(115200);
|
|
62
|
+
delay(2000);
|
|
63
|
+
|
|
64
|
+
Serial.println("\n\n=== Multi-Bridge: PRIMARY BRIDGE ===\n");
|
|
65
|
+
|
|
66
|
+
// Initialize mesh as primary bridge with priority 10 (highest)
|
|
67
|
+
mesh.setDebugMsgTypes(ERROR | STARTUP | CONNECTION);
|
|
68
|
+
|
|
69
|
+
// Enable multi-bridge coordination mode
|
|
70
|
+
mesh.enableMultiBridge(true);
|
|
71
|
+
|
|
72
|
+
// Set bridge selection strategy (PRIORITY_BASED is default)
|
|
73
|
+
mesh.setBridgeSelectionStrategy(painlessMesh::PRIORITY_BASED);
|
|
74
|
+
|
|
75
|
+
// Initialize as bridge with priority 10 (primary)
|
|
76
|
+
mesh.initAsBridge(MESH_PREFIX, MESH_PASSWORD,
|
|
77
|
+
ROUTER_SSID, ROUTER_PASSWORD,
|
|
78
|
+
&userScheduler, MESH_PORT, 10); // Priority 10 = Primary
|
|
79
|
+
|
|
80
|
+
mesh.onReceive(&receivedCallback);
|
|
81
|
+
mesh.onNewConnection(&newConnectionCallback);
|
|
82
|
+
mesh.onChangedConnections(&changedConnectionCallback);
|
|
83
|
+
|
|
84
|
+
// Add status reporting task
|
|
85
|
+
userScheduler.addTask(taskBridgeStatus);
|
|
86
|
+
taskBridgeStatus.enable();
|
|
87
|
+
|
|
88
|
+
Serial.println("\n=== Primary Bridge Ready ===");
|
|
89
|
+
Serial.println("This node is the PRIMARY bridge (Priority 10)");
|
|
90
|
+
Serial.println("It will be preferred for all mesh traffic");
|
|
91
|
+
Serial.println("If it fails, the secondary bridge will take over automatically\n");
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
void loop() {
|
|
95
|
+
mesh.update();
|
|
96
|
+
}
|