@alteriom/painlessmesh 1.8.0 → 1.8.2

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,1025 @@
1
+ # Multi-Bridge Setup Guide
2
+
3
+ > **Complete guide for deploying multiple simultaneous bridge nodes in painlessMesh**
4
+
5
+ ## Table of Contents
6
+
7
+ - [Overview](#overview)
8
+ - [Architecture](#architecture)
9
+ - [Use Cases](#use-cases)
10
+ - [Requirements](#requirements)
11
+ - [Configuration](#configuration)
12
+ - [API Reference](#api-reference)
13
+ - [Examples](#examples)
14
+ - [Testing](#testing)
15
+ - [Troubleshooting](#troubleshooting)
16
+ - [Advanced Topics](#advanced-topics)
17
+
18
+ ## Overview
19
+
20
+ Multi-bridge coordination enables multiple bridge nodes to operate simultaneously in a painlessMesh network, providing:
21
+
22
+ - **High Availability**: Zero downtime during bridge failures
23
+ - **Load Balancing**: Distribute traffic across multiple Internet connections
24
+ - **Geographic Distribution**: Bridges in different physical locations
25
+ - **Redundancy**: Multiple paths to the Internet
26
+ - **Traffic Shaping**: Different message types to different bridges (planned)
27
+
28
+ ### Key Features
29
+
30
+ | Feature | Status | Description |
31
+ |---------|--------|-------------|
32
+ | Multiple Simultaneous Bridges | ✅ Implemented | 2-5 bridges can operate concurrently |
33
+ | Bridge Priority System | ✅ Implemented | Priorities 1-10 for deterministic selection |
34
+ | Selection Strategies | ✅ Implemented | Priority-based, Round-robin, Best-signal |
35
+ | Automatic Coordination | ✅ Implemented | Bridges discover and coordinate automatically |
36
+ | Load Reporting | ✅ Implemented | Bridges report current load percentage |
37
+ | Failover Integration | ✅ Implemented | Works with bridge failover (Issue #64) |
38
+
39
+ ## Architecture
40
+
41
+ ### System Diagram
42
+
43
+ ```
44
+ Internet
45
+ |
46
+ +--------------+---------------+
47
+ | |
48
+ Router A Router B
49
+ | |
50
+ [Bridge 1] [Bridge 2]
51
+ Priority: 10 Priority: 5
52
+ Role: Primary Role: Secondary
53
+ | |
54
+ +-------+----------+-----------+
55
+ | |
56
+ [Node A] [Node B]
57
+ Regular Regular
58
+ Nodes Nodes
59
+ ```
60
+
61
+ ### Component Roles
62
+
63
+ **Bridge Nodes:**
64
+ - Connect to both mesh (AP) and router (STA)
65
+ - Broadcast coordination messages (Type 613) every 30s
66
+ - Report priority, role, load, and peer bridges
67
+ - Act as Internet gateways for mesh
68
+
69
+ **Regular Nodes:**
70
+ - Connect to mesh only (STA mode)
71
+ - Receive bridge coordination messages
72
+ - Track available bridges and their priorities
73
+ - Select best bridge using configured strategy
74
+
75
+ ### Coordination Protocol
76
+
77
+ **Message Type:** 613 (BRIDGE_COORDINATION)
78
+
79
+ **Message Structure:**
80
+ ```json
81
+ {
82
+ "type": 613,
83
+ "from": 123456,
84
+ "routing": 2,
85
+ "priority": 10,
86
+ "role": "primary",
87
+ "load": 45,
88
+ "timestamp": 1234567890,
89
+ "peerBridges": [789012, 345678]
90
+ }
91
+ ```
92
+
93
+ **Broadcast Interval:** 30 seconds
94
+ **Timeout:** 60 seconds (2 missed heartbeats)
95
+
96
+ ## Use Cases
97
+
98
+ ### Use Case 1: High-Availability Production System
99
+
100
+ **Scenario:** Critical IoT system that cannot tolerate Internet outages
101
+
102
+ **Setup:**
103
+ - Primary Bridge (Priority 10): Main Internet connection
104
+ - Secondary Bridge (Priority 5): Backup Internet connection
105
+ - Strategy: PRIORITY_BASED (default)
106
+
107
+ **Behavior:**
108
+ - All traffic uses primary bridge normally
109
+ - If primary fails, automatic switch to secondary
110
+ - Zero downtime, no manual intervention
111
+
112
+ **Example:**
113
+ ```cpp
114
+ // Primary Bridge
115
+ mesh.initAsBridge(MESH_SSID, MESH_PASSWORD,
116
+ "PrimaryRouter", "pass1",
117
+ &scheduler, 5555, 10);
118
+
119
+ // Secondary Bridge
120
+ mesh.initAsBridge(MESH_SSID, MESH_PASSWORD,
121
+ "BackupRouter", "pass2",
122
+ &scheduler, 5555, 5);
123
+ ```
124
+
125
+ ### Use Case 2: Load Balancing
126
+
127
+ **Scenario:** High-traffic mesh network with multiple Internet connections
128
+
129
+ **Setup:**
130
+ - Bridge 1 (Priority 7): Internet connection A
131
+ - Bridge 2 (Priority 7): Internet connection B
132
+ - Strategy: ROUND_ROBIN
133
+
134
+ **Behavior:**
135
+ - Messages distributed evenly across both bridges
136
+ - 50/50 traffic split
137
+ - Maximizes available bandwidth
138
+
139
+ **Example:**
140
+ ```cpp
141
+ // On regular nodes
142
+ mesh.setBridgeSelectionStrategy(painlessMesh::ROUND_ROBIN);
143
+
144
+ // Both bridges equal priority
145
+ mesh.initAsBridge(MESH_SSID, MESH_PASSWORD,
146
+ "RouterA", "pass1",
147
+ &scheduler, 5555, 7);
148
+ ```
149
+
150
+ ### Use Case 3: Geographic Distribution
151
+
152
+ **Scenario:** Large mesh network spanning multiple buildings
153
+
154
+ **Setup:**
155
+ - Building A: Bridge 1 (Priority 10) → Local Internet A
156
+ - Building B: Bridge 2 (Priority 10) → Local Internet B
157
+ - Strategy: BEST_SIGNAL
158
+
159
+ **Behavior:**
160
+ - Nodes use closest bridge (best signal strength)
161
+ - Automatic selection based on location
162
+ - Optimizes latency and reliability
163
+
164
+ **Example:**
165
+ ```cpp
166
+ // On regular nodes
167
+ mesh.setBridgeSelectionStrategy(painlessMesh::BEST_SIGNAL);
168
+
169
+ // Both bridges same priority, selected by signal
170
+ mesh.initAsBridge(MESH_SSID, MESH_PASSWORD,
171
+ "BuildingA_Router", "pass1",
172
+ &scheduler, 5555, 10);
173
+ ```
174
+
175
+ ## Requirements
176
+
177
+ ### Hardware
178
+
179
+ **Bridge Nodes:**
180
+ - ESP32 or ESP8266 with WiFi
181
+ - 2+ MB flash recommended
182
+ - Stable power supply (no battery)
183
+
184
+ **Regular Nodes:**
185
+ - ESP32 or ESP8266 with WiFi
186
+ - 1+ MB flash
187
+ - Battery power acceptable
188
+
189
+ **Network:**
190
+ - 1+ WiFi routers with Internet connection
191
+ - All devices must support 2.4 GHz WiFi
192
+ - Same WiFi channel recommended for all
193
+
194
+ ### Software
195
+
196
+ **Library Version:**
197
+ - painlessMesh v1.8.1 or later
198
+ - ArduinoJson v6.x or v7.x
199
+ - TaskScheduler v3.x
200
+
201
+ **Platform:**
202
+ - Arduino IDE 1.8+ or PlatformIO
203
+ - ESP32 Arduino Core 2.x+ or ESP8266 Core 3.x+
204
+
205
+ **Dependencies:**
206
+ - Bridge Status Broadcast (Issue #63) ✅
207
+ - Bridge Failover (Issue #64) ✅
208
+
209
+ ## Configuration
210
+
211
+ ### Step 1: Enable Multi-Bridge Mode
212
+
213
+ Multi-bridge mode must be enabled on **all bridge nodes**:
214
+
215
+ ```cpp
216
+ mesh.enableMultiBridge(true);
217
+ ```
218
+
219
+ **Note:** Regular nodes automatically detect multi-bridge mode and don't need explicit configuration.
220
+
221
+ ### Step 2: Set Bridge Priority
222
+
223
+ When initializing a bridge, specify priority (1-10):
224
+
225
+ ```cpp
226
+ mesh.initAsBridge(MESH_SSID, MESH_PASSWORD,
227
+ ROUTER_SSID, ROUTER_PASSWORD,
228
+ &scheduler, MESH_PORT,
229
+ priority); // 1-10
230
+ ```
231
+
232
+ **Priority Guidelines:**
233
+
234
+ | Priority | Role | When to Use |
235
+ |----------|------|-------------|
236
+ | 10 | Primary | Main bridge, best Internet connection |
237
+ | 8-9 | Primary-High | Co-primary for load sharing |
238
+ | 5-7 | Secondary | Backup bridge, hot standby |
239
+ | 2-4 | Tertiary | Last resort backup |
240
+ | 1 | Standby | Only if all others fail |
241
+
242
+ **Role Assignment:**
243
+ - Priority ≥ 8 → "primary"
244
+ - Priority ≥ 5 → "secondary"
245
+ - Priority < 5 → "standby"
246
+
247
+ ### Step 3: Configure Selection Strategy
248
+
249
+ Choose how regular nodes select bridges:
250
+
251
+ ```cpp
252
+ // Priority-Based (default) - Always use highest priority
253
+ mesh.setBridgeSelectionStrategy(painlessMesh::PRIORITY_BASED);
254
+
255
+ // Round-Robin - Distribute load evenly
256
+ mesh.setBridgeSelectionStrategy(painlessMesh::ROUND_ROBIN);
257
+
258
+ // Best Signal - Use bridge with best RSSI
259
+ mesh.setBridgeSelectionStrategy(painlessMesh::BEST_SIGNAL);
260
+ ```
261
+
262
+ **Strategy Comparison:**
263
+
264
+ | Strategy | Pros | Cons | Best For |
265
+ |----------|------|------|----------|
266
+ | PRIORITY_BASED | Predictable, deterministic | All traffic on one bridge | Production systems |
267
+ | ROUND_ROBIN | Even load distribution | May not respect priority | High-traffic systems |
268
+ | BEST_SIGNAL | Optimizes latency | Requires WiFi scanning | Large/mobile networks |
269
+
270
+ ### Step 4: Set Maximum Bridges (Optional)
271
+
272
+ Limit number of tracked bridges:
273
+
274
+ ```cpp
275
+ mesh.setMaxBridges(3); // Default: 2, Max: 5
276
+ ```
277
+
278
+ **Recommendations:**
279
+ - Small networks (< 20 nodes): 2 bridges
280
+ - Medium networks (20-50 nodes): 3 bridges
281
+ - Large networks (50+ nodes): 4-5 bridges
282
+
283
+ ## API Reference
284
+
285
+ ### Configuration Methods
286
+
287
+ #### enableMultiBridge()
288
+
289
+ ```cpp
290
+ void enableMultiBridge(bool enabled)
291
+ ```
292
+
293
+ Enable or disable multi-bridge coordination mode.
294
+
295
+ **Parameters:**
296
+ - `enabled` - true to enable, false to disable
297
+
298
+ **Example:**
299
+ ```cpp
300
+ mesh.enableMultiBridge(true);
301
+ ```
302
+
303
+ **Notes:**
304
+ - Must be called on bridge nodes before `initAsBridge()`
305
+ - Regular nodes auto-detect multi-bridge mode
306
+ - Default: disabled (single-bridge mode)
307
+
308
+ #### setBridgeSelectionStrategy()
309
+
310
+ ```cpp
311
+ void setBridgeSelectionStrategy(BridgeSelectionStrategy strategy)
312
+ ```
313
+
314
+ Set bridge selection algorithm for regular nodes.
315
+
316
+ **Parameters:**
317
+ - `strategy` - One of:
318
+ - `painlessMesh::PRIORITY_BASED` - Use highest priority (default)
319
+ - `painlessMesh::ROUND_ROBIN` - Distribute evenly
320
+ - `painlessMesh::BEST_SIGNAL` - Use best RSSI
321
+
322
+ **Example:**
323
+ ```cpp
324
+ mesh.setBridgeSelectionStrategy(painlessMesh::ROUND_ROBIN);
325
+ ```
326
+
327
+ #### setMaxBridges()
328
+
329
+ ```cpp
330
+ void setMaxBridges(uint8_t maxBridges)
331
+ ```
332
+
333
+ Set maximum number of concurrent bridges to track.
334
+
335
+ **Parameters:**
336
+ - `maxBridges` - Maximum bridges (1-5)
337
+
338
+ **Example:**
339
+ ```cpp
340
+ mesh.setMaxBridges(3);
341
+ ```
342
+
343
+ **Notes:**
344
+ - Values < 1 clamped to 1
345
+ - Values > 5 clamped to 5
346
+ - Default: 2
347
+
348
+ ### Bridge Initialization
349
+
350
+ #### initAsBridge() with Priority
351
+
352
+ ```cpp
353
+ void initAsBridge(TSTRING meshSSID, TSTRING meshPassword,
354
+ TSTRING routerSSID, TSTRING routerPassword,
355
+ Scheduler *baseScheduler, uint16_t port,
356
+ uint8_t priority)
357
+ ```
358
+
359
+ Initialize node as bridge with specified priority.
360
+
361
+ **Parameters:**
362
+ - `meshSSID` - Mesh network name
363
+ - `meshPassword` - Mesh password
364
+ - `routerSSID` - Router SSID to connect to
365
+ - `routerPassword` - Router password
366
+ - `baseScheduler` - Task scheduler instance
367
+ - `port` - TCP port (default: 5555)
368
+ - `priority` - Bridge priority (1-10)
369
+
370
+ **Example:**
371
+ ```cpp
372
+ mesh.initAsBridge("MyMesh", "meshpass",
373
+ "MyRouter", "routerpass",
374
+ &scheduler, 5555, 10);
375
+ ```
376
+
377
+ **Notes:**
378
+ - Priority 10 = highest (primary bridge)
379
+ - Priority 1 = lowest (standby bridge)
380
+ - Auto-detects router channel
381
+ - Sets node as root automatically
382
+
383
+ ### Bridge Discovery Methods
384
+
385
+ #### getActiveBridges()
386
+
387
+ ```cpp
388
+ std::vector<uint32_t> getActiveBridges()
389
+ ```
390
+
391
+ Get list of all currently active bridge node IDs.
392
+
393
+ **Returns:**
394
+ - Vector of bridge node IDs with Internet connection
395
+
396
+ **Example:**
397
+ ```cpp
398
+ auto bridges = mesh.getActiveBridges();
399
+ Serial.printf("Active bridges: %d\n", bridges.size());
400
+ for (auto bridgeId : bridges) {
401
+ Serial.printf(" - Bridge: %u\n", bridgeId);
402
+ }
403
+ ```
404
+
405
+ **Notes:**
406
+ - Only includes bridges with Internet connection
407
+ - Only includes healthy bridges (seen within 60s)
408
+ - Updates automatically from coordination messages
409
+
410
+ #### getRecommendedBridge()
411
+
412
+ ```cpp
413
+ uint32_t getRecommendedBridge()
414
+ ```
415
+
416
+ Get best bridge node ID based on current strategy.
417
+
418
+ **Returns:**
419
+ - Bridge node ID, or 0 if no bridge available
420
+
421
+ **Example:**
422
+ ```cpp
423
+ uint32_t bridgeId = mesh.getRecommendedBridge();
424
+ if (bridgeId != 0) {
425
+ mesh.sendSingle(bridgeId, "Hello!");
426
+ } else {
427
+ Serial.println("No bridge available");
428
+ }
429
+ ```
430
+
431
+ **Notes:**
432
+ - Respects current selection strategy
433
+ - Returns 0 if no bridges available
434
+ - Thread-safe, can call frequently
435
+
436
+ #### selectBridge()
437
+
438
+ ```cpp
439
+ void selectBridge(uint32_t bridgeNodeId)
440
+ ```
441
+
442
+ Manually select specific bridge for next transmission.
443
+
444
+ **Parameters:**
445
+ - `bridgeNodeId` - Node ID of bridge to use
446
+
447
+ **Example:**
448
+ ```cpp
449
+ // Override strategy for critical message
450
+ mesh.selectBridge(primaryBridgeId);
451
+ mesh.sendSingle(primaryBridgeId, criticalData);
452
+ ```
453
+
454
+ **Notes:**
455
+ - One-time override of selection strategy
456
+ - Resets after one message
457
+ - Use sparingly for critical messages only
458
+
459
+ #### isMultiBridgeEnabled()
460
+
461
+ ```cpp
462
+ bool isMultiBridgeEnabled() const
463
+ ```
464
+
465
+ Check if multi-bridge mode is enabled.
466
+
467
+ **Returns:**
468
+ - true if multi-bridge coordination active
469
+
470
+ **Example:**
471
+ ```cpp
472
+ if (mesh.isMultiBridgeEnabled()) {
473
+ Serial.println("Multi-bridge mode active");
474
+ }
475
+ ```
476
+
477
+ ## Examples
478
+
479
+ See complete examples in `examples/multi_bridge/`:
480
+
481
+ - **primary_bridge.ino** - Primary bridge configuration
482
+ - **secondary_bridge.ino** - Secondary bridge configuration
483
+ - **regular_node.ino** - Regular node with bridge awareness
484
+
485
+ ### Complete Primary Bridge Example
486
+
487
+ ```cpp
488
+ #include "painlessMesh.h"
489
+
490
+ #define MESH_PREFIX "ProductionMesh"
491
+ #define MESH_PASSWORD "meshpass"
492
+ #define MESH_PORT 5555
493
+ #define ROUTER_SSID "PrimaryRouter"
494
+ #define ROUTER_PASSWORD "routerpass"
495
+
496
+ Scheduler userScheduler;
497
+ painlessMesh mesh;
498
+
499
+ // Monitor bridge status
500
+ Task taskBridgeMonitor(10000, TASK_FOREVER, [](){
501
+ Serial.println("\n=== Primary Bridge Status ===");
502
+ Serial.printf("Node ID: %u\n", mesh.getNodeId());
503
+ Serial.printf("Connected Nodes: %d\n", mesh.getNodeList().size());
504
+
505
+ auto activeBridges = mesh.getActiveBridges();
506
+ Serial.printf("Active Bridges: %d\n", activeBridges.size());
507
+ for (auto bridgeId : activeBridges) {
508
+ Serial.printf(" - Bridge: %u%s\n", bridgeId,
509
+ (bridgeId == mesh.getNodeId()) ? " (ME)" : "");
510
+ }
511
+
512
+ Serial.printf("Recommended Bridge: %u\n", mesh.getRecommendedBridge());
513
+ Serial.println("============================\n");
514
+ });
515
+
516
+ void setup() {
517
+ Serial.begin(115200);
518
+ delay(2000);
519
+
520
+ Serial.println("\n=== PRIMARY BRIDGE INITIALIZATION ===\n");
521
+
522
+ // Configure debugging
523
+ mesh.setDebugMsgTypes(ERROR | STARTUP | CONNECTION);
524
+
525
+ // Enable multi-bridge coordination
526
+ mesh.enableMultiBridge(true);
527
+
528
+ // Set selection strategy (PRIORITY_BASED is default)
529
+ mesh.setBridgeSelectionStrategy(painlessMesh::PRIORITY_BASED);
530
+
531
+ // Initialize as primary bridge (priority 10)
532
+ mesh.initAsBridge(MESH_PREFIX, MESH_PASSWORD,
533
+ ROUTER_SSID, ROUTER_PASSWORD,
534
+ &userScheduler, MESH_PORT, 10);
535
+
536
+ // Setup callbacks
537
+ mesh.onReceive([](uint32_t from, String& msg) {
538
+ Serial.printf("Received from %u: %s\n", from, msg.c_str());
539
+ });
540
+
541
+ mesh.onNewConnection([](uint32_t nodeId) {
542
+ Serial.printf("New connection: %u\n", nodeId);
543
+ });
544
+
545
+ // Add monitoring task
546
+ userScheduler.addTask(taskBridgeMonitor);
547
+ taskBridgeMonitor.enable();
548
+
549
+ Serial.println("\n=== PRIMARY BRIDGE READY ===");
550
+ Serial.println("Priority: 10 (Primary)");
551
+ Serial.println("This bridge will handle all mesh traffic");
552
+ Serial.println("Secondary bridge will activate if this fails\n");
553
+ }
554
+
555
+ void loop() {
556
+ mesh.update();
557
+ }
558
+ ```
559
+
560
+ ### Complete Regular Node Example
561
+
562
+ ```cpp
563
+ #include "painlessMesh.h"
564
+
565
+ #define MESH_PREFIX "ProductionMesh"
566
+ #define MESH_PASSWORD "meshpass"
567
+ #define MESH_PORT 5555
568
+
569
+ Scheduler userScheduler;
570
+ painlessMesh mesh;
571
+
572
+ // Send test messages periodically
573
+ Task taskSendMessage(5000, TASK_FOREVER, [](){
574
+ uint32_t bridgeId = mesh.getRecommendedBridge();
575
+
576
+ if (bridgeId != 0) {
577
+ String msg = "Hello from node " + String(mesh.getNodeId());
578
+ Serial.printf("Sending to bridge %u: %s\n", bridgeId, msg.c_str());
579
+ mesh.sendSingle(bridgeId, msg);
580
+ } else {
581
+ Serial.println("No bridge available - message queued");
582
+ }
583
+ });
584
+
585
+ // Monitor network status
586
+ Task taskNetworkStatus(15000, TASK_FOREVER, [](){
587
+ Serial.println("\n=== Network Status ===");
588
+ Serial.printf("Node ID: %u\n", mesh.getNodeId());
589
+ Serial.printf("Connected Nodes: %d\n", mesh.getNodeList().size());
590
+
591
+ auto activeBridges = mesh.getActiveBridges();
592
+ Serial.printf("Active Bridges: %d\n", activeBridges.size());
593
+ for (auto bridgeId : activeBridges) {
594
+ Serial.printf(" - Bridge: %u\n", bridgeId);
595
+ }
596
+
597
+ Serial.printf("Internet Available: %s\n",
598
+ mesh.hasInternetConnection() ? "YES" : "NO");
599
+ Serial.printf("Recommended Bridge: %u\n",
600
+ mesh.getRecommendedBridge());
601
+ Serial.println("=====================\n");
602
+ });
603
+
604
+ void setup() {
605
+ Serial.begin(115200);
606
+ delay(2000);
607
+
608
+ Serial.println("\n=== REGULAR NODE INITIALIZATION ===\n");
609
+
610
+ // Configure debugging
611
+ mesh.setDebugMsgTypes(ERROR | STARTUP | CONNECTION);
612
+
613
+ // Initialize as regular node
614
+ mesh.init(MESH_PREFIX, MESH_PASSWORD, &userScheduler, MESH_PORT);
615
+
616
+ // Setup callbacks
617
+ mesh.onReceive([](uint32_t from, String& msg) {
618
+ Serial.printf("Received from %u: %s\n", from, msg.c_str());
619
+ });
620
+
621
+ mesh.onBridgeStatusChanged([](uint32_t bridgeId, bool hasInternet) {
622
+ Serial.printf("Bridge %u: Internet %s\n",
623
+ bridgeId, hasInternet ? "ONLINE" : "OFFLINE");
624
+ });
625
+
626
+ mesh.onNewConnection([](uint32_t nodeId) {
627
+ Serial.printf("New connection: %u\n", nodeId);
628
+ });
629
+
630
+ // Add tasks
631
+ userScheduler.addTask(taskSendMessage);
632
+ userScheduler.addTask(taskNetworkStatus);
633
+ taskSendMessage.enable();
634
+ taskNetworkStatus.enable();
635
+
636
+ Serial.println("\n=== REGULAR NODE READY ===");
637
+ Serial.println("Automatically discovers bridges");
638
+ Serial.println("Uses configured selection strategy\n");
639
+ }
640
+
641
+ void loop() {
642
+ mesh.update();
643
+ }
644
+ ```
645
+
646
+ ## Testing
647
+
648
+ ### Test Scenarios
649
+
650
+ #### Scenario 1: Dual Bridge Normal Operation
651
+
652
+ **Objective:** Verify two bridges coordinate and nodes prefer primary
653
+
654
+ **Steps:**
655
+ 1. Flash primary bridge (priority 10)
656
+ 2. Flash secondary bridge (priority 5)
657
+ 3. Flash 2-3 regular nodes
658
+ 4. Power on all devices
659
+ 5. Monitor serial output
660
+
661
+ **Expected Results:**
662
+ - ✅ Both bridges connect to routers
663
+ - ✅ Both bridges see each other in coordination messages
664
+ - ✅ Regular nodes discover both bridges
665
+ - ✅ Regular nodes prefer primary bridge (priority 10)
666
+ - ✅ `getActiveBridges()` returns 2 bridge IDs
667
+ - ✅ `getRecommendedBridge()` returns primary bridge ID
668
+
669
+ #### Scenario 2: Primary Bridge Failure
670
+
671
+ **Objective:** Verify automatic failover to secondary bridge
672
+
673
+ **Steps:**
674
+ 1. Setup dual bridges and regular nodes (as above)
675
+ 2. Wait for stable operation (2+ minutes)
676
+ 3. Disconnect primary bridge power
677
+ 4. Monitor regular nodes for failover
678
+
679
+ **Expected Results:**
680
+ - ✅ Regular nodes detect primary bridge offline within 60s
681
+ - ✅ `getActiveBridges()` returns 1 bridge ID (secondary only)
682
+ - ✅ `getRecommendedBridge()` returns secondary bridge ID
683
+ - ✅ Messages continue flowing through secondary bridge
684
+ - ✅ No messages lost during transition
685
+
686
+ #### Scenario 3: Round-Robin Load Balancing
687
+
688
+ **Objective:** Verify even distribution across bridges
689
+
690
+ **Steps:**
691
+ 1. Setup dual bridges with equal priority (both 7)
692
+ 2. Flash regular node with ROUND_ROBIN strategy
693
+ 3. Send 10 messages from regular node
694
+ 4. Monitor which bridge receives each message
695
+
696
+ **Expected Results:**
697
+ - ✅ First message → Bridge 1
698
+ - ✅ Second message → Bridge 2
699
+ - ✅ Third message → Bridge 1
700
+ - ✅ Distribution is approximately 50/50
701
+ - ✅ Both bridges handle traffic
702
+
703
+ #### Scenario 4: Best Signal Selection
704
+
705
+ **Objective:** Verify selection based on WiFi signal strength
706
+
707
+ **Steps:**
708
+ 1. Setup dual bridges at different physical locations
709
+ 2. Flash regular node with BEST_SIGNAL strategy
710
+ 3. Move regular node between locations
711
+ 4. Monitor bridge selection
712
+
713
+ **Expected Results:**
714
+ - ✅ Node near Bridge 1 selects Bridge 1
715
+ - ✅ Node near Bridge 2 selects Bridge 2
716
+ - ✅ Selection changes as node moves
717
+ - ✅ Always uses bridge with best RSSI
718
+
719
+ ### Unit Tests
720
+
721
+ Run comprehensive unit tests:
722
+
723
+ ```bash
724
+ cd painlessMesh
725
+ cmake -G Ninja .
726
+ ninja
727
+ ./bin/catch_plugin
728
+ ```
729
+
730
+ **Expected Output:**
731
+ ```
732
+ ===============================================================================
733
+ All tests passed (67 assertions in 4 test cases)
734
+ ```
735
+
736
+ **Test Coverage:**
737
+ - ✅ BridgeCoordinationPackage serialization
738
+ - ✅ JSON round-trip integrity
739
+ - ✅ Empty peer list handling
740
+ - ✅ Maximum value edge cases
741
+ - ✅ Field preservation
742
+
743
+ ## Troubleshooting
744
+
745
+ ### Issue: No Bridges Found
746
+
747
+ **Symptoms:**
748
+ ```
749
+ Active Bridges: 0
750
+ Internet Available: NO
751
+ Recommended Bridge: 0
752
+ ```
753
+
754
+ **Possible Causes:**
755
+ 1. Bridges not powered on
756
+ 2. Router credentials incorrect
757
+ 3. Bridges failed to connect to router
758
+ 4. Mesh SSID/password mismatch
759
+
760
+ **Solutions:**
761
+ 1. Verify bridges are powered and running
762
+ 2. Check router credentials in bridge code
763
+ 3. Verify router SSID is correct
764
+ 4. Check serial output of bridges for connection status
765
+ 5. Ensure all devices use same MESH_PREFIX and MESH_PASSWORD
766
+
767
+ **Diagnostic Commands:**
768
+ ```cpp
769
+ // On bridge node
770
+ Serial.printf("Router connected: %s\n",
771
+ WiFi.status() == WL_CONNECTED ? "YES" : "NO");
772
+ Serial.printf("Router IP: %s\n",
773
+ WiFi.localIP().toString().c_str());
774
+ Serial.printf("Router RSSI: %d dBm\n", WiFi.RSSI());
775
+ ```
776
+
777
+ ### Issue: Bridges Not Coordinating
778
+
779
+ **Symptoms:**
780
+ ```
781
+ Active Bridges: 1
782
+ Expected: 2
783
+ Coordination messages not appearing in logs
784
+ ```
785
+
786
+ **Possible Causes:**
787
+ 1. `enableMultiBridge(true)` not called on bridges
788
+ 2. Bridges not connected to mesh
789
+ 3. Mesh network partitioned
790
+ 4. Coordination broadcast failing
791
+
792
+ **Solutions:**
793
+ 1. Verify `enableMultiBridge(true)` called before `initAsBridge()`
794
+ 2. Check mesh connectivity between bridges
795
+ 3. Enable CONNECTION debug messages
796
+ 4. Verify bridges see each other in mesh node list
797
+
798
+ **Diagnostic Commands:**
799
+ ```cpp
800
+ // On bridge node
801
+ Serial.printf("Multi-bridge enabled: %s\n",
802
+ mesh.isMultiBridgeEnabled() ? "YES" : "NO");
803
+
804
+ auto nodes = mesh.getNodeList(false);
805
+ Serial.printf("Mesh nodes: %d\n", nodes.size());
806
+ for (auto node : nodes) {
807
+ Serial.printf(" - Node: %u\n", node);
808
+ }
809
+ ```
810
+
811
+ ### Issue: Wrong Bridge Selected
812
+
813
+ **Symptoms:**
814
+ ```
815
+ Expected: Primary bridge (priority 10)
816
+ Actual: Secondary bridge (priority 5)
817
+ ```
818
+
819
+ **Possible Causes:**
820
+ 1. Priority not set correctly
821
+ 2. Primary bridge offline
822
+ 3. Wrong selection strategy
823
+ 4. Primary bridge no Internet connection
824
+
825
+ **Solutions:**
826
+ 1. Verify priority values in `initAsBridge()` calls
827
+ 2. Check primary bridge is online and healthy
828
+ 3. Verify strategy is PRIORITY_BASED
829
+ 4. Check primary bridge has Internet connection
830
+
831
+ **Diagnostic Commands:**
832
+ ```cpp
833
+ // On regular node
834
+ auto bridges = mesh.getBridges();
835
+ for (auto& bridge : bridges) {
836
+ Serial.printf("Bridge %u: RSSI=%d, Internet=%s, Healthy=%s\n",
837
+ bridge.nodeId, bridge.routerRSSI,
838
+ bridge.internetConnected ? "YES" : "NO",
839
+ bridge.isHealthy() ? "YES" : "NO");
840
+ }
841
+ ```
842
+
843
+ ### Issue: Failover Not Working
844
+
845
+ **Symptoms:**
846
+ ```
847
+ Primary bridge offline
848
+ Regular nodes not switching to secondary
849
+ Messages timing out
850
+ ```
851
+
852
+ **Possible Causes:**
853
+ 1. Secondary bridge not available
854
+ 2. Bridge timeout not expired (60s)
855
+ 3. Secondary bridge no Internet connection
856
+ 4. Bridge status broadcasts disabled
857
+
858
+ **Solutions:**
859
+ 1. Verify secondary bridge is online and connected
860
+ 2. Wait full 60s timeout period
861
+ 3. Check secondary bridge Internet connection
862
+ 4. Verify `enableBridgeStatusBroadcast(true)` called
863
+
864
+ **Diagnostic Commands:**
865
+ ```cpp
866
+ // Monitor bridge health
867
+ auto primary = mesh.getPrimaryBridge();
868
+ if (primary != nullptr) {
869
+ Serial.printf("Primary: %u (seen %u ms ago)\n",
870
+ primary->nodeId, millis() - primary->lastSeen);
871
+ } else {
872
+ Serial.println("No primary bridge available");
873
+ }
874
+ ```
875
+
876
+ ### Issue: High Memory Usage
877
+
878
+ **Symptoms:**
879
+ ```
880
+ Free heap: < 20KB
881
+ Crashes or resets
882
+ Memory allocation failures
883
+ ```
884
+
885
+ **Possible Causes:**
886
+ 1. Too many tracked bridges
887
+ 2. Large peer bridge lists
888
+ 3. Memory leak in application code
889
+
890
+ **Solutions:**
891
+ 1. Reduce `setMaxBridges()` to 2-3
892
+ 2. Limit number of concurrent bridges
893
+ 3. Profile application memory usage
894
+ 4. Check for memory leaks in custom code
895
+
896
+ **Diagnostic Commands:**
897
+ ```cpp
898
+ Serial.printf("Free heap: %u bytes\n", ESP.getFreeHeap());
899
+ Serial.printf("Max bridges: %u\n", maxConcurrentBridges);
900
+ Serial.printf("Known bridges: %u\n", knownBridges.size());
901
+ ```
902
+
903
+ ## Advanced Topics
904
+
905
+ ### Three or More Bridges
906
+
907
+ For very large or critical deployments:
908
+
909
+ ```cpp
910
+ // Bridge 1: Primary (highest priority)
911
+ mesh.initAsBridge(ssid, pass, router1, pass1, &sched, port, 10);
912
+
913
+ // Bridge 2: Secondary (medium priority)
914
+ mesh.initAsBridge(ssid, pass, router2, pass2, &sched, port, 7);
915
+
916
+ // Bridge 3: Tertiary (backup)
917
+ mesh.initAsBridge(ssid, pass, router3, pass3, &sched, port, 3);
918
+
919
+ // Set max tracked bridges
920
+ mesh.setMaxBridges(3);
921
+ ```
922
+
923
+ **Considerations:**
924
+ - Memory usage increases with more bridges
925
+ - Coordination overhead increases
926
+ - Recommended maximum: 5 bridges
927
+ - Most deployments work well with 2-3 bridges
928
+
929
+ ### Geographic Distribution
930
+
931
+ For mesh networks spanning multiple buildings:
932
+
933
+ ```cpp
934
+ // Building A Bridge (equal priority)
935
+ mesh.initAsBridge(ssid, pass, "BuildingA_Router", pass1, &sched, port, 10);
936
+
937
+ // Building B Bridge (equal priority)
938
+ mesh.initAsBridge(ssid, pass, "BuildingB_Router", pass2, &sched, port, 10);
939
+
940
+ // Regular nodes use BEST_SIGNAL strategy
941
+ mesh.setBridgeSelectionStrategy(painlessMesh::BEST_SIGNAL);
942
+ ```
943
+
944
+ **Benefits:**
945
+ - Nodes automatically use closest bridge
946
+ - Optimizes latency and reliability
947
+ - Supports mobile nodes
948
+
949
+ ### Traffic Shaping (Planned)
950
+
951
+ Future enhancement for routing different traffic types:
952
+
953
+ ```cpp
954
+ // Coming in v1.8.2+
955
+ mesh.setBridgeSelectionStrategy(painlessMesh::TRAFFIC_TYPE);
956
+ mesh.routeTrafficType(ALARM_MESSAGE, primaryBridge);
957
+ mesh.routeTrafficType(SENSOR_DATA, secondaryBridge);
958
+ ```
959
+
960
+ ### Weighted Round-Robin (Planned)
961
+
962
+ Future enhancement for unequal load distribution:
963
+
964
+ ```cpp
965
+ // Coming in v1.8.2+
966
+ mesh.setBridgeWeight(bridge1, 70); // 70% of traffic
967
+ mesh.setBridgeWeight(bridge2, 30); // 30% of traffic
968
+ ```
969
+
970
+ ### Performance Tuning
971
+
972
+ **Memory Optimization:**
973
+ ```cpp
974
+ // Reduce max bridges if memory limited
975
+ mesh.setMaxBridges(2);
976
+
977
+ // Increase coordination interval to reduce overhead
978
+ // (requires code modification - not configurable via API)
979
+ ```
980
+
981
+ **Network Optimization:**
982
+ ```cpp
983
+ // Adjust bridge timeout for faster failover
984
+ mesh.setBridgeTimeout(30000); // 30 seconds instead of 60
985
+
986
+ // Adjust bridge status broadcast interval
987
+ mesh.setBridgeStatusInterval(15000); // 15 seconds instead of 30
988
+ ```
989
+
990
+ ## Related Documentation
991
+
992
+ - [Bridge Status Feature](../BRIDGE_STATUS_FEATURE.md) - Bridge status broadcasts
993
+ - [Bridge Failover](../docs/BRIDGE_FAILOVER.md) - Automatic failover system
994
+ - [Bridge Architecture](../BRIDGE_ARCHITECTURE_IMPLEMENTATION.md) - Overall bridge design
995
+ - [Examples](../examples/multi_bridge/README.md) - Complete working examples
996
+ - [API Reference](../src/arduino/wifi.hpp) - Full API documentation
997
+
998
+ ## Support
999
+
1000
+ **Resources:**
1001
+ - GitHub Issues: https://github.com/Alteriom/painlessMesh/issues
1002
+ - Discussions: https://github.com/Alteriom/painlessMesh/discussions
1003
+ - Examples: `examples/multi_bridge/`
1004
+ - Wiki: https://alteriom.github.io/painlessMesh/
1005
+
1006
+ **Reporting Issues:**
1007
+ 1. Check troubleshooting section
1008
+ 2. Enable debug messages: `mesh.setDebugMsgTypes(ERROR | STARTUP | CONNECTION);`
1009
+ 3. Capture serial output from all nodes
1010
+ 4. Report with code snippets and logs
1011
+
1012
+ ## Changelog
1013
+
1014
+ ### v1.8.1
1015
+ - ✅ Initial multi-bridge coordination implementation
1016
+ - ✅ BridgeCoordinationPackage (Type 613)
1017
+ - ✅ Three bridge selection strategies
1018
+ - ✅ Bridge priority system (1-10)
1019
+ - ✅ Complete API and examples
1020
+
1021
+ ---
1022
+
1023
+ **Document Version:** 1.0
1024
+ **Last Updated:** 2025-11-11
1025
+ **Status:** Complete and Production-Ready