@alteriom/painlessmesh 1.6.1 → 1.7.2
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 +380 -143
- package/LICENSE +674 -674
- package/README.md +477 -434
- package/RELEASE_GUIDE.md +504 -418
- package/docs/DOCUMENTATION_MIGRATION_PLAN.md +175 -175
- package/docs/IMPLEMENTATION_PLAN_MESH_TOPOLOGY.md +1062 -0
- package/docs/MESH_TOPOLOGY_GUIDE.md +992 -0
- package/docs/MESH_TOPOLOGY_PROGRESS.md +422 -0
- package/docs/MQTT_BRIDGE_COMMANDS.md +894 -0
- package/docs/MQTT_BRIDGE_IMPLEMENTATION_SUMMARY.md +324 -0
- package/docs/MQTT_COMMAND_SCHEMA_PROPOSAL.md +576 -0
- package/docs/MQTT_SCHEMA_COMPLIANCE.md +285 -0
- package/docs/MQTT_SCHEMA_PROPOSALS.md +446 -0
- package/docs/MQTT_SCHEMA_REVIEW.md +690 -0
- package/docs/OTA_COMMANDS_REFERENCE.md +554 -0
- package/docs/PHASE1_GUIDE.md +349 -0
- package/docs/PHASE2_GUIDE.md +543 -0
- package/docs/README.md +77 -70
- package/docs/SCHEMA_VALIDATION_CHECKLIST.md +222 -0
- package/docs/alteriom/overview.md +507 -507
- package/docs/api/core-api.md +606 -606
- package/docs/architecture/mesh-architecture.md +378 -378
- package/docs/architecture/plugin-system.md +516 -516
- package/docs/getting-started/first-mesh.md +409 -409
- package/docs/getting-started/installation.md +274 -274
- package/docs/getting-started/quickstart.md +157 -157
- package/docs/improvements/FEATURE_PROPOSALS.md +337 -0
- package/docs/improvements/PHASE1_IMPLEMENTATION.md +325 -0
- package/docs/improvements/PHASE2_IMPLEMENTATION.md +567 -0
- package/docs/improvements/README.md +86 -68
- package/docs/improvements/ota-and-status-enhancements.md +911 -0
- package/docs/improvements/ota-status-architecture-diagrams.md +658 -0
- package/docs/improvements/ota-status-quick-reference.md +284 -0
- package/docs/platformio-publishing.md +255 -0
- package/docs/platformio-setup-summary.md +121 -0
- package/docs/troubleshooting/common-issues.md +520 -520
- package/docs/troubleshooting/faq.md +472 -472
- package/docs/tutorials/basic-examples.md +717 -717
- package/docs/wiki/API-Reference.md +245 -245
- package/docs/wiki/Complete-Documentation.md +122 -122
- package/examples/alteriom/README.md +139 -81
- package/examples/alteriom/alteriom.ino +186 -185
- package/examples/alteriom/alteriom_sensor_package.hpp +240 -127
- package/examples/alteriom/platformio.ini +24 -24
- package/examples/alteriomImproved/alteriom_sensor_package.hpp +224 -0
- package/examples/{alteriom → alteriomImproved}/improved_sensor_node.ino +245 -245
- package/examples/alteriomImproved/platformio.ini +25 -0
- package/examples/alteriomPhase1/alteriom_sensor_package.hpp +224 -0
- package/examples/alteriomPhase1/phase1_features.ino +242 -0
- package/examples/alteriomPhase1/platformio.ini +25 -0
- package/examples/alteriomPhase2/alteriom_sensor_package.hpp +224 -0
- package/examples/alteriomPhase2/phase2_features.ino +186 -0
- package/examples/alteriomPhase2/platformio.ini +25 -0
- package/examples/{alteriom → alteriomSensorNode}/alteriom_sensor_node.ino +183 -183
- package/examples/alteriomSensorNode/alteriom_sensor_package.hpp +224 -0
- package/examples/alteriomSensorNode/platformio.ini +25 -0
- package/examples/basic/basic.ino +66 -66
- package/examples/basic/platformio.ini +25 -25
- package/examples/bridge/bridge.ino +51 -51
- package/examples/bridge/mesh_event_publisher.hpp +253 -0
- package/examples/bridge/mesh_topology_reporter.hpp +303 -0
- package/examples/bridge/mqtt_command_bridge.hpp +459 -0
- package/examples/bridge/mqtt_status_bridge.hpp +519 -0
- package/examples/bridge/platformio.ini +25 -25
- package/examples/echoNode/echoNode.ino +33 -33
- package/examples/echoNode/platformio.ini +25 -25
- package/examples/logClient/logClient.ino +109 -109
- package/examples/logClient/platformio.ini +25 -25
- package/examples/logServer/logServer.ino +81 -81
- package/examples/logServer/platformio.ini +25 -25
- package/examples/meshCommandNode/alteriom_sensor_package.hpp +235 -0
- package/examples/meshCommandNode/meshCommandNode.ino +263 -0
- package/examples/meshCommandNode/platformio.ini +25 -0
- package/examples/mqttBridge/mqttBridge.ino +118 -118
- package/examples/mqttBridge/platformio.ini +26 -26
- package/examples/mqttCommandBridge/alteriom_sensor_package.hpp +235 -0
- package/examples/mqttCommandBridge/mesh_event_publisher.hpp +253 -0
- package/examples/mqttCommandBridge/mesh_topology_reporter.hpp +303 -0
- package/examples/mqttCommandBridge/mqttCommandBridge.ino +252 -0
- package/examples/mqttCommandBridge/mqtt_command_bridge.hpp +459 -0
- package/examples/mqttCommandBridge/platformio.ini +26 -0
- package/examples/mqttStatusBridge/mqttStatusBridge.ino +216 -0
- package/examples/mqttStatusBridge/mqtt_status_bridge.hpp +522 -0
- package/examples/mqttStatusBridge/platformio.ini +26 -0
- package/examples/mqttTopologyTest/README.md +467 -0
- package/examples/mqttTopologyTest/mqttTopologyTest.ino +748 -0
- package/examples/mqttTopologyTest/platformio.ini +26 -0
- package/examples/namedMesh/namedMesh.ino +97 -97
- package/examples/namedMesh/platformio.ini +25 -25
- package/examples/otaReceiver/otaReceiver.ino +79 -79
- package/examples/otaReceiver/platformio.ini +25 -25
- package/examples/otaSender/otaSender.ino +160 -151
- package/examples/otaSender/platformio.ini +25 -25
- package/examples/startHere/platformio.ini +25 -25
- package/examples/startHere/startHere.ino +159 -159
- package/examples/webServer/platformio.ini +27 -27
- package/examples/webServer/webServer.ino +89 -89
- package/keywords.txt +48 -48
- package/library.json +55 -34
- package/library.properties +10 -10
- package/package.json +86 -78
- package/src/AlteriomPainlessMesh.h +97 -97
- package/src/arduino/wifi.hpp +365 -365
- package/src/boost/asynctcp.hpp +279 -279
- package/src/painlessMesh.h +70 -70
- package/src/painlessMeshSTA.cpp +236 -236
- package/src/painlessMeshSTA.h +58 -58
- package/src/painlessTaskOptions.h +4 -4
- package/src/painlessmesh/base64.hpp +111 -111
- package/src/painlessmesh/buffer.hpp +229 -229
- package/src/painlessmesh/callback.hpp +91 -91
- package/src/painlessmesh/configuration.hpp +77 -77
- package/src/painlessmesh/connection.hpp +192 -192
- package/src/painlessmesh/layout.hpp +188 -188
- package/src/painlessmesh/logger.hpp +158 -158
- package/src/painlessmesh/memory.hpp +119 -119
- package/src/painlessmesh/mesh.hpp +761 -560
- package/src/painlessmesh/metrics.hpp +322 -322
- package/src/painlessmesh/ntp.hpp +263 -263
- package/src/painlessmesh/ota.hpp +582 -553
- package/src/painlessmesh/plugin.hpp +188 -188
- package/src/painlessmesh/protocol.hpp +813 -813
- package/src/painlessmesh/router.hpp +322 -322
- package/src/painlessmesh/tcp.hpp +71 -71
- package/src/painlessmesh/validation.hpp +238 -238
- package/src/plugin/performance.hpp +214 -214
- package/src/plugin/remote.hpp +64 -64
- package/src/scheduler.cpp +10 -10
- package/src/wifi.cpp +2 -2
|
@@ -0,0 +1,554 @@
|
|
|
1
|
+
# OTA Commands and API Reference
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
|
|
5
|
+
This document provides a complete reference for OTA (Over-The-Air) firmware update commands in painlessMesh, including Phase 2 broadcast mode enhancements and Alteriom MQTT schema compliance for firmware status reporting.
|
|
6
|
+
|
|
7
|
+
## Table of Contents
|
|
8
|
+
|
|
9
|
+
1. [OTA Command API](#ota-command-api)
|
|
10
|
+
2. [Broadcast OTA (Phase 2)](#broadcast-ota-phase-2)
|
|
11
|
+
3. [Firmware Status Reporting](#firmware-status-reporting)
|
|
12
|
+
4. [MQTT Schema Compliance](#mqtt-schema-compliance)
|
|
13
|
+
5. [Complete Examples](#complete-examples)
|
|
14
|
+
6. [Troubleshooting](#troubleshooting)
|
|
15
|
+
|
|
16
|
+
---
|
|
17
|
+
|
|
18
|
+
## OTA Command API
|
|
19
|
+
|
|
20
|
+
### mesh.offerOTA()
|
|
21
|
+
|
|
22
|
+
Announce and distribute firmware updates to mesh nodes.
|
|
23
|
+
|
|
24
|
+
```cpp
|
|
25
|
+
std::shared_ptr<Task> offerOTA(
|
|
26
|
+
TSTRING role,
|
|
27
|
+
TSTRING hardware,
|
|
28
|
+
TSTRING md5,
|
|
29
|
+
size_t noPart,
|
|
30
|
+
bool forced = false,
|
|
31
|
+
bool broadcasted = false, // Phase 2 feature
|
|
32
|
+
bool compressed = false // Phase 1 feature
|
|
33
|
+
)
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
#### Parameters
|
|
37
|
+
|
|
38
|
+
| Parameter | Type | Description | Default |
|
|
39
|
+
|-----------|------|-------------|---------|
|
|
40
|
+
| `role` | TSTRING | Target node role (e.g., "sensor", "gateway") | Required |
|
|
41
|
+
| `hardware` | TSTRING | Hardware type: "ESP32" or "ESP8266" | Required |
|
|
42
|
+
| `md5` | TSTRING | MD5 hash of firmware binary for version checking | Required |
|
|
43
|
+
| `noPart` | size_t | Total number of firmware chunks | Required |
|
|
44
|
+
| `forced` | bool | Force update even if MD5 matches current version | false |
|
|
45
|
+
| `broadcasted` | bool | **[Phase 2]** Enable broadcast distribution mode | false |
|
|
46
|
+
| `compressed` | bool | [Phase 1] Enable compression (40-60% bandwidth savings) | false |
|
|
47
|
+
|
|
48
|
+
#### Returns
|
|
49
|
+
|
|
50
|
+
`std::shared_ptr<Task>` - Shared pointer to task managing OTA announcements
|
|
51
|
+
|
|
52
|
+
#### Behavior
|
|
53
|
+
|
|
54
|
+
**Unicast Mode (broadcasted=false):**
|
|
55
|
+
- Each node requests chunks individually
|
|
56
|
+
- Root node responds to each request
|
|
57
|
+
- Network traffic: O(N × F) where N=nodes, F=firmware size
|
|
58
|
+
- Best for: 1-10 nodes
|
|
59
|
+
|
|
60
|
+
**Broadcast Mode (broadcasted=true):**
|
|
61
|
+
- Root node broadcasts chunks once
|
|
62
|
+
- All nodes receive simultaneously
|
|
63
|
+
- Network traffic: O(F) - independent of node count
|
|
64
|
+
- Best for: 10-100+ nodes
|
|
65
|
+
- ~98% traffic reduction for 50-node mesh
|
|
66
|
+
|
|
67
|
+
#### Example
|
|
68
|
+
|
|
69
|
+
```cpp
|
|
70
|
+
// Phase 2 Broadcast OTA with compression
|
|
71
|
+
auto otaTask = mesh.offerOTA(
|
|
72
|
+
"sensor", // Role
|
|
73
|
+
"ESP32", // Hardware
|
|
74
|
+
firmwareMD5, // MD5 hash
|
|
75
|
+
numChunks, // Number of chunks
|
|
76
|
+
false, // Not forced
|
|
77
|
+
true, // BROADCAST MODE (Phase 2)
|
|
78
|
+
true // Compressed (Phase 1)
|
|
79
|
+
);
|
|
80
|
+
|
|
81
|
+
// Monitor OTA progress
|
|
82
|
+
otaTask->setCallback([]() {
|
|
83
|
+
Serial.println("OTA announcement sent");
|
|
84
|
+
});
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
### mesh.initOTAReceive()
|
|
88
|
+
|
|
89
|
+
Initialize OTA receiver on a node to accept firmware updates.
|
|
90
|
+
|
|
91
|
+
```cpp
|
|
92
|
+
void initOTAReceive(
|
|
93
|
+
TSTRING role,
|
|
94
|
+
std::function<void(size_t current, size_t total)> progressCallback = nullptr,
|
|
95
|
+
bool acceptCompressed = true,
|
|
96
|
+
bool acceptBroadcast = true
|
|
97
|
+
)
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
#### Parameters
|
|
101
|
+
|
|
102
|
+
| Parameter | Type | Description | Default |
|
|
103
|
+
|-----------|------|-------------|---------|
|
|
104
|
+
| `role` | TSTRING | This node's role for matching firmware | Required |
|
|
105
|
+
| `progressCallback` | function | Callback for progress updates (current, total) | nullptr |
|
|
106
|
+
| `acceptCompressed` | bool | Accept compressed firmware (Phase 1) | true |
|
|
107
|
+
| `acceptBroadcast` | bool | Accept broadcast OTA (Phase 2) | true |
|
|
108
|
+
|
|
109
|
+
#### Example
|
|
110
|
+
|
|
111
|
+
```cpp
|
|
112
|
+
mesh.initOTAReceive(
|
|
113
|
+
"sensor",
|
|
114
|
+
[](size_t current, size_t total) {
|
|
115
|
+
Serial.printf("OTA Progress: %d/%d (%d%%)\n",
|
|
116
|
+
current, total, (current * 100) / total);
|
|
117
|
+
},
|
|
118
|
+
true, // Accept compressed
|
|
119
|
+
true // Accept broadcast
|
|
120
|
+
);
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
---
|
|
124
|
+
|
|
125
|
+
## Broadcast OTA (Phase 2)
|
|
126
|
+
|
|
127
|
+
### Architecture
|
|
128
|
+
|
|
129
|
+
Broadcast OTA distributes firmware to all nodes simultaneously instead of individually, dramatically reducing network traffic and update time.
|
|
130
|
+
|
|
131
|
+
**Message Flow:**
|
|
132
|
+
|
|
133
|
+
```
|
|
134
|
+
1. Root Node Announces Update:
|
|
135
|
+
Root → ALL: Broadcast Announce {role, hardware, md5, noPart, broadcasted=true}
|
|
136
|
+
|
|
137
|
+
2. Root Node Broadcasts Chunks:
|
|
138
|
+
Root → ALL: Broadcast Data(chunk 0)
|
|
139
|
+
Root → ALL: Broadcast Data(chunk 1)
|
|
140
|
+
Root → ALL: Broadcast Data(chunk 2)
|
|
141
|
+
...
|
|
142
|
+
Root → ALL: Broadcast Data(chunk N)
|
|
143
|
+
|
|
144
|
+
3. All Nodes Process:
|
|
145
|
+
Each node:
|
|
146
|
+
- Receives broadcasts
|
|
147
|
+
- Assembles chunks
|
|
148
|
+
- Verifies MD5
|
|
149
|
+
- Flashes firmware
|
|
150
|
+
- Reboots into new version
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
### Performance Comparison
|
|
154
|
+
|
|
155
|
+
| Mesh Size | Unicast Transmissions | Broadcast Transmissions | Reduction | Time Improvement |
|
|
156
|
+
|-----------|----------------------|------------------------|-----------|------------------|
|
|
157
|
+
| 10 nodes | 1,500 | 150 | 90% | ~10x faster |
|
|
158
|
+
| 50 nodes | 7,500 | 150 | 98% | ~50x faster |
|
|
159
|
+
| 100 nodes | 15,000 | 150 | 99% | ~100x faster |
|
|
160
|
+
|
|
161
|
+
*Assuming 150 firmware chunks*
|
|
162
|
+
|
|
163
|
+
### Memory Requirements
|
|
164
|
+
|
|
165
|
+
**Per Node:**
|
|
166
|
+
- Chunk buffer: ~1-2KB
|
|
167
|
+
- Tracking bitmap: ~1KB (for 150 chunks)
|
|
168
|
+
- Total overhead: +2-5KB
|
|
169
|
+
|
|
170
|
+
**Scalability:**
|
|
171
|
+
- Tested: Up to 100 nodes
|
|
172
|
+
- Theoretical: 200+ nodes with proper rate limiting
|
|
173
|
+
- Recommended: 10-100 nodes for optimal performance
|
|
174
|
+
|
|
175
|
+
### Implementation Details
|
|
176
|
+
|
|
177
|
+
The broadcast mode is implemented through a minimal change to `src/painlessmesh/ota.hpp`:
|
|
178
|
+
|
|
179
|
+
```cpp
|
|
180
|
+
static Data replyTo(const DataRequest& req, TSTRING data, size_t partNo) {
|
|
181
|
+
Data d;
|
|
182
|
+
// ... initialize fields ...
|
|
183
|
+
|
|
184
|
+
// Phase 2: Set BROADCAST routing when broadcasted flag is true
|
|
185
|
+
if (req.broadcasted) {
|
|
186
|
+
d.routing = router::BROADCAST;
|
|
187
|
+
}
|
|
188
|
+
return d;
|
|
189
|
+
}
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
### Best Practices
|
|
193
|
+
|
|
194
|
+
1. **Rate Limiting:** Don't broadcast chunks faster than nodes can process
|
|
195
|
+
2. **Chunk Size:** Use 1024-2048 byte chunks for optimal balance
|
|
196
|
+
3. **Network Stability:** Ensure mesh is stable before starting OTA
|
|
197
|
+
4. **Monitoring:** Use progress callbacks to track update status
|
|
198
|
+
5. **Fallback:** Keep unicast mode available for small deployments
|
|
199
|
+
|
|
200
|
+
---
|
|
201
|
+
|
|
202
|
+
## Firmware Status Reporting
|
|
203
|
+
|
|
204
|
+
### Alteriom MQTT Schema v1 Compliance
|
|
205
|
+
|
|
206
|
+
The firmware update process can report status using the Alteriom MQTT schema `firmware_status.schema.json` v1.
|
|
207
|
+
|
|
208
|
+
#### Schema Structure
|
|
209
|
+
|
|
210
|
+
```json
|
|
211
|
+
{
|
|
212
|
+
"schema_version": 1,
|
|
213
|
+
"device_id": "node-123456",
|
|
214
|
+
"device_type": "sensor",
|
|
215
|
+
"timestamp": "2024-10-11T16:30:00Z",
|
|
216
|
+
"firmware_version": "1.0.0",
|
|
217
|
+
"status": "downloading",
|
|
218
|
+
"from_version": "1.0.0",
|
|
219
|
+
"to_version": "2.0.0",
|
|
220
|
+
"progress_pct": 45.5,
|
|
221
|
+
"error": null
|
|
222
|
+
}
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
#### Status Values
|
|
226
|
+
|
|
227
|
+
| Status | Description | Required Fields |
|
|
228
|
+
|--------|-------------|----------------|
|
|
229
|
+
| `pending` | Update queued, not started | None |
|
|
230
|
+
| `downloading` | Downloading firmware chunks | `progress_pct` recommended |
|
|
231
|
+
| `flashing` | Writing firmware to flash | `progress_pct` recommended |
|
|
232
|
+
| `verifying` | Verifying firmware integrity | None |
|
|
233
|
+
| `rebooting` | Rebooting into new firmware | None |
|
|
234
|
+
| `completed` | Update successful | `to_version` |
|
|
235
|
+
| `failed` | Update failed | `error` required |
|
|
236
|
+
|
|
237
|
+
#### Example Implementation
|
|
238
|
+
|
|
239
|
+
```cpp
|
|
240
|
+
// Report OTA progress via MQTT
|
|
241
|
+
void reportOTAStatus(String status, float progress = -1, String error = "") {
|
|
242
|
+
DynamicJsonDocument doc(512);
|
|
243
|
+
|
|
244
|
+
// Envelope fields (required)
|
|
245
|
+
doc["schema_version"] = 1;
|
|
246
|
+
doc["device_id"] = String(mesh.getNodeId());
|
|
247
|
+
doc["device_type"] = "sensor";
|
|
248
|
+
doc["timestamp"] = getCurrentISO8601Timestamp();
|
|
249
|
+
doc["firmware_version"] = FIRMWARE_VERSION;
|
|
250
|
+
|
|
251
|
+
// Firmware status fields
|
|
252
|
+
doc["status"] = status;
|
|
253
|
+
doc["from_version"] = OLD_VERSION;
|
|
254
|
+
doc["to_version"] = NEW_VERSION;
|
|
255
|
+
|
|
256
|
+
if (progress >= 0) {
|
|
257
|
+
doc["progress_pct"] = progress;
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
if (error.length() > 0) {
|
|
261
|
+
doc["error"] = error;
|
|
262
|
+
} else {
|
|
263
|
+
doc["error"] = nullptr;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
String payload;
|
|
267
|
+
serializeJson(doc, payload);
|
|
268
|
+
mqttClient.publish("device/firmware/status", payload.c_str());
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
// Usage in OTA progress callback
|
|
272
|
+
mesh.initOTAReceive("sensor", [](size_t current, size_t total) {
|
|
273
|
+
float progress = (current * 100.0) / total;
|
|
274
|
+
|
|
275
|
+
if (current == 0) {
|
|
276
|
+
reportOTAStatus("downloading", 0);
|
|
277
|
+
} else if (current < total) {
|
|
278
|
+
reportOTAStatus("downloading", progress);
|
|
279
|
+
} else {
|
|
280
|
+
reportOTAStatus("flashing", 100);
|
|
281
|
+
}
|
|
282
|
+
});
|
|
283
|
+
```
|
|
284
|
+
|
|
285
|
+
---
|
|
286
|
+
|
|
287
|
+
## MQTT Schema Compliance
|
|
288
|
+
|
|
289
|
+
### Gateway Metrics
|
|
290
|
+
|
|
291
|
+
The MQTT Status Bridge publishes gateway metrics in full compliance with Alteriom MQTT schema v1.
|
|
292
|
+
|
|
293
|
+
**Schema:** `gateway_metrics.schema.json` v1
|
|
294
|
+
|
|
295
|
+
**Required Fields:**
|
|
296
|
+
- Envelope: `schema_version`, `device_id`, `device_type`, `timestamp`, `firmware_version`
|
|
297
|
+
- Metrics: `uptime_s` (minimum required)
|
|
298
|
+
|
|
299
|
+
**Published Message:**
|
|
300
|
+
```json
|
|
301
|
+
{
|
|
302
|
+
"schema_version": 1,
|
|
303
|
+
"device_id": "gateway-001",
|
|
304
|
+
"device_type": "gateway",
|
|
305
|
+
"timestamp": "2024-10-11T16:30:00Z",
|
|
306
|
+
"firmware_version": "2.1.0",
|
|
307
|
+
"metrics": {
|
|
308
|
+
"uptime_s": 3600,
|
|
309
|
+
"mesh_nodes": 12,
|
|
310
|
+
"memory_usage_pct": 45.2,
|
|
311
|
+
"connected_devices": 12
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
```
|
|
315
|
+
|
|
316
|
+
### Validation
|
|
317
|
+
|
|
318
|
+
To validate messages against the schema:
|
|
319
|
+
|
|
320
|
+
```javascript
|
|
321
|
+
// Node.js validation example
|
|
322
|
+
const { validators } = require('@alteriom/mqtt-schema');
|
|
323
|
+
|
|
324
|
+
const message = JSON.parse(mqttPayload);
|
|
325
|
+
const result = validators.gatewayMetrics(message);
|
|
326
|
+
|
|
327
|
+
if (!result.valid) {
|
|
328
|
+
console.error('Validation errors:', result.errors);
|
|
329
|
+
}
|
|
330
|
+
```
|
|
331
|
+
|
|
332
|
+
---
|
|
333
|
+
|
|
334
|
+
## Complete Examples
|
|
335
|
+
|
|
336
|
+
### Example 1: Basic OTA Sender
|
|
337
|
+
|
|
338
|
+
```cpp
|
|
339
|
+
#include <painlessMesh.h>
|
|
340
|
+
|
|
341
|
+
#define MESH_PREFIX "mesh"
|
|
342
|
+
#define MESH_PASSWORD "password"
|
|
343
|
+
#define MESH_PORT 5555
|
|
344
|
+
|
|
345
|
+
painlessMesh mesh;
|
|
346
|
+
|
|
347
|
+
void setup() {
|
|
348
|
+
Serial.begin(115200);
|
|
349
|
+
mesh.init(MESH_PREFIX, MESH_PASSWORD, MESH_PORT);
|
|
350
|
+
|
|
351
|
+
// Calculate firmware chunks
|
|
352
|
+
File firmware = SD.open("/firmware.bin");
|
|
353
|
+
size_t fileSize = firmware.size();
|
|
354
|
+
size_t numChunks = (fileSize + 1023) / 1024; // 1KB chunks
|
|
355
|
+
String md5 = calculateMD5(firmware);
|
|
356
|
+
|
|
357
|
+
// Offer OTA with Phase 2 broadcast
|
|
358
|
+
auto otaTask = mesh.offerOTA(
|
|
359
|
+
"sensor",
|
|
360
|
+
"ESP32",
|
|
361
|
+
md5,
|
|
362
|
+
numChunks,
|
|
363
|
+
false, // not forced
|
|
364
|
+
true, // BROADCAST
|
|
365
|
+
true // compressed
|
|
366
|
+
);
|
|
367
|
+
|
|
368
|
+
Serial.println("Broadcasting OTA update...");
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
void loop() {
|
|
372
|
+
mesh.update();
|
|
373
|
+
}
|
|
374
|
+
```
|
|
375
|
+
|
|
376
|
+
### Example 2: OTA Receiver with Status Reporting
|
|
377
|
+
|
|
378
|
+
```cpp
|
|
379
|
+
#include <painlessMesh.h>
|
|
380
|
+
#include <PubSubClient.h>
|
|
381
|
+
|
|
382
|
+
painlessMesh mesh;
|
|
383
|
+
PubSubClient mqttClient;
|
|
384
|
+
|
|
385
|
+
void reportOTAStatus(String status, float progress = -1) {
|
|
386
|
+
// Implementation as shown above
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
void setup() {
|
|
390
|
+
Serial.begin(115200);
|
|
391
|
+
mesh.init(MESH_PREFIX, MESH_PASSWORD, MESH_PORT);
|
|
392
|
+
|
|
393
|
+
// Initialize OTA receiver with progress reporting
|
|
394
|
+
mesh.initOTAReceive(
|
|
395
|
+
"sensor",
|
|
396
|
+
[](size_t current, size_t total) {
|
|
397
|
+
float pct = (current * 100.0) / total;
|
|
398
|
+
Serial.printf("OTA: %d/%d (%.1f%%)\n", current, total, pct);
|
|
399
|
+
|
|
400
|
+
// Report via MQTT
|
|
401
|
+
if (current == 0) {
|
|
402
|
+
reportOTAStatus("downloading", 0);
|
|
403
|
+
} else if (current < total) {
|
|
404
|
+
reportOTAStatus("downloading", pct);
|
|
405
|
+
} else {
|
|
406
|
+
reportOTAStatus("flashing", 100);
|
|
407
|
+
}
|
|
408
|
+
},
|
|
409
|
+
true, // accept compressed
|
|
410
|
+
true // accept broadcast
|
|
411
|
+
);
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
void loop() {
|
|
415
|
+
mesh.update();
|
|
416
|
+
mqttClient.loop();
|
|
417
|
+
}
|
|
418
|
+
```
|
|
419
|
+
|
|
420
|
+
### Example 3: Full MQTT Bridge with Schema Compliance
|
|
421
|
+
|
|
422
|
+
```cpp
|
|
423
|
+
#include <painlessMesh.h>
|
|
424
|
+
#include <PubSubClient.h>
|
|
425
|
+
#include "examples/bridge/mqtt_status_bridge.hpp"
|
|
426
|
+
|
|
427
|
+
painlessMesh mesh;
|
|
428
|
+
WiFiClient wifiClient;
|
|
429
|
+
PubSubClient mqttClient(wifiClient);
|
|
430
|
+
MqttStatusBridge* statusBridge;
|
|
431
|
+
|
|
432
|
+
void setup() {
|
|
433
|
+
Serial.begin(115200);
|
|
434
|
+
|
|
435
|
+
// Initialize mesh as bridge node
|
|
436
|
+
mesh.init(MESH_PREFIX, MESH_PASSWORD, MESH_PORT, WIFI_AP_STA);
|
|
437
|
+
mesh.setRoot(true);
|
|
438
|
+
mesh.setContainsRoot(true);
|
|
439
|
+
mesh.stationManual(WIFI_SSID, WIFI_PASSWORD);
|
|
440
|
+
|
|
441
|
+
// Connect to MQTT broker
|
|
442
|
+
mqttClient.setServer(MQTT_BROKER, 1883);
|
|
443
|
+
mqttClient.connect("painlessMesh-bridge");
|
|
444
|
+
|
|
445
|
+
// Initialize schema-compliant MQTT status bridge
|
|
446
|
+
statusBridge = new MqttStatusBridge(mesh, mqttClient);
|
|
447
|
+
statusBridge->setDeviceId("gateway-001");
|
|
448
|
+
statusBridge->setFirmwareVersion("2.1.0");
|
|
449
|
+
statusBridge->setPublishInterval(30000); // 30 seconds
|
|
450
|
+
statusBridge->enableMetrics(true); // Schema v1 compliant
|
|
451
|
+
statusBridge->enableTopology(true);
|
|
452
|
+
statusBridge->enableAlerts(true);
|
|
453
|
+
statusBridge->begin();
|
|
454
|
+
|
|
455
|
+
Serial.println("MQTT Status Bridge started");
|
|
456
|
+
Serial.println("Publishing schema-compliant gateway metrics");
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
void loop() {
|
|
460
|
+
mesh.update();
|
|
461
|
+
mqttClient.loop();
|
|
462
|
+
}
|
|
463
|
+
```
|
|
464
|
+
|
|
465
|
+
---
|
|
466
|
+
|
|
467
|
+
## Troubleshooting
|
|
468
|
+
|
|
469
|
+
### Common Issues
|
|
470
|
+
|
|
471
|
+
#### 1. OTA Not Starting
|
|
472
|
+
|
|
473
|
+
**Symptom:** Nodes don't respond to OTA announcements
|
|
474
|
+
|
|
475
|
+
**Solutions:**
|
|
476
|
+
- Verify role matches between sender and receiver
|
|
477
|
+
- Check hardware type (ESP32 vs ESP8266)
|
|
478
|
+
- Ensure nodes have OTA initialized with `initOTAReceive()`
|
|
479
|
+
- Check MD5 is different from current firmware
|
|
480
|
+
|
|
481
|
+
#### 2. Broadcast OTA Slow/Failing
|
|
482
|
+
|
|
483
|
+
**Symptom:** Broadcast mode slower than expected or nodes miss chunks
|
|
484
|
+
|
|
485
|
+
**Solutions:**
|
|
486
|
+
- Reduce broadcast rate (add delays between chunks)
|
|
487
|
+
- Check mesh stability (`mesh.getNodeList()` should be stable)
|
|
488
|
+
- Verify sufficient memory on nodes (check `ESP.getFreeHeap()`)
|
|
489
|
+
- Consider smaller chunk size for congested meshes
|
|
490
|
+
- Reduce number of nodes or use unicast for <10 nodes
|
|
491
|
+
|
|
492
|
+
#### 3. Schema Validation Failures
|
|
493
|
+
|
|
494
|
+
**Symptom:** MQTT consumers reject messages
|
|
495
|
+
|
|
496
|
+
**Solutions:**
|
|
497
|
+
- Verify `schema_version` is exactly 1 (integer)
|
|
498
|
+
- Check `device_id` matches pattern `^[A-Za-z0-9_-]+$` (no spaces)
|
|
499
|
+
- Ensure `device_type` is exactly "gateway" or "sensor"
|
|
500
|
+
- Validate timestamp is ISO 8601: `YYYY-MM-DDTHH:MM:SSZ`
|
|
501
|
+
- Check `firmware_version` is not empty and ≤40 characters
|
|
502
|
+
- Ensure `metrics` object exists for gateway_metrics
|
|
503
|
+
- Verify `uptime_s` is present and non-negative integer
|
|
504
|
+
|
|
505
|
+
#### 4. Timestamp Issues
|
|
506
|
+
|
|
507
|
+
**Symptom:** Timestamps rejected or incorrect
|
|
508
|
+
|
|
509
|
+
**Solutions:**
|
|
510
|
+
- Use NTP time sync for accurate timestamps
|
|
511
|
+
- Implement RTC module for offline accuracy
|
|
512
|
+
- Fallback implementation uses Unix epoch + millis()
|
|
513
|
+
- Ensure format: `1970-01-15T12:34:56Z` (must include date and Z suffix)
|
|
514
|
+
- Validate with regex: `^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}Z$`
|
|
515
|
+
|
|
516
|
+
### Debug Commands
|
|
517
|
+
|
|
518
|
+
```cpp
|
|
519
|
+
// Enable OTA debug logging
|
|
520
|
+
mesh.setDebugMsgTypes(ERROR | STARTUP | OTA);
|
|
521
|
+
|
|
522
|
+
// Check OTA status
|
|
523
|
+
Serial.printf("Accepting OTA: %s\n", mesh.isAcceptingOTA() ? "YES" : "NO");
|
|
524
|
+
|
|
525
|
+
// Monitor memory during OTA
|
|
526
|
+
Serial.printf("Free heap: %d bytes\n", ESP.getFreeHeap());
|
|
527
|
+
|
|
528
|
+
// Validate MQTT message locally
|
|
529
|
+
#include <ArduinoJson.h>
|
|
530
|
+
DynamicJsonDocument doc(1024);
|
|
531
|
+
deserializeJson(doc, mqttPayload);
|
|
532
|
+
// Check required fields manually
|
|
533
|
+
bool valid = doc.containsKey("schema_version") &&
|
|
534
|
+
doc["schema_version"] == 1 &&
|
|
535
|
+
doc.containsKey("metrics");
|
|
536
|
+
```
|
|
537
|
+
|
|
538
|
+
---
|
|
539
|
+
|
|
540
|
+
## References
|
|
541
|
+
|
|
542
|
+
- **painlessMesh OTA Plugin:** [src/painlessmesh/ota.hpp](../src/painlessmesh/ota.hpp)
|
|
543
|
+
- **MQTT Status Bridge:** [examples/bridge/mqtt_status_bridge.hpp](../examples/bridge/mqtt_status_bridge.hpp)
|
|
544
|
+
- **Alteriom MQTT Schema:** https://www.npmjs.com/package/@alteriom/mqtt-schema
|
|
545
|
+
- **Phase 2 Guide:** [PHASE2_GUIDE.md](PHASE2_GUIDE.md)
|
|
546
|
+
- **Schema Compliance:** [MQTT_SCHEMA_COMPLIANCE.md](MQTT_SCHEMA_COMPLIANCE.md)
|
|
547
|
+
- **Phase 2 Implementation:** [improvements/PHASE2_IMPLEMENTATION.md](improvements/PHASE2_IMPLEMENTATION.md)
|
|
548
|
+
|
|
549
|
+
---
|
|
550
|
+
|
|
551
|
+
**Last Updated:** October 2024
|
|
552
|
+
**Schema Version:** v1
|
|
553
|
+
**painlessMesh Version:** 1.6.1+
|
|
554
|
+
**Phase:** 2 (Broadcast OTA + MQTT Status Bridge)
|