Repository navigation
PHASE2_GUIDE
Phase 2 adds production-ready features for scalable OTA distribution and professional monitoring:
- ✅ Broadcast OTA - True mesh-wide firmware distribution scaling to 50+ nodes
- ✅ MQTT Status Bridge - Professional monitoring integration with Grafana, InfluxDB, Prometheus
What it does: Distributes firmware to all nodes simultaneously via broadcast, dramatically reducing network traffic.
Key Benefits:
- ~98% network traffic reduction vs unicast (1 broadcast vs N unicasts)
- Faster distribution - All nodes receive chunks in parallel
- Scales to 50-100+ nodes efficiently
- Backward compatible - Works with Phase 1 compression
- Memory efficient - Only +2-5KB per node
Architecture:
Root Node All Nodes
├─> Broadcast: Announce ─┐
├─> Broadcast: Chunk 0 ─┼─> Listen & Cache
├─> Broadcast: Chunk 1 ─┼─> Assemble Firmware
└─> Broadcast: Chunk N ─┘ Reboot When Complete
What it does: Publishes comprehensive mesh status to MQTT topics for professional monitoring tools.
Key Benefits:
- Cloud integration via MQTT
- Professional monitoring - Grafana, InfluxDB, Prometheus, Home Assistant
- Real-time visibility into mesh health
- Automated alerting for critical conditions
- Configurable publishing intervals and topics
MQTT Topics:
-
mesh/status/nodes- List of all nodes in mesh -
mesh/status/topology- Complete mesh structure JSON -
mesh/status/metrics- Performance statistics -
mesh/status/alerts- Active alert conditions -
mesh/status/node/{id}- Per-node detailed status (optional)
- Phase 1 features installed (Compressed OTA + Enhanced Status)
- ESP32 or ESP8266 hardware
- For MQTT Bridge: MQTT broker (Mosquitto, HiveMQ, etc.)
- For MQTT Bridge: External WiFi connection
#include "painlessMesh.h"
painlessMesh mesh;
void setup() {
mesh.init(MESH_PREFIX, MESH_PASSWORD, &scheduler, MESH_PORT);
#ifdef PAINLESSMESH_ENABLE_OTA
// Phase 2: Enable broadcast mode
mesh.offerOTA(
"sensor", // Role
"ESP32", // Hardware
firmwareMD5, // MD5 hash
numParts, // Number of chunks
false, // Not forced
true, // *** BROADCAST MODE ***
true // Compressed (Phase 1)
);
#endif
}#include <PubSubClient.h>
#include "examples/bridge/mqtt_status_bridge.hpp"
painlessMesh mesh;
PubSubClient mqttClient(mqttBroker, 1883, mqttCallback, wifiClient);
MqttStatusBridge* statusBridge;
void setup() {
// Initialize mesh as bridge node
mesh.init(MESH_PREFIX, MESH_PASSWORD, MESH_PORT, WIFI_AP_STA, 6);
mesh.setRoot(true);
mesh.setContainsRoot(true);
// Connect to external WiFi for MQTT
mesh.stationManual(STATION_SSID, STATION_PASSWORD);
// Initialize status bridge
statusBridge = new MqttStatusBridge(mesh, mqttClient);
statusBridge->setPublishInterval(30000); // 30 seconds
statusBridge->begin();
}std::shared_ptr<Task> offerOTA(
TSTRING role,
TSTRING hardware,
TSTRING md5,
size_t noPart,
bool forced = false,
bool broadcasted = false, // Phase 2: Broadcast mode
bool compressed = false // Phase 1: Compression
)Parameters:
-
role- Node role this firmware is for (e.g., "sensor", "gateway") -
hardware- Hardware type: "ESP32" or "ESP8266" -
md5- MD5 hash of firmware (for version checking) -
noPart- Number of firmware chunks -
forced- Force update even if MD5 matches (default: false) -
broadcasted- [Phase 2] Enable broadcast mode (default: false) -
compressed- [Phase 1] Enable compression (default: false)
Returns: Shared pointer to Task that manages OTA announcements
Example:
auto otaTask = mesh.offerOTA(
"sensor", "ESP32", md5, 100,
false, // not forced
true, // BROADCAST MODE
true // compressed
);MqttStatusBridge(painlessMesh& mesh, PubSubClient& mqttClient)void setPublishInterval(uint32_t interval)Set publishing interval in milliseconds (default: 30000)
void setTopicPrefix(const String& prefix)Set MQTT topic prefix (default: "mesh/status/")
void enableTopology(bool enable)Enable/disable topology publishing (default: true)
void enableMetrics(bool enable)Enable/disable metrics publishing (default: true)
void enableAlerts(bool enable)Enable/disable alerts publishing (default: true)
void enablePerNode(bool enable)Enable/disable per-node status publishing (default: false)
void begin()Start publishing status to MQTT
void stop()Stop publishing
void publishNow()Trigger immediate status publish (useful for testing)
#include "painlessMesh.h"
#define MESH_PREFIX "myMesh"
#define MESH_PASSWORD "password"
#define MESH_PORT 5555
#define OTA_PART_SIZE 1024
painlessMesh mesh;
void setup() {
mesh.init(MESH_PREFIX, MESH_PASSWORD, MESH_PORT);
#ifdef PAINLESSMESH_ENABLE_OTA
// Setup OTA sender
mesh.initOTASend(firmwareCallback, OTA_PART_SIZE);
// Announce firmware with broadcast + compression
mesh.offerOTA(
"sensor", // role
"ESP32", // hardware
"abc123...", // MD5
150, // number of chunks
false, // not forced
true, // BROADCAST
true // compressed
);
#endif
}
size_t firmwareCallback(ota::DataRequest pkg, char* buffer) {
// Read firmware chunk from storage
// Return bytes read into buffer
}#include <PubSubClient.h>
#include "examples/bridge/mqtt_status_bridge.hpp"
MqttStatusBridge* bridge;
void setup() {
// ... initialize mesh and MQTT ...
bridge = new MqttStatusBridge(mesh, mqttClient);
// Custom configuration
bridge->setPublishInterval(60000); // Publish every minute
bridge->setTopicPrefix("alteriom/"); // Custom topic prefix
bridge->enableTopology(true); // Publish topology
bridge->enableMetrics(true); // Publish metrics
bridge->enableAlerts(true); // Publish alerts
bridge->enablePerNode(false); // Disable per-node (too many nodes)
bridge->begin();
}
void loop() {
mesh.update();
mqttClient.loop();
// Trigger manual publish on demand
if (buttonPressed()) {
bridge->publishNow();
}
}#include "painlessMesh.h"
#include "examples/alteriom/alteriom_sensor_package.hpp"
#include "examples/bridge/mqtt_status_bridge.hpp"
void setup() {
mesh.init(...);
// Phase 1: Enhanced Status
alteriom::EnhancedStatusPackage status;
status.uptime = millis() / 1000;
status.nodeCount = mesh.getNodeList().size();
mesh.sendBroadcast(status.toJsonString());
// Phase 2: Broadcast OTA
mesh.offerOTA("sensor", "ESP32", md5, parts, false, true, true);
// Phase 2: MQTT Bridge
statusBridge->begin();
}| Mesh Size | Unicast Traffic | Broadcast Traffic | Reduction |
|---|---|---|---|
| 10 nodes | 10x chunks | 1x chunks | 90% |
| 50 nodes | 50x chunks | 1x chunks | 98% |
| 100 nodes | 100x chunks | 1x chunks | 99% |
Example: 150 chunk firmware update
- Unicast mode: 50 nodes × 150 chunks = 7,500 transmissions
- Broadcast mode: 1 × 150 chunks = 150 transmissions
- Reduction: 7,350 fewer transmissions (98%)
| Feature | Memory Impact | Notes |
|---|---|---|
| Broadcast OTA | +2-5KB per node | Chunk bitmap + buffer |
| MQTT Bridge | +5-8KB root node | Status collection |
| Feature | Messages/Interval | Size | Total/Interval |
|---|---|---|---|
| Node List | 1 | ~200 bytes | 200 bytes |
| Topology | 1 | ~1-5KB | 1-5KB |
| Metrics | 1 | ~300 bytes | 300 bytes |
| Alerts | 1 | ~400 bytes | 400 bytes |
| Per-node (50 nodes) | 50 | ~150 bytes | ~7.5KB |
Recommended: Disable per-node publishing for meshes >20 nodes
{
"datasource": "MQTT",
"targets": [
{
"topic": "mesh/status/metrics",
"field": "nodeCount"
}
]
}[[inputs.mqtt_consumer]]
servers = ["tcp://localhost:1883"]
topics = [
"mesh/status/metrics",
"mesh/status/alerts"
]
data_format = "json"mqtt:
server: tcp://localhost:1883
topics:
- mesh/status/metrics
- mesh/status/alertssensor:
- platform: mqtt
name: "Mesh Node Count"
state_topic: "mesh/status/metrics"
value_template: "{{ value_json.nodeCount }}"
- platform: mqtt
name: "Mesh Free Heap"
state_topic: "mesh/status/metrics"
value_template: "{{ value_json.freeHeap }}"
unit_of_measurement: "bytes"Problem: Nodes not receiving broadcast chunks
Solutions:
- Ensure
broadcasted=truein offerOTA() - Check mesh connectivity (all nodes must be connected)
- Verify nodes are running receiver code with
initOTAReceive() - Check for mesh congestion (add delays between chunks)
Problem: Out-of-sequence chunks
Solution: This is normal! Broadcast mode handles out-of-order delivery automatically.
Problem: No status published to MQTT
Solutions:
- Check MQTT broker connectivity
- Verify mqttClient.connected() returns true
- Check publish interval (default 30s)
- Enable Serial debug output
Problem: High MQTT traffic
Solutions:
- Increase publish interval (30s → 60s → 120s)
- Disable per-node publishing
- Disable topology publishing if mesh structure is static
Step 1: Update offerOTA() calls
// Phase 1
mesh.offerOTA(role, hardware, md5, parts, false, false, true);
// Phase 2 - just add broadcast flag
mesh.offerOTA(role, hardware, md5, parts, false, true, true);
// ^^^^Step 2: Add MQTT bridge (optional)
#include "examples/bridge/mqtt_status_bridge.hpp"
MqttStatusBridge* bridge = new MqttStatusBridge(mesh, mqttClient);
bridge->begin();✅ Fully backward compatible
- Broadcast defaults to
false(unicast mode) - Non-broadcast nodes work alongside broadcast nodes
- MQTT bridge is optional add-on
- All Phase 1 features continue to work
✅ Use broadcast mode when:
- Mesh has 10+ nodes
- All nodes need same firmware
- Network bandwidth is limited
- Fast distribution is critical
❌ Don't use broadcast mode when:
- Mesh has <5 nodes (unicast is sufficient)
- Different nodes need different firmware
- Targeting specific nodes only
✅ Use MQTT bridge when:
- Production deployment
- Remote monitoring required
- Integration with existing tools (Grafana, etc.)
- Automated alerting needed
- Cloud connectivity available
❌ Don't use MQTT bridge when:
- Development/testing environment
- No external WiFi available
- No MQTT broker available
- Mesh is purely offline
Small meshes (1-10 nodes):
- Publish interval: 30 seconds
- Enable all features
- Enable per-node status
Medium meshes (10-50 nodes):
- Publish interval: 60 seconds
- Enable topology, metrics, alerts
- Disable per-node status
Large meshes (50+ nodes):
- Publish interval: 120 seconds
- Enable metrics and alerts only
- Disable topology and per-node
- Progressive rollout OTA (Option 1B)
- Real-time telemetry streams (Option 2C)
- Proactive alerting system
- Large-scale mesh support (100+ nodes)
- PHASE2_IMPLEMENTATION.md - Technical details
- examples/alteriom/phase2_features.ino - Complete example
- examples/bridge/mqtt_status_bridge_example.ino - MQTT example
- FEATURE_PROPOSALS.md - All features overview
Questions? Open an issue on GitHub with logs and configuration details.
Status: ✅ Phase 2 Complete - Production Ready
Recommended For: Medium to large mesh deployments (10-100+ nodes)
Risk: Low (backward compatible, well-tested)
Value: High (scalability + professional monitoring)