@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.
Files changed (80) hide show
  1. package/CHANGELOG.md +144 -0
  2. package/LICENSE +674 -0
  3. package/README.md +434 -0
  4. package/RELEASE_GUIDE.md +419 -0
  5. package/docs/DOCUMENTATION_MIGRATION_PLAN.md +176 -0
  6. package/docs/README.md +71 -0
  7. package/docs/alteriom/overview.md +508 -0
  8. package/docs/api/core-api.md +607 -0
  9. package/docs/architecture/mesh-architecture.md +379 -0
  10. package/docs/architecture/plugin-system.md +517 -0
  11. package/docs/getting-started/first-mesh.md +410 -0
  12. package/docs/getting-started/installation.md +275 -0
  13. package/docs/getting-started/quickstart.md +158 -0
  14. package/docs/improvements/README.md +69 -0
  15. package/docs/troubleshooting/common-issues.md +521 -0
  16. package/docs/troubleshooting/faq.md +473 -0
  17. package/docs/tutorials/basic-examples.md +718 -0
  18. package/docs/wiki/API-Reference.md +246 -0
  19. package/docs/wiki/Complete-Documentation.md +123 -0
  20. package/examples/alteriom/README.md +82 -0
  21. package/examples/alteriom/alteriom.ino +186 -0
  22. package/examples/alteriom/alteriom_sensor_node.ino +184 -0
  23. package/examples/alteriom/alteriom_sensor_package.hpp +128 -0
  24. package/examples/alteriom/improved_sensor_node.ino +246 -0
  25. package/examples/alteriom/platformio.ini +25 -0
  26. package/examples/basic/basic.ino +66 -0
  27. package/examples/basic/platformio.ini +25 -0
  28. package/examples/bridge/bridge.ino +51 -0
  29. package/examples/bridge/platformio.ini +25 -0
  30. package/examples/echoNode/echoNode.ino +33 -0
  31. package/examples/echoNode/platformio.ini +25 -0
  32. package/examples/logClient/logClient.ino +109 -0
  33. package/examples/logClient/platformio.ini +25 -0
  34. package/examples/logServer/logServer.ino +81 -0
  35. package/examples/logServer/platformio.ini +25 -0
  36. package/examples/mqttBridge/mqttBridge.ino +118 -0
  37. package/examples/mqttBridge/platformio.ini +26 -0
  38. package/examples/namedMesh/namedMesh.ino +97 -0
  39. package/examples/namedMesh/platformio.ini +25 -0
  40. package/examples/otaReceiver/otaReceiver.ino +79 -0
  41. package/examples/otaReceiver/platformio.ini +25 -0
  42. package/examples/otaSender/nodemcu32s_connections.JPG +0 -0
  43. package/examples/otaSender/otaSender.ino +151 -0
  44. package/examples/otaSender/platformio.ini +25 -0
  45. package/examples/startHere/platformio.ini +25 -0
  46. package/examples/startHere/startHere.ino +159 -0
  47. package/examples/webServer/platformio.ini +27 -0
  48. package/examples/webServer/webServer.ino +89 -0
  49. package/keywords.txt +49 -0
  50. package/library.json +34 -0
  51. package/library.properties +11 -0
  52. package/package.json +78 -0
  53. package/src/AlteriomPainlessMesh.h +98 -0
  54. package/src/arduino/wifi.hpp +365 -0
  55. package/src/boost/asynctcp.hpp +279 -0
  56. package/src/painlessMesh.h +70 -0
  57. package/src/painlessMeshSTA.cpp +236 -0
  58. package/src/painlessMeshSTA.h +58 -0
  59. package/src/painlessTaskOptions.h +4 -0
  60. package/src/painlessmesh/base64.hpp +111 -0
  61. package/src/painlessmesh/buffer.hpp +229 -0
  62. package/src/painlessmesh/callback.hpp +91 -0
  63. package/src/painlessmesh/configuration.hpp +77 -0
  64. package/src/painlessmesh/connection.hpp +192 -0
  65. package/src/painlessmesh/layout.hpp +188 -0
  66. package/src/painlessmesh/logger.hpp +158 -0
  67. package/src/painlessmesh/memory.hpp +120 -0
  68. package/src/painlessmesh/mesh.hpp +560 -0
  69. package/src/painlessmesh/metrics.hpp +323 -0
  70. package/src/painlessmesh/ntp.hpp +263 -0
  71. package/src/painlessmesh/ota.hpp +553 -0
  72. package/src/painlessmesh/plugin.hpp +188 -0
  73. package/src/painlessmesh/protocol.hpp +813 -0
  74. package/src/painlessmesh/router.hpp +322 -0
  75. package/src/painlessmesh/tcp.hpp +71 -0
  76. package/src/painlessmesh/validation.hpp +239 -0
  77. package/src/plugin/performance.hpp +214 -0
  78. package/src/plugin/remote.hpp +64 -0
  79. package/src/scheduler.cpp +10 -0
  80. 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