@alteriom/painlessmesh 1.10.0 → 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 (54) hide show
  1. package/BRIDGE_TO_INTERNET.md +167 -29
  2. package/CHANGELOG.md +483 -0
  3. package/CONTRIBUTING.md +56 -53
  4. package/README.md +100 -95
  5. package/RELEASE_GUIDE.md +81 -780
  6. package/examples/alteriom/README.md +8 -10
  7. package/examples/alteriom/alteriom.ino +2 -2
  8. package/examples/alteriom/alteriom_sensor_package.hpp +17 -11
  9. package/examples/alteriom/mppt_example/alteriom_custom_package_template.hpp +320 -0
  10. package/examples/alteriom/mppt_example/alteriom_sensor_package.hpp +1389 -0
  11. package/examples/alteriom/mppt_example/{alteriom_mppt_example.ino → mppt_example.ino} +4 -0
  12. package/examples/basic/test/simulator/README.md +3 -3
  13. package/examples/bridge_failover/README.md +51 -14
  14. package/examples/commandControl/commandControl.ino +86 -0
  15. package/examples/commandControl/platformio.ini +26 -0
  16. package/examples/mqttBridge/mqttBridge.ino +4 -0
  17. package/examples/mqttBridge/platformio.ini +1 -1
  18. package/examples/otaSender/otaSender.ino +5 -1
  19. package/examples/priority/README.md +1 -1
  20. package/examples/priority/{priority_basic_example.ino → priority_basic_example/priority_basic_example.ino} +4 -4
  21. package/examples/priority/{priority_with_queue.ino → priority_with_queue/priority_with_queue.ino} +20 -2
  22. package/examples/reliableSensorLogging/platformio.ini +26 -0
  23. package/examples/reliableSensorLogging/reliableSensorLogging.ino +151 -0
  24. package/examples/sendToInternet/README.md +12 -5
  25. package/examples/sendToInternet/{CMakeLists.txt → pc_node/CMakeLists.txt} +7 -7
  26. package/examples/sendToInternet/{PC_NODE_README.md → pc_node/PC_NODE_README.md} +15 -15
  27. package/examples/sendToInternet/{build.sh → pc_node/build.sh} +5 -5
  28. package/examples/sendToInternet/{pc_mesh_node.cpp → pc_node/pc_mesh_node.cpp} +12 -1
  29. package/examples/sharedGateway/README.md +1 -2
  30. package/keywords.txt +50 -1
  31. package/library.json +8 -6
  32. package/library.properties +2 -2
  33. package/package.json +3 -3
  34. package/src/AlteriomPainlessMesh.h +3 -3
  35. package/src/arduino/wifi.hpp +556 -126
  36. package/src/painlessMesh.h +2 -2
  37. package/src/painlessMeshSTA.cpp +607 -87
  38. package/src/painlessMeshSTA.h +135 -3
  39. package/src/painlessmesh/ack.hpp +283 -0
  40. package/src/painlessmesh/buffer.hpp +70 -8
  41. package/src/painlessmesh/callback.hpp +38 -5
  42. package/src/painlessmesh/configuration.hpp +69 -1
  43. package/src/painlessmesh/connection.hpp +12 -5
  44. package/src/painlessmesh/gateway.hpp +270 -5
  45. package/src/painlessmesh/layout.hpp +70 -2
  46. package/src/painlessmesh/logger.hpp +15 -0
  47. package/src/painlessmesh/mesh.hpp +552 -48
  48. package/src/painlessmesh/ntp.hpp +2 -4
  49. package/src/painlessmesh/plugin.hpp +30 -6
  50. package/src/painlessmesh/protocol.hpp +55 -2
  51. package/src/painlessmesh/router.hpp +192 -77
  52. package/src/painlessmesh/tcp.hpp +10 -0
  53. package/src/painlessmesh/message_tracker.hpp +0 -311
  54. /package/examples/sendToInternet/{mock_server_test.ino → mock_server_test/mock_server_test.ino} +0 -0
package/README.md CHANGED
@@ -4,7 +4,7 @@
4
4
 
5
5
  <div align="center">
6
6
 
7
- **Version 1.10.0** - Tunable TCP connect-retry behaviour via `setTcpRetryConfig()` (#378)
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,54 +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.10.0 (August 12, 2026)
564
-
565
- **Tunable TCP Connect-Retry Behaviour (issue #378)**
566
-
567
- - The five TCP connect-retry values that were hardcoded in `tcp.hpp` are now tunable per mesh instance via `mesh.setTcpRetryConfig()` / `mesh.getTcpRetryConfig()` (#378, PR #395)
568
- - Defaults match the previous constants exactly — **no behaviour change unless you call the setter**
569
- - New `examples/tcpRetryConfig/` with real-time, high-reliability and battery-saver profiles
570
- - `MessageQueue` documentation corrected: it is a manual buffer, never an auto-flush queue (#385)
571
- - `MIN_FREE_MEMORY` / `MAX_MESSAGE_QUEUE` deprecated as ignored no-ops, values preserved for source compatibility (#385)
572
- - Fixed `bridge_failover` example failing to compile on an unqualified `plugin::` type (#360)
573
-
574
- > **npm users:** v1.9.21 was never published to npm ([#381](https://github.com/Alteriom/painlessMesh/issues/381) — expired token). npm's previous version is 1.9.20, so upgrading from npm picks up both releases. GitHub, PlatformIO and Arduino were unaffected.
575
-
576
- **Previous Release: v1.9.21 (August 4, 2026)**
577
-
578
- **Crash Fixes: Task & TCP Connection Lifecycle (issue #373)**
579
-
580
- - Fixed use-after-free in `Task::disable()` that crashed nodes on every peer disconnect (#373, PR #376 by @vaz82)
581
- - Fixed `PackageHandler::stop()` destroying the currently-executing task during bridge promotion
582
- - Fixed stale-pcb heap corruption window in `~BufferedConnection()` and an `onError`/`onConnect` double-handling race
583
- - New AddressSanitizer CI job and regression test for the task/connection cleanup lifecycle
599
+ ## Latest Release: v2.0.0 (September 7, 2026)
584
600
 
585
- **Previous Release: v1.9.20 (March 27, 2026)** Full repo cleanup, bug fixes & bridge coordination callbacks:
601
+ **Delivery confirmation, a unified send path, and a mesh that holds together on real hardware**
586
602
 
587
- - New `onBridgeCoordination()` and `onBridgeCoordinationChanged()` monitoring callbacks
588
- - Fixed 13 critical/high/medium bugs (double-free, RSSI overflow, memory leaks, blocking delays)
589
- - Removed ~3,600 lines of dead code and 40 AI-generated docs
590
- - Fixed all CI/CD workflows (corrected action versions)
591
- - Removed 15 fake test files, fixed test infrastructure
592
- - Documentation consistency audit — fixed broken links, API examples, version references
593
-
594
- **Recent Key Features (v1.9.0 - v1.9.16):**
595
-
596
- - 🔍 **Mesh Connectivity Detection** - New `hasActiveMeshConnections()` and `getLastKnownBridge()` APIs
597
- - 🌉 **Improved Bridge Detection** - `getPrimaryBridge()` returns last known bridge when disconnected
598
- - ⚡ **Enhanced TCP Reliability** - Exponential backoff and increased retries for mesh connections
599
- - 🛡️ **Race Condition Fixes** - Improved bridge status and connection validation
600
- - 📦 **Consolidated Examples** - Streamlined to 15 essential examples
601
- - ⚙️ **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.
602
608
 
603
609
  **[📋 Full CHANGELOG](CHANGELOG.md)**
604
610
 
605
611
  ## Getting Help
606
612
 
607
- - **[FAQ](USER_GUIDE.md)** - Common questions and solutions
608
- - **[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
609
615
  - **[GitHub Issues](https://github.com/Alteriom/painlessMesh/issues)** - Bug reports and feature requests
610
- - **[Community Forum](https://groups.google.com/forum/#!forum/painlessmesh-user)** - Community support
611
616
  - **[API Documentation](https://alteriom.github.io/painlessMesh/#/api/doxygen)** - Generated API docs
612
617
 
613
618
  ## Development
@@ -625,7 +630,8 @@ run-parts --regex catch_ bin/ # Run tests
625
630
 
626
631
  ### Requirements
627
632
 
628
- - **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
629
635
  - **Dependencies**: ArduinoJson 7.x, TaskScheduler 4.x
630
636
  - **Development**: CMake, Ninja, Boost (for desktop testing)
631
637
 
@@ -678,7 +684,7 @@ painlessMesh features a state-of-the-art automated CI/CD pipeline:
678
684
  ./scripts/validate-release.sh # Validate release readiness
679
685
 
680
686
  # Edit CHANGELOG.md, then commit with release prefix
681
- git commit -am "release: v1.5.7"
687
+ git commit -am "release: vX.Y.Z"
682
688
  git push origin main # Triggers automated release
683
689
  ```
684
690
 
@@ -686,13 +692,8 @@ See [RELEASE_GUIDE.md](RELEASE_GUIDE.md) for complete release documentation.
686
692
 
687
693
  ## Contributing
688
694
 
689
- 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.
690
-
691
- ## Funding
692
-
693
- If you like the library please consider supporting its development. Your contributions help me spend more time improving painlessMesh.
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).
694
696
 
695
- **[Donate via PayPal](https://www.paypal.com/paypalme/domlavoie)** • [dominic.lavoie@gmail.com](mailto:dominic.lavoie@gmail.com)
696
697
 
697
698
  ## 📚 Documentation
698
699
 
@@ -708,18 +709,18 @@ If you like the library please consider supporting its development. Your contrib
708
709
  ### 🚀 Quick Links
709
710
 
710
711
  **New to AlteriomPainlessMesh?**
711
- - [Quick Start](USER_GUIDE.md) - Get your first mesh running in 5 minutes
712
- - [Installation](USER_GUIDE.md) - Arduino IDE and PlatformIO setup
713
- - [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
714
715
 
715
716
  **Reference Documentation:**
716
- - [Core API](USER_GUIDE.md) - painlessMesh class methods
717
+ - [Core API](USER_GUIDE.md#api-reference) - painlessMesh class methods
717
718
  - [Alteriom Extensions](examples/alteriom/README.md) - SensorPackage, CommandPackage, StatusPackage
718
- - [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
719
720
 
720
721
  **Need Help?**
721
- - [FAQ](USER_GUIDE.md) - Frequently asked questions
722
- - [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
723
724
  - [GitHub Issues](https://github.com/Alteriom/painlessMesh/issues) - Bug reports and support
724
725
 
725
726
  ## 🔧 Quick API Reference
@@ -755,7 +756,11 @@ mesh.setDebugMsgTypes(ERROR | STARTUP | CONNECTION);
755
756
 
756
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).**
757
758
 
758
- # 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)
759
764
 
760
765
  Most development of painlessMesh has been done as a hobby, but some specific features have been funded by the companies listed below:
761
766