@alteriom/painlessmesh 1.9.4 → 1.9.5

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
@@ -5,6 +5,23 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [1.9.5] - 2025-12-03
9
+
10
+ ### Fixed
11
+
12
+ - **Bridge Status Send Race Condition** (#224) - Fixed sendToInternet failures caused by race condition
13
+ - **Root Cause**: Bridge would attempt to send status messages to nodes that had already disconnected during the 500ms delay after `changedConnectionCallbacks`
14
+ - **Symptom**: Silent failures when the connection times out before the delayed task executes, causing sendToInternet to fail
15
+ - **Solution**: Added connection validation before sending bridge status:
16
+ - Check `findRoute()` and `conn->connected()` before attempting to send
17
+ - Use direct high-priority send via `conn->addMessage(msg, true)` to avoid redundant routing lookup
18
+ - Added debug logging for cancelled sends to aid troubleshooting
19
+ - **Impact**: Improved reliability of sendToInternet by ensuring bridge status is only sent to active connections
20
+
21
+ - **Isolated Bridge Retry Mechanism** (#225) - Fixed isolated bridge retry when mesh network not found
22
+ - Nodes that fail initial bridge setup can now retry automatically via mesh connection monitoring
23
+ - **Impact**: More reliable bridge establishment in challenging network conditions
24
+
8
25
  ## [1.9.4] - 2025-12-03
9
26
 
10
27
  ### Fixed
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.9.4",
9
+ "version": "1.9.5",
10
10
  "frameworks": [
11
11
  "arduino"
12
12
  ],
@@ -1,5 +1,5 @@
1
1
  name=Alteriom PainlessMesh
2
- version=1.9.4
2
+ version=1.9.5
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.4",
3
+ "version": "1.9.5",
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",
@@ -1129,6 +1129,16 @@ class Mesh : public painlessmesh::Mesh<Connection> {
1129
1129
  // Small delay to ensure connection is fully stable, then send directly to the new node
1130
1130
  // This avoids issues with time sync blocking broadcast messages
1131
1131
  this->addTask(500, TASK_ONCE, [this, nodeId]() {
1132
+ // Check if the connection is still valid - the node may have disconnected
1133
+ // during the 500ms delay (e.g., due to timeout or network issues)
1134
+ // This prevents attempting to send messages to dropped connections
1135
+ // findRoute returns nullptr if node is not in the routing table
1136
+ auto conn = router::findRoute<Connection>((*this), nodeId);
1137
+ if (!conn || !conn->connected()) {
1138
+ Log(CONNECTION, "Bridge status send cancelled: Node %u no longer connected\n", nodeId);
1139
+ return;
1140
+ }
1141
+
1132
1142
  // Create bridge status message
1133
1143
  JsonDocument doc;
1134
1144
  JsonObject obj = doc.to<JsonObject>();
@@ -1154,10 +1164,11 @@ class Mesh : public painlessmesh::Mesh<Connection> {
1154
1164
  Log(CONNECTION, "Sending bridge status directly to node %u (Internet: %s)\n",
1155
1165
  nodeId, hasInternet ? "YES" : "NO");
1156
1166
 
1157
- // Send directly to the new node using raw message to preserve type BRIDGE_STATUS
1158
- // Using sendSingle() would wrap it in type 1 (SINGLE) and hide type BRIDGE_STATUS
1159
- protocol::Variant variant(msg);
1160
- router::send<Connection>(variant, (*this));
1167
+ // Send directly to the connection with high priority
1168
+ // This ensures the message is sent immediately rather than queued
1169
+ // The JSON message format is the same as what router::send() produces
1170
+ // (Variant serializes to the same JSON format we built manually)
1171
+ conn->addMessage(msg, true);
1161
1172
  });
1162
1173
  });
1163
1174
 
@@ -239,13 +239,24 @@ void ICACHE_FLASH_ATTR StationScan::connectToAP() {
239
239
  mesh->apInit(mesh->getNodeId());
240
240
  Log(CONNECTION, "connectToAP(): AP restarted on channel %d\n", detectedChannel);
241
241
  }
242
+ // Reset counter only when mesh is found on a new channel
243
+ // This allows isolated bridge retry to continue when mesh is truly absent
244
+ consecutiveEmptyScans = 0;
242
245
  } else if (detectedChannel == 0) {
243
246
  Log(CONNECTION,
244
247
  "connectToAP(): Mesh not found on any channel during re-scan\n");
248
+ // Do NOT reset consecutiveEmptyScans here - mesh is still absent
249
+ // This allows isolated bridge retry mechanism to trigger when
250
+ // the counter exceeds ISOLATED_BRIDGE_RETRY_SCAN_THRESHOLD
251
+ } else {
252
+ // detectedChannel == mesh->_meshChannel
253
+ // Mesh found on same channel we're already on - no channel change needed
254
+ // Reset counter since mesh exists, nodes may appear in subsequent scans
255
+ Log(CONNECTION,
256
+ "connectToAP(): Mesh found on current channel %d, no channel change needed\n",
257
+ detectedChannel);
258
+ consecutiveEmptyScans = 0;
245
259
  }
246
-
247
- // Reset counter after re-scan attempt
248
- consecutiveEmptyScans = 0;
249
260
  }
250
261
 
251
262
  if (WiFi.status() == WL_CONNECTED &&