@alteriom/painlessmesh 1.6.1

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.
Files changed (80) hide show
  1. package/CHANGELOG.md +144 -0
  2. package/LICENSE +674 -0
  3. package/README.md +434 -0
  4. package/RELEASE_GUIDE.md +419 -0
  5. package/docs/DOCUMENTATION_MIGRATION_PLAN.md +176 -0
  6. package/docs/README.md +71 -0
  7. package/docs/alteriom/overview.md +508 -0
  8. package/docs/api/core-api.md +607 -0
  9. package/docs/architecture/mesh-architecture.md +379 -0
  10. package/docs/architecture/plugin-system.md +517 -0
  11. package/docs/getting-started/first-mesh.md +410 -0
  12. package/docs/getting-started/installation.md +275 -0
  13. package/docs/getting-started/quickstart.md +158 -0
  14. package/docs/improvements/README.md +69 -0
  15. package/docs/troubleshooting/common-issues.md +521 -0
  16. package/docs/troubleshooting/faq.md +473 -0
  17. package/docs/tutorials/basic-examples.md +718 -0
  18. package/docs/wiki/API-Reference.md +246 -0
  19. package/docs/wiki/Complete-Documentation.md +123 -0
  20. package/examples/alteriom/README.md +82 -0
  21. package/examples/alteriom/alteriom.ino +186 -0
  22. package/examples/alteriom/alteriom_sensor_node.ino +184 -0
  23. package/examples/alteriom/alteriom_sensor_package.hpp +128 -0
  24. package/examples/alteriom/improved_sensor_node.ino +246 -0
  25. package/examples/alteriom/platformio.ini +25 -0
  26. package/examples/basic/basic.ino +66 -0
  27. package/examples/basic/platformio.ini +25 -0
  28. package/examples/bridge/bridge.ino +51 -0
  29. package/examples/bridge/platformio.ini +25 -0
  30. package/examples/echoNode/echoNode.ino +33 -0
  31. package/examples/echoNode/platformio.ini +25 -0
  32. package/examples/logClient/logClient.ino +109 -0
  33. package/examples/logClient/platformio.ini +25 -0
  34. package/examples/logServer/logServer.ino +81 -0
  35. package/examples/logServer/platformio.ini +25 -0
  36. package/examples/mqttBridge/mqttBridge.ino +118 -0
  37. package/examples/mqttBridge/platformio.ini +26 -0
  38. package/examples/namedMesh/namedMesh.ino +97 -0
  39. package/examples/namedMesh/platformio.ini +25 -0
  40. package/examples/otaReceiver/otaReceiver.ino +79 -0
  41. package/examples/otaReceiver/platformio.ini +25 -0
  42. package/examples/otaSender/nodemcu32s_connections.JPG +0 -0
  43. package/examples/otaSender/otaSender.ino +151 -0
  44. package/examples/otaSender/platformio.ini +25 -0
  45. package/examples/startHere/platformio.ini +25 -0
  46. package/examples/startHere/startHere.ino +159 -0
  47. package/examples/webServer/platformio.ini +27 -0
  48. package/examples/webServer/webServer.ino +89 -0
  49. package/keywords.txt +49 -0
  50. package/library.json +34 -0
  51. package/library.properties +11 -0
  52. package/package.json +78 -0
  53. package/src/AlteriomPainlessMesh.h +98 -0
  54. package/src/arduino/wifi.hpp +365 -0
  55. package/src/boost/asynctcp.hpp +279 -0
  56. package/src/painlessMesh.h +70 -0
  57. package/src/painlessMeshSTA.cpp +236 -0
  58. package/src/painlessMeshSTA.h +58 -0
  59. package/src/painlessTaskOptions.h +4 -0
  60. package/src/painlessmesh/base64.hpp +111 -0
  61. package/src/painlessmesh/buffer.hpp +229 -0
  62. package/src/painlessmesh/callback.hpp +91 -0
  63. package/src/painlessmesh/configuration.hpp +77 -0
  64. package/src/painlessmesh/connection.hpp +192 -0
  65. package/src/painlessmesh/layout.hpp +188 -0
  66. package/src/painlessmesh/logger.hpp +158 -0
  67. package/src/painlessmesh/memory.hpp +120 -0
  68. package/src/painlessmesh/mesh.hpp +560 -0
  69. package/src/painlessmesh/metrics.hpp +323 -0
  70. package/src/painlessmesh/ntp.hpp +263 -0
  71. package/src/painlessmesh/ota.hpp +553 -0
  72. package/src/painlessmesh/plugin.hpp +188 -0
  73. package/src/painlessmesh/protocol.hpp +813 -0
  74. package/src/painlessmesh/router.hpp +322 -0
  75. package/src/painlessmesh/tcp.hpp +71 -0
  76. package/src/painlessmesh/validation.hpp +239 -0
  77. package/src/plugin/performance.hpp +214 -0
  78. package/src/plugin/remote.hpp +64 -0
  79. package/src/scheduler.cpp +10 -0
  80. package/src/wifi.cpp +2 -0
