@alteriom/painlessmesh 1.8.8 → 1.8.9

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 CHANGED
@@ -7,6 +7,52 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [1.8.9] - 2025-11-12
11
+
12
+ ### Fixed
13
+
14
+ - **Bridge Self-Registration (Type 610 & 613)** - Bridge nodes now properly track themselves in status and coordination
15
+ - **Bridge Status Broadcasting (Type 610)**: Fixed bridge nodes reporting "Known bridges: 0" despite being active
16
+ - Added immediate self-registration task in `initBridgeStatusBroadcast()` (line ~746)
17
+ - Bridge now calls `updateBridgeStatus()` with own nodeId immediately after initialization
18
+ - Added self-update in `sendBridgeStatus()` (line ~1192) before broadcasting
19
+ - Ensures bridge appears in its own `knownBridges` list from the start
20
+ - **Bridge Coordination (Type 613)**: Fixed multi-bridge priority tracking
21
+ - Added self-registration in `initBridgeCoordination()` (line ~803)
22
+ - Bridge now adds own priority to `bridgePriorities` map: `bridgePriorities[this->nodeId] = bridgePriority`
23
+ - Added priority self-update in `sendBridgeCoordination()` (line ~869) before broadcasting
24
+ - Ensures primary bridge selection works correctly with multiple bridges
25
+ - **Root Cause**: Mesh networks don't loop broadcasts back to sender by design
26
+ - Nodes receive broadcasts from other nodes but not their own messages
27
+ - Requires explicit local state management for any tracking data
28
+ - **Before Fix**:
29
+ - Bridge reports "Known bridges: 0" and "No primary bridge available!"
30
+ - Multi-bridge setups fail to select primary bridge (missing own priority)
31
+ - Bridge failover unreliable due to incomplete bridge tracking
32
+ - **After Fix**:
33
+ - Bridge correctly reports "Known bridges: 1" (or more in multi-bridge setups)
34
+ - Primary bridge selection works properly with all bridge priorities present
35
+ - Self-tracking pattern now consistent across all periodic broadcast types
36
+ - Core fixes in `src/arduino/wifi.hpp`
37
+ - Resolves @woodlist GitHub issue - bridge showing "Known bridges: 0"
38
+ - Comprehensive analysis documented in COMPREHENSIVE_BROADCAST_ANALYSIS.md
39
+
40
+ ### Changed
41
+
42
+ - **Build System** - Switched Docker compiler from clang++ to g++
43
+ - Changed ENV CXX in Dockerfile from clang++ to g++
44
+ - Resolves template instantiation crashes during Docker builds
45
+ - Build verification confirms successful compilation with g++
46
+
47
+ ### Documentation
48
+
49
+ - **Broadcast Message Analysis** - Added comprehensive review documentation
50
+ - Created COMPREHENSIVE_BROADCAST_ANALYSIS.md with full analysis of all 4 broadcast types
51
+ - Documents self-tracking requirements for Type 610 (STATUS) and 613 (COORDINATION)
52
+ - Confirms Type 611 (ELECTION) already implements correct self-registration
53
+ - Confirms Type 612 (TAKEOVER) doesn't require self-tracking (notification only)
54
+ - Establishes pattern guidelines for future broadcast implementations
55
+
10
56
  ## [1.8.8] - 2025-11-12
11
57
 
12
58
  ### Fixed
