@alteriom/painlessmesh 1.8.4 → 1.8.6
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 +50 -0
- package/docs/design/.gitkeep +1 -0
- package/docs/design/STATION_CREDENTIALS_DESIGN.md +182 -0
- package/examples/bridge_failover/README.md +63 -10
- package/examples/bridge_failover/bridge_failover.ino +16 -4
- package/examples/ntpTimeSyncBridge/ntpTimeSyncBridge.ino +1 -1
- package/examples/ntpTimeSyncNode/ntpTimeSyncNode.ino +1 -1
- package/library.json +1 -1
- package/library.properties +1 -1
- package/package.json +1 -1
- package/src/arduino/wifi.hpp +50 -3
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,56 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [1.8.6] - 2025-11-12
|
|
11
|
+
|
|
12
|
+
### Fixed
|
|
13
|
+
|
|
14
|
+
- **Bridge Failover Auto-Election (Issue #117)** - Bridge election now triggers when no initial bridge exists
|
|
15
|
+
- Added periodic monitoring task (30s interval) that detects absence of healthy bridge
|
|
16
|
+
- Activates after 60s startup grace period to allow network stabilization
|
|
17
|
+
- Randomized election delay (1-3s) prevents thundering herd problem
|
|
18
|
+
- Respects existing safeguards: election state, 60s cooldown, router visibility
|
|
19
|
+
- **Before**: No initial bridge → no election → mesh stays bridgeless indefinitely
|
|
20
|
+
- **After**: No initial bridge → 60s startup → monitoring detects absence → election triggered → best RSSI node becomes bridge
|
|
21
|
+
- Fully backward compatible: pre-designated bridge mode continues to work as before
|
|
22
|
+
- Core fix in `src/arduino/wifi.hpp`
|
|
23
|
+
- Resolves @woodlist's "Bridge_failover example does not work" issue
|
|
24
|
+
|
|
25
|
+
### Changed
|
|
26
|
+
|
|
27
|
+
- **bridge_failover Example Documentation** - Clarified two deployment modes
|
|
28
|
+
- Auto-Election Mode: All nodes regular (`INITIAL_BRIDGE=false`), RSSI-based election after 60s
|
|
29
|
+
- Pre-Designated Mode: Traditional single initial bridge setup
|
|
30
|
+
- Updated README.md with comprehensive auto-election documentation
|
|
31
|
+
- Enhanced header comments in bridge_failover.ino
|
|
32
|
+
|
|
33
|
+
### Housekeeping
|
|
34
|
+
|
|
35
|
+
- Synchronized package-lock.json version to 1.8.5
|
|
36
|
+
|
|
37
|
+
## [1.8.5] - 2025-11-12
|
|
38
|
+
|
|
39
|
+
### Fixed
|
|
40
|
+
|
|
41
|
+
- **ntpTimeSyncBridge and ntpTimeSyncNode Compilation (Issue #108)** - Arduino IDE compilation errors fixed
|
|
42
|
+
- Fixed include path in ntpTimeSyncBridge.ino from `"examples/alteriom/alteriom_sensor_package.hpp"` to `"alteriom_sensor_package.hpp"`
|
|
43
|
+
- Fixed include path in ntpTimeSyncNode.ino from `"examples/alteriom/alteriom_sensor_package.hpp"` to `"alteriom_sensor_package.hpp"`
|
|
44
|
+
- Arduino IDE compiles sketches with sketch directory as working directory, requiring local header files
|
|
45
|
+
- Resolves @woodlist's compilation error: "No such file or directory"
|
|
46
|
+
|
|
47
|
+
- **Arduino String Method Compatibility** - Fixed incompatible method call in wifi.hpp
|
|
48
|
+
- Changed `stationSSID.empty()` to `stationSSID.isEmpty()` in src/arduino/wifi.hpp line 171
|
|
49
|
+
- Arduino's String class uses `isEmpty()` method instead of STL's `empty()`
|
|
50
|
+
- Fixes CI build failures for ESP32/ESP8266 examples
|
|
51
|
+
- Related to station credentials feature added in #113
|
|
52
|
+
|
|
53
|
+
### Documentation
|
|
54
|
+
|
|
55
|
+
- **Example Sketches** - Updated NTP time synchronization examples
|
|
56
|
+
- ntpTimeSyncBridge now compiles correctly in Arduino IDE
|
|
57
|
+
- ntpTimeSyncNode now compiles correctly in Arduino IDE
|
|
58
|
+
- Examples: `examples/ntpTimeSyncBridge/`, `examples/ntpTimeSyncNode/`
|
|
59
|
+
|
|
10
60
|
## [1.8.4] - 2025-11-12
|
|
11
61
|
|
|
12
62
|
### Fixed
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
Created
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
# Station Credentials Design Rationale
|
|
2
|
+
|
|
3
|
+
## Question
|
|
4
|
+
|
|
5
|
+
Why does `mesh.init()` require a separate `mesh.stationManual()` call to connect to a router, instead of accepting station credentials directly?
|
|
6
|
+
|
|
7
|
+
## Answer: Multiple Valid Approaches
|
|
8
|
+
|
|
9
|
+
The library now supports **three approaches** for connecting a bridge node to a router, each with different use cases:
|
|
10
|
+
|
|
11
|
+
### 1. Separate stationManual() Call (Original Design)
|
|
12
|
+
|
|
13
|
+
```cpp
|
|
14
|
+
mesh.init(MESH_PREFIX, MESH_PASSWORD, &userScheduler, MESH_PORT, WIFI_AP_STA, 6);
|
|
15
|
+
mesh.stationManual(STATION_SSID, STATION_PASSWORD);
|
|
16
|
+
mesh.setRoot(true);
|
|
17
|
+
mesh.setContainsRoot(true);
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
**When to use:**
|
|
21
|
+
- Maximum flexibility - can change router connection without reinitializing mesh
|
|
22
|
+
- Dynamic router selection at runtime
|
|
23
|
+
- Need to call `setHostname()` or other WiFi configuration between init and connection
|
|
24
|
+
- Following existing examples or legacy code
|
|
25
|
+
|
|
26
|
+
**Advantages:**
|
|
27
|
+
- Separation of concerns: mesh setup vs router connection
|
|
28
|
+
- Can reconnect to different routers without mesh reinitialization
|
|
29
|
+
- More control over connection timing and error handling
|
|
30
|
+
|
|
31
|
+
### 2. Optional Parameters in init() (New Convenience Feature)
|
|
32
|
+
|
|
33
|
+
```cpp
|
|
34
|
+
mesh.init(MESH_PREFIX, MESH_PASSWORD, &userScheduler, MESH_PORT,
|
|
35
|
+
WIFI_AP_STA, 6, 0, MAX_CONN,
|
|
36
|
+
STATION_SSID, STATION_PASSWORD); // Optional parameters
|
|
37
|
+
mesh.setRoot(true);
|
|
38
|
+
mesh.setContainsRoot(true);
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
**When to use:**
|
|
42
|
+
- Simple bridge setup with known credentials
|
|
43
|
+
- Static configuration (credentials won't change)
|
|
44
|
+
- Want slightly more concise code
|
|
45
|
+
- Don't need hostname or other WiFi customization
|
|
46
|
+
|
|
47
|
+
**Advantages:**
|
|
48
|
+
- One line instead of two for basic bridge setup
|
|
49
|
+
- All connection parameters in one place
|
|
50
|
+
- Still maintains full flexibility of other options
|
|
51
|
+
|
|
52
|
+
### 3. initAsBridge() Method (Recommended for New Projects)
|
|
53
|
+
|
|
54
|
+
```cpp
|
|
55
|
+
mesh.initAsBridge(MESH_PREFIX, MESH_PASSWORD,
|
|
56
|
+
STATION_SSID, STATION_PASSWORD,
|
|
57
|
+
&userScheduler, MESH_PORT);
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
**When to use:**
|
|
61
|
+
- New bridge implementations (recommended)
|
|
62
|
+
- Want automatic channel detection
|
|
63
|
+
- Need simplest possible setup
|
|
64
|
+
- Following modern best practices
|
|
65
|
+
|
|
66
|
+
**Advantages:**
|
|
67
|
+
- **Automatic channel detection** - no manual channel configuration needed
|
|
68
|
+
- Automatically sets node as root
|
|
69
|
+
- Maintains router connection through channel switches
|
|
70
|
+
- Broadcasts bridge status (Type 610) automatically
|
|
71
|
+
- Comprehensive initialization in one call
|
|
72
|
+
|
|
73
|
+
## Design Rationale for Original Separation
|
|
74
|
+
|
|
75
|
+
The original design separated `init()` and `stationManual()` for good architectural reasons:
|
|
76
|
+
|
|
77
|
+
### 1. Separation of Concerns
|
|
78
|
+
|
|
79
|
+
**Mesh Setup (`init()`):**
|
|
80
|
+
- Creates mesh network (AP mode)
|
|
81
|
+
- Sets up mesh routing and protocol
|
|
82
|
+
- Configures mesh-specific parameters
|
|
83
|
+
- Lifetime: typically never changes
|
|
84
|
+
|
|
85
|
+
**Router Connection (`stationManual()`):**
|
|
86
|
+
- Connects to external WiFi (STA mode)
|
|
87
|
+
- Different lifecycle - may connect/disconnect/change
|
|
88
|
+
- Network-specific credentials and settings
|
|
89
|
+
- Can be reconfigured at runtime
|
|
90
|
+
|
|
91
|
+
This separation allows clean code organization and different lifecycles for each concern.
|
|
92
|
+
|
|
93
|
+
### 2. Not All Nodes Need Router Connection
|
|
94
|
+
|
|
95
|
+
In a typical mesh network:
|
|
96
|
+
- **1 bridge node**: Needs router connection (AP+STA mode)
|
|
97
|
+
- **N regular nodes**: Mesh only (AP mode, or AP+STA for mesh connections)
|
|
98
|
+
|
|
99
|
+
Regular nodes use:
|
|
100
|
+
```cpp
|
|
101
|
+
mesh.init(MESH_PREFIX, MESH_PASSWORD, &userScheduler, MESH_PORT, WIFI_AP_STA);
|
|
102
|
+
// No stationManual() call - not a bridge
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
If `init()` always required station credentials, it would be confusing for regular nodes.
|
|
106
|
+
|
|
107
|
+
### 3. Dynamic Router Switching
|
|
108
|
+
|
|
109
|
+
Some advanced use cases require changing router connections at runtime:
|
|
110
|
+
|
|
111
|
+
```cpp
|
|
112
|
+
// Initial setup
|
|
113
|
+
mesh.init(...);
|
|
114
|
+
mesh.stationManual("Router1", "pass1");
|
|
115
|
+
|
|
116
|
+
// Later, switch to different router
|
|
117
|
+
mesh.stationManual("Router2", "pass2");
|
|
118
|
+
|
|
119
|
+
// Or respond to failover
|
|
120
|
+
void onRouterDisconnect() {
|
|
121
|
+
mesh.stationManual(backupSSID, backupPassword);
|
|
122
|
+
}
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
With station credentials baked into `init()`, this flexibility would be lost.
|
|
126
|
+
|
|
127
|
+
### 4. Additional WiFi Configuration
|
|
128
|
+
|
|
129
|
+
Many users need to configure WiFi settings between initialization and connection:
|
|
130
|
+
|
|
131
|
+
```cpp
|
|
132
|
+
mesh.init(...);
|
|
133
|
+
mesh.setHostname("MESH_BRIDGE"); // Must be before stationManual()
|
|
134
|
+
mesh.stationManual(...);
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
The separation provides a natural place for these configurations.
|
|
138
|
+
|
|
139
|
+
### 5. Error Handling and Retry Logic
|
|
140
|
+
|
|
141
|
+
Separating the calls allows better error handling:
|
|
142
|
+
|
|
143
|
+
```cpp
|
|
144
|
+
mesh.init(...); // This typically doesn't fail
|
|
145
|
+
|
|
146
|
+
// Retry router connection with backoff
|
|
147
|
+
for (int retry = 0; retry < 3; retry++) {
|
|
148
|
+
if (tryStationConnect()) break;
|
|
149
|
+
delay(1000 * (retry + 1));
|
|
150
|
+
}
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
## Comparison Table
|
|
154
|
+
|
|
155
|
+
| Approach | Setup Complexity | Flexibility | Channel Detection | Best For |
|
|
156
|
+
|----------|-----------------|-------------|-------------------|----------|
|
|
157
|
+
| **stationManual()** | Medium | Highest | Manual | Dynamic configs, legacy code |
|
|
158
|
+
| **init() params** | Low-Medium | High | Manual | Simple static bridges |
|
|
159
|
+
| **initAsBridge()** | Lowest | Medium | Automatic | New projects, recommended |
|
|
160
|
+
|
|
161
|
+
## Recommendation
|
|
162
|
+
|
|
163
|
+
**For new projects:** Use `initAsBridge()` - it's the modern, recommended approach with automatic channel detection.
|
|
164
|
+
|
|
165
|
+
**For existing projects:** The original `init()` + `stationManual()` pattern remains fully supported and appropriate.
|
|
166
|
+
|
|
167
|
+
**For simple bridges:** The new optional parameters in `init()` provide a middle ground with good flexibility.
|
|
168
|
+
|
|
169
|
+
All three approaches are valid and will continue to be supported. Choose based on your specific needs.
|
|
170
|
+
|
|
171
|
+
## Implementation Note
|
|
172
|
+
|
|
173
|
+
When station credentials are passed to `init()`, the implementation internally calls `stationManual()` after mesh initialization. This maintains consistency and code reuse while providing convenience.
|
|
174
|
+
|
|
175
|
+
```cpp
|
|
176
|
+
// Inside init() implementation
|
|
177
|
+
if (!stationSSID.empty() && (connectMode & WIFI_STA)) {
|
|
178
|
+
this->stationManual(stationSSID, stationPassword);
|
|
179
|
+
}
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
This design ensures all three approaches use the same underlying connection logic.
|
|
@@ -30,10 +30,20 @@ Regular nodes track these broadcasts and detect failures when:
|
|
|
30
30
|
|
|
31
31
|
### 2. Election Trigger
|
|
32
32
|
|
|
33
|
-
|
|
33
|
+
An election is triggered when:
|
|
34
|
+
|
|
35
|
+
**Scenario 1: No Bridge Exists (Auto-Election Mode)**
|
|
36
|
+
- After 60-second startup period
|
|
37
|
+
- Periodic monitoring (every 30s) detects no healthy bridge
|
|
34
38
|
- Nodes with router credentials configured start an election
|
|
35
39
|
- Nodes without credentials remain passive
|
|
36
40
|
|
|
41
|
+
**Scenario 2: Bridge Failure**
|
|
42
|
+
- Primary bridge fails or loses Internet connectivity
|
|
43
|
+
- No status received within 60 seconds (configurable timeout)
|
|
44
|
+
- Bridge reports `internetConnected: false`
|
|
45
|
+
- Nodes detect failure and start election
|
|
46
|
+
|
|
37
47
|
### 3. Election Protocol
|
|
38
48
|
|
|
39
49
|
**Step 1: Candidacy Broadcast (Type 611)**
|
|
@@ -97,34 +107,77 @@ To prevent oscillation:
|
|
|
97
107
|
|
|
98
108
|
## Setup Instructions
|
|
99
109
|
|
|
100
|
-
|
|
110
|
+
You can choose between two deployment modes:
|
|
111
|
+
|
|
112
|
+
### Option A: Auto-Election Mode (Recommended)
|
|
113
|
+
|
|
114
|
+
**Best for**: Equal peers where any node can become the bridge based on signal strength.
|
|
115
|
+
|
|
116
|
+
1. Keep `INITIAL_BRIDGE = false` on **ALL nodes**
|
|
117
|
+
2. Configure mesh credentials (MESH_PREFIX, MESH_PASSWORD)
|
|
118
|
+
3. Configure router credentials (ROUTER_SSID, ROUTER_PASSWORD)
|
|
119
|
+
4. Flash the same sketch to all ESP32/ESP8266 devices
|
|
120
|
+
5. Power on all nodes simultaneously
|
|
121
|
+
|
|
122
|
+
**What happens**:
|
|
123
|
+
- Nodes start as regular mesh nodes
|
|
124
|
+
- After 60-second startup period, automatic monitoring begins
|
|
125
|
+
- If no bridge exists, nodes trigger an election
|
|
126
|
+
- Node with best router RSSI wins and becomes bridge
|
|
127
|
+
- Other nodes remain regular with failover capability
|
|
128
|
+
|
|
129
|
+
**Advantages**:
|
|
130
|
+
- Simpler setup - no need to designate a specific node
|
|
131
|
+
- True dynamic failover - any node can become bridge
|
|
132
|
+
- Best bridge selected based on signal strength
|
|
133
|
+
|
|
134
|
+
### Option B: Pre-Designated Bridge Mode
|
|
135
|
+
|
|
136
|
+
**Best for**: When you want a specific node to start as the bridge.
|
|
137
|
+
|
|
138
|
+
**1. Flash Initial Bridge Node**
|
|
101
139
|
|
|
102
140
|
1. Set `INITIAL_BRIDGE` to `true`
|
|
103
141
|
2. Configure router credentials
|
|
104
142
|
3. Flash to one ESP32/ESP8266
|
|
105
143
|
4. This node will connect to router and start as bridge
|
|
106
144
|
|
|
107
|
-
|
|
145
|
+
**2. Flash Regular Nodes**
|
|
108
146
|
|
|
109
147
|
1. Set `INITIAL_BRIDGE` to `false`
|
|
110
148
|
2. Configure same mesh and router credentials
|
|
111
149
|
3. Flash to other ESP32/ESP8266 devices
|
|
112
150
|
4. These nodes can become bridges via election
|
|
113
151
|
|
|
114
|
-
|
|
152
|
+
**Advantages**:
|
|
153
|
+
- Immediate bridge availability (no 60s wait)
|
|
154
|
+
- Predictable initial bridge selection
|
|
155
|
+
- Good for nodes with fixed locations
|
|
156
|
+
|
|
157
|
+
### Test Failover Scenarios
|
|
158
|
+
|
|
159
|
+
**Scenario 1: Auto-Election (No Initial Bridge)**
|
|
160
|
+
1. Flash all nodes with `INITIAL_BRIDGE = false`
|
|
161
|
+
2. Power on all nodes
|
|
162
|
+
3. Wait 60 seconds for startup period
|
|
163
|
+
4. Automatic monitoring detects no bridge
|
|
164
|
+
5. Election starts automatically within 30 seconds
|
|
165
|
+
6. Node with best router signal becomes bridge
|
|
166
|
+
7. Monitor serial output to see election process
|
|
115
167
|
|
|
116
|
-
**Scenario
|
|
117
|
-
1. Power off the
|
|
168
|
+
**Scenario 2: Bridge Goes Offline**
|
|
169
|
+
1. Power off the current bridge node (initial or elected)
|
|
118
170
|
2. After 60 seconds, regular nodes detect failure
|
|
119
171
|
3. Election starts automatically
|
|
120
172
|
4. Node with best router signal becomes new bridge
|
|
121
173
|
5. Monitor serial output to see election process
|
|
122
174
|
|
|
123
|
-
**Scenario
|
|
124
|
-
1. Disconnect router from Internet
|
|
175
|
+
**Scenario 3: Bridge Loses Internet**
|
|
176
|
+
1. Disconnect router from Internet (or block bridge node's Internet)
|
|
125
177
|
2. Bridge reports `internetConnected: false`
|
|
126
|
-
3.
|
|
127
|
-
4.
|
|
178
|
+
3. Nodes detect loss of Internet via status broadcasts
|
|
179
|
+
4. Election starts to find a node with working Internet
|
|
180
|
+
5. New bridge elected if another node has Internet access
|
|
128
181
|
|
|
129
182
|
## Serial Output
|
|
130
183
|
|
|
@@ -9,14 +9,26 @@
|
|
|
9
9
|
// - ESP32 or ESP8266
|
|
10
10
|
// - WiFi router with Internet connection
|
|
11
11
|
//
|
|
12
|
-
// Setup:
|
|
12
|
+
// Setup Options:
|
|
13
|
+
//
|
|
14
|
+
// OPTION A - Auto-Election Mode (Recommended):
|
|
15
|
+
// 1. Configure your mesh credentials (MESH_PREFIX, MESH_PASSWORD)
|
|
16
|
+
// 2. Configure your router credentials (ROUTER_SSID, ROUTER_PASSWORD)
|
|
17
|
+
// 3. Keep INITIAL_BRIDGE = false on ALL nodes
|
|
18
|
+
// 4. Flash multiple nodes with this sketch
|
|
19
|
+
// 5. After startup (~60 seconds), nodes will automatically elect a bridge
|
|
20
|
+
// based on best router signal strength (RSSI)
|
|
21
|
+
//
|
|
22
|
+
// OPTION B - Pre-Designated Bridge Mode:
|
|
13
23
|
// 1. Configure your mesh credentials (MESH_PREFIX, MESH_PASSWORD)
|
|
14
24
|
// 2. Configure your router credentials (ROUTER_SSID, ROUTER_PASSWORD)
|
|
15
|
-
// 3.
|
|
16
|
-
// 4.
|
|
17
|
-
// 5.
|
|
25
|
+
// 3. Set INITIAL_BRIDGE = true on ONE node (your designated bridge)
|
|
26
|
+
// 4. Keep INITIAL_BRIDGE = false on all other nodes
|
|
27
|
+
// 5. Flash the nodes - designated bridge starts immediately
|
|
28
|
+
// 6. If designated bridge fails, others will hold election
|
|
18
29
|
//
|
|
19
30
|
// Features Demonstrated:
|
|
31
|
+
// - Automatic bridge election when no bridge exists
|
|
20
32
|
// - Automatic bridge failure detection via heartbeats
|
|
21
33
|
// - RSSI-based election protocol
|
|
22
34
|
// - Deterministic winner selection with tiebreakers
|
package/library.json
CHANGED
package/library.properties
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
name=Alteriom PainlessMesh
|
|
2
|
-
version=1.8.
|
|
2
|
+
version=1.8.6
|
|
3
3
|
author=Coopdis,Scotty Franzyshen,Edwin van Leeuwen,Germán Martín,Maximilian Schwarz,Doanh Doanh,Alteriom
|
|
4
4
|
maintainer=Alteriom
|
|
5
5
|
sentence=A painless way to setup a mesh with ESP8266 and ESP32 devices with Alteriom extensions
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@alteriom/painlessmesh",
|
|
3
|
-
"version": "1.8.
|
|
3
|
+
"version": "1.8.6",
|
|
4
4
|
"description": "painlessMesh is a user-friendly library for creating mesh networks with ESP8266 and ESP32 devices. This Alteriom fork includes additional packages for sensor data (SensorPackage), device commands (CommandPackage), and status monitoring (StatusPackage). It handles routing and network management automatically, so you can focus on your application. The library uses JSON-based messaging and syncs time across all nodes, making it ideal for coordinated behaviour like synchronized light displays or sensor networks reporting to a central node.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"arduino",
|
package/src/arduino/wifi.hpp
CHANGED
|
@@ -44,7 +44,8 @@ class Mesh : public painlessmesh::Mesh<Connection> {
|
|
|
44
44
|
*/
|
|
45
45
|
void init(TSTRING ssid, TSTRING password, uint16_t port = 5555,
|
|
46
46
|
WiFiMode_t connectMode = WIFI_AP_STA, uint8_t channel = 1,
|
|
47
|
-
uint8_t hidden = 0, uint8_t maxconn = MAX_CONN
|
|
47
|
+
uint8_t hidden = 0, uint8_t maxconn = MAX_CONN,
|
|
48
|
+
TSTRING stationSSID = "", TSTRING stationPassword = "") {
|
|
48
49
|
using namespace logger;
|
|
49
50
|
// Init random generator seed to generate delay variance
|
|
50
51
|
randomSeed(millis());
|
|
@@ -154,6 +155,44 @@ class Mesh : public painlessmesh::Mesh<Connection> {
|
|
|
154
155
|
}
|
|
155
156
|
});
|
|
156
157
|
|
|
158
|
+
// Add periodic monitoring task to detect when no bridge exists
|
|
159
|
+
// This handles the case where no node was initially configured as a bridge
|
|
160
|
+
this->addTask(30000, TASK_FOREVER, [this]() {
|
|
161
|
+
// Only check if failover is enabled and we have credentials
|
|
162
|
+
if (!bridgeFailoverEnabled || !routerCredentialsConfigured) {
|
|
163
|
+
return;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
// Don't check if we're already a bridge
|
|
167
|
+
if (this->isBridge()) {
|
|
168
|
+
return;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
// Skip check during startup period (60 seconds) to allow initial bridge discovery
|
|
172
|
+
if (millis() < 60000) {
|
|
173
|
+
return;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// Check if there are any healthy bridges
|
|
177
|
+
bool hasHealthyBridge = false;
|
|
178
|
+
for (const auto& bridge : this->getBridges()) {
|
|
179
|
+
if (bridge.isHealthy(bridgeTimeoutMs) && bridge.internetConnected) {
|
|
180
|
+
hasHealthyBridge = true;
|
|
181
|
+
break;
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
// If no healthy bridge exists, trigger an election
|
|
186
|
+
if (!hasHealthyBridge) {
|
|
187
|
+
Log(CONNECTION, "Bridge monitor: No healthy bridge detected, triggering election\n");
|
|
188
|
+
// Small delay to randomize election start across nodes
|
|
189
|
+
uint32_t randomDelay = random(1000, 3000);
|
|
190
|
+
this->addTask(randomDelay, TASK_ONCE, [this]() {
|
|
191
|
+
this->startBridgeElection();
|
|
192
|
+
});
|
|
193
|
+
}
|
|
194
|
+
});
|
|
195
|
+
|
|
157
196
|
tcpServerInit();
|
|
158
197
|
eventHandleInit();
|
|
159
198
|
|
|
@@ -165,6 +204,12 @@ class Mesh : public painlessmesh::Mesh<Connection> {
|
|
|
165
204
|
if (connectMode & WIFI_STA) {
|
|
166
205
|
this->initStation();
|
|
167
206
|
}
|
|
207
|
+
|
|
208
|
+
// If station credentials provided, connect to router
|
|
209
|
+
if (!stationSSID.isEmpty() && (connectMode & WIFI_STA)) {
|
|
210
|
+
Log(STARTUP, "init(): Connecting to station %s\n", stationSSID.c_str());
|
|
211
|
+
this->stationManual(stationSSID, stationPassword);
|
|
212
|
+
}
|
|
168
213
|
}
|
|
169
214
|
|
|
170
215
|
/** Initialize the mesh network
|
|
@@ -187,9 +232,11 @@ class Mesh : public painlessmesh::Mesh<Connection> {
|
|
|
187
232
|
void init(TSTRING ssid, TSTRING password, Scheduler *baseScheduler,
|
|
188
233
|
uint16_t port = 5555, WiFiMode_t connectMode = WIFI_AP_STA,
|
|
189
234
|
uint8_t channel = 1, uint8_t hidden = 0,
|
|
190
|
-
uint8_t maxconn = MAX_CONN
|
|
235
|
+
uint8_t maxconn = MAX_CONN,
|
|
236
|
+
TSTRING stationSSID = "", TSTRING stationPassword = "") {
|
|
191
237
|
this->setScheduler(baseScheduler);
|
|
192
|
-
init(ssid, password, port, connectMode, channel, hidden, maxconn
|
|
238
|
+
init(ssid, password, port, connectMode, channel, hidden, maxconn,
|
|
239
|
+
stationSSID, stationPassword);
|
|
193
240
|
}
|
|
194
241
|
|
|
195
242
|
/**
|