@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,299 @@
1
+ # Internet Access in painlessMesh - Quick Reference
2
+
3
+ ## Quick Answer
4
+
5
+ **Q: Why can't my mesh nodes access the internet?**
6
+
7
+ **A: Only the bridge node has internet access. Regular mesh nodes must forward data through the bridge.**
8
+
9
+ ```text
10
+ Internet
11
+ |
12
+ Router ← Your WiFi
13
+ |
14
+ Bridge Node ← Only this node can access internet
15
+ |
16
+ Mesh Network ← These nodes cannot access internet directly
17
+ / | \
18
+ Node1 Node2 Node3
19
+ ```
20
+
21
+ ## Common Error Messages
22
+
23
+ If you see these errors on regular mesh nodes, you're trying to access the internet directly:
24
+
25
+ ```
26
+ [HTTPS] GET... failed, error: connection refused
27
+ WiFi.status() != WL_CONNECTED
28
+ HTTP request timeout
29
+ Connection failed
30
+ ```
31
+
32
+ ## Quick Fix
33
+
34
+ ### ❌ Wrong Approach (Doesn't Work)
35
+
36
+ ```cpp
37
+ // Regular mesh node trying to access internet
38
+ HTTPClient http;
39
+ http.begin("http://api.example.com/data");
40
+ http.POST(sensorData); // FAILS!
41
+ ```
42
+
43
+ ### ✅ Correct Approach (Works)
44
+
45
+ ```cpp
46
+ // Regular mesh node sends to bridge
47
+ mesh.sendSingle(bridgeNodeId, sensorData);
48
+
49
+ // Bridge node forwards to internet
50
+ void receivedCallback(uint32_t from, String& msg) {
51
+ if (WiFi.status() == WL_CONNECTED) {
52
+ HTTPClient http;
53
+ http.begin("http://api.example.com/data");
54
+ http.POST(msg); // WORKS!
55
+ http.end();
56
+ }
57
+ }
58
+ ```
59
+
60
+ ## Architecture Patterns
61
+
62
+ ### Pattern 1: Single Bridge (Basic)
63
+
64
+ One designated node is the bridge:
65
+
66
+ ```cpp
67
+ // ==== BRIDGE NODE ====
68
+ mesh.initAsBridge(MESH_PREFIX, MESH_PASSWORD,
69
+ ROUTER_SSID, ROUTER_PASSWORD,
70
+ &userScheduler, 5555);
71
+
72
+ // ==== SENSOR NODES ====
73
+ mesh.init(MESH_PREFIX, MESH_PASSWORD, &userScheduler, 5555);
74
+ ```
75
+
76
+ ### Pattern 2: Bridge Failover (High Availability)
77
+
78
+ Multiple nodes can become bridge automatically:
79
+
80
+ ```cpp
81
+ // All nodes have router credentials
82
+ mesh.setRouterCredentials(ROUTER_SSID, ROUTER_PASSWORD);
83
+ mesh.enableBridgeFailover(true);
84
+
85
+ mesh.onBridgeRoleChanged(&bridgeRoleCallback);
86
+ ```
87
+
88
+ ### Pattern 3: Multi-Bridge (Load Balancing)
89
+
90
+ Multiple simultaneous bridges:
91
+
92
+ ```cpp
93
+ // Primary bridge (priority 10)
94
+ mesh.initAsBridge(..., 10);
95
+
96
+ // Secondary bridge (priority 5)
97
+ mesh.initAsBridge(..., 5);
98
+ ```
99
+
100
+ ## By Use Case
101
+
102
+ ### HTTP/HTTPS Requests
103
+
104
+ ```cpp
105
+ // Bridge forwards HTTP requests
106
+ void receivedCallback(uint32_t from, String& msg) {
107
+ HTTPClient http;
108
+ http.begin("https://api.example.com/endpoint");
109
+ http.addHeader("Content-Type", "application/json");
110
+ http.POST(msg);
111
+ http.end();
112
+ }
113
+ ```
114
+
115
+ ### MQTT Publishing
116
+
117
+ ```cpp
118
+ // Bridge publishes to MQTT
119
+ #include "PubSubClient.h"
120
+ PubSubClient mqtt(wifiClient);
121
+
122
+ void receivedCallback(uint32_t from, String& msg) {
123
+ String topic = "mesh/sensor/" + String(from);
124
+ mqtt.publish(topic.c_str(), msg.c_str());
125
+ }
126
+ ```
127
+
128
+ ### WhatsApp Notifications
129
+
130
+ ```cpp
131
+ // Bridge sends WhatsApp messages
132
+ #include "Callmebot_ESP32.h"
133
+ Callmebot_ESP32 whatsapp;
134
+
135
+ void receivedCallback(uint32_t from, String& msg) {
136
+ DynamicJsonDocument doc(1024);
137
+ deserializeJson(doc, msg);
138
+
139
+ if (doc["alarm"] == true) {
140
+ String alert = "Alarm from sensor " + String(from);
141
+ whatsapp.sendMessage(alert);
142
+ }
143
+ }
144
+ ```
145
+
146
+ ### Cloud Services (AWS, Azure, GCP)
147
+
148
+ ```cpp
149
+ // Bridge forwards to cloud
150
+ void receivedCallback(uint32_t from, String& msg) {
151
+ // AWS IoT Core
152
+ awsClient.publish("iot/sensor/data", msg);
153
+
154
+ // Azure IoT Hub
155
+ azureClient.sendEvent(msg);
156
+
157
+ // Google Cloud IoT
158
+ googleClient.publishTelemetry(msg);
159
+ }
160
+ ```
161
+
162
+ ### Email Notifications
163
+
164
+ ```cpp
165
+ // Bridge sends email
166
+ #include "ESP_Mail_Client.h"
167
+
168
+ void receivedCallback(uint32_t from, String& msg) {
169
+ SMTPData smtpData;
170
+ smtpData.setLogin(SMTP_HOST, SMTP_PORT, EMAIL, PASSWORD);
171
+ smtpData.setMessage("Sensor Alert", msg);
172
+ MailClient.sendMail(smtpData);
173
+ }
174
+ ```
175
+
176
+ ## Technical Explanation
177
+
178
+ ### Why Only Bridge Has Internet?
179
+
180
+ ESP8266/ESP32 WiFi hardware operates on a single channel:
181
+
182
+ **Bridge Node (WIFI_AP_STA):**
183
+ - Creates mesh Access Point (AP) on channel X
184
+ - Connects to router Station (STA) on channel X
185
+ - Both on same channel = Internet access ✅
186
+
187
+ **Regular Node (WIFI_AP):**
188
+ - Creates mesh Access Point (AP) on channel X
189
+ - No router connection
190
+ - No internet access ❌
191
+
192
+ ### Can I Make All Nodes Bridges?
193
+
194
+ Technically yes, but **not recommended** because:
195
+
196
+ 1. **Memory overhead**: +5-10KB RAM per node
197
+ 2. **Performance degradation**: All compete for router
198
+ 3. **Router limits**: Max clients (typically 10-32)
199
+ 4. **Power consumption**: Extra WiFi connection
200
+ 5. **Loses mesh benefits**: Defeats purpose of mesh
201
+
202
+ ## Complete Examples
203
+
204
+ ### Sensor Network with Cloud Upload
205
+
206
+ **Bridge:**
207
+ ```cpp
208
+ #include "painlessMesh.h"
209
+ #include "HTTPClient.h"
210
+
211
+ painlessMesh mesh;
212
+
213
+ void setup() {
214
+ mesh.initAsBridge(MESH_PREFIX, MESH_PASSWORD,
215
+ ROUTER_SSID, ROUTER_PASSWORD,
216
+ &userScheduler, 5555);
217
+ mesh.onReceive(&receivedCallback);
218
+ }
219
+
220
+ void receivedCallback(uint32_t from, String& msg) {
221
+ if (WiFi.status() == WL_CONNECTED) {
222
+ HTTPClient http;
223
+ http.begin("https://cloud.example.com/api/sensor");
224
+ http.addHeader("Authorization", "Bearer " + API_KEY);
225
+ http.addHeader("Content-Type", "application/json");
226
+
227
+ int httpCode = http.POST(msg);
228
+ if (httpCode == HTTP_CODE_OK) {
229
+ Serial.println("Data uploaded to cloud");
230
+ }
231
+ http.end();
232
+ }
233
+ }
234
+ ```
235
+
236
+ **Sensor Node:**
237
+ ```cpp
238
+ #include "painlessMesh.h"
239
+
240
+ painlessMesh mesh;
241
+ #define BRIDGE_NODE_ID 1234567890
242
+
243
+ void setup() {
244
+ mesh.init(MESH_PREFIX, MESH_PASSWORD, &userScheduler, 5555);
245
+ }
246
+
247
+ void loop() {
248
+ mesh.update();
249
+ sendSensorData();
250
+ }
251
+
252
+ void sendSensorData() {
253
+ static unsigned long lastSend = 0;
254
+ if (millis() - lastSend < 60000) return;
255
+ lastSend = millis();
256
+
257
+ String data = "{\"sensor\":\"temp\",\"value\":" +
258
+ String(readTemperature()) + ",\"nodeId\":" +
259
+ String(mesh.getNodeId()) + "}";
260
+
261
+ mesh.sendSingle(BRIDGE_NODE_ID, data);
262
+ }
263
+ ```
264
+
265
+ ## Troubleshooting Checklist
266
+
267
+ - [ ] Verify which node is the bridge
268
+ - [ ] Check bridge has `initAsBridge()` or router credentials
269
+ - [ ] Confirm bridge shows `WiFi.status() == WL_CONNECTED`
270
+ - [ ] Verify regular nodes use `mesh.init()` without router
271
+ - [ ] Check sensor nodes send to bridge, not directly to internet
272
+ - [ ] Verify bridge forwards received messages to internet
273
+ - [ ] Test bridge internet connection with simple HTTP request
274
+ - [ ] Check serial output for connection errors
275
+ - [ ] Monitor memory usage on bridge node
276
+ - [ ] Verify firewall/router allows bridge's outbound connections
277
+
278
+ ## More Information
279
+
280
+ - **[Common Architecture Mistakes](common-architecture-mistakes.md)** - Detailed guide
281
+ - **[BRIDGE_TO_INTERNET.md](../../BRIDGE_TO_INTERNET.md)** - Complete setup guide
282
+ - **[Bridge Failover](../BRIDGE_FAILOVER.md)** - High availability
283
+ - **[FAQ](faq.md)** - Common questions
284
+ - **[examples/mqttBridge/](../../examples/mqttBridge/)** - Working example
285
+
286
+ ## Still Having Issues?
287
+
288
+ When asking for help, provide:
289
+
290
+ 1. Which node is the bridge? (show setup code)
291
+ 2. Which nodes are sensors? (show setup code)
292
+ 3. Serial output from bridge (with debug enabled)
293
+ 4. Serial output from sensor node (with debug enabled)
294
+ 5. Complete error message
295
+ 6. What you're trying to access (HTTP API, MQTT, etc.)
296
+
297
+ Post in:
298
+ - [GitHub Issues](https://github.com/Alteriom/painlessMesh/issues)
299
+ - [Community Forum](https://groups.google.com/forum/#!forum/painlessmesh-user)
@@ -0,0 +1,40 @@
1
+ cmake_minimum_required(VERSION 3.10)
2
+ project(BasicExampleSimulatorTests)
3
+
4
+ set(CMAKE_CXX_STANDARD 14)
5
+ set(CMAKE_CXX_STANDARD_REQUIRED ON)
6
+
7
+ # Path to the simulator (submodule)
8
+ set(SIMULATOR_ROOT "${CMAKE_CURRENT_SOURCE_DIR}/../../../../test/simulator")
9
+
10
+ # Include simulator's CMake configuration
11
+ if(EXISTS "${SIMULATOR_ROOT}/CMakeLists.txt")
12
+ # Add simulator as subdirectory
13
+ add_subdirectory(${SIMULATOR_ROOT} ${CMAKE_CURRENT_BINARY_DIR}/simulator)
14
+ else()
15
+ message(FATAL_ERROR "Simulator not found at ${SIMULATOR_ROOT}. Did you initialize the submodule?")
16
+ endif()
17
+
18
+ # Include painlessMesh library
19
+ set(PAINLESS_MESH_ROOT "${CMAKE_CURRENT_SOURCE_DIR}/../../../..")
20
+ include_directories(
21
+ ${PAINLESS_MESH_ROOT}/src
22
+ ${CMAKE_CURRENT_SOURCE_DIR}/firmware
23
+ )
24
+
25
+ # Register our custom firmware with the simulator
26
+ # This allows the YAML scenarios to reference "basic_example" template
27
+ add_library(basic_example_firmware INTERFACE)
28
+ target_include_directories(basic_example_firmware INTERFACE
29
+ ${CMAKE_CURRENT_SOURCE_DIR}/firmware
30
+ )
31
+
32
+ # Link with simulator
33
+ target_link_libraries(basic_example_firmware INTERFACE
34
+ painlessmesh_simulator
35
+ )
36
+
37
+ # The simulator executable will be built by the simulator's CMakeLists.txt
38
+ # We just need to ensure our firmware headers are available
39
+ message(STATUS "Basic example firmware tests configured")
40
+ message(STATUS "Run tests with: ./painlessmesh-simulator --config scenarios/basic_mesh_test.yaml")
@@ -0,0 +1,149 @@
1
+ # Basic Example - Simulator Tests
2
+
3
+ This directory contains simulator-based tests for the `basic.ino` example using the [painlessMesh-simulator](https://github.com/Alteriom/painlessMesh-simulator).
4
+
5
+ ## Overview
6
+
7
+ The simulator allows testing the basic example with 10+ virtual nodes without physical hardware, validating:
8
+ - Mesh formation with multiple nodes
9
+ - Message broadcasting to all nodes
10
+ - Callback functionality
11
+ - Dynamic node joining
12
+ - Time synchronization
13
+
14
+ ## Quick Start
15
+
16
+ ### Prerequisites
17
+
18
+ Install dependencies (Ubuntu/Debian):
19
+ ```bash
20
+ sudo apt-get install cmake ninja-build libboost-dev libboost-program-options-dev libyaml-cpp-dev
21
+ ```
22
+
23
+ ### Initialize Submodule
24
+
25
+ If not already done:
26
+ ```bash
27
+ cd ../../../../test
28
+ git submodule update --init simulator
29
+ cd -
30
+ ```
31
+
32
+ ### Build and Run
33
+
34
+ ```bash
35
+ # Build simulator
36
+ mkdir build && cd build
37
+ cmake -G Ninja ..
38
+ ninja
39
+
40
+ # Run test scenario
41
+ bin/painlessmesh-simulator --config ../scenarios/basic_mesh_test.yaml
42
+
43
+ # Or using the simulator from test/simulator
44
+ cd ../../../../test/simulator
45
+ mkdir -p build && cd build
46
+ cmake -G Ninja ..
47
+ ninja
48
+ bin/painlessmesh-simulator --config ../../../examples/basic/test/simulator/scenarios/basic_mesh_test.yaml
49
+ ```
50
+
51
+ ## Test Scenarios
52
+
53
+ ### basic_mesh_test.yaml
54
+
55
+ Tests basic functionality with 10 nodes:
56
+ - ✓ Mesh formation
57
+ - ✓ Message broadcasting
58
+ - ✓ Callback triggering
59
+ - ✓ Dynamic node joining (node added at 30s)
60
+ - ✓ Time synchronization
61
+
62
+ **Expected Results:**
63
+ - All nodes connect within 30 seconds
64
+ - Each node receives 5+ messages within 60 seconds
65
+ - Time differences < 10ms after 45 seconds
66
+
67
+ ## Firmware Adapter
68
+
69
+ The `firmware/basic_firmware.hpp` file wraps the `basic.ino` logic for simulation:
70
+
71
+ ```cpp
72
+ #include "simulator/firmware/firmware_base.hpp"
73
+
74
+ class BasicFirmware : public FirmwareBase {
75
+ // Implements same logic as basic.ino
76
+ // - setup() initializes mesh and callbacks
77
+ // - loop() calls mesh.update()
78
+ // - sendMessage() broadcasts periodically
79
+ };
80
+ ```
81
+
82
+ This allows running the **exact same code** that runs on hardware.
83
+
84
+ ## Adding More Tests
85
+
86
+ Create new YAML scenarios in `scenarios/`:
87
+
88
+ ```yaml
89
+ simulation:
90
+ name: "Stress Test"
91
+ duration: 120
92
+
93
+ nodes:
94
+ - template: "basic_example"
95
+ count: 50 # Test with 50 nodes
96
+
97
+ topology:
98
+ type: "ring" # Different topology
99
+
100
+ events:
101
+ - type: "network_partition" # Inject failures
102
+ time: 60
103
+ ```
104
+
105
+ ## Metrics and Validation
106
+
107
+ Test results are saved to `results/basic_test_results.csv`:
108
+
109
+ ```csv
110
+ timestamp,node_id,messages_sent,messages_received,topology_changes
111
+ 0,6481,0,0,1
112
+ 1,6481,1,0,1
113
+ 2,6481,1,2,1
114
+ ...
115
+ ```
116
+
117
+ Analyze with:
118
+ ```python
119
+ import pandas as pd
120
+ df = pd.read_csv('results/basic_test_results.csv')
121
+ print(df.groupby('node_id')['messages_received'].sum())
122
+ ```
123
+
124
+ ## Documentation
125
+
126
+ - [Simulator Documentation](../../../../test/simulator/README.md)
127
+ - [Integration Guide](../../../../test/simulator/docs/INTEGRATING_INTO_YOUR_PROJECT.md)
128
+ - [Configuration Reference](../../../../test/simulator/docs/CONFIGURATION_GUIDE.md)
129
+
130
+ ## Troubleshooting
131
+
132
+ **Simulator not found:**
133
+ ```bash
134
+ cd ../../../../test
135
+ git submodule update --init simulator
136
+ ```
137
+
138
+ **Build errors:**
139
+ ```bash
140
+ # Check dependencies
141
+ sudo apt-get install libboost-dev libyaml-cpp-dev
142
+
143
+ # Clean build
144
+ rm -rf build && mkdir build && cd build
145
+ cmake -G Ninja ..
146
+ ```
147
+
148
+ **Test failures:**
149
+ Check the simulator output for details on which validation failed.
@@ -0,0 +1,117 @@
1
+ #pragma once
2
+
3
+ #include "simulator/firmware/firmware_base.hpp"
4
+ #include <painlessMesh.h>
5
+
6
+ /**
7
+ * @brief Firmware adapter for basic.ino example
8
+ *
9
+ * This wraps the basic example sketch logic for testing with the simulator.
10
+ * It allows running the exact same code that runs on ESP32/ESP8266 hardware
11
+ * in a simulated environment with 100+ virtual nodes.
12
+ */
13
+ class BasicFirmware : public FirmwareBase {
14
+ public:
15
+ BasicFirmware() : mesh_(nullptr), userScheduler_(nullptr) {}
16
+
17
+ ~BasicFirmware() override {
18
+ if (taskSendMessage_) {
19
+ delete taskSendMessage_;
20
+ }
21
+ }
22
+
23
+ void setup(painlessMesh* mesh, Scheduler* userScheduler) override {
24
+ mesh_ = mesh;
25
+ userScheduler_ = userScheduler;
26
+
27
+ // Same configuration as basic.ino
28
+ const char* MESH_PREFIX = "whateverYouLike";
29
+ const char* MESH_PASSWORD = "somethingSneaky";
30
+ const uint16_t MESH_PORT = 5555;
31
+
32
+ // Set debug message types (same as basic.ino)
33
+ mesh_->setDebugMsgTypes(ERROR | STARTUP | CONNECTION);
34
+
35
+ // Initialize mesh
36
+ mesh_->init(MESH_PREFIX, MESH_PASSWORD, userScheduler_, MESH_PORT);
37
+
38
+ // Register callbacks
39
+ mesh_->onReceive([this](uint32_t from, String& msg) {
40
+ this->receivedCallback(from, msg);
41
+ });
42
+
43
+ mesh_->onNewConnection([this](uint32_t nodeId) {
44
+ this->newConnectionCallback(nodeId);
45
+ });
46
+
47
+ mesh_->onChangedConnections([this]() {
48
+ this->changedConnectionCallback();
49
+ });
50
+
51
+ mesh_->onNodeTimeAdjusted([this](int32_t offset) {
52
+ this->nodeTimeAdjustedCallback(offset);
53
+ });
54
+
55
+ // Setup periodic message sending task
56
+ taskSendMessage_ = new Task(TASK_SECOND * 1, TASK_FOREVER,
57
+ [this]() { this->sendMessage(); });
58
+ userScheduler_->addTask(*taskSendMessage_);
59
+ taskSendMessage_->enable();
60
+ }
61
+
62
+ void loop() override {
63
+ if (mesh_) {
64
+ mesh_->update();
65
+ }
66
+ }
67
+
68
+ const char* getName() const override {
69
+ return "BasicExample";
70
+ }
71
+
72
+ private:
73
+ painlessMesh* mesh_;
74
+ Scheduler* userScheduler_;
75
+ Task* taskSendMessage_ = nullptr;
76
+
77
+ void sendMessage() {
78
+ if (!mesh_) return;
79
+
80
+ String msg = "Hello from node ";
81
+ msg += mesh_->getNodeId();
82
+ mesh_->sendBroadcast(msg);
83
+
84
+ // Random interval between 1 and 5 seconds (same as basic.ino)
85
+ taskSendMessage_->setInterval(random(TASK_SECOND * 1, TASK_SECOND * 5));
86
+ }
87
+
88
+ void receivedCallback(uint32_t from, String& msg) {
89
+ // In simulator, we can log to stdout or collect metrics
90
+ printf("Node %u: Received from %u msg=%s\n",
91
+ mesh_->getNodeId(), from, msg.c_str());
92
+
93
+ // Track metrics for validation
94
+ messagesReceived_++;
95
+ }
96
+
97
+ void newConnectionCallback(uint32_t nodeId) {
98
+ printf("Node %u: New Connection, nodeId = %u\n",
99
+ mesh_->getNodeId(), nodeId);
100
+ connectionsEstablished_++;
101
+ }
102
+
103
+ void changedConnectionCallback() {
104
+ printf("Node %u: Changed connections\n", mesh_->getNodeId());
105
+ topologyChanges_++;
106
+ }
107
+
108
+ void nodeTimeAdjustedCallback(int32_t offset) {
109
+ printf("Node %u: Adjusted time %u. Offset = %d\n",
110
+ mesh_->getNodeId(), mesh_->getNodeTime(), offset);
111
+ }
112
+
113
+ // Metrics for test validation
114
+ uint32_t messagesReceived_ = 0;
115
+ uint32_t connectionsEstablished_ = 0;
116
+ uint32_t topologyChanges_ = 0;
117
+ };
@@ -0,0 +1,81 @@
1
+ # Test scenario for basic.ino example
2
+ # This validates that the basic example works correctly with multiple nodes
3
+
4
+ simulation:
5
+ name: "Basic Example - Mesh Formation and Broadcasting"
6
+ description: "Validates basic.ino with 10 nodes forming a mesh and exchanging messages"
7
+ duration: 60 # seconds
8
+ time_scale: 1.0
9
+ seed: 12345
10
+
11
+ network:
12
+ default_latency:
13
+ min_ms: 1
14
+ max_ms: 10
15
+ default_packet_loss:
16
+ rate: 0.0
17
+ default_bandwidth:
18
+ bits_per_second: 1000000
19
+
20
+ nodes:
21
+ # Create individual nodes - simulator doesn't support template/count syntax
22
+ - id: "node1"
23
+ firmware: "SimpleBroadcast"
24
+ config:
25
+ mesh_prefix: "whateverYouLike"
26
+ mesh_password: "somethingSneaky"
27
+ mesh_port: 5555
28
+ broadcast_interval: "1000"
29
+ broadcast_message: "Hello from node"
30
+
31
+ - id: "node2"
32
+ firmware: "SimpleBroadcast"
33
+ config:
34
+ mesh_prefix: "whateverYouLike"
35
+ mesh_password: "somethingSneaky"
36
+ mesh_port: 5555
37
+ broadcast_interval: "1000"
38
+ broadcast_message: "Hello from node"
39
+
40
+ - id: "node3"
41
+ firmware: "SimpleBroadcast"
42
+ config:
43
+ mesh_prefix: "whateverYouLike"
44
+ mesh_password: "somethingSneaky"
45
+ mesh_port: 5555
46
+ broadcast_interval: "1000"
47
+ broadcast_message: "Hello from node"
48
+
49
+ - id: "node4"
50
+ firmware: "SimpleBroadcast"
51
+ config:
52
+ mesh_prefix: "whateverYouLike"
53
+ mesh_password: "somethingSneaky"
54
+ mesh_port: 5555
55
+ broadcast_interval: "1000"
56
+ broadcast_message: "Hello from node"
57
+
58
+ - id: "node5"
59
+ firmware: "SimpleBroadcast"
60
+ config:
61
+ mesh_prefix: "whateverYouLike"
62
+ mesh_password: "somethingSneaky"
63
+ mesh_port: 5555
64
+ broadcast_interval: "1000"
65
+ broadcast_message: "Hello from node"
66
+
67
+ topology:
68
+ type: "mesh" # Full mesh - all nodes connected
69
+
70
+ # No events - nodes start automatically
71
+ events: []
72
+
73
+ # Metrics to collect
74
+ metrics:
75
+ output: "basic_test_results.csv"
76
+ interval: 5
77
+ collect:
78
+ - "messages_sent"
79
+ - "messages_received"
80
+ - "bytes_sent"
81
+ - "bytes_received"