@alteriom/painlessmesh 1.7.6 → 1.7.7

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 (36) hide show
  1. package/CHANGELOG.md +107 -0
  2. package/README.md +81 -6
  3. package/RELEASE_GUIDE.md +36 -0
  4. package/docs/MQTT_BRIDGE_COMMANDS.md +10 -10
  5. package/docs/MQTT_BRIDGE_IMPLEMENTATION_SUMMARY.md +1 -1
  6. package/docs/MQTT_SCHEMA_COMPLIANCE.md +57 -2
  7. package/docs/PHASE1_GUIDE.md +1 -1
  8. package/docs/alteriom/overview.md +2 -2
  9. package/docs/architecture/plugin-system.md +1 -1
  10. package/docs/archive/RELEASE_SUMMARY.md +1 -1
  11. package/docs/releases/RELEASE_CHECKLIST_v1.7.6.md +389 -0
  12. package/docs/releases/RELEASE_SUMMARY_v1.7.7.md +391 -0
  13. package/docs/v1.7.7_MQTT_IMPROVEMENTS.md +776 -0
  14. package/docs/wiki/API-Reference.md +2 -2
  15. package/docs/wiki/Complete-Documentation.md +1 -1
  16. package/examples/alteriom/README.md +137 -3
  17. package/examples/alteriom/alteriom.ino +1 -1
  18. package/examples/alteriom/alteriom_sensor_package.hpp +557 -2
  19. package/examples/alteriomImproved/alteriom_sensor_package.hpp +1 -1
  20. package/examples/alteriomImproved/improved_sensor_node.ino +1 -1
  21. package/examples/alteriomMetricsHealth/alteriom_sensor_package.hpp +796 -0
  22. package/examples/alteriomMetricsHealth/metrics_health_node.ino +418 -0
  23. package/examples/alteriomMetricsHealth/platformio.ini +26 -0
  24. package/examples/alteriomPhase1/alteriom_sensor_package.hpp +1 -1
  25. package/examples/alteriomPhase1/phase1_features.ino +2 -2
  26. package/examples/alteriomPhase2/alteriom_sensor_package.hpp +1 -1
  27. package/examples/alteriomSensorNode/alteriom_sensor_node.ino +1 -1
  28. package/examples/alteriomSensorNode/alteriom_sensor_package.hpp +1 -1
  29. package/examples/bridge/enhanced_mqtt_bridge.hpp +610 -0
  30. package/examples/bridge/enhanced_mqtt_bridge_example.ino +226 -0
  31. package/examples/meshCommandNode/alteriom_sensor_package.hpp +1 -1
  32. package/examples/mqttCommandBridge/alteriom_sensor_package.hpp +1 -1
  33. package/examples/mqttTopologyTest/mqttTopologyTest.ino +5 -1
  34. package/library.json +1 -1
  35. package/library.properties +1 -1
  36. package/package.json +2 -2
