@alteriom/painlessmesh 1.9.18 → 1.9.20

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 (41) hide show
  1. package/CHANGELOG.md +62 -0
  2. package/README.md +82 -63
  3. package/examples/alteriom/README.md +4 -4
  4. package/examples/alteriom/alteriom_custom_package_template.hpp +320 -0
  5. package/examples/alteriom/alteriom_sensor_package.hpp +1 -1
  6. package/examples/alteriom/mppt_example/alteriom_mppt_example.ino +208 -0
  7. package/examples/bridge_failover/bridge_failover.ino +17 -0
  8. package/examples/sendToInternet/CMakeLists.txt +54 -0
  9. package/examples/sendToInternet/PC_NODE_README.md +517 -0
  10. package/examples/sendToInternet/README.md +39 -1
  11. package/examples/sendToInternet/build.sh +153 -0
  12. package/examples/sendToInternet/mock_server_test.ino +361 -0
  13. package/examples/sendToInternet/pc_mesh_node.cpp +361 -0
  14. package/library.json +4 -1
  15. package/library.properties +1 -1
  16. package/package.json +3 -3
  17. package/src/AlteriomPainlessMesh.h +5 -13
  18. package/src/arduino/wifi.hpp +306 -100
  19. package/src/connection.cpp +10 -0
  20. package/src/painlessMesh.h +1 -14
  21. package/src/painlessmesh/connection.hpp +11 -16
  22. package/src/painlessmesh/gateway.hpp +0 -1061
  23. package/src/painlessmesh/mesh.hpp +58 -86
  24. package/src/painlessmesh/message_queue.hpp +1 -2
  25. package/src/painlessmesh/metrics.hpp +2 -262
  26. package/src/painlessmesh/validation.hpp +0 -143
  27. package/docs/README.md +0 -132
  28. package/docs/alteriom/overview.md +0 -531
  29. package/docs/api/core-api.md +0 -607
  30. package/docs/api/shared-gateway.md +0 -1207
  31. package/docs/architecture/mesh-architecture.md +0 -399
  32. package/docs/architecture/plugin-system.md +0 -517
  33. package/docs/getting-started/arduino-manual-install.md +0 -313
  34. package/docs/getting-started/first-mesh.md +0 -410
  35. package/docs/getting-started/installation.md +0 -275
  36. package/docs/getting-started/quickstart.md +0 -158
  37. package/docs/troubleshooting/common-issues.md +0 -679
  38. package/docs/troubleshooting/debugging.md +0 -455
  39. package/docs/troubleshooting/external-device-connection.md +0 -283
  40. package/docs/troubleshooting/faq.md +0 -574
  41. package/docs/tutorials/basic-examples.md +0 -718
package/CHANGELOG.md CHANGED
@@ -13,6 +13,68 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
13
13
 
14
14
  ### Fixed
15
15
 
16
+ ## [1.9.20] - 2026-03-27
17
+
18
+ ### Added
19
+
20
+ - **Bridge Coordination Monitoring Callbacks** - User-facing API for observing bridge coordination events
21
+ - `mesh.onBridgeCoordination(cb)` — fires on every coordination message (~30s per bridge)
22
+ - `mesh.onBridgeCoordinationChanged(cb)` — fires on state changes: "new", "updated", "lost"
23
+ - Works on both bridge and non-bridge nodes (monitoring is not bridge-only)
24
+ - Lost bridge detection with 60-second timeout
25
+ - 5 new test scenarios with 18 assertions for change detection logic
26
+
27
+ ### Fixed
28
+
29
+ - **Full Repo Cleanup** - Comprehensive audit and cleanup of the entire codebase (#357)
30
+ - Fix double-free of mScheduler in ~Mesh() destructor
31
+ - Fix int8_t RSSI aggregation overflow (widened to int32_t)
32
+ - Fix Task object memory leak in scheduleAsyncClientDeletion
33
+ - Replace blocking delay(1000) with yield-based approach in bridge init
34
+ - Add max-iteration guard to getPathToNode (prevents infinite loop)
35
+ - Move static vars from connection.hpp to connection.cpp (ODR violation fix)
36
+ - Initialize shouldContainRoot to false (undefined behavior fix)
37
+ - Fix getAccurateTime() to always return seconds
38
+ - Fix cancelInternetRequest reentrancy via scheduled callback
39
+ - Widen BFS hop counter from uint8_t to uint16_t
40
+ - Remove dead revert path in promoteToBridge
41
+ - Cap knownBridgePeers at 32 entries
42
+ - Increase WiFi event semaphore timeout from 100 to 1000 ticks
43
+ - Inline getGatewayCount() to avoid temporary vector allocation
44
+ - Remove MessageQueue::reserve(1000) (wasted heap on ESP8266)
45
+ - Fix Timer::elapsed_us() to return microseconds on ESP32
46
+ - Cache hasActualInternetAccess() with 60s TTL
47
+ - Remove ~3,600 lines of dead code (unused classes, stubs, AI-generated shelf code)
48
+ - Remove 40 AI-generated markdown files from root directory
49
+ - Delete abandoned documentation systems (docs-website/, website/)
50
+ - Fix all CI/CD workflow action versions
51
+ - Remove 15 fake test files (only REQUIRE(true) assertions)
52
+ - Fix millis()/micros() test mocks to prevent uint32_t overflow
53
+ - **Documentation Consistency** - Feature audit and doc fixes (#358)
54
+ - Fix README version (1.9.17 -> 1.9.20)
55
+ - Fix 6 broken documentation links
56
+ - Fix Message Queue API examples to use real enableMessageQueue/queueMessage API
57
+ - Remove non-existent onBridgeCoordination callback from examples
58
+ - Fix EnhancedStatusPackage type ID comment (203 -> 604)
59
+ - Rewrite Quick Start example to use TaskScheduler instead of delay()
60
+ - Remove dead conditional includes from painlessMesh.h
61
+ - Add missing examples to library.json
62
+
63
+ ## [1.9.19] - 2025-12-21
64
+
65
+ ### Fixed
66
+
67
+ - **Gateway Connectivity Error Non-Retryable Fix** - Infrastructure errors no longer waste time with futile retries
68
+ - Gateway connectivity errors ("Router has no internet", "Gateway WiFi not connected") are now non-retryable
69
+ - Distinguishes between infrastructure issues (need user fix) and transient errors (can retry)
70
+ - Provides immediate failure feedback instead of retrying for ~14+ seconds
71
+ - Saves battery and network resources by avoiding retries that can't succeed
72
+ - Clear error messages help users identify and fix infrastructure problems quickly
73
+ - Transient errors (HTTP 203, 5xx, 429, timeouts) still retry with exponential backoff
74
+ - New comprehensive test suite with 25 assertions covering retry logic
75
+ - Documentation: ISSUE_GATEWAY_CONNECTIVITY_NON_RETRYABLE_FIX.md
76
+ - Fully backward compatible: No API changes required
77
+
16
78
  ## [1.9.18] - 2025-12-21
17
79
 
18
80
  ### Added
package/README.md CHANGED
@@ -4,7 +4,7 @@
4
4
 
5
5
  <div align="center">
6
6
 
7
- **Version 1.9.18** - Internet connectivity check enhancement with DNS resolution verification
7
+ **Version 1.9.20** - Full repo cleanup, bug fixes, and documentation consistency
8
8
 
9
9
  [![CI/CD Pipeline](https://github.com/Alteriom/painlessMesh/actions/workflows/ci.yml/badge.svg)](https://github.com/Alteriom/painlessMesh/actions/workflows/ci.yml)
10
10
  [![Documentation](https://github.com/Alteriom/painlessMesh/actions/workflows/docs.yml/badge.svg)](https://github.com/Alteriom/painlessMesh/actions/workflows/docs.yml)
@@ -209,11 +209,25 @@ mesh.initAsBridge(MESH_PREFIX, MESH_PASSWORD,
209
209
  ROUTER_SSID, ROUTER_PASSWORD,
210
210
  &userScheduler, MESH_PORT, 10);
211
211
 
212
- // Configure load balancing strategy
213
- mesh.setBridgeSelectionStrategy(ROUND_ROBIN);
212
+ // Enable bridge failover for high availability
213
+ mesh.enableBridgeFailover(true);
214
+
215
+ // Monitor bridge coordination (fires every ~30s per bridge)
216
+ mesh.onBridgeCoordination(
217
+ [](const plugin::BridgeCoordinationPackage& pkg, uint32_t fromNode) {
218
+ Serial.printf("Bridge %u: priority=%d, load=%d%%\n",
219
+ fromNode, pkg.priority, pkg.load);
220
+ }
221
+ );
214
222
 
215
- // Monitor bridge coordination
216
- mesh.onBridgeCoordination(&bridgeCoordinationCallback);
223
+ // Get notified when bridge state changes
224
+ mesh.onBridgeCoordinationChanged(
225
+ [](const plugin::BridgeCoordinationPackage& pkg, uint32_t fromNode,
226
+ TSTRING changeType) {
227
+ Serial.printf("Bridge %s: %u (role=%s)\n",
228
+ changeType.c_str(), fromNode, pkg.role.c_str());
229
+ }
230
+ );
217
231
  ```
218
232
 
219
233
  See [BRIDGE_TO_INTERNET.md](BRIDGE_TO_INTERNET.md) for multi-bridge documentation.
@@ -239,16 +253,11 @@ See [BRIDGE_TO_INTERNET.md](BRIDGE_TO_INTERNET.md) for multi-bridge documentatio
239
253
  **Example:**
240
254
  ```cpp
241
255
  // Enable message queue with max 100 messages
242
- mesh.enableMessageQueue(true);
243
- mesh.setMaxQueueSize(100);
256
+ mesh.enableMessageQueue(true, 100);
244
257
 
245
258
  // Queue critical alarm message
246
259
  String criticalAlarm = "{\"sensor\":\"O2\",\"value\":2.5,\"alarm\":true}";
247
- mesh.queueMessage(criticalAlarm, CRITICAL);
248
-
249
- // Set callbacks
250
- mesh.onQueueFull(&queueFullCallback);
251
- mesh.onQueueFlushed(&queueFlushedCallback);
260
+ mesh.queueMessage(bridgeNodeId, criticalAlarm);
252
261
  ```
253
262
 
254
263
  See [BRIDGE_TO_INTERNET.md](BRIDGE_TO_INTERNET.md) for message queue documentation.
@@ -393,46 +402,34 @@ If platformio is used to install the library, then the dependencies will be inst
393
402
 
394
403
  ```cpp
395
404
  #include "painlessMesh.h"
396
- #include "examples/alteriom/alteriom_sensor_package.hpp"
397
405
 
398
- using namespace alteriom;
399
-
400
- #define MESH_PREFIX "AlteriomMesh"
401
- #define MESH_PASSWORD "your_password"
402
- #define MESH_PORT 5555
406
+ #define MESH_PREFIX "whateverYouLike"
407
+ #define MESH_PASSWORD "somethingSneaky"
408
+ #define MESH_PORT 5555
403
409
 
404
410
  Scheduler userScheduler;
405
411
  painlessMesh mesh;
406
412
 
407
- void setup() {
408
- Serial.begin(115200);
409
- mesh.init(MESH_PREFIX, MESH_PASSWORD, &userScheduler, MESH_PORT);
410
- mesh.onReceive(&receivedCallback);
413
+ Task taskSendMessage(TASK_SECOND * 30, TASK_FOREVER, []() {
414
+ String msg = "Hello from node ";
415
+ msg += mesh.getNodeId();
416
+ mesh.sendBroadcast(msg);
417
+ });
418
+
419
+ void receivedCallback(uint32_t from, String &msg) {
420
+ Serial.printf("Received from %u: %s\n", from, msg.c_str());
411
421
  }
412
422
 
413
- void loop() {
414
- mesh.update();
415
-
416
- // Create and send sensor data
417
- SensorPackage sensor;
418
- sensor.temperature = 25.5;
419
- sensor.humidity = 60.0;
420
- sensor.sensorId = mesh.getNodeId();
421
- sensor.timestamp = mesh.getNodeTime();
422
-
423
- mesh.sendBroadcast(sensor.toJsonString());
424
- delay(30000); // Send every 30 seconds
423
+ void setup() {
424
+ Serial.begin(115200);
425
+ mesh.init(MESH_PREFIX, MESH_PASSWORD, &userScheduler, MESH_PORT);
426
+ mesh.onReceive(&receivedCallback);
427
+ userScheduler.addTask(taskSendMessage);
428
+ taskSendMessage.enable();
425
429
  }
426
430
 
427
- void receivedCallback(uint32_t from, String& msg) {
428
- JsonDocument doc; // ArduinoJson v7
429
- deserializeJson(doc, msg);
430
-
431
- if (doc["type"] == 200) { // SensorPackage
432
- SensorPackage sensor(doc.as<JsonObject>());
433
- Serial.printf("Sensor %u: %.1f°C, %.1f%% RH\n",
434
- sensor.sensorId, sensor.temperature, sensor.humidity);
435
- }
431
+ void loop() {
432
+ mesh.update();
436
433
  }
437
434
  ```
438
435
 
@@ -563,14 +560,16 @@ These are the message types used by applications built on painlessMesh:
563
560
  - **Event Coordination** - Synchronized displays, distributed processing
564
561
  - **Bridge Networks** - Connect mesh to WiFi/Internet/MQTT - [📖 Bridge Guide](BRIDGE_TO_INTERNET.md)
565
562
 
566
- ## Latest Release: v1.9.17 (December 21, 2025)
563
+ ## Latest Release: v1.9.20 (March 27, 2026)
567
564
 
568
- **Documentation Enhancement & Package Organization**
565
+ **Full Repo Cleanup, Bug Fixes & Bridge Coordination Callbacks**
569
566
 
570
- - 📚 **Production-Level Documentation** - Removed phase terminology for clearer product positioning
571
- - 📦 **Complete Package Catalog** - All 19 application-level package types documented in numerical order
572
- - 🎯 **Enhanced Feature Clarity** - Improved Advanced Features section with comprehensive capability list
573
- - **Professional Quality** - Documentation suitable for enterprise adoption and production deployments
567
+ - New `onBridgeCoordination()` and `onBridgeCoordinationChanged()` monitoring callbacks
568
+ - Fixed 13 critical/high/medium bugs (double-free, RSSI overflow, memory leaks, blocking delays)
569
+ - Removed ~3,600 lines of dead code and 40 AI-generated docs
570
+ - Fixed all CI/CD workflows (corrected action versions)
571
+ - Removed 15 fake test files, fixed test infrastructure
572
+ - Documentation consistency audit — fixed broken links, API examples, version references
574
573
 
575
574
  **Recent Key Features (v1.9.0 - v1.9.16):**
576
575
 
@@ -578,16 +577,16 @@ These are the message types used by applications built on painlessMesh:
578
577
  - 🌉 **Improved Bridge Detection** - `getPrimaryBridge()` returns last known bridge when disconnected
579
578
  - ⚡ **Enhanced TCP Reliability** - Exponential backoff and increased retries for mesh connections
580
579
  - 🛡️ **Race Condition Fixes** - Improved bridge status and connection validation
581
- - 📦 **Consolidated Examples** - Streamlined to 14 essential examples
580
+ - 📦 **Consolidated Examples** - Streamlined to 15 essential examples
582
581
  - ⚙️ **Configurable Election Timing** - Prevent split-brain with `setElectionStartupDelay()` and `setElectionRandomDelay()`
583
582
 
584
583
  **[📋 Full CHANGELOG](CHANGELOG.md)**
585
584
 
586
585
  ## Getting Help
587
586
 
588
- - **[FAQ](docs/troubleshooting/faq.md)** - Common questions and solutions
589
- - **[Common Issues](docs/troubleshooting/common-issues.md)** - Troubleshooting guide
590
- - **[GitHub Issues](https://github.com/Alteriom/painlessMesh/issues)** - Bug reports and feature requests
587
+ - **[FAQ](USER_GUIDE.md)** - Common questions and solutions
588
+ - **[Common Issues](USER_GUIDE.md)** - Troubleshooting guide
589
+ - **[GitHub Issues](https://github.com/Alteriom/painlessMesh/issues)** - Bug reports and feature requests
591
590
  - **[Community Forum](https://groups.google.com/forum/#!forum/painlessmesh-user)** - Community support
592
591
  - **[API Documentation](https://alteriom.github.io/painlessMesh/#/api/doxygen)** - Generated API docs
593
592
 
@@ -610,6 +609,28 @@ run-parts --regex catch_ bin/ # Run tests
610
609
  - **Dependencies**: ArduinoJson 7.x, TaskScheduler 4.x
611
610
  - **Development**: CMake, Ninja, Boost (for desktop testing)
612
611
 
612
+ ### Testing Bridge/Internet Functionality
613
+
614
+ painlessMesh includes a **Mock HTTP Server** for testing `sendToInternet()` functionality without requiring actual Internet connectivity. This enables:
615
+
616
+ - 🚀 **Fast testing cycles** - Instant responses instead of waiting for external APIs
617
+ - 🔧 **Offline development** - No Internet connection required
618
+ - ✅ **Reproducible scenarios** - Control all test conditions precisely
619
+ - 🤖 **CI/CD automation** - Automated testing in pipelines
620
+
621
+ ```bash
622
+ # Start mock server
623
+ cd test/mock-http-server
624
+ python3 server.py
625
+
626
+ # Test various HTTP scenarios
627
+ curl http://localhost:8080/status/200 # Success
628
+ curl http://localhost:8080/status/404 # Not Found
629
+ curl http://localhost:8080/whatsapp?... # WhatsApp API simulation
630
+ ```
631
+
632
+ See [Mock HTTP Server Documentation](test/mock-http-server/README.md) for complete usage guide.
633
+
613
634
  ### CI/CD Pipeline
614
635
 
615
636
  painlessMesh features a state-of-the-art automated CI/CD pipeline:
@@ -651,8 +672,6 @@ We try to follow the [git flow](https://www.atlassian.com/git/tutorials/comparin
651
672
 
652
673
  If you like the library please consider supporting its development. Your contributions help me spend more time improving painlessMesh.
653
674
 
654
- [![PayPal Donation](paypal/qrcode.png)](https://www.paypal.com/paypalme/domlavoie)
655
-
656
675
  **[Donate via PayPal](https://www.paypal.com/paypalme/domlavoie)** • [dominic.lavoie@gmail.com](mailto:dominic.lavoie@gmail.com)
657
676
 
658
677
  ## 📚 Documentation
@@ -669,18 +688,18 @@ If you like the library please consider supporting its development. Your contrib
669
688
  ### 🚀 Quick Links
670
689
 
671
690
  **New to AlteriomPainlessMesh?**
672
- - [Quick Start](docs/getting-started/quickstart.md) - Get your first mesh running in 5 minutes
673
- - [Installation](docs/getting-started/installation.md) - Arduino IDE and PlatformIO setup
674
- - [First Mesh](docs/getting-started/first-mesh.md) - Build a multi-node network
691
+ - [Quick Start](USER_GUIDE.md) - Get your first mesh running in 5 minutes
692
+ - [Installation](USER_GUIDE.md) - Arduino IDE and PlatformIO setup
693
+ - [First Mesh](USER_GUIDE.md) - Build a multi-node network
675
694
 
676
695
  **Reference Documentation:**
677
- - [Core API](docs/api/core-api.md) - painlessMesh class methods
678
- - [Alteriom Extensions](docs/alteriom/overview.md) - SensorPackage, CommandPackage, StatusPackage
679
- - [Examples](examples/) - 16 working examples for common scenarios
696
+ - [Core API](USER_GUIDE.md) - painlessMesh class methods
697
+ - [Alteriom Extensions](examples/alteriom/README.md) - SensorPackage, CommandPackage, StatusPackage
698
+ - [Examples](examples/) - 15 working examples for common scenarios
680
699
 
681
700
  **Need Help?**
682
- - [FAQ](docs/troubleshooting/faq.md) - Frequently asked questions
683
- - [Common Issues](docs/troubleshooting/common-issues.md) - Troubleshooting guide
701
+ - [FAQ](USER_GUIDE.md) - Frequently asked questions
702
+ - [Common Issues](USER_GUIDE.md) - Troubleshooting guide
684
703
  - [GitHub Issues](https://github.com/Alteriom/painlessMesh/issues) - Bug reports and support
685
704
 
686
705
  ## 🔧 Quick API Reference
@@ -34,7 +34,7 @@ Broadcast package for sharing device health and status information.
34
34
  - `wifiStrength` - WiFi signal strength (0-100)
35
35
  - `firmwareVersion` - Current firmware version string
36
36
 
37
- ### EnhancedStatusPackage (Type 203) - Phase 1
37
+ ### EnhancedStatusPackage (Type 604)
38
38
  Extended status package with comprehensive health metrics (18 fields).
39
39
 
40
40
  **Additional Fields:**
@@ -177,12 +177,12 @@ Complete Arduino sketch demonstrating:
177
177
  - Message type discrimination
178
178
  - Integration with painlessMesh
179
179
 
180
- ### `phase1_features.ino` (NEW)
181
- Phase 1 OTA enhancement example demonstrating:
180
+ ### `phase1_features.ino`
181
+ OTA enhancement example demonstrating:
182
182
  - Compressed OTA transfer infrastructure
183
183
  - Enhanced status reporting with comprehensive metrics
184
184
  - Alert system implementation
185
- - Usage patterns for Phase 1 features
185
+ - Usage patterns for advanced features
186
186
 
187
187
  ### `metrics_health_node.ino` (NEW in v1.7.7)
188
188
  Comprehensive monitoring node example demonstrating:
@@ -0,0 +1,320 @@
1
+ #ifndef ALTERIOM_CUSTOM_PACKAGE_TEMPLATE_HPP
2
+ #define ALTERIOM_CUSTOM_PACKAGE_TEMPLATE_HPP
3
+
4
+ #include "painlessmesh/plugin.hpp"
5
+
6
+ /**
7
+ * @file alteriom_custom_package_template.hpp
8
+ * @brief Template and example for creating custom Alteriom packages
9
+ *
10
+ * HOW TO CREATE A CUSTOM PACKAGE
11
+ * ===============================
12
+ *
13
+ * This file serves two purposes:
14
+ * 1. A step-by-step guide to creating custom painlessMesh packages
15
+ * 2. A concrete example: MpptPackage for MPPT solar charge controllers
16
+ *
17
+ * QUICK START
18
+ * -----------
19
+ * To create your own custom package:
20
+ * 1. Pick an unused Type ID from the table below (use 203+ range)
21
+ * 2. Choose a base class: BroadcastPackage (all nodes) or SinglePackage (one
22
+ * node)
23
+ * 3. Add your data fields with appropriate types
24
+ * 4. Implement the JSON constructor and addTo() method
25
+ * 5. Add a test in test/catch/catch_custom_package.cpp
26
+ *
27
+ * RESERVED TYPE IDS
28
+ * -----------------
29
+ * The following IDs are already used; do NOT reuse them:
30
+ *
31
+ * 200 : SensorPackage (environmental sensors: temp, humidity, pressure)
32
+ * 202 : StatusPackage (device health and configuration)
33
+ * 203 : MpptPackage (MPPT solar charge controller data) <-- this file
34
+ * 204 : MetricsPackage (network performance metrics)
35
+ * 400 : CommandPackage (device control commands)
36
+ * 600 : MeshNodeListPackage
37
+ * 601 : MeshTopologyPackage
38
+ * 602 : MeshAlertPackage
39
+ * 603 : MeshBridgePackage
40
+ * 604 : EnhancedStatusPackage
41
+ * 605 : HealthCheckPackage
42
+ * 610 : BridgeStatusPackage
43
+ * 611 : BridgeElectionPackage
44
+ * 612 : BridgeTakeoverPackage
45
+ * 614 : NTPTimeSyncPackage
46
+ *
47
+ * Available ranges: 205-399 (add your package here and update this table).
48
+ *
49
+ *
50
+ * CHOOSING BASE CLASS
51
+ * -------------------
52
+ *
53
+ * BroadcastPackage – sent to every node in the mesh.
54
+ * Use for: sensor readings, status updates, telemetry data.
55
+ * Base fields: from, routing (BROADCAST), type (noJsonFields = 3)
56
+ *
57
+ * SinglePackage – sent to one specific destination node.
58
+ * Use for: commands, acknowledgements, targeted responses.
59
+ * Base fields: from, dest, routing (SINGLE), type (noJsonFields = 4)
60
+ *
61
+ *
62
+ * FIELD TYPE GUIDELINES
63
+ * ---------------------
64
+ *
65
+ * Choose types appropriate for your platform:
66
+ *
67
+ * uint8_t – flags, states, small counts (0-255)
68
+ * uint16_t – larger counts, port numbers, voltages in mV (0-65535)
69
+ * uint32_t – device IDs, Unix timestamps, large counters
70
+ * int8_t – signed small values, e.g. temperature in °C (-128 to +127)
71
+ * float – measured values requiring decimals (4 bytes; fine on both
72
+ * ESP8266 and ESP32)
73
+ * double – high-precision measurements (8 bytes; prefer float on ESP8266)
74
+ * TSTRING – text strings (always use TSTRING, NOT Arduino String)
75
+ * bool – boolean flags; see BOOLEAN NAMING CONVENTION below
76
+ *
77
+ * BOOLEAN NAMING CONVENTION
78
+ * -------------------------
79
+ * *Set suffix – configuration data has been provided
80
+ * e.g., serverAddressSet = true
81
+ * *Enabled suffix – feature is currently active/on
82
+ * e.g., loggingEnabled = true
83
+ * is* prefix – current runtime state
84
+ * e.g., isCharging = true
85
+ *
86
+ *
87
+ * JSON FIELD NAMING
88
+ * -----------------
89
+ *
90
+ * Use SHORT keys to minimise over-the-air message sizes:
91
+ *
92
+ * batteryVoltage -> "bv"
93
+ * solarCurrent -> "sc"
94
+ * chargeState -> "cs"
95
+ * deviceId -> "did"
96
+ * timestamp -> "ts"
97
+ *
98
+ * Always document the mapping in a comment near the field declaration.
99
+ *
100
+ *
101
+ * TIME FIELDS
102
+ * -----------
103
+ *
104
+ * For interval / duration fields, follow the Alteriom time convention:
105
+ * - Store internally in milliseconds (uint32_t)
106
+ * - Serialise both a _ms and a _s variant in JSON
107
+ * - Deserialise from the _ms variant only
108
+ *
109
+ * Timestamp fields (Unix epoch seconds) are an exception: single field, no
110
+ * dual-unit serialisation needed.
111
+ *
112
+ *
113
+ * ARDUINOJSON COMPATIBILITY
114
+ * -------------------------
115
+ *
116
+ * Always wrap the jsonObjectSize() method in an
117
+ * #if ARDUINOJSON_VERSION_MAJOR < 7 guard. ArduinoJson v7 computes document
118
+ * sizes automatically; v6 requires an explicit capacity hint.
119
+ *
120
+ * The formula is:
121
+ * JSON_OBJECT_SIZE(noJsonFields + <number of your own fields>)
122
+ * + <total length of all TSTRING fields>
123
+ *
124
+ *
125
+ * MINIMAL PACKAGE TEMPLATE
126
+ * ========================
127
+ *
128
+ * Copy this skeleton and replace the placeholder names / IDs:
129
+ *
130
+ * @code
131
+ * namespace alteriom {
132
+ *
133
+ * class MyCustomPackage : public painlessmesh::plugin::BroadcastPackage {
134
+ * public:
135
+ * // --- Your data fields ---
136
+ * uint32_t myId = 0;
137
+ * float myValue = 0.0f;
138
+ * TSTRING myText = "";
139
+ *
140
+ * // MQTT message_type (set to your chosen type ID)
141
+ * uint16_t messageType = 205;
142
+ *
143
+ * MyCustomPackage() : BroadcastPackage(205) {}
144
+ *
145
+ * MyCustomPackage(JsonObject jsonObj) : BroadcastPackage(jsonObj) {
146
+ * myId = jsonObj["id"];
147
+ * myValue = jsonObj["val"];
148
+ * myText = jsonObj["txt"].as<TSTRING>();
149
+ * messageType = jsonObj["message_type"] | 205;
150
+ * }
151
+ *
152
+ * JsonObject addTo(JsonObject&& jsonObj) const {
153
+ * jsonObj = BroadcastPackage::addTo(std::move(jsonObj));
154
+ * jsonObj["id"] = myId;
155
+ * jsonObj["val"] = myValue;
156
+ * jsonObj["txt"] = myText;
157
+ * jsonObj["message_type"] = messageType;
158
+ * return jsonObj;
159
+ * }
160
+ *
161
+ * #if ARDUINOJSON_VERSION_MAJOR < 7
162
+ * size_t jsonObjectSize() const {
163
+ * // noJsonFields covers base-class fields; 3 = number of YOUR fields
164
+ * return JSON_OBJECT_SIZE(noJsonFields + 3) + myText.length();
165
+ * }
166
+ * #endif
167
+ * };
168
+ *
169
+ * } // namespace alteriom
170
+ * @endcode
171
+ *
172
+ *
173
+ * CONCRETE EXAMPLE: MpptPackage
174
+ * ==============================
175
+ *
176
+ * The MpptPackage (Type 203) transmits real-time telemetry from an MPPT solar
177
+ * charge controller (e.g. Renegy, Epever, Victron). It is a BroadcastPackage
178
+ * so every node in the mesh receives the data automatically.
179
+ *
180
+ * Fields at a glance:
181
+ *
182
+ * solarVoltage (float, V) – PV panel open-circuit / input voltage
183
+ * solarCurrent (float, A) – PV panel current
184
+ * solarPower (uint16_t, W) – PV panel instantaneous power
185
+ * batteryVoltage (float, V) – Battery terminal voltage
186
+ * batterySOC (uint8_t, %) – State of charge 0–100
187
+ * loadVoltage (float, V) – Load output voltage
188
+ * loadCurrent (float, A) – Load output current
189
+ * chargeState (uint8_t) – Controller state (see ChargeState enum)
190
+ * controllerTemp (int8_t, °C) – Internal controller temperature
191
+ * deviceId (uint32_t) – Unique hardware identifier
192
+ * timestamp (uint32_t) – Unix timestamp of the reading
193
+ */
194
+
195
+ namespace alteriom {
196
+
197
+ /**
198
+ * @brief Charge state values for MpptPackage::chargeState
199
+ */
200
+ enum ChargeState : uint8_t {
201
+ CHARGE_OFF = 0, ///< Charging disabled
202
+ CHARGE_NORMAL = 1, ///< Normal PWM charging
203
+ CHARGE_MPPT = 2, ///< Maximum Power Point Tracking active
204
+ CHARGE_EQUALIZE = 3, ///< Equalisation charge (battery maintenance)
205
+ CHARGE_BOOST = 4, ///< Boost / bulk charge stage
206
+ CHARGE_FLOAT = 5, ///< Float / maintenance stage
207
+ CHARGE_LIMITED = 6 ///< Current-limited charging
208
+ };
209
+
210
+ /**
211
+ * @brief Real-time telemetry from an MPPT solar charge controller
212
+ *
213
+ * Broadcasts voltage, current, power and status from an MPPT charge controller
214
+ * to all nodes in the mesh (e.g. for logging, display, or load management).
215
+ *
216
+ * Adapting for your controller
217
+ * ----------------------------
218
+ * Most MPPT controllers expose data over RS-232/RS-485 or I²C. Read the raw
219
+ * values from your hardware, assign them to the struct fields, then call
220
+ * sendBroadcast() as shown in alteriom_mppt_example.ino.
221
+ *
222
+ * Type ID: 203
223
+ */
224
+ class MpptPackage : public painlessmesh::plugin::BroadcastPackage {
225
+ public:
226
+ // JSON key : "sv" – PV panel voltage in Volts
227
+ float solarVoltage = 0.0f;
228
+ // JSON key : "sc" – PV panel current in Amperes
229
+ float solarCurrent = 0.0f;
230
+ // JSON key : "sp" – PV panel power in Watts
231
+ uint16_t solarPower = 0;
232
+ // JSON key : "bv" – Battery terminal voltage in Volts
233
+ float batteryVoltage = 0.0f;
234
+ // JSON key : "bsoc" – Battery state of charge, 0–100 %
235
+ uint8_t batterySOC = 0;
236
+ // JSON key : "lv" – Load output voltage in Volts
237
+ float loadVoltage = 0.0f;
238
+ // JSON key : "lc" – Load output current in Amperes
239
+ float loadCurrent = 0.0f;
240
+ // JSON key : "cs" – Charge controller state (see ChargeState enum)
241
+ uint8_t chargeState = CHARGE_OFF;
242
+ // JSON key : "ct" – Controller internal temperature in °C (signed)
243
+ int8_t controllerTemp = 0;
244
+ // JSON key : "did" – Unique hardware / node identifier
245
+ uint32_t deviceId = 0;
246
+ // JSON key : "ts" – Unix timestamp of measurement (seconds since epoch)
247
+ uint32_t timestamp = 0;
248
+
249
+ // MQTT Schema message_type for fast classification at the bridge
250
+ uint16_t messageType = 203; // MPPT_DATA
251
+
252
+ // -------------------------------------------------------------------------
253
+ // Constructors
254
+ // -------------------------------------------------------------------------
255
+
256
+ MpptPackage() : BroadcastPackage(203) {}
257
+
258
+ /**
259
+ * @brief Deserialise from a JSON object received over the mesh
260
+ *
261
+ * @param jsonObj Parsed JSON object (ArduinoJson JsonObject)
262
+ */
263
+ MpptPackage(JsonObject jsonObj) : BroadcastPackage(jsonObj) {
264
+ solarVoltage = jsonObj["sv"];
265
+ solarCurrent = jsonObj["sc"];
266
+ solarPower = jsonObj["sp"];
267
+ batteryVoltage = jsonObj["bv"];
268
+ batterySOC = jsonObj["bsoc"];
269
+ loadVoltage = jsonObj["lv"];
270
+ loadCurrent = jsonObj["lc"];
271
+ chargeState = jsonObj["cs"];
272
+ controllerTemp = jsonObj["ct"];
273
+ deviceId = jsonObj["did"];
274
+ timestamp = jsonObj["ts"];
275
+ messageType = jsonObj["message_type"] | 203;
276
+ }
277
+
278
+ // -------------------------------------------------------------------------
279
+ // Serialisation
280
+ // -------------------------------------------------------------------------
281
+
282
+ /**
283
+ * @brief Serialise this package into the provided JSON object
284
+ *
285
+ * Call addTo() on a freshly created JsonObject, then serialise with
286
+ * ArduinoJson's serializeJson() before passing the result to
287
+ * mesh.sendBroadcast().
288
+ */
289
+ JsonObject addTo(JsonObject&& jsonObj) const {
290
+ jsonObj = BroadcastPackage::addTo(std::move(jsonObj));
291
+ jsonObj["sv"] = solarVoltage;
292
+ jsonObj["sc"] = solarCurrent;
293
+ jsonObj["sp"] = solarPower;
294
+ jsonObj["bv"] = batteryVoltage;
295
+ jsonObj["bsoc"] = batterySOC;
296
+ jsonObj["lv"] = loadVoltage;
297
+ jsonObj["lc"] = loadCurrent;
298
+ jsonObj["cs"] = chargeState;
299
+ jsonObj["ct"] = controllerTemp;
300
+ jsonObj["did"] = deviceId;
301
+ jsonObj["ts"] = timestamp;
302
+ jsonObj["message_type"] = messageType;
303
+ return jsonObj;
304
+ }
305
+
306
+ #if ARDUINOJSON_VERSION_MAJOR < 7
307
+ /**
308
+ * @brief Required capacity hint for ArduinoJson v6
309
+ *
310
+ * noJsonFields covers the 3 base-class fields (from, routing, type).
311
+ * The +12 accounts for the 12 fields declared in this class.
312
+ * No TSTRING fields, so no extra string length term.
313
+ */
314
+ size_t jsonObjectSize() const { return JSON_OBJECT_SIZE(noJsonFields + 12); }
315
+ #endif
316
+ };
317
+
318
+ } // namespace alteriom
319
+
320
+ #endif // ALTERIOM_CUSTOM_PACKAGE_TEMPLATE_HPP
@@ -520,7 +520,7 @@ class StatusPackage : public painlessmesh::plugin::BroadcastPackage {
520
520
  *
521
521
  * This is an extended version of StatusPackage that includes additional
522
522
  * mesh statistics, performance metrics, and alerting capabilities.
523
- * Type ID 203 is used to distinguish from the basic StatusPackage (202).
523
+ * Type ID 604 is used to distinguish from the basic StatusPackage (202).
524
524
  */
525
525
  class EnhancedStatusPackage : public painlessmesh::plugin::BroadcastPackage {
526
526
  public: