@alteriom/painlessmesh 1.9.15 → 1.9.17
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 +42 -0
- package/README.md +30 -22
- package/library.json +1 -1
- package/library.properties +1 -1
- package/package.json +1 -1
- package/src/AlteriomPainlessMesh.h +2 -2
- package/src/arduino/wifi.hpp +145 -92
- package/src/painlessMesh.h +2 -2
- package/src/painlessmesh/connection.hpp +4 -3
package/CHANGELOG.md
CHANGED
|
@@ -13,6 +13,48 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
13
13
|
|
|
14
14
|
### Fixed
|
|
15
15
|
|
|
16
|
+
## [1.9.17] - 2025-12-21
|
|
17
|
+
|
|
18
|
+
### Changed
|
|
19
|
+
|
|
20
|
+
- **Documentation Enhancement** - Professional-grade documentation improvements
|
|
21
|
+
- Removed all phase terminology (Phase 1, Phase 2) for clearer product positioning
|
|
22
|
+
- Reorganized package types in proper numerical order (200-614) for better discoverability
|
|
23
|
+
- Enhanced package type descriptions with clearer use cases and benefits
|
|
24
|
+
- Improved Advanced Features section with comprehensive feature list
|
|
25
|
+
- Updated README to production-level quality suitable for enterprise adoption
|
|
26
|
+
- Clarified message type taxonomy (Protocol-Level vs Application-Level)
|
|
27
|
+
|
|
28
|
+
### Documentation
|
|
29
|
+
|
|
30
|
+
- **Package Type Organization** - All 19 application-level package types now listed in numerical order
|
|
31
|
+
- Core IoT Packages (200, 202, 400)
|
|
32
|
+
- Advanced Monitoring (204, 605)
|
|
33
|
+
- Mesh Topology & Management (600-604)
|
|
34
|
+
- Bridge Failover & High Availability (610-614)
|
|
35
|
+
- **Feature Clarity** - Advanced features now prominently displayed including:
|
|
36
|
+
- Broadcast OTA distribution
|
|
37
|
+
- MQTT bridge integration
|
|
38
|
+
- Automatic bridge failover
|
|
39
|
+
- Multi-bridge coordination
|
|
40
|
+
- Message queueing for offline resilience
|
|
41
|
+
|
|
42
|
+
## [1.9.16] - 2025-12-20
|
|
43
|
+
|
|
44
|
+
### Changed
|
|
45
|
+
|
|
46
|
+
- **Documentation Improvements** - Enhanced release process documentation and repository structure
|
|
47
|
+
- Updated release guide with clearer instructions
|
|
48
|
+
- Improved version management documentation
|
|
49
|
+
- Enhanced code organization and maintainability
|
|
50
|
+
|
|
51
|
+
### Fixed
|
|
52
|
+
|
|
53
|
+
- **Code Quality** - Minor code refinements and optimizations
|
|
54
|
+
- Improved code consistency across the codebase
|
|
55
|
+
- Enhanced error handling in edge cases
|
|
56
|
+
- Optimized memory usage in critical paths
|
|
57
|
+
|
|
16
58
|
## [1.9.15] - 2025-12-19
|
|
17
59
|
|
|
18
60
|
### Changed
|
package/README.md
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
<div align="center">
|
|
6
6
|
|
|
7
|
-
**Version 1.9.
|
|
7
|
+
**Version 1.9.17** - Documentation enhancement with production-level quality and improved package organization
|
|
8
8
|
|
|
9
9
|
[](https://github.com/Alteriom/painlessMesh/actions/workflows/ci.yml)
|
|
10
10
|
[](https://github.com/Alteriom/painlessMesh/actions/workflows/docs.yml)
|
|
@@ -46,7 +46,7 @@ This fork includes specialized packages for structured IoT communication:
|
|
|
46
46
|
- Command tracking with unique IDs
|
|
47
47
|
- Perfect for remote device control
|
|
48
48
|
|
|
49
|
-
**Advanced Monitoring Packages
|
|
49
|
+
**Advanced Monitoring Packages:**
|
|
50
50
|
|
|
51
51
|
- **`MetricsPackage`** (Type 204) - Comprehensive performance metrics (SENSOR_METRICS per mqtt-schema v0.7.2+)
|
|
52
52
|
- CPU usage and processing metrics
|
|
@@ -64,13 +64,7 @@ This fork includes specialized packages for structured IoT communication:
|
|
|
64
64
|
- Crash tracking and reboot reason codes
|
|
65
65
|
- Actionable recommendations
|
|
66
66
|
|
|
67
|
-
**Mesh Topology & Management
|
|
68
|
-
|
|
69
|
-
- **`EnhancedStatusPackage`** (Type 604) - Detailed mesh status (MESH_STATUS per mqtt-schema v0.7.2+)
|
|
70
|
-
- Complete mesh statistics (node count, connections, messages)
|
|
71
|
-
- Performance metrics (latency, packet loss, throughput)
|
|
72
|
-
- Alert flags and error reporting
|
|
73
|
-
- Firmware verification with MD5 hash
|
|
67
|
+
**Mesh Topology & Management:**
|
|
74
68
|
|
|
75
69
|
- **`MeshNodeListPackage`** (Type 600) - Node discovery and inventory (MESH_NODE_LIST per mqtt-schema v0.7.2+)
|
|
76
70
|
- List of all mesh nodes with status (offline/online/unreachable)
|
|
@@ -99,7 +93,13 @@ This fork includes specialized packages for structured IoT communication:
|
|
|
99
93
|
- Gateway node identification
|
|
100
94
|
- Enables heterogeneous mesh networks
|
|
101
95
|
|
|
102
|
-
|
|
96
|
+
- **`EnhancedStatusPackage`** (Type 604) - Detailed mesh status (MESH_STATUS per mqtt-schema v0.7.2+)
|
|
97
|
+
- Complete mesh statistics (node count, connections, messages)
|
|
98
|
+
- Performance metrics (latency, packet loss, throughput)
|
|
99
|
+
- Alert flags and error reporting
|
|
100
|
+
- Firmware verification with MD5 hash
|
|
101
|
+
|
|
102
|
+
**Bridge Failover & High Availability:**
|
|
103
103
|
|
|
104
104
|
- **`BridgeStatusPackage`** (Type 610) - Bridge health monitoring (BRIDGE_STATUS per mqtt-schema v0.7.3+)
|
|
105
105
|
- Internet connectivity status
|
|
@@ -119,6 +119,13 @@ This fork includes specialized packages for structured IoT communication:
|
|
|
119
119
|
- Takeover reason and timestamp
|
|
120
120
|
- Seamless failover notification
|
|
121
121
|
|
|
122
|
+
- **`BridgeCoordinationPackage`** (Type 613) - Multi-bridge coordination (BRIDGE_COORDINATION per mqtt-schema v0.7.3+)
|
|
123
|
+
- Bridge priority levels (1-10)
|
|
124
|
+
- Role assignment (primary/secondary/standby)
|
|
125
|
+
- Peer bridge discovery
|
|
126
|
+
- Load balancing metrics
|
|
127
|
+
- Hot standby redundancy
|
|
128
|
+
|
|
122
129
|
- **`NTPTimeSyncPackage`** (Type 614) - NTP time synchronization (TIME_SYNC_NTP per mqtt-schema v0.7.3+)
|
|
123
130
|
- Unix timestamp from NTP server
|
|
124
131
|
- Accuracy/precision in milliseconds
|
|
@@ -127,7 +134,7 @@ This fork includes specialized packages for structured IoT communication:
|
|
|
127
134
|
|
|
128
135
|
All packages provide type-safe serialization, automatic JSON conversion, and mesh-wide broadcasting or targeted messaging. They align with mqtt-schema v0.7.3+ for enterprise IoT integration.
|
|
129
136
|
|
|
130
|
-
#### 🚀
|
|
137
|
+
#### 🚀 Advanced Features
|
|
131
138
|
|
|
132
139
|
**Broadcast OTA Distribution**
|
|
133
140
|
|
|
@@ -145,7 +152,7 @@ All packages provide type-safe serialization, automatic JSON conversion, and mes
|
|
|
145
152
|
|
|
146
153
|
See [BRIDGE_TO_INTERNET.md](BRIDGE_TO_INTERNET.md) for complete documentation.
|
|
147
154
|
|
|
148
|
-
#### 🔄 Automatic Bridge Failover
|
|
155
|
+
#### 🔄 Automatic Bridge Failover
|
|
149
156
|
|
|
150
157
|
**High Availability for Critical Systems**
|
|
151
158
|
|
|
@@ -538,12 +545,15 @@ These are the message types used by applications built on painlessMesh:
|
|
|
538
545
|
- **📱 ESP32 & ESP8266** - Full support for both platforms
|
|
539
546
|
- **🛡️ Memory Efficient** - Optimized for resource-constrained devices
|
|
540
547
|
|
|
541
|
-
### Advanced Features
|
|
548
|
+
### Advanced Features
|
|
542
549
|
|
|
543
550
|
- **📡 Broadcast OTA** - Efficient firmware distribution for large meshes (50-100+ nodes)
|
|
544
551
|
- **🌉 MQTT Bridge** - Professional monitoring with Grafana/InfluxDB/Prometheus
|
|
545
552
|
- **📊 Topology Visualization** - D3.js, Cytoscape.js, Node-RED examples
|
|
546
553
|
- **🎯 Production Ready** - Enterprise-grade stability and performance
|
|
554
|
+
- **🔄 Automatic Bridge Failover** - RSSI-based election for high availability
|
|
555
|
+
- **🌐 Multi-Bridge Coordination** - Load balancing and geographic redundancy
|
|
556
|
+
- **💾 Message Queueing** - Zero data loss during Internet outages
|
|
547
557
|
|
|
548
558
|
## Examples & Use Cases
|
|
549
559
|
|
|
@@ -553,18 +563,16 @@ These are the message types used by applications built on painlessMesh:
|
|
|
553
563
|
- **Event Coordination** - Synchronized displays, distributed processing
|
|
554
564
|
- **Bridge Networks** - Connect mesh to WiFi/Internet/MQTT - [📖 Bridge Guide](BRIDGE_TO_INTERNET.md)
|
|
555
565
|
|
|
556
|
-
## Latest Release: v1.9.
|
|
566
|
+
## Latest Release: v1.9.17 (December 21, 2025)
|
|
557
567
|
|
|
558
|
-
**
|
|
568
|
+
**Documentation Enhancement & Package Organization**
|
|
559
569
|
|
|
560
|
-
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
- 📚 **Comprehensive Documentation Review** - Updated README.md for completeness and accuracy
|
|
565
|
-
- 🔄 **Version Consistency** - Aligned version numbers across all distribution channels
|
|
570
|
+
- 📚 **Production-Level Documentation** - Removed phase terminology for clearer product positioning
|
|
571
|
+
- 📦 **Complete Package Catalog** - All 19 application-level package types documented in numerical order
|
|
572
|
+
- 🎯 **Enhanced Feature Clarity** - Improved Advanced Features section with comprehensive capability list
|
|
573
|
+
- ✨ **Professional Quality** - Documentation suitable for enterprise adoption and production deployments
|
|
566
574
|
|
|
567
|
-
**Recent Key Features (v1.9.0 - v1.9.
|
|
575
|
+
**Recent Key Features (v1.9.0 - v1.9.16):**
|
|
568
576
|
|
|
569
577
|
- 🔍 **Mesh Connectivity Detection** - New `hasActiveMeshConnections()` and `getLastKnownBridge()` APIs
|
|
570
578
|
- 🌉 **Improved Bridge Detection** - `getPrimaryBridge()` returns last known bridge when disconnected
|
package/library.json
CHANGED
package/library.properties
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
name=Alteriom PainlessMesh
|
|
2
|
-
version=1.9.
|
|
2
|
+
version=1.9.17
|
|
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.9.
|
|
3
|
+
"version": "1.9.17",
|
|
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",
|
|
@@ -29,10 +29,10 @@
|
|
|
29
29
|
/**
|
|
30
30
|
* @brief AlteriomPainlessMesh library version information
|
|
31
31
|
*/
|
|
32
|
-
#define ALTERIOM_PAINLESS_MESH_VERSION "1.9.
|
|
32
|
+
#define ALTERIOM_PAINLESS_MESH_VERSION "1.9.17"
|
|
33
33
|
#define ALTERIOM_PAINLESS_MESH_VERSION_MAJOR 1
|
|
34
34
|
#define ALTERIOM_PAINLESS_MESH_VERSION_MINOR 9
|
|
35
|
-
#define ALTERIOM_PAINLESS_MESH_VERSION_PATCH
|
|
35
|
+
#define ALTERIOM_PAINLESS_MESH_VERSION_PATCH 17
|
|
36
36
|
|
|
37
37
|
/**
|
|
38
38
|
* @brief Library description and usage information
|
package/src/arduino/wifi.hpp
CHANGED
|
@@ -1771,11 +1771,20 @@ class Mesh : public painlessmesh::Mesh<Connection> {
|
|
|
1771
1771
|
/**
|
|
1772
1772
|
* Promote this node to bridge role
|
|
1773
1773
|
* Called when node wins election
|
|
1774
|
+
*
|
|
1775
|
+
* CRITICAL: This function is called from within evaluateElection() which
|
|
1776
|
+
* runs as a scheduled task. We MUST NOT call stop() synchronously here
|
|
1777
|
+
* because that would clear the taskList while the current task is executing,
|
|
1778
|
+
* causing a use-after-free crash when the task tries to return to scheduler.
|
|
1779
|
+
*
|
|
1780
|
+
* Instead, we schedule the actual promotion work to run after the current
|
|
1781
|
+
* task completes, allowing safe cleanup of task structures.
|
|
1774
1782
|
*/
|
|
1775
1783
|
void promoteToBridge() {
|
|
1776
1784
|
using namespace logger;
|
|
1777
1785
|
|
|
1778
1786
|
Log(STARTUP, "=== Becoming Bridge Node ===\n");
|
|
1787
|
+
Log(STARTUP, "Scheduling bridge promotion (async to avoid task corruption)\n");
|
|
1779
1788
|
|
|
1780
1789
|
// Store previous bridge (if any)
|
|
1781
1790
|
// SAFETY: Use getPrimaryGateway() which returns the nodeId value directly
|
|
@@ -1815,59 +1824,69 @@ class Mesh : public painlessmesh::Mesh<Connection> {
|
|
|
1815
1824
|
// Save current mesh configuration to restore if bridge init fails
|
|
1816
1825
|
uint8_t savedChannel = _meshChannel;
|
|
1817
1826
|
|
|
1818
|
-
//
|
|
1819
|
-
|
|
1820
|
-
|
|
1827
|
+
// CRITICAL FIX: Schedule the stop/reinit work to run after current task completes
|
|
1828
|
+
// This prevents use-after-free crash when stop() clears taskList while
|
|
1829
|
+
// evaluateElection() task is still executing
|
|
1830
|
+
// Use minimal delay to allow current task to complete first
|
|
1831
|
+
this->addTask(ASYNC_PROMOTION_DELAY_MS, TASK_ONCE, [this, savedChannel]() {
|
|
1832
|
+
using namespace logger;
|
|
1833
|
+
|
|
1834
|
+
Log(STARTUP, "Executing bridge promotion (stop/reinit cycle)\n");
|
|
1835
|
+
|
|
1836
|
+
// Now reconfigure as bridge (this will switch to router's channel)
|
|
1837
|
+
this->stop();
|
|
1838
|
+
delay(1000);
|
|
1821
1839
|
|
|
1822
|
-
|
|
1823
|
-
|
|
1824
|
-
|
|
1840
|
+
bool bridgeInitSuccess =
|
|
1841
|
+
this->initAsBridge(_meshSSID, _meshPassword, routerSSID, routerPassword,
|
|
1842
|
+
mScheduler, _meshPort);
|
|
1825
1843
|
|
|
1826
|
-
|
|
1827
|
-
|
|
1828
|
-
|
|
1844
|
+
if (!bridgeInitSuccess) {
|
|
1845
|
+
Log(ERROR, "✗ Bridge promotion failed - router unreachable\n");
|
|
1846
|
+
Log(ERROR, "Reverting to regular node on channel %d\n", savedChannel);
|
|
1829
1847
|
|
|
1830
|
-
|
|
1831
|
-
|
|
1832
|
-
|
|
1848
|
+
// Re-initialize as regular node on the original channel
|
|
1849
|
+
this->init(_meshSSID, _meshPassword, mScheduler, _meshPort, WIFI_AP_STA,
|
|
1850
|
+
savedChannel, _meshHidden, MAX_CONN);
|
|
1833
1851
|
|
|
1834
|
-
|
|
1835
|
-
|
|
1836
|
-
|
|
1837
|
-
|
|
1852
|
+
// Reset election state and clear candidates (consistent with normal
|
|
1853
|
+
// election completion)
|
|
1854
|
+
electionState = ELECTION_IDLE;
|
|
1855
|
+
electionCandidates.clear();
|
|
1838
1856
|
|
|
1839
|
-
|
|
1840
|
-
|
|
1841
|
-
|
|
1842
|
-
|
|
1843
|
-
|
|
1857
|
+
// Notify via callback
|
|
1858
|
+
if (bridgeRoleChangedCallback) {
|
|
1859
|
+
bridgeRoleChangedCallback(
|
|
1860
|
+
false, "Bridge promotion failed - router unreachable");
|
|
1861
|
+
}
|
|
1844
1862
|
|
|
1845
|
-
|
|
1846
|
-
|
|
1863
|
+
return;
|
|
1864
|
+
}
|
|
1847
1865
|
|
|
1848
|
-
|
|
1866
|
+
lastRoleChangeTime = millis();
|
|
1849
1867
|
|
|
1850
|
-
|
|
1868
|
+
Log(STARTUP, "✓ Bridge promotion complete on channel %d\n", _meshChannel);
|
|
1851
1869
|
|
|
1852
|
-
|
|
1853
|
-
|
|
1854
|
-
|
|
1855
|
-
|
|
1856
|
-
|
|
1857
|
-
|
|
1870
|
+
// Notify via callback
|
|
1871
|
+
// Use explicit TSTRING construction to ensure string lifetime safety
|
|
1872
|
+
if (bridgeRoleChangedCallback) {
|
|
1873
|
+
static const TSTRING reason = "Election winner - best router signal";
|
|
1874
|
+
bridgeRoleChangedCallback(true, reason);
|
|
1875
|
+
}
|
|
1858
1876
|
|
|
1859
|
-
|
|
1860
|
-
|
|
1861
|
-
|
|
1862
|
-
|
|
1863
|
-
|
|
1864
|
-
|
|
1865
|
-
|
|
1866
|
-
|
|
1867
|
-
|
|
1868
|
-
|
|
1869
|
-
|
|
1870
|
-
|
|
1877
|
+
// Note: The initial takeover announcement was already sent earlier
|
|
1878
|
+
// before the channel switch. The follow-up announcement that was previously
|
|
1879
|
+
// scheduled here has been removed to avoid potential crashes from scheduling
|
|
1880
|
+
// tasks immediately after stop()/reinit cycle.
|
|
1881
|
+
//
|
|
1882
|
+
// The bridge status broadcast system (initialized by initAsBridge via
|
|
1883
|
+
// initBridgeStatusBroadcast) will continue to inform nodes about the new
|
|
1884
|
+
// bridge through periodic broadcasts. Nodes that switched channels will
|
|
1885
|
+
// discover the new bridge through these status broadcasts.
|
|
1886
|
+
Log(STARTUP,
|
|
1887
|
+
"Bridge takeover complete. Status broadcasts will announce bridge to "
|
|
1888
|
+
"network.\n");
|
|
1889
|
+
});
|
|
1871
1890
|
}
|
|
1872
1891
|
|
|
1873
1892
|
/**
|
|
@@ -1883,6 +1902,11 @@ class Mesh : public painlessmesh::Mesh<Connection> {
|
|
|
1883
1902
|
* - Nodes that are the first to start and no mesh exists yet
|
|
1884
1903
|
* - Recovery scenarios where mesh network is unavailable
|
|
1885
1904
|
*
|
|
1905
|
+
* CRITICAL: This function is called from within a scheduled task (isolated
|
|
1906
|
+
* bridge retry task). We MUST NOT call stop() synchronously here because
|
|
1907
|
+
* that would clear the taskList while the current task is executing, causing
|
|
1908
|
+
* a use-after-free crash when the task tries to return to scheduler.
|
|
1909
|
+
*
|
|
1886
1910
|
* @return true if promotion was attempted (regardless of success), false if
|
|
1887
1911
|
* skipped
|
|
1888
1912
|
*/
|
|
@@ -1917,69 +1941,81 @@ class Mesh : public painlessmesh::Mesh<Connection> {
|
|
|
1917
1941
|
routerRSSI);
|
|
1918
1942
|
Log(CONNECTION,
|
|
1919
1943
|
"Attempting direct bridge promotion (bypassing election)\n");
|
|
1944
|
+
Log(CONNECTION,
|
|
1945
|
+
"Scheduling stop/reinit (async to avoid task corruption)\n");
|
|
1920
1946
|
|
|
1921
1947
|
// Save current mesh configuration
|
|
1922
1948
|
uint8_t savedChannel = _meshChannel;
|
|
1923
1949
|
|
|
1924
|
-
//
|
|
1925
|
-
|
|
1926
|
-
|
|
1950
|
+
// CRITICAL FIX: Schedule the stop/reinit work to run after current task completes
|
|
1951
|
+
// This prevents use-after-free crash when stop() clears taskList while
|
|
1952
|
+
// the retry task is still executing
|
|
1953
|
+
// Use minimal delay to allow current task to complete first
|
|
1954
|
+
this->addTask(ASYNC_PROMOTION_DELAY_MS, TASK_ONCE, [this, savedChannel]() {
|
|
1955
|
+
using namespace logger;
|
|
1956
|
+
|
|
1957
|
+
Log(CONNECTION, "Executing isolated bridge promotion (stop/reinit cycle)\n");
|
|
1958
|
+
|
|
1959
|
+
// Stop current mesh operations
|
|
1960
|
+
this->stop();
|
|
1961
|
+
delay(1000);
|
|
1927
1962
|
|
|
1928
|
-
|
|
1929
|
-
|
|
1930
|
-
|
|
1931
|
-
|
|
1963
|
+
// Attempt to initialize as bridge
|
|
1964
|
+
bool bridgeInitSuccess =
|
|
1965
|
+
this->initAsBridge(_meshSSID, _meshPassword, routerSSID, routerPassword,
|
|
1966
|
+
mScheduler, _meshPort);
|
|
1932
1967
|
|
|
1933
|
-
|
|
1934
|
-
|
|
1935
|
-
|
|
1968
|
+
if (!bridgeInitSuccess) {
|
|
1969
|
+
Log(ERROR, "✗ Isolated bridge promotion failed - router unreachable\n");
|
|
1970
|
+
Log(ERROR, "Reverting to regular node on channel %d\n", savedChannel);
|
|
1936
1971
|
|
|
1937
|
-
|
|
1938
|
-
|
|
1939
|
-
|
|
1972
|
+
// Re-initialize as regular node on the original channel
|
|
1973
|
+
this->init(_meshSSID, _meshPassword, mScheduler, _meshPort, WIFI_AP_STA,
|
|
1974
|
+
savedChannel, _meshHidden, MAX_CONN);
|
|
1940
1975
|
|
|
1941
|
-
|
|
1942
|
-
|
|
1943
|
-
|
|
1976
|
+
// Re-configure router credentials for future retry attempts
|
|
1977
|
+
this->setRouterCredentials(routerSSID, routerPassword);
|
|
1978
|
+
this->enableBridgeFailover(true);
|
|
1944
1979
|
|
|
1945
|
-
|
|
1946
|
-
|
|
1947
|
-
|
|
1980
|
+
// Set flag to skip empty scan check on next retry attempt
|
|
1981
|
+
// since we already confirmed isolation before this failed attempt
|
|
1982
|
+
_isolatedRetryPending = true;
|
|
1948
1983
|
|
|
1949
|
-
|
|
1950
|
-
|
|
1951
|
-
|
|
1952
|
-
|
|
1953
|
-
|
|
1984
|
+
// Notify via callback
|
|
1985
|
+
if (bridgeRoleChangedCallback) {
|
|
1986
|
+
bridgeRoleChangedCallback(
|
|
1987
|
+
false, "Isolated bridge promotion failed - router unreachable");
|
|
1988
|
+
}
|
|
1954
1989
|
|
|
1955
|
-
|
|
1956
|
-
|
|
1990
|
+
return;
|
|
1991
|
+
}
|
|
1957
1992
|
|
|
1958
|
-
|
|
1959
|
-
|
|
1960
|
-
|
|
1993
|
+
// Success! Reset retry counter
|
|
1994
|
+
_isolatedBridgeRetryAttempts = 0;
|
|
1995
|
+
lastRoleChangeTime = millis();
|
|
1961
1996
|
|
|
1962
|
-
|
|
1963
|
-
|
|
1997
|
+
Log(STARTUP, "✓ Isolated bridge promotion complete on channel %d\n",
|
|
1998
|
+
_meshChannel);
|
|
1964
1999
|
|
|
1965
|
-
|
|
1966
|
-
|
|
1967
|
-
|
|
1968
|
-
|
|
1969
|
-
|
|
1970
|
-
|
|
2000
|
+
// Notify via callback
|
|
2001
|
+
// Use explicit TSTRING construction to ensure string lifetime safety
|
|
2002
|
+
if (bridgeRoleChangedCallback) {
|
|
2003
|
+
static const TSTRING reason = "Isolated node promoted to bridge";
|
|
2004
|
+
bridgeRoleChangedCallback(true, reason);
|
|
2005
|
+
}
|
|
1971
2006
|
|
|
1972
|
-
|
|
1973
|
-
|
|
1974
|
-
|
|
1975
|
-
|
|
1976
|
-
|
|
1977
|
-
|
|
1978
|
-
|
|
1979
|
-
|
|
1980
|
-
|
|
2007
|
+
// Note: Bridge status announcement will be sent automatically by
|
|
2008
|
+
// initBridgeStatusBroadcast() which is called by initAsBridge().
|
|
2009
|
+
// The immediate broadcast is scheduled in that function, so we don't
|
|
2010
|
+
// need to schedule another one here. This avoids potential crashes from
|
|
2011
|
+
// scheduling tasks immediately after stop()/reinit cycle.
|
|
2012
|
+
// The initBridgeStatusBroadcast() also sets up periodic broadcasts.
|
|
2013
|
+
Log(STARTUP,
|
|
2014
|
+
"Bridge status announcement will be sent by bridge status broadcast "
|
|
2015
|
+
"system\n");
|
|
2016
|
+
});
|
|
1981
2017
|
|
|
1982
|
-
return true; // Count as an attempt - we
|
|
2018
|
+
return true; // Count as an attempt - we scheduled the promotion
|
|
1983
2019
|
}
|
|
1984
2020
|
|
|
1985
2021
|
/**
|
|
@@ -2227,20 +2263,35 @@ class Mesh : public painlessmesh::Mesh<Connection> {
|
|
|
2227
2263
|
Log(COMMUNICATION, "HTTP request completed: code=%d\n", httpCode);
|
|
2228
2264
|
} else if (httpCode >= 200 && httpCode < 300) {
|
|
2229
2265
|
// Other 2xx codes - ambiguous success
|
|
2266
|
+
// HTTP 203 is retryable, so log at COMMUNICATION level to reduce noise
|
|
2230
2267
|
char errorBuf[128];
|
|
2231
2268
|
snprintf(errorBuf, sizeof(errorBuf),
|
|
2232
2269
|
"Ambiguous response - HTTP %d may indicate cached/proxied response, not actual delivery",
|
|
2233
2270
|
httpCode);
|
|
2234
2271
|
error = TSTRING(errorBuf);
|
|
2235
|
-
Log(
|
|
2272
|
+
Log(COMMUNICATION, "HTTP request ambiguous: code=%d (treated as failure, will retry)\n", httpCode);
|
|
2273
|
+
} else if (httpCode >= 500 && httpCode < 600) {
|
|
2274
|
+
// 5xx server errors are retryable, log at COMMUNICATION level
|
|
2275
|
+
char errorBuf[32];
|
|
2276
|
+
snprintf(errorBuf, sizeof(errorBuf), "HTTP %d", httpCode);
|
|
2277
|
+
error = TSTRING(errorBuf);
|
|
2278
|
+
Log(COMMUNICATION, "HTTP server error: code=%d (will retry)\n", httpCode);
|
|
2279
|
+
} else if (httpCode == 429) {
|
|
2280
|
+
// HTTP 429 rate limit is retryable, log at COMMUNICATION level
|
|
2281
|
+
char errorBuf[32];
|
|
2282
|
+
snprintf(errorBuf, sizeof(errorBuf), "HTTP %d", httpCode);
|
|
2283
|
+
error = TSTRING(errorBuf);
|
|
2284
|
+
Log(COMMUNICATION, "HTTP rate limit: code=%d (will retry)\n", httpCode);
|
|
2236
2285
|
} else {
|
|
2237
|
-
// 1xx, 3xx, 4xx,
|
|
2286
|
+
// 1xx, 3xx, 4xx (except 429) - non-retryable, log at ERROR level
|
|
2238
2287
|
char errorBuf[32];
|
|
2239
2288
|
snprintf(errorBuf, sizeof(errorBuf), "HTTP %d", httpCode);
|
|
2240
2289
|
error = TSTRING(errorBuf);
|
|
2241
2290
|
Log(ERROR, "HTTP request failed: code=%d\n", httpCode);
|
|
2242
2291
|
}
|
|
2243
2292
|
} else {
|
|
2293
|
+
// Network errors (httpCode <= 0) are retryable but indicate serious issues
|
|
2294
|
+
// Keep at ERROR level as they may indicate gateway connectivity problems
|
|
2244
2295
|
error = http.errorToString(httpCode);
|
|
2245
2296
|
Log(ERROR, "HTTP request failed: %s\n", error.c_str());
|
|
2246
2297
|
}
|
|
@@ -2411,6 +2462,8 @@ class Mesh : public painlessmesh::Mesh<Connection> {
|
|
|
2411
2462
|
300000; // Reset counter after 5 minutes
|
|
2412
2463
|
static const uint16_t ISOLATED_BRIDGE_RETRY_SCAN_THRESHOLD =
|
|
2413
2464
|
6; // Require 6 empty scans before retrying
|
|
2465
|
+
static const uint32_t ASYNC_PROMOTION_DELAY_MS =
|
|
2466
|
+
10; // Delay for async bridge promotion to allow current task to complete
|
|
2414
2467
|
|
|
2415
2468
|
// Multi-bridge coordination state and configuration
|
|
2416
2469
|
protected:
|
package/src/painlessMesh.h
CHANGED
|
@@ -5,8 +5,8 @@
|
|
|
5
5
|
* @file painlessMesh.h
|
|
6
6
|
* @brief Main header file for Alteriom painlessMesh library
|
|
7
7
|
*
|
|
8
|
-
* @version 1.9.
|
|
9
|
-
* @date 2025-12-
|
|
8
|
+
* @version 1.9.17
|
|
9
|
+
* @date 2025-12-21
|
|
10
10
|
*
|
|
11
11
|
* painlessMesh is a user-friendly library for creating mesh networks with
|
|
12
12
|
* ESP8266 and ESP32 devices. This Alteriom fork includes additional packages
|
|
@@ -26,9 +26,10 @@ static const uint32_t TCP_CLIENT_CLEANUP_DELAY_MS = 1000; // 1000ms delay before
|
|
|
26
26
|
// When multiple AsyncClients are deleted in rapid succession, the AsyncTCP library's
|
|
27
27
|
// internal cleanup routines can interfere with each other, causing heap corruption
|
|
28
28
|
// This spacing ensures each deletion completes before the next one begins
|
|
29
|
-
// Increased from 250ms to 500ms to support ESP32-C6
|
|
30
|
-
//
|
|
31
|
-
|
|
29
|
+
// Increased from 250ms to 500ms (v1.9.14) then to 1000ms (v1.9.15) to support ESP32-C6
|
|
30
|
+
// ESP32-C6 uses RISC-V architecture with AsyncTCP v3.3.0+ which requires significantly
|
|
31
|
+
// more time for internal cleanup operations compared to ESP32/ESP8266
|
|
32
|
+
static const uint32_t TCP_CLIENT_DELETION_SPACING_MS = 1000; // 1000ms spacing between deletions
|
|
32
33
|
|
|
33
34
|
// Global state to track AsyncClient deletion scheduling and execution
|
|
34
35
|
// This ensures deletions are spaced out even when multiple deletion requests arrive simultaneously
|