@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.
- package/CHANGELOG.md +62 -0
- package/README.md +82 -63
- package/examples/alteriom/README.md +4 -4
- package/examples/alteriom/alteriom_custom_package_template.hpp +320 -0
- package/examples/alteriom/alteriom_sensor_package.hpp +1 -1
- package/examples/alteriom/mppt_example/alteriom_mppt_example.ino +208 -0
- package/examples/bridge_failover/bridge_failover.ino +17 -0
- package/examples/sendToInternet/CMakeLists.txt +54 -0
- package/examples/sendToInternet/PC_NODE_README.md +517 -0
- package/examples/sendToInternet/README.md +39 -1
- package/examples/sendToInternet/build.sh +153 -0
- package/examples/sendToInternet/mock_server_test.ino +361 -0
- package/examples/sendToInternet/pc_mesh_node.cpp +361 -0
- package/library.json +4 -1
- package/library.properties +1 -1
- package/package.json +3 -3
- package/src/AlteriomPainlessMesh.h +5 -13
- package/src/arduino/wifi.hpp +306 -100
- package/src/connection.cpp +10 -0
- package/src/painlessMesh.h +1 -14
- package/src/painlessmesh/connection.hpp +11 -16
- package/src/painlessmesh/gateway.hpp +0 -1061
- package/src/painlessmesh/mesh.hpp +58 -86
- package/src/painlessmesh/message_queue.hpp +1 -2
- package/src/painlessmesh/metrics.hpp +2 -262
- package/src/painlessmesh/validation.hpp +0 -143
- package/docs/README.md +0 -132
- package/docs/alteriom/overview.md +0 -531
- package/docs/api/core-api.md +0 -607
- package/docs/api/shared-gateway.md +0 -1207
- package/docs/architecture/mesh-architecture.md +0 -399
- package/docs/architecture/plugin-system.md +0 -517
- package/docs/getting-started/arduino-manual-install.md +0 -313
- package/docs/getting-started/first-mesh.md +0 -410
- package/docs/getting-started/installation.md +0 -275
- package/docs/getting-started/quickstart.md +0 -158
- package/docs/troubleshooting/common-issues.md +0 -679
- package/docs/troubleshooting/debugging.md +0 -455
- package/docs/troubleshooting/external-device-connection.md +0 -283
- package/docs/troubleshooting/faq.md +0 -574
- 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.
|
|
7
|
+
**Version 1.9.20** - Full repo cleanup, bug fixes, and documentation consistency
|
|
8
8
|
|
|
9
9
|
[](https://github.com/Alteriom/painlessMesh/actions/workflows/ci.yml)
|
|
10
10
|
[](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
|
-
//
|
|
213
|
-
mesh.
|
|
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
|
-
//
|
|
216
|
-
mesh.
|
|
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(
|
|
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
|
-
|
|
399
|
-
|
|
400
|
-
#define
|
|
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
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
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
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
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
|
|
428
|
-
|
|
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.
|
|
563
|
+
## Latest Release: v1.9.20 (March 27, 2026)
|
|
567
564
|
|
|
568
|
-
**
|
|
565
|
+
**Full Repo Cleanup, Bug Fixes & Bridge Coordination Callbacks**
|
|
569
566
|
|
|
570
|
-
-
|
|
571
|
-
-
|
|
572
|
-
-
|
|
573
|
-
-
|
|
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
|
|
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](
|
|
589
|
-
- **[Common Issues](
|
|
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
|
-
[](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](
|
|
673
|
-
- [Installation](
|
|
674
|
-
- [First Mesh](
|
|
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](
|
|
678
|
-
- [Alteriom Extensions](
|
|
679
|
-
- [Examples](examples/) -
|
|
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](
|
|
683
|
-
- [Common Issues](
|
|
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
|
|
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`
|
|
181
|
-
|
|
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
|
|
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
|
|
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:
|