@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.
Files changed (129) hide show
  1. package/CHANGELOG.md +380 -143
  2. package/LICENSE +674 -674
  3. package/README.md +477 -434
  4. package/RELEASE_GUIDE.md +504 -418
  5. package/docs/DOCUMENTATION_MIGRATION_PLAN.md +175 -175
  6. package/docs/IMPLEMENTATION_PLAN_MESH_TOPOLOGY.md +1062 -0
  7. package/docs/MESH_TOPOLOGY_GUIDE.md +992 -0
  8. package/docs/MESH_TOPOLOGY_PROGRESS.md +422 -0
  9. package/docs/MQTT_BRIDGE_COMMANDS.md +894 -0
  10. package/docs/MQTT_BRIDGE_IMPLEMENTATION_SUMMARY.md +324 -0
  11. package/docs/MQTT_COMMAND_SCHEMA_PROPOSAL.md +576 -0
  12. package/docs/MQTT_SCHEMA_COMPLIANCE.md +285 -0
  13. package/docs/MQTT_SCHEMA_PROPOSALS.md +446 -0
  14. package/docs/MQTT_SCHEMA_REVIEW.md +690 -0
  15. package/docs/OTA_COMMANDS_REFERENCE.md +554 -0
  16. package/docs/PHASE1_GUIDE.md +349 -0
  17. package/docs/PHASE2_GUIDE.md +543 -0
  18. package/docs/README.md +77 -70
  19. package/docs/SCHEMA_VALIDATION_CHECKLIST.md +222 -0
  20. package/docs/alteriom/overview.md +507 -507
  21. package/docs/api/core-api.md +606 -606
  22. package/docs/architecture/mesh-architecture.md +378 -378
  23. package/docs/architecture/plugin-system.md +516 -516
  24. package/docs/getting-started/first-mesh.md +409 -409
  25. package/docs/getting-started/installation.md +274 -274
  26. package/docs/getting-started/quickstart.md +157 -157
  27. package/docs/improvements/FEATURE_PROPOSALS.md +337 -0
  28. package/docs/improvements/PHASE1_IMPLEMENTATION.md +325 -0
  29. package/docs/improvements/PHASE2_IMPLEMENTATION.md +567 -0
  30. package/docs/improvements/README.md +86 -68
  31. package/docs/improvements/ota-and-status-enhancements.md +911 -0
  32. package/docs/improvements/ota-status-architecture-diagrams.md +658 -0
  33. package/docs/improvements/ota-status-quick-reference.md +284 -0
  34. package/docs/platformio-publishing.md +255 -0
  35. package/docs/platformio-setup-summary.md +121 -0
  36. package/docs/troubleshooting/common-issues.md +520 -520
  37. package/docs/troubleshooting/faq.md +472 -472
  38. package/docs/tutorials/basic-examples.md +717 -717
  39. package/docs/wiki/API-Reference.md +245 -245
  40. package/docs/wiki/Complete-Documentation.md +122 -122
  41. package/examples/alteriom/README.md +139 -81
  42. package/examples/alteriom/alteriom.ino +186 -185
  43. package/examples/alteriom/alteriom_sensor_package.hpp +240 -127
  44. package/examples/alteriom/platformio.ini +24 -24
  45. package/examples/alteriomImproved/alteriom_sensor_package.hpp +224 -0
  46. package/examples/{alteriom → alteriomImproved}/improved_sensor_node.ino +245 -245
  47. package/examples/alteriomImproved/platformio.ini +25 -0
  48. package/examples/alteriomPhase1/alteriom_sensor_package.hpp +224 -0
  49. package/examples/alteriomPhase1/phase1_features.ino +242 -0
  50. package/examples/alteriomPhase1/platformio.ini +25 -0
  51. package/examples/alteriomPhase2/alteriom_sensor_package.hpp +224 -0
  52. package/examples/alteriomPhase2/phase2_features.ino +186 -0
  53. package/examples/alteriomPhase2/platformio.ini +25 -0
  54. package/examples/{alteriom → alteriomSensorNode}/alteriom_sensor_node.ino +183 -183
  55. package/examples/alteriomSensorNode/alteriom_sensor_package.hpp +224 -0
  56. package/examples/alteriomSensorNode/platformio.ini +25 -0
  57. package/examples/basic/basic.ino +66 -66
  58. package/examples/basic/platformio.ini +25 -25
  59. package/examples/bridge/bridge.ino +51 -51
  60. package/examples/bridge/mesh_event_publisher.hpp +253 -0
  61. package/examples/bridge/mesh_topology_reporter.hpp +303 -0
  62. package/examples/bridge/mqtt_command_bridge.hpp +459 -0
  63. package/examples/bridge/mqtt_status_bridge.hpp +519 -0
  64. package/examples/bridge/platformio.ini +25 -25
  65. package/examples/echoNode/echoNode.ino +33 -33
  66. package/examples/echoNode/platformio.ini +25 -25
  67. package/examples/logClient/logClient.ino +109 -109
  68. package/examples/logClient/platformio.ini +25 -25
  69. package/examples/logServer/logServer.ino +81 -81
  70. package/examples/logServer/platformio.ini +25 -25
  71. package/examples/meshCommandNode/alteriom_sensor_package.hpp +235 -0
  72. package/examples/meshCommandNode/meshCommandNode.ino +263 -0
  73. package/examples/meshCommandNode/platformio.ini +25 -0
  74. package/examples/mqttBridge/mqttBridge.ino +118 -118
  75. package/examples/mqttBridge/platformio.ini +26 -26
  76. package/examples/mqttCommandBridge/alteriom_sensor_package.hpp +235 -0
  77. package/examples/mqttCommandBridge/mesh_event_publisher.hpp +253 -0
  78. package/examples/mqttCommandBridge/mesh_topology_reporter.hpp +303 -0
  79. package/examples/mqttCommandBridge/mqttCommandBridge.ino +252 -0
  80. package/examples/mqttCommandBridge/mqtt_command_bridge.hpp +459 -0
  81. package/examples/mqttCommandBridge/platformio.ini +26 -0
  82. package/examples/mqttStatusBridge/mqttStatusBridge.ino +216 -0
  83. package/examples/mqttStatusBridge/mqtt_status_bridge.hpp +522 -0
  84. package/examples/mqttStatusBridge/platformio.ini +26 -0
  85. package/examples/mqttTopologyTest/README.md +467 -0
  86. package/examples/mqttTopologyTest/mqttTopologyTest.ino +748 -0
  87. package/examples/mqttTopologyTest/platformio.ini +26 -0
  88. package/examples/namedMesh/namedMesh.ino +97 -97
  89. package/examples/namedMesh/platformio.ini +25 -25
  90. package/examples/otaReceiver/otaReceiver.ino +79 -79
  91. package/examples/otaReceiver/platformio.ini +25 -25
  92. package/examples/otaSender/otaSender.ino +160 -151
  93. package/examples/otaSender/platformio.ini +25 -25
  94. package/examples/startHere/platformio.ini +25 -25
  95. package/examples/startHere/startHere.ino +159 -159
  96. package/examples/webServer/platformio.ini +27 -27
  97. package/examples/webServer/webServer.ino +89 -89
  98. package/keywords.txt +48 -48
  99. package/library.json +55 -34
  100. package/library.properties +10 -10
  101. package/package.json +86 -78
  102. package/src/AlteriomPainlessMesh.h +97 -97
  103. package/src/arduino/wifi.hpp +365 -365
  104. package/src/boost/asynctcp.hpp +279 -279
  105. package/src/painlessMesh.h +70 -70
  106. package/src/painlessMeshSTA.cpp +236 -236
  107. package/src/painlessMeshSTA.h +58 -58
  108. package/src/painlessTaskOptions.h +4 -4
  109. package/src/painlessmesh/base64.hpp +111 -111
  110. package/src/painlessmesh/buffer.hpp +229 -229
  111. package/src/painlessmesh/callback.hpp +91 -91
  112. package/src/painlessmesh/configuration.hpp +77 -77
  113. package/src/painlessmesh/connection.hpp +192 -192
  114. package/src/painlessmesh/layout.hpp +188 -188
  115. package/src/painlessmesh/logger.hpp +158 -158
  116. package/src/painlessmesh/memory.hpp +119 -119
  117. package/src/painlessmesh/mesh.hpp +761 -560
  118. package/src/painlessmesh/metrics.hpp +322 -322
  119. package/src/painlessmesh/ntp.hpp +263 -263
  120. package/src/painlessmesh/ota.hpp +582 -553
  121. package/src/painlessmesh/plugin.hpp +188 -188
  122. package/src/painlessmesh/protocol.hpp +813 -813
  123. package/src/painlessmesh/router.hpp +322 -322
  124. package/src/painlessmesh/tcp.hpp +71 -71
  125. package/src/painlessmesh/validation.hpp +238 -238
  126. package/src/plugin/performance.hpp +214 -214
  127. package/src/plugin/remote.hpp +64 -64
  128. package/src/scheduler.cpp +10 -10
  129. package/src/wifi.cpp +2 -2
