@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.
- package/CHANGELOG.md +43 -0
- package/README.md +2 -0
- package/RELEASE_NOTES_1.8.15.md +160 -0
- package/RELEASE_READINESS_PLAN.md +323 -0
- package/TESTING_WITH_SIMULATOR.md +259 -0
- package/docs/BRIDGE_INITIALIZATION_FALLBACK.md +357 -0
- package/docs/SIMULATOR_TESTING.md +408 -0
- package/docs/troubleshooting/common-architecture-mistakes.md +438 -0
- package/docs/troubleshooting/common-issues.md +28 -0
- package/docs/troubleshooting/faq.md +113 -12
- package/docs/troubleshooting/internet-access-faq.md +299 -0
- package/examples/basic/test/simulator/CMakeLists.txt +40 -0
- package/examples/basic/test/simulator/README.md +149 -0
- package/examples/basic/test/simulator/firmware/basic_firmware.hpp +117 -0
- package/examples/basic/test/simulator/scenarios/basic_mesh_test.yaml +81 -0
- package/examples/bridge/bridge.ino +17 -4
- package/examples/bridge_failover/bridge_failover.ino +17 -3
- package/examples/multi_bridge/primary_bridge.ino +15 -3
- package/examples/multi_bridge/secondary_bridge.ino +15 -3
- package/library.json +1 -1
- package/library.properties +1 -1
- package/package.json +5 -2
- package/src/arduino/wifi.hpp +60 -9
|
@@ -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
|
-
|
|
52
|
-
|
|
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
|
-
|
|
127
|
-
|
|
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
|
-
|
|
78
|
-
|
|
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
|
-
|
|
92
|
-
|
|
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
package/library.properties
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
name=Alteriom PainlessMesh
|
|
2
|
-
version=1.8.
|
|
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.
|
|
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
|
}
|
package/src/arduino/wifi.hpp
CHANGED
|
@@ -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
|
-
|
|
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,
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|