@alteriom/painlessmesh 1.9.20 → 2.0.0

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.
Files changed (61) hide show
  1. package/BRIDGE_TO_INTERNET.md +167 -29
  2. package/CHANGELOG.md +604 -0
  3. package/CONTRIBUTING.md +56 -53
  4. package/README.md +100 -75
  5. package/RELEASE_GUIDE.md +81 -641
  6. package/examples/alteriom/README.md +8 -10
  7. package/examples/alteriom/alteriom.ino +2 -2
  8. package/examples/alteriom/alteriom_custom_package_template.hpp +11 -11
  9. package/examples/alteriom/alteriom_sensor_package.hpp +17 -11
  10. package/examples/alteriom/mppt_example/alteriom_custom_package_template.hpp +320 -0
  11. package/examples/alteriom/mppt_example/alteriom_sensor_package.hpp +1389 -0
  12. package/examples/alteriom/mppt_example/{alteriom_mppt_example.ino → mppt_example.ino} +5 -1
  13. package/examples/basic/test/simulator/README.md +3 -3
  14. package/examples/bridge_failover/README.md +51 -14
  15. package/examples/bridge_failover/bridge_failover.ino +2 -2
  16. package/examples/commandControl/commandControl.ino +86 -0
  17. package/examples/commandControl/platformio.ini +26 -0
  18. package/examples/mqttBridge/mqttBridge.ino +4 -0
  19. package/examples/mqttBridge/platformio.ini +1 -1
  20. package/examples/otaSender/otaSender.ino +5 -1
  21. package/examples/priority/README.md +1 -1
  22. package/examples/priority/{priority_basic_example.ino → priority_basic_example/priority_basic_example.ino} +4 -4
  23. package/examples/priority/{priority_with_queue.ino → priority_with_queue/priority_with_queue.ino} +20 -2
  24. package/examples/reliableSensorLogging/platformio.ini +26 -0
  25. package/examples/reliableSensorLogging/reliableSensorLogging.ino +151 -0
  26. package/examples/sendToInternet/README.md +12 -5
  27. package/examples/sendToInternet/{CMakeLists.txt → pc_node/CMakeLists.txt} +7 -7
  28. package/examples/sendToInternet/{PC_NODE_README.md → pc_node/PC_NODE_README.md} +15 -15
  29. package/examples/sendToInternet/{build.sh → pc_node/build.sh} +5 -5
  30. package/examples/sendToInternet/{pc_mesh_node.cpp → pc_node/pc_mesh_node.cpp} +12 -1
  31. package/examples/sharedGateway/README.md +1 -2
  32. package/examples/tcpRetryConfig/README.md +110 -0
  33. package/examples/tcpRetryConfig/platformio.ini +26 -0
  34. package/examples/tcpRetryConfig/tcpRetryConfig.ino +154 -0
  35. package/keywords.txt +53 -1
  36. package/library.json +8 -6
  37. package/library.properties +2 -2
  38. package/package.json +3 -3
  39. package/src/AlteriomPainlessMesh.h +4 -4
  40. package/src/arduino/wifi.hpp +605 -143
  41. package/src/painlessMesh.h +2 -2
  42. package/src/painlessMeshSTA.cpp +607 -87
  43. package/src/painlessMeshSTA.h +135 -3
  44. package/src/painlessTaskOptions.h +9 -0
  45. package/src/painlessmesh/ack.hpp +283 -0
  46. package/src/painlessmesh/buffer.hpp +74 -9
  47. package/src/painlessmesh/callback.hpp +38 -5
  48. package/src/painlessmesh/configuration.hpp +82 -3
  49. package/src/painlessmesh/connection.hpp +43 -16
  50. package/src/painlessmesh/gateway.hpp +270 -5
  51. package/src/painlessmesh/layout.hpp +70 -2
  52. package/src/painlessmesh/logger.hpp +15 -0
  53. package/src/painlessmesh/mesh.hpp +625 -70
  54. package/src/painlessmesh/message_queue.hpp +24 -13
  55. package/src/painlessmesh/ntp.hpp +2 -4
  56. package/src/painlessmesh/plugin.hpp +52 -6
  57. package/src/painlessmesh/protocol.hpp +55 -2
  58. package/src/painlessmesh/router.hpp +192 -77
  59. package/src/painlessmesh/tcp.hpp +168 -29
  60. package/src/painlessmesh/message_tracker.hpp +0 -311
  61. /package/examples/sendToInternet/{mock_server_test.ino → mock_server_test/mock_server_test.ino} +0 -0
package/CONTRIBUTING.md CHANGED
@@ -1,79 +1,82 @@
1
1
  # Contributing
2
2
 