@@ -66,7 +66,8 @@ class CommandPackage : public painlessmesh::plugin::SinglePackage {
66
66
  TSTRING parameters = ""; // Command parameters as JSON string
67
67
  uint32_t commandId = 0; // Unique command identifier for tracking
68
68
 
69
- CommandPackage() : SinglePackage(201) {} // Type ID 201 for Alteriom commands
69
+ CommandPackage()
70
+ : SinglePackage(400) {} // Type ID 400 (COMMAND per mqtt-schema v0.7.2+)
70
71
 
71
72
  CommandPackage(JsonObject jsonObj) : SinglePackage(jsonObj) {
72
73
  command = jsonObj["cmd"];
@@ -174,7 +175,8 @@ class EnhancedStatusPackage : public painlessmesh::plugin::BroadcastPackage {
174
175
  TSTRING lastError = ""; // Last error message for diagnostics
175
176
 
176
177
  EnhancedStatusPackage()
177
- : BroadcastPackage(203) {} // Type ID 203 for enhanced status
178
+ : BroadcastPackage(604) {
179
+ } // Type ID 604 for enhanced status (MESH_STATUS per mqtt-schema v0.7.2+)
178
180
 
179
181
  EnhancedStatusPackage(JsonObject jsonObj) : BroadcastPackage(jsonObj) {
180
182
  deviceStatus = jsonObj["status"];
@@ -236,6 +238,559 @@ class EnhancedStatusPackage : public painlessmesh::plugin::BroadcastPackage {
236
238
  #endif
237
239
  };
238
240
 
241
+ /**
242
+ * @brief Detailed performance metrics package for monitoring (Phase 2)
243
+ *
244
+ * Provides comprehensive performance data including CPU usage, memory trends,
245
+ * network throughput, and other metrics useful for dashboards and monitoring.
246
+ * Type ID 204 for Alteriom metrics.
247
+ */
248
+ class MetricsPackage : public painlessmesh::plugin::BroadcastPackage {
249
+ public:
250
+ // CPU and Processing
251
+ uint8_t cpuUsage = 0; // CPU usage percentage (0-100)
252
+ uint32_t loopIterations = 0; // Loop iterations per second
253
+ uint16_t taskQueueSize = 0; // Number of pending tasks
254
+
255
+ // Memory Metrics
256
+ uint32_t freeHeap = 0; // Free heap memory in bytes
257
+ uint32_t minFreeHeap = 0; // Minimum free heap since boot
258
+ uint32_t heapFragmentation = 0; // Heap fragmentation percentage
259
+ uint32_t maxAllocHeap = 0; // Largest allocatable block
260
+
261
+ // Network Performance
262
+ uint32_t bytesReceived = 0; // Total bytes received
263
+ uint32_t bytesSent = 0; // Total bytes sent
264
+ uint16_t packetsReceived = 0; // Total packets received
265
+ uint16_t packetsSent = 0; // Total packets sent
266
+ uint16_t packetsDropped = 0; // Packets dropped
267
+ uint16_t currentThroughput = 0; // Current throughput in bytes/sec
268
+
269
+ // Timing and Latency
270
+ uint32_t avgResponseTime = 0; // Average response time in microseconds
271
+ uint32_t maxResponseTime = 0; // Maximum response time in microseconds
272
+ uint16_t avgMeshLatency = 0; // Average mesh latency in milliseconds
273
+
274
+ // Connection Quality
275
+ uint8_t connectionQuality = 0; // Overall connection quality (0-100)
276
+ int8_t wifiRSSI = 0; // WiFi RSSI in dBm
277
+
278
+ // Collection metadata
279
+ uint32_t collectionTimestamp = 0; // When metrics were collected
280
+ uint32_t collectionInterval = 0; // Interval between collections in ms
281
+
282
+ // MQTT Schema v0.7.2+ message_type for faster classification
283
+ uint16_t messageType = 204; // SENSOR_METRICS (aligns with schema v0.7.2+)
284
+
285
+ MetricsPackage() : BroadcastPackage(204) {}
286
+
287
+ MetricsPackage(JsonObject jsonObj) : BroadcastPackage(jsonObj) {
288
+ cpuUsage = jsonObj["cpu"];
289
+ loopIterations = jsonObj["loops"];
290
+ taskQueueSize = jsonObj["tasks"];
291
+
292
+ freeHeap = jsonObj["heap"];
293
+ minFreeHeap = jsonObj["minHeap"];
294
+ heapFragmentation = jsonObj["fragHeap"];
295
+ maxAllocHeap = jsonObj["maxHeap"];
296
+
297
+ bytesReceived = jsonObj["bytesRx"];
298
+ bytesSent = jsonObj["bytesTx"];
299
+ packetsReceived = jsonObj["pktsRx"];
300
+ packetsSent = jsonObj["pktsTx"];
301
+ packetsDropped = jsonObj["pktsDrop"];
302
+ currentThroughput = jsonObj["throughput"];
303
+
304
+ avgResponseTime = jsonObj["avgResp"];
305
+ maxResponseTime = jsonObj["maxResp"];
306
+ avgMeshLatency = jsonObj["avgLat"];
307
+
308
+ connectionQuality = jsonObj["connQual"];
309
+ wifiRSSI = jsonObj["rssi"];
310
+
311
+ collectionTimestamp = jsonObj["ts"];
312
+ collectionInterval = jsonObj["interval"];
313
+ messageType = jsonObj["message_type"] | 204;
314
+ }
315
+
316
+ JsonObject addTo(JsonObject&& jsonObj) const {
317
+ jsonObj = BroadcastPackage::addTo(std::move(jsonObj));
318
+
319
+ // CPU and Processing
320
+ jsonObj["cpu"] = cpuUsage;
321
+ jsonObj["loops"] = loopIterations;
322
+ jsonObj["tasks"] = taskQueueSize;
323
+
324
+ // Memory Metrics
325
+ jsonObj["heap"] = freeHeap;
326
+ jsonObj["minHeap"] = minFreeHeap;
327
+ jsonObj["fragHeap"] = heapFragmentation;
328
+ jsonObj["maxHeap"] = maxAllocHeap;
329
+
330
+ // Network Performance
331
+ jsonObj["bytesRx"] = bytesReceived;
332
+ jsonObj["bytesTx"] = bytesSent;
333
+ jsonObj["pktsRx"] = packetsReceived;
334
+ jsonObj["pktsTx"] = packetsSent;
335
+ jsonObj["pktsDrop"] = packetsDropped;
336
+ jsonObj["throughput"] = currentThroughput;
337
+
338
+ // Timing and Latency
339
+ jsonObj["avgResp"] = avgResponseTime;
340
+ jsonObj["maxResp"] = maxResponseTime;
341
+ jsonObj["avgLat"] = avgMeshLatency;
342
+
343
+ // Connection Quality
344
+ jsonObj["connQual"] = connectionQuality;
345
+ jsonObj["rssi"] = wifiRSSI;
346
+
347
+ // Metadata
348
+ jsonObj["ts"] = collectionTimestamp;
349
+ jsonObj["interval"] = collectionInterval;
350
+ jsonObj["message_type"] = messageType; // MQTT Schema v0.7.1+
351
+
352
+ return jsonObj;
353
+ }
354
+
355
+ #if ARDUINOJSON_VERSION_MAJOR < 7
356
+ size_t jsonObjectSize() const { return JSON_OBJECT_SIZE(noJsonFields + 22); }
357
+ #endif
358
+ };
359
+
360
+ /**
361
+ * @brief Health check package for proactive problem detection (Phase 2)
362
+ *
363
+ * Provides early warning indicators and health status to detect issues
364
+ * before they cause failures. Used for predictive maintenance and alerting.
365
+ * Type ID 605 for Alteriom health checks (MESH_METRICS per mqtt-schema
366
+ * v0.7.2+).
367
+ */
368
+ class HealthCheckPackage : public painlessmesh::plugin::BroadcastPackage {
369
+ public:
370
+ // Overall Health Status (0=critical, 1=warning, 2=healthy)
371
+ uint8_t healthStatus = 2;
372
+
373
+ // Problem Indicators (bit flags)
374
+ uint16_t problemFlags = 0; // Bit flags for specific problems
375
+ /*
376
+ * Problem flag bits:
377
+ * 0x0001 - Low memory warning
378
+ * 0x0002 - High CPU usage
379
+ * 0x0004 - Connection instability
380
+ * 0x0008 - High packet loss
381
+ * 0x0010 - Network congestion
382
+ * 0x0020 - Low battery (if applicable)
383
+ * 0x0040 - Thermal warning
384
+ * 0x0080 - Mesh partition detected
385
+ * 0x0100 - OTA in progress
386
+ * 0x0200 - Configuration error
387
+ */
388
+
389
+ // Memory Health
390
+ uint8_t memoryHealth = 100; // Memory health score (0-100)
391
+ uint32_t memoryTrend = 0; // Bytes/hour memory loss (for leak detection)
392
+
393
+ // Network Health
394
+ uint8_t networkHealth = 100; // Network health score (0-100)
395
+ uint8_t packetLossPercent = 0; // Packet loss percentage
396
+ uint8_t reconnectionCount = 0; // Reconnections in last hour
397
+
398
+ // Performance Health
399
+ uint8_t performanceHealth = 100; // Performance health score (0-100)
400
+ uint32_t missedDeadlines = 0; // Missed task deadlines
401
+ uint16_t maxLoopTime = 0; // Maximum loop execution time in ms
402
+
403
+ // Environmental (if sensors available)
404
+ int8_t temperature = 0; // Device temperature in Celsius
405
+ uint8_t temperatureHealth = 100; // Temperature health score
406
+
407
+ // Uptime and Stability
408
+ uint32_t uptime = 0; // Uptime in seconds
409
+ uint16_t crashCount = 0; // Crash/restart count
410
+ uint32_t lastRebootReason = 0; // Last reboot reason code
411
+
412
+ // Predictive indicators
413
+ uint16_t estimatedTimeToFailure =
414
+ 0; // Estimated hours until failure (0=unknown)
415
+ TSTRING recommendations = ""; // Recommended actions
416
+
417
+ // Check metadata
418
+ uint32_t checkTimestamp = 0; // When check was performed
419
+ uint32_t nextCheckDue = 0; // When next check is due
420
+
421
+ // MQTT Schema v0.7.2+ message_type for faster classification
422
+ uint16_t messageType = 605; // MESH_METRICS type code (mesh performance
423
+ // health per mqtt-schema v0.7.2+)
424
+
425
+ HealthCheckPackage() : BroadcastPackage(605) {}
426
+
427
+ HealthCheckPackage(JsonObject jsonObj) : BroadcastPackage(jsonObj) {
428
+ healthStatus = jsonObj["health"];
429
+ problemFlags = jsonObj["problems"];
430
+
431
+ memoryHealth = jsonObj["memHealth"];
432
+ memoryTrend = jsonObj["memTrend"];
433
+
434
+ networkHealth = jsonObj["netHealth"];
435
+ packetLossPercent = jsonObj["loss"];
436
+ reconnectionCount = jsonObj["reconn"];
437
+
438
+ performanceHealth = jsonObj["perfHealth"];
439
+ missedDeadlines = jsonObj["missed"];
440
+ maxLoopTime = jsonObj["maxLoop"];
441
+
442
+ temperature = jsonObj["temp"];
443
+ temperatureHealth = jsonObj["tempHealth"];
444
+
445
+ uptime = jsonObj["uptime"];
446
+ crashCount = jsonObj["crashes"];
447
+ lastRebootReason = jsonObj["reboot"];
448
+
449
+ estimatedTimeToFailure = jsonObj["ettf"];
450
+ recommendations = jsonObj["recommend"].as<TSTRING>();
451
+
452
+ checkTimestamp = jsonObj["ts"];
453
+ nextCheckDue = jsonObj["nextCheck"];
454
+ messageType = jsonObj["message_type"] | 605;
455
+ }
456
+
457
+ JsonObject addTo(JsonObject&& jsonObj) const {
458
+ jsonObj = BroadcastPackage::addTo(std::move(jsonObj));
459
+
460
+ jsonObj["health"] = healthStatus;
461
+ jsonObj["problems"] = problemFlags;
462
+
463
+ jsonObj["memHealth"] = memoryHealth;
464
+ jsonObj["memTrend"] = memoryTrend;
465
+
466
+ jsonObj["netHealth"] = networkHealth;
467
+ jsonObj["loss"] = packetLossPercent;
468
+ jsonObj["reconn"] = reconnectionCount;
469
+
470
+ jsonObj["perfHealth"] = performanceHealth;
471
+ jsonObj["missed"] = missedDeadlines;
472
+ jsonObj["maxLoop"] = maxLoopTime;
473
+
474
+ jsonObj["temp"] = temperature;
475
+ jsonObj["tempHealth"] = temperatureHealth;
476
+
477
+ jsonObj["uptime"] = uptime;
478
+ jsonObj["crashes"] = crashCount;
479
+ jsonObj["reboot"] = lastRebootReason;
480
+
481
+ jsonObj["ettf"] = estimatedTimeToFailure;
482
+ jsonObj["recommend"] = recommendations;
483
+
484
+ jsonObj["ts"] = checkTimestamp;
485
+ jsonObj["nextCheck"] = nextCheckDue;
486
+ jsonObj["message_type"] = messageType; // MQTT Schema v0.7.1+
487
+
488
+ return jsonObj;
489
+ }
490
+
491
+ #if ARDUINOJSON_VERSION_MAJOR < 7
492
+ size_t jsonObjectSize() const {
493
+ return JSON_OBJECT_SIZE(noJsonFields + 20) + recommendations.length();
494
+ }
495
+ #endif
496
+ };
497
+
498
+ /**
499
+ * @brief Mesh node information for a single node
500
+ */
501
+ struct MeshNodeInfo {
502
+ uint32_t nodeId = 0; // Node identifier
503
+ uint8_t status = 0; // 0=offline, 1=online, 2=unreachable
504
+ uint32_t lastSeen = 0; // Unix timestamp of last communication
505
+ int8_t signalStrength = 0; // RSSI in dBm
506
+ };
507
+
508
+ /**
509
+ * @brief Mesh node list package (Type 600 - MESH_NODE_LIST)
510
+ *
511
+ * Provides list of all nodes in the mesh network with their status.
512
+ * Type ID 600 for MESH_NODE_LIST per mqtt-schema v0.7.2+.
513
+ */
514
+ class MeshNodeListPackage : public painlessmesh::plugin::BroadcastPackage {
515
+ public:
516
+ // Array of node information (max 50 nodes)
517
+ MeshNodeInfo nodes[50];
518
+ uint8_t nodeCount = 0; // Actual number of nodes
519
+ TSTRING meshId = ""; // Mesh network identifier
520
+
521
+ // MQTT Schema v0.7.2+ message_type
522
+ uint16_t messageType = 600; // MESH_NODE_LIST
523
+
524
+ MeshNodeListPackage() : BroadcastPackage(600) {}
525
+
526
+ MeshNodeListPackage(JsonObject jsonObj) : BroadcastPackage(jsonObj) {
527
+ JsonArray nodesArray = jsonObj["nodes"];
528
+ nodeCount = nodesArray.size();
529
+ if (nodeCount > 50) nodeCount = 50;
530
+
531
+ for (uint8_t i = 0; i < nodeCount; i++) {
532
+ JsonObject node = nodesArray[i];
533
+ nodes[i].nodeId = node["nodeId"];
534
+ nodes[i].status = node["status"];
535
+ nodes[i].lastSeen = node["lastSeen"];
536
+ nodes[i].signalStrength = node["rssi"];
537
+ }
538
+
539
+ meshId = jsonObj["meshId"].as<TSTRING>();
540
+ messageType = jsonObj["message_type"] | 600;
541
+ }
542
+
543
+ JsonObject addTo(JsonObject&& jsonObj) const {
544
+ jsonObj = BroadcastPackage::addTo(std::move(jsonObj));
545
+
546
+ JsonArray nodesArray = jsonObj["nodes"].to<JsonArray>();
547
+ for (uint8_t i = 0; i < nodeCount; i++) {
548
+ JsonObject node = nodesArray.add<JsonObject>();
549
+ node["nodeId"] = nodes[i].nodeId;
550
+ node["status"] = nodes[i].status;
551
+ node["lastSeen"] = nodes[i].lastSeen;
552
+ node["rssi"] = nodes[i].signalStrength;
553
+ }
554
+
555
+ jsonObj["nodeCount"] = nodeCount;
556
+ jsonObj["meshId"] = meshId;
557
+ jsonObj["message_type"] = messageType;
558
+
559
+ return jsonObj;
560
+ }
561
+
562
+ #if ARDUINOJSON_VERSION_MAJOR < 7
563
+ size_t jsonObjectSize() const {
564
+ return JSON_OBJECT_SIZE(noJsonFields + 3) + JSON_ARRAY_SIZE(nodeCount) +
565
+ nodeCount * JSON_OBJECT_SIZE(4) + meshId.length();
566
+ }
567
+ #endif
568
+ };
569
+
570
+ /**
571
+ * @brief Mesh connection information
572
+ */
573
+ struct MeshConnection {
574
+ uint32_t fromNode = 0; // Source node ID
575
+ uint32_t toNode = 0; // Destination node ID
576
+ float linkQuality = 0.0; // Link quality 0.0-1.0
577
+ uint16_t latencyMs = 0; // Latency in milliseconds
578
+ uint8_t hopCount = 1; // Number of hops
579
+ };
580
+
581
+ /**
582
+ * @brief Mesh topology package (Type 601 - MESH_TOPOLOGY)
583
+ *
584
+ * Provides mesh network topology with all connections.
585
+ * Type ID 601 for MESH_TOPOLOGY per mqtt-schema v0.7.2+.
586
+ */
587
+ class MeshTopologyPackage : public painlessmesh::plugin::BroadcastPackage {
588
+ public:
589
+ // Array of connections (max 100 connections)
590
+ MeshConnection connections[100];
591
+ uint8_t connectionCount = 0; // Actual number of connections
592
+ uint32_t rootNode = 0; // Root/gateway node ID
593
+
594
+ // MQTT Schema v0.7.2+ message_type
595
+ uint16_t messageType = 601; // MESH_TOPOLOGY
596
+
597
+ MeshTopologyPackage() : BroadcastPackage(601) {}
598
+
599
+ MeshTopologyPackage(JsonObject jsonObj) : BroadcastPackage(jsonObj) {
600
+ JsonArray connsArray = jsonObj["connections"];
601
+ connectionCount = connsArray.size();
602
+ if (connectionCount > 100) connectionCount = 100;
603
+
604
+ for (uint8_t i = 0; i < connectionCount; i++) {
605
+ JsonObject conn = connsArray[i];
606
+ connections[i].fromNode = conn["from"];
607
+ connections[i].toNode = conn["to"];
608
+ connections[i].linkQuality = conn["quality"];
609
+ connections[i].latencyMs = conn["latency"];
610
+ connections[i].hopCount = conn["hops"];
611
+ }
612
+
613
+ rootNode = jsonObj["rootNode"];
614
+ messageType = jsonObj["message_type"] | 601;
615
+ }
616
+
617
+ JsonObject addTo(JsonObject&& jsonObj) const {
618
+ jsonObj = BroadcastPackage::addTo(std::move(jsonObj));
619
+
620
+ JsonArray connsArray = jsonObj["connections"].to<JsonArray>();
621
+ for (uint8_t i = 0; i < connectionCount; i++) {
622
+ JsonObject conn = connsArray.add<JsonObject>();
623
+ conn["from"] = connections[i].fromNode;
624
+ conn["to"] = connections[i].toNode;
625
+ conn["quality"] = connections[i].linkQuality;
626
+ conn["latency"] = connections[i].latencyMs;
627
+ conn["hops"] = connections[i].hopCount;
628
+ }
629
+
630
+ jsonObj["totalConnections"] = connectionCount;
631
+ jsonObj["rootNode"] = rootNode;
632
+ jsonObj["message_type"] = messageType;
633
+
634
+ return jsonObj;
635
+ }
636
+
637
+ #if ARDUINOJSON_VERSION_MAJOR < 7
638
+ size_t jsonObjectSize() const {
639
+ return JSON_OBJECT_SIZE(noJsonFields + 3) +
640
+ JSON_ARRAY_SIZE(connectionCount) +
641
+ connectionCount * JSON_OBJECT_SIZE(5);
642
+ }
643
+ #endif
644
+ };
645
+
646
+ /**
647
+ * @brief Mesh alert information
648
+ */
649
+ struct MeshAlert {
650
+ uint8_t alertType =
651
+ 0; // 0=low_memory, 1=node_offline, 2=connection_lost, etc.
652
+ uint8_t severity = 0; // 0=info, 1=warning, 2=critical
653
+ TSTRING message = ""; // Human-readable message
654
+ uint32_t nodeId = 0; // Related node ID
655
+ float metricValue = 0.0; // Related metric value
656
+ float threshold = 0.0; // Threshold that triggered alert
657
+ uint32_t alertId = 0; // Unique alert ID
658
+ };
659
+
660
+ /**
661
+ * @brief Mesh alert package (Type 602 - MESH_ALERT)
662
+ *
663
+ * Provides mesh network alerts for critical events.
664
+ * Type ID 602 for MESH_ALERT per mqtt-schema v0.7.2+.
665
+ */
666
+ class MeshAlertPackage : public painlessmesh::plugin::BroadcastPackage {
667
+ public:
668
+ // Array of alerts (max 20 alerts)
669
+ MeshAlert alerts[20];
670
+ uint8_t alertCount = 0; // Actual number of alerts
671
+
672
+ // MQTT Schema v0.7.2+ message_type
673
+ uint16_t messageType = 602; // MESH_ALERT
674
+
675
+ MeshAlertPackage() : BroadcastPackage(602) {}
676
+
677
+ MeshAlertPackage(JsonObject jsonObj) : BroadcastPackage(jsonObj) {
678
+ JsonArray alertsArray = jsonObj["alerts"];
679
+ alertCount = alertsArray.size();
680
+ if (alertCount > 20) alertCount = 20;
681
+
682
+ for (uint8_t i = 0; i < alertCount; i++) {
683
+ JsonObject alert = alertsArray[i];
684
+ alerts[i].alertType = alert["type"];
685
+ alerts[i].severity = alert["severity"];
686
+ alerts[i].message = alert["msg"].as<TSTRING>();
687
+ alerts[i].nodeId = alert["nodeId"];
688
+ alerts[i].metricValue = alert["value"];
689
+ alerts[i].threshold = alert["threshold"];
690
+ alerts[i].alertId = alert["alertId"];
691
+ }
692
+
693
+ messageType = jsonObj["message_type"] | 602;
694
+ }
695
+
696
+ JsonObject addTo(JsonObject&& jsonObj) const {
697
+ jsonObj = BroadcastPackage::addTo(std::move(jsonObj));
698
+
699
+ JsonArray alertsArray = jsonObj["alerts"].to<JsonArray>();
700
+ for (uint8_t i = 0; i < alertCount; i++) {
701
+ JsonObject alert = alertsArray.add<JsonObject>();
702
+ alert["type"] = alerts[i].alertType;
703
+ alert["severity"] = alerts[i].severity;
704
+ alert["msg"] = alerts[i].message;
705
+ alert["nodeId"] = alerts[i].nodeId;
706
+ alert["value"] = alerts[i].metricValue;
707
+ alert["threshold"] = alerts[i].threshold;
708
+ alert["alertId"] = alerts[i].alertId;
709
+ }
710
+
711
+ jsonObj["alertCount"] = alertCount;
712
+ jsonObj["message_type"] = messageType;
713
+
714
+ return jsonObj;
715
+ }
716
+
717
+ #if ARDUINOJSON_VERSION_MAJOR < 7
718
+ size_t jsonObjectSize() const {
719
+ size_t size = JSON_OBJECT_SIZE(noJsonFields + 2) +
720
+ JSON_ARRAY_SIZE(alertCount) +
721
+ alertCount * JSON_OBJECT_SIZE(7);
722
+ for (uint8_t i = 0; i < alertCount; i++) {
723
+ size += alerts[i].message.length();
724
+ }
725
+ return size;
726
+ }
727
+ #endif
728
+ };
729
+
730
+ /**
731
+ * @brief Mesh bridge package (Type 603 - MESH_BRIDGE)
732
+ *
733
+ * Encapsulates native mesh protocol messages for bridging.
734
+ * Type ID 603 for MESH_BRIDGE per mqtt-schema v0.7.2+.
735
+ */
736
+ class MeshBridgePackage : public painlessmesh::plugin::BroadcastPackage {
737
+ public:
738
+ uint8_t meshProtocol = 0; // 0=painlessMesh, 1=esp-now, 2=ble-mesh, etc.
739
+ uint32_t fromNodeId = 0; // Source node ID
740
+ uint32_t toNodeId = 0; // Destination node ID (0=broadcast)
741
+ uint16_t meshType = 0; // Mesh protocol-specific message type
742
+ TSTRING rawPayload = ""; // Raw payload (hex/base64 encoded)
743
+ int8_t rssi = 0; // Signal strength
744
+ uint8_t hopCount = 0; // Number of hops
745
+ uint32_t meshTimestamp = 0; // Mesh protocol timestamp
746
+ uint32_t gatewayNodeId = 0; // Gateway's node ID
747
+ TSTRING meshNetworkId = ""; // Mesh network identifier
748
+
749
+ // MQTT Schema v0.7.2+ message_type
750
+ uint16_t messageType = 603; // MESH_BRIDGE
751
+
752
+ MeshBridgePackage() : BroadcastPackage(603) {}
753
+
754
+ MeshBridgePackage(JsonObject jsonObj) : BroadcastPackage(jsonObj) {
755
+ meshProtocol = jsonObj["protocol"];
756
+ fromNodeId = jsonObj["fromNode"];
757
+ toNodeId = jsonObj["toNode"];
758
+ meshType = jsonObj["meshType"];
759
+ rawPayload = jsonObj["payload"].as<TSTRING>();
760
+ rssi = jsonObj["rssi"];
761
+ hopCount = jsonObj["hops"];
762
+ meshTimestamp = jsonObj["meshTs"];
763
+ gatewayNodeId = jsonObj["gateway"];
764
+ meshNetworkId = jsonObj["meshId"].as<TSTRING>();
765
+ messageType = jsonObj["message_type"] | 603;
766
+ }
767
+
768
+ JsonObject addTo(JsonObject&& jsonObj) const {
769
+ jsonObj = BroadcastPackage::addTo(std::move(jsonObj));
770
+
771
+ jsonObj["protocol"] = meshProtocol;
772
+ jsonObj["fromNode"] = fromNodeId;
773
+ jsonObj["toNode"] = toNodeId;
774
+ jsonObj["meshType"] = meshType;
775
+ jsonObj["payload"] = rawPayload;
776
+ jsonObj["rssi"] = rssi;
777
+ jsonObj["hops"] = hopCount;
778
+ jsonObj["meshTs"] = meshTimestamp;
779
+ jsonObj["gateway"] = gatewayNodeId;
780
+ jsonObj["meshId"] = meshNetworkId;
781
+ jsonObj["message_type"] = messageType;
782
+
783
+ return jsonObj;
784
+ }
785
+
786
+ #if ARDUINOJSON_VERSION_MAJOR < 7
787
+ size_t jsonObjectSize() const {
788
+ return JSON_OBJECT_SIZE(noJsonFields + 11) + rawPayload.length() +
789
+ meshNetworkId.length();
790
+ }
791
+ #endif
792
+ };
793
+
239
794
  } // namespace alteriom
240
795
 
241
796
  #endif // ALTERIOM_SENSOR_PACKAGE_HPP
@@ -60,7 +60,7 @@ class CommandPackage : public painlessmesh::plugin::SinglePackage {
60
60
  TSTRING parameters = ""; // Command parameters as JSON string
61
61
  uint32_t commandId = 0; // Unique command identifier for tracking
62
62
 
63
- CommandPackage() : SinglePackage(201) {} // Type ID 201 for Alteriom commands
63
+ CommandPackage() : SinglePackage(400) {} // Type ID 201 for Alteriom commands
64
64
 
65
65
  CommandPackage(JsonObject jsonObj) : SinglePackage(jsonObj) {
66
66
  command = jsonObj["cmd"];
@@ -186,7 +186,7 @@ void receivedCallback(uint32_t from, String& msg) {
186
186
  case 200: // SensorPackage
187
187
  handleSensorData(alteriom::SensorPackage(obj), from);
188
188
  break;
189
- case 201: // CommandPackage
189
+ case 400: // CommandPackage
190
190
  handleCommand(alteriom::CommandPackage(obj), from);
191
191
  break;
192
192
  case 202: // StatusPackage