@alteriom/painlessmesh 1.10.0 → 2.0.0
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/BRIDGE_TO_INTERNET.md +167 -29
- package/CHANGELOG.md +483 -0
- package/CONTRIBUTING.md +56 -53
- package/README.md +100 -95
- package/RELEASE_GUIDE.md +81 -780
- package/examples/alteriom/README.md +8 -10
- package/examples/alteriom/alteriom.ino +2 -2
- package/examples/alteriom/alteriom_sensor_package.hpp +17 -11
- package/examples/alteriom/mppt_example/alteriom_custom_package_template.hpp +320 -0
- package/examples/alteriom/mppt_example/alteriom_sensor_package.hpp +1389 -0
- package/examples/alteriom/mppt_example/{alteriom_mppt_example.ino → mppt_example.ino} +4 -0
- package/examples/basic/test/simulator/README.md +3 -3
- package/examples/bridge_failover/README.md +51 -14
- package/examples/commandControl/commandControl.ino +86 -0
- package/examples/commandControl/platformio.ini +26 -0
- package/examples/mqttBridge/mqttBridge.ino +4 -0
- package/examples/mqttBridge/platformio.ini +1 -1
- package/examples/otaSender/otaSender.ino +5 -1
- package/examples/priority/README.md +1 -1
- package/examples/priority/{priority_basic_example.ino → priority_basic_example/priority_basic_example.ino} +4 -4
- package/examples/priority/{priority_with_queue.ino → priority_with_queue/priority_with_queue.ino} +20 -2
- package/examples/reliableSensorLogging/platformio.ini +26 -0
- package/examples/reliableSensorLogging/reliableSensorLogging.ino +151 -0
- package/examples/sendToInternet/README.md +12 -5
- package/examples/sendToInternet/{CMakeLists.txt → pc_node/CMakeLists.txt} +7 -7
- package/examples/sendToInternet/{PC_NODE_README.md → pc_node/PC_NODE_README.md} +15 -15
- package/examples/sendToInternet/{build.sh → pc_node/build.sh} +5 -5
- package/examples/sendToInternet/{pc_mesh_node.cpp → pc_node/pc_mesh_node.cpp} +12 -1
- package/examples/sharedGateway/README.md +1 -2
- package/keywords.txt +50 -1
- package/library.json +8 -6
- package/library.properties +2 -2
- package/package.json +3 -3
- package/src/AlteriomPainlessMesh.h +3 -3
- package/src/arduino/wifi.hpp +556 -126
- package/src/painlessMesh.h +2 -2
- package/src/painlessMeshSTA.cpp +607 -87
- package/src/painlessMeshSTA.h +135 -3
- package/src/painlessmesh/ack.hpp +283 -0
- package/src/painlessmesh/buffer.hpp +70 -8
- package/src/painlessmesh/callback.hpp +38 -5
- package/src/painlessmesh/configuration.hpp +69 -1
- package/src/painlessmesh/connection.hpp +12 -5
- package/src/painlessmesh/gateway.hpp +270 -5
- package/src/painlessmesh/layout.hpp +70 -2
- package/src/painlessmesh/logger.hpp +15 -0
- package/src/painlessmesh/mesh.hpp +552 -48
- package/src/painlessmesh/ntp.hpp +2 -4
- package/src/painlessmesh/plugin.hpp +30 -6
- package/src/painlessmesh/protocol.hpp +55 -2
- package/src/painlessmesh/router.hpp +192 -77
- package/src/painlessmesh/tcp.hpp +10 -0
- package/src/painlessmesh/message_tracker.hpp +0 -311
- /package/examples/sendToInternet/{mock_server_test.ino → mock_server_test/mock_server_test.ino} +0 -0
|
@@ -44,7 +44,7 @@ Extended status package with comprehensive health metrics (18 fields).
|
|
|
44
44
|
- `avgLatency`, `packetLossRate`, `throughput` - Performance metrics
|
|
45
45
|
- `alertFlags`, `lastError` - Alert system
|
|
46
46
|
|
|
47
|
-
### MetricsPackage (Type 204)
|
|
47
|
+
### MetricsPackage (Type 204)
|
|
48
48
|
Comprehensive performance metrics for detailed monitoring and dashboards.
|
|
49
49
|
|
|
50
50
|
**Key Fields:**
|
|
@@ -61,7 +61,7 @@ Comprehensive performance metrics for detailed monitoring and dashboards.
|
|
|
61
61
|
- Network throughput analysis
|
|
62
62
|
- Latency monitoring
|
|
63
63
|
|
|
64
|
-
### HealthCheckPackage (Type 605)
|
|
64
|
+
### HealthCheckPackage (Type 605)
|
|
65
65
|
Proactive health monitoring with problem detection and recommendations.
|
|
66
66
|
|
|
67
67
|
**Key Fields:**
|
|
@@ -93,7 +93,7 @@ Proactive health monitoring with problem detection and recommendations.
|
|
|
93
93
|
- Automated alerting
|
|
94
94
|
- Memory leak detection
|
|
95
95
|
|
|
96
|
-
### MeshNodeListPackage (Type 600)
|
|
96
|
+
### MeshNodeListPackage (Type 600)
|
|
97
97
|
List of all nodes in the mesh network with their status.
|
|
98
98
|
|
|
99
99
|
**Fields:**
|
|
@@ -110,7 +110,7 @@ List of all nodes in the mesh network with their status.
|
|
|
110
110
|
- Network health visualization
|
|
111
111
|
- Device inventory management
|
|
112
112
|
|
|
113
|
-
### MeshTopologyPackage (Type 601)
|
|
113
|
+
### MeshTopologyPackage (Type 601)
|
|
114
114
|
Mesh network topology with all connections between nodes.
|
|
115
115
|
|
|
116
116
|
**Fields:**
|
|
@@ -128,7 +128,7 @@ Mesh network topology with all connections between nodes.
|
|
|
128
128
|
- Network optimization
|
|
129
129
|
- Path analysis
|
|
130
130
|
|
|
131
|
-
### MeshAlertPackage (Type 602)
|
|
131
|
+
### MeshAlertPackage (Type 602)
|
|
132
132
|
Mesh network alerts for critical events and warnings.
|
|
133
133
|
|
|
134
134
|
**Fields:**
|
|
@@ -147,7 +147,7 @@ Mesh network alerts for critical events and warnings.
|
|
|
147
147
|
- Problem notification
|
|
148
148
|
- Event logging
|
|
149
149
|
|
|
150
|
-
### MeshBridgePackage (Type 603)
|
|
150
|
+
### MeshBridgePackage (Type 603)
|
|
151
151
|
Bridge package for encapsulating native mesh protocol messages.
|
|
152
152
|
|
|
153
153
|
**Fields:**
|
|
@@ -184,7 +184,7 @@ OTA enhancement example demonstrating:
|
|
|
184
184
|
- Alert system implementation
|
|
185
185
|
- Usage patterns for advanced features
|
|
186
186
|
|
|
187
|
-
### `metrics_health_node.ino`
|
|
187
|
+
### `metrics_health_node.ino`
|
|
188
188
|
Comprehensive monitoring node example demonstrating:
|
|
189
189
|
- MetricsPackage (Type 204) collection and broadcasting
|
|
190
190
|
- HealthCheckPackage (Type 605) proactive monitoring (MESH_METRICS)
|
|
@@ -278,9 +278,7 @@ This validates:
|
|
|
278
278
|
|
|
279
279
|
For developers adding new configuration fields to Alteriom packages:
|
|
280
280
|
|
|
281
|
-
- **[API Design Guidelines](../../docs/API_DESIGN_GUIDELINES.md)** - Comprehensive guide on when to use nested vs flat JSON structures
|
|
282
281
|
- **[Time Field Naming Convention](alteriom_sensor_package.hpp#L10-L55)** - How to handle time-based fields (ms/s variants)
|
|
283
|
-
- **[Boolean Naming Convention](../../docs/BOOLEAN_NAMING_CONVENTION.md)** - Consistent patterns for boolean fields (*Set, *Enabled, is*)
|
|
284
282
|
- **[JSON Structure Guidelines](alteriom_sensor_package.hpp#L57-L121)** - Quick reference for nesting patterns
|
|
285
283
|
|
|
286
|
-
These guidelines ensure consistency and maintainability across all Alteriom packages.
|
|
284
|
+
These guidelines ensure consistency and maintainability across all Alteriom packages.
|
|
@@ -90,7 +90,7 @@ void handleStatusRequest() {
|
|
|
90
90
|
status.uptime = millis() / 1000;
|
|
91
91
|
status.freeMemory = ESP.getFreeHeap() / 1024; // KB
|
|
92
92
|
status.wifiStrength = 75; // Placeholder
|
|
93
|
-
status.firmwareVersion = "
|
|
93
|
+
status.firmwareVersion = "2.0.0";
|
|
94
94
|
|
|
95
95
|
// Serialize and send (using ArduinoJson v7 API)
|
|
96
96
|
JsonDocument doc;
|
|
@@ -184,4 +184,4 @@ void changedConnectionCallback() { Serial.printf("Changed connections\n"); }
|
|
|
184
184
|
|
|
185
185
|
void nodeTimeAdjustedCallback(int32_t offset) {
|
|
186
186
|
Serial.printf("Adjusted time %u. Offset = %d\n", mesh.getNodeTime(), offset);
|
|
187
|
-
}
|
|
187
|
+
}
|
|
@@ -1233,7 +1233,7 @@ class BridgeStatusPackage : public painlessmesh::plugin::BroadcastPackage {
|
|
|
1233
1233
|
*
|
|
1234
1234
|
* Election process:
|
|
1235
1235
|
* 1. Bridge failure detected (no heartbeat for 60+ seconds)
|
|
1236
|
-
* 2. Nodes broadcast BridgeElectionPackage with
|
|
1236
|
+
* 2. Nodes broadcast BridgeElectionPackage with router RSSI and channel
|
|
1237
1237
|
* 3. 5-second collection window for all candidates
|
|
1238
1238
|
* 4. Each node evaluates all candidates locally (deterministic)
|
|
1239
1239
|
* 5. Winner promotes itself to bridge, others remain as regular nodes
|
|
@@ -1242,11 +1242,12 @@ class BridgeStatusPackage : public painlessmesh::plugin::BroadcastPackage {
|
|
|
1242
1242
|
*/
|
|
1243
1243
|
class BridgeElectionPackage : public painlessmesh::plugin::BroadcastPackage {
|
|
1244
1244
|
public:
|
|
1245
|
-
int8_t routerRSSI = 0;
|
|
1246
|
-
|
|
1247
|
-
uint32_t
|
|
1248
|
-
uint32_t
|
|
1249
|
-
|
|
1245
|
+
int8_t routerRSSI = 0; // Router WiFi signal strength in dBm (-127 to 0)
|
|
1246
|
+
uint8_t routerChannel = 0; // Channel the candidate would use as bridge
|
|
1247
|
+
uint32_t uptime = 0; // Node uptime in milliseconds
|
|
1248
|
+
uint32_t freeMemory = 0; // Free memory in bytes
|
|
1249
|
+
uint32_t timestamp = 0; // Election timestamp
|
|
1250
|
+
TSTRING routerSSID = ""; // Router SSID (for verification)
|
|
1250
1251
|
|
|
1251
1252
|
// MQTT Schema v0.7.3+ message_type
|
|
1252
1253
|
uint16_t messageType = 611; // BRIDGE_ELECTION
|
|
@@ -1255,6 +1256,7 @@ class BridgeElectionPackage : public painlessmesh::plugin::BroadcastPackage {
|
|
|
1255
1256
|
|
|
1256
1257
|
BridgeElectionPackage(JsonObject jsonObj) : BroadcastPackage(jsonObj) {
|
|
1257
1258
|
routerRSSI = jsonObj["routerRSSI"] | 0;
|
|
1259
|
+
routerChannel = jsonObj["routerChannel"] | 0;
|
|
1258
1260
|
uptime = jsonObj["uptime"] | 0;
|
|
1259
1261
|
freeMemory = jsonObj["freeMemory"] | 0;
|
|
1260
1262
|
timestamp = jsonObj["timestamp"] | 0;
|
|
@@ -1265,6 +1267,7 @@ class BridgeElectionPackage : public painlessmesh::plugin::BroadcastPackage {
|
|
|
1265
1267
|
JsonObject addTo(JsonObject&& jsonObj) const {
|
|
1266
1268
|
jsonObj = BroadcastPackage::addTo(std::move(jsonObj));
|
|
1267
1269
|
jsonObj["routerRSSI"] = routerRSSI;
|
|
1270
|
+
jsonObj["routerChannel"] = routerChannel;
|
|
1268
1271
|
jsonObj["uptime"] = uptime;
|
|
1269
1272
|
jsonObj["freeMemory"] = freeMemory;
|
|
1270
1273
|
jsonObj["timestamp"] = timestamp;
|
|
@@ -1275,7 +1278,7 @@ class BridgeElectionPackage : public painlessmesh::plugin::BroadcastPackage {
|
|
|
1275
1278
|
|
|
1276
1279
|
#if ARDUINOJSON_VERSION_MAJOR < 7
|
|
1277
1280
|
size_t jsonObjectSize() const {
|
|
1278
|
-
return JSON_OBJECT_SIZE(noJsonFields +
|
|
1281
|
+
return JSON_OBJECT_SIZE(noJsonFields + 7) + routerSSID.length();
|
|
1279
1282
|
}
|
|
1280
1283
|
#endif
|
|
1281
1284
|
};
|
|
@@ -1294,8 +1297,9 @@ class BridgeTakeoverPackage : public painlessmesh::plugin::BroadcastPackage {
|
|
|
1294
1297
|
uint32_t previousBridge = 0; // Previous bridge node ID (0 if none)
|
|
1295
1298
|
TSTRING reason =
|
|
1296
1299
|
""; // Reason for takeover (e.g., "Election winner - best router signal")
|
|
1297
|
-
int8_t routerRSSI = 0;
|
|
1298
|
-
|
|
1300
|
+
int8_t routerRSSI = 0; // New bridge's router signal strength
|
|
1301
|
+
uint8_t routerChannel = 0; // Channel peers follow during takeover
|
|
1302
|
+
uint32_t timestamp = 0; // Takeover timestamp
|
|
1299
1303
|
|
|
1300
1304
|
// MQTT Schema v0.7.3+ message_type
|
|
1301
1305
|
uint16_t messageType = 612; // BRIDGE_TAKEOVER
|
|
@@ -1306,6 +1310,7 @@ class BridgeTakeoverPackage : public painlessmesh::plugin::BroadcastPackage {
|
|
|
1306
1310
|
previousBridge = jsonObj["previousBridge"] | 0;
|
|
1307
1311
|
reason = jsonObj["reason"].as<TSTRING>();
|
|
1308
1312
|
routerRSSI = jsonObj["routerRSSI"] | 0;
|
|
1313
|
+
routerChannel = jsonObj["routerChannel"] | 0;
|
|
1309
1314
|
timestamp = jsonObj["timestamp"] | 0;
|
|
1310
1315
|
messageType = jsonObj["message_type"] | 612;
|
|
1311
1316
|
}
|
|
@@ -1315,6 +1320,7 @@ class BridgeTakeoverPackage : public painlessmesh::plugin::BroadcastPackage {
|
|
|
1315
1320
|
jsonObj["previousBridge"] = previousBridge;
|
|
1316
1321
|
jsonObj["reason"] = reason;
|
|
1317
1322
|
jsonObj["routerRSSI"] = routerRSSI;
|
|
1323
|
+
jsonObj["routerChannel"] = routerChannel;
|
|
1318
1324
|
jsonObj["timestamp"] = timestamp;
|
|
1319
1325
|
jsonObj["message_type"] = messageType;
|
|
1320
1326
|
return jsonObj;
|
|
@@ -1322,7 +1328,7 @@ class BridgeTakeoverPackage : public painlessmesh::plugin::BroadcastPackage {
|
|
|
1322
1328
|
|
|
1323
1329
|
#if ARDUINOJSON_VERSION_MAJOR < 7
|
|
1324
1330
|
size_t jsonObjectSize() const {
|
|
1325
|
-
return JSON_OBJECT_SIZE(noJsonFields +
|
|
1331
|
+
return JSON_OBJECT_SIZE(noJsonFields + 6) + reason.length();
|
|
1326
1332
|
}
|
|
1327
1333
|
#endif
|
|
1328
1334
|
};
|
|
@@ -1380,4 +1386,4 @@ class NTPTimeSyncPackage : public painlessmesh::plugin::BroadcastPackage {
|
|
|
1380
1386
|
|
|
1381
1387
|
} // namespace alteriom
|
|
1382
1388
|
|
|
1383
|
-
#endif // ALTERIOM_SENSOR_PACKAGE_HPP
|
|
1389
|
+
#endif // ALTERIOM_SENSOR_PACKAGE_HPP
|
|
@@ -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 206+ 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
|
+
* 204 : MetricsPackage (network performance metrics)
|
|
34
|
+
* 205 : MpptPackage (MPPT solar charge controller data) <-- this file
|
|
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: 206-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 = 206;
|
|
142
|
+
*
|
|
143
|
+
* MyCustomPackage() : BroadcastPackage(206) {}
|
|
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"] | 206;
|
|
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 205) 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: 205
|
|
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 = 205; // MPPT_DATA
|
|
251
|
+
|
|
252
|
+
// -------------------------------------------------------------------------
|
|
253
|
+
// Constructors
|
|
254
|
+
// -------------------------------------------------------------------------
|
|
255
|
+
|
|
256
|
+
MpptPackage() : BroadcastPackage(205) {}
|
|
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"] | 205;
|
|
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
|