@alteriom/painlessmesh 1.10.0 → 2.0.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/BRIDGE_TO_INTERNET.md +167 -29
- package/CHANGELOG.md +542 -0
- package/CONTRIBUTING.md +56 -53
- package/README.md +102 -95
- package/RELEASE_GUIDE.md +94 -788
- package/examples/alteriom/README.md +8 -10
- package/examples/alteriom/alteriom.ino +2 -2
- package/examples/alteriom/alteriom_sensor_package.hpp +17 -11
- package/examples/alteriom/mppt_example/alteriom_custom_package_template.hpp +320 -0
- package/examples/alteriom/mppt_example/alteriom_sensor_package.hpp +1389 -0
- package/examples/alteriom/mppt_example/{alteriom_mppt_example.ino → mppt_example.ino} +4 -0
- package/examples/basic/test/simulator/README.md +3 -3
- package/examples/bridge_failover/README.md +51 -14
- package/examples/commandControl/commandControl.ino +86 -0
- package/examples/commandControl/platformio.ini +26 -0
- package/examples/mqttBridge/mqttBridge.ino +4 -0
- package/examples/mqttBridge/platformio.ini +1 -1
- package/examples/otaSender/otaSender.ino +5 -1
- package/examples/priority/README.md +1 -1
- package/examples/priority/{priority_basic_example.ino → priority_basic_example/priority_basic_example.ino} +4 -4
- package/examples/priority/{priority_with_queue.ino → priority_with_queue/priority_with_queue.ino} +20 -2
- package/examples/reliableSensorLogging/platformio.ini +26 -0
- package/examples/reliableSensorLogging/reliableSensorLogging.ino +151 -0
- package/examples/sendToInternet/README.md +12 -5
- package/examples/sendToInternet/{CMakeLists.txt → pc_node/CMakeLists.txt} +7 -7
- package/examples/sendToInternet/{PC_NODE_README.md → pc_node/PC_NODE_README.md} +15 -15
- package/examples/sendToInternet/{build.sh → pc_node/build.sh} +5 -5
- package/examples/sendToInternet/{pc_mesh_node.cpp → pc_node/pc_mesh_node.cpp} +12 -1
- package/examples/sendToInternet/platformio.ini +2 -2
- package/examples/sharedGateway/README.md +1 -2
- package/keywords.txt +50 -1
- package/library.json +8 -6
- package/library.properties +2 -2
- package/package.json +3 -3
- package/src/AlteriomPainlessMesh.h +4 -4
- package/src/arduino/wifi.hpp +556 -126
- package/src/painlessMesh.h +2 -2
- package/src/painlessMeshSTA.cpp +607 -87
- package/src/painlessMeshSTA.h +135 -3
- package/src/painlessmesh/ack.hpp +283 -0
- package/src/painlessmesh/buffer.hpp +70 -8
- package/src/painlessmesh/callback.hpp +38 -5
- package/src/painlessmesh/configuration.hpp +69 -1
- package/src/painlessmesh/connection.hpp +12 -5
- package/src/painlessmesh/gateway.hpp +270 -5
- package/src/painlessmesh/layout.hpp +70 -2
- package/src/painlessmesh/logger.hpp +15 -0
- package/src/painlessmesh/mesh.hpp +552 -48
- package/src/painlessmesh/ntp.hpp +2 -4
- package/src/painlessmesh/plugin.hpp +30 -6
- package/src/painlessmesh/protocol.hpp +55 -2
- package/src/painlessmesh/router.hpp +192 -77
- package/src/painlessmesh/tcp.hpp +10 -0
- package/src/painlessmesh/message_tracker.hpp +0 -311
- /package/examples/sendToInternet/{mock_server_test.ino → mock_server_test/mock_server_test.ino} +0 -0
package/BRIDGE_TO_INTERNET.md
CHANGED
|
@@ -6,7 +6,7 @@ You can bridge your mesh network to the Internet by creating a **gateway node**
|
|
|
6
6
|
|
|
7
7
|
The **bridge-centric approach** automatically detects your router's channel and configures the mesh accordingly. No manual channel configuration required!
|
|
8
8
|
|
|
9
|
-
### Resilient Initialization
|
|
9
|
+
### Resilient Initialization
|
|
10
10
|
|
|
11
11
|
**Power-up order no longer matters!** The bridge will initialize successfully even if:
|
|
12
12
|
- Router is not yet powered on
|
|
@@ -127,15 +127,31 @@ STARTUP: Mesh channel auto-detected: 6
|
|
|
127
127
|
|
|
128
128
|
### Automatic Channel Re-synchronization
|
|
129
129
|
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
130
|
+
A bridge lives on its router's channel, so the mesh has to be there too. The
|
|
131
|
+
library keeps it there without any configuration on the regular nodes:
|
|
132
|
+
|
|
133
|
+
- **A bridge announces its channel.** Every bridge status carries
|
|
134
|
+
`routerChannel`, and an elected bridge's takeover message does too; peers
|
|
135
|
+
that hear a takeover move their AP and station to that channel a second
|
|
136
|
+
later. The channel a node last heard a bridge from is its *home*.
|
|
137
|
+
- **A node that finds nothing looks everywhere.** After two empty scans
|
|
138
|
+
(about 30 s) a node re-detects the mesh channel. A node with nothing under
|
|
139
|
+
its AP scans all channels at once; a node with stations attached scans one
|
|
140
|
+
channel per pass so its children are not dropped.
|
|
141
|
+
- **A node follows the mesh, not a straggler.** A disconnected node joins
|
|
142
|
+
the mesh wherever it is. A connected node leaves its partition only for a
|
|
143
|
+
strictly bigger one, and only after seeing it on two consecutive scans;
|
|
144
|
+
a node at home does not leave for a partition elsewhere, and away from
|
|
145
|
+
home it returns as soon as it sees the mesh there.
|
|
146
|
+
- **An uplink lost at home is rescanned there.** The bridge's AP is on this
|
|
147
|
+
channel; the node scans it again before looking anywhere else.
|
|
148
|
+
|
|
149
|
+
Set `mesh.setContainsRoot(true)` on every regular node of a mesh that has a
|
|
150
|
+
bridge (`initAsBridge()` sets it on the bridge): it is what lets a node that
|
|
151
|
+
is still connected to a partition the bridge has left notice that it has no
|
|
152
|
+
root and go looking for the bridge's channel.
|
|
153
|
+
|
|
154
|
+
## Manual Channel Configuration
|
|
139
155
|
|
|
140
156
|
If you prefer the traditional approach or need more control, you can still manually configure the channel:
|
|
141
157
|
|
|
@@ -176,7 +192,7 @@ The new `initAsBridge()` method automatically handles all channel detection and
|
|
|
176
192
|
|
|
177
193
|
#### With Manual Configuration
|
|
178
194
|
|
|
179
|
-
When using
|
|
195
|
+
When using `stationManual()`, the library automatically handles channel switching. The ESP32/ESP8266 will:
|
|
180
196
|
|
|
181
197
|
1. Initially operate the mesh AP on your specified channel (e.g., channel 6)
|
|
182
198
|
2. Automatically switch to the router's channel when connecting via `stationManual()`
|
|
@@ -199,16 +215,18 @@ When using the legacy `stationManual()` approach, the library will automatically
|
|
|
199
215
|
- Call `mesh.setRoot(true)` on the bridge node
|
|
200
216
|
- Call `mesh.setContainsRoot(true)` on all mesh nodes for optimal routing
|
|
201
217
|
|
|
202
|
-
3. **ESP32-C6
|
|
218
|
+
3. **ESP32-C5 / ESP32-C6**: these need the ESP32 Arduino core 3.x and AsyncTCP v3.4.7 or later; `tcp_alloc` crashes on the C6 are the sign of an older AsyncTCP. See the [dependencies](README.md#dependencies) in the README.
|
|
203
219
|
|
|
204
220
|
## Complete Examples
|
|
205
221
|
|
|
206
222
|
We provide several working bridge examples in the repository:
|
|
207
223
|
|
|
208
|
-
- **Basic Bridge**: `examples/bridge/bridge.ino`
|
|
209
|
-
- **
|
|
210
|
-
- **
|
|
211
|
-
- **
|
|
224
|
+
- **Basic Bridge**: [`examples/bridge/bridge.ino`](examples/bridge/bridge.ino)
|
|
225
|
+
- **Bridge Failover**: [`examples/bridge_failover/`](examples/bridge_failover/) - Automatic election when the bridge goes
|
|
226
|
+
- **Shared Gateway**: [`examples/sharedGateway/`](examples/sharedGateway/) - Every node on the router
|
|
227
|
+
- **Send to Internet**: [`examples/sendToInternet/`](examples/sendToInternet/) - HTTP requests through the gateway, with a mock server for offline testing
|
|
228
|
+
- **MQTT Bridge**: [`examples/mqttBridge/mqttBridge.ino`](examples/mqttBridge/mqttBridge.ino) - Bridges mesh to MQTT broker
|
|
229
|
+
- **Web Server Bridge**: [`examples/webServer/webServer.ino`](examples/webServer/webServer.ino) - Provides web interface
|
|
212
230
|
|
|
213
231
|
## Forwarding Data to Internet
|
|
214
232
|
|
|
@@ -244,24 +262,144 @@ Mesh Network
|
|
|
244
262
|
Node1 Node2 Node3...
|
|
245
263
|
```
|
|
246
264
|
|
|
265
|
+
## Sending Data Through the Gateway
|
|
266
|
+
|
|
267
|
+
Regular nodes have no IP route to the Internet; `HTTPClient` on a regular
|
|
268
|
+
node fails with *connection refused*. They send through the gateway instead:
|
|
269
|
+
|
|
270
|
+
```cpp
|
|
271
|
+
// On every node, after mesh.init(): enables sending on regular nodes and
|
|
272
|
+
// relaying on the bridge
|
|
273
|
+
mesh.enableSendToInternet();
|
|
274
|
+
|
|
275
|
+
// Later, on a regular node
|
|
276
|
+
if (mesh.hasInternetConnection()) { // a gateway with Internet is known
|
|
277
|
+
mesh.sendToInternet(
|
|
278
|
+
"https://api.example.com/data", jsonPayload,
|
|
279
|
+
[](bool success, uint16_t httpStatus, String error) {
|
|
280
|
+
Serial.printf("Delivery: %s (%u)\n", success ? "OK" : error.c_str(), httpStatus);
|
|
281
|
+
});
|
|
282
|
+
}
|
|
283
|
+
```
|
|
284
|
+
|
|
285
|
+
The request travels to the gateway as a `GATEWAY_DATA` package, the gateway
|
|
286
|
+
makes the HTTP call and answers with `GATEWAY_ACK`. The gateway's HTTP work
|
|
287
|
+
is bounded by `NODE_TIMEOUT` (2 s per socket wait at the stock 10 s
|
|
288
|
+
watchdog), and its peers' watchdogs are postponed by exactly the time the
|
|
289
|
+
call took, so a slow endpoint cannot partition the mesh around the gateway.
|
|
290
|
+
An endpoint that needs longer needs a larger `NODE_TIMEOUT`; the
|
|
291
|
+
`static_assert` in `painlessmesh/gateway.hpp` says so at compile time.
|
|
292
|
+
|
|
293
|
+
## Bridge Failover
|
|
294
|
+
|
|
295
|
+
Any node given the router's credentials can take over when the bridge goes:
|
|
296
|
+
|
|
297
|
+
```cpp
|
|
298
|
+
mesh.init(MESH_PREFIX, MESH_PASSWORD, &userScheduler, MESH_PORT, WIFI_AP_STA, 0);
|
|
299
|
+
mesh.setContainsRoot(true);
|
|
300
|
+
mesh.setRouterCredentials(ROUTER_SSID, ROUTER_PASSWORD);
|
|
301
|
+
mesh.enableBridgeFailover(true);
|
|
302
|
+
mesh.onBridgeRoleChanged([](bool isBridge, const String& reason) {
|
|
303
|
+
Serial.printf("%s: %s\n", isBridge ? "Promoted to bridge" : "Regular node", reason.c_str());
|
|
304
|
+
});
|
|
305
|
+
```
|
|
306
|
+
|
|
307
|
+
A bridge that stops cleanly (`mesh.stop()`) broadcasts that it is leaving
|
|
308
|
+
and the candidates elect within seconds; a bridge that loses power is
|
|
309
|
+
noticed when its last status ages out (60 s, plus up to one 30 s monitor
|
|
310
|
+
tick). The winner is the candidate with the best router RSSI, then the
|
|
311
|
+
longest uptime, then the most free memory, then the lowest node ID; it
|
|
312
|
+
promotes itself with `initAsBridge()` and announces its channel. The
|
|
313
|
+
protocol, its tuning (`setElectionStartupDelay()`,
|
|
314
|
+
`setElectionRandomDelay()`, `setBridgeTimeout()`, `setMinimumBridgeRSSI()`)
|
|
315
|
+
and its troubleshooting are in
|
|
316
|
+
[examples/bridge_failover/README.md](examples/bridge_failover/README.md).
|
|
317
|
+
|
|
318
|
+
## Multi-Bridge Coordination
|
|
319
|
+
|
|
320
|
+
Several bridges can serve one mesh. Each is started with a priority (10 is
|
|
321
|
+
primary, 1 is standby), and the bridges announce themselves to each other
|
|
322
|
+
every 30 s:
|
|
323
|
+
|
|
324
|
+
```cpp
|
|
325
|
+
mesh.initAsBridge(MESH_PREFIX, MESH_PASSWORD, ROUTER_SSID, ROUTER_PASSWORD,
|
|
326
|
+
&userScheduler, MESH_PORT, 10); // priority 10
|
|
327
|
+
mesh.setBridgeSelectionStrategy(painlessMesh::PRIORITY_BASED); // or ROUND_ROBIN, BEST_SIGNAL
|
|
328
|
+
mesh.onBridgeCoordination(
|
|
329
|
+
[](const painlessmesh::plugin::BridgeCoordinationPackage& pkg, uint32_t from) {
|
|
330
|
+
Serial.printf("Bridge %u: priority %d, load %d%%\n", from, pkg.priority, pkg.load);
|
|
331
|
+
});
|
|
332
|
+
mesh.onBridgeCoordinationChanged(
|
|
333
|
+
[](const painlessmesh::plugin::BridgeCoordinationPackage& pkg, uint32_t from, String change) {
|
|
334
|
+
Serial.printf("Bridge %s: %u (%s)\n", change.c_str(), from, pkg.role.c_str());
|
|
335
|
+
});
|
|
336
|
+
```
|
|
337
|
+
|
|
338
|
+
`getPrimaryBridge()` returns the bridge the strategy currently selects;
|
|
339
|
+
`getBridges()` lists every known bridge with its Internet state, RSSI and
|
|
340
|
+
last-seen time.
|
|
341
|
+
|
|
342
|
+
## Message Queue for Offline Periods
|
|
343
|
+
|
|
344
|
+
When no gateway has Internet, a node can hold messages and send them later:
|
|
345
|
+
|
|
346
|
+
```cpp
|
|
347
|
+
mesh.enableMessageQueue(true, 100); // after mesh.init()
|
|
348
|
+
uint32_t id = mesh.queueMessage(alarmJson, "https://api.example.com/alarm", PRIORITY_CRITICAL);
|
|
349
|
+
|
|
350
|
+
// Drain when connectivity returns: the queue never sends on its own
|
|
351
|
+
if (mesh.hasInternetConnection()) {
|
|
352
|
+
for (auto& queued : mesh.flushMessageQueue()) {
|
|
353
|
+
mesh.sendToInternet(queued.destination, queued.payload, onResult);
|
|
354
|
+
mesh.removeQueuedMessage(queued.id);
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
```
|
|
358
|
+
|
|
359
|
+
Priorities are `PRIORITY_CRITICAL`, `PRIORITY_HIGH`, `PRIORITY_NORMAL` and
|
|
360
|
+
`PRIORITY_LOW`; when the queue is full the lowest priority is evicted first
|
|
361
|
+
and a critical message is never evicted. `getQueueStats()` reports queued,
|
|
362
|
+
dropped and flushed counts.
|
|
363
|
+
|
|
364
|
+
## Shared Gateway Mode
|
|
365
|
+
|
|
366
|
+
When every node is within reach of the router, every node can be its own
|
|
367
|
+
gateway and the mesh is the fallback:
|
|
368
|
+
|
|
369
|
+
```cpp
|
|
370
|
+
mesh.initAsSharedGateway(MESH_PREFIX, MESH_PASSWORD, ROUTER_SSID, ROUTER_PASSWORD,
|
|
371
|
+
&userScheduler, MESH_PORT);
|
|
372
|
+
mesh.sendToInternet(url, payload, onResult); // local uplink if healthy, else via the mesh
|
|
373
|
+
mesh.onGatewayChanged([](uint32_t oldGateway, uint32_t newGateway) {
|
|
374
|
+
Serial.printf("Primary gateway %u -> %u\n", oldGateway, newGateway);
|
|
375
|
+
});
|
|
376
|
+
```
|
|
377
|
+
|
|
378
|
+
A node's own uplink is health-checked periodically (`hasLocalInternet()`),
|
|
379
|
+
and a request is served locally when it is healthy. See
|
|
380
|
+
[examples/sharedGateway](examples/sharedGateway/).
|
|
381
|
+
|
|
247
382
|
## Frequently Asked Questions
|
|
248
383
|
|
|
249
|
-
### Why
|
|
384
|
+
### Why are there three ways to connect a bridge?
|
|
250
385
|
|
|
251
|
-
|
|
386
|
+
1. `init()` followed by `stationManual()` — the original API, still the most
|
|
387
|
+
flexible: you choose the channel and the mode.
|
|
388
|
+
2. `init()` with `stationSSID` and `stationPassword` — the same, in one call.
|
|
389
|
+
3. `initAsBridge()` — detects the router's channel, sets the root flags,
|
|
390
|
+
retries the router in the background, and starts the bridge status
|
|
391
|
+
broadcasts. Use this unless you have a reason not to.
|
|
252
392
|
|
|
253
|
-
|
|
254
|
-
2. **Convenience**: Pass credentials directly to `init()` (new feature)
|
|
255
|
-
3. **Modern**: Use `initAsBridge()` with auto-detection (recommended)
|
|
393
|
+
### Does the bridge need to be up first?
|
|
256
394
|
|
|
257
|
-
|
|
395
|
+
No. Regular nodes started with `channel = 0` scan for the mesh, and a mesh
|
|
396
|
+
that forms before the bridge follows the bridge to its channel when it
|
|
397
|
+
appears. The ESP8266 should be a leaf in a mesh of more than a few nodes
|
|
398
|
+
(`init(..., maxconn = 0)`); see the README.
|
|
258
399
|
|
|
259
400
|
## Additional Resources
|
|
260
401
|
|
|
261
|
-
- [
|
|
262
|
-
- [
|
|
263
|
-
- [
|
|
264
|
-
- [
|
|
265
|
-
- [Station Credentials Design](docs/design/STATION_CREDENTIALS_DESIGN.md) - Why three approaches exist
|
|
266
|
-
|
|
267
|
-
Feel free to ask if you need help with specific use cases like MQTT integration, web servers, or custom data forwarding!
|
|
402
|
+
- [USER_GUIDE.md](USER_GUIDE.md) — the complete guide, including the API reference
|
|
403
|
+
- [examples/bridge_failover/README.md](examples/bridge_failover/README.md) — the election protocol in detail
|
|
404
|
+
- [SECURITY.md](SECURITY.md) — what gateway HTTPS does and does not protect
|
|
405
|
+
- [CHANGELOG.md](CHANGELOG.md) — what changed in 2.0, and what to know before upgrading
|