@@ -0,0 +1,184 @@
1
+ //************************************************************
2
+ // Alteriom Sensor Node Example
3
+ //
4
+ // This example demonstrates how to create an Alteriom-specific
5
+ // sensor node using custom packages with painlessMesh
6
+ //************************************************************
7
+
8
+ #include "alteriom_sensor_package.hpp"
9
+ #include "painlessMesh.h"
10
+
11
+ #define MESH_PREFIX "AlteriomMesh"
12
+ #define MESH_PASSWORD "somethingSneaky"
13
+ #define MESH_PORT 5555
14
+
15
+ Scheduler userScheduler; // to control your personal task
16
+ painlessMesh mesh;
17
+
18
+ // Alteriom package handlers
19
+ using namespace alteriom;
20
+
21
+ // User stub
22
+ void sendSensorData();
23
+ void handleIncomingPackage(uint32_t from, String& msg);
24
+ void handleCommandPackage(CommandPackage& cmd);
25
+ void handleStatusRequest();
26
+
27
+ Task taskSendSensorData(30000, TASK_FOREVER, &sendSensorData);
28
+ Task taskSendStatus(60000, TASK_FOREVER, &handleStatusRequest);
29
+
30
+ void setup() {
31
+ Serial.begin(115200);
32
+
33
+ // Initialize mesh
34
+ mesh.setDebugMsgTypes(ERROR | MESH_STATUS | CONNECTION | SYNC |
35
+ COMMUNICATION | GENERAL | MSG_TYPES | REMOTE);
36
+ mesh.init(MESH_PREFIX, MESH_PASSWORD, &userScheduler, MESH_PORT);
37
+ mesh.onReceive(&handleIncomingPackage);
38
+ mesh.onNewConnection(&newConnectionCallback);
39
+ mesh.onChangedConnections(&changedConnectionCallback);
40
+ mesh.onNodeTimeAdjusted(&nodeTimeAdjustedCallback);
41
+
42
+ // Add tasks
43
+ userScheduler.addTask(taskSendSensorData);
44
+ taskSendSensorData.enable();
45
+
46
+ userScheduler.addTask(taskSendStatus);
47
+ taskSendStatus.enable();
48
+
49
+ // Initialize sensors (placeholder)
50
+ Serial.println("Alteriom Sensor Node initialized");
51
+ }
52
+
53
+ void loop() { mesh.update(); }
54
+
55
+ void sendSensorData() {
56
+ // Create sensor package
57
+ SensorPackage sensorData;
58
+ sensorData.from = mesh.getNodeId();
59
+ sensorData.sensorId = mesh.getNodeId(); // Use node ID as sensor ID
60
+ sensorData.timestamp = mesh.getNodeTime();
61
+
62
+ // Read sensor values (placeholder - replace with actual sensor readings)
63
+ sensorData.temperature =
64
+ 23.5 + random(-50, 50) / 10.0; // Simulate temperature
65
+ sensorData.humidity = 45.0 + random(-100, 100) / 10.0; // Simulate humidity
66
+ sensorData.pressure = 1013.25 + random(-50, 50) / 10.0; // Simulate pressure
67
+ sensorData.batteryLevel = 85 + random(-20, 15); // Simulate battery
68
+
69
+ // Serialize and send
70
+ DynamicJsonDocument doc(sensorData.jsonObjectSize());
71
+ JsonObject obj = doc.to<JsonObject>();
72
+ sensorData.addTo(std::move(obj));
73
+
74
+ String msg;
75
+ serializeJson(doc, msg);
76
+ mesh.sendBroadcast(msg);
77
+
78
+ Serial.printf("Sent sensor data: T=%.1f, H=%.1f, P=%.1f, Bat=%d%%\n",
79
+ sensorData.temperature, sensorData.humidity,
80
+ sensorData.pressure, sensorData.batteryLevel);
81
+ }
82
+
83
+ void handleStatusRequest() {
84
+ StatusPackage status;
85
+ status.from = mesh.getNodeId();
86
+ status.deviceStatus = 0x01; // Device operational
87
+ status.uptime = millis() / 1000;
88
+ status.freeMemory = ESP.getFreeHeap() / 1024; // KB
89
+ status.wifiStrength = 75; // Placeholder
90
+ status.firmwareVersion = "1.0.0-alteriom";
91
+
92
+ // Serialize and send
93
+ DynamicJsonDocument doc(status.jsonObjectSize());
94
+ JsonObject obj = doc.to<JsonObject>();
95
+ status.addTo(std::move(obj));
96
+
97
+ String msg;
98
+ serializeJson(doc, msg);
99
+ mesh.sendBroadcast(msg);
100
+
101
+ Serial.printf("Sent status: uptime=%ds, mem=%dKB\n", status.uptime,
102
+ status.freeMemory);
103
+ }
104
+
105
+ void handleIncomingPackage(uint32_t from, String& msg) {
106
+ Serial.printf("Received from %u: %s\n", from, msg.c_str());
107
+
108
+ // Parse the JSON message
109
+ DynamicJsonDocument doc(1024);
110
+ deserializeJson(doc, msg);
111
+ JsonObject obj = doc.as<JsonObject>();
112
+
113
+ // Check message type
114
+ uint8_t msgType = obj["type"];
115
+
116
+ switch (msgType) {
117
+ case 200: // SensorPackage
118
+ {
119
+ SensorPackage receivedSensor(obj);
120
+ Serial.printf("Sensor data from %u: T=%.1f, H=%.1f\n",
121
+ receivedSensor.from, receivedSensor.temperature,
122
+ receivedSensor.humidity);
123
+ // Process sensor data (store, forward, analyze, etc.)
124
+ } break;
125
+
126
+ case 201: // CommandPackage
127
+ {
128
+ CommandPackage receivedCmd(obj);
129
+ if (receivedCmd.dest == mesh.getNodeId()) {
130
+ handleCommandPackage(receivedCmd);
131
+ }
132
+ } break;
133
+
134
+ case 202: // StatusPackage
135
+ {
136
+ StatusPackage receivedStatus(obj);
137
+ Serial.printf("Status from %u: uptime=%ds, mem=%dKB, fw=%s\n",
138
+ receivedStatus.from, receivedStatus.uptime,
139
+ receivedStatus.freeMemory,
140
+ receivedStatus.firmwareVersion.c_str());
141
+ } break;
142
+
143
+ default:
144
+ Serial.printf("Unknown message type: %d\n", msgType);
145
+ break;
146
+ }
147
+ }
148
+
149
+ void handleCommandPackage(CommandPackage& cmd) {
150
+ Serial.printf("Received command %d for device %u\n", cmd.command,
151
+ cmd.targetDevice);
152
+
153
+ // Process different command types
154
+ switch (cmd.command) {
155
+ case 1: // Reset command
156
+ Serial.println("Reset command received");
157
+ // Implement reset logic
158
+ break;
159
+
160
+ case 2: // Configuration update
161
+ Serial.printf("Config update: %s\n", cmd.parameters.c_str());
162
+ // Parse and apply configuration
163
+ break;
164
+
165
+ case 3: // Data request
166
+ Serial.println("Data request received");
167
+ sendSensorData(); // Send current sensor data
168
+ break;
169
+
170
+ default:
171
+ Serial.printf("Unknown command: %d\n", cmd.command);
172
+ break;
173
+ }
174
+ }
175
+
176
+ void newConnectionCallback(uint32_t nodeId) {
177
+ Serial.printf("New Connection, nodeId = %u\n", nodeId);
178
+ }
179
+
180
+ void changedConnectionCallback() { Serial.printf("Changed connections\n"); }
181
+
182
+ void nodeTimeAdjustedCallback(int32_t offset) {
183
+ Serial.printf("Adjusted time %u. Offset = %d\n", mesh.getNodeTime(), offset);
184
+ }
@@ -0,0 +1,128 @@
1
+ #ifndef ALTERIOM_SENSOR_PACKAGE_HPP
2
+ #define ALTERIOM_SENSOR_PACKAGE_HPP
3
+
4
+ #include "painlessmesh/plugin.hpp"
5
+
6
+ namespace alteriom {
7
+
8
+ /**
9
+ * @brief Sensor data package for broadcasting environmental measurements
10
+ *
11
+ * This package is designed for Alteriom's IoT sensor network requirements,
12
+ * allowing nodes to share environmental data across the mesh.
13
+ */
14
+ class SensorPackage : public painlessmesh::plugin::BroadcastPackage {
15
+ public:
16
+ double temperature = 0.0; // Temperature in Celsius
17
+ double humidity = 0.0; // Relative humidity percentage
18
+ double pressure = 0.0; // Atmospheric pressure in hPa
19
+ uint32_t sensorId = 0; // Unique sensor identifier
20
+ uint32_t timestamp = 0; // Unix timestamp of measurement
21
+ uint8_t batteryLevel = 0; // Battery level percentage
22
+
23
+ SensorPackage()
24
+ : BroadcastPackage(200) {} // Type ID 200 for Alteriom sensors
25
+
26
+ SensorPackage(JsonObject jsonObj) : BroadcastPackage(jsonObj) {
27
+ temperature = jsonObj["temp"];
28
+ humidity = jsonObj["hum"];
29
+ pressure = jsonObj["press"];
30
+ sensorId = jsonObj["sid"];
31
+ timestamp = jsonObj["ts"];
32
+ batteryLevel = jsonObj["bat"];
33
+ }
34
+
35
+ JsonObject addTo(JsonObject&& jsonObj) const {
36
+ jsonObj = BroadcastPackage::addTo(std::move(jsonObj));
37
+ jsonObj["temp"] = temperature;
38
+ jsonObj["hum"] = humidity;
39
+ jsonObj["press"] = pressure;
40
+ jsonObj["sid"] = sensorId;
41
+ jsonObj["ts"] = timestamp;
42
+ jsonObj["bat"] = batteryLevel;
43
+ return jsonObj;
44
+ }
45
+
46
+ #if ARDUINOJSON_VERSION_MAJOR < 7
47
+ size_t jsonObjectSize() const { return JSON_OBJECT_SIZE(noJsonFields + 6); }
48
+ #endif
49
+ };
50
+
51
+ /**
52
+ * @brief Command package for controlling Alteriom devices
53
+ *
54
+ * Single-destination package for sending specific commands to individual nodes.
55
+ */
56
+ class CommandPackage : public painlessmesh::plugin::SinglePackage {
57
+ public:
58
+ uint8_t command = 0; // Command type
59
+ uint32_t targetDevice = 0; // Target device ID
60
+ TSTRING parameters = ""; // Command parameters as JSON string
61
+ uint32_t commandId = 0; // Unique command identifier for tracking
62
+
63
+ CommandPackage() : SinglePackage(201) {} // Type ID 201 for Alteriom commands
64
+
65
+ CommandPackage(JsonObject jsonObj) : SinglePackage(jsonObj) {
66
+ command = jsonObj["cmd"];
67
+ targetDevice = jsonObj["target"];
68
+ parameters = jsonObj["params"].as<TSTRING>();
69
+ commandId = jsonObj["cid"];
70
+ }
71
+
72
+ JsonObject addTo(JsonObject&& jsonObj) const {
73
+ jsonObj = SinglePackage::addTo(std::move(jsonObj));
74
+ jsonObj["cmd"] = command;
75
+ jsonObj["target"] = targetDevice;
76
+ jsonObj["params"] = parameters;
77
+ jsonObj["cid"] = commandId;
78
+ return jsonObj;
79
+ }
80
+
81
+ #if ARDUINOJSON_VERSION_MAJOR < 7
82
+ size_t jsonObjectSize() const {
83
+ return JSON_OBJECT_SIZE(noJsonFields + 4) + parameters.length();
84
+ }
85
+ #endif
86
+ };
87
+
88
+ /**
89
+ * @brief Status report package for device health monitoring
90
+ */
91
+ class StatusPackage : public painlessmesh::plugin::BroadcastPackage {
92
+ public:
93
+ uint8_t deviceStatus = 0; // Device status flags
94
+ uint32_t uptime = 0; // Device uptime in seconds
95
+ uint16_t freeMemory = 0; // Free memory in KB
96
+ uint8_t wifiStrength = 0; // WiFi signal strength
97
+ TSTRING firmwareVersion = ""; // Current firmware version
98
+
99
+ StatusPackage() : BroadcastPackage(202) {} // Type ID 202 for Alteriom status
100
+
101
+ StatusPackage(JsonObject jsonObj) : BroadcastPackage(jsonObj) {
102
+ deviceStatus = jsonObj["status"];
103
+ uptime = jsonObj["uptime"];
104
+ freeMemory = jsonObj["mem"];
105
+ wifiStrength = jsonObj["wifi"];
106
+ firmwareVersion = jsonObj["fw"].as<TSTRING>();
107
+ }
108
+
109
+ JsonObject addTo(JsonObject&& jsonObj) const {
110
+ jsonObj = BroadcastPackage::addTo(std::move(jsonObj));
111
+ jsonObj["status"] = deviceStatus;
112
+ jsonObj["uptime"] = uptime;
113
+ jsonObj["mem"] = freeMemory;
114
+ jsonObj["wifi"] = wifiStrength;
115
+ jsonObj["fw"] = firmwareVersion;
116
+ return jsonObj;
117
+ }
118
+
119
+ #if ARDUINOJSON_VERSION_MAJOR < 7
120
+ size_t jsonObjectSize() const {
121
+ return JSON_OBJECT_SIZE(noJsonFields + 5) + firmwareVersion.length();
122
+ }
123
+ #endif
124
+ };
125
+
126
+ } // namespace alteriom
127
+
128
+ #endif // ALTERIOM_SENSOR_PACKAGE_HPP
@@ -0,0 +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);
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
@@ -0,0 +1,66 @@
1
+ //************************************************************
2
+ // this is a simple example that uses the painlessMesh library
3
+ //
4
+ // 1. sends a silly message to every node on the mesh at a random time between 1 and 5 seconds
5
+ // 2. prints anything it receives to Serial.print
6
+ //
7
+ //
8
+ //************************************************************
9
+ #include "painlessMesh.h"
10
+
11
+ #define MESH_PREFIX "whateverYouLike"
12
+ #define MESH_PASSWORD "somethingSneaky"
13
+ #define MESH_PORT 5555
14
+
15
+ Scheduler userScheduler; // to control your personal task
16
+ painlessMesh mesh;
17
+
18
+ // User stub
19
+ void sendMessage() ; // Prototype so PlatformIO doesn't complain
20
+
21
+ Task taskSendMessage( TASK_SECOND * 1 , TASK_FOREVER, &sendMessage );
22
+
23
+ void sendMessage() {
24
+ String msg = "Hello from node ";
25
+ msg += mesh.getNodeId();
26
+ mesh.sendBroadcast( msg );
27
+ taskSendMessage.setInterval( random( TASK_SECOND * 1, TASK_SECOND * 5 ));
28
+ }
29
+
30
+ // Needed for painless library
31
+ void receivedCallback( uint32_t from, String &msg ) {
32
+ Serial.printf("startHere: Received from %u msg=%s\n", from, msg.c_str());
33
+ }
34
+
35
+ void newConnectionCallback(uint32_t nodeId) {
36
+ Serial.printf("--> startHere: New Connection, nodeId = %u\n", nodeId);
37
+ }
38
+
39
+ void changedConnectionCallback() {
40
+ Serial.printf("Changed connections\n");
41
+ }
42
+
43
+ void nodeTimeAdjustedCallback(int32_t offset) {
44
+ Serial.printf("Adjusted time %u. Offset = %d\n", mesh.getNodeTime(),offset);
45
+ }
46
+
47
+ void setup() {
48
+ Serial.begin(115200);
49
+
50
+ //mesh.setDebugMsgTypes( ERROR | MESH_STATUS | CONNECTION | SYNC | COMMUNICATION | GENERAL | MSG_TYPES | REMOTE ); // all types on
51
+ mesh.setDebugMsgTypes( ERROR | STARTUP ); // set before init() so that you can see startup messages
52
+
53
+ mesh.init( MESH_PREFIX, MESH_PASSWORD, &userScheduler, MESH_PORT );
54
+ mesh.onReceive(&receivedCallback);
55
+ mesh.onNewConnection(&newConnectionCallback);
56
+ mesh.onChangedConnections(&changedConnectionCallback);
57
+ mesh.onNodeTimeAdjusted(&nodeTimeAdjustedCallback);
58
+
59
+ userScheduler.addTask( taskSendMessage );
60
+ taskSendMessage.enable();
61
+ }
62
+
63
+ void loop() {
64
+ // it will run the user scheduler as well
65
+ mesh.update();
66
+ }
@@ -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 = ../../
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 = ../../
23
+ lib_deps =
24
+ ${env.lib_deps} ; Inherit common dependencies
25
+ esp32async/AsyncTCP