@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,259 @@
1
+ # Testing painlessMesh Examples with the Simulator
2
+
3
+ ## Quick Start Guide
4
+
5
+ This guide shows how to validate painlessMesh examples using the integrated simulator.
6
+
7
+ ### Prerequisites
8
+
9
+ ```bash
10
+ # Ubuntu/Debian
11
+ sudo apt-get install cmake ninja-build libboost-dev libboost-program-options-dev libyaml-cpp-dev
12
+
13
+ # macOS
14
+ brew install cmake ninja boost yaml-cpp
15
+ ```
16
+
17
+ ### Initialize Simulator
18
+
19
+ ```bash
20
+ # Clone with submodules (if starting fresh)
21
+ git clone --recursive https://github.com/Alteriom/painlessMesh.git
22
+
23
+ # OR initialize if already cloned
24
+ cd painlessMesh
25
+ git submodule update --init test/simulator
26
+ ```
27
+
28
+ ### Run Basic Example Test
29
+
30
+ ```bash
31
+ # Build simulator
32
+ cd test/simulator
33
+ mkdir build && cd build
34
+ cmake -G Ninja ..
35
+ ninja
36
+
37
+ # Run basic example validation
38
+ bin/painlessmesh-simulator --config \
39
+ ../../../examples/basic/test/simulator/scenarios/basic_mesh_test.yaml
40
+ ```
41
+
42
+ ### Expected Output
43
+
44
+ ```
45
+ Starting simulation: Basic Example - Mesh Formation and Broadcasting
46
+ Duration: 60 seconds
47
+
48
+ [00:00] Initializing 10 nodes...
49
+ [00:05] Node 6481 connected to mesh
50
+ [00:08] Node 6482 connected to mesh
51
+ [00:12] Node 6483 connected to mesh
52
+ ...
53
+ [00:30] ✓ All nodes connected (10/10)
54
+ [00:45] ✓ Messages delivered (avg 8.5 messages/node)
55
+ [00:60] ✓ Time synchronized (max drift: 5.2ms)
56
+
57
+ PASS: All validation criteria met
58
+ Results saved to: results/basic_test_results.csv
59
+ ```
60
+
61
+ ## What Gets Tested
62
+
63
+ ### Basic Example Test Scenario
64
+
65
+ The `basic_mesh_test.yaml` scenario validates:
66
+
67
+ 1. **Mesh Formation** ✓
68
+ - 10 virtual nodes form a connected mesh
69
+ - All nodes discover each other within 30 seconds
70
+
71
+ 2. **Message Broadcasting** ✓
72
+ - Each node periodically sends broadcasts
73
+ - All nodes receive messages from others
74
+ - Minimum 5 messages delivered per node
75
+
76
+ 3. **Time Synchronization** ✓
77
+ - Node clocks start with random offsets
78
+ - Time sync protocol converges within 45 seconds
79
+ - Final time differences < 10ms
80
+
81
+ 4. **Dynamic Topology** ✓
82
+ - New node joins at 30 seconds
83
+ - Network adapts and includes new node
84
+ - Messages continue to flow
85
+
86
+ ## Test Configuration
87
+
88
+ Edit `examples/basic/test/simulator/scenarios/basic_mesh_test.yaml`:
89
+
90
+ ```yaml
91
+ simulation:
92
+ duration: 60 # Test duration in seconds
93
+
94
+ nodes:
95
+ - template: "basic_example"
96
+ count: 10 # Number of virtual nodes
97
+
98
+ topology:
99
+ type: "random"
100
+ connectivity: 0.7 # 70% mesh connectivity
101
+
102
+ validation:
103
+ - check: "all_nodes_connected"
104
+ timeout: 30
105
+ - check: "messages_delivered"
106
+ min_messages_per_node: 5
107
+ ```
108
+
109
+ ## Analyzing Results
110
+
111
+ Results are saved to CSV:
112
+
113
+ ```bash
114
+ cat results/basic_test_results.csv
115
+ ```
116
+
117
+ ```csv
118
+ timestamp,node_id,messages_sent,messages_received,connections
119
+ 0,6481,0,0,0
120
+ 1,6481,1,0,2
121
+ 5,6481,1,3,3
122
+ 10,6481,2,7,3
123
+ ...
124
+ ```
125
+
126
+ Use Python/Excel/etc to analyze:
127
+
128
+ ```python
129
+ import pandas as pd
130
+
131
+ df = pd.read_csv('results/basic_test_results.csv')
132
+ print(f"Total messages: {df['messages_sent'].sum()}")
133
+ print(f"Avg per node: {df.groupby('node_id')['messages_received'].max().mean()}")
134
+ ```
135
+
136
+ ## Adding More Tests
137
+
138
+ ### Test with 50 Nodes
139
+
140
+ Copy and modify the scenario:
141
+
142
+ ```bash
143
+ cp examples/basic/test/simulator/scenarios/basic_mesh_test.yaml \
144
+ examples/basic/test/simulator/scenarios/stress_test.yaml
145
+ ```
146
+
147
+ Edit `stress_test.yaml`:
148
+ ```yaml
149
+ nodes:
150
+ - template: "basic_example"
151
+ count: 50 # Scale up!
152
+ ```
153
+
154
+ Run it:
155
+ ```bash
156
+ ./painlessmesh-simulator --config \
157
+ ../../../examples/basic/test/simulator/scenarios/stress_test.yaml
158
+ ```
159
+
160
+ ### Inject Network Failures
161
+
162
+ Add events to `basic_mesh_test.yaml`:
163
+
164
+ ```yaml
165
+ events:
166
+ # Partition network at 30s
167
+ - type: "network_partition"
168
+ time: 30
169
+ duration: 15
170
+ groups: [[0,1,2,3,4], [5,6,7,8,9]]
171
+
172
+ # Heal at 45s
173
+ - type: "network_heal"
174
+ time: 45
175
+ ```
176
+
177
+ This tests if the mesh recovers from partitions!
178
+
179
+ ## Testing Other Examples
180
+
181
+ To test other examples (startHere, echo, etc):
182
+
183
+ 1. Create `test/simulator/` directory in the example
184
+ 2. Copy firmware adapter pattern from `basic/`
185
+ 3. Create YAML scenario
186
+ 4. Run test
187
+
188
+ See [docs/SIMULATOR_TESTING.md](docs/SIMULATOR_TESTING.md) for detailed instructions.
189
+
190
+ ## CI/CD Integration
191
+
192
+ Add to `.github/workflows/ci.yml`:
193
+
194
+ ```yaml
195
+ - name: Test examples with simulator
196
+ run: |
197
+ cd test/simulator/build
198
+ bin/painlessmesh-simulator --config \
199
+ ../../../examples/basic/test/simulator/scenarios/basic_mesh_test.yaml
200
+ ```
201
+
202
+ ## Troubleshooting
203
+
204
+ ### Simulator not found
205
+
206
+ ```bash
207
+ git submodule update --init test/simulator
208
+ ```
209
+
210
+ ### Build fails
211
+
212
+ ```bash
213
+ # Check dependencies
214
+ sudo apt-get install cmake ninja-build libboost-dev libyaml-cpp-dev
215
+
216
+ # Clean rebuild
217
+ cd test/simulator
218
+ rm -rf build && mkdir build && cd build
219
+ cmake -G Ninja .. && ninja
220
+ ```
221
+
222
+ ### Test times out
223
+
224
+ Increase duration in YAML:
225
+ ```yaml
226
+ simulation:
227
+ duration: 120 # Give more time
228
+ ```
229
+
230
+ ### Need more details
231
+
232
+ Enable verbose logging:
233
+ ```bash
234
+ bin/painlessmesh-simulator --config test.yaml --verbose
235
+ ```
236
+
237
+ ## Documentation
238
+
239
+ - **Complete Guide**: [docs/SIMULATOR_TESTING.md](docs/SIMULATOR_TESTING.md)
240
+ - **Simulator Repo**: https://github.com/Alteriom/painlessMesh-simulator
241
+ - **Getting Started**: [test/simulator/GETTING_STARTED.md](test/simulator/GETTING_STARTED.md)
242
+ - **Configuration**: [test/simulator/docs/CONFIGURATION_GUIDE.md](test/simulator/docs/CONFIGURATION_GUIDE.md)
243
+
244
+ ## Benefits of Simulator Testing
245
+
246
+ ✅ **Fast** - Complete test in 60 seconds vs hours with hardware
247
+ ✅ **Scalable** - Test with 100+ nodes on a laptop
248
+ ✅ **Reproducible** - Same scenario always gives same results
249
+ ✅ **Realistic** - Actual firmware code runs in simulated environment
250
+ ✅ **Automated** - Integrate with CI/CD
251
+ ✅ **Cost-effective** - No hardware required
252
+
253
+ ## Next Steps
254
+
255
+ 1. Run the basic example test (see above)
256
+ 2. Experiment with different scenarios
257
+ 3. Add tests for other examples you use
258
+ 4. Integrate into your CI/CD pipeline
259
+ 5. Share your scenarios with the community!
@@ -0,0 +1,357 @@
1
+ # Bridge Initialization and Fallback Patterns
2
+
3
+ ## Overview
4
+
5
+ This document describes the behavior of `initAsBridge()` when router connection fails, and demonstrates recommended fallback patterns that give library users control over error handling and recovery strategies.
6
+
7
+ ## Design Philosophy
8
+
9
+ As a library, painlessMesh provides the building blocks for mesh networking but **does not dictate application-level recovery strategies**. When bridge initialization fails, the library:
10
+
11
+ 1. **Returns failure status** (`false`) instead of forcing actions like restart
12
+ 2. **Provides clear error logging** to help diagnose issues
13
+ 3. **Leaves mesh state clean** for user-controlled recovery
14
+ 4. **Allows graceful fallback** to regular mesh node
15
+
16
+ This design enables flexible deployment patterns:
17
+ - Single-bridge networks with manual recovery
18
+ - Multi-bridge networks with automatic redundancy
19
+ - Hybrid approaches with controlled failover behavior
20
+
21
+ ## Bridge Initialization Behavior
22
+
23
+ ### Success Path
24
+
25
+ When `initAsBridge()` successfully connects to the router:
26
+
27
+ ```cpp
28
+ bool initAsBridge(TSTRING meshSSID, TSTRING meshPassword,
29
+ TSTRING routerSSID, TSTRING routerPassword,
30
+ Scheduler *baseScheduler, uint16_t port = 5555);
31
+ ```
32
+
33
+ **Steps:**
34
+ 1. Disconnect from any existing WiFi connections
35
+ 2. Connect to router in STA mode (30 second timeout)
36
+ 3. Detect router's WiFi channel
37
+ 4. Initialize mesh AP on same channel as router
38
+ 5. Re-establish router connection using stationManual
39
+ 6. Set node as root/bridge
40
+ 7. Start bridge status broadcasting
41
+ 8. **Return `true`**
42
+
43
+ **Result:** Device functions as bridge on router's channel, maintaining both router and mesh connectivity.
44
+
45
+ ### Failure Path
46
+
47
+ When router connection fails (timeout after 30 seconds):
48
+
49
+ **Steps:**
50
+ 1. Log error: "Failed to connect to router"
51
+ 2. Log error: "Cannot become bridge without router connection"
52
+ 3. Log error: "Bridge initialization aborted - remaining as regular node"
53
+ 4. **Return `false` without initializing mesh**
54
+
55
+ **Result:** Device is not initialized as bridge OR regular node. User code must decide next steps.
56
+
57
+ ### Why No Automatic Recovery?
58
+
59
+ The library does not automatically restart or force fallback because:
60
+
61
+ 1. **User control**: Application knows its deployment context and requirements
62
+ 2. **Network stability**: Avoid restart loops that could destabilize mesh
63
+ 3. **Flexibility**: Different use cases need different recovery strategies
64
+ 4. **Predictability**: Library behavior should be deterministic and explicit
65
+
66
+ ## Recommended Fallback Patterns
67
+
68
+ ### Pattern 1: Fallback to Regular Node (Basic)
69
+
70
+ **Use Case:** Single bridge network where bridge is not critical to mesh operation.
71
+
72
+ ```cpp
73
+ bool bridgeSuccess = mesh.initAsBridge(MESH_PREFIX, MESH_PASSWORD,
74
+ ROUTER_SSID, ROUTER_PASSWORD,
75
+ &userScheduler, MESH_PORT);
76
+
77
+ if (!bridgeSuccess) {
78
+ Serial.println("✗ Failed to initialize as bridge!");
79
+ Serial.println("Router unreachable - falling back to regular mesh node");
80
+
81
+ // Fallback: Initialize as regular mesh node
82
+ // Device can still participate in mesh without bridge functionality
83
+ mesh.init(MESH_PREFIX, MESH_PASSWORD, &userScheduler, MESH_PORT);
84
+
85
+ Serial.println("✓ Initialized as regular mesh node");
86
+ Serial.println("Note: To function as a bridge, fix router and restart");
87
+ }
88
+ ```
89
+
90
+ **Benefits:**
91
+ - Device remains part of mesh network
92
+ - Can receive messages from other nodes
93
+ - Can participate in mesh topology
94
+ - Manual intervention needed to restore bridge role
95
+
96
+ ### Pattern 2: Fallback with Auto-Promotion (Recommended)
97
+
98
+ **Use Case:** Networks where any node can become bridge when router becomes available.
99
+
100
+ ```cpp
101
+ bool bridgeSuccess = mesh.initAsBridge(MESH_PREFIX, MESH_PASSWORD,
102
+ ROUTER_SSID, ROUTER_PASSWORD,
103
+ &userScheduler, MESH_PORT);
104
+
105
+ if (!bridgeSuccess) {
106
+ Serial.println("✗ Failed to initialize as bridge!");
107
+ Serial.println("Router unreachable - enabling bridge failover");
108
+
109
+ // Fallback: Regular node with automatic bridge promotion
110
+ mesh.init(MESH_PREFIX, MESH_PASSWORD, &userScheduler, MESH_PORT);
111
+ mesh.setRouterCredentials(ROUTER_SSID, ROUTER_PASSWORD);
112
+ mesh.enableBridgeFailover(true);
113
+ mesh.setElectionTimeout(5000);
114
+
115
+ Serial.println("✓ Running as regular node");
116
+ Serial.println("Will auto-promote to bridge when router available");
117
+ }
118
+ ```
119
+
120
+ **Benefits:**
121
+ - Automatic recovery when router comes online
122
+ - No manual intervention needed
123
+ - Participates in bridge elections
124
+ - Maintains mesh connectivity throughout
125
+
126
+ ### Pattern 3: Multi-Bridge Redundancy
127
+
128
+ **Use Case:** Critical networks with multiple potential bridges.
129
+
130
+ **Primary Bridge:**
131
+ ```cpp
132
+ bool bridgeSuccess = mesh.initAsBridge(MESH_PREFIX, MESH_PASSWORD,
133
+ ROUTER_SSID, ROUTER_PASSWORD,
134
+ &userScheduler, MESH_PORT, 10); // Priority 10
135
+
136
+ if (!bridgeSuccess) {
137
+ Serial.println("✗ Primary bridge init failed!");
138
+ Serial.println("Router unreachable - secondary should be active");
139
+
140
+ // Fallback: Regular node (secondary bridge takes over)
141
+ mesh.init(MESH_PREFIX, MESH_PASSWORD, &userScheduler, MESH_PORT);
142
+
143
+ Serial.println("✓ Running as regular node");
144
+ Serial.println("Secondary bridge should provide connectivity");
145
+ }
146
+ ```
147
+
148
+ **Secondary Bridge:**
149
+ ```cpp
150
+ // Secondary bridge configuration (priority 5)
151
+ // If primary fails, secondary provides redundancy
152
+ ```
153
+
154
+ **Benefits:**
155
+ - Immediate redundancy via secondary bridge
156
+ - Graceful degradation of primary
157
+ - No single point of failure
158
+ - Maintains Internet connectivity for mesh
159
+
160
+ ### Pattern 4: Retry with Exponential Backoff
161
+
162
+ **Use Case:** Environments with intermittent router availability.
163
+
164
+ ```cpp
165
+ const int MAX_RETRIES = 3;
166
+ int retryCount = 0;
167
+ int retryDelay = 5000; // Start with 5 seconds
168
+
169
+ while (retryCount < MAX_RETRIES) {
170
+ bool bridgeSuccess = mesh.initAsBridge(MESH_PREFIX, MESH_PASSWORD,
171
+ ROUTER_SSID, ROUTER_PASSWORD,
172
+ &userScheduler, MESH_PORT);
173
+
174
+ if (bridgeSuccess) {
175
+ Serial.println("✓ Bridge initialized successfully");
176
+ break;
177
+ }
178
+
179
+ retryCount++;
180
+ if (retryCount < MAX_RETRIES) {
181
+ Serial.printf("Retry %d/%d in %d seconds...\n",
182
+ retryCount, MAX_RETRIES, retryDelay/1000);
183
+ delay(retryDelay);
184
+ retryDelay *= 2; // Exponential backoff
185
+ }
186
+ }
187
+
188
+ if (retryCount >= MAX_RETRIES) {
189
+ Serial.println("Max retries reached - falling back to regular node");
190
+ mesh.init(MESH_PREFIX, MESH_PASSWORD, &userScheduler, MESH_PORT);
191
+ }
192
+ ```
193
+
194
+ **Benefits:**
195
+ - Handles temporary router outages
196
+ - Exponential backoff prevents network flooding
197
+ - Eventually falls back if persistent failure
198
+ - Configurable retry strategy
199
+
200
+ ### Pattern 5: User-Controlled Restart
201
+
202
+ **Use Case:** Explicit bridge nodes that require bridge functionality to operate.
203
+
204
+ ```cpp
205
+ bool bridgeSuccess = mesh.initAsBridge(MESH_PREFIX, MESH_PASSWORD,
206
+ ROUTER_SSID, ROUTER_PASSWORD,
207
+ &userScheduler, MESH_PORT);
208
+
209
+ if (!bridgeSuccess) {
210
+ Serial.println("✗ Failed to initialize as bridge!");
211
+ Serial.println("This device must function as a bridge");
212
+ Serial.println("Restarting in 30 seconds to retry...");
213
+ delay(30000);
214
+ ESP.restart(); // User's choice to restart
215
+ }
216
+ ```
217
+
218
+ **Benefits:**
219
+ - Clear intent that bridge role is required
220
+ - User controls restart timing
221
+ - Can implement watchdog or LED indicators
222
+ - Appropriate for dedicated bridge hardware
223
+
224
+ ## Channel Discovery and Mesh Formation
225
+
226
+ ### Two Nodes on Different Channels
227
+
228
+ When two mesh nodes start on different channels:
229
+
230
+ 1. **Station Scan Discovery:**
231
+ - Each node scans for mesh SSID on all channels
232
+ - Nodes discover each other via beacon frames
233
+ - Connection negotiation determines channel
234
+
235
+ 2. **Channel Selection:**
236
+ - Node with more connections typically maintains its channel
237
+ - New node switches to join existing network
238
+ - Root node (bridge) has priority in channel selection
239
+
240
+ 3. **Bridge Channel Priority:**
241
+ - Bridge nodes maintain router's channel
242
+ - Regular nodes switch to bridge's channel to join
243
+ - This ensures bridge maintains router connection
244
+
245
+ ### Bridge Appearance and Channel Changes
246
+
247
+ When a new bridge appears in an existing mesh:
248
+
249
+ 1. **Bridge Broadcasts Status (Type 610):**
250
+ - Announces presence on its channel (router's channel)
251
+ - Other nodes receive status updates
252
+
253
+ 2. **Nodes Evaluate Connectivity:**
254
+ - If bridge is on different channel, nodes must decide:
255
+ - Stay on current mesh channel?
256
+ - Switch to bridge's channel for Internet access?
257
+
258
+ 3. **Channel Re-synchronization:**
259
+ - Nodes may perform channel re-sync if mesh fragmented
260
+ - After 6 consecutive empty scans, trigger re-sync
261
+ - Re-scan all channels to find mesh
262
+
263
+ 4. **Avoiding Channel Chase Loops:**
264
+ - Election mechanism includes channel check
265
+ - Nodes defer election if approaching re-sync threshold
266
+ - Prevents oscillation between channels
267
+
268
+ ## Best Practices
269
+
270
+ ### 1. Choose Appropriate Fallback Pattern
271
+
272
+ - **Critical Infrastructure:** Use Pattern 3 (Multi-Bridge Redundancy)
273
+ - **General IoT:** Use Pattern 2 (Auto-Promotion)
274
+ - **Testing/Development:** Use Pattern 1 (Basic Fallback)
275
+ - **Dedicated Bridges:** Use Pattern 5 (User-Controlled Restart)
276
+
277
+ ### 2. Monitor Bridge Status
278
+
279
+ ```cpp
280
+ mesh.onBridgeStatusChanged([](uint32_t bridgeNodeId, bool hasInternet) {
281
+ Serial.printf("Bridge %u: Internet %s\n",
282
+ bridgeNodeId, hasInternet ? "UP" : "DOWN");
283
+ });
284
+ ```
285
+
286
+ ### 3. Handle Bridge Role Changes
287
+
288
+ ```cpp
289
+ mesh.onBridgeRoleChanged([](bool isBridge, String reason) {
290
+ if (isBridge) {
291
+ Serial.printf("Promoted to bridge: %s\n", reason.c_str());
292
+ } else {
293
+ Serial.printf("Demoted from bridge: %s\n", reason.c_str());
294
+ }
295
+ });
296
+ ```
297
+
298
+ ### 4. Avoid Restart Loops
299
+
300
+ - Implement maximum retry counts
301
+ - Use exponential backoff for retries
302
+ - Monitor consecutive failures and adjust strategy
303
+ - Consider manual intervention threshold
304
+
305
+ ### 5. Document Deployment Strategy
306
+
307
+ Clearly document in your application code:
308
+ - Which fallback pattern is used
309
+ - Why that pattern was chosen
310
+ - Expected behavior during failures
311
+ - Manual recovery procedures if needed
312
+
313
+ ## Testing Recommendations
314
+
315
+ ### Test Scenario 1: Router Unreachable at Startup
316
+
317
+ 1. Configure node as bridge
318
+ 2. Make router unreachable (wrong password, powered off, etc.)
319
+ 3. Verify node falls back gracefully
320
+ 4. Check node can join mesh as regular node
321
+ 5. Verify no restart loops occur
322
+
323
+ ### Test Scenario 2: Router Becomes Available Later
324
+
325
+ 1. Start with router unreachable
326
+ 2. Node falls back to regular node with failover enabled
327
+ 3. Power on router
328
+ 4. Verify node detects router and promotes to bridge
329
+ 5. Check channel switching works correctly
330
+
331
+ ### Test Scenario 3: Multi-Bridge Failover
332
+
333
+ 1. Start primary and secondary bridges
334
+ 2. Make primary's router unreachable
335
+ 3. Verify primary falls back to regular node
336
+ 4. Check secondary bridge continues providing connectivity
337
+ 5. Restore primary's router and verify it resumes bridge role
338
+
339
+ ### Test Scenario 4: Channel Chase Prevention
340
+
341
+ 1. Start mesh on channel 6
342
+ 2. Add bridge on channel 11
343
+ 3. Verify nodes handle channel mismatch
344
+ 4. Check no continuous restart or channel oscillation
345
+ 5. Verify eventual mesh convergence on single channel
346
+
347
+ ## Summary
348
+
349
+ painlessMesh provides flexible bridge initialization with graceful failure handling:
350
+
351
+ - **Library returns status**, doesn't force actions
352
+ - **Users choose recovery strategy** based on their requirements
353
+ - **Multiple fallback patterns** for different use cases
354
+ - **Channel management** prevents network instability
355
+ - **Comprehensive callbacks** for monitoring and control
356
+
357
+ This design philosophy ensures painlessMesh can be used in diverse deployments while maintaining network stability and giving users full control over their mesh behavior.