@alteriom/painlessmesh 1.8.4 → 1.8.5
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 +23 -0
- package/docs/design/.gitkeep +1 -0
- package/docs/design/STATION_CREDENTIALS_DESIGN.md +182 -0
- 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 +12 -3
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,29 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [1.8.5] - 2025-11-12
|
|
11
|
+
|
|
12
|
+
### Fixed
|
|
13
|
+
|
|
14
|
+
- **ntpTimeSyncBridge and ntpTimeSyncNode Compilation (Issue #108)** - Arduino IDE compilation errors fixed
|
|
15
|
+
- Fixed include path in ntpTimeSyncBridge.ino from `"examples/alteriom/alteriom_sensor_package.hpp"` to `"alteriom_sensor_package.hpp"`
|
|
16
|
+
- Fixed include path in ntpTimeSyncNode.ino from `"examples/alteriom/alteriom_sensor_package.hpp"` to `"alteriom_sensor_package.hpp"`
|
|
17
|
+
- Arduino IDE compiles sketches with sketch directory as working directory, requiring local header files
|
|
18
|
+
- Resolves @woodlist's compilation error: "No such file or directory"
|
|
19
|
+
|
|
20
|
+
- **Arduino String Method Compatibility** - Fixed incompatible method call in wifi.hpp
|
|
21
|
+
- Changed `stationSSID.empty()` to `stationSSID.isEmpty()` in src/arduino/wifi.hpp line 171
|
|
22
|
+
- Arduino's String class uses `isEmpty()` method instead of STL's `empty()`
|
|
23
|
+
- Fixes CI build failures for ESP32/ESP8266 examples
|
|
24
|
+
- Related to station credentials feature added in #113
|
|
25
|
+
|
|
26
|
+
### Documentation
|
|
27
|
+
|
|
28
|
+
- **Example Sketches** - Updated NTP time synchronization examples
|
|
29
|
+
- ntpTimeSyncBridge now compiles correctly in Arduino IDE
|
|
30
|
+
- ntpTimeSyncNode now compiles correctly in Arduino IDE
|
|
31
|
+
- Examples: `examples/ntpTimeSyncBridge/`, `examples/ntpTimeSyncNode/`
|
|
32
|
+
|
|
10
33
|
## [1.8.4] - 2025-11-12
|
|
11
34
|
|
|
12
35
|
### 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.
|
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.5
|
|
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.5",
|
|
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());
|
|
@@ -165,6 +166,12 @@ class Mesh : public painlessmesh::Mesh<Connection> {
|
|
|
165
166
|
if (connectMode & WIFI_STA) {
|
|
166
167
|
this->initStation();
|
|
167
168
|
}
|
|
169
|
+
|
|
170
|
+
// If station credentials provided, connect to router
|
|
171
|
+
if (!stationSSID.isEmpty() && (connectMode & WIFI_STA)) {
|
|
172
|
+
Log(STARTUP, "init(): Connecting to station %s\n", stationSSID.c_str());
|
|
173
|
+
this->stationManual(stationSSID, stationPassword);
|
|
174
|
+
}
|
|
168
175
|
}
|
|
169
176
|
|
|
170
177
|
/** Initialize the mesh network
|
|
@@ -187,9 +194,11 @@ class Mesh : public painlessmesh::Mesh<Connection> {
|
|
|
187
194
|
void init(TSTRING ssid, TSTRING password, Scheduler *baseScheduler,
|
|
188
195
|
uint16_t port = 5555, WiFiMode_t connectMode = WIFI_AP_STA,
|
|
189
196
|
uint8_t channel = 1, uint8_t hidden = 0,
|
|
190
|
-
uint8_t maxconn = MAX_CONN
|
|
197
|
+
uint8_t maxconn = MAX_CONN,
|
|
198
|
+
TSTRING stationSSID = "", TSTRING stationPassword = "") {
|
|
191
199
|
this->setScheduler(baseScheduler);
|
|
192
|
-
init(ssid, password, port, connectMode, channel, hidden, maxconn
|
|
200
|
+
init(ssid, password, port, connectMode, channel, hidden, maxconn,
|
|
201
|
+
stationSSID, stationPassword);
|
|
193
202
|
}
|
|
194
203
|
|
|
195
204
|
/**
|