@@ -0,0 +1,303 @@
1
+ #ifndef MESH_TOPOLOGY_REPORTER_HPP
2
+ #define MESH_TOPOLOGY_REPORTER_HPP
3
+
4
+ #include "painlessMesh.h"
5
+ #include <ArduinoJson.h>
6
+
7
+ /**
8
+ * MeshTopologyReporter - Generate schema-compliant topology messages
9
+ *
10
+ * Generates mesh topology messages conforming to @alteriom/mqtt-schema v0.5.0
11
+ * mesh_topology.schema.json specification.
12
+ *
13
+ * Features:
14
+ * - Full topology snapshots with nodes, connections, and metrics
15
+ * - Incremental updates for node join/leave events
16
+ * - Change detection to avoid redundant publishes
17
+ * - Network metrics aggregation
18
+ *
19
+ * @author Alteriom Development Team
20
+ * @date October 12, 2025
21
+ */
22
+ class MeshTopologyReporter {
23
+ private:
24
+ painlessMesh& mesh;
25
+ String meshId;
26
+ String lastTopologyHash; // Detect changes
27
+ String firmwareVersion;
28
+
29
+ public:
30
+ MeshTopologyReporter(painlessMesh& mesh, const String& meshId, const String& fwVersion = "GW 2.3.4")
31
+ : mesh(mesh), meshId(meshId), firmwareVersion(fwVersion) {}
32
+
33
+ /**
34
+ * Generate full topology message (schema v0.5.0 compliant)
35
+ *
36
+ * Returns JSON string ready for MQTT publish to:
37
+ * alteriom/mesh/{mesh_id}/topology
38
+ */
39
+ String generateFullTopology() {
40
+ DynamicJsonDocument doc(8192); // Large buffer for full topology
41
+
42
+ // Envelope fields (from @alteriom/mqtt-schema)
43
+ doc["schema_version"] = 1;
44
+ doc["device_id"] = generateDeviceId(mesh.getNodeId());
45
+ doc["device_type"] = "gateway";
46
+ doc["timestamp"] = getISO8601Timestamp();
47
+ doc["firmware_version"] = firmwareVersion;
48
+
49
+ // Event discriminator
50
+ doc["event"] = "mesh_topology";
51
+
52
+ // Mesh identification
53
+ doc["mesh_id"] = meshId;
54
+ doc["gateway_node_id"] = generateDeviceId(mesh.getNodeId());
55
+
56
+ // Nodes array
57
+ JsonArray nodes = doc.createNestedArray("nodes");
58
+ addNodeInfo(nodes, mesh.getNodeId(), "gateway"); // Self
59
+
60
+ auto nodeList = mesh.getNodeList(false);
61
+ for (auto nodeId : nodeList) {
62
+ addNodeInfo(nodes, nodeId, "sensor"); // Assume sensor role
63
+ }
64
+
65
+ // Connections array
66
+ JsonArray connections = doc.createNestedArray("connections");
67
+ addConnectionInfo(connections);
68
+
69
+ // Network metrics
70
+ JsonObject metrics = doc.createNestedObject("metrics");
71
+ metrics["total_nodes"] = nodeList.size() + 1; // +1 for gateway
72
+ metrics["online_nodes"] = nodeList.size() + 1;
73
+ metrics["network_diameter"] = calculateNetworkDiameter();
74
+ metrics["avg_connection_quality"] = calculateAvgQuality();
75
+ metrics["messages_per_second"] = calculateThroughput();
76
+
77
+ doc["update_type"] = "full";
78
+
79
+ String output;
80
+ serializeJson(doc, output);
81
+ return output;
82
+ }
83
+
84
+ /**
85
+ * Generate incremental topology update
86
+ *
87
+ * @param nodeId Node that joined or left
88
+ * @param joined true if node joined, false if left
89
+ */
90
+ String generateIncrementalUpdate(uint32_t nodeId, bool joined) {
91
+ DynamicJsonDocument doc(2048);
92
+
93
+ // Envelope fields
94
+ doc["schema_version"] = 1;
95
+ doc["device_id"] = generateDeviceId(mesh.getNodeId());
96
+ doc["device_type"] = "gateway";
97
+ doc["timestamp"] = getISO8601Timestamp();
98
+ doc["firmware_version"] = firmwareVersion;
99
+
100
+ doc["event"] = "mesh_topology";
101
+ doc["mesh_id"] = meshId;
102
+ doc["gateway_node_id"] = generateDeviceId(mesh.getNodeId());
103
+
104
+ // Node that changed
105
+ JsonArray nodes = doc.createNestedArray("nodes");
106
+ if (joined) {
107
+ addNodeInfo(nodes, nodeId, "sensor");
108
+ } else {
109
+ // For node leaving, mark as offline
110
+ JsonObject node = nodes.createNestedObject();
111
+ node["node_id"] = generateDeviceId(nodeId);
112
+ node["status"] = "offline";
113
+ }
114
+
115
+ // Update connections
116
+ JsonArray connections = doc.createNestedArray("connections");
117
+ // Add connections related to this node
118
+ addConnectionInfoForNode(connections, nodeId);
119
+
120
+ // Updated metrics
121
+ auto nodeList = mesh.getNodeList(false);
122
+ JsonObject metrics = doc.createNestedObject("metrics");
123
+ metrics["total_nodes"] = nodeList.size() + 1;
124
+ metrics["online_nodes"] = nodeList.size() + 1;
125
+
126
+ doc["update_type"] = "incremental";
127
+
128
+ String output;
129
+ serializeJson(doc, output);
130
+ return output;
131
+ }
132
+
133
+ /**
134
+ * Check if topology has changed since last check
135
+ * Updates internal hash if changed
136
+ */
137
+ bool hasTopologyChanged() {
138
+ String currentHash = calculateTopologyHash();
139
+ if (currentHash != lastTopologyHash) {
140
+ lastTopologyHash = currentHash;
141
+ return true;
142
+ }
143
+ return false;
144
+ }
145
+
146
+ private:
147
+ /**
148
+ * Add node information to JSON array
149
+ */
150
+ void addNodeInfo(JsonArray& nodes, uint32_t nodeId, const char* role) {
151
+ JsonObject node = nodes.createNestedObject();
152
+ node["node_id"] = generateDeviceId(nodeId);
153
+ node["role"] = role;
154
+ node["status"] = "online";
155
+ node["last_seen"] = getISO8601Timestamp();
156
+
157
+ // Gateway node has additional info
158
+ if (nodeId == mesh.getNodeId()) {
159
+ node["firmware_version"] = firmwareVersion;
160
+ node["uptime_seconds"] = millis() / 1000;
161
+ #if defined(ESP32) || defined(ESP8266)
162
+ node["free_memory_kb"] = ESP.getFreeHeap() / 1024;
163
+ #else
164
+ node["free_memory_kb"] = 0;
165
+ #endif
166
+ node["connection_count"] = mesh.getNodeList().size();
167
+ } else {
168
+ // For other nodes, use placeholder values
169
+ // TODO: Get actual values from node status messages
170
+ node["firmware_version"] = "SN 2.3.4";
171
+ node["uptime_seconds"] = 0;
172
+ node["free_memory_kb"] = 0;
173
+ node["connection_count"] = 0;
174
+ }
175
+ }
176
+
177
+ /**
178
+ * Add connection information to JSON array
179
+ */
180
+ void addConnectionInfo(JsonArray& connections) {
181
+ // Get direct connections with quality metrics
182
+ auto connDetails = mesh.getConnectionDetails();
183
+
184
+ String gatewayId = generateDeviceId(mesh.getNodeId());
185
+
186
+ for (auto& conn : connDetails) {
187
+ JsonObject connObj = connections.createNestedObject();
188
+ connObj["from_node"] = gatewayId;
189
+ connObj["to_node"] = generateDeviceId(conn.nodeId);
190
+ connObj["quality"] = conn.quality;
191
+ connObj["latency_ms"] = conn.avgLatency;
192
+ connObj["rssi"] = conn.rssi;
193
+ connObj["hop_count"] = conn.hopCount;
194
+ }
195
+
196
+ // TODO: Add indirect connections from mesh topology
197
+ // For now, we only report direct connections from gateway
198
+ }
199
+
200
+ /**
201
+ * Add connection information for a specific node
202
+ */
203
+ void addConnectionInfoForNode(JsonArray& connections, uint32_t nodeId) {
204
+ auto connDetails = mesh.getConnectionDetails();
205
+ String gatewayId = generateDeviceId(mesh.getNodeId());
206
+ String targetId = generateDeviceId(nodeId);
207
+
208
+ for (auto& conn : connDetails) {
209
+ if (conn.nodeId == nodeId) {
210
+ JsonObject connObj = connections.createNestedObject();
211
+ connObj["from_node"] = gatewayId;
212
+ connObj["to_node"] = targetId;
213
+ connObj["quality"] = conn.quality;
214
+ connObj["latency_ms"] = conn.avgLatency;
215
+ connObj["rssi"] = conn.rssi;
216
+ connObj["hop_count"] = conn.hopCount;
217
+ }
218
+ }
219
+ }
220
+
221
+ /**
222
+ * Calculate network diameter (max hop count)
223
+ */
224
+ int calculateNetworkDiameter() {
225
+ int maxHops = 0;
226
+ auto nodeList = mesh.getNodeList(false);
227
+
228
+ for (auto nodeId : nodeList) {
229
+ int hops = mesh.getHopCount(nodeId);
230
+ if (hops > maxHops) {
231
+ maxHops = hops;
232
+ }
233
+ }
234
+
235
+ return maxHops;
236
+ }
237
+
238
+ /**
239
+ * Calculate average connection quality
240
+ */
241
+ float calculateAvgQuality() {
242
+ auto connDetails = mesh.getConnectionDetails();
243
+
244
+ if (connDetails.empty()) {
245
+ return 0.0;
246
+ }
247
+
248
+ int totalQuality = 0;
249
+ for (auto& conn : connDetails) {
250
+ totalQuality += conn.quality;
251
+ }
252
+
253
+ return (float)totalQuality / connDetails.size();
254
+ }
255
+
256
+ /**
257
+ * Calculate network throughput (messages per second)
258
+ */
259
+ float calculateThroughput() {
260
+ // TODO: Implement actual throughput tracking
261
+ // For now return placeholder
262
+ return 12.4;
263
+ }
264
+
265
+ /**
266
+ * Calculate topology hash for change detection
267
+ */
268
+ String calculateTopologyHash() {
269
+ auto nodeList = mesh.getNodeList(true);
270
+ String hash = String(nodeList.size());
271
+ for (auto nodeId : nodeList) {
272
+ hash += String(nodeId);
273
+ }
274
+ return hash;
275
+ }
276
+
277
+ /**
278
+ * Generate device ID in Alteriom format (ALT-XXXXXXXXXXXX)
279
+ */
280
+ String generateDeviceId(uint32_t nodeId) {
281
+ char deviceId[20];
282
+ snprintf(deviceId, sizeof(deviceId), "ALT-%012X", nodeId);
283
+ return String(deviceId);
284
+ }
285
+
286
+ /**
287
+ * Get current timestamp in ISO 8601 format
288
+ * TODO: Use proper NTP time if available
289
+ */
290
+ String getISO8601Timestamp() {
291
+ // For now, use epoch-based timestamp
292
+ // In production, this should use actual NTP-synced time
293
+ unsigned long ms = millis();
294
+ char timestamp[32];
295
+ snprintf(timestamp, sizeof(timestamp), "2025-10-12T%02lu:%02lu:%02luZ",
296
+ (ms / 3600000) % 24,
297
+ (ms / 60000) % 60,
298
+ (ms / 1000) % 60);
299
+ return String(timestamp);
300
+ }
301
+ };
302
+
303
+ #endif // MESH_TOPOLOGY_REPORTER_HPP