3
- We try to follow the [git flow](https://www.atlassian.com/git/tutorials/comparing-workflows/gitflow-workflow) development model. Which means that we have a `develop` branch and `main` branch. All development is done under feature branches, which are (when finished) merged into the development branch. When a new version is released we merge the `develop` branch into the `main` branch.
3
+ ## Branches
4
4
 
5
- ## Git flow
5
+ - `main` holds released code. A push to `main` that carries a version bump,
6
+ or whose head commit message starts with `release:`, is what tags and
7
+ publishes a release (see [RELEASE_GUIDE.md](RELEASE_GUIDE.md)).
8
+ - `Feat/next-release` is the integration branch for the next version. Open
9
+ pull requests against it.
10
+ - Work happens on short-lived feature branches (`fix/…`, `feat/…`, `docs/…`)
11
+ cut from `Feat/next-release`.
6
12
 
7
- If you would like to use [git flow tools](http://danielkummer.github.io/git-flow-cheatsheet/) you are more than welcome to. We use it and it's pretty nifty. If you see a `feature\` prefix on a comment then that is git flow automating branch creation. It does need more typing than just plain git so I suggest creating shell aliases for the commands.
13
+ Maintainers merge quickly, often as a squash. Push every commit you describe
14
+ before you describe it, and cut follow-up work from the merged base rather
15
+ than from a stale branch.
8
16
 
9
- ## Submit a pull request:
17
+ ## Submit a pull request
10
18
 
11
- * If your push triggered a 'you just pushed...' message from GitHub then click on the button provided by that pop up to create a pull request.
12
- * If not, then create a pull request and point it to your branch.
13
- * Make sure that you're attempting to merge into `develop` and not `main`.
14
- * Get your code reviewed by another contributor. If there are no contributors who possess the same set of skills then get them to review it anyway but explain what the code does beforehand and why. Use it as an opportunity for discussion around the feature set, to transfer knowledge, and to possibly [rubber duck](https://en.wikipedia.org/wiki/Rubber_duck_debugging) your code.
15
- * Once the code is reviewed then have your reviewer merge your code.
19
+ - Point the pull request at `Feat/next-release`, not `main`.
20
+ - Say what was wrong, how you know (a log, a test, a measurement), and what
21
+ the change does about it. For anything that touches the radio, routing,
22
+ the gateway or OTA, the evidence is a serial log or a run on the
23
+ hardware-in-the-loop rig; a unit test alone is not enough there, because
24
+ the unit tests mock the radio.
25
+ - Get your code reviewed by another contributor, and let the reviewer merge.
16
26
 
17
- NOTE: Tests *must* pass in order for the code to be merged.
27
+ Tests must pass for the code to be merged. Add a changelog entry under
28
+ `## [Unreleased]` in [CHANGELOG.md](CHANGELOG.md) for any user-visible change.
18
29
 
19
- NOTE: Always do a `git pull` on `develop` before you start working to capture the latest changes.
30
+ ## Testing requirements
20
31
 
21
- ## Testing Requirements
32
+ ### Running the desktop tests
22
33
 
23
- ### Running Tests
24
-
25
- Before submitting a pull request, ensure all tests pass:
34
+ Before submitting a pull request, build and run the Catch2 and Boost suites:
26
35
 
27
36
  ```bash
28
- # Build and run unit/integration tests
37
+ git submodule update --init
29
38
  cmake -G Ninja .
30
39
  ninja
31
40
  run-parts --regex catch_ bin/
32
-
33
- # Run simulator tests (for examples)
34
- cd test/simulator
35
- mkdir build && cd build
36
- cmake -G Ninja .. && ninja
37
- bin/painlessmesh-simulator --config ../../../examples/basic/test/simulator/scenarios/basic_mesh_test.yaml
38
41
  ```
39
42
 
40
- ### Adding Tests for New Features
41
-
42
- When adding new features or examples:
43
-
44
- 1. **Unit Tests**: Add tests in `test/catch/` for new components
45
- 2. **Integration Tests**: Add to `test/boost/tcp_integration.cpp` for core functionality
46
- 3. **Simulator Tests**: Create test scenarios in `examples/your_example/test/simulator/` for new examples
47
- 4. **Documentation**: Update relevant test documentation
48
-
49
- ### Example Validation with Simulator
43
+ The same suites run in CI under gcc, clang and AddressSanitizer, and CI also
44
+ compiles every example for esp32 and esp8266 with `arduino-cli`, builds the
45
+ PlatformIO projects under `test/ci/`, and checks formatting and library
46
+ metadata.
50
47
 
51
- All example sketches should have simulator tests that validate behavior with multiple virtual nodes:
48
+ ### Simulator and hardware tests
52
49
 
53
- 1. Create firmware adapter in `examples/your_example/test/simulator/firmware/`
54
- 2. Create YAML test scenarios in `examples/your_example/test/simulator/scenarios/`
55
- 3. Document test setup in `examples/your_example/test/simulator/README.md`
50
+ Multi-node behaviour is tested with the external
51
+ [painlessMesh-simulator](https://github.com/Alteriom/painlessMesh-simulator);
52
+ scenarios for an example live under `examples/<example>/test/simulator/`
53
+ (see `examples/basic/test/simulator/`). Radio, routing, gateway, failover
54
+ and OTA behaviour is validated on the Alteriom hardware-in-the-loop farm; a
55
+ maintainer runs it on a pull request by adding the `run-hil` label, and the
56
+ release gate is three consecutive clean runs of the whole suite.
56
57
 
57
- See [Simulator Testing Guide](docs/SIMULATOR_TESTING.md) for complete instructions.
58
+ ### Adding tests for new features
58
59
 
59
- This ensures examples:
60
- - Work as documented with multiple nodes
61
- - Handle edge cases properly
62
- - Don't regress with library changes
63
- - Serve as validated references for users
60
+ 1. **Unit tests**: add to `test/catch/` for new components.
61
+ 2. **Integration tests**: add to `test/boost/tcp_integration.cpp` for core
62
+ behaviour that spans connections.
63
+ 3. **Simulator scenarios**: add YAML scenarios under
64
+ `examples/<example>/test/simulator/` for new examples.
65
+ 4. **Documentation**: keep `USER_GUIDE.md`, `README.md` and the example's
66
+ own README in step with the change.
64
67
 
65
68
  ## Versioning
66
69
 
67
- This project will try its best to adhere to [semver](http://semver.org/) i.e, a codified guide to versioning software. When a new feature is developed or a bug is fixed the version will need to be bumped to signify the change.
68
-
69
- The semver string is built like this:
70
-
71
- Major.Minor.Patch
72
-
73
- A major version bump means that a massive change took place and that application will probably have to be redeployed because a *backwards incompatible* version was released. Example: A library => model relationship change which requires previous configuration options to become invalid.
74
-
75
- A minor version is a *backwards compatible* addition or change to the core software. Most development activity will be this type of version bump. Example: A new feature or model.
70
+ This project follows [semver](https://semver.org/). Version 2.0 patch
71
+ releases must remain wire-compatible with the 2.0 protocol; anything that
72
+ changes what a node puts on the wire, or what an existing sketch has to do to
73
+ keep working, is a major version.
76
74
 
77
- A patch version is a *backwards compatible* bug fix or application configuration change.
75
+ - **Major**: a backwards-incompatible change (a new or changed protocol
76
+ message, a removed API).
77
+ - **Minor**: a backwards-compatible addition.
78
+ - **Patch**: a backwards-compatible fix.
78
79
 
79
- Documentation doesn't require a version bump.
80
+ Documentation does not require a version bump. The three version files
81
+ (`library.properties`, `library.json`, `package.json`) are changed together
82
+ with `./scripts/bump-version.sh`.
package/README.md CHANGED
@@ -4,7 +4,7 @@
4
4
 
5
5
  <div align="center">
6
6
 
7
- **Version 1.9.20** - Full repo cleanup, bug fixes, and documentation consistency
7
+ **Version 2.0.0** delivery confirmation, a unified send path, and gateway failover, routing and channel following validated on a six-family hardware rig
8
8
 
9
9
  [![CI/CD Pipeline](https://github.com/Alteriom/painlessMesh/actions/workflows/ci.yml/badge.svg)](https://github.com/Alteriom/painlessMesh/actions/workflows/ci.yml)
10
10
  [![Documentation](https://github.com/Alteriom/painlessMesh/actions/workflows/docs.yml/badge.svg)](https://github.com/Alteriom/painlessMesh/actions/workflows/docs.yml)
@@ -12,7 +12,6 @@
12
12
  [![GitHub release](https://img.shields.io/github/v/release/Alteriom/painlessMesh?label=version)](https://github.com/Alteriom/painlessMesh/releases)
13
13
  [![NPM Version](https://img.shields.io/npm/v/@alteriom/painlessmesh?label=npm)](https://www.npmjs.com/package/@alteriom/painlessmesh)
14
14
  [![PlatformIO Registry](https://badges.registry.platformio.org/packages/alteriom/library/AlteriomPainlessMesh.svg)](https://registry.platformio.org/libraries/alteriom/AlteriomPainlessMesh)
15
- [![Arduino Library Manager](https://img.shields.io/badge/Arduino-Library%20Manager-blue.svg)](https://www.arduino.cc/reference/en/libraries/alteriompainlessmesh/)
16
15
 
17
16
  </div>
18
17
 
@@ -156,12 +155,13 @@ See [BRIDGE_TO_INTERNET.md](BRIDGE_TO_INTERNET.md) for complete documentation.
156
155
 
157
156
  **High Availability for Critical Systems**
158
157
 
159
- - 🎯 **RSSI-Based Election** - Best signal strength wins bridge role
160
- - 🔍 **Automatic Detection** - 60-second failure detection via heartbeats
161
- - **Fast Failover** - 60-70 second typical recovery time
162
- - 🌐 **Distributed Consensus** - No single coordinator, deterministic winner selection
163
- - 🛡️ **Split-Brain Prevention** - State machine prevents concurrent elections
164
- - 📊 **Tiebreaker Rules** - RSSI Uptime Memory Node ID
158
+ - 🎯 **RSSI-Based Election** - Best router signal wins the bridge role; ties go to uptime, then free memory, then node ID
159
+ - 📣 **A bridge that stops cleanly says so** - `mesh.stop()` on a bridge announces it is leaving, and the candidates hold their election within seconds
160
+ - 🔍 **Heartbeat Detection** - A bridge that loses power is noticed when its last status ages out: the bridge timeout (60 s) plus up to one 30 s monitor tick
161
+ - 🏠 **The mesh follows the bridge** - Peers learn the elected bridge's channel from the takeover message and treat it as home; a node that loses its uplink there rescans that channel first
162
+ - 🌐 **Distributed Consensus** - No single coordinator; every node evaluates the same candidates by the same rules
163
+ - 🛡️ **Split-Brain Prevention** - One election at a time, a 60 s hold between role changes, and a minimum router RSSI for a lone candidate
164
+ - 🔬 **Validated on hardware** - The failover scenario runs on the Alteriom rig with a real router; see [CHANGELOG.md](CHANGELOG.md)
165
165
 
166
166
  **Use Cases:**
167
167
  - Fish farm alarm systems requiring 24/7 Internet connectivity
@@ -184,7 +184,7 @@ void bridgeRoleCallback(bool isBridge, const String& reason) {
184
184
 
185
185
  See [bridge_failover example](examples/bridge_failover/) for complete documentation.
186
186
 
187
- #### 🌉 Multi-Bridge Coordination (v1.8.2)
187
+ #### 🌉 Multi-Bridge Coordination
188
188
 
189
189
  **Enterprise Load Balancing and Geographic Redundancy**
190
190
 
@@ -232,7 +232,7 @@ mesh.onBridgeCoordinationChanged(
232
232
 
233
233
  See [BRIDGE_TO_INTERNET.md](BRIDGE_TO_INTERNET.md) for multi-bridge documentation.
234
234
 
235
- #### 📬 Message Queue for Offline Mode (v1.8.2)
235
+ #### 📬 Message Queue for Offline Mode
236
236
 
237
237
  **Zero Data Loss During Internet Outages**
238
238
 
@@ -255,14 +255,15 @@ See [BRIDGE_TO_INTERNET.md](BRIDGE_TO_INTERNET.md) for multi-bridge documentatio
255
255
  // Enable message queue with max 100 messages
256
256
  mesh.enableMessageQueue(true, 100);
257
257
 
258
- // Queue critical alarm message
258
+ // Queue a critical alarm for the Internet endpoint; it is sent when a
259
+ // gateway with Internet is available and never evicted by lower priorities
259
260
  String criticalAlarm = "{\"sensor\":\"O2\",\"value\":2.5,\"alarm\":true}";
260
- mesh.queueMessage(bridgeNodeId, criticalAlarm);
261
+ mesh.queueMessage(criticalAlarm, "https://api.example.com/alarm", PRIORITY_CRITICAL);
261
262
  ```
262
263
 
263
264
  See [BRIDGE_TO_INTERNET.md](BRIDGE_TO_INTERNET.md) for message queue documentation.
264
265
 
265
- #### 🌐 Shared Gateway Mode (v1.9.0+)
266
+ #### 🌐 Shared Gateway Mode
266
267
 
267
268
  **All Nodes as Internet Gateways with Automatic Failover**
268
269
 
@@ -298,8 +299,8 @@ mesh.sendToInternet(
298
299
  );
299
300
 
300
301
  // Monitor gateway changes
301
- mesh.onGatewayChanged([](uint32_t newGateway) {
302
- Serial.printf("Primary gateway: %u\n", newGateway);
302
+ mesh.onGatewayChanged([](uint32_t oldGateway, uint32_t newGateway) {
303
+ Serial.printf("Primary gateway: %u -> %u\n", oldGateway, newGateway);
303
304
  });
304
305
  ```
305
306
 
@@ -331,6 +332,32 @@ The library handles routing and network management automatically, so you can foc
331
332
 
332
333
  painlessMesh is a true ad-hoc network, meaning that no-planning, central controller, or router is required. Any system of 1 or more nodes will self-organize into fully functional mesh. The maximum size of the mesh is limited (we think) by the amount of memory in the heap that can be allocated to the sub-connections buffer and so should be really quite high.
333
334
 
335
+ ### ESP8266 capacity
336
+
337
+ The ESP8266 is specified for **small meshes**, or as a **leaf** in larger ones.
338
+ Measured as an interior node of a seven-node mesh it runs at 10–13 KB free —
339
+ a working set that tracks its live connections and the traffic through them,
340
+ not a leak — and at that level a single 8 KB package or one OTA part can fail
341
+ to allocate. That is the part's limit, not a library defect. Every ESP32
342
+ family holds within a few percent of its starting heap in the same mesh.
343
+
344
+ Configure an ESP8266 as a leaf with `init(..., maxconn = 0)` (or `1` to allow
345
+ one child). The library checks every thirty seconds on ESP8266 and logs an
346
+ `ERROR` when the node is below 12 KB free with more than one child attached;
347
+ `mesh.overCapacity()` reports the same condition to the sketch, and
348
+ `mesh.apChildren()` says how many are attached.
349
+
350
+ ### Validated on hardware
351
+
352
+ Every 2.0 change to failover, routing, channel following and the station
353
+ scan was found in the serial logs of the Alteriom hardware-in-the-loop rig —
354
+ an ESP32, ESP32-C3, ESP32-C5, ESP32-C6, ESP32-S3 and ESP8266 in one mesh with
355
+ a real router — and confirmed there. The release gate is three consecutive
356
+ clean runs of the rig's whole suite (mesh formation, delivery and
357
+ acknowledgement, priorities, dedicated and shared gateways, Internet relay,
358
+ gateway failover, mesh OTA, sustained soak). Each fix in the changelog names
359
+ the run that found it.
360
+
334
361
  ### JSON based
335
362
 
336
363
  painlessMesh uses JSON objects for all its messaging. There are a couple of reasons for this. First, it makes the code and the messages human readable and painless to understand and second, it makes it painless to integrate painlessMesh with javascript front-ends, web applications, and other apps. Some performance is lost, but I haven’t been running into performance issues yet. Converting to binary messaging would be fairly straight forward if someone wants to contribute.
@@ -341,29 +368,29 @@ painlessMesh is designed to be used with Arduino, but it does not use the Arduin
341
368
 
342
369
  ### painlessMesh is not IP networking
343
370
 
344
- painlessMesh does not create a TCP/IP network of nodes. Rather each of the nodes is uniquely identified by its 32bit chipId which is retrieved from the esp8266/esp32 using the `system_get_chip_id()` call in the SDK. Every node will have a unique number. Messages can either be broadcast to all the nodes on the mesh, or sent specifically to an individual node which is identified by its `nodeId.
371
+ painlessMesh does not create a TCP/IP network of nodes. Rather each of the nodes is uniquely identified by its 32bit chipId which is retrieved from the esp8266/esp32 using the `system_get_chip_id()` call in the SDK. Every node will have a unique number. Messages can either be broadcast to all the nodes on the mesh, or sent specifically to an individual node which is identified by its `nodeId`.
345
372
 
346
373
  ### Limitations and caveats
347
374
 
348
375
  - Try to avoid using `delay()` in your code. To maintain the mesh we need to perform some tasks in the background. Using `delay()` will stop these tasks from happening and can cause the mesh to lose stability/fall apart. Instead, we recommend using [TaskScheduler](http://playground.arduino.cc/Code/TaskScheduler) which is used in `painlessMesh` itself. Documentation can be found [here](https://github.com/arkhipenko/TaskScheduler/wiki/Full-Document). For other examples on how to use the scheduler see the example folder.
349
376
  - `painlessMesh` subscribes to WiFi events. Please be aware that as a result `painlessMesh` can be incompatible with user programs/other libraries that try to bind to the same events.
350
377
  - Try to be conservative in the number of messages (and especially broadcast messages) you sent per minute. This is to prevent the hardware from overloading. Both esp8266 and esp32 are limited in processing power/memory, making it easy to overload the mesh and destabilize it. And while `painlessMesh` tries to prevent this from happening, it is not always possible to do so.
351
- - Messages can go missing or be dropped due to high traffic and you can not rely on all messages to be delivered. One suggestion to work around is to resend messages every so often. Even if some go missing, most should go through. Another option is to have your nodes send replies when they receive a message. The sending nodes can the resend the message if they haven’t gotten a reply in a certain amount of time.
378
+ - Messages can go missing or be dropped due to high traffic and you can not rely on all messages to be delivered. One suggestion to work around is to resend messages every so often. Even if some go missing, most should go through. Another option is to have your nodes send replies when they receive a message. The sending nodes can then resend the message if they haven’t gotten a reply in a certain amount of time.
352
379
 
353
380
  ## Installation
354
381
 
355
382
  ### Arduino Library Manager
356
383
 
357
- **Once registered**, installation will be available via Arduino IDE:
384
+ The library is in the Arduino Library Manager as **Alteriom PainlessMesh**:
358
385
 
359
386
  1. Open Arduino IDE
360
387
  2. Go to **Tools** → **Manage Libraries...**
361
- 3. Search for **"AlteriomPainlessMesh"**
362
- 4. Click **Install**
388
+ 3. Search for **"Alteriom PainlessMesh"** and click **Install**; accept the dependencies it offers (ArduinoJson, TaskScheduler, PubSubClient)
389
+ 4. Also install **AsyncTCP** (ESP32) or **ESPAsyncTCP** (ESP8266) from the same dialog; the Library Manager cannot pick one per platform, so they are not listed as dependencies
363
390
 
364
- The library includes the header file `AlteriomPainlessMesh.h` which provides access to both the core painlessMesh functionality and Alteriom-specific extensions.
391
+ The index follows a GitHub release within a day. The header `AlteriomPainlessMesh.h` gives access to the core painlessMesh functionality and the Alteriom extensions; `painlessMesh.h` alone gives the core.
365
392
 
366
- #### Manual Installation (Current Method)
393
+ #### Manual Installation
367
394
 
368
395
  **Option 1: Download ZIP from GitHub Release**
369
396
 
@@ -383,7 +410,12 @@ git clone https://github.com/Alteriom/painlessMesh.git AlteriomPainlessMesh
383
410
 
384
411
  ### PlatformIO
385
412
 
386
- `painlessMesh` is included in both the Arduino Library Manager and the platformio library registry and can easily be installed via either of those methods.
413
+ The library is published on the [PlatformIO registry](https://registry.platformio.org/libraries/alteriom/AlteriomPainlessMesh) as `alteriom/AlteriomPainlessMesh`:
414
+
415
+ ```ini
416
+ lib_deps =
417
+ alteriom/AlteriomPainlessMesh@^2.0.0
418
+ ```
387
419
 
388
420
  ### Dependencies
389
421
 
@@ -392,7 +424,7 @@ painlessMesh makes use of the following libraries, which can be installed throug
392
424
  - [ArduinoJson](https://github.com/bblanchon/ArduinoJson)
393
425
  - [TaskScheduler](https://github.com/arkhipenko/TaskScheduler)
394
426
  - [ESPAsyncTCP](https://github.com/me-no-dev/ESPAsyncTCP) (ESP8266)
395
- - [AsyncTCP](https://github.com/ESP32Async/AsyncTCP) (ESP32) - v3.3.0+ required for ESP32-C6
427
+ - [AsyncTCP](https://github.com/ESP32Async/AsyncTCP) (ESP32) - v3.4.7 or later (v3.3.0 was the first to work on the ESP32-C6)
396
428
 
397
429
  If platformio is used to install the library, then the dependencies will be installed automatically.
398
430
 
@@ -502,9 +534,13 @@ These types are used internally by painlessMesh for mesh management and are hand
502
534
  | 4 | `TIME_SYNC` | Time synchronization protocol messages |
503
535
  | 5 | `NODE_SYNC_REQUEST` | Node discovery and topology requests |
504
536
  | 6 | `NODE_SYNC_REPLY` | Node discovery and topology responses |
505
- | 7 | `CONTROL` | Deprecated control messages |
506
- | 8 | `BROADCAST` | Internal broadcast routing indicator |
507
- | 9 | `SINGLE` | Internal single-node routing indicator |
537
+ | 7 | `CONTROL` | Deprecated, unused |
538
+ | 8 | `BROADCAST` | Application data for every node |
539
+ | 9 | `SINGLE` | Application data for one node |
540
+ | 620 | `GATEWAY_DATA` | An Internet request routed to a gateway (`sendToInternet()`) |
541
+ | 621 | `GATEWAY_ACK` | The gateway's answer to it |
542
+ | 622 | `GATEWAY_HEARTBEAT` | Gateway health monitoring |
543
+ | 630 | `MESSAGE_ACK` | Delivery confirmation for a message sent with a callback (2.0) |
508
544
 
509
545
  **Note**: These protocol types are managed automatically by painlessMesh and are not typically used in application code.
510
546
 
@@ -516,19 +552,19 @@ These are the message types used by applications built on painlessMesh:
516
552
  |------|-------|---------|--------|
517
553
  | 200 | `SensorPackage` | Environmental data | `temperature`, `humidity`, `pressure`, `sensorId`, `timestamp`, `batteryLevel` |
518
554
  | 202 | `StatusPackage` | Health monitoring | `deviceStatus`, `uptime`, `freeMemory`, `wifiStrength`, `firmwareVersion` |
519
- | 204 | `MetricsPackage` | Sensor metrics (v1.7.7+, aligns with schema v0.7.2+) | `cpuUsage`, `freeHeap`, `bytesReceived`, `currentThroughput`, `connectionQuality`, `wifiRSSI` |
520
- | 400 | `CommandPackage` | Device control (v1.7.7+, moved from 201) | `command`, `targetDevice`, `parameters`, `commandId` |
521
- | 600 | `MeshNodeListPackage` | Mesh node list (v1.7.7+, MESH_NODE_LIST) | `nodes[]` (nodeId, status, lastSeen, signalStrength), `nodeCount`, `meshId` |
522
- | 601 | `MeshTopologyPackage` | Mesh topology (v1.7.7+, MESH_TOPOLOGY) | `connections[]` (fromNode, toNode, linkQuality, latencyMs), `rootNode` |
523
- | 602 | `MeshAlertPackage` | Mesh alerts (v1.7.7+, MESH_ALERT) | `alerts[]` (alertType, severity, message, nodeId), `alertCount` |
524
- | 603 | `MeshBridgePackage` | Mesh bridge (v1.7.7+, MESH_BRIDGE) | `meshProtocol`, `fromNodeId`, `toNodeId`, `meshType`, `rawPayload`, `rssi`, `hopCount` |
555
+ | 204 | `MetricsPackage` | Sensor metrics (schema v0.7.2+) | `cpuUsage`, `freeHeap`, `bytesReceived`, `currentThroughput`, `connectionQuality`, `wifiRSSI` |
556
+ | 400 | `CommandPackage` | Device control | `command`, `targetDevice`, `parameters`, `commandId` |
557
+ | 600 | `MeshNodeListPackage` | Mesh node list (`MESH_NODE_LIST`) | `nodes[]` (nodeId, status, lastSeen, signalStrength), `nodeCount`, `meshId` |
558
+ | 601 | `MeshTopologyPackage` | Mesh topology (`MESH_TOPOLOGY`) | `connections[]` (fromNode, toNode, linkQuality, latencyMs), `rootNode` |
559
+ | 602 | `MeshAlertPackage` | Mesh alerts (`MESH_ALERT`) | `alerts[]` (alertType, severity, message, nodeId), `alertCount` |
560
+ | 603 | `MeshBridgePackage` | Mesh bridge (`MESH_BRIDGE`) | `meshProtocol`, `fromNodeId`, `toNodeId`, `meshType`, `rawPayload`, `rssi`, `hopCount` |
525
561
  | 604 | `EnhancedStatusPackage` | Mesh status (MESH_STATUS per schema v0.7.2+) | `nodeCount`, `connectionCount`, `messagesReceived`, `messagesSent`, `avgLatency`, `packetLossRate` |
526
- | 605 | `HealthCheckPackage` | Mesh metrics (v1.7.7+, MESH_METRICS per schema v0.7.2+) | `healthStatus`, `problemFlags`, `memoryHealth`, `networkHealth`, `performanceHealth`, `recommendations` |
527
- | 610 | `BridgeStatusPackage` | Bridge health monitoring (v1.8.0+, BRIDGE_STATUS per schema v0.7.3+) | `internetConnected`, `routerRSSI`, `routerChannel`, `uptime`, `gatewayIP`, `timestamp` |
528
- | 611 | `BridgeElectionPackage` | Bridge failover election (v1.8.0+, BRIDGE_ELECTION per schema v0.7.3+) | `routerRSSI`, `uptime`, `freeMemory`, `timestamp`, `routerSSID` |
529
- | 612 | `BridgeTakeoverPackage` | Bridge role announcement (v1.8.0+, BRIDGE_TAKEOVER per schema v0.7.3+) | `previousBridge`, `reason`, `timestamp` |
530
- | 613 | `BridgeCoordinationPackage` | Multi-bridge coordination (v1.8.2+, BRIDGE_COORDINATION) | `priority`, `role`, `peerBridges[]`, `load`, `timestamp` |
531
- | 614 | `NTPTimeSyncPackage` | NTP time synchronization (v1.8.0+, TIME_SYNC_NTP per schema v0.7.3+) | `ntpTime`, `accuracy`, `source`, `timestamp` |
562
+ | 605 | `HealthCheckPackage` | Mesh metrics (`MESH_METRICS`, schema v0.7.2+) | `healthStatus`, `problemFlags`, `memoryHealth`, `networkHealth`, `performanceHealth`, `recommendations` |
563
+ | 610 | `BridgeStatusPackage` | Bridge health monitoring (`BRIDGE_STATUS`) | `internetConnected`, `routerRSSI`, `routerChannel`, `uptime`, `gatewayIP`, `timestamp` |
564
+ | 611 | `BridgeElectionPackage` | Bridge failover election (`BRIDGE_ELECTION`) | `routerRSSI`, `routerChannel`, `uptime`, `freeMemory`, `timestamp`, `routerSSID` |
565
+ | 612 | `BridgeTakeoverPackage` | Bridge role announcement (`BRIDGE_TAKEOVER`) | `previousBridge`, `reason`, `routerChannel`, `timestamp` |
566
+ | 613 | `BridgeCoordinationPackage` | Multi-bridge coordination (`BRIDGE_COORDINATION`) | `priority`, `role`, `peerBridges[]`, `load`, `timestamp` |
567
+ | 614 | `NTPTimeSyncPackage` | NTP time synchronization (`TIME_SYNC_NTP`) | `ntpTime`, `accuracy`, `source`, `timestamp` |
532
568
 
533
569
  ## Key Features
534
570
 
@@ -560,34 +596,23 @@ These are the message types used by applications built on painlessMesh:
560
596
  - **Event Coordination** - Synchronized displays, distributed processing
561
597
  - **Bridge Networks** - Connect mesh to WiFi/Internet/MQTT - [📖 Bridge Guide](BRIDGE_TO_INTERNET.md)
562
598
 
563
- ## Latest Release: v1.9.20 (March 27, 2026)
564
-
565
- **Full Repo Cleanup, Bug Fixes & Bridge Coordination Callbacks**
599
+ ## Latest Release: v2.0.0 (September 7, 2026)
566
600
 
567
- - New `onBridgeCoordination()` and `onBridgeCoordinationChanged()` monitoring callbacks
568
- - Fixed 13 critical/high/medium bugs (double-free, RSSI overflow, memory leaks, blocking delays)
569
- - Removed ~3,600 lines of dead code and 40 AI-generated docs
570
- - Fixed all CI/CD workflows (corrected action versions)
571
- - Removed 15 fake test files, fixed test infrastructure
572
- - Documentation consistency audit — fixed broken links, API examples, version references
601
+ **Delivery confirmation, a unified send path, and a mesh that holds together on real hardware**
573
602
 
574
- **Recent Key Features (v1.9.0 - v1.9.16):**
575
-
576
- - 🔍 **Mesh Connectivity Detection** - New `hasActiveMeshConnections()` and `getLastKnownBridge()` APIs
577
- - 🌉 **Improved Bridge Detection** - `getPrimaryBridge()` returns last known bridge when disconnected
578
- - **Enhanced TCP Reliability** - Exponential backoff and increased retries for mesh connections
579
- - 🛡️ **Race Condition Fixes** - Improved bridge status and connection validation
580
- - 📦 **Consolidated Examples** - Streamlined to 15 essential examples
581
- - ⚙️ **Configurable Election Timing** - Prevent split-brain with `setElectionStartupDelay()` and `setElectionRandomDelay()`
603
+ - `sendSingle()` and `sendBroadcast()` accept a delivery callback — it fires with `delivered=true` and the round-trip latency on acknowledgment, or `delivered=false` on timeout — and a `SendOptions` struct that carries a priority and a callback in one call. Priority is kept across hops.
604
+ - Thirty-odd defects in gateway failover, channel following, routing and the station scan, every one found in the serial logs of a six-family hardware rig and fixed there: a bridge that stops says so, the mesh follows an elected bridge to its channel and treats it as home, a node is in one place in every neighbour's tree, a dead connection is not a route, a stale scan does not consume a live one.
605
+ - The ESP32-C5 and ESP32-C6 (Arduino core 3.x) no longer hang after a channel follow; the ESP8266 is specified for small meshes or as a leaf, and says so at runtime.
606
+ - Gateway HTTP work is bounded by `NODE_TIMEOUT`, so an Internet request can no longer partition the mesh around its own gateway.
607
+ - The release candidate passed the rig's whole suite three times in a row. Delivery confirmation and cross-hop priority need 2.0 on every node of the path.
582
608
 
583
609
  **[📋 Full CHANGELOG](CHANGELOG.md)**
584
610
 
585
611
  ## Getting Help
586
612
 
587
- - **[FAQ](USER_GUIDE.md)** - Common questions and solutions
588
- - **[Common Issues](USER_GUIDE.md)** - Troubleshooting guide
613
+ - **[Troubleshooting](USER_GUIDE.md#troubleshooting)** - Common issues, debug configuration, best practices
614
+ - **[FAQ](docsify-site/troubleshooting/faq.md)** - Frequently asked questions
589
615
  - **[GitHub Issues](https://github.com/Alteriom/painlessMesh/issues)** - Bug reports and feature requests
590
- - **[Community Forum](https://groups.google.com/forum/#!forum/painlessmesh-user)** - Community support
591
616
  - **[API Documentation](https://alteriom.github.io/painlessMesh/#/api/doxygen)** - Generated API docs
592
617
 
593
618
  ## Development
@@ -605,7 +630,8 @@ run-parts --regex catch_ bin/ # Run tests
605
630
 
606
631
  ### Requirements
607
632
 
608
- - **ESP32/ESP8266**: Arduino Core 2.0.0+
633
+ - **ESP32**: Arduino core 2.0.x or 3.x; the ESP32-C5 and ESP32-C6 need 3.x
634
+ - **ESP8266**: Arduino core 3.x
609
635
  - **Dependencies**: ArduinoJson 7.x, TaskScheduler 4.x
610
636
  - **Development**: CMake, Ninja, Boost (for desktop testing)
611
637
 
@@ -658,7 +684,7 @@ painlessMesh features a state-of-the-art automated CI/CD pipeline:
658
684
  ./scripts/validate-release.sh # Validate release readiness
659
685
 
660
686
  # Edit CHANGELOG.md, then commit with release prefix
661
- git commit -am "release: v1.5.7"
687
+ git commit -am "release: vX.Y.Z"
662
688
  git push origin main # Triggers automated release
663
689
  ```
664
690
 
@@ -666,13 +692,8 @@ See [RELEASE_GUIDE.md](RELEASE_GUIDE.md) for complete release documentation.
666
692
 
667
693
  ## Contributing
668
694
 
669
- We try to follow the [git flow](https://www.atlassian.com/git/tutorials/comparing-workflows/gitflow-workflow) development model. Which means that we have a `develop` branch and `main` branch. All development is done under feature branches, which are (when finished) merged into the development branch. When a new version is released we merge the `develop` branch into the `main` branch. For more details see the [CONTRIBUTING.md](CONTRIBUTING.md) file.
695
+ Pull requests go to `Feat/next-release`, the integration branch for the next version; `main` holds released code and a push to it publishes a release. For branches, tests and what a pull request needs to show, see [CONTRIBUTING.md](CONTRIBUTING.md).
670
696
 
671
- ## Funding
672
-
673
- If you like the library please consider supporting its development. Your contributions help me spend more time improving painlessMesh.
674
-
675
- **[Donate via PayPal](https://www.paypal.com/paypalme/domlavoie)** • [dominic.lavoie@gmail.com](mailto:dominic.lavoie@gmail.com)
676
697
 
677
698
  ## 📚 Documentation
678
699
 
@@ -688,18 +709,18 @@ If you like the library please consider supporting its development. Your contrib
688
709
  ### 🚀 Quick Links
689
710
 
690
711
  **New to AlteriomPainlessMesh?**
691
- - [Quick Start](USER_GUIDE.md) - Get your first mesh running in 5 minutes
692
- - [Installation](USER_GUIDE.md) - Arduino IDE and PlatformIO setup
693
- - [First Mesh](USER_GUIDE.md) - Build a multi-node network
712
+ - [Quick Start](USER_GUIDE.md#getting-started) - Get your first mesh running in 5 minutes
713
+ - [Installation](USER_GUIDE.md#installation) - Arduino IDE and PlatformIO setup
714
+ - [First Mesh](USER_GUIDE.md#your-first-mesh-network) - Build a multi-node network
694
715
 
695
716
  **Reference Documentation:**
696
- - [Core API](USER_GUIDE.md) - painlessMesh class methods
717
+ - [Core API](USER_GUIDE.md#api-reference) - painlessMesh class methods
697
718
  - [Alteriom Extensions](examples/alteriom/README.md) - SensorPackage, CommandPackage, StatusPackage
698
- - [Examples](examples/) - 15 working examples for common scenarios
719
+ - [Examples](examples/) - 18 example directories, 21 sketches, every one compiled for esp32 and esp8266 on each pull request; [.github/DOCUMENTATION.md](.github/DOCUMENTATION.md) lists what each shows
699
720
 
700
721
  **Need Help?**
701
- - [FAQ](USER_GUIDE.md) - Frequently asked questions
702
- - [Common Issues](USER_GUIDE.md) - Troubleshooting guide
722
+ - [Troubleshooting](USER_GUIDE.md#troubleshooting) - Common issues and how to read the logs
723
+ - [FAQ](docsify-site/troubleshooting/faq.md) - Frequently asked questions
703
724
  - [GitHub Issues](https://github.com/Alteriom/painlessMesh/issues) - Bug reports and support
704
725
 
705
726
  ## 🔧 Quick API Reference
@@ -735,7 +756,11 @@ mesh.setDebugMsgTypes(ERROR | STARTUP | CONNECTION);
735
756
 
736
757
  **For complete API documentation, see [USER_GUIDE.md](USER_GUIDE.md#api-reference) or [online docs](https://alteriom.github.io/painlessMesh/#/api/core-api).**
737
758
 
738
- # Funding
759
+ ## Funding
760
+
761
+ If you like the library please consider supporting its development. Your contributions help me spend more time improving painlessMesh.
762
+
763
+ **[Donate via PayPal](https://www.paypal.com/paypalme/domlavoie)** • [dominic.lavoie@gmail.com](mailto:dominic.lavoie@gmail.com)
739
764
 
740
765
  Most development of painlessMesh has been done as a hobby, but some specific features have been funded by the companies listed below:
741
766