@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.
@@ -0,0 +1,438 @@
1
+ # Common Architecture Mistakes
2
+
3
+ This document explains common misunderstandings about painlessMesh architecture and how to fix them.
4
+
5
+ ## Mistake #1: Expecting Regular Nodes to Have Internet Access
6
+
7
+ ### The Problem
8
+
9
+ **What users expect:**
10
+ ```cpp
11
+ // Regular mesh node trying to make HTTP requests
12
+ void sendSensorData() {
13
+ HTTPClient http;
14
+ http.begin("http://api.example.com/sensor");
15
+ http.POST("{\"temp\":25.5}"); // ❌ This fails!
16
+ http.end();
17
+ }
18
+ ```
19
+
20
+ **Error message:**
21
+ ```
22
+ [HTTPS] GET... failed, error: connection refused
23
+ ```
24
+
25
+ ### Why This Fails
26
+
27
+ painlessMesh creates a **mesh network**, not a mesh-routed internet gateway. Only the bridge node connects to your WiFi router.
28
+
29
+ **Architecture:**
30
+ ```text
31
+ Internet
32
+ |
33
+ Router (WiFi - Channel 6)
34
+ |
35
+ Bridge Node (WIFI_AP_STA mode)
36
+ | ← Only this node has internet!
37
+ |
38
+ Mesh Network (Channel 6)
39
+ / | \
40
+ Node1 Node2 Node3 ← These nodes do NOT have internet!
41
+ ```
42
+
43
+ **Technical explanation:**
44
+
45
+ 1. **Bridge node** (`WIFI_AP_STA` mode):
46
+ - Acts as Access Point (AP) for mesh
47
+ - Acts as Station (STA) connected to router
48
+ - Has internet access via router connection
49
+ - Can make HTTP/HTTPS requests
50
+
51
+ 2. **Regular nodes** (`WIFI_AP` mode):
52
+ - Only act as Access Points for mesh
53
+ - No router connection
54
+ - No internet access
55
+ - Cannot make HTTP/HTTPS requests directly
56
+
57
+ ESP8266/ESP32 WiFi hardware can only operate on one channel at a time. Regular nodes use their WiFi radio to create the mesh AP - they cannot simultaneously connect to your router.
58
+
59
+ ### The Solution
60
+
61
+ **Pattern 1: Forward through bridge (Recommended)**
62
+
63
+ ```cpp
64
+ // ==== BRIDGE NODE ====
65
+ #include "painlessMesh.h"
66
+ #include "HTTPClient.h"
67
+
68
+ painlessMesh mesh;
69
+
70
+ void setup() {
71
+ // Initialize as bridge with internet access
72
+ mesh.initAsBridge(MESH_PREFIX, MESH_PASSWORD,
73
+ ROUTER_SSID, ROUTER_PASSWORD,
74
+ &userScheduler, 5555);
75
+
76
+ mesh.onReceive(&receivedCallback);
77
+ }
78
+
79
+ void receivedCallback(uint32_t from, String& msg) {
80
+ // Parse message from mesh nodes
81
+ DynamicJsonDocument doc(1024);
82
+ deserializeJson(doc, msg);
83
+
84
+ // Forward to internet service
85
+ if (WiFi.status() == WL_CONNECTED) {
86
+ HTTPClient http;
87
+ http.begin("http://api.example.com/sensor");
88
+ http.addHeader("Content-Type", "application/json");
89
+ int httpCode = http.POST(msg);
90
+
91
+ if (httpCode > 0) {
92
+ Serial.printf("Data forwarded to cloud: %d\n", httpCode);
93
+ } else {
94
+ Serial.printf("HTTP POST failed: %s\n", http.errorToString(httpCode).c_str());
95
+ }
96
+
97
+ http.end();
98
+ } else {
99
+ Serial.println("No internet connection - data not forwarded");
100
+ }
101
+ }
102
+
103
+ // ==== REGULAR SENSOR NODE ====
104
+ #include "painlessMesh.h"
105
+
106
+ painlessMesh mesh;
107
+ uint32_t bridgeNodeId = 0;
108
+
109
+ void setup() {
110
+ // Regular node - no router connection
111
+ mesh.init(MESH_PREFIX, MESH_PASSWORD, &userScheduler, 5555);
112
+ mesh.onReceive(&receivedCallback);
113
+ mesh.onNewConnection(&newConnectionCallback);
114
+ }
115
+
116
+ void loop() {
117
+ mesh.update();
118
+
119
+ // Read sensor
120
+ float temperature = readTemperatureSensor();
121
+
122
+ // Send to bridge (NOT directly to internet!)
123
+ if (bridgeNodeId != 0) {
124
+ String msg = "{\"sensor\":\"temp\",\"value\":" + String(temperature) + "}";
125
+ mesh.sendSingle(bridgeNodeId, msg);
126
+ Serial.println("Data sent to bridge for forwarding");
127
+ }
128
+ }
129
+
130
+ void newConnectionCallback(uint32_t nodeId) {
131
+ // You could implement bridge discovery here
132
+ // For now, configure bridge ID manually or via broadcast discovery
133
+ }
134
+ ```
135
+
136
+ **Pattern 2: Bridge failover (High availability)**
137
+
138
+ ```cpp
139
+ // All nodes configured with router credentials
140
+ mesh.setRouterCredentials(ROUTER_SSID, ROUTER_PASSWORD);
141
+ mesh.enableBridgeFailover(true);
142
+
143
+ mesh.onBridgeRoleChanged(&bridgeRoleCallback);
144
+
145
+ void bridgeRoleCallback(bool isBridge, String reason) {
146
+ if (isBridge) {
147
+ Serial.printf("I am now bridge: %s\n", reason.c_str());
148
+ // This node now has internet access
149
+ startForwardingToCloud();
150
+ } else {
151
+ Serial.println("I am a regular node - no internet access");
152
+ // This node must forward through bridge
153
+ }
154
+ }
155
+ ```
156
+
157
+ See [BRIDGE_FAILOVER.md](../BRIDGE_FAILOVER.md) for details.
158
+
159
+ ## Mistake #2: Thinking All Nodes Should Be Bridges
160
+
161
+ ### The Problem
162
+
163
+ Some users try to make every node a bridge:
164
+
165
+ ```cpp
166
+ // ❌ Bad idea: Making all nodes bridges
167
+ void setup() {
168
+ // Every node connects to router
169
+ mesh.initAsBridge(MESH_PREFIX, MESH_PASSWORD,
170
+ ROUTER_SSID, ROUTER_PASSWORD,
171
+ &userScheduler, 5555);
172
+ }
173
+ ```
174
+
175
+ ### Why This Is Problematic
176
+
177
+ 1. **Memory overhead**: Each router connection uses 5-10KB RAM
178
+ 2. **Performance**: All nodes compete for router bandwidth
179
+ 3. **Complexity**: Loses benefits of mesh architecture
180
+ 4. **Channel conflicts**: All nodes must match router channel
181
+ 5. **Connection limit**: Router has max client limit
182
+
183
+ ### The Solution
184
+
185
+ **Use the bridge-forwarding pattern:**
186
+
187
+ - **1 bridge node**: Connects to router and mesh
188
+ - **N regular nodes**: Connect to mesh only, forward data to bridge
189
+ - **Bridge forwards**: Takes messages and forwards to internet
190
+
191
+ This is the intended architecture and scales much better.
192
+
193
+ ## Mistake #3: Not Identifying the Bridge Node
194
+
195
+ ### The Problem
196
+
197
+ Regular nodes send data but don't know which node is the bridge:
198
+
199
+ ```cpp
200
+ // ❌ How do I know which node is the bridge?
201
+ void sendData() {
202
+ String msg = "{\"data\":\"value\"}";
203
+ mesh.sendBroadcast(msg); // Wasteful - sends to everyone!
204
+ }
205
+ ```
206
+
207
+ ### The Solution
208
+
209
+ **Option 1: Bridge discovery via status broadcasts**
210
+
211
+ ```cpp
212
+ // Bridge node broadcasts status
213
+ #include "examples/alteriom/alteriom_sensor_package.hpp"
214
+ using namespace alteriom;
215
+
216
+ // Bridge node
217
+ Task taskBridgeStatus(30000, TASK_FOREVER, []() {
218
+ if (mesh.isRoot()) {
219
+ BridgeStatusPackage status;
220
+ status.from = mesh.getNodeId();
221
+ status.internetConnected = (WiFi.status() == WL_CONNECTED);
222
+ status.routerRSSI = WiFi.RSSI();
223
+
224
+ mesh.sendBroadcast(status.toJsonString());
225
+ }
226
+ });
227
+
228
+ // Regular node
229
+ uint32_t bridgeNodeId = 0;
230
+
231
+ void receivedCallback(uint32_t from, String& msg) {
232
+ DynamicJsonDocument doc(1024);
233
+ deserializeJson(doc, msg);
234
+
235
+ if (doc["type"] == 610) { // BridgeStatusPackage
236
+ bridgeNodeId = from;
237
+ Serial.printf("Bridge node discovered: %u\n", bridgeNodeId);
238
+ }
239
+ }
240
+
241
+ void sendDataToBridge() {
242
+ if (bridgeNodeId != 0) {
243
+ mesh.sendSingle(bridgeNodeId, myData);
244
+ } else {
245
+ Serial.println("Bridge not yet discovered");
246
+ }
247
+ }
248
+ ```
249
+
250
+ **Option 2: Hardcode bridge ID (simple approach)**
251
+
252
+ ```cpp
253
+ // Configure bridge ID on all nodes
254
+ #define BRIDGE_NODE_ID 1234567890
255
+
256
+ void sendDataToBridge() {
257
+ mesh.sendSingle(BRIDGE_NODE_ID, myData);
258
+ }
259
+ ```
260
+
261
+ **Option 3: Use root node as bridge**
262
+
263
+ ```cpp
264
+ void setup() {
265
+ mesh.setRoot(true); // Bridge should be root
266
+ mesh.setContainsRoot(true); // Tell all nodes
267
+ }
268
+
269
+ void sendDataToBridge() {
270
+ // Send to root node (which is the bridge)
271
+ auto nodeList = mesh.getNodeList();
272
+ for (auto nodeId : nodeList) {
273
+ // Check if this node is root
274
+ // (painlessMesh automatically routes toward root)
275
+ }
276
+ }
277
+ ```
278
+
279
+ ## Mistake #4: Using Wrong WiFi Mode
280
+
281
+ ### The Problem
282
+
283
+ ```cpp
284
+ // ❌ Regular node trying to use AP+STA mode
285
+ void setup() {
286
+ mesh.init(MESH_PREFIX, MESH_PASSWORD, &userScheduler, 5555,
287
+ WIFI_AP_STA); // This is for bridges!
288
+
289
+ // Then trying to connect to router
290
+ WiFi.begin(ROUTER_SSID, ROUTER_PASSWORD); // Conflicts with mesh!
291
+ }
292
+ ```
293
+
294
+ ### The Solution
295
+
296
+ **Bridge nodes:**
297
+ ```cpp
298
+ // Use initAsBridge() which handles AP+STA mode correctly
299
+ mesh.initAsBridge(MESH_PREFIX, MESH_PASSWORD,
300
+ ROUTER_SSID, ROUTER_PASSWORD,
301
+ &userScheduler, 5555);
302
+ ```
303
+
304
+ **Regular nodes:**
305
+ ```cpp
306
+ // Use default WIFI_AP mode (or let init() choose)
307
+ mesh.init(MESH_PREFIX, MESH_PASSWORD, &userScheduler, 5555);
308
+ // No router connection needed
309
+ ```
310
+
311
+ ## Mistake #5: Blocking Code on Regular Nodes Waiting for Internet
312
+
313
+ ### The Problem
314
+
315
+ ```cpp
316
+ // ❌ Regular node waiting forever for internet that never comes
317
+ void sendToCloud() {
318
+ HTTPClient http;
319
+ http.begin("http://api.example.com");
320
+ http.setTimeout(30000); // Waits 30 seconds
321
+ http.POST(data); // Will always timeout!
322
+ }
323
+ ```
324
+
325
+ ### The Solution
326
+
327
+ **Check your node role:**
328
+
329
+ ```cpp
330
+ bool isBridgeNode = false;
331
+
332
+ void setup() {
333
+ if (shouldBeBridge()) {
334
+ mesh.initAsBridge(...);
335
+ isBridgeNode = true;
336
+ } else {
337
+ mesh.init(...);
338
+ isBridgeNode = false;
339
+ }
340
+ }
341
+
342
+ void sendData() {
343
+ if (isBridgeNode && WiFi.status() == WL_CONNECTED) {
344
+ // Bridge can send directly to internet
345
+ http.POST("http://api.example.com", data);
346
+ } else {
347
+ // Regular node forwards to bridge
348
+ mesh.sendSingle(bridgeNodeId, data);
349
+ }
350
+ }
351
+ ```
352
+
353
+ ## Real-World Example: WhatsApp Notifications
354
+
355
+ This is a real issue reported by a user trying to use the Callmebot-ESP32 library with painlessMesh.
356
+
357
+ ### Original (Incorrect) Approach
358
+
359
+ ```cpp
360
+ // ❌ Regular mesh node trying to send WhatsApp messages
361
+ #include "Callmebot_ESP32.h"
362
+
363
+ Callmebot_ESP32 whatsapp;
364
+
365
+ void sendAlarm() {
366
+ // This fails on regular nodes: "connection refused"
367
+ whatsapp.sendMessage("Sensor alarm!"); // ❌ No internet!
368
+ }
369
+ ```
370
+
371
+ ### Corrected Approach
372
+
373
+ ```cpp
374
+ // ==== BRIDGE NODE ====
375
+ #include "painlessMesh.h"
376
+ #include "Callmebot_ESP32.h"
377
+
378
+ Callmebot_ESP32 whatsapp;
379
+
380
+ void setup() {
381
+ // Bridge has internet access
382
+ mesh.initAsBridge(MESH_PREFIX, MESH_PASSWORD,
383
+ ROUTER_SSID, ROUTER_PASSWORD,
384
+ &userScheduler, 5555);
385
+
386
+ whatsapp.begin(PHONE_NUMBER, API_KEY);
387
+ mesh.onReceive(&receivedCallback);
388
+ }
389
+
390
+ void receivedCallback(uint32_t from, String& msg) {
391
+ DynamicJsonDocument doc(1024);
392
+ deserializeJson(doc, msg);
393
+
394
+ // Check for alarm messages from mesh nodes
395
+ if (doc["alarm"] == true) {
396
+ String alertMsg = "ALARM from sensor " + String(from);
397
+ alertMsg += ": " + doc["message"].as<String>();
398
+
399
+ // Bridge can send WhatsApp messages
400
+ if (WiFi.status() == WL_CONNECTED) {
401
+ whatsapp.sendMessage(alertMsg);
402
+ Serial.println("WhatsApp alert sent!");
403
+ }
404
+ }
405
+ }
406
+
407
+ // ==== SENSOR NODE ====
408
+ void checkSensorAndAlert() {
409
+ float oxygenLevel = readO2Sensor();
410
+
411
+ if (oxygenLevel < CRITICAL_THRESHOLD) {
412
+ // Send alarm to bridge (not directly to WhatsApp!)
413
+ String alarm = "{\"alarm\":true,\"sensor\":\"O2\",";
414
+ alarm += "\"value\":" + String(oxygenLevel) + ",";
415
+ alarm += "\"message\":\"Critical O2 level!\"}";
416
+
417
+ mesh.sendSingle(bridgeNodeId, alarm);
418
+ Serial.println("Alarm sent to bridge for WhatsApp notification");
419
+ }
420
+ }
421
+ ```
422
+
423
+ ## Summary: Architecture Best Practices
424
+
425
+ 1. ✅ **One bridge node**: Connects to router and mesh, has internet access
426
+ 2. ✅ **Regular nodes**: Connect to mesh only, NO internet access
427
+ 3. ✅ **Forward pattern**: Regular nodes send data to bridge, bridge forwards to internet
428
+ 4. ✅ **Bridge discovery**: Implement mechanism for nodes to find the bridge
429
+ 5. ✅ **Error handling**: Bridge checks internet connectivity before forwarding
430
+ 6. ✅ **Failover option**: Use bridge failover for high availability (v1.8.0+)
431
+
432
+ ## Additional Resources
433
+
434
+ - [BRIDGE_TO_INTERNET.md](../../BRIDGE_TO_INTERNET.md) - Complete bridge setup guide
435
+ - [BRIDGE_FAILOVER.md](../BRIDGE_FAILOVER.md) - Automatic failover for reliability
436
+ - [examples/mqttBridge/](../../examples/mqttBridge/) - Working example of bridge pattern
437
+ - [Mesh Architecture](../architecture/mesh-architecture.md) - Understanding mesh design
438
+ - [FAQ](faq.md) - Common questions and answers
@@ -2,6 +2,34 @@
2
2
 
3
3
  This guide covers the most frequently encountered problems when working with painlessMesh and their solutions.
4
4
 
5
+ ## Architecture & Design Issues
6
+
7
+ ### Regular Nodes Cannot Access Internet / HTTP Requests Fail
8
+
9
+ **Symptoms:**
10
+ - HTTP/HTTPS requests fail with "connection refused"
11
+ - `WiFi.status()` shows disconnected on regular mesh nodes
12
+ - Internet services (APIs, WhatsApp bot, etc.) only work on bridge node
13
+
14
+ **Cause:**
15
+
16
+ This is **expected behavior**, not a bug. Only bridge nodes have internet access.
17
+
18
+ **Solution:**
19
+
20
+ See the dedicated guide for this common architecture mistake:
21
+
22
+ 📖 **[Common Architecture Mistakes](common-architecture-mistakes.md)**
23
+
24
+ **Quick Summary:**
25
+
26
+ painlessMesh uses a bridge-forwarding pattern:
27
+ - **Bridge node**: Has internet access, forwards data to/from internet
28
+ - **Regular nodes**: No internet access, send data to bridge
29
+ - **Architecture**: Regular nodes → Bridge → Internet
30
+
31
+ Regular nodes must send data to the bridge, which then forwards to internet services.
32
+
5
33
  ## Platform-Specific Issues
6
34
 
7
35
  ### ESP32-C6 Crashes on Startup
@@ -293,30 +293,131 @@ String createSecureMessage(String data) {
293
293
 
294
294
  ### Q: Can I connect the mesh to the internet?
295
295
 
296
- **A:** Yes, using bridge nodes:
296
+ **A:** Yes, using bridge nodes. **Important:** Only the bridge node has internet access - regular mesh nodes do NOT have internet access.
297
+
298
+ **Architecture:**
299
+ ```text
300
+ Internet
301
+ |
302
+ Router (WiFi)
303
+ |
304
+ Bridge Node (AP+STA mode) ← Only this node has internet access
305
+ |
306
+ Mesh Network
307
+ / | \
308
+ Node1 Node2 Node3... ← These nodes do NOT have internet access
309
+ ```
297
310
 
311
+ **Bridge Node Setup:**
298
312
  ```cpp
299
313
  // Bridge node connects to both mesh and internet
300
314
  void setup() {
301
- // Connect to mesh
315
+ // Modern approach: Auto-detect channel and connect
316
+ mesh.initAsBridge(MESH_PREFIX, MESH_PASSWORD,
317
+ ROUTER_SSID, ROUTER_PASSWORD,
318
+ &userScheduler, MESH_PORT);
319
+
320
+ mesh.onReceive(&receivedCallback);
321
+ }
322
+
323
+ // Forward mesh data to internet services
324
+ void receivedCallback(uint32_t from, String& msg) {
325
+ // Bridge forwards messages to HTTP server, MQTT broker, etc.
326
+ if (WiFi.status() == WL_CONNECTED) {
327
+ httpClient.POST("http://myserver.com/api/data", msg);
328
+ }
329
+ }
330
+ ```
331
+
332
+ **Regular Nodes:**
333
+ ```cpp
334
+ // Regular nodes send data TO the bridge (no direct internet access)
335
+ void setup() {
302
336
  mesh.init(MESH_PREFIX, MESH_PASSWORD, &userScheduler, MESH_PORT);
337
+ mesh.onReceive(&receivedCallback);
338
+ }
339
+
340
+ void sendDataToInternet() {
341
+ // Regular nodes send to bridge, which forwards to internet
342
+ String data = "{\"sensor\":\"temp\",\"value\":25.5}";
343
+ mesh.sendSingle(bridgeNodeId, data);
344
+ // Bridge will forward this to internet services
345
+ }
346
+ ```
347
+
348
+ See [BRIDGE_TO_INTERNET.md](../../BRIDGE_TO_INTERNET.md) for complete documentation.
349
+
350
+ ### Q: Why can't my regular mesh nodes access the Internet / make HTTP requests?
351
+
352
+ **A:** This is expected behavior. **Only the bridge node has internet access** - regular mesh nodes only communicate with other mesh nodes.
353
+
354
+ **Why this happens:**
355
+
356
+ ESP8266/ESP32 WiFi hardware can only operate on one channel at a time. In a mesh network:
357
+
358
+ - **Bridge node** uses `WIFI_AP_STA` mode:
359
+ - Access Point (AP) for mesh on channel X
360
+ - Station (STA) connected to router on channel X
361
+ - Has internet access via router
362
+
363
+ - **Regular nodes** use `WIFI_AP` mode:
364
+ - Access Point (AP) for mesh only
365
+ - No router connection
366
+ - No internet access
367
+
368
+ **Solution - Forward through bridge:**
369
+
370
+ ```cpp
371
+ // ==== BRIDGE NODE ====
372
+ #include "HTTPClient.h"
373
+
374
+ void receivedCallback(uint32_t from, String& msg) {
375
+ // Parse message from mesh nodes
376
+ DynamicJsonDocument doc(1024);
377
+ deserializeJson(doc, msg);
303
378
 
304
- // Also connect to home WiFi
305
- WiFi.begin("HomeWiFi", "password");
306
- while (WiFi.status() != WL_CONNECTED) {
307
- delay(500);
379
+ // Forward to internet service
380
+ if (WiFi.status() == WL_CONNECTED) {
381
+ HTTPClient http;
382
+ http.begin("http://api.example.com/data");
383
+ http.addHeader("Content-Type", "application/json");
384
+ http.POST(msg);
385
+ http.end();
308
386
  }
387
+ }
388
+
389
+ // ==== REGULAR NODE ====
390
+ void sendDataToCloud() {
391
+ // Create message
392
+ String msg = "{\"sensor\":\"temp\",\"value\":25.5}";
393
+
394
+ // Send to bridge (NOT directly to internet!)
395
+ mesh.sendSingle(bridgeNodeId, msg);
309
396
 
310
- Serial.println("Bridge node: Connected to both mesh and internet");
397
+ // Bridge will forward to internet service
311
398
  }
399
+ ```
312
400
 
313
- // Forward mesh data to internet services
314
- mesh.onReceive([](uint32_t from, String& msg) {
315
- // Forward to HTTP server, MQTT broker, etc.
316
- httpClient.POST("http://myserver.com/api/data", msg);
317
- });
401
+ **Common mistake:**
402
+
403
+ ```cpp
404
+ // ❌ This will NOT work on regular mesh nodes:
405
+ HTTPClient http;
406
+ http.begin("http://api.example.com/data");
407
+ http.POST(data); // ERROR: No internet connection!
408
+
409
+ // ✅ Correct approach - send to bridge:
410
+ mesh.sendSingle(bridgeNodeId, data); // Bridge forwards to internet
318
411
  ```
319
412
 
413
+ **Architecture patterns:**
414
+
415
+ 1. **Single bridge**: One node connects to router, others forward through it
416
+ 2. **Bridge failover**: Multiple nodes have router credentials, automatic failover
417
+ 3. **Multi-bridge**: Multiple simultaneous bridges for load balancing
418
+
419
+ See [BRIDGE_TO_INTERNET.md](../../BRIDGE_TO_INTERNET.md) and [BRIDGE_FAILOVER.md](../BRIDGE_FAILOVER.md).
420
+
320
421
  ### Q: Can I use MQTT with painlessMesh?
321
422
 
322
423
  **A:** Yes, through bridge nodes or by running MQTT alongside the mesh: