@alteriom/painlessmesh 1.8.7 → 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,69 @@ 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
+
56
+ ## [1.8.8] - 2025-11-12
57
+
58
+ ### Fixed
59
+
60
+ - **Bridge Internet Connectivity Detection (Mobile Hotspot Compatibility)** - Fixed false negative with mobile hotspots
61
+ - Changed internet detection from checking gateway IP to checking local IP address
62
+ - Gateway IP may not be immediately available after connection, especially with mobile hotspots
63
+ - Some networks (mobile tethering) may not provide gateway IP via DHCP at all
64
+ - **Before**: Bridge connected to mobile hotspot → gets local IP → reports "Internet: NO" (gateway IP not available)
65
+ - **After**: Bridge connected to mobile hotspot → gets local IP → correctly reports "Internet: YES"
66
+ - Having a valid local IP + WiFi connected status is sufficient to indicate internet access
67
+ - Enhanced logging to show WiFi status, local IP, and gateway IP for better debugging
68
+ - Core fix in `src/arduino/wifi.hpp` line 1189-1191
69
+ - Improves upon 1.8.7 gateway IP check which didn't work with all network types
70
+ - Resolves issue where bridge connects successfully but still reports no internet
71
+ - Fixes Alteriom/painlessMesh#129
72
+
10
73
  ## [1.8.7] - 2025-11-12
11
74
 
12
75
  ### 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
@@ -344,12 +344,18 @@ bool amBridge = mesh.isBridge();
344
344
 
345
345
  **Symptoms**: Bridge shows "Internet available: NO" or `hasInternet: false` despite router having Internet
346
346
 
347
- **Cause**: Fixed in v1.8.5+. Previously only checked WiFi connection status, not actual Internet availability.
347
+ **Cause**: Fixed progressively in v1.8.5+ and v1.8.7+.
348
+
349
+ **Historical Fixes**:
350
+ - **v1.8.5**: Added gateway IP check in addition to WiFi connection status
351
+ - **v1.8.7**: Improved gateway IP checking logic
352
+ - **v1.8.8+**: Changed to check local IP instead of gateway IP for better mobile hotspot compatibility
348
353
 
349
354
  **Solutions**:
350
- - Update to painlessMesh v1.8.5 or later
351
- - Bridge now checks both WiFi connection AND valid gateway IP
352
- - If still seeing issues, verify router's gateway IP is accessible: check `WiFi.gatewayIP()` returns valid IP (not 0.0.0.0)
355
+ - Update to latest painlessMesh version
356
+ - Bridge now checks WiFi connection AND valid local IP address
357
+ - Works reliably with all network types including mobile hotspots and tethering
358
+ - Local IP check is more reliable than gateway IP, which may not be available on all network types
353
359
 
354
360
  ### Election Doesn't Start
355
361
 
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.7",
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.7
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.7",
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",
@@ -1186,15 +1218,24 @@ class Mesh : public painlessmesh::Mesh<Connection> {
1186
1218
  obj["routing"] = 2; // BROADCAST routing
1187
1219
  obj["timestamp"] = this->getNodeTime();
1188
1220
 
1189
- // Check Internet connectivity: WiFi connected AND valid gateway IP
1221
+ // Check Internet connectivity: WiFi connected AND valid IP address
1222
+ // We check for valid local IP instead of gateway IP because:
1223
+ // 1. Gateway IP might not be immediately available after connection
1224
+ // 2. Some networks (mobile hotspots) may not provide gateway IP via DHCP
1225
+ // 3. Having a valid local IP + being connected is sufficient for internet access
1190
1226
  bool hasInternet = (WiFi.status() == WL_CONNECTED) &&
1191
- (WiFi.gatewayIP() != IPAddress(0, 0, 0, 0));
1227
+ (WiFi.localIP() != IPAddress(0, 0, 0, 0));
1192
1228
  obj["internetConnected"] = hasInternet;
1193
1229
 
1194
- obj["routerRSSI"] = WiFi.RSSI();
1195
- obj["routerChannel"] = WiFi.channel();
1196
- obj["uptime"] = millis();
1197
- 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;
1198
1239
  obj["message_type"] = 610;
1199
1240
 
1200
1241
  String msg;
@@ -1202,6 +1243,13 @@ class Mesh : public painlessmesh::Mesh<Connection> {
1202
1243
 
1203
1244
  Log(GENERAL, "sendBridgeStatus(): Broadcasting status (Internet: %s)\n",
1204
1245
  hasInternet ? "Connected" : "Disconnected");
1246
+ Log(GENERAL, "sendBridgeStatus(): WiFi status=%d, localIP=%s, gatewayIP=%s\n",
1247
+ WiFi.status(), WiFi.localIP().toString().c_str(), WiFi.gatewayIP().toString().c_str());
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());
1205
1253
 
1206
1254
  this->sendBroadcast(msg);
1207
1255
  }
@@ -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