@alteriom/painlessmesh 1.7.7 → 1.7.9
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 +115 -1
- package/README.md +21 -11
- package/RELEASE_GUIDE.md +57 -8
- package/docs/API_DESIGN_GUIDELINES.md +414 -0
- package/docs/BOOLEAN_NAMING_CONVENTION.md +235 -0
- package/docs/README.md +2 -1
- package/docs/RELEASE_AGENT_SUMMARY.md +386 -0
- package/docs/alteriom/overview.md +23 -0
- package/docs/releases/RELEASE_SUMMARY_v1.7.7.md +1 -1
- package/docs/troubleshooting/ESP32_C6_COMPATIBILITY.md +157 -0
- package/docs/troubleshooting/common-issues.md +28 -0
- package/docs/v1.7.7_MQTT_IMPROVEMENTS.md +21 -3
- package/examples/alteriom/README.md +13 -1
- package/examples/alteriom/alteriom_sensor_package.hpp +377 -3
- package/examples/alteriom/platformio.ini +1 -1
- package/examples/alteriomImproved/platformio.ini +1 -1
- package/examples/alteriomMetricsHealth/metrics_health_node.ino +18 -7
- package/examples/alteriomMetricsHealth/platformio.ini +1 -1
- package/examples/alteriomPhase1/platformio.ini +1 -1
- package/examples/alteriomPhase2/platformio.ini +1 -1
- package/examples/alteriomSensorNode/platformio.ini +1 -1
- package/examples/basic/platformio.ini +1 -1
- package/examples/bridge/alteriom_sensor_package.hpp +1170 -0
- package/examples/bridge/bridge.ino +2 -2
- package/examples/bridge/enhanced_mqtt_bridge.hpp +1 -1
- package/examples/bridge/mqtt_command_bridge.hpp +2 -2
- package/examples/bridge/platformio.ini +2 -1
- package/examples/echoNode/platformio.ini +1 -1
- package/examples/logClient/platformio.ini +1 -1
- package/examples/logServer/platformio.ini +1 -1
- package/examples/mqttStatusBridge/platformio.ini +1 -1
- package/examples/namedMesh/platformio.ini +1 -1
- package/examples/otaReceiver/platformio.ini +1 -1
- package/examples/startHere/platformio.ini +1 -1
- package/examples/webServer/platformio.ini +1 -1
- package/library.json +93 -53
- package/library.properties +1 -1
- package/package.json +2 -2
- package/src/arduino/wifi.hpp +9 -0
- package/src/painlessMeshSTA.cpp +5 -0
|
@@ -0,0 +1,1170 @@
|
|
|
1
|
+
#ifndef ALTERIOM_SENSOR_PACKAGE_HPP
|
|
2
|
+
#define ALTERIOM_SENSOR_PACKAGE_HPP
|
|
3
|
+
|
|
4
|
+
#include "painlessmesh/plugin.hpp"
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* @file alteriom_sensor_package.hpp
|
|
8
|
+
* @brief Alteriom custom package definitions for painlessMesh
|
|
9
|
+
*
|
|
10
|
+
* TIME FIELD NAMING CONVENTION
|
|
11
|
+
* ============================
|
|
12
|
+
*
|
|
13
|
+
* For consistency and developer convenience, all time-based configuration
|
|
14
|
+
* fields in Alteriom packages follow a standardized naming convention:
|
|
15
|
+
*
|
|
16
|
+
* INTERNAL STORAGE:
|
|
17
|
+
* - Always use milliseconds (uint32_t)
|
|
18
|
+
* - Use descriptive field names WITHOUT unit suffixes
|
|
19
|
+
* Examples: sensorReadInterval, transmissionInterval
|
|
20
|
+
*
|
|
21
|
+
* JSON SERIALIZATION:
|
|
22
|
+
* - ALWAYS provide BOTH millisecond and second variants:
|
|
23
|
+
* - {fieldname}_ms : The value in milliseconds (uint32_t)
|
|
24
|
+
* - {fieldname}_s : The value in seconds (uint32_t, calculated as ms/1000)
|
|
25
|
+
*
|
|
26
|
+
* JSON DESERIALIZATION:
|
|
27
|
+
* - Read from the _ms variant (milliseconds are the source of truth)
|
|
28
|
+
* - The _s variant is provided for consumer convenience but not used for input
|
|
29
|
+
*
|
|
30
|
+
* EXAMPLE:
|
|
31
|
+
* ```cpp
|
|
32
|
+
* // C++ field declaration
|
|
33
|
+
* uint32_t sensorReadInterval = 0; // Internal storage in milliseconds
|
|
34
|
+
*
|
|
35
|
+
* // JSON serialization (in addTo method)
|
|
36
|
+
* sensors["read_interval_ms"] = sensorReadInterval; // 30000
|
|
37
|
+
* sensors["read_interval_s"] = sensorReadInterval / 1000; // 30
|
|
38
|
+
*
|
|
39
|
+
* // JSON deserialization (in constructor)
|
|
40
|
+
* sensorReadInterval = sensors["read_interval_ms"] | 0;
|
|
41
|
+
* ```
|
|
42
|
+
*
|
|
43
|
+
* BENEFITS:
|
|
44
|
+
* - Consumer Convenience: No mental overhead for unit conversion
|
|
45
|
+
* - Flexibility: Consumers choose the unit appropriate for their context
|
|
46
|
+
* - Self-Documenting: Field names clearly indicate available units
|
|
47
|
+
* - Precision: Millisecond precision preserved, seconds for readability
|
|
48
|
+
* - Consistency: Predictable pattern across all time-based fields
|
|
49
|
+
*
|
|
50
|
+
* WHEN TO APPLY:
|
|
51
|
+
* - Use this convention for ALL time-based configuration/interval fields
|
|
52
|
+
* - Applies to fields typically >= 1000ms (1 second)
|
|
53
|
+
* - Examples: intervals, timeouts, durations, delays
|
|
54
|
+
* - Does NOT apply to timestamps (which should remain in seconds as per Unix
|
|
55
|
+
* convention)
|
|
56
|
+
*
|
|
57
|
+
*
|
|
58
|
+
* JSON STRUCTURE NESTING GUIDELINES
|
|
59
|
+
* ==================================
|
|
60
|
+
*
|
|
61
|
+
* Alteriom packages follow consistent patterns for organizing configuration
|
|
62
|
+
* data in JSON structures. This ensures maintainability and predictability
|
|
63
|
+
* across the API.
|
|
64
|
+
*
|
|
65
|
+
* IMPORTANT: As of PR #37, ALL configuration sections always serialize with
|
|
66
|
+
* default values, providing predictable JSON structure. Consumers no longer
|
|
67
|
+
* need to check for key existence. Default values (0, false, "") indicate "not
|
|
68
|
+
* configured" state.
|
|
69
|
+
*
|
|
70
|
+
* See docs/API_DESIGN_GUIDELINES.md for comprehensive documentation.
|
|
71
|
+
*
|
|
72
|
+
* QUICK REFERENCE:
|
|
73
|
+
*
|
|
74
|
+
* Use FLAT structure (simple key-value pairs) when:
|
|
75
|
+
* - Section has < 4 total fields
|
|
76
|
+
* - No clear logical subsystems
|
|
77
|
+
* - Simple value types
|
|
78
|
+
*
|
|
79
|
+
* Use NESTED structure (grouped subsections) when:
|
|
80
|
+
* - 3+ fields belong to same logical subsystem
|
|
81
|
+
* - Clear semantic grouping exists
|
|
82
|
+
* - Future extensibility anticipated
|
|
83
|
+
* - Subsystem has distinct meaning
|
|
84
|
+
*
|
|
85
|
+
* CURRENT STRUCTURE PATTERNS:
|
|
86
|
+
*
|
|
87
|
+
* Flat Sections:
|
|
88
|
+
* - display_config: enabled, brightness, timeout (3 fields, no subsystems)
|
|
89
|
+
* - power_config: deep_sleep_enabled, deep_sleep_interval, battery_percent (3
|
|
90
|
+
* fields)
|
|
91
|
+
* - mqtt_retry: retry settings and backoff parameters (9 fields, cohesive
|
|
92
|
+
* purpose)
|
|
93
|
+
* - ota: enabled, server, port (3 fields)
|
|
94
|
+
* - encoding: compression and format settings (3 fields)
|
|
95
|
+
*
|
|
96
|
+
* Nested Sections:
|
|
97
|
+
* - sensors.calibration: temperature_offset, humidity_offset, pressure_offset
|
|
98
|
+
* (Rationale: Calibration is distinct subsystem, optional, semantically
|
|
99
|
+
* separate)
|
|
100
|
+
* - organization: organizationId, customerId, deviceGroup, device_name, etc.
|
|
101
|
+
* (Rationale: Optional metadata subsystem, may not be present on all devices)
|
|
102
|
+
*
|
|
103
|
+
* EXAMPLES:
|
|
104
|
+
*
|
|
105
|
+
* Flat structure:
|
|
106
|
+
* ```json
|
|
107
|
+
* "display_config": {
|
|
108
|
+
* "enabled": true,
|
|
109
|
+
* "brightness": 128,
|
|
110
|
+
* "timeout_ms": 30000,
|
|
111
|
+
* "timeout_s": 30
|
|
112
|
+
* }
|
|
113
|
+
* ```
|
|
114
|
+
*
|
|
115
|
+
* Nested structure:
|
|
116
|
+
* ```json
|
|
117
|
+
* "sensors": {
|
|
118
|
+
* "read_interval_ms": 30000,
|
|
119
|
+
* "read_interval_s": 30,
|
|
120
|
+
* "calibration": {
|
|
121
|
+
* "temperature_offset": 0.5,
|
|
122
|
+
* "humidity_offset": -2.0,
|
|
123
|
+
* "pressure_offset": 0.0
|
|
124
|
+
* }
|
|
125
|
+
* }
|
|
126
|
+
* ```
|
|
127
|
+
*
|
|
128
|
+
* DECISION RULE:
|
|
129
|
+
* When in doubt, prefer flat structures. Nesting should provide clear
|
|
130
|
+
* organizational or extensibility benefits to justify the added complexity.
|
|
131
|
+
*/
|
|
132
|
+
|
|
133
|
+
namespace alteriom {
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* @brief Sensor data package for broadcasting environmental measurements
|
|
137
|
+
*
|
|
138
|
+
* This package is designed for Alteriom's IoT sensor network requirements,
|
|
139
|
+
* allowing nodes to share environmental data across the mesh.
|
|
140
|
+
*/
|
|
141
|
+
class SensorPackage : public painlessmesh::plugin::BroadcastPackage {
|
|
142
|
+
public:
|
|
143
|
+
// Temperature in Celsius
|
|
144
|
+
double temperature = 0.0;
|
|
145
|
+
// Relative humidity percentage
|
|
146
|
+
double humidity = 0.0;
|
|
147
|
+
// Atmospheric pressure in hPa
|
|
148
|
+
double pressure = 0.0;
|
|
149
|
+
// Unique sensor identifier
|
|
150
|
+
uint32_t sensorId = 0;
|
|
151
|
+
// Unix timestamp of measurement
|
|
152
|
+
uint32_t timestamp = 0;
|
|
153
|
+
// Battery level percentage
|
|
154
|
+
uint8_t batteryLevel = 0;
|
|
155
|
+
|
|
156
|
+
// MQTT Schema v0.7.3+ message_type for faster classification
|
|
157
|
+
uint16_t messageType = 200; // SENSOR_DATA
|
|
158
|
+
|
|
159
|
+
// Type ID 200 for Alteriom sensors
|
|
160
|
+
SensorPackage() : BroadcastPackage(200) {}
|
|
161
|
+
|
|
162
|
+
SensorPackage(JsonObject jsonObj) : BroadcastPackage(jsonObj) {
|
|
163
|
+
temperature = jsonObj["temp"];
|
|
164
|
+
humidity = jsonObj["hum"];
|
|
165
|
+
pressure = jsonObj["press"];
|
|
166
|
+
sensorId = jsonObj["sid"];
|
|
167
|
+
timestamp = jsonObj["ts"];
|
|
168
|
+
batteryLevel = jsonObj["bat"];
|
|
169
|
+
messageType = jsonObj["message_type"] | 200;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
JsonObject addTo(JsonObject&& jsonObj) const {
|
|
173
|
+
jsonObj = BroadcastPackage::addTo(std::move(jsonObj));
|
|
174
|
+
jsonObj["temp"] = temperature;
|
|
175
|
+
jsonObj["hum"] = humidity;
|
|
176
|
+
jsonObj["press"] = pressure;
|
|
177
|
+
jsonObj["sid"] = sensorId;
|
|
178
|
+
jsonObj["ts"] = timestamp;
|
|
179
|
+
jsonObj["bat"] = batteryLevel;
|
|
180
|
+
jsonObj["message_type"] = messageType;
|
|
181
|
+
return jsonObj;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
#if ARDUINOJSON_VERSION_MAJOR < 7
|
|
185
|
+
size_t jsonObjectSize() const { return JSON_OBJECT_SIZE(noJsonFields + 6); }
|
|
186
|
+
#endif
|
|
187
|
+
};
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* @brief Command package for controlling Alteriom devices
|
|
191
|
+
*
|
|
192
|
+
* Single-destination package for sending specific commands to individual nodes.
|
|
193
|
+
*/
|
|
194
|
+
class CommandPackage : public painlessmesh::plugin::SinglePackage {
|
|
195
|
+
public:
|
|
196
|
+
uint8_t command = 0; // Command type
|
|
197
|
+
uint32_t targetDevice = 0; // Target device ID
|
|
198
|
+
TSTRING parameters = ""; // Command parameters as JSON string
|
|
199
|
+
uint32_t commandId = 0; // Unique command identifier for tracking
|
|
200
|
+
|
|
201
|
+
// MQTT Schema v0.7.3+ message_type for faster classification
|
|
202
|
+
uint16_t messageType = 400; // COMMAND
|
|
203
|
+
|
|
204
|
+
CommandPackage()
|
|
205
|
+
: SinglePackage(400) {} // Type ID 400 (COMMAND per mqtt-schema v0.7.3+)
|
|
206
|
+
|
|
207
|
+
CommandPackage(JsonObject jsonObj) : SinglePackage(jsonObj) {
|
|
208
|
+
command = jsonObj["cmd"];
|
|
209
|
+
targetDevice = jsonObj["target"];
|
|
210
|
+
parameters = jsonObj["params"].as<TSTRING>();
|
|
211
|
+
commandId = jsonObj["cid"];
|
|
212
|
+
messageType = jsonObj["message_type"] | 400;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
JsonObject addTo(JsonObject&& jsonObj) const {
|
|
216
|
+
jsonObj = SinglePackage::addTo(std::move(jsonObj));
|
|
217
|
+
jsonObj["cmd"] = command;
|
|
218
|
+
jsonObj["target"] = targetDevice;
|
|
219
|
+
jsonObj["params"] = parameters;
|
|
220
|
+
jsonObj["cid"] = commandId;
|
|
221
|
+
jsonObj["message_type"] = messageType;
|
|
222
|
+
return jsonObj;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
#if ARDUINOJSON_VERSION_MAJOR < 7
|
|
226
|
+
size_t jsonObjectSize() const {
|
|
227
|
+
return JSON_OBJECT_SIZE(noJsonFields + 4) + parameters.length();
|
|
228
|
+
}
|
|
229
|
+
#endif
|
|
230
|
+
};
|
|
231
|
+
|
|
232
|
+
/**
|
|
233
|
+
* @brief Status report package for device health monitoring
|
|
234
|
+
*
|
|
235
|
+
* BOOLEAN FIELD NAMING CONVENTION
|
|
236
|
+
* ================================
|
|
237
|
+
*
|
|
238
|
+
* This package follows a standardized naming convention for boolean fields
|
|
239
|
+
* to improve code clarity and reduce ambiguity. See
|
|
240
|
+
* docs/BOOLEAN_NAMING_CONVENTION.md for complete documentation.
|
|
241
|
+
*
|
|
242
|
+
* Three patterns are used:
|
|
243
|
+
*
|
|
244
|
+
* 1. *Set suffix: Configuration data has been provided
|
|
245
|
+
* Example: deviceSecretSet = true means secret is configured
|
|
246
|
+
* Does NOT indicate if feature is enabled or working
|
|
247
|
+
*
|
|
248
|
+
* 2. *Enabled suffix: Feature is currently active/turned on
|
|
249
|
+
* Example: displayEnabled = true means display feature is active
|
|
250
|
+
* Independent of whether required configuration exists
|
|
251
|
+
*
|
|
252
|
+
* 3. is* prefix or *Connected: Current runtime state
|
|
253
|
+
* Example: mqttConnected = true means currently connected
|
|
254
|
+
* Reflects actual runtime conditions, not configuration
|
|
255
|
+
*
|
|
256
|
+
* A feature may have both *Set and *Enabled fields:
|
|
257
|
+
* - otaServerSet=true, otaEnabled=false: Server configured but feature disabled
|
|
258
|
+
* - otaServerSet=false, otaEnabled=true: Feature enabled but no server
|
|
259
|
+
* (invalid)
|
|
260
|
+
* - otaServerSet=true, otaEnabled=true: Fully configured and active
|
|
261
|
+
*/
|
|
262
|
+
class StatusPackage : public painlessmesh::plugin::BroadcastPackage {
|
|
263
|
+
public:
|
|
264
|
+
uint8_t deviceStatus = 0; // Device status flags
|
|
265
|
+
uint32_t uptime = 0; // Device uptime in seconds
|
|
266
|
+
uint16_t freeMemory = 0; // Free memory in KB
|
|
267
|
+
uint8_t wifiStrength = 0; // WiFi signal strength
|
|
268
|
+
TSTRING firmwareVersion = ""; // Current firmware version
|
|
269
|
+
|
|
270
|
+
// Command response fields (for MQTT bridge)
|
|
271
|
+
uint32_t responseToCommand = 0; // CommandId this is responding to
|
|
272
|
+
TSTRING responseMessage = ""; // Success/error message
|
|
273
|
+
|
|
274
|
+
// Organization metadata (Build 8052 - Phase 2.7)
|
|
275
|
+
TSTRING organizationId = ""; // Organization identifier
|
|
276
|
+
TSTRING customerId = ""; // Customer identifier
|
|
277
|
+
TSTRING deviceGroup = ""; // Device group/category
|
|
278
|
+
TSTRING deviceName = ""; // Device name
|
|
279
|
+
TSTRING deviceLocation = ""; // Device location
|
|
280
|
+
bool deviceSecretSet =
|
|
281
|
+
false; // *Set: Has device secret been configured? (not enabled/disabled)
|
|
282
|
+
|
|
283
|
+
// Sensor configuration (Build 8057 - Gateway format compatibility)
|
|
284
|
+
// Note: Time fields follow Alteriom time field naming convention (see file
|
|
285
|
+
// header) Stored in milliseconds, serialized as both _ms and _s variants
|
|
286
|
+
uint32_t sensorReadInterval = 0; // Sensor read interval in milliseconds
|
|
287
|
+
uint32_t transmissionInterval = 0; // Transmission interval in milliseconds
|
|
288
|
+
double tempOffset = 0.0; // Temperature calibration offset
|
|
289
|
+
double humidityOffset = 0.0; // Humidity calibration offset
|
|
290
|
+
double pressureOffset = 0.0; // Pressure calibration offset
|
|
291
|
+
|
|
292
|
+
// Sensor inventory (Build 8057 - Separate from config to avoid collision)
|
|
293
|
+
uint8_t sensorCount = 0; // Number of sensors attached
|
|
294
|
+
uint8_t sensorTypeMask = 0; // Bitmask of sensor types present
|
|
295
|
+
|
|
296
|
+
// Display configuration
|
|
297
|
+
bool displayEnabled =
|
|
298
|
+
false; // *Enabled: Is display feature currently active?
|
|
299
|
+
uint8_t displayBrightness = 0; // Display brightness (0-255)
|
|
300
|
+
uint32_t displayTimeout = 0; // Display timeout in milliseconds
|
|
301
|
+
|
|
302
|
+
// Power configuration
|
|
303
|
+
bool deepSleepEnabled =
|
|
304
|
+
false; // *Enabled: Is deep sleep mode currently active?
|
|
305
|
+
uint32_t deepSleepInterval = 0; // Deep sleep interval in milliseconds
|
|
306
|
+
uint8_t batteryPercent = 0; // Battery percentage (0-100)
|
|
307
|
+
|
|
308
|
+
// MQTT retry configuration
|
|
309
|
+
uint8_t mqttMaxRetryAttempts = 0; // Maximum retry attempts
|
|
310
|
+
uint32_t mqttCircuitBreakerMs = 0; // Circuit breaker timeout in milliseconds
|
|
311
|
+
bool mqttHourlyRetryEnabled =
|
|
312
|
+
false; // *Enabled: Is hourly retry feature active?
|
|
313
|
+
uint32_t mqttInitialRetryMs = 0; // Initial retry delay in milliseconds
|
|
314
|
+
uint32_t mqttMaxRetryMs = 0; // Maximum retry delay in milliseconds
|
|
315
|
+
float mqttBackoffMultiplier =
|
|
316
|
+
0.0; // Backoff multiplier for exponential backoff
|
|
317
|
+
|
|
318
|
+
// MQTT Schema v0.7.3+ message_type for faster classification
|
|
319
|
+
uint16_t messageType = 202; // SENSOR_STATUS
|
|
320
|
+
|
|
321
|
+
StatusPackage() : BroadcastPackage(202) {} // Type ID 202 for Alteriom status
|
|
322
|
+
|
|
323
|
+
StatusPackage(JsonObject jsonObj) : BroadcastPackage(jsonObj) {
|
|
324
|
+
deviceStatus = jsonObj["status"];
|
|
325
|
+
uptime = jsonObj["uptime"];
|
|
326
|
+
freeMemory = jsonObj["mem"];
|
|
327
|
+
wifiStrength = jsonObj["wifi"];
|
|
328
|
+
firmwareVersion = jsonObj["fw"].as<TSTRING>();
|
|
329
|
+
responseToCommand = jsonObj["respTo"] | 0;
|
|
330
|
+
responseMessage = jsonObj["respMsg"].as<TSTRING>();
|
|
331
|
+
|
|
332
|
+
// Deserialize organization metadata (camelCase format)
|
|
333
|
+
if (jsonObj["organization"].is<JsonObject>()) {
|
|
334
|
+
JsonObject org = jsonObj["organization"];
|
|
335
|
+
organizationId = org["organizationId"].as<TSTRING>();
|
|
336
|
+
customerId = org["customerId"].as<TSTRING>();
|
|
337
|
+
deviceGroup = org["deviceGroup"].as<TSTRING>();
|
|
338
|
+
deviceName = org["device_name"].as<TSTRING>();
|
|
339
|
+
deviceLocation = org["device_location"].as<TSTRING>();
|
|
340
|
+
deviceSecretSet = org["device_secret_set"] | false;
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
// Deserialize sensor configuration (Build 8057 - Gateway format)
|
|
344
|
+
if (jsonObj["sensors"].is<JsonObject>()) {
|
|
345
|
+
JsonObject sensors = jsonObj["sensors"];
|
|
346
|
+
sensorReadInterval = sensors["read_interval_ms"] | 0;
|
|
347
|
+
transmissionInterval = sensors["transmission_interval_ms"] | 0;
|
|
348
|
+
|
|
349
|
+
if (sensors["calibration"].is<JsonObject>()) {
|
|
350
|
+
JsonObject calibration = sensors["calibration"];
|
|
351
|
+
tempOffset = calibration["temperature_offset"] | 0.0;
|
|
352
|
+
humidityOffset = calibration["humidity_offset"] | 0.0;
|
|
353
|
+
pressureOffset = calibration["pressure_offset"] | 0.0;
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
// Deserialize sensor inventory (Build 8057 - Separate key)
|
|
358
|
+
if (jsonObj["sensor_inventory"].is<JsonObject>()) {
|
|
359
|
+
JsonObject sensorInventory = jsonObj["sensor_inventory"];
|
|
360
|
+
sensorCount = sensorInventory["count"] | 0;
|
|
361
|
+
sensorTypeMask = sensorInventory["type_mask"] | 0;
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
// Deserialize display configuration (with backward compatibility)
|
|
365
|
+
if (jsonObj["display_config"].is<JsonObject>()) {
|
|
366
|
+
JsonObject displayConfig = jsonObj["display_config"];
|
|
367
|
+
displayEnabled = displayConfig["enabled"] | false;
|
|
368
|
+
displayBrightness = displayConfig["brightness"] | 0;
|
|
369
|
+
// Support both old and new field names for backward compatibility
|
|
370
|
+
displayTimeout =
|
|
371
|
+
displayConfig["timeout_ms"] | displayConfig["timeout"] | 0;
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
// Deserialize power configuration (with backward compatibility)
|
|
375
|
+
if (jsonObj["power_config"].is<JsonObject>()) {
|
|
376
|
+
JsonObject powerConfig = jsonObj["power_config"];
|
|
377
|
+
deepSleepEnabled = powerConfig["deep_sleep_enabled"] | false;
|
|
378
|
+
// Support both old and new field names for backward compatibility
|
|
379
|
+
deepSleepInterval = powerConfig["deep_sleep_interval_ms"] |
|
|
380
|
+
powerConfig["deep_sleep_interval"] | 0;
|
|
381
|
+
batteryPercent = powerConfig["battery_percent"] | 0;
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
// Deserialize MQTT retry configuration
|
|
385
|
+
if (jsonObj["mqtt_retry"].is<JsonObject>()) {
|
|
386
|
+
JsonObject mqttRetry = jsonObj["mqtt_retry"];
|
|
387
|
+
mqttMaxRetryAttempts = mqttRetry["max_attempts"] | 0;
|
|
388
|
+
mqttCircuitBreakerMs = mqttRetry["circuit_breaker_ms"] | 0;
|
|
389
|
+
mqttHourlyRetryEnabled = mqttRetry["hourly_retry_enabled"] | false;
|
|
390
|
+
mqttInitialRetryMs = mqttRetry["initial_retry_ms"] | 0;
|
|
391
|
+
mqttMaxRetryMs = mqttRetry["max_retry_ms"] | 0;
|
|
392
|
+
mqttBackoffMultiplier = mqttRetry["backoff_multiplier"] | 0.0;
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
// Deserialize message_type field
|
|
396
|
+
messageType = jsonObj["message_type"] | 202;
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
JsonObject addTo(JsonObject&& jsonObj) const {
|
|
400
|
+
jsonObj = BroadcastPackage::addTo(std::move(jsonObj));
|
|
401
|
+
jsonObj["status"] = deviceStatus;
|
|
402
|
+
jsonObj["uptime"] = uptime;
|
|
403
|
+
jsonObj["mem"] = freeMemory;
|
|
404
|
+
jsonObj["wifi"] = wifiStrength;
|
|
405
|
+
jsonObj["fw"] = firmwareVersion;
|
|
406
|
+
if (responseToCommand > 0) {
|
|
407
|
+
jsonObj["respTo"] = responseToCommand;
|
|
408
|
+
jsonObj["respMsg"] = responseMessage;
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
// Serialize organization metadata (mixed case per MQTT Schema v0.7.2)
|
|
412
|
+
// Always serialize to ensure predictable JSON structure
|
|
413
|
+
JsonObject org = jsonObj["organization"].to<JsonObject>();
|
|
414
|
+
org["organizationId"] = organizationId;
|
|
415
|
+
org["customerId"] = customerId;
|
|
416
|
+
org["deviceGroup"] = deviceGroup;
|
|
417
|
+
org["device_name"] = deviceName;
|
|
418
|
+
org["device_location"] = deviceLocation;
|
|
419
|
+
org["device_secret_set"] = deviceSecretSet;
|
|
420
|
+
|
|
421
|
+
// Serialize sensor configuration (Build 8057 - Match gateway format)
|
|
422
|
+
// Always serialize to ensure predictable JSON structure
|
|
423
|
+
JsonObject sensors = jsonObj["sensors"].to<JsonObject>();
|
|
424
|
+
sensors["read_interval_ms"] = sensorReadInterval;
|
|
425
|
+
sensors["read_interval_s"] = sensorReadInterval / 1000;
|
|
426
|
+
sensors["transmission_interval_ms"] = transmissionInterval;
|
|
427
|
+
sensors["transmission_interval_s"] = transmissionInterval / 1000;
|
|
428
|
+
|
|
429
|
+
// Nested calibration object - always serialize for consistency
|
|
430
|
+
JsonObject calibration = sensors["calibration"].to<JsonObject>();
|
|
431
|
+
calibration["temperature_offset"] = tempOffset;
|
|
432
|
+
calibration["humidity_offset"] = humidityOffset;
|
|
433
|
+
calibration["pressure_offset"] = pressureOffset;
|
|
434
|
+
|
|
435
|
+
// Serialize sensor inventory (Build 8057 - Separate key to avoid collision)
|
|
436
|
+
// Always serialize to ensure predictable JSON structure
|
|
437
|
+
JsonObject sensorInventory = jsonObj["sensor_inventory"].to<JsonObject>();
|
|
438
|
+
sensorInventory["count"] = sensorCount;
|
|
439
|
+
sensorInventory["type_mask"] = sensorTypeMask;
|
|
440
|
+
|
|
441
|
+
// Serialize display configuration (with both _ms and _s variants)
|
|
442
|
+
// Always serialize to ensure predictable JSON structure
|
|
443
|
+
JsonObject displayConfig = jsonObj["display_config"].to<JsonObject>();
|
|
444
|
+
displayConfig["enabled"] = displayEnabled;
|
|
445
|
+
displayConfig["brightness"] = displayBrightness;
|
|
446
|
+
displayConfig["timeout_ms"] = displayTimeout;
|
|
447
|
+
displayConfig["timeout_s"] = displayTimeout / 1000;
|
|
448
|
+
|
|
449
|
+
// Serialize power configuration (with both _ms and _s variants)
|
|
450
|
+
// Always serialize to ensure predictable JSON structure
|
|
451
|
+
JsonObject powerConfig = jsonObj["power_config"].to<JsonObject>();
|
|
452
|
+
powerConfig["deep_sleep_enabled"] = deepSleepEnabled;
|
|
453
|
+
powerConfig["deep_sleep_interval_ms"] = deepSleepInterval;
|
|
454
|
+
powerConfig["deep_sleep_interval_s"] = deepSleepInterval / 1000;
|
|
455
|
+
powerConfig["battery_percent"] = batteryPercent;
|
|
456
|
+
|
|
457
|
+
// Serialize MQTT retry configuration (with both _ms and _s variants)
|
|
458
|
+
// Always serialize to ensure predictable JSON structure
|
|
459
|
+
JsonObject mqttRetry = jsonObj["mqtt_retry"].to<JsonObject>();
|
|
460
|
+
mqttRetry["max_attempts"] = mqttMaxRetryAttempts;
|
|
461
|
+
mqttRetry["circuit_breaker_ms"] = mqttCircuitBreakerMs;
|
|
462
|
+
mqttRetry["circuit_breaker_s"] = mqttCircuitBreakerMs / 1000;
|
|
463
|
+
mqttRetry["hourly_retry_enabled"] = mqttHourlyRetryEnabled;
|
|
464
|
+
mqttRetry["initial_retry_ms"] = mqttInitialRetryMs;
|
|
465
|
+
mqttRetry["initial_retry_s"] = mqttInitialRetryMs / 1000;
|
|
466
|
+
mqttRetry["max_retry_ms"] = mqttMaxRetryMs;
|
|
467
|
+
mqttRetry["max_retry_s"] = mqttMaxRetryMs / 1000;
|
|
468
|
+
mqttRetry["backoff_multiplier"] = mqttBackoffMultiplier;
|
|
469
|
+
|
|
470
|
+
// Serialize message_type field
|
|
471
|
+
jsonObj["message_type"] = messageType;
|
|
472
|
+
|
|
473
|
+
return jsonObj;
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
#if ARDUINOJSON_VERSION_MAJOR < 7
|
|
477
|
+
size_t jsonObjectSize() const {
|
|
478
|
+
size_t size = JSON_OBJECT_SIZE(noJsonFields + 7) +
|
|
479
|
+
firmwareVersion.length() + responseMessage.length();
|
|
480
|
+
|
|
481
|
+
// Always add organization object size for predictable structure
|
|
482
|
+
size += JSON_OBJECT_SIZE(6) + organizationId.length() +
|
|
483
|
+
customerId.length() + deviceGroup.length() + deviceName.length() +
|
|
484
|
+
deviceLocation.length();
|
|
485
|
+
|
|
486
|
+
// Always add sensor configuration object size (Build 8057)
|
|
487
|
+
// sensors object with read_interval_ms, read_interval_s,
|
|
488
|
+
// transmission_interval_ms, transmission_interval_s, calibration
|
|
489
|
+
size += JSON_OBJECT_SIZE(5);
|
|
490
|
+
// calibration nested object - always included
|
|
491
|
+
size += JSON_OBJECT_SIZE(3);
|
|
492
|
+
|
|
493
|
+
// Always add sensor inventory object size (Build 8057)
|
|
494
|
+
size += JSON_OBJECT_SIZE(
|
|
495
|
+
2); // sensor_inventory object with count and type_mask
|
|
496
|
+
|
|
497
|
+
// Always add display configuration object size
|
|
498
|
+
// display_config object with enabled, brightness, timeout_ms, timeout_s
|
|
499
|
+
size += JSON_OBJECT_SIZE(4);
|
|
500
|
+
|
|
501
|
+
// Always add power configuration object size
|
|
502
|
+
// power_config object with deep_sleep_enabled, deep_sleep_interval_ms,
|
|
503
|
+
// deep_sleep_interval_s, battery_percent
|
|
504
|
+
size += JSON_OBJECT_SIZE(4);
|
|
505
|
+
|
|
506
|
+
// Always add MQTT retry configuration object size
|
|
507
|
+
// mqtt_retry object with max_attempts, circuit_breaker_ms,
|
|
508
|
+
// circuit_breaker_s, hourly_retry_enabled, initial_retry_ms,
|
|
509
|
+
// initial_retry_s, max_retry_ms, max_retry_s, backoff_multiplier
|
|
510
|
+
size +=
|
|
511
|
+
JSON_OBJECT_SIZE(9) + 10; // Extra space for backoff_multiplier string
|
|
512
|
+
|
|
513
|
+
return size;
|
|
514
|
+
}
|
|
515
|
+
#endif
|
|
516
|
+
};
|
|
517
|
+
|
|
518
|
+
/**
|
|
519
|
+
* @brief Enhanced status package with comprehensive health metrics (Phase 1)
|
|
520
|
+
*
|
|
521
|
+
* This is an extended version of StatusPackage that includes additional
|
|
522
|
+
* mesh statistics, performance metrics, and alerting capabilities.
|
|
523
|
+
* Type ID 203 is used to distinguish from the basic StatusPackage (202).
|
|
524
|
+
*/
|
|
525
|
+
class EnhancedStatusPackage : public painlessmesh::plugin::BroadcastPackage {
|
|
526
|
+
public:
|
|
527
|
+
// Device Health (from original StatusPackage)
|
|
528
|
+
uint8_t deviceStatus = 0; // Device status flags
|
|
529
|
+
uint32_t uptime = 0; // Device uptime in seconds
|
|
530
|
+
uint16_t freeMemory = 0; // Free memory in KB
|
|
531
|
+
uint8_t wifiStrength = 0; // WiFi signal strength
|
|
532
|
+
TSTRING firmwareVersion = ""; // Current firmware version
|
|
533
|
+
TSTRING firmwareMD5 = ""; // Firmware hash for OTA verification
|
|
534
|
+
|
|
535
|
+
// Mesh Statistics
|
|
536
|
+
uint16_t nodeCount = 0; // Number of known nodes in mesh
|
|
537
|
+
uint8_t connectionCount = 0; // Number of direct connections
|
|
538
|
+
uint32_t messagesReceived = 0; // Total messages received
|
|
539
|
+
uint32_t messagesSent = 0; // Total messages sent
|
|
540
|
+
uint32_t messagesDropped = 0; // Total messages dropped/failed
|
|
541
|
+
|
|
542
|
+
// Performance Metrics
|
|
543
|
+
uint16_t avgLatency = 0; // Average message latency in ms
|
|
544
|
+
uint8_t packetLossRate = 0; // Packet loss rate percentage (0-100)
|
|
545
|
+
uint16_t throughput = 0; // Network throughput in bytes/sec
|
|
546
|
+
|
|
547
|
+
// Warnings/Alerts
|
|
548
|
+
uint8_t alertFlags = 0; // Bit flags for various alert conditions
|
|
549
|
+
TSTRING lastError = ""; // Last error message for diagnostics
|
|
550
|
+
|
|
551
|
+
EnhancedStatusPackage()
|
|
552
|
+
: BroadcastPackage(604) {
|
|
553
|
+
} // Type ID 604 for enhanced status (MESH_STATUS per mqtt-schema v0.7.2+)
|
|
554
|
+
|
|
555
|
+
EnhancedStatusPackage(JsonObject jsonObj) : BroadcastPackage(jsonObj) {
|
|
556
|
+
deviceStatus = jsonObj["status"];
|
|
557
|
+
uptime = jsonObj["uptime"];
|
|
558
|
+
freeMemory = jsonObj["mem"];
|
|
559
|
+
wifiStrength = jsonObj["wifi"];
|
|
560
|
+
firmwareVersion = jsonObj["fw"].as<TSTRING>();
|
|
561
|
+
firmwareMD5 = jsonObj["fwMD5"].as<TSTRING>();
|
|
562
|
+
|
|
563
|
+
nodeCount = jsonObj["nodes"];
|
|
564
|
+
connectionCount = jsonObj["conns"];
|
|
565
|
+
messagesReceived = jsonObj["msgRx"];
|
|
566
|
+
messagesSent = jsonObj["msgTx"];
|
|
567
|
+
messagesDropped = jsonObj["msgDrop"];
|
|
568
|
+
|
|
569
|
+
avgLatency = jsonObj["latency"];
|
|
570
|
+
packetLossRate = jsonObj["loss"];
|
|
571
|
+
throughput = jsonObj["throughput"];
|
|
572
|
+
|
|
573
|
+
alertFlags = jsonObj["alerts"];
|
|
574
|
+
lastError = jsonObj["lastErr"].as<TSTRING>();
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
JsonObject addTo(JsonObject&& jsonObj) const {
|
|
578
|
+
jsonObj = BroadcastPackage::addTo(std::move(jsonObj));
|
|
579
|
+
|
|
580
|
+
// Device Health
|
|
581
|
+
jsonObj["status"] = deviceStatus;
|
|
582
|
+
jsonObj["uptime"] = uptime;
|
|
583
|
+
jsonObj["mem"] = freeMemory;
|
|
584
|
+
jsonObj["wifi"] = wifiStrength;
|
|
585
|
+
jsonObj["fw"] = firmwareVersion;
|
|
586
|
+
jsonObj["fwMD5"] = firmwareMD5;
|
|
587
|
+
|
|
588
|
+
// Mesh Statistics
|
|
589
|
+
jsonObj["nodes"] = nodeCount;
|
|
590
|
+
jsonObj["conns"] = connectionCount;
|
|
591
|
+
jsonObj["msgRx"] = messagesReceived;
|
|
592
|
+
jsonObj["msgTx"] = messagesSent;
|
|
593
|
+
jsonObj["msgDrop"] = messagesDropped;
|
|
594
|
+
|
|
595
|
+
// Performance Metrics
|
|
596
|
+
jsonObj["latency"] = avgLatency;
|
|
597
|
+
jsonObj["loss"] = packetLossRate;
|
|
598
|
+
jsonObj["throughput"] = throughput;
|
|
599
|
+
|
|
600
|
+
// Alerts
|
|
601
|
+
jsonObj["alerts"] = alertFlags;
|
|
602
|
+
jsonObj["lastErr"] = lastError;
|
|
603
|
+
|
|
604
|
+
return jsonObj;
|
|
605
|
+
}
|
|
606
|
+
|
|
607
|
+
#if ARDUINOJSON_VERSION_MAJOR < 7
|
|
608
|
+
size_t jsonObjectSize() const {
|
|
609
|
+
return JSON_OBJECT_SIZE(noJsonFields + 18) + firmwareVersion.length() +
|
|
610
|
+
firmwareMD5.length() + lastError.length();
|
|
611
|
+
}
|
|
612
|
+
#endif
|
|
613
|
+
};
|
|
614
|
+
|
|
615
|
+
/**
|
|
616
|
+
* @brief Detailed performance metrics package for monitoring (Phase 2)
|
|
617
|
+
*
|
|
618
|
+
* Provides comprehensive performance data including CPU usage, memory trends,
|
|
619
|
+
* network throughput, and other metrics useful for dashboards and monitoring.
|
|
620
|
+
* Type ID 204 for Alteriom metrics.
|
|
621
|
+
*/
|
|
622
|
+
class MetricsPackage : public painlessmesh::plugin::BroadcastPackage {
|
|
623
|
+
public:
|
|
624
|
+
// CPU and Processing
|
|
625
|
+
uint8_t cpuUsage = 0; // CPU usage percentage (0-100)
|
|
626
|
+
uint32_t loopIterations = 0; // Loop iterations per second
|
|
627
|
+
uint16_t taskQueueSize = 0; // Number of pending tasks
|
|
628
|
+
|
|
629
|
+
// Memory Metrics
|
|
630
|
+
uint32_t freeHeap = 0; // Free heap memory in bytes
|
|
631
|
+
uint32_t minFreeHeap = 0; // Minimum free heap since boot
|
|
632
|
+
uint32_t heapFragmentation = 0; // Heap fragmentation percentage
|
|
633
|
+
uint32_t maxAllocHeap = 0; // Largest allocatable block
|
|
634
|
+
|
|
635
|
+
// Network Performance
|
|
636
|
+
uint32_t bytesReceived = 0; // Total bytes received
|
|
637
|
+
uint32_t bytesSent = 0; // Total bytes sent
|
|
638
|
+
uint16_t packetsReceived = 0; // Total packets received
|
|
639
|
+
uint16_t packetsSent = 0; // Total packets sent
|
|
640
|
+
uint16_t packetsDropped = 0; // Packets dropped
|
|
641
|
+
uint16_t currentThroughput = 0; // Current throughput in bytes/sec
|
|
642
|
+
|
|
643
|
+
// Timing and Latency
|
|
644
|
+
uint32_t avgResponseTime = 0; // Average response time in microseconds
|
|
645
|
+
uint32_t maxResponseTime = 0; // Maximum response time in microseconds
|
|
646
|
+
uint16_t avgMeshLatency = 0; // Average mesh latency in milliseconds
|
|
647
|
+
|
|
648
|
+
// Connection Quality
|
|
649
|
+
uint8_t connectionQuality = 0; // Overall connection quality (0-100)
|
|
650
|
+
int8_t wifiRSSI = 0; // WiFi RSSI in dBm
|
|
651
|
+
|
|
652
|
+
// Collection metadata
|
|
653
|
+
uint32_t collectionTimestamp = 0; // When metrics were collected
|
|
654
|
+
uint32_t collectionInterval = 0; // Interval between collections in ms
|
|
655
|
+
|
|
656
|
+
// MQTT Schema v0.7.2+ message_type for faster classification
|
|
657
|
+
uint16_t messageType = 204; // SENSOR_METRICS (aligns with schema v0.7.2+)
|
|
658
|
+
|
|
659
|
+
MetricsPackage() : BroadcastPackage(204) {}
|
|
660
|
+
|
|
661
|
+
MetricsPackage(JsonObject jsonObj) : BroadcastPackage(jsonObj) {
|
|
662
|
+
cpuUsage = jsonObj["cpu"];
|
|
663
|
+
loopIterations = jsonObj["loops"];
|
|
664
|
+
taskQueueSize = jsonObj["tasks"];
|
|
665
|
+
|
|
666
|
+
freeHeap = jsonObj["heap"];
|
|
667
|
+
minFreeHeap = jsonObj["minHeap"];
|
|
668
|
+
heapFragmentation = jsonObj["fragHeap"];
|
|
669
|
+
maxAllocHeap = jsonObj["maxHeap"];
|
|
670
|
+
|
|
671
|
+
bytesReceived = jsonObj["bytesRx"];
|
|
672
|
+
bytesSent = jsonObj["bytesTx"];
|
|
673
|
+
packetsReceived = jsonObj["pktsRx"];
|
|
674
|
+
packetsSent = jsonObj["pktsTx"];
|
|
675
|
+
packetsDropped = jsonObj["pktsDrop"];
|
|
676
|
+
currentThroughput = jsonObj["throughput"];
|
|
677
|
+
|
|
678
|
+
avgResponseTime = jsonObj["avgResp"];
|
|
679
|
+
maxResponseTime = jsonObj["maxResp"];
|
|
680
|
+
avgMeshLatency = jsonObj["avgLat"];
|
|
681
|
+
|
|
682
|
+
connectionQuality = jsonObj["connQual"];
|
|
683
|
+
wifiRSSI = jsonObj["rssi"];
|
|
684
|
+
|
|
685
|
+
collectionTimestamp = jsonObj["ts"];
|
|
686
|
+
collectionInterval = jsonObj["interval"];
|
|
687
|
+
messageType = jsonObj["message_type"] | 204;
|
|
688
|
+
}
|
|
689
|
+
|
|
690
|
+
JsonObject addTo(JsonObject&& jsonObj) const {
|
|
691
|
+
jsonObj = BroadcastPackage::addTo(std::move(jsonObj));
|
|
692
|
+
|
|
693
|
+
// CPU and Processing
|
|
694
|
+
jsonObj["cpu"] = cpuUsage;
|
|
695
|
+
jsonObj["loops"] = loopIterations;
|
|
696
|
+
jsonObj["tasks"] = taskQueueSize;
|
|
697
|
+
|
|
698
|
+
// Memory Metrics
|
|
699
|
+
jsonObj["heap"] = freeHeap;
|
|
700
|
+
jsonObj["minHeap"] = minFreeHeap;
|
|
701
|
+
jsonObj["fragHeap"] = heapFragmentation;
|
|
702
|
+
jsonObj["maxHeap"] = maxAllocHeap;
|
|
703
|
+
|
|
704
|
+
// Network Performance
|
|
705
|
+
jsonObj["bytesRx"] = bytesReceived;
|
|
706
|
+
jsonObj["bytesTx"] = bytesSent;
|
|
707
|
+
jsonObj["pktsRx"] = packetsReceived;
|
|
708
|
+
jsonObj["pktsTx"] = packetsSent;
|
|
709
|
+
jsonObj["pktsDrop"] = packetsDropped;
|
|
710
|
+
jsonObj["throughput"] = currentThroughput;
|
|
711
|
+
|
|
712
|
+
// Timing and Latency
|
|
713
|
+
jsonObj["avgResp"] = avgResponseTime;
|
|
714
|
+
jsonObj["maxResp"] = maxResponseTime;
|
|
715
|
+
jsonObj["avgLat"] = avgMeshLatency;
|
|
716
|
+
|
|
717
|
+
// Connection Quality
|
|
718
|
+
jsonObj["connQual"] = connectionQuality;
|
|
719
|
+
jsonObj["rssi"] = wifiRSSI;
|
|
720
|
+
|
|
721
|
+
// Metadata
|
|
722
|
+
jsonObj["ts"] = collectionTimestamp;
|
|
723
|
+
jsonObj["interval"] = collectionInterval;
|
|
724
|
+
jsonObj["message_type"] = messageType; // MQTT Schema v0.7.1+
|
|
725
|
+
|
|
726
|
+
return jsonObj;
|
|
727
|
+
}
|
|
728
|
+
|
|
729
|
+
#if ARDUINOJSON_VERSION_MAJOR < 7
|
|
730
|
+
size_t jsonObjectSize() const { return JSON_OBJECT_SIZE(noJsonFields + 22); }
|
|
731
|
+
#endif
|
|
732
|
+
};
|
|
733
|
+
|
|
734
|
+
/**
|
|
735
|
+
* @brief Health check package for proactive problem detection (Phase 2)
|
|
736
|
+
*
|
|
737
|
+
* Provides early warning indicators and health status to detect issues
|
|
738
|
+
* before they cause failures. Used for predictive maintenance and alerting.
|
|
739
|
+
* Type ID 605 for Alteriom health checks (MESH_METRICS per mqtt-schema
|
|
740
|
+
* v0.7.2+).
|
|
741
|
+
*/
|
|
742
|
+
class HealthCheckPackage : public painlessmesh::plugin::BroadcastPackage {
|
|
743
|
+
public:
|
|
744
|
+
// Overall Health Status (0=critical, 1=warning, 2=healthy)
|
|
745
|
+
uint8_t healthStatus = 2;
|
|
746
|
+
|
|
747
|
+
// Problem Indicators (bit flags)
|
|
748
|
+
uint16_t problemFlags = 0; // Bit flags for specific problems
|
|
749
|
+
/*
|
|
750
|
+
* Problem flag bits:
|
|
751
|
+
* 0x0001 - Low memory warning
|
|
752
|
+
* 0x0002 - High CPU usage
|
|
753
|
+
* 0x0004 - Connection instability
|
|
754
|
+
* 0x0008 - High packet loss
|
|
755
|
+
* 0x0010 - Network congestion
|
|
756
|
+
* 0x0020 - Low battery (if applicable)
|
|
757
|
+
* 0x0040 - Thermal warning
|
|
758
|
+
* 0x0080 - Mesh partition detected
|
|
759
|
+
* 0x0100 - OTA in progress
|
|
760
|
+
* 0x0200 - Configuration error
|
|
761
|
+
*/
|
|
762
|
+
|
|
763
|
+
// Memory Health
|
|
764
|
+
uint8_t memoryHealth = 100; // Memory health score (0-100)
|
|
765
|
+
uint32_t memoryTrend = 0; // Bytes/hour memory loss (for leak detection)
|
|
766
|
+
|
|
767
|
+
// Network Health
|
|
768
|
+
uint8_t networkHealth = 100; // Network health score (0-100)
|
|
769
|
+
uint8_t packetLossPercent = 0; // Packet loss percentage
|
|
770
|
+
uint8_t reconnectionCount = 0; // Reconnections in last hour
|
|
771
|
+
|
|
772
|
+
// Performance Health
|
|
773
|
+
uint8_t performanceHealth = 100; // Performance health score (0-100)
|
|
774
|
+
uint32_t missedDeadlines = 0; // Missed task deadlines
|
|
775
|
+
uint16_t maxLoopTime = 0; // Maximum loop execution time in ms
|
|
776
|
+
|
|
777
|
+
// Environmental (if sensors available)
|
|
778
|
+
int8_t temperature = 0; // Device temperature in Celsius
|
|
779
|
+
uint8_t temperatureHealth = 100; // Temperature health score
|
|
780
|
+
|
|
781
|
+
// Uptime and Stability
|
|
782
|
+
uint32_t uptime = 0; // Uptime in seconds
|
|
783
|
+
uint16_t crashCount = 0; // Crash/restart count
|
|
784
|
+
uint32_t lastRebootReason = 0; // Last reboot reason code
|
|
785
|
+
|
|
786
|
+
// Predictive indicators
|
|
787
|
+
uint16_t estimatedTimeToFailure =
|
|
788
|
+
0; // Estimated hours until failure (0=unknown)
|
|
789
|
+
TSTRING recommendations = ""; // Recommended actions
|
|
790
|
+
|
|
791
|
+
// Check metadata
|
|
792
|
+
uint32_t checkTimestamp = 0; // When check was performed
|
|
793
|
+
uint32_t nextCheckDue = 0; // When next check is due
|
|
794
|
+
|
|
795
|
+
// MQTT Schema v0.7.2+ message_type for faster classification
|
|
796
|
+
uint16_t messageType = 605; // MESH_METRICS type code (mesh performance
|
|
797
|
+
// health per mqtt-schema v0.7.2+)
|
|
798
|
+
|
|
799
|
+
HealthCheckPackage() : BroadcastPackage(605) {}
|
|
800
|
+
|
|
801
|
+
HealthCheckPackage(JsonObject jsonObj) : BroadcastPackage(jsonObj) {
|
|
802
|
+
healthStatus = jsonObj["health"];
|
|
803
|
+
problemFlags = jsonObj["problems"];
|
|
804
|
+
|
|
805
|
+
memoryHealth = jsonObj["memHealth"];
|
|
806
|
+
memoryTrend = jsonObj["memTrend"];
|
|
807
|
+
|
|
808
|
+
networkHealth = jsonObj["netHealth"];
|
|
809
|
+
packetLossPercent = jsonObj["loss"];
|
|
810
|
+
reconnectionCount = jsonObj["reconn"];
|
|
811
|
+
|
|
812
|
+
performanceHealth = jsonObj["perfHealth"];
|
|
813
|
+
missedDeadlines = jsonObj["missed"];
|
|
814
|
+
maxLoopTime = jsonObj["maxLoop"];
|
|
815
|
+
|
|
816
|
+
temperature = jsonObj["temp"];
|
|
817
|
+
temperatureHealth = jsonObj["tempHealth"];
|
|
818
|
+
|
|
819
|
+
uptime = jsonObj["uptime"];
|
|
820
|
+
crashCount = jsonObj["crashes"];
|
|
821
|
+
lastRebootReason = jsonObj["reboot"];
|
|
822
|
+
|
|
823
|
+
estimatedTimeToFailure = jsonObj["ettf"];
|
|
824
|
+
recommendations = jsonObj["recommend"].as<TSTRING>();
|
|
825
|
+
|
|
826
|
+
checkTimestamp = jsonObj["ts"];
|
|
827
|
+
nextCheckDue = jsonObj["nextCheck"];
|
|
828
|
+
messageType = jsonObj["message_type"] | 605;
|
|
829
|
+
}
|
|
830
|
+
|
|
831
|
+
JsonObject addTo(JsonObject&& jsonObj) const {
|
|
832
|
+
jsonObj = BroadcastPackage::addTo(std::move(jsonObj));
|
|
833
|
+
|
|
834
|
+
jsonObj["health"] = healthStatus;
|
|
835
|
+
jsonObj["problems"] = problemFlags;
|
|
836
|
+
|
|
837
|
+
jsonObj["memHealth"] = memoryHealth;
|
|
838
|
+
jsonObj["memTrend"] = memoryTrend;
|
|
839
|
+
|
|
840
|
+
jsonObj["netHealth"] = networkHealth;
|
|
841
|
+
jsonObj["loss"] = packetLossPercent;
|
|
842
|
+
jsonObj["reconn"] = reconnectionCount;
|
|
843
|
+
|
|
844
|
+
jsonObj["perfHealth"] = performanceHealth;
|
|
845
|
+
jsonObj["missed"] = missedDeadlines;
|
|
846
|
+
jsonObj["maxLoop"] = maxLoopTime;
|
|
847
|
+
|
|
848
|
+
jsonObj["temp"] = temperature;
|
|
849
|
+
jsonObj["tempHealth"] = temperatureHealth;
|
|
850
|
+
|
|
851
|
+
jsonObj["uptime"] = uptime;
|
|
852
|
+
jsonObj["crashes"] = crashCount;
|
|
853
|
+
jsonObj["reboot"] = lastRebootReason;
|
|
854
|
+
|
|
855
|
+
jsonObj["ettf"] = estimatedTimeToFailure;
|
|
856
|
+
jsonObj["recommend"] = recommendations;
|
|
857
|
+
|
|
858
|
+
jsonObj["ts"] = checkTimestamp;
|
|
859
|
+
jsonObj["nextCheck"] = nextCheckDue;
|
|
860
|
+
jsonObj["message_type"] = messageType; // MQTT Schema v0.7.1+
|
|
861
|
+
|
|
862
|
+
return jsonObj;
|
|
863
|
+
}
|
|
864
|
+
|
|
865
|
+
#if ARDUINOJSON_VERSION_MAJOR < 7
|
|
866
|
+
size_t jsonObjectSize() const {
|
|
867
|
+
return JSON_OBJECT_SIZE(noJsonFields + 20) + recommendations.length();
|
|
868
|
+
}
|
|
869
|
+
#endif
|
|
870
|
+
};
|
|
871
|
+
|
|
872
|
+
/**
|
|
873
|
+
* @brief Mesh node information for a single node
|
|
874
|
+
*/
|
|
875
|
+
struct MeshNodeInfo {
|
|
876
|
+
uint32_t nodeId = 0; // Node identifier
|
|
877
|
+
uint8_t status = 0; // 0=offline, 1=online, 2=unreachable
|
|
878
|
+
uint32_t lastSeen = 0; // Unix timestamp of last communication
|
|
879
|
+
int8_t signalStrength = 0; // RSSI in dBm
|
|
880
|
+
};
|
|
881
|
+
|
|
882
|
+
/**
|
|
883
|
+
* @brief Mesh node list package (Type 600 - MESH_NODE_LIST)
|
|
884
|
+
*
|
|
885
|
+
* Provides list of all nodes in the mesh network with their status.
|
|
886
|
+
* Type ID 600 for MESH_NODE_LIST per mqtt-schema v0.7.2+.
|
|
887
|
+
*/
|
|
888
|
+
class MeshNodeListPackage : public painlessmesh::plugin::BroadcastPackage {
|
|
889
|
+
public:
|
|
890
|
+
// Array of node information (max 50 nodes)
|
|
891
|
+
MeshNodeInfo nodes[50];
|
|
892
|
+
uint8_t nodeCount = 0; // Actual number of nodes
|
|
893
|
+
TSTRING meshId = ""; // Mesh network identifier
|
|
894
|
+
|
|
895
|
+
// MQTT Schema v0.7.2+ message_type
|
|
896
|
+
uint16_t messageType = 600; // MESH_NODE_LIST
|
|
897
|
+
|
|
898
|
+
MeshNodeListPackage() : BroadcastPackage(600) {}
|
|
899
|
+
|
|
900
|
+
MeshNodeListPackage(JsonObject jsonObj) : BroadcastPackage(jsonObj) {
|
|
901
|
+
JsonArray nodesArray = jsonObj["nodes"];
|
|
902
|
+
nodeCount = nodesArray.size();
|
|
903
|
+
if (nodeCount > 50) nodeCount = 50;
|
|
904
|
+
|
|
905
|
+
for (uint8_t i = 0; i < nodeCount; i++) {
|
|
906
|
+
JsonObject node = nodesArray[i];
|
|
907
|
+
nodes[i].nodeId = node["nodeId"];
|
|
908
|
+
nodes[i].status = node["status"];
|
|
909
|
+
nodes[i].lastSeen = node["lastSeen"];
|
|
910
|
+
nodes[i].signalStrength = node["rssi"];
|
|
911
|
+
}
|
|
912
|
+
|
|
913
|
+
meshId = jsonObj["meshId"].as<TSTRING>();
|
|
914
|
+
messageType = jsonObj["message_type"] | 600;
|
|
915
|
+
}
|
|
916
|
+
|
|
917
|
+
JsonObject addTo(JsonObject&& jsonObj) const {
|
|
918
|
+
jsonObj = BroadcastPackage::addTo(std::move(jsonObj));
|
|
919
|
+
|
|
920
|
+
JsonArray nodesArray = jsonObj["nodes"].to<JsonArray>();
|
|
921
|
+
for (uint8_t i = 0; i < nodeCount; i++) {
|
|
922
|
+
JsonObject node = nodesArray.add<JsonObject>();
|
|
923
|
+
node["nodeId"] = nodes[i].nodeId;
|
|
924
|
+
node["status"] = nodes[i].status;
|
|
925
|
+
node["lastSeen"] = nodes[i].lastSeen;
|
|
926
|
+
node["rssi"] = nodes[i].signalStrength;
|
|
927
|
+
}
|
|
928
|
+
|
|
929
|
+
jsonObj["nodeCount"] = nodeCount;
|
|
930
|
+
jsonObj["meshId"] = meshId;
|
|
931
|
+
jsonObj["message_type"] = messageType;
|
|
932
|
+
|
|
933
|
+
return jsonObj;
|
|
934
|
+
}
|
|
935
|
+
|
|
936
|
+
#if ARDUINOJSON_VERSION_MAJOR < 7
|
|
937
|
+
size_t jsonObjectSize() const {
|
|
938
|
+
return JSON_OBJECT_SIZE(noJsonFields + 3) + JSON_ARRAY_SIZE(nodeCount) +
|
|
939
|
+
nodeCount * JSON_OBJECT_SIZE(4) + meshId.length();
|
|
940
|
+
}
|
|
941
|
+
#endif
|
|
942
|
+
};
|
|
943
|
+
|
|
944
|
+
/**
|
|
945
|
+
* @brief Mesh connection information
|
|
946
|
+
*/
|
|
947
|
+
struct MeshConnection {
|
|
948
|
+
uint32_t fromNode = 0; // Source node ID
|
|
949
|
+
uint32_t toNode = 0; // Destination node ID
|
|
950
|
+
float linkQuality = 0.0; // Link quality 0.0-1.0
|
|
951
|
+
uint16_t latencyMs = 0; // Latency in milliseconds
|
|
952
|
+
uint8_t hopCount = 1; // Number of hops
|
|
953
|
+
};
|
|
954
|
+
|
|
955
|
+
/**
|
|
956
|
+
* @brief Mesh topology package (Type 601 - MESH_TOPOLOGY)
|
|
957
|
+
*
|
|
958
|
+
* Provides mesh network topology with all connections.
|
|
959
|
+
* Type ID 601 for MESH_TOPOLOGY per mqtt-schema v0.7.2+.
|
|
960
|
+
*/
|
|
961
|
+
class MeshTopologyPackage : public painlessmesh::plugin::BroadcastPackage {
|
|
962
|
+
public:
|
|
963
|
+
// Array of connections (max 100 connections)
|
|
964
|
+
MeshConnection connections[100];
|
|
965
|
+
uint8_t connectionCount = 0; // Actual number of connections
|
|
966
|
+
uint32_t rootNode = 0; // Root/gateway node ID
|
|
967
|
+
|
|
968
|
+
// MQTT Schema v0.7.2+ message_type
|
|
969
|
+
uint16_t messageType = 601; // MESH_TOPOLOGY
|
|
970
|
+
|
|
971
|
+
MeshTopologyPackage() : BroadcastPackage(601) {}
|
|
972
|
+
|
|
973
|
+
MeshTopologyPackage(JsonObject jsonObj) : BroadcastPackage(jsonObj) {
|
|
974
|
+
JsonArray connsArray = jsonObj["connections"];
|
|
975
|
+
connectionCount = connsArray.size();
|
|
976
|
+
if (connectionCount > 100) connectionCount = 100;
|
|
977
|
+
|
|
978
|
+
for (uint8_t i = 0; i < connectionCount; i++) {
|
|
979
|
+
JsonObject conn = connsArray[i];
|
|
980
|
+
connections[i].fromNode = conn["from"];
|
|
981
|
+
connections[i].toNode = conn["to"];
|
|
982
|
+
connections[i].linkQuality = conn["quality"];
|
|
983
|
+
connections[i].latencyMs = conn["latency"];
|
|
984
|
+
connections[i].hopCount = conn["hops"];
|
|
985
|
+
}
|
|
986
|
+
|
|
987
|
+
rootNode = jsonObj["rootNode"];
|
|
988
|
+
messageType = jsonObj["message_type"] | 601;
|
|
989
|
+
}
|
|
990
|
+
|
|
991
|
+
JsonObject addTo(JsonObject&& jsonObj) const {
|
|
992
|
+
jsonObj = BroadcastPackage::addTo(std::move(jsonObj));
|
|
993
|
+
|
|
994
|
+
JsonArray connsArray = jsonObj["connections"].to<JsonArray>();
|
|
995
|
+
for (uint8_t i = 0; i < connectionCount; i++) {
|
|
996
|
+
JsonObject conn = connsArray.add<JsonObject>();
|
|
997
|
+
conn["from"] = connections[i].fromNode;
|
|
998
|
+
conn["to"] = connections[i].toNode;
|
|
999
|
+
conn["quality"] = connections[i].linkQuality;
|
|
1000
|
+
conn["latency"] = connections[i].latencyMs;
|
|
1001
|
+
conn["hops"] = connections[i].hopCount;
|
|
1002
|
+
}
|
|
1003
|
+
|
|
1004
|
+
jsonObj["totalConnections"] = connectionCount;
|
|
1005
|
+
jsonObj["rootNode"] = rootNode;
|
|
1006
|
+
jsonObj["message_type"] = messageType;
|
|
1007
|
+
|
|
1008
|
+
return jsonObj;
|
|
1009
|
+
}
|
|
1010
|
+
|
|
1011
|
+
#if ARDUINOJSON_VERSION_MAJOR < 7
|
|
1012
|
+
size_t jsonObjectSize() const {
|
|
1013
|
+
return JSON_OBJECT_SIZE(noJsonFields + 3) +
|
|
1014
|
+
JSON_ARRAY_SIZE(connectionCount) +
|
|
1015
|
+
connectionCount * JSON_OBJECT_SIZE(5);
|
|
1016
|
+
}
|
|
1017
|
+
#endif
|
|
1018
|
+
};
|
|
1019
|
+
|
|
1020
|
+
/**
|
|
1021
|
+
* @brief Mesh alert information
|
|
1022
|
+
*/
|
|
1023
|
+
struct MeshAlert {
|
|
1024
|
+
uint8_t alertType =
|
|
1025
|
+
0; // 0=low_memory, 1=node_offline, 2=connection_lost, etc.
|
|
1026
|
+
uint8_t severity = 0; // 0=info, 1=warning, 2=critical
|
|
1027
|
+
TSTRING message = ""; // Human-readable message
|
|
1028
|
+
uint32_t nodeId = 0; // Related node ID
|
|
1029
|
+
float metricValue = 0.0; // Related metric value
|
|
1030
|
+
float threshold = 0.0; // Threshold that triggered alert
|
|
1031
|
+
uint32_t alertId = 0; // Unique alert ID
|
|
1032
|
+
};
|
|
1033
|
+
|
|
1034
|
+
/**
|
|
1035
|
+
* @brief Mesh alert package (Type 602 - MESH_ALERT)
|
|
1036
|
+
*
|
|
1037
|
+
* Provides mesh network alerts for critical events.
|
|
1038
|
+
* Type ID 602 for MESH_ALERT per mqtt-schema v0.7.2+.
|
|
1039
|
+
*/
|
|
1040
|
+
class MeshAlertPackage : public painlessmesh::plugin::BroadcastPackage {
|
|
1041
|
+
public:
|
|
1042
|
+
// Array of alerts (max 20 alerts)
|
|
1043
|
+
MeshAlert alerts[20];
|
|
1044
|
+
uint8_t alertCount = 0; // Actual number of alerts
|
|
1045
|
+
|
|
1046
|
+
// MQTT Schema v0.7.2+ message_type
|
|
1047
|
+
uint16_t messageType = 602; // MESH_ALERT
|
|
1048
|
+
|
|
1049
|
+
MeshAlertPackage() : BroadcastPackage(602) {}
|
|
1050
|
+
|
|
1051
|
+
MeshAlertPackage(JsonObject jsonObj) : BroadcastPackage(jsonObj) {
|
|
1052
|
+
JsonArray alertsArray = jsonObj["alerts"];
|
|
1053
|
+
alertCount = alertsArray.size();
|
|
1054
|
+
if (alertCount > 20) alertCount = 20;
|
|
1055
|
+
|
|
1056
|
+
for (uint8_t i = 0; i < alertCount; i++) {
|
|
1057
|
+
JsonObject alert = alertsArray[i];
|
|
1058
|
+
alerts[i].alertType = alert["type"];
|
|
1059
|
+
alerts[i].severity = alert["severity"];
|
|
1060
|
+
alerts[i].message = alert["msg"].as<TSTRING>();
|
|
1061
|
+
alerts[i].nodeId = alert["nodeId"];
|
|
1062
|
+
alerts[i].metricValue = alert["value"];
|
|
1063
|
+
alerts[i].threshold = alert["threshold"];
|
|
1064
|
+
alerts[i].alertId = alert["alertId"];
|
|
1065
|
+
}
|
|
1066
|
+
|
|
1067
|
+
messageType = jsonObj["message_type"] | 602;
|
|
1068
|
+
}
|
|
1069
|
+
|
|
1070
|
+
JsonObject addTo(JsonObject&& jsonObj) const {
|
|
1071
|
+
jsonObj = BroadcastPackage::addTo(std::move(jsonObj));
|
|
1072
|
+
|
|
1073
|
+
JsonArray alertsArray = jsonObj["alerts"].to<JsonArray>();
|
|
1074
|
+
for (uint8_t i = 0; i < alertCount; i++) {
|
|
1075
|
+
JsonObject alert = alertsArray.add<JsonObject>();
|
|
1076
|
+
alert["type"] = alerts[i].alertType;
|
|
1077
|
+
alert["severity"] = alerts[i].severity;
|
|
1078
|
+
alert["msg"] = alerts[i].message;
|
|
1079
|
+
alert["nodeId"] = alerts[i].nodeId;
|
|
1080
|
+
alert["value"] = alerts[i].metricValue;
|
|
1081
|
+
alert["threshold"] = alerts[i].threshold;
|
|
1082
|
+
alert["alertId"] = alerts[i].alertId;
|
|
1083
|
+
}
|
|
1084
|
+
|
|
1085
|
+
jsonObj["alertCount"] = alertCount;
|
|
1086
|
+
jsonObj["message_type"] = messageType;
|
|
1087
|
+
|
|
1088
|
+
return jsonObj;
|
|
1089
|
+
}
|
|
1090
|
+
|
|
1091
|
+
#if ARDUINOJSON_VERSION_MAJOR < 7
|
|
1092
|
+
size_t jsonObjectSize() const {
|
|
1093
|
+
size_t size = JSON_OBJECT_SIZE(noJsonFields + 2) +
|
|
1094
|
+
JSON_ARRAY_SIZE(alertCount) +
|
|
1095
|
+
alertCount * JSON_OBJECT_SIZE(7);
|
|
1096
|
+
for (uint8_t i = 0; i < alertCount; i++) {
|
|
1097
|
+
size += alerts[i].message.length();
|
|
1098
|
+
}
|
|
1099
|
+
return size;
|
|
1100
|
+
}
|
|
1101
|
+
#endif
|
|
1102
|
+
};
|
|
1103
|
+
|
|
1104
|
+
/**
|
|
1105
|
+
* @brief Mesh bridge package (Type 603 - MESH_BRIDGE)
|
|
1106
|
+
*
|
|
1107
|
+
* Encapsulates native mesh protocol messages for bridging.
|
|
1108
|
+
* Type ID 603 for MESH_BRIDGE per mqtt-schema v0.7.2+.
|
|
1109
|
+
*/
|
|
1110
|
+
class MeshBridgePackage : public painlessmesh::plugin::BroadcastPackage {
|
|
1111
|
+
public:
|
|
1112
|
+
uint8_t meshProtocol = 0; // 0=painlessMesh, 1=esp-now, 2=ble-mesh, etc.
|
|
1113
|
+
uint32_t fromNodeId = 0; // Source node ID
|
|
1114
|
+
uint32_t toNodeId = 0; // Destination node ID (0=broadcast)
|
|
1115
|
+
uint16_t meshType = 0; // Mesh protocol-specific message type
|
|
1116
|
+
TSTRING rawPayload = ""; // Raw payload (hex/base64 encoded)
|
|
1117
|
+
int8_t rssi = 0; // Signal strength
|
|
1118
|
+
uint8_t hopCount = 0; // Number of hops
|
|
1119
|
+
uint32_t meshTimestamp = 0; // Mesh protocol timestamp
|
|
1120
|
+
uint32_t gatewayNodeId = 0; // Gateway's node ID
|
|
1121
|
+
TSTRING meshNetworkId = ""; // Mesh network identifier
|
|
1122
|
+
|
|
1123
|
+
// MQTT Schema v0.7.2+ message_type
|
|
1124
|
+
uint16_t messageType = 603; // MESH_BRIDGE
|
|
1125
|
+
|
|
1126
|
+
MeshBridgePackage() : BroadcastPackage(603) {}
|
|
1127
|
+
|
|
1128
|
+
MeshBridgePackage(JsonObject jsonObj) : BroadcastPackage(jsonObj) {
|
|
1129
|
+
meshProtocol = jsonObj["protocol"];
|
|
1130
|
+
fromNodeId = jsonObj["fromNode"];
|
|
1131
|
+
toNodeId = jsonObj["toNode"];
|
|
1132
|
+
meshType = jsonObj["meshType"];
|
|
1133
|
+
rawPayload = jsonObj["payload"].as<TSTRING>();
|
|
1134
|
+
rssi = jsonObj["rssi"];
|
|
1135
|
+
hopCount = jsonObj["hops"];
|
|
1136
|
+
meshTimestamp = jsonObj["meshTs"];
|
|
1137
|
+
gatewayNodeId = jsonObj["gateway"];
|
|
1138
|
+
meshNetworkId = jsonObj["meshId"].as<TSTRING>();
|
|
1139
|
+
messageType = jsonObj["message_type"] | 603;
|
|
1140
|
+
}
|
|
1141
|
+
|
|
1142
|
+
JsonObject addTo(JsonObject&& jsonObj) const {
|
|
1143
|
+
jsonObj = BroadcastPackage::addTo(std::move(jsonObj));
|
|
1144
|
+
|
|
1145
|
+
jsonObj["protocol"] = meshProtocol;
|
|
1146
|
+
jsonObj["fromNode"] = fromNodeId;
|
|
1147
|
+
jsonObj["toNode"] = toNodeId;
|
|
1148
|
+
jsonObj["meshType"] = meshType;
|
|
1149
|
+
jsonObj["payload"] = rawPayload;
|
|
1150
|
+
jsonObj["rssi"] = rssi;
|
|
1151
|
+
jsonObj["hops"] = hopCount;
|
|
1152
|
+
jsonObj["meshTs"] = meshTimestamp;
|
|
1153
|
+
jsonObj["gateway"] = gatewayNodeId;
|
|
1154
|
+
jsonObj["meshId"] = meshNetworkId;
|
|
1155
|
+
jsonObj["message_type"] = messageType;
|
|
1156
|
+
|
|
1157
|
+
return jsonObj;
|
|
1158
|
+
}
|
|
1159
|
+
|
|
1160
|
+
#if ARDUINOJSON_VERSION_MAJOR < 7
|
|
1161
|
+
size_t jsonObjectSize() const {
|
|
1162
|
+
return JSON_OBJECT_SIZE(noJsonFields + 11) + rawPayload.length() +
|
|
1163
|
+
meshNetworkId.length();
|
|
1164
|
+
}
|
|
1165
|
+
#endif
|
|
1166
|
+
};
|
|
1167
|
+
|
|
1168
|
+
} // namespace alteriom
|
|
1169
|
+
|
|
1170
|
+
#endif // ALTERIOM_SENSOR_PACKAGE_HPP
|