@alteriom/painlessmesh 1.6.1
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 +144 -0
- package/LICENSE +674 -0
- package/README.md +434 -0
- package/RELEASE_GUIDE.md +419 -0
- package/docs/DOCUMENTATION_MIGRATION_PLAN.md +176 -0
- package/docs/README.md +71 -0
- package/docs/alteriom/overview.md +508 -0
- package/docs/api/core-api.md +607 -0
- package/docs/architecture/mesh-architecture.md +379 -0
- package/docs/architecture/plugin-system.md +517 -0
- package/docs/getting-started/first-mesh.md +410 -0
- package/docs/getting-started/installation.md +275 -0
- package/docs/getting-started/quickstart.md +158 -0
- package/docs/improvements/README.md +69 -0
- package/docs/troubleshooting/common-issues.md +521 -0
- package/docs/troubleshooting/faq.md +473 -0
- package/docs/tutorials/basic-examples.md +718 -0
- package/docs/wiki/API-Reference.md +246 -0
- package/docs/wiki/Complete-Documentation.md +123 -0
- package/examples/alteriom/README.md +82 -0
- package/examples/alteriom/alteriom.ino +186 -0
- package/examples/alteriom/alteriom_sensor_node.ino +184 -0
- package/examples/alteriom/alteriom_sensor_package.hpp +128 -0
- package/examples/alteriom/improved_sensor_node.ino +246 -0
- package/examples/alteriom/platformio.ini +25 -0
- package/examples/basic/basic.ino +66 -0
- package/examples/basic/platformio.ini +25 -0
- package/examples/bridge/bridge.ino +51 -0
- package/examples/bridge/platformio.ini +25 -0
- package/examples/echoNode/echoNode.ino +33 -0
- package/examples/echoNode/platformio.ini +25 -0
- package/examples/logClient/logClient.ino +109 -0
- package/examples/logClient/platformio.ini +25 -0
- package/examples/logServer/logServer.ino +81 -0
- package/examples/logServer/platformio.ini +25 -0
- package/examples/mqttBridge/mqttBridge.ino +118 -0
- package/examples/mqttBridge/platformio.ini +26 -0
- package/examples/namedMesh/namedMesh.ino +97 -0
- package/examples/namedMesh/platformio.ini +25 -0
- package/examples/otaReceiver/otaReceiver.ino +79 -0
- package/examples/otaReceiver/platformio.ini +25 -0
- package/examples/otaSender/nodemcu32s_connections.JPG +0 -0
- package/examples/otaSender/otaSender.ino +151 -0
- package/examples/otaSender/platformio.ini +25 -0
- package/examples/startHere/platformio.ini +25 -0
- package/examples/startHere/startHere.ino +159 -0
- package/examples/webServer/platformio.ini +27 -0
- package/examples/webServer/webServer.ino +89 -0
- package/keywords.txt +49 -0
- package/library.json +34 -0
- package/library.properties +11 -0
- package/package.json +78 -0
- package/src/AlteriomPainlessMesh.h +98 -0
- package/src/arduino/wifi.hpp +365 -0
- package/src/boost/asynctcp.hpp +279 -0
- package/src/painlessMesh.h +70 -0
- package/src/painlessMeshSTA.cpp +236 -0
- package/src/painlessMeshSTA.h +58 -0
- package/src/painlessTaskOptions.h +4 -0
- package/src/painlessmesh/base64.hpp +111 -0
- package/src/painlessmesh/buffer.hpp +229 -0
- package/src/painlessmesh/callback.hpp +91 -0
- package/src/painlessmesh/configuration.hpp +77 -0
- package/src/painlessmesh/connection.hpp +192 -0
- package/src/painlessmesh/layout.hpp +188 -0
- package/src/painlessmesh/logger.hpp +158 -0
- package/src/painlessmesh/memory.hpp +120 -0
- package/src/painlessmesh/mesh.hpp +560 -0
- package/src/painlessmesh/metrics.hpp +323 -0
- package/src/painlessmesh/ntp.hpp +263 -0
- package/src/painlessmesh/ota.hpp +553 -0
- package/src/painlessmesh/plugin.hpp +188 -0
- package/src/painlessmesh/protocol.hpp +813 -0
- package/src/painlessmesh/router.hpp +322 -0
- package/src/painlessmesh/tcp.hpp +71 -0
- package/src/painlessmesh/validation.hpp +239 -0
- package/src/plugin/performance.hpp +214 -0
- package/src/plugin/remote.hpp +64 -0
- package/src/scheduler.cpp +10 -0
- package/src/wifi.cpp +2 -0
|
@@ -0,0 +1,517 @@
|
|
|
1
|
+
# Plugin System Architecture
|
|
2
|
+
|
|
3
|
+
The painlessMesh plugin system provides a type-safe, extensible framework for creating custom message types and handlers. This document explains how the plugin system works internally and how to extend it.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
The plugin system enables:
|
|
8
|
+
- **Type-safe messaging** between nodes
|
|
9
|
+
- **Custom package definitions** for specific use cases
|
|
10
|
+
- **Automatic serialization/deserialization** to/from JSON
|
|
11
|
+
- **Message routing control** (broadcast, single, neighbor)
|
|
12
|
+
- **Event-driven processing** with callbacks
|
|
13
|
+
|
|
14
|
+
## Architecture Components
|
|
15
|
+
|
|
16
|
+
```
|
|
17
|
+
┌─────────────────────────────────────────────────────────────┐
|
|
18
|
+
│ Application Layer │
|
|
19
|
+
│ ┌─────────────────┐ ┌─────────────────┐ ┌──────────────┐ │
|
|
20
|
+
│ │ SensorPackage │ │ CommandPackage │ │ StatusPackage│ │
|
|
21
|
+
│ └─────────────────┘ └─────────────────┘ └──────────────┘ │
|
|
22
|
+
├─────────────────────────────────────────────────────────────┤
|
|
23
|
+
│ Plugin Framework │
|
|
24
|
+
│ ┌─────────────────┐ ┌─────────────────┐ ┌──────────────┐ │
|
|
25
|
+
│ │ SinglePackage │ │BroadcastPackage │ │NeighbourPkg │ │
|
|
26
|
+
│ └─────────────────┘ └─────────────────┘ └──────────────┘ │
|
|
27
|
+
├─────────────────────────────────────────────────────────────┤
|
|
28
|
+
│ Protocol Layer │
|
|
29
|
+
│ ┌─────────────────┐ ┌─────────────────┐ ┌──────────────┐ │
|
|
30
|
+
│ │PackageInterface │ │ Variant │ │ PackageHandler│ │
|
|
31
|
+
│ └─────────────────┘ └─────────────────┘ └──────────────┘ │
|
|
32
|
+
├─────────────────────────────────────────────────────────────┤
|
|
33
|
+
│ Mesh Core │
|
|
34
|
+
└─────────────────────────────────────────────────────────────┘
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## Core Interfaces
|
|
38
|
+
|
|
39
|
+
### PackageInterface
|
|
40
|
+
|
|
41
|
+
The base interface all packages must implement:
|
|
42
|
+
|
|
43
|
+
```cpp
|
|
44
|
+
namespace painlessmesh {
|
|
45
|
+
namespace protocol {
|
|
46
|
+
|
|
47
|
+
class PackageInterface {
|
|
48
|
+
public:
|
|
49
|
+
uint32_t from; // Source node ID
|
|
50
|
+
router::Type routing; // Routing strategy
|
|
51
|
+
int type; // Package type identifier
|
|
52
|
+
|
|
53
|
+
// Serialization to JSON
|
|
54
|
+
virtual JsonObject addTo(JsonObject&& jsonObj) const = 0;
|
|
55
|
+
|
|
56
|
+
// Size calculation for buffer allocation
|
|
57
|
+
virtual size_t jsonObjectSize() const = 0;
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
}} // namespace painlessmesh::protocol
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
### Package Base Classes
|
|
64
|
+
|
|
65
|
+
#### SinglePackage
|
|
66
|
+
For point-to-point messages to specific nodes:
|
|
67
|
+
|
|
68
|
+
```cpp
|
|
69
|
+
class SinglePackage : public protocol::PackageInterface {
|
|
70
|
+
public:
|
|
71
|
+
uint32_t dest; // Destination node ID
|
|
72
|
+
int noJsonFields = 4; // Base field count
|
|
73
|
+
|
|
74
|
+
SinglePackage(int type) : routing(router::SINGLE), type(type) {}
|
|
75
|
+
|
|
76
|
+
SinglePackage(JsonObject jsonObj) {
|
|
77
|
+
from = jsonObj["from"];
|
|
78
|
+
dest = jsonObj["dest"];
|
|
79
|
+
type = jsonObj["type"];
|
|
80
|
+
routing = static_cast<router::Type>(jsonObj["routing"].as<int>());
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
JsonObject addTo(JsonObject&& jsonObj) const override {
|
|
84
|
+
jsonObj["from"] = from;
|
|
85
|
+
jsonObj["dest"] = dest;
|
|
86
|
+
jsonObj["routing"] = static_cast<int>(routing);
|
|
87
|
+
jsonObj["type"] = type;
|
|
88
|
+
return jsonObj;
|
|
89
|
+
}
|
|
90
|
+
};
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
#### BroadcastPackage
|
|
94
|
+
For messages to all nodes in the mesh:
|
|
95
|
+
|
|
96
|
+
```cpp
|
|
97
|
+
class BroadcastPackage : public protocol::PackageInterface {
|
|
98
|
+
public:
|
|
99
|
+
int noJsonFields = 3; // Base field count
|
|
100
|
+
|
|
101
|
+
BroadcastPackage(int type) : routing(router::BROADCAST), type(type) {}
|
|
102
|
+
|
|
103
|
+
BroadcastPackage(JsonObject jsonObj) {
|
|
104
|
+
from = jsonObj["from"];
|
|
105
|
+
type = jsonObj["type"];
|
|
106
|
+
routing = static_cast<router::Type>(jsonObj["routing"].as<int>());
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
JsonObject addTo(JsonObject&& jsonObj) const override {
|
|
110
|
+
jsonObj["from"] = from;
|
|
111
|
+
jsonObj["routing"] = static_cast<int>(routing);
|
|
112
|
+
jsonObj["type"] = type;
|
|
113
|
+
return jsonObj;
|
|
114
|
+
}
|
|
115
|
+
};
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
#### NeighbourPackage
|
|
119
|
+
For messages to directly connected nodes only:
|
|
120
|
+
|
|
121
|
+
```cpp
|
|
122
|
+
class NeighbourPackage : public plugin::SinglePackage {
|
|
123
|
+
public:
|
|
124
|
+
NeighbourPackage(int type) : SinglePackage(type) {
|
|
125
|
+
routing = router::NEIGHBOUR;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
NeighbourPackage(JsonObject jsonObj) : SinglePackage(jsonObj) {}
|
|
129
|
+
};
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
## Type System
|
|
133
|
+
|
|
134
|
+
### Package Type IDs
|
|
135
|
+
|
|
136
|
+
Each package type needs a unique identifier:
|
|
137
|
+
|
|
138
|
+
```cpp
|
|
139
|
+
// Core painlessMesh types: 1-12 (reserved)
|
|
140
|
+
enum CoreTypes {
|
|
141
|
+
MSG_TYPE = 1,
|
|
142
|
+
TIME_SYNC = 2,
|
|
143
|
+
NODE_SYNC = 3,
|
|
144
|
+
// ... other core types
|
|
145
|
+
};
|
|
146
|
+
|
|
147
|
+
// Custom types: 20+ (recommended)
|
|
148
|
+
enum CustomTypes {
|
|
149
|
+
SENSOR_DATA = 20,
|
|
150
|
+
DEVICE_COMMAND = 21,
|
|
151
|
+
STATUS_REPORT = 22,
|
|
152
|
+
// ... your custom types
|
|
153
|
+
};
|
|
154
|
+
|
|
155
|
+
// Alteriom types: 200+ (for Alteriom extensions)
|
|
156
|
+
enum AlteriomTypes {
|
|
157
|
+
ALTERIOM_SENSOR = 200,
|
|
158
|
+
ALTERIOM_COMMAND = 201,
|
|
159
|
+
ALTERIOM_STATUS = 202
|
|
160
|
+
};
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
### Routing Types
|
|
164
|
+
|
|
165
|
+
```cpp
|
|
166
|
+
namespace router {
|
|
167
|
+
enum Type {
|
|
168
|
+
SINGLE = 0, // Point-to-point
|
|
169
|
+
BROADCAST = 1, // To all nodes
|
|
170
|
+
NEIGHBOUR = 2 // To direct neighbors only
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
## Message Processing Flow
|
|
176
|
+
|
|
177
|
+
### Outbound Messages
|
|
178
|
+
|
|
179
|
+
```
|
|
180
|
+
Application
|
|
181
|
+
↓
|
|
182
|
+
Create Package → Serialize to JSON → Queue for Transmission
|
|
183
|
+
↓ ↓ ↓
|
|
184
|
+
Set Fields addTo() Send via TCP
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
### Inbound Messages
|
|
188
|
+
|
|
189
|
+
```
|
|
190
|
+
TCP Reception → JSON Parse → Type Lookup → Deserialize → Callback
|
|
191
|
+
↓ ↓ ↓ ↓ ↓
|
|
192
|
+
Raw Message JsonObject Type ID Package User Handler
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
## Variant System
|
|
196
|
+
|
|
197
|
+
The `Variant` class provides type-safe serialization and deserialization:
|
|
198
|
+
|
|
199
|
+
```cpp
|
|
200
|
+
namespace protocol {
|
|
201
|
+
|
|
202
|
+
class Variant {
|
|
203
|
+
public:
|
|
204
|
+
// Create from package
|
|
205
|
+
Variant(const PackageInterface* pkg);
|
|
206
|
+
|
|
207
|
+
// Deserialize to specific type
|
|
208
|
+
template<typename T>
|
|
209
|
+
T to() const;
|
|
210
|
+
|
|
211
|
+
// Get routing information
|
|
212
|
+
uint32_t dest() const;
|
|
213
|
+
router::Type routing() const;
|
|
214
|
+
int type() const;
|
|
215
|
+
|
|
216
|
+
// Serialize to string
|
|
217
|
+
void printTo(String& output) const;
|
|
218
|
+
};
|
|
219
|
+
|
|
220
|
+
}
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
### Usage Example
|
|
224
|
+
|
|
225
|
+
```cpp
|
|
226
|
+
// Sending
|
|
227
|
+
SensorPackage sensor;
|
|
228
|
+
sensor.temperature = 25.0;
|
|
229
|
+
sensor.humidity = 60.0;
|
|
230
|
+
|
|
231
|
+
protocol::Variant variant(&sensor);
|
|
232
|
+
String message;
|
|
233
|
+
variant.printTo(message);
|
|
234
|
+
// Send message via mesh
|
|
235
|
+
|
|
236
|
+
// Receiving
|
|
237
|
+
void receivedCallback(uint32_t from, String& msg) {
|
|
238
|
+
DynamicJsonDocument doc(1024);
|
|
239
|
+
deserializeJson(doc, msg);
|
|
240
|
+
JsonObject obj = doc.as<JsonObject>();
|
|
241
|
+
|
|
242
|
+
int msgType = obj["type"];
|
|
243
|
+
if (msgType == SENSOR_DATA) {
|
|
244
|
+
protocol::Variant variant(obj);
|
|
245
|
+
SensorPackage received = variant.to<SensorPackage>();
|
|
246
|
+
// Process sensor data
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
## Creating Custom Packages
|
|
252
|
+
|
|
253
|
+
### Step 1: Define Package Class
|
|
254
|
+
|
|
255
|
+
```cpp
|
|
256
|
+
class WeatherPackage : public painlessmesh::plugin::BroadcastPackage {
|
|
257
|
+
public:
|
|
258
|
+
// Data fields
|
|
259
|
+
float temperature = 0.0;
|
|
260
|
+
float humidity = 0.0;
|
|
261
|
+
float pressure = 0.0;
|
|
262
|
+
uint32_t timestamp = 0;
|
|
263
|
+
TSTRING location = "";
|
|
264
|
+
|
|
265
|
+
// Constructor with type ID
|
|
266
|
+
WeatherPackage() : BroadcastPackage(25) {} // Use unique ID
|
|
267
|
+
|
|
268
|
+
// Deserialization constructor
|
|
269
|
+
WeatherPackage(JsonObject jsonObj) : BroadcastPackage(jsonObj) {
|
|
270
|
+
temperature = jsonObj["temp"];
|
|
271
|
+
humidity = jsonObj["hum"];
|
|
272
|
+
pressure = jsonObj["pres"];
|
|
273
|
+
timestamp = jsonObj["time"];
|
|
274
|
+
location = jsonObj["loc"].as<TSTRING>();
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
// Serialization method
|
|
278
|
+
JsonObject addTo(JsonObject&& jsonObj) const override {
|
|
279
|
+
jsonObj = BroadcastPackage::addTo(std::move(jsonObj));
|
|
280
|
+
jsonObj["temp"] = temperature;
|
|
281
|
+
jsonObj["hum"] = humidity;
|
|
282
|
+
jsonObj["pres"] = pressure;
|
|
283
|
+
jsonObj["time"] = timestamp;
|
|
284
|
+
jsonObj["loc"] = location;
|
|
285
|
+
return jsonObj;
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
#if ARDUINOJSON_VERSION_MAJOR < 7
|
|
289
|
+
// Size calculation for buffer allocation
|
|
290
|
+
size_t jsonObjectSize() const override {
|
|
291
|
+
return JSON_OBJECT_SIZE(noJsonFields + 5) + location.length();
|
|
292
|
+
}
|
|
293
|
+
#endif
|
|
294
|
+
};
|
|
295
|
+
```
|
|
296
|
+
|
|
297
|
+
### Step 2: Register Handler
|
|
298
|
+
|
|
299
|
+
```cpp
|
|
300
|
+
void setup() {
|
|
301
|
+
// Initialize mesh
|
|
302
|
+
mesh.init(MESH_PREFIX, MESH_PASSWORD, &userScheduler, MESH_PORT);
|
|
303
|
+
|
|
304
|
+
// Register handler for weather packages
|
|
305
|
+
mesh.onPackage(25, [](protocol::Variant& variant) {
|
|
306
|
+
WeatherPackage weather = variant.to<WeatherPackage>();
|
|
307
|
+
|
|
308
|
+
Serial.printf("Weather from %u: T=%.1f°C, H=%.1f%%, P=%.1f hPa at %s\n",
|
|
309
|
+
weather.from, weather.temperature, weather.humidity,
|
|
310
|
+
weather.pressure, weather.location.c_str());
|
|
311
|
+
|
|
312
|
+
return false; // Don't stop propagation
|
|
313
|
+
});
|
|
314
|
+
}
|
|
315
|
+
```
|
|
316
|
+
|
|
317
|
+
### Step 3: Send Packages
|
|
318
|
+
|
|
319
|
+
```cpp
|
|
320
|
+
void sendWeatherData() {
|
|
321
|
+
WeatherPackage weather;
|
|
322
|
+
weather.from = mesh.getNodeId();
|
|
323
|
+
weather.temperature = readTemperature();
|
|
324
|
+
weather.humidity = readHumidity();
|
|
325
|
+
weather.pressure = readPressure();
|
|
326
|
+
weather.timestamp = mesh.getNodeTime();
|
|
327
|
+
weather.location = "Sensor Station Alpha";
|
|
328
|
+
|
|
329
|
+
mesh.sendPackage(&weather);
|
|
330
|
+
}
|
|
331
|
+
```
|
|
332
|
+
|
|
333
|
+
## Advanced Features
|
|
334
|
+
|
|
335
|
+
### Task Integration
|
|
336
|
+
|
|
337
|
+
The plugin system integrates with TaskScheduler:
|
|
338
|
+
|
|
339
|
+
```cpp
|
|
340
|
+
// Add recurring task
|
|
341
|
+
auto task = mesh.addTask(30000, TASK_FOREVER, [](){
|
|
342
|
+
WeatherPackage weather;
|
|
343
|
+
// ... populate data
|
|
344
|
+
mesh.sendPackage(&weather);
|
|
345
|
+
});
|
|
346
|
+
|
|
347
|
+
// One-time task
|
|
348
|
+
mesh.addTask([](){
|
|
349
|
+
StatusPackage status;
|
|
350
|
+
// ... populate status
|
|
351
|
+
mesh.sendPackage(&status);
|
|
352
|
+
});
|
|
353
|
+
```
|
|
354
|
+
|
|
355
|
+
### Message Filtering
|
|
356
|
+
|
|
357
|
+
```cpp
|
|
358
|
+
// Handler that filters messages
|
|
359
|
+
mesh.onPackage(SENSOR_DATA, [](protocol::Variant& variant) {
|
|
360
|
+
SensorPackage sensor = variant.to<SensorPackage>();
|
|
361
|
+
|
|
362
|
+
// Only process recent data
|
|
363
|
+
if (mesh.getNodeTime() - sensor.timestamp > 60000000) { // 60 seconds
|
|
364
|
+
return false; // Ignore old data
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
// Process valid sensor data
|
|
368
|
+
processSensorData(sensor);
|
|
369
|
+
return false;
|
|
370
|
+
});
|
|
371
|
+
```
|
|
372
|
+
|
|
373
|
+
### Conditional Routing
|
|
374
|
+
|
|
375
|
+
```cpp
|
|
376
|
+
class ConditionalPackage : public painlessmesh::plugin::SinglePackage {
|
|
377
|
+
public:
|
|
378
|
+
bool urgent = false;
|
|
379
|
+
|
|
380
|
+
ConditionalPackage() : SinglePackage(30) {}
|
|
381
|
+
|
|
382
|
+
// Override routing based on urgency
|
|
383
|
+
router::Type getRouting() const {
|
|
384
|
+
return urgent ? router::BROADCAST : router::SINGLE;
|
|
385
|
+
}
|
|
386
|
+
};
|
|
387
|
+
```
|
|
388
|
+
|
|
389
|
+
## Memory Management
|
|
390
|
+
|
|
391
|
+
### ArduinoJson Integration
|
|
392
|
+
|
|
393
|
+
```cpp
|
|
394
|
+
// Efficient buffer sizing
|
|
395
|
+
size_t jsonObjectSize() const override {
|
|
396
|
+
size_t baseSize = JSON_OBJECT_SIZE(noJsonFields + customFieldCount);
|
|
397
|
+
size_t stringSize = stringField1.length() + stringField2.length();
|
|
398
|
+
return baseSize + stringSize;
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
// Use appropriate document size
|
|
402
|
+
DynamicJsonDocument doc(package.jsonObjectSize() + 100); // Add safety margin
|
|
403
|
+
```
|
|
404
|
+
|
|
405
|
+
### String Handling
|
|
406
|
+
|
|
407
|
+
```cpp
|
|
408
|
+
// Use TSTRING for cross-platform compatibility
|
|
409
|
+
TSTRING deviceName = "WeatherStation01";
|
|
410
|
+
|
|
411
|
+
// Efficient string operations
|
|
412
|
+
void updateName(const TSTRING& newName) {
|
|
413
|
+
deviceName = newName;
|
|
414
|
+
deviceName.reserve(32); // Pre-allocate for efficiency
|
|
415
|
+
}
|
|
416
|
+
```
|
|
417
|
+
|
|
418
|
+
## Error Handling
|
|
419
|
+
|
|
420
|
+
### Serialization Errors
|
|
421
|
+
|
|
422
|
+
```cpp
|
|
423
|
+
JsonObject addTo(JsonObject&& jsonObj) const override {
|
|
424
|
+
try {
|
|
425
|
+
jsonObj = BroadcastPackage::addTo(std::move(jsonObj));
|
|
426
|
+
|
|
427
|
+
// Validate data before serialization
|
|
428
|
+
if (temperature < -50 || temperature > 100) {
|
|
429
|
+
Serial.println("Warning: Temperature out of range");
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
jsonObj["temp"] = temperature;
|
|
433
|
+
return jsonObj;
|
|
434
|
+
} catch (...) {
|
|
435
|
+
Serial.println("Error serializing weather package");
|
|
436
|
+
return jsonObj;
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
```
|
|
440
|
+
|
|
441
|
+
### Deserialization Validation
|
|
442
|
+
|
|
443
|
+
```cpp
|
|
444
|
+
WeatherPackage(JsonObject jsonObj) : BroadcastPackage(jsonObj) {
|
|
445
|
+
// Validate required fields exist
|
|
446
|
+
if (jsonObj.containsKey("temp")) {
|
|
447
|
+
temperature = jsonObj["temp"];
|
|
448
|
+
} else {
|
|
449
|
+
Serial.println("Missing temperature field");
|
|
450
|
+
temperature = 0.0;
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
// Range validation
|
|
454
|
+
if (temperature < -50 || temperature > 100) {
|
|
455
|
+
Serial.printf("Invalid temperature: %.1f\n", temperature);
|
|
456
|
+
temperature = 0.0;
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
```
|
|
460
|
+
|
|
461
|
+
## Best Practices
|
|
462
|
+
|
|
463
|
+
### Package Design
|
|
464
|
+
|
|
465
|
+
1. **Keep packages small** - Minimize memory usage
|
|
466
|
+
2. **Use appropriate routing** - Don't broadcast when single-cast suffices
|
|
467
|
+
3. **Include timestamps** - Enable data age validation
|
|
468
|
+
4. **Validate inputs** - Check ranges and formats
|
|
469
|
+
5. **Version your schemas** - Plan for future changes
|
|
470
|
+
|
|
471
|
+
### Type Management
|
|
472
|
+
|
|
473
|
+
1. **Use unique type IDs** - Avoid conflicts with other packages
|
|
474
|
+
2. **Document type assignments** - Maintain a registry
|
|
475
|
+
3. **Group related types** - Use ranges for related functionality
|
|
476
|
+
4. **Reserve ranges** - Plan for future expansion
|
|
477
|
+
|
|
478
|
+
### Performance
|
|
479
|
+
|
|
480
|
+
1. **Pre-calculate sizes** - Implement `jsonObjectSize()` accurately
|
|
481
|
+
2. **Minimize string operations** - Use fixed-size fields when possible
|
|
482
|
+
3. **Batch operations** - Send multiple readings in one package
|
|
483
|
+
4. **Cache frequently used objects** - Avoid repeated allocations
|
|
484
|
+
|
|
485
|
+
## Integration with Alteriom
|
|
486
|
+
|
|
487
|
+
The Alteriom extensions demonstrate advanced plugin usage:
|
|
488
|
+
|
|
489
|
+
```cpp
|
|
490
|
+
namespace alteriom {
|
|
491
|
+
|
|
492
|
+
class SensorPackage : public painlessmesh::plugin::BroadcastPackage {
|
|
493
|
+
// Environmental sensor data
|
|
494
|
+
// Type ID: 200
|
|
495
|
+
};
|
|
496
|
+
|
|
497
|
+
class CommandPackage : public painlessmesh::plugin::SinglePackage {
|
|
498
|
+
// Device control commands
|
|
499
|
+
// Type ID: 201
|
|
500
|
+
};
|
|
501
|
+
|
|
502
|
+
class StatusPackage : public painlessmesh::plugin::BroadcastPackage {
|
|
503
|
+
// Device health and status
|
|
504
|
+
// Type ID: 202
|
|
505
|
+
};
|
|
506
|
+
|
|
507
|
+
}
|
|
508
|
+
```
|
|
509
|
+
|
|
510
|
+
See [Alteriom Extensions](../alteriom/overview.md) for detailed usage examples.
|
|
511
|
+
|
|
512
|
+
## Next Steps
|
|
513
|
+
|
|
514
|
+
- Learn about [Message Routing](routing.md) algorithms
|
|
515
|
+
- Explore [Alteriom Packages](../alteriom/sensor-packages.md) for real-world examples
|
|
516
|
+
- See [Performance Optimization](../advanced/performance.md) for efficiency tips
|
|
517
|
+
- Check [Custom Packages Tutorial](../tutorials/custom-packages.md) for hands-on examples
|