@@ -0,0 +1,221 @@
1
+ # Bridge Status Self-Registration Fix
2
+
3
+ ## Issue Reported by @woodlist
4
+
5
+ **Problem**: After successfully promoting to bridge via election, the node reports:
6
+ ```
7
+ I am bridge: YES
8
+ Internet available: NO
9
+ Known bridges: 0
10
+ No primary bridge available! ❌
11
+ ```
12
+
13
+ ## Root Cause
14
+
15
+ When a node becomes a bridge (either via `initAsBridge()` or election promotion), it broadcasts bridge status messages to the mesh network, but **it does not add itself to its own `knownBridges` list**.
16
+
17
+ ### Why This Happens
18
+
19
+ 1. Node wins election and promotes to bridge ✅
20
+ 2. Node calls `initBridgeStatusBroadcast()` ✅
21
+ 3. Node sends bridge status broadcasts ✅
22
+ 4. **Other nodes receive broadcasts and update their `knownBridges` ✅**
23
+ 5. **Bridge node itself never receives its own broadcast ❌**
24
+ 6. Bridge node's `knownBridges` remains empty ❌
25
+ 7. `getPrimaryBridge()` returns `nullptr` because list is empty ❌
26
+
27
+ ### Code Flow
28
+
29
+ ```cpp
30
+ // When bridge promotes
31
+ promoteToBridge()
32
+ → initAsBridge()
33
+ → initBridgeStatusBroadcast()
34
+ → sendBridgeStatus() // Broadcasts to network
35
+ → sendBroadcast(msg) // Bridge doesn't receive its own broadcasts
36
+ ```
37
+
38
+ ## The Fix
39
+
40
+ ### Changes to `src/arduino/wifi.hpp`
41
+
42
+ #### 1. Modified `initBridgeStatusBroadcast()`
43
+
44
+ Added self-registration during bridge initialization:
45
+
46
+ ```cpp
47
+ void initBridgeStatusBroadcast() {
48
+ using namespace logger;
49
+
50
+ if (!this->isBridge() || !this->bridgeStatusBroadcastEnabled) {
51
+ return;
52
+ }
53
+
54
+ Log(STARTUP, "initBridgeStatusBroadcast(): Setting up bridge status broadcast\n");
55
+
56
+ // NEW: Register ourselves as a bridge in the knownBridges list
57
+ // This ensures the bridge knows about itself and reports correct status
58
+ this->addTask([this]() {
59
+ bool hasInternet = (WiFi.status() == WL_CONNECTED) &&
60
+ (WiFi.localIP() != IPAddress(0, 0, 0, 0));
61
+
62
+ this->updateBridgeStatus(
63
+ this->nodeId, // bridgeNodeId
64
+ hasInternet, // internetConnected
65
+ WiFi.RSSI(), // routerRSSI
66
+ WiFi.channel(), // routerChannel
67
+ millis(), // uptime
68
+ WiFi.gatewayIP().toString(),// gatewayIP
69
+ this->getNodeTime() // timestamp
70
+ );
71
+
72
+ Log(STARTUP, "initBridgeStatusBroadcast(): Registered self as bridge (nodeId: %u)\n",
73
+ this->nodeId);
74
+ });
75
+
76
+ // ... rest of method unchanged
77
+ }
78
+ ```
79
+
80
+ #### 2. Modified `sendBridgeStatus()`
81
+
82
+ Added self-update before broadcasting:
83
+
84
+ ```cpp
85
+ void sendBridgeStatus() {
86
+ using namespace logger;
87
+
88
+ if (!this->bridgeStatusBroadcastEnabled) {
89
+ return;
90
+ }
91
+
92
+ // ... create JSON message ...
93
+
94
+ bool hasInternet = (WiFi.status() == WL_CONNECTED) &&
95
+ (WiFi.localIP() != IPAddress(0, 0, 0, 0));
96
+
97
+ int8_t rssi = WiFi.RSSI();
98
+ uint8_t channel = WiFi.channel();
99
+ uint32_t uptime = millis();
100
+ TSTRING gatewayIP = WiFi.gatewayIP().toString();
101
+
102
+ // ... add to JSON ...
103
+
104
+ Log(GENERAL, "sendBridgeStatus(): Broadcasting status (Internet: %s)\n",
105
+ hasInternet ? "Connected" : "Disconnected");
106
+ Log(GENERAL, "sendBridgeStatus(): WiFi status=%d, localIP=%s, gatewayIP=%s\n",
107
+ WiFi.status(), WiFi.localIP().toString().c_str(), WiFi.gatewayIP().toString().c_str());
108
+
109
+ // NEW: Update our own bridge status in knownBridges list
110
+ // This ensures the bridge reports itself correctly when queried
111
+ this->updateBridgeStatus(this->nodeId, hasInternet, rssi, channel,
112
+ uptime, gatewayIP, this->getNodeTime());
113
+
114
+ this->sendBroadcast(msg);
115
+ }
116
+ ```
117
+
118
+ ## Expected Behavior After Fix
119
+
120
+ ### Before Fix
121
+ ```
122
+ --- Bridge Status ---
123
+ I am bridge: YES
124
+ Internet available: NO
125
+ Known bridges: 0
126
+ No primary bridge available! ❌
127
+ --------------------
128
+ ```
129
+
130
+ ### After Fix
131
+ ```
132
+ --- Bridge Status ---
133
+ I am bridge: YES
134
+ Internet available: NO ⚠️ (May be YES if router has internet)
135
+ Known bridges: 1
136
+ Primary bridge: 3394043125 (RSSI: -36 dBm) ✅
137
+ --------------------
138
+ ```
139
+
140
+ ## Technical Details
141
+
142
+ ### Why Two Registration Points?
143
+
144
+ 1. **`initBridgeStatusBroadcast()`** - Initial registration when bridge first starts
145
+ - Ensures bridge is in `knownBridges` immediately after promotion
146
+ - Provides accurate status for early queries
147
+
148
+ 2. **`sendBridgeStatus()`** - Periodic updates
149
+ - Keeps bridge info current in `knownBridges`
150
+ - Updates RSSI, uptime, internet connectivity dynamically
151
+ - Ensures consistency between broadcast and local state
152
+
153
+ ### Internet Connectivity
154
+
155
+ The "Internet available: NO" in @woodlist's log may be accurate depending on:
156
+ - Router has active internet connection
157
+ - DHCP has assigned valid IP address (not 0.0.0.0)
158
+ - `WiFi.localIP()` returns valid address
159
+
160
+ Check with:
161
+ ```cpp
162
+ Log(GENERAL, "sendBridgeStatus(): WiFi status=%d, localIP=%s, gatewayIP=%s\n",
163
+ WiFi.status(), WiFi.localIP().toString().c_str(), WiFi.gatewayIP().toString().c_str());
164
+ ```
165
+
166
+ This log line was present in the code but not visible in the serial output @woodlist provided.
167
+
168
+ ## Impact
169
+
170
+ ### Fixed Issues
171
+ ✅ Bridge nodes now correctly report themselves in `knownBridges`
172
+ ✅ `getPrimaryBridge()` returns valid bridge pointer for bridge nodes
173
+ ✅ Bridge status displays accurately show "Known bridges: 1" minimum
174
+ ✅ Regular nodes can immediately discover newly promoted bridges
175
+
176
+ ### Backward Compatibility
177
+ ✅ No breaking changes to existing API
178
+ ✅ Compatible with existing bridge setups
179
+ ✅ Works with both manual `initAsBridge()` and election-based promotion
180
+ ✅ Multi-bridge configurations continue to function normally
181
+
182
+ ## Testing
183
+
184
+ ### Manual Test Procedure
185
+ 1. Flash bridge_failover example to ESP32/ESP8266
186
+ 2. Start all nodes without pre-configured bridge
187
+ 3. Wait for 60-second grace period
188
+ 4. Observe election and promotion
189
+ 5. Check bridge status output
190
+
191
+ Expected to see:
192
+ - "Known bridges: 1" or more
193
+ - Valid primary bridge with node ID and RSSI
194
+ - No "No primary bridge available!" error
195
+
196
+ ### Unit Tests
197
+ Existing tests in `test/catch/catch_diagnostics_api.cpp` verify:
198
+ - `updateBridgeStatus()` adds bridges correctly
199
+ - `getPrimaryBridge()` returns correct bridge based on RSSI/health
200
+ - Bridge tracking and health monitoring work as expected
201
+
202
+ ## Files Modified
203
+ - `src/arduino/wifi.hpp`
204
+ - `initBridgeStatusBroadcast()` - Added self-registration
205
+ - `sendBridgeStatus()` - Added periodic self-update
206
+
207
+ ## Related Issues
208
+ - Fixes "No primary bridge available" on bridge nodes
209
+ - Related to bridge election feature (v1.8.6)
210
+ - Complements bridge failover implementation
211
+
212
+ ## Credits
213
+ **Reported by**: @woodlist
214
+ **Analysis**: GitHub Copilot
215
+ **Fix**: Self-registration pattern for bridge tracking
216
+
217
+ ---
218
+
219
+ **Version**: To be included in v1.8.7+
220
+ **Date**: November 12, 2025
221
+ **Status**: Implementation complete, pending testing
package/library.json CHANGED
@@ -6,7 +6,7 @@
6
6
  "type": "git",
7
7
  "url": "https://github.com/Alteriom/painlessMesh"
8
8
  },
9
- "version": "1.8.8",
9
+ "version": "1.8.9",
10
10
  "frameworks": [
11
11
  "arduino"
12
12
  ],
@@ -1,5 +1,5 @@
1
1
  name=Alteriom PainlessMesh
2
- version=1.8.8
2
+ version=1.8.9
3
3
  author=Coopdis,Scotty Franzyshen,Edwin van Leeuwen,Germán Martín,Maximilian Schwarz,Doanh Doanh,Alteriom
4
4
  maintainer=Alteriom
5
5
  sentence=A painless way to setup a mesh with ESP8266 and ESP32 devices with Alteriom extensions
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@alteriom/painlessmesh",
3
- "version": "1.8.8",
3
+ "version": "1.8.9",
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.8.7"
32
+ #define ALTERIOM_PAINLESS_MESH_VERSION "1.8.9"
33
33
  #define ALTERIOM_PAINLESS_MESH_VERSION_MAJOR 1
34
34
  #define ALTERIOM_PAINLESS_MESH_VERSION_MINOR 8
35
- #define ALTERIOM_PAINLESS_MESH_VERSION_PATCH 7
35
+ #define ALTERIOM_PAINLESS_MESH_VERSION_PATCH 9
36
36
 
37
37
  /**
38
38
  * @brief Library description and usage information
@@ -752,6 +752,27 @@ class Mesh : public painlessmesh::Mesh<Connection> {
752
752
 
753
753
  Log(STARTUP, "initBridgeStatusBroadcast(): Setting up bridge status broadcast\n");
754
754
 
755
+ // Register ourselves as a bridge in the knownBridges list
756
+ // This ensures the bridge knows about itself and reports correct status
757
+ this->addTask([this]() {
758
+ // Check Internet connectivity: WiFi connected AND valid IP address
759
+ bool hasInternet = (WiFi.status() == WL_CONNECTED) &&
760
+ (WiFi.localIP() != IPAddress(0, 0, 0, 0));
761
+
762
+ this->updateBridgeStatus(
763
+ this->nodeId, // bridgeNodeId
764
+ hasInternet, // internetConnected
765
+ WiFi.RSSI(), // routerRSSI
766
+ WiFi.channel(), // routerChannel
767
+ millis(), // uptime
768
+ WiFi.gatewayIP().toString(),// gatewayIP
769
+ this->getNodeTime() // timestamp
770
+ );
771
+
772
+ Log(STARTUP, "initBridgeStatusBroadcast(): Registered self as bridge (nodeId: %u)\n",
773
+ this->nodeId);
774
+ });
775
+
755
776
  // Create periodic task to broadcast bridge status
756
777
  bridgeStatusTask = this->addTask(
757
778
  this->bridgeStatusIntervalMs,
@@ -791,6 +812,12 @@ class Mesh : public painlessmesh::Mesh<Connection> {
791
812
 
792
813
  Log(STARTUP, "initBridgeCoordination(): Setting up multi-bridge coordination\n");
793
814
 
815
+ // Register our own priority in the bridgePriorities map
816
+ // This ensures getRecommendedBridge() with PRIORITY_BASED strategy works correctly
817
+ bridgePriorities[this->nodeId] = bridgePriority;
818
+ Log(STARTUP, "initBridgeCoordination(): Registered self priority (nodeId: %u, priority: %d)\n",
819
+ this->nodeId, bridgePriority);
820
+
794
821
  // Register handler for incoming coordination messages (Type 613)
795
822
  this->callbackList.onPackage(
796
823
  613, // BRIDGE_COORDINATION type
@@ -881,6 +908,11 @@ class Mesh : public painlessmesh::Mesh<Connection> {
881
908
 
882
909
  String msg;
883
910
  serializeJson(doc, msg);
911
+
912
+ // Update our own priority in bridgePriorities map
913
+ // This ensures priority-based selection always has current data
914
+ bridgePriorities[this->nodeId] = bridgePriority;
915
+
884
916
  this->sendBroadcast(msg);
885
917
 
886
918
  Log(CONNECTION, "Bridge coordination sent: priority=%d, role=%s, load=%d%%\n",
@@ -1195,10 +1227,15 @@ class Mesh : public painlessmesh::Mesh<Connection> {
1195
1227
  (WiFi.localIP() != IPAddress(0, 0, 0, 0));
1196
1228
  obj["internetConnected"] = hasInternet;
1197
1229
 
1198
- obj["routerRSSI"] = WiFi.RSSI();
1199
- obj["routerChannel"] = WiFi.channel();
1200
- obj["uptime"] = millis();
1201
- obj["gatewayIP"] = WiFi.gatewayIP().toString();
1230
+ int8_t rssi = WiFi.RSSI();
1231
+ uint8_t channel = WiFi.channel();
1232
+ uint32_t uptime = millis();
1233
+ TSTRING gatewayIP = WiFi.gatewayIP().toString();
1234
+
1235
+ obj["routerRSSI"] = rssi;
1236
+ obj["routerChannel"] = channel;
1237
+ obj["uptime"] = uptime;
1238
+ obj["gatewayIP"] = gatewayIP;
1202
1239
  obj["message_type"] = 610;
1203
1240
 
1204
1241
  String msg;
@@ -1209,6 +1246,11 @@ class Mesh : public painlessmesh::Mesh<Connection> {
1209
1246
  Log(GENERAL, "sendBridgeStatus(): WiFi status=%d, localIP=%s, gatewayIP=%s\n",
1210
1247
  WiFi.status(), WiFi.localIP().toString().c_str(), WiFi.gatewayIP().toString().c_str());
1211
1248
 
1249
+ // Update our own bridge status in knownBridges list
1250
+ // This ensures the bridge reports itself correctly when queried
1251
+ this->updateBridgeStatus(this->nodeId, hasInternet, rssi, channel,
1252
+ uptime, gatewayIP, this->getNodeTime());
1253
+
1212
1254
  this->sendBroadcast(msg);
1213
1255
  }
1214
1256
  void eventHandleInit() {
@@ -5,7 +5,7 @@
5
5
  * @file painlessMesh.h
6
6
  * @brief Main header file for Alteriom painlessMesh library
7
7
  *
8
- * @version 1.8.7
8
+ * @version 1.8.9
9
9
  * @date 2025-11-12
10
10
  *
11
11
  * painlessMesh is a user-friendly library for creating mesh networks with