@alteriom/painlessmesh 1.6.1 → 1.7.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.
- package/CHANGELOG.md +380 -143
- package/LICENSE +674 -674
- package/README.md +477 -434
- package/RELEASE_GUIDE.md +504 -418
- package/docs/DOCUMENTATION_MIGRATION_PLAN.md +175 -175
- package/docs/IMPLEMENTATION_PLAN_MESH_TOPOLOGY.md +1062 -0
- package/docs/MESH_TOPOLOGY_GUIDE.md +992 -0
- package/docs/MESH_TOPOLOGY_PROGRESS.md +422 -0
- package/docs/MQTT_BRIDGE_COMMANDS.md +894 -0
- package/docs/MQTT_BRIDGE_IMPLEMENTATION_SUMMARY.md +324 -0
- package/docs/MQTT_COMMAND_SCHEMA_PROPOSAL.md +576 -0
- package/docs/MQTT_SCHEMA_COMPLIANCE.md +285 -0
- package/docs/MQTT_SCHEMA_PROPOSALS.md +446 -0
- package/docs/MQTT_SCHEMA_REVIEW.md +690 -0
- package/docs/OTA_COMMANDS_REFERENCE.md +554 -0
- package/docs/PHASE1_GUIDE.md +349 -0
- package/docs/PHASE2_GUIDE.md +543 -0
- package/docs/README.md +77 -70
- package/docs/SCHEMA_VALIDATION_CHECKLIST.md +222 -0
- package/docs/alteriom/overview.md +507 -507
- package/docs/api/core-api.md +606 -606
- package/docs/architecture/mesh-architecture.md +378 -378
- package/docs/architecture/plugin-system.md +516 -516
- package/docs/getting-started/first-mesh.md +409 -409
- package/docs/getting-started/installation.md +274 -274
- package/docs/getting-started/quickstart.md +157 -157
- package/docs/improvements/FEATURE_PROPOSALS.md +337 -0
- package/docs/improvements/PHASE1_IMPLEMENTATION.md +325 -0
- package/docs/improvements/PHASE2_IMPLEMENTATION.md +567 -0
- package/docs/improvements/README.md +86 -68
- package/docs/improvements/ota-and-status-enhancements.md +911 -0
- package/docs/improvements/ota-status-architecture-diagrams.md +658 -0
- package/docs/improvements/ota-status-quick-reference.md +284 -0
- package/docs/platformio-publishing.md +255 -0
- package/docs/platformio-setup-summary.md +121 -0
- package/docs/troubleshooting/common-issues.md +520 -520
- package/docs/troubleshooting/faq.md +472 -472
- package/docs/tutorials/basic-examples.md +717 -717
- package/docs/wiki/API-Reference.md +245 -245
- package/docs/wiki/Complete-Documentation.md +122 -122
- package/examples/alteriom/README.md +139 -81
- package/examples/alteriom/alteriom.ino +186 -185
- package/examples/alteriom/alteriom_sensor_package.hpp +240 -127
- package/examples/alteriom/platformio.ini +24 -24
- package/examples/alteriomImproved/alteriom_sensor_package.hpp +224 -0
- package/examples/{alteriom → alteriomImproved}/improved_sensor_node.ino +245 -245
- package/examples/alteriomImproved/platformio.ini +25 -0
- package/examples/alteriomPhase1/alteriom_sensor_package.hpp +224 -0
- package/examples/alteriomPhase1/phase1_features.ino +242 -0
- package/examples/alteriomPhase1/platformio.ini +25 -0
- package/examples/alteriomPhase2/alteriom_sensor_package.hpp +224 -0
- package/examples/alteriomPhase2/phase2_features.ino +186 -0
- package/examples/alteriomPhase2/platformio.ini +25 -0
- package/examples/{alteriom → alteriomSensorNode}/alteriom_sensor_node.ino +183 -183
- package/examples/alteriomSensorNode/alteriom_sensor_package.hpp +224 -0
- package/examples/alteriomSensorNode/platformio.ini +25 -0
- package/examples/basic/basic.ino +66 -66
- package/examples/basic/platformio.ini +25 -25
- package/examples/bridge/bridge.ino +51 -51
- package/examples/bridge/mesh_event_publisher.hpp +253 -0
- package/examples/bridge/mesh_topology_reporter.hpp +303 -0
- package/examples/bridge/mqtt_command_bridge.hpp +459 -0
- package/examples/bridge/mqtt_status_bridge.hpp +519 -0
- package/examples/bridge/platformio.ini +25 -25
- package/examples/echoNode/echoNode.ino +33 -33
- package/examples/echoNode/platformio.ini +25 -25
- package/examples/logClient/logClient.ino +109 -109
- package/examples/logClient/platformio.ini +25 -25
- package/examples/logServer/logServer.ino +81 -81
- package/examples/logServer/platformio.ini +25 -25
- package/examples/meshCommandNode/alteriom_sensor_package.hpp +235 -0
- package/examples/meshCommandNode/meshCommandNode.ino +263 -0
- package/examples/meshCommandNode/platformio.ini +25 -0
- package/examples/mqttBridge/mqttBridge.ino +118 -118
- package/examples/mqttBridge/platformio.ini +26 -26
- package/examples/mqttCommandBridge/alteriom_sensor_package.hpp +235 -0
- package/examples/mqttCommandBridge/mesh_event_publisher.hpp +253 -0
- package/examples/mqttCommandBridge/mesh_topology_reporter.hpp +303 -0
- package/examples/mqttCommandBridge/mqttCommandBridge.ino +252 -0
- package/examples/mqttCommandBridge/mqtt_command_bridge.hpp +459 -0
- package/examples/mqttCommandBridge/platformio.ini +26 -0
- package/examples/mqttStatusBridge/mqttStatusBridge.ino +216 -0
- package/examples/mqttStatusBridge/mqtt_status_bridge.hpp +522 -0
- package/examples/mqttStatusBridge/platformio.ini +26 -0
- package/examples/mqttTopologyTest/README.md +467 -0
- package/examples/mqttTopologyTest/mqttTopologyTest.ino +748 -0
- package/examples/mqttTopologyTest/platformio.ini +26 -0
- package/examples/namedMesh/namedMesh.ino +97 -97
- package/examples/namedMesh/platformio.ini +25 -25
- package/examples/otaReceiver/otaReceiver.ino +79 -79
- package/examples/otaReceiver/platformio.ini +25 -25
- package/examples/otaSender/otaSender.ino +160 -151
- package/examples/otaSender/platformio.ini +25 -25
- package/examples/startHere/platformio.ini +25 -25
- package/examples/startHere/startHere.ino +159 -159
- package/examples/webServer/platformio.ini +27 -27
- package/examples/webServer/webServer.ino +89 -89
- package/keywords.txt +48 -48
- package/library.json +55 -34
- package/library.properties +10 -10
- package/package.json +86 -78
- package/src/AlteriomPainlessMesh.h +97 -97
- package/src/arduino/wifi.hpp +365 -365
- package/src/boost/asynctcp.hpp +279 -279
- package/src/painlessMesh.h +70 -70
- package/src/painlessMeshSTA.cpp +236 -236
- package/src/painlessMeshSTA.h +58 -58
- package/src/painlessTaskOptions.h +4 -4
- package/src/painlessmesh/base64.hpp +111 -111
- package/src/painlessmesh/buffer.hpp +229 -229
- package/src/painlessmesh/callback.hpp +91 -91
- package/src/painlessmesh/configuration.hpp +77 -77
- package/src/painlessmesh/connection.hpp +192 -192
- package/src/painlessmesh/layout.hpp +188 -188
- package/src/painlessmesh/logger.hpp +158 -158
- package/src/painlessmesh/memory.hpp +119 -119
- package/src/painlessmesh/mesh.hpp +761 -560
- package/src/painlessmesh/metrics.hpp +322 -322
- package/src/painlessmesh/ntp.hpp +263 -263
- package/src/painlessmesh/ota.hpp +582 -553
- package/src/painlessmesh/plugin.hpp +188 -188
- package/src/painlessmesh/protocol.hpp +813 -813
- package/src/painlessmesh/router.hpp +322 -322
- package/src/painlessmesh/tcp.hpp +71 -71
- package/src/painlessmesh/validation.hpp +238 -238
- package/src/plugin/performance.hpp +214 -214
- package/src/plugin/remote.hpp +64 -64
- package/src/scheduler.cpp +10 -10
- package/src/wifi.cpp +2 -2
|
@@ -1,246 +1,246 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Improved Sensor Node Example with Validation and Metrics
|
|
3
|
-
*
|
|
4
|
-
* This example demonstrates the new security and performance improvements
|
|
5
|
-
* in painlessMesh including input validation, rate limiting, and performance
|
|
6
|
-
* metrics.
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
#include "alteriom_sensor_package.hpp"
|
|
10
|
-
#include "painlessMesh.h"
|
|
11
|
-
|
|
12
|
-
// Note: The improvement modules (validation, metrics, memory) are automatically
|
|
13
|
-
// included through painlessMesh.h when the corresponding features are enabled
|
|
14
|
-
|
|
15
|
-
#define MESH_PREFIX "AlteriomImprovedMesh"
|
|
16
|
-
#define MESH_PASSWORD "securePassword123"
|
|
17
|
-
#define MESH_PORT 5555
|
|
18
|
-
|
|
19
|
-
#define SENSOR_TASK_INTERVAL 30000 // Send sensor data every 30 seconds
|
|
20
|
-
#define METRICS_TASK_INTERVAL 60000 // Report metrics every minute
|
|
21
|
-
#define VALIDATION_ENABLED true // Enable message validation
|
|
22
|
-
|
|
23
|
-
Scheduler userScheduler;
|
|
24
|
-
painlessMesh mesh;
|
|
25
|
-
|
|
26
|
-
// New improvement features
|
|
27
|
-
painlessmesh::validation::MessageValidator validator;
|
|
28
|
-
painlessmesh::validation::RateLimiter rateLimiter(5, 1000); // 5 msgs/sec max
|
|
29
|
-
painlessmesh::metrics::MetricsCollector metrics;
|
|
30
|
-
painlessmesh::memory::ObjectPool<alteriom::SensorPackage> sensorPool(5);
|
|
31
|
-
|
|
32
|
-
// Task declarations
|
|
33
|
-
Task sensorTask(SENSOR_TASK_INTERVAL, TASK_FOREVER, &sendSensorData);
|
|
34
|
-
Task metricsTask(METRICS_TASK_INTERVAL, TASK_FOREVER, &reportMetrics);
|
|
35
|
-
|
|
36
|
-
void setup() {
|
|
37
|
-
Serial.begin(115200);
|
|
38
|
-
|
|
39
|
-
// Configure validation with custom settings
|
|
40
|
-
painlessmesh::validation::ValidationConfig config;
|
|
41
|
-
config.max_message_size = 4096; // Smaller max size for sensors
|
|
42
|
-
config.max_string_length = 256; // Reasonable string limits
|
|
43
|
-
config.strict_type_checking = true; // Enable strict validation
|
|
44
|
-
validator.set_config(config);
|
|
45
|
-
|
|
46
|
-
mesh.setDebugMsgTypes(ERROR | STARTUP | CONNECTION);
|
|
47
|
-
mesh.init(MESH_PREFIX, MESH_PASSWORD, &userScheduler, MESH_PORT);
|
|
48
|
-
mesh.onReceive(&receivedCallback);
|
|
49
|
-
mesh.onNewConnection(&newConnectionCallback);
|
|
50
|
-
mesh.onChangedConnections(&changedConnectionCallback);
|
|
51
|
-
mesh.onNodeTimeAdjusted(&nodeTimeAdjustedCallback);
|
|
52
|
-
|
|
53
|
-
userScheduler.addTask(sensorTask);
|
|
54
|
-
userScheduler.addTask(metricsTask);
|
|
55
|
-
sensorTask.enable();
|
|
56
|
-
metricsTask.enable();
|
|
57
|
-
|
|
58
|
-
Serial.println("Enhanced Sensor Node Started");
|
|
59
|
-
Serial.println(
|
|
60
|
-
"Features: Validation, Rate Limiting, Metrics, Memory Optimization");
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
void loop() {
|
|
64
|
-
mesh.update();
|
|
65
|
-
metrics.update(); // Update performance metrics
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
void sendSensorData() {
|
|
69
|
-
// Use object pool for memory efficiency
|
|
70
|
-
auto sensorPackage = sensorPool.acquire();
|
|
71
|
-
|
|
72
|
-
// Simulate sensor readings
|
|
73
|
-
sensorPackage->temperature = 20.0 + random(-5, 15); // 15-35°C
|
|
74
|
-
sensorPackage->humidity = 40.0 + random(0, 40); // 40-80%
|
|
75
|
-
sensorPackage->pressure = 1013.25 + random(-50, 50); // Near sea level
|
|
76
|
-
sensorPackage->batteryLevel = 85 + random(-10, 15); // Battery simulation
|
|
77
|
-
sensorPackage->sensorId = mesh.getNodeId();
|
|
78
|
-
sensorPackage->timestamp = mesh.getNodeTime();
|
|
79
|
-
|
|
80
|
-
// Convert to message variant
|
|
81
|
-
painlessmesh::protocol::Variant variant(sensorPackage.get());
|
|
82
|
-
String message;
|
|
83
|
-
variant.printTo(message);
|
|
84
|
-
|
|
85
|
-
// Validate message before sending (optional security check)
|
|
86
|
-
if (VALIDATION_ENABLED) {
|
|
87
|
-
JsonDocument doc;
|
|
88
|
-
deserializeJson(doc, message);
|
|
89
|
-
JsonObject obj = doc.as<JsonObject>();
|
|
90
|
-
|
|
91
|
-
auto result = validator.validate_message(obj, message.length());
|
|
92
|
-
if (result != painlessmesh::validation::ValidationResult::VALID) {
|
|
93
|
-
Serial.printf("Message validation failed: %s\n",
|
|
94
|
-
validator.get_error_message(result));
|
|
95
|
-
sensorPool.release(std::move(sensorPackage)); // Return to pool
|
|
96
|
-
return;
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
// Send the message
|
|
101
|
-
bool sent = mesh.sendBroadcast(message);
|
|
102
|
-
if (sent) {
|
|
103
|
-
metrics.message_stats().record_sent_message(message.length());
|
|
104
|
-
Serial.printf(
|
|
105
|
-
"Sensor data sent: T=%.1f°C, H=%.1f%%, P=%.1f hPa, Batt=%d%%\n",
|
|
106
|
-
sensorPackage->temperature, sensorPackage->humidity,
|
|
107
|
-
sensorPackage->pressure, sensorPackage->batteryLevel);
|
|
108
|
-
} else {
|
|
109
|
-
metrics.message_stats().record_dropped_message();
|
|
110
|
-
Serial.println("Failed to send sensor data - network congestion");
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
// Return object to pool for reuse
|
|
114
|
-
sensorPool.release(std::move(sensorPackage));
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
void reportMetrics() {
|
|
118
|
-
String metricsJson = metrics.generate_status_json();
|
|
119
|
-
Serial.println("=== Performance Metrics ===");
|
|
120
|
-
Serial.println(metricsJson);
|
|
121
|
-
|
|
122
|
-
// Check for performance issues
|
|
123
|
-
if (metrics.has_performance_issues()) {
|
|
124
|
-
Serial.println("WARNING: Performance issues detected!");
|
|
125
|
-
|
|
126
|
-
if (metrics.memory_stats().is_memory_critical()) {
|
|
127
|
-
Serial.println("- Critical memory shortage");
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
if (metrics.message_stats().packet_loss_rate() > 0.1) {
|
|
131
|
-
Serial.printf("- High packet loss: %.1f%%\n",
|
|
132
|
-
metrics.message_stats().packet_loss_rate() * 100);
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
if (metrics.topology_stats().connection_stability() < 0.8) {
|
|
136
|
-
Serial.printf("- Poor connection stability: %.1f%%\n",
|
|
137
|
-
metrics.topology_stats().connection_stability() * 100);
|
|
138
|
-
}
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
Serial.println("===========================");
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
void receivedCallback(uint32_t from, String& msg) {
|
|
145
|
-
// Record metrics
|
|
146
|
-
metrics.message_stats().record_received_message(msg.length());
|
|
147
|
-
|
|
148
|
-
// Rate limiting check
|
|
149
|
-
if (!rateLimiter.allow_message(from)) {
|
|
150
|
-
Serial.printf("Rate limit exceeded for node %u, dropping message\n", from);
|
|
151
|
-
metrics.message_stats().record_dropped_message();
|
|
152
|
-
return;
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
// Validate incoming message
|
|
156
|
-
if (VALIDATION_ENABLED) {
|
|
157
|
-
JsonDocument doc;
|
|
158
|
-
DeserializationError error = deserializeJson(doc, msg);
|
|
159
|
-
|
|
160
|
-
if (error) {
|
|
161
|
-
Serial.printf("JSON parsing failed from %u: %s\n", from, error.c_str());
|
|
162
|
-
metrics.message_stats().record_parse_error();
|
|
163
|
-
return;
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
JsonObject obj = doc.as<JsonObject>();
|
|
167
|
-
auto result = validator.validate_message(obj, msg.length());
|
|
168
|
-
|
|
169
|
-
if (result != painlessmesh::validation::ValidationResult::VALID) {
|
|
170
|
-
Serial.printf("Message validation failed from %u: %s\n", from,
|
|
171
|
-
validator.get_error_message(result));
|
|
172
|
-
metrics.message_stats().record_validation_error();
|
|
173
|
-
return;
|
|
174
|
-
}
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
// Process the validated message
|
|
178
|
-
JsonDocument doc;
|
|
179
|
-
deserializeJson(doc, msg);
|
|
180
|
-
JsonObject obj = doc.as<JsonObject>();
|
|
181
|
-
uint8_t msgType = obj["type"];
|
|
182
|
-
|
|
183
|
-
switch (msgType) {
|
|
184
|
-
case 200: // SensorPackage
|
|
185
|
-
handleSensorData(alteriom::SensorPackage(obj), from);
|
|
186
|
-
break;
|
|
187
|
-
case 201: // CommandPackage
|
|
188
|
-
handleCommand(alteriom::CommandPackage(obj), from);
|
|
189
|
-
break;
|
|
190
|
-
case 202: // StatusPackage
|
|
191
|
-
handleStatus(alteriom::StatusPackage(obj), from);
|
|
192
|
-
break;
|
|
193
|
-
default:
|
|
194
|
-
Serial.printf("Unknown message type %d from %u\n", msgType, from);
|
|
195
|
-
}
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
void handleSensorData(alteriom::SensorPackage sensorData, uint32_t from) {
|
|
199
|
-
Serial.printf("Sensor data from %u: T=%.1f°C, H=%.1f%%, P=%.1f hPa\n", from,
|
|
200
|
-
sensorData.temperature, sensorData.humidity,
|
|
201
|
-
sensorData.pressure);
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
void handleCommand(alteriom::CommandPackage command, uint32_t from) {
|
|
205
|
-
Serial.printf("Command from %u: cmd=%d, target=%u\n", from, command.command,
|
|
206
|
-
command.targetDevice);
|
|
207
|
-
|
|
208
|
-
// Example: Handle LED control command
|
|
209
|
-
if (command.command == 1 && command.targetDevice == mesh.getNodeId()) {
|
|
210
|
-
digitalWrite(LED_BUILTIN,
|
|
211
|
-
command.parameters.indexOf("ON") >= 0 ? HIGH : LOW);
|
|
212
|
-
Serial.println("LED command executed");
|
|
213
|
-
}
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
void handleStatus(alteriom::StatusPackage status, uint32_t from) {
|
|
217
|
-
Serial.printf("Status from %u: status=%d, uptime=%u, mem=%u\n", from,
|
|
218
|
-
status.deviceStatus, status.uptime, status.freeMemory);
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
void newConnectionCallback(uint32_t nodeId) {
|
|
222
|
-
Serial.printf("New connection: %u\n", nodeId);
|
|
223
|
-
|
|
224
|
-
// Update topology metrics
|
|
225
|
-
auto nodeList = mesh.getNodeList();
|
|
226
|
-
metrics.topology_stats().record_topology_change(
|
|
227
|
-
nodeList.size() + 1, // +1 for this node
|
|
228
|
-
mesh.connectionCount(),
|
|
229
|
-
0 // Max hops calculation not implemented in this example
|
|
230
|
-
);
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
void changedConnectionCallback() {
|
|
234
|
-
Serial.printf("Changed connections\n");
|
|
235
|
-
|
|
236
|
-
// Update topology metrics
|
|
237
|
-
auto nodeList = mesh.getNodeList();
|
|
238
|
-
metrics.topology_stats().record_topology_change(
|
|
239
|
-
nodeList.size() + 1, mesh.connectionCount(),
|
|
240
|
-
0 // Max hops calculation not implemented in this example
|
|
241
|
-
);
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
void nodeTimeAdjustedCallback(int32_t offset) {
|
|
245
|
-
Serial.printf("Adjusted time %u. Offset = %d\n", mesh.getNodeTime(), offset);
|
|
1
|
+
/**
|
|
2
|
+
* Improved Sensor Node Example with Validation and Metrics
|
|
3
|
+
*
|
|
4
|
+
* This example demonstrates the new security and performance improvements
|
|
5
|
+
* in painlessMesh including input validation, rate limiting, and performance
|
|
6
|
+
* metrics.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
#include "alteriom_sensor_package.hpp"
|
|
10
|
+
#include "painlessMesh.h"
|
|
11
|
+
|
|
12
|
+
// Note: The improvement modules (validation, metrics, memory) are automatically
|
|
13
|
+
// included through painlessMesh.h when the corresponding features are enabled
|
|
14
|
+
|
|
15
|
+
#define MESH_PREFIX "AlteriomImprovedMesh"
|
|
16
|
+
#define MESH_PASSWORD "securePassword123"
|
|
17
|
+
#define MESH_PORT 5555
|
|
18
|
+
|
|
19
|
+
#define SENSOR_TASK_INTERVAL 30000 // Send sensor data every 30 seconds
|
|
20
|
+
#define METRICS_TASK_INTERVAL 60000 // Report metrics every minute
|
|
21
|
+
#define VALIDATION_ENABLED true // Enable message validation
|
|
22
|
+
|
|
23
|
+
Scheduler userScheduler;
|
|
24
|
+
painlessMesh mesh;
|
|
25
|
+
|
|
26
|
+
// New improvement features
|
|
27
|
+
painlessmesh::validation::MessageValidator validator;
|
|
28
|
+
painlessmesh::validation::RateLimiter rateLimiter(5, 1000); // 5 msgs/sec max
|
|
29
|
+
painlessmesh::metrics::MetricsCollector metrics;
|
|
30
|
+
painlessmesh::memory::ObjectPool<alteriom::SensorPackage> sensorPool(5);
|
|
31
|
+
|
|
32
|
+
// Task declarations
|
|
33
|
+
Task sensorTask(SENSOR_TASK_INTERVAL, TASK_FOREVER, &sendSensorData);
|
|
34
|
+
Task metricsTask(METRICS_TASK_INTERVAL, TASK_FOREVER, &reportMetrics);
|
|
35
|
+
|
|
36
|
+
void setup() {
|
|
37
|
+
Serial.begin(115200);
|
|
38
|
+
|
|
39
|
+
// Configure validation with custom settings
|
|
40
|
+
painlessmesh::validation::ValidationConfig config;
|
|
41
|
+
config.max_message_size = 4096; // Smaller max size for sensors
|
|
42
|
+
config.max_string_length = 256; // Reasonable string limits
|
|
43
|
+
config.strict_type_checking = true; // Enable strict validation
|
|
44
|
+
validator.set_config(config);
|
|
45
|
+
|
|
46
|
+
mesh.setDebugMsgTypes(ERROR | STARTUP | CONNECTION);
|
|
47
|
+
mesh.init(MESH_PREFIX, MESH_PASSWORD, &userScheduler, MESH_PORT);
|
|
48
|
+
mesh.onReceive(&receivedCallback);
|
|
49
|
+
mesh.onNewConnection(&newConnectionCallback);
|
|
50
|
+
mesh.onChangedConnections(&changedConnectionCallback);
|
|
51
|
+
mesh.onNodeTimeAdjusted(&nodeTimeAdjustedCallback);
|
|
52
|
+
|
|
53
|
+
userScheduler.addTask(sensorTask);
|
|
54
|
+
userScheduler.addTask(metricsTask);
|
|
55
|
+
sensorTask.enable();
|
|
56
|
+
metricsTask.enable();
|
|
57
|
+
|
|
58
|
+
Serial.println("Enhanced Sensor Node Started");
|
|
59
|
+
Serial.println(
|
|
60
|
+
"Features: Validation, Rate Limiting, Metrics, Memory Optimization");
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
void loop() {
|
|
64
|
+
mesh.update();
|
|
65
|
+
metrics.update(); // Update performance metrics
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
void sendSensorData() {
|
|
69
|
+
// Use object pool for memory efficiency
|
|
70
|
+
auto sensorPackage = sensorPool.acquire();
|
|
71
|
+
|
|
72
|
+
// Simulate sensor readings
|
|
73
|
+
sensorPackage->temperature = 20.0 + random(-5, 15); // 15-35°C
|
|
74
|
+
sensorPackage->humidity = 40.0 + random(0, 40); // 40-80%
|
|
75
|
+
sensorPackage->pressure = 1013.25 + random(-50, 50); // Near sea level
|
|
76
|
+
sensorPackage->batteryLevel = 85 + random(-10, 15); // Battery simulation
|
|
77
|
+
sensorPackage->sensorId = mesh.getNodeId();
|
|
78
|
+
sensorPackage->timestamp = mesh.getNodeTime();
|
|
79
|
+
|
|
80
|
+
// Convert to message variant
|
|
81
|
+
painlessmesh::protocol::Variant variant(sensorPackage.get());
|
|
82
|
+
String message;
|
|
83
|
+
variant.printTo(message);
|
|
84
|
+
|
|
85
|
+
// Validate message before sending (optional security check)
|
|
86
|
+
if (VALIDATION_ENABLED) {
|
|
87
|
+
JsonDocument doc;
|
|
88
|
+
deserializeJson(doc, message);
|
|
89
|
+
JsonObject obj = doc.as<JsonObject>();
|
|
90
|
+
|
|
91
|
+
auto result = validator.validate_message(obj, message.length());
|
|
92
|
+
if (result != painlessmesh::validation::ValidationResult::VALID) {
|
|
93
|
+
Serial.printf("Message validation failed: %s\n",
|
|
94
|
+
validator.get_error_message(result));
|
|
95
|
+
sensorPool.release(std::move(sensorPackage)); // Return to pool
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// Send the message
|
|
101
|
+
bool sent = mesh.sendBroadcast(message);
|
|
102
|
+
if (sent) {
|
|
103
|
+
metrics.message_stats().record_sent_message(message.length());
|
|
104
|
+
Serial.printf(
|
|
105
|
+
"Sensor data sent: T=%.1f°C, H=%.1f%%, P=%.1f hPa, Batt=%d%%\n",
|
|
106
|
+
sensorPackage->temperature, sensorPackage->humidity,
|
|
107
|
+
sensorPackage->pressure, sensorPackage->batteryLevel);
|
|
108
|
+
} else {
|
|
109
|
+
metrics.message_stats().record_dropped_message();
|
|
110
|
+
Serial.println("Failed to send sensor data - network congestion");
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// Return object to pool for reuse
|
|
114
|
+
sensorPool.release(std::move(sensorPackage));
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
void reportMetrics() {
|
|
118
|
+
String metricsJson = metrics.generate_status_json();
|
|
119
|
+
Serial.println("=== Performance Metrics ===");
|
|
120
|
+
Serial.println(metricsJson);
|
|
121
|
+
|
|
122
|
+
// Check for performance issues
|
|
123
|
+
if (metrics.has_performance_issues()) {
|
|
124
|
+
Serial.println("WARNING: Performance issues detected!");
|
|
125
|
+
|
|
126
|
+
if (metrics.memory_stats().is_memory_critical()) {
|
|
127
|
+
Serial.println("- Critical memory shortage");
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
if (metrics.message_stats().packet_loss_rate() > 0.1) {
|
|
131
|
+
Serial.printf("- High packet loss: %.1f%%\n",
|
|
132
|
+
metrics.message_stats().packet_loss_rate() * 100);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
if (metrics.topology_stats().connection_stability() < 0.8) {
|
|
136
|
+
Serial.printf("- Poor connection stability: %.1f%%\n",
|
|
137
|
+
metrics.topology_stats().connection_stability() * 100);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
Serial.println("===========================");
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
void receivedCallback(uint32_t from, String& msg) {
|
|
145
|
+
// Record metrics
|
|
146
|
+
metrics.message_stats().record_received_message(msg.length());
|
|
147
|
+
|
|
148
|
+
// Rate limiting check
|
|
149
|
+
if (!rateLimiter.allow_message(from)) {
|
|
150
|
+
Serial.printf("Rate limit exceeded for node %u, dropping message\n", from);
|
|
151
|
+
metrics.message_stats().record_dropped_message();
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
// Validate incoming message
|
|
156
|
+
if (VALIDATION_ENABLED) {
|
|
157
|
+
JsonDocument doc;
|
|
158
|
+
DeserializationError error = deserializeJson(doc, msg);
|
|
159
|
+
|
|
160
|
+
if (error) {
|
|
161
|
+
Serial.printf("JSON parsing failed from %u: %s\n", from, error.c_str());
|
|
162
|
+
metrics.message_stats().record_parse_error();
|
|
163
|
+
return;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
JsonObject obj = doc.as<JsonObject>();
|
|
167
|
+
auto result = validator.validate_message(obj, msg.length());
|
|
168
|
+
|
|
169
|
+
if (result != painlessmesh::validation::ValidationResult::VALID) {
|
|
170
|
+
Serial.printf("Message validation failed from %u: %s\n", from,
|
|
171
|
+
validator.get_error_message(result));
|
|
172
|
+
metrics.message_stats().record_validation_error();
|
|
173
|
+
return;
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
// Process the validated message
|
|
178
|
+
JsonDocument doc;
|
|
179
|
+
deserializeJson(doc, msg);
|
|
180
|
+
JsonObject obj = doc.as<JsonObject>();
|
|
181
|
+
uint8_t msgType = obj["type"];
|
|
182
|
+
|
|
183
|
+
switch (msgType) {
|
|
184
|
+
case 200: // SensorPackage
|
|
185
|
+
handleSensorData(alteriom::SensorPackage(obj), from);
|
|
186
|
+
break;
|
|
187
|
+
case 201: // CommandPackage
|
|
188
|
+
handleCommand(alteriom::CommandPackage(obj), from);
|
|
189
|
+
break;
|
|
190
|
+
case 202: // StatusPackage
|
|
191
|
+
handleStatus(alteriom::StatusPackage(obj), from);
|
|
192
|
+
break;
|
|
193
|
+
default:
|
|
194
|
+
Serial.printf("Unknown message type %d from %u\n", msgType, from);
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
void handleSensorData(alteriom::SensorPackage sensorData, uint32_t from) {
|
|
199
|
+
Serial.printf("Sensor data from %u: T=%.1f°C, H=%.1f%%, P=%.1f hPa\n", from,
|
|
200
|
+
sensorData.temperature, sensorData.humidity,
|
|
201
|
+
sensorData.pressure);
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
void handleCommand(alteriom::CommandPackage command, uint32_t from) {
|
|
205
|
+
Serial.printf("Command from %u: cmd=%d, target=%u\n", from, command.command,
|
|
206
|
+
command.targetDevice);
|
|
207
|
+
|
|
208
|
+
// Example: Handle LED control command
|
|
209
|
+
if (command.command == 1 && command.targetDevice == mesh.getNodeId()) {
|
|
210
|
+
digitalWrite(LED_BUILTIN,
|
|
211
|
+
command.parameters.indexOf("ON") >= 0 ? HIGH : LOW);
|
|
212
|
+
Serial.println("LED command executed");
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
void handleStatus(alteriom::StatusPackage status, uint32_t from) {
|
|
217
|
+
Serial.printf("Status from %u: status=%d, uptime=%u, mem=%u\n", from,
|
|
218
|
+
status.deviceStatus, status.uptime, status.freeMemory);
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
void newConnectionCallback(uint32_t nodeId) {
|
|
222
|
+
Serial.printf("New connection: %u\n", nodeId);
|
|
223
|
+
|
|
224
|
+
// Update topology metrics
|
|
225
|
+
auto nodeList = mesh.getNodeList();
|
|
226
|
+
metrics.topology_stats().record_topology_change(
|
|
227
|
+
nodeList.size() + 1, // +1 for this node
|
|
228
|
+
mesh.connectionCount(),
|
|
229
|
+
0 // Max hops calculation not implemented in this example
|
|
230
|
+
);
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
void changedConnectionCallback() {
|
|
234
|
+
Serial.printf("Changed connections\n");
|
|
235
|
+
|
|
236
|
+
// Update topology metrics
|
|
237
|
+
auto nodeList = mesh.getNodeList();
|
|
238
|
+
metrics.topology_stats().record_topology_change(
|
|
239
|
+
nodeList.size() + 1, mesh.connectionCount(),
|
|
240
|
+
0 // Max hops calculation not implemented in this example
|
|
241
|
+
);
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
void nodeTimeAdjustedCallback(int32_t offset) {
|
|
245
|
+
Serial.printf("Adjusted time %u. Offset = %d\n", mesh.getNodeTime(), offset);
|
|
246
246
|
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
[platformio]
|
|
2
|
+
src_dir = .
|
|
3
|
+
|
|
4
|
+
[env]
|
|
5
|
+
lib_deps =
|
|
6
|
+
bblanchon/ArduinoJson
|
|
7
|
+
arkhipenko/TaskScheduler
|
|
8
|
+
|
|
9
|
+
[env:esp8266]
|
|
10
|
+
platform = espressif8266
|
|
11
|
+
board = nodemcuv2
|
|
12
|
+
framework = arduino
|
|
13
|
+
lib_extra_dirs = ../../ ; Load the local copy of painlessmesh. For your own example add painlessmesh to the lib_deps
|
|
14
|
+
lib_deps =
|
|
15
|
+
${env.lib_deps} ; Inherit common dependencies
|
|
16
|
+
esp32async/ESPAsyncTCP ; Only for ESP8266
|
|
17
|
+
|
|
18
|
+
[env:esp32]
|
|
19
|
+
platform = espressif32
|
|
20
|
+
board = esp32dev
|
|
21
|
+
framework = arduino
|
|
22
|
+
lib_extra_dirs = ../../ ; Load the local copy of painlessmesh. For your own example add painlessmesh to the lib_deps
|
|
23
|
+
lib_deps =
|
|
24
|
+
${env.lib_deps} ; Inherit common dependencies
|
|
25
|
+
esp32async/AsyncTCP
|