Repository navigation
PHASE1_GUIDE
github-actions[bot] edited this page Oct 24, 2025
·
2 revisions
Phase 1 implements two key enhancements to painlessMesh:
- Option 1E: Compressed OTA Transfer - 40-60% bandwidth reduction
- Option 2A: Enhanced StatusPackage - Comprehensive health monitoring
These features provide immediate value with minimal complexity and risk.
- ✅ 40-60% bandwidth reduction - Less network congestion
- ✅ Faster updates - Reduced transfer time
- ✅ Lower energy consumption - Less radio time
- ✅ Universal benefit - Works with all distribution methods
- ✅ Backward compatible - Works with uncompressed nodes
- ✅ Comprehensive monitoring - Device health + mesh stats + performance
- ✅ Proactive alerting - Detect issues before they become critical
- ✅ Standardized format - Consistent across all Alteriom nodes
- ✅ Easy integration - Ready for dashboards and monitoring tools
- ✅ Minimal overhead - ~500 bytes per status report
#include "painlessMesh.h"
#define PAINLESSMESH_ENABLE_OTA
painlessMesh mesh;
void setup() {
// Initialize mesh...
// Setup OTA sender with callback to provide firmware data
mesh.initOTASend(firmwareCallback, OTA_PART_SIZE);
// Announce firmware with compression enabled
String md5 = "abc123def456"; // MD5 hash of firmware
size_t parts = 100; // Number of firmware chunks
mesh.offerOTA(
"sensor", // Role
"ESP32", // Hardware
md5, // MD5 hash
parts, // Number of parts
false, // Not forced
false, // Not broadcast (Phase 2)
true // COMPRESSED! ← Phase 1 feature
);
}
size_t firmwareCallback(painlessmesh::plugin::ota::DataRequest req, char* buffer) {
// Load firmware chunk into buffer
// Return size of data loaded
return loadFirmwareChunk(req.partNo, buffer);
}#include "painlessMesh.h"
#define PAINLESSMESH_ENABLE_OTA
painlessMesh mesh;
void setup() {
// Initialize mesh...
// Setup OTA receiver - automatically handles compression
mesh.initOTAReceive("sensor", progressCallback);
// That's it! Compression is handled automatically
}
void progressCallback(int current, int total) {
Serial.printf("OTA Progress: %d/%d (%.1f%%)\n",
current, total, 100.0 * current / total);
}Key Points:
- Compression is transparent to receiver nodes
- Nodes automatically decompress during flash write
- MD5 verification uses original (uncompressed) firmware hash
- Backward compatible - compressed nodes can receive uncompressed OTA
#include "alteriom_sensor_package.hpp"
#include "painlessMesh.h"
painlessMesh mesh;
void sendEnhancedStatus() {
alteriom::EnhancedStatusPackage status;
// Device Health
status.deviceStatus = 0x01; // Online
status.uptime = millis() / 1000; // Seconds
status.freeMemory = ESP.getFreeHeap() / 1024; // KB
status.wifiStrength = map(WiFi.RSSI(), -100, -50, 0, 100);
status.firmwareVersion = "1.0.0";
status.firmwareMD5 = "abc123def456";
// Mesh Statistics
status.nodeCount = mesh.getNodeList().size();
status.connectionCount = mesh.connectionCount();
status.messagesReceived = getTotalMessagesReceived();
status.messagesSent = getTotalMessagesSent();
status.messagesDropped = getTotalMessagesDropped();
// Performance Metrics
status.avgLatency = calculateAverageLatency(); // ms
status.packetLossRate = calculatePacketLoss(); // 0-100%
status.throughput = calculateThroughput(); // bytes/sec
// Alerts
status.alertFlags = 0;
if (status.freeMemory < 32) status.alertFlags |= 0x01; // Low memory
if (status.wifiStrength < 30) status.alertFlags |= 0x02; // Weak signal
status.lastError = (status.alertFlags == 0) ? "" : "See alerts";
// Send as broadcast
String msg;
protocol::Variant(&status).printTo(msg);
mesh.sendBroadcast(msg);
}void receivedCallback(uint32_t from, String& msg) {
DynamicJsonDocument doc(2048);
deserializeJson(doc, msg);
int msgType = doc["type"];
if (msgType == 203) { // Enhanced Status
protocol::Variant variant(msg);
auto status = variant.to<alteriom::EnhancedStatusPackage>();
Serial.printf("Status from %u: v%s\n", from, status.firmwareVersion.c_str());
Serial.printf(" Uptime: %ds, Memory: %dKB, Nodes: %d\n",
status.uptime, status.freeMemory, status.nodeCount);
Serial.printf(" Messages: %d RX, %d TX, %d dropped\n",
status.messagesReceived, status.messagesSent,
status.messagesDropped);
Serial.printf(" Performance: %dms latency, %d%% loss, %d B/s\n",
status.avgLatency, status.packetLossRate, status.throughput);
if (status.alertFlags) {
Serial.printf(" ALERTS: 0x%02X - %s\n",
status.alertFlags, status.lastError.c_str());
}
}
}The alertFlags field uses bit flags for different alert conditions:
| Bit | Value | Meaning |
|---|---|---|
| 0 | 0x01 | Low memory warning |
| 1 | 0x02 | Weak WiFi signal |
| 2 | 0x04 | Isolated node (no connections) |
| 3 | 0x08 | High packet loss rate |
| 4 | 0x10 | High message latency |
| 5 | 0x20 | Frequent disconnections |
| 6 | 0x40 | Custom application alert |
| 7 | 0x80 | Critical error condition |
| Type | Package Name | Description |
|---|---|---|
| 200 | SensorPackage | Environmental sensor data |
| 400 | CommandPackage | Device control commands |
| 202 | StatusPackage | Basic status (original) |
| 203 | EnhancedStatusPackage | Comprehensive status (Phase 1) |
For production use, integrate with painlessMesh metrics:
#include "painlessmesh/metrics.hpp"
void sendEnhancedStatus() {
alteriom::EnhancedStatusPackage status;
// ... set device health fields ...
// Integrate with mesh metrics (if enabled)
#ifdef PAINLESSMESH_ENABLE_METRICS
auto& metrics = mesh.getMetrics();
status.messagesReceived = metrics.message_stats().messages_received;
status.messagesSent = metrics.message_stats().messages_sent;
status.avgLatency = metrics.message_stats().average_latency_ms();
// ... other metrics ...
#endif
// ... send status ...
}See the examples/alteriom/ directory for complete examples:
- phase1_features.ino - Comprehensive Phase 1 demonstration
- improved_sensor_node.ino - Enhanced sensor with validation
- Memory: +4-8KB RAM (decompression buffer)
- CPU: Minimal decompression overhead
- Bandwidth: 40-60% reduction (typical)
- Update Time: 35-70s for 10 nodes (vs 60-120s uncompressed)
- Memory: +500 bytes per status report
- Network: ~1.5KB per status message
- CPU: Negligible serialization overhead
- Frequency: Configurable (recommend 30-60s interval)
Both features are fully backward compatible:
- Nodes without compression support can still receive uncompressed OTA
- Mixed mesh with compressed and uncompressed nodes works correctly
- Compression flag is optional in announce messages
- Uses new type ID (203) to distinguish from basic status (202)
- Nodes can send both basic and enhanced status
- Receivers can handle both types simultaneously
- Default values for all fields (safe to omit fields)
// Before (Phase 0)
mesh.offerOTA("sensor", "ESP32", md5, parts);
// After (Phase 1) - just add compression flag
mesh.offerOTA("sensor", "ESP32", md5, parts, false, false, true);
// ^^^^^ ^^^^^ ^^^^
// forced bcast compress// Before (basic status)
alteriom::StatusPackage status;
status.uptime = millis() / 1000;
status.freeMemory = ESP.getFreeHeap() / 1024;
// ... send ...
// After (enhanced status) - just add fields
alteriom::EnhancedStatusPackage status;
status.uptime = millis() / 1000;
status.freeMemory = ESP.getFreeHeap() / 1024;
status.nodeCount = mesh.getNodeList().size(); // New fields
status.messagesReceived = getTotalRx(); // New fields
// ... send ...Run the test suite to verify Phase 1 features:
cd /path/to/painlessMesh
cmake -G Ninja .
ninja
./bin/catch_alteriom_packagesAll tests should pass (80+ assertions).
Phase 1 provides the foundation for Phase 2 features:
- Broadcast OTA (Option 1A) - Single transmission to all nodes
- MQTT Status Bridge (Option 2E) - Cloud integration
- Grafana/InfluxDB dashboards - Professional monitoring
- Check that
PAINLESSMESH_ENABLE_OTAis defined - Verify compression flag is set to
true - Ensure sender has compression library available
- Check receiver has sufficient RAM for decompression buffer
- Verify type ID is 203 (not 202)
- Check receiver handles type 203 in callback
- Ensure sufficient buffer size (2048+ bytes for JSON parsing)
- Verify all string fields have valid values
- Reduce status broadcast frequency (use 60s+ intervals)
- Check memory availability (need ~8KB free for OTA)
- Monitor packet loss rate in enhanced status
- Consider mesh topology (star vs mesh affects performance)
For questions or issues:
- Check full proposal:
docs/improvements/FEATURE_PROPOSALS.md - Review examples:
examples/alteriom/phase1_features.ino - Open GitHub issue with logs and configuration
Phase 1 Status: ✅ Implemented and Tested
Expected Performance: 40-60% OTA bandwidth reduction + comprehensive status monitoring
Risk Level: Low (backward compatible, minimal changes)