@alteriom/painlessmesh 1.8.13 → 1.8.15

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.
@@ -47,9 +47,22 @@ void setup() {
47
47
  // 3. Set this node as root
48
48
  // 4. Maintain router connection
49
49
  // 5. Start broadcasting bridge status (Type 610) every 30 seconds
50
- mesh.initAsBridge(MESH_PREFIX, MESH_PASSWORD,
51
- ROUTER_SSID, ROUTER_PASSWORD,
52
- &userScheduler, MESH_PORT);
50
+ bool bridgeSuccess = mesh.initAsBridge(MESH_PREFIX, MESH_PASSWORD,
51
+ ROUTER_SSID, ROUTER_PASSWORD,
52
+ &userScheduler, MESH_PORT);
53
+
54
+ if (!bridgeSuccess) {
55
+ Serial.println("✗ Failed to initialize as bridge!");
56
+ Serial.println("Router unreachable - falling back to regular mesh node");
57
+ Serial.println("The node will join the mesh without bridge functionality");
58
+
59
+ // Fallback: Initialize as regular mesh node
60
+ // This allows the device to still participate in the mesh
61
+ mesh.init(MESH_PREFIX, MESH_PASSWORD, &userScheduler, MESH_PORT);
62
+
63
+ Serial.println("✓ Initialized as regular mesh node");
64
+ Serial.println("Note: To function as a bridge, fix router connectivity and restart");
65
+ }
53
66
 
54
67
  // Optional: Configure bridge status broadcasting
55
68
  // mesh.setBridgeStatusInterval(60000); // Change to 60 seconds
@@ -61,7 +74,7 @@ void setup() {
61
74
  // Set up message callback
62
75
  mesh.onReceive(&receivedCallback);
63
76
 
64
- Serial.println("Bridge node initialized and ready!");
77
+ Serial.println("Bridge node initialized and ready!");
65
78
  Serial.println("Broadcasting bridge status to mesh every 30 seconds");
66
79
  }
67
80
 
@@ -122,9 +122,23 @@ void setup() {
122
122
  Serial.println("This node will start as the primary bridge\n");
123
123
 
124
124
  // Initialize as bridge with automatic channel detection
125
- mesh.initAsBridge(MESH_PREFIX, MESH_PASSWORD,
126
- ROUTER_SSID, ROUTER_PASSWORD,
127
- &userScheduler, MESH_PORT);
125
+ bool bridgeSuccess = mesh.initAsBridge(MESH_PREFIX, MESH_PASSWORD,
126
+ ROUTER_SSID, ROUTER_PASSWORD,
127
+ &userScheduler, MESH_PORT);
128
+
129
+ if (!bridgeSuccess) {
130
+ Serial.println("✗ Failed to initialize as bridge!");
131
+ Serial.println("Router unreachable - falling back to regular node with failover");
132
+
133
+ // Fallback: Initialize as regular node with bridge failover enabled
134
+ // This allows automatic promotion to bridge when router becomes available
135
+ mesh.init(MESH_PREFIX, MESH_PASSWORD, &userScheduler, MESH_PORT);
136
+ mesh.setRouterCredentials(ROUTER_SSID, ROUTER_PASSWORD);
137
+ mesh.enableBridgeFailover(true);
138
+ mesh.setElectionTimeout(5000);
139
+
140
+ Serial.println("✓ Running as regular node - will auto-promote when router available");
141
+ }
128
142
  } else {
129
143
  Serial.println("Mode: REGULAR NODE (Failover Enabled)");
130
144
  Serial.println("This node can become bridge via election\n");
@@ -73,9 +73,21 @@ void setup() {
73
73
  mesh.setBridgeSelectionStrategy(painlessMesh::PRIORITY_BASED);
74
74
 
75
75
  // Initialize as bridge with priority 10 (primary)
76
- mesh.initAsBridge(MESH_PREFIX, MESH_PASSWORD,
77
- ROUTER_SSID, ROUTER_PASSWORD,
78
- &userScheduler, MESH_PORT, 10); // Priority 10 = Primary
76
+ bool bridgeSuccess = mesh.initAsBridge(MESH_PREFIX, MESH_PASSWORD,
77
+ ROUTER_SSID, ROUTER_PASSWORD,
78
+ &userScheduler, MESH_PORT, 10); // Priority 10 = Primary
79
+
80
+ if (!bridgeSuccess) {
81
+ Serial.println("✗ Failed to initialize as primary bridge!");
82
+ Serial.println("Router unreachable - falling back to regular mesh node");
83
+
84
+ // Fallback: Initialize as regular node
85
+ // In a multi-bridge setup, secondary bridge should take over
86
+ mesh.init(MESH_PREFIX, MESH_PASSWORD, &userScheduler, MESH_PORT);
87
+
88
+ Serial.println("✓ Running as regular node - secondary bridge should provide connectivity");
89
+ Serial.println("Note: Fix router connectivity and restart to resume primary bridge role");
90
+ }
79
91
 
80
92
  mesh.onReceive(&receivedCallback);
81
93
  mesh.onNewConnection(&newConnectionCallback);
@@ -87,9 +87,21 @@ void setup() {
87
87
  mesh.setBridgeSelectionStrategy(painlessMesh::PRIORITY_BASED);
88
88
 
89
89
  // Initialize as bridge with priority 5 (secondary)
90
- mesh.initAsBridge(MESH_PREFIX, MESH_PASSWORD,
91
- ROUTER_SSID, ROUTER_PASSWORD,
92
- &userScheduler, MESH_PORT, 5); // Priority 5 = Secondary
90
+ bool bridgeSuccess = mesh.initAsBridge(MESH_PREFIX, MESH_PASSWORD,
91
+ ROUTER_SSID, ROUTER_PASSWORD,
92
+ &userScheduler, MESH_PORT, 5); // Priority 5 = Secondary
93
+
94
+ if (!bridgeSuccess) {
95
+ Serial.println("✗ Failed to initialize as secondary bridge!");
96
+ Serial.println("Router unreachable - falling back to regular mesh node");
97
+
98
+ // Fallback: Initialize as regular node
99
+ // Device will still participate in mesh, relying on other bridges
100
+ mesh.init(MESH_PREFIX, MESH_PASSWORD, &userScheduler, MESH_PORT);
101
+
102
+ Serial.println("✓ Running as regular node - relying on other bridges for connectivity");
103
+ Serial.println("Note: Fix router connectivity and restart to resume bridge role");
104
+ }
93
105
 
94
106
  mesh.onReceive(&receivedCallback);
95
107
  mesh.onNewConnection(&newConnectionCallback);
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.13",
9
+ "version": "1.8.15",
10
10
  "frameworks": [
11
11
  "arduino"
12
12
  ],
@@ -1,5 +1,5 @@
1
1
  name=Alteriom PainlessMesh
2
- version=1.8.13
2
+ version=1.8.15
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.13",
3
+ "version": "1.8.15",
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",
@@ -85,6 +85,9 @@
85
85
  "CHANGELOG.md",
86
86
  "RELEASE_GUIDE.md",
87
87
  "DOCUMENTATION_INDEX.md",
88
- "RELEASE_NOTES_1.8.13.md"
88
+ "RELEASE_NOTES_1.8.13.md",
89
+ "RELEASE_NOTES_1.8.15.md",
90
+ "RELEASE_READINESS_PLAN.md",
91
+ "TESTING_WITH_SIMULATOR.md"
89
92
  ]
90
93
  }
@@ -260,7 +260,7 @@ class Mesh : public painlessmesh::Mesh<Connection> {
260
260
  * @param baseScheduler Task scheduler for mesh operations
261
261
  * @param port TCP port for mesh communication (default: 5555)
262
262
  */
263
- void initAsBridge(TSTRING meshSSID, TSTRING meshPassword,
263
+ bool initAsBridge(TSTRING meshSSID, TSTRING meshPassword,
264
264
  TSTRING routerSSID, TSTRING routerPassword,
265
265
  Scheduler *baseScheduler, uint16_t port = 5555) {
266
266
  using namespace logger;
@@ -298,14 +298,17 @@ class Mesh : public painlessmesh::Mesh<Connection> {
298
298
  detectedChannel = WiFi.channel();
299
299
  // Validate channel is in valid range (1-13 for 2.4GHz)
300
300
  if (detectedChannel < 1 || detectedChannel > 13) {
301
- Log(ERROR, "\n✗ Invalid channel detected: %d, using default channel 1\n", detectedChannel);
301
+ Log(ERROR, "\n✗ Invalid channel detected: %d, falling back to channel 1\n", detectedChannel);
302
302
  detectedChannel = 1;
303
303
  } else {
304
304
  Log(STARTUP, "\n✓ Router connected on channel %d\n", detectedChannel);
305
305
  Log(STARTUP, "✓ Router IP: %s\n", WiFi.localIP().toString().c_str());
306
306
  }
307
307
  } else {
308
- Log(ERROR, "\n✗ Failed to connect to router, using default channel 1\n");
308
+ Log(ERROR, "\n✗ Failed to connect to router\n");
309
+ Log(ERROR, "Cannot become bridge without router connection\n");
310
+ Log(ERROR, "Bridge initialization aborted - remaining as regular node\n");
311
+ return false;
309
312
  }
310
313
 
311
314
  Log(STARTUP, "Step 2: Initializing mesh on channel %d...\n", detectedChannel);
@@ -331,6 +334,7 @@ class Mesh : public painlessmesh::Mesh<Connection> {
331
334
  Log(STARTUP, " Mesh Channel: %d (matches router)\n", detectedChannel);
332
335
  Log(STARTUP, " Router: %s\n", routerSSID.c_str());
333
336
  Log(STARTUP, " Port: %d\n", port);
337
+ return true;
334
338
  }
335
339
 
336
340
  /**
@@ -347,7 +351,7 @@ class Mesh : public painlessmesh::Mesh<Connection> {
347
351
  * @param port TCP port for mesh communication (default: 5555)
348
352
  * @param priority Bridge priority: 10=highest (primary), 5=medium (secondary), 1=lowest (default: 5)
349
353
  */
350
- void initAsBridge(TSTRING meshSSID, TSTRING meshPassword,
354
+ bool initAsBridge(TSTRING meshSSID, TSTRING meshPassword,
351
355
  TSTRING routerSSID, TSTRING routerPassword,
352
356
  Scheduler *baseScheduler, uint16_t port, uint8_t priority) {
353
357
  using namespace logger;
@@ -370,12 +374,14 @@ class Mesh : public painlessmesh::Mesh<Connection> {
370
374
  priority, bridgeRole.c_str());
371
375
 
372
376
  // Call the base initAsBridge method
373
- initAsBridge(meshSSID, meshPassword, routerSSID, routerPassword, baseScheduler, port);
377
+ bool success = initAsBridge(meshSSID, meshPassword, routerSSID, routerPassword, baseScheduler, port);
374
378
 
375
- // Setup multi-bridge coordination if enabled
376
- if (multiBridgeEnabled) {
379
+ // Setup multi-bridge coordination if enabled and bridge init succeeded
380
+ if (success && multiBridgeEnabled) {
377
381
  initBridgeCoordination();
378
382
  }
383
+
384
+ return success;
379
385
  }
380
386
 
381
387
  /**
@@ -618,6 +624,28 @@ class Mesh : public painlessmesh::Mesh<Connection> {
618
624
  return activeBridges;
619
625
  }
620
626
 
627
+ /**
628
+ * Check if any bridge in the mesh has Internet connectivity
629
+ *
630
+ * Override of base class method to also check if THIS node is a bridge
631
+ * with Internet connectivity, not just other bridges in the mesh.
632
+ *
633
+ * @return true if at least one bridge (including this node) has Internet
634
+ */
635
+ bool hasInternetConnection() {
636
+ // First check if THIS node is a bridge with Internet
637
+ if (this->isBridge()) {
638
+ // Check Internet connectivity: WiFi connected AND valid IP address
639
+ bool hasInternet = (WiFi.status() == WL_CONNECTED) && (WiFi.localIP() != IPAddress(0, 0, 0, 0));
640
+ if (hasInternet) {
641
+ return true;
642
+ }
643
+ }
644
+
645
+ // Then check other bridges in the mesh (call parent implementation)
646
+ return painlessmesh::Mesh<Connection>::hasInternetConnection();
647
+ }
648
+
621
649
  /**
622
650
  * Get recommended bridge for message transmission
623
651
  *
@@ -1248,12 +1276,35 @@ class Mesh : public painlessmesh::Mesh<Connection> {
1248
1276
  delay(1000);
1249
1277
  Log(STARTUP, "✓ Takeover announcement sent on channel %d\n", _meshChannel);
1250
1278
 
1279
+ // Save current mesh configuration to restore if bridge init fails
1280
+ uint8_t savedChannel = _meshChannel;
1281
+
1251
1282
  // Now reconfigure as bridge (this will switch to router's channel)
1252
1283
  this->stop();
1253
1284
  delay(1000);
1254
1285
 
1255
- this->initAsBridge(_meshSSID, _meshPassword, routerSSID, routerPassword,
1256
- mScheduler, _meshPort);
1286
+ bool bridgeInitSuccess = this->initAsBridge(_meshSSID, _meshPassword, routerSSID, routerPassword,
1287
+ mScheduler, _meshPort);
1288
+
1289
+ if (!bridgeInitSuccess) {
1290
+ Log(ERROR, "✗ Bridge promotion failed - router unreachable\n");
1291
+ Log(ERROR, "Reverting to regular node on channel %d\n", savedChannel);
1292
+
1293
+ // Re-initialize as regular node on the original channel
1294
+ this->init(_meshSSID, _meshPassword, mScheduler, _meshPort, WIFI_AP_STA,
1295
+ savedChannel, _meshHidden, MAX_CONN);
1296
+
1297
+ // Reset election state and clear candidates (consistent with normal election completion)
1298
+ electionState = ELECTION_IDLE;
1299
+ electionCandidates.clear();
1300
+
1301
+ // Notify via callback
1302
+ if (bridgeRoleChangedCallback) {
1303
+ bridgeRoleChangedCallback(false, "Bridge promotion failed - router unreachable");
1304
+ }
1305
+
1306
+ return;
1307
+ }
1257
1308
 
1258
1309
  lastRoleChangeTime = millis();
1259
1310