EnerOS v0.36.0
Release Date: 2026-05-02 Codename: IoT Git Tag: v0.36.0 Support Status: Stable Total Crates: 84 (6 new) Test Cases: 10700+ (700 new)
Overview
EnerOS v0.36.0 “IoT” is an IoT ubiquitous access-focused release, extending EnerOS’s device access capabilities from traditional IEC 61850/SCADA to the IoT protocol system, achieving ubiquitous access to all elements of “source-grid-load-storage.” This release enables EnerOS to access millions of IoT devices, supporting plug-and-play access for new devices such as smart distribution grids, distributed renewable energy, user-side energy storage, and EV charging piles.
The core design philosophy of the IoT release is “protocol-agnostic + device shadow” — upper-layer applications do not directly interface with various heterogeneous communication protocols, but interact through a unified device model and device shadows. The device shadow, as a digital proxy of the physical device in the kernel, decouples the device’s online status from the application’s access needs: when the device is offline, applications can still read the last known state from the shadow; when the device comes online, the shadow automatically syncs and appends data from the offline period.
This release introduces five core capabilities: MQTT Protocol Support, OPC UA Protocol Support, Device Shadow Engine, Batch Device Management, and Device Access Gateway. All capabilities are implemented through five new crates: eneros-iot, eneros-iot-mqtt, eneros-iot-opcua, eneros-iot-shadow, and eneros-iot-batch.
Key Metrics
| Metric | Value | Description |
|---|---|---|
| MQTT concurrent connections | 100K | Single node |
| OPC UA device access | 50K | Single node |
| Device shadow update latency | 2ms | Per device |
| Batch config deployment | 10K devices/sec | Parallel |
| New Crates | 6 | IoT-related |
| New tests | 700+ | Includes 150 end-to-end |
New Features
1. MQTT Protocol Support
Adds the eneros-iot-mqtt crate, providing a high-performance MQTT 5.0 broker and client, supporting million-scale concurrent device access. MQTT is the de facto standard in the IoT field, widely used in smart meters, charging piles, distributed solar, and other devices.
MQTT Broker Configuration
use eneros_iot_mqtt::{MqttBroker, BrokerConfig, AuthConfig};
let broker = MqttBroker::new(BrokerConfig {
listen: "0.0.0.0:1883".parse()?,
listen_tls: Some("0.0.0.0:8883".parse()?),
max_connections: 100_000,
max_packet_size: 256 * 1024,
auth: AuthConfig::Certificate {
ca: "/etc/eneros/ca.pem",
require_client_cert: true,
},
session_persistence: true,
retained_messages: 100_000,
}).await?;
// Start broker
broker.start().await?;
Device Access and Data Flow
use eneros_iot_mqtt::{DeviceClient, TopicPattern};
// Device side: publish telemetry data
let client = DeviceClient::connect("mqtts://broker.eneros.io:8883").await?;
client.publish(
TopicPattern::telemetry("meter-001"),
json!({
"voltage": 220.3,
"current": 15.2,
"power": 3.35,
"timestamp": now_iso8601(),
}),
QoS::AtLeastOnce,
).await?;
// Platform side: subscribe to device data stream
let mut stream = broker.subscribe(TopicPattern::telemetry("+")).await?;
while let Some(msg) = stream.next().await {
let device_id = msg.topic.extract_device_id()?;
let telemetry: Telemetry = serde_json::from_slice(&msg.payload)?;
// Auto-write to timeseries engine
timeseries.write(device_id, telemetry).await?;
}
MQTT Topic Design
| Topic Pattern | Purpose | QoS | Example |
|---|---|---|---|
| devices/{id}/telemetry | Telemetry data reporting | 1 | devices/meter-001/telemetry |
| devices/{id}/command | Command delivery | 2 | devices/meter-001/command |
| devices/{id}/event | Event reporting | 1 | devices/meter-001/event |
| devices/{id}/shadow | Shadow update | 1 | devices/meter-001/shadow |
| devices/{id}/status | Online status | 1 | devices/meter-001/status |
| broadcast/{topic} | Broadcast message | 0 | broadcast/ota-notice |
Performance Benchmarks
| Metric | Value | Description |
|---|---|---|
| Concurrent connections | 100K | Single node |
| Message throughput | 500K/sec | QoS 1 |
| Message latency P99 | 3ms | Same node |
| Memory usage | 1.2 GB | 100K connections |
| TLS handshake | 8ms | Certificate authentication |
2. OPC UA Protocol Support
Adds the eneros-iot-opcua crate, providing OPC UA client and server for accessing industrial control systems (DCS/PLC/RTU). OPC UA is the mainstream protocol in industrial automation, supporting information models and security mechanisms.
OPC UA Client
use eneros_iot_opcua::{OpcUaClient, ClientConfig, SecurityPolicy};
let client = OpcUaClient::new(ClientConfig {
endpoint: "opc.tcp://10.20.30.40:4840",
security: SecurityPolicy::Basic256Sha256,
auth: AuthMode::UserName {
username: "eneros",
password: "********",
},
subscription_interval: Duration::milliseconds(100),
}).await?;
// Connect and browse address space
let namespace = client.get_namespace("urn:PLC:Substation1").await?;
let nodes = client.browse(&namespace.root()).await?;
// Subscribe to node data changes
let subscription = client.subscribe(vec![
"ns=2;s=Bus1.Voltage",
"ns=2;s=Line3.Current",
"ns=2;s=CircuitBreaker42.Status",
]).await?;
let mut stream = subscription.data_changes();
while let Some(change) = stream.next().await {
println!("Node {} changed: {:?}", change.node_id, change.value);
timeseries.write(change.node_id, change.value).await?;
}
OPC UA Information Model Mapping
| OPC UA Concept | EnerOS Concept | Mapping Method |
|---|---|---|
| Node | Device/measurement point | Auto-registration |
| Object | Device instance | Maps to Equipment |
| Variable | Telemetry data | Maps to TimeseriesPoint |
| Method | Control command | Maps to Command |
| Event | Event alarm | Maps to Event |
3. Device Shadow Engine
Adds the eneros-iot-shadow crate, maintaining a digital shadow for each device, containing desired state, reported state, and last known state.
Shadow Data Structure
use eneros_iot_shadow::{DeviceShadow, ShadowState};
let shadow = shadow_store.get_or_create("meter-001").await?;
// Device reports state
shadow.update_reported(json!({
"voltage": 220.3,
"current": 15.2,
"power_factor": 0.95,
"status": "online",
})).await?;
// Application sets desired state
shadow.update_desired(json!({
"switch": "on",
"brightness": 80,
})).await?;
// Get current shadow state
let state: ShadowState = shadow.state().await?;
println!("Desired state: {:?}", state.desired);
println!("Reported state: {:?}", state.reported);
println!("Delta: {:?}", state.delta); // Difference between desired and reported
Shadow Update Flow
// Auto-sync delta after device comes online
shadow.on_device_online(|device_id| {
let delta = shadow.delta().await?;
if !delta.is_empty() {
// Deliver desired state to device
mqtt_client.publish(
TopicPattern::command(device_id),
delta,
QoS::AtLeastOnce,
).await?;
}
}).await?;
// Preserve last state when device goes offline
shadow.on_device_offline(|device_id| {
shadow.freeze_last_known().await?;
}).await?;
Shadow Versioning and Conflicts
| Field | Type | Description |
|---|---|---|
| version | u64 | Monotonically increasing version number |
| timestamp | DateTime | Last update time |
| desired | JSON | Desired state |
| reported | JSON | Reported state |
| last_known | JSON | Last known state |
| metadata | Map | Per-field update time |
4. Batch Device Management
Adds the eneros-iot-batch crate, supporting batch registration, configuration, upgrade, and control for large-scale devices.
Batch Registration
use eneros_iot_batch::{BatchManager, BatchOperation};
let manager = BatchManager::new(&ctx);
// Batch register devices (CSV import)
let job = manager.batch_register()
.source("devices.csv")
.template(DeviceTemplate::new("smart-meter-v3"))
.parallelism(32)
.on_progress(|p| println!("Registration progress: {:.1}%", p.percentage))
.execute().await?;
println!("Success: {}, Failure: {}", job.success, job.failure);
Batch Configuration Deployment
// Batch configure 10,000 devices
let rollout = manager.batch_config()
.target(DeviceFilter::model("smart-meter-v3"))
.config(ConfigPatch {
collection_interval: Duration::seconds(15),
reporting_endpoint: "mqtts://broker.eneros.io:8883",
firmware_version: "3.2.1",
})
.strategy(RolloutStrategy::Wave {
wave_size: 500,
wave_interval: Duration::minutes(5),
failure_threshold: 0.02, // Pause at 2% failure rate
})
.execute().await?;
Batch Operation Types
| Operation | Description | Concurrency | Duration (10K devices) |
|---|---|---|---|
| Register | Batch register devices | 32 | 5 minutes |
| Configure | Batch deploy config | 16 | 10 minutes |
| Upgrade | Batch OTA upgrade | 8 | 30 minutes |
| Control | Batch control commands | 64 | 30 seconds |
| Query | Batch status query | 128 | 10 seconds |
5. Device Access Gateway
Adds the eneros-iot crate (IoT core), providing a unified device access gateway, supporting multi-protocol coexistence and protocol conversion.
Multi-protocol Access
use eneros_iot::{DeviceGateway, ProtocolAdapter};
let gateway = DeviceGateway::new(&ctx);
// Register protocol adapters
gateway.register_adapter(ProtocolAdapter::mqtt(mqtt_config))?;
gateway.register_adapter(ProtocolAdapter::opcua(opcua_config))?;
gateway.register_adapter(ProtocolAdapter::modbus(modbus_config))?;
gateway.register_adapter(ProtocolAdapter::iec104(iec104_config))?;
// Unified device model
gateway.on_device_data(|data| {
// Regardless of which protocol it comes from, unified to EnerOS device model
let device_id = data.device_id;
let telemetry = data.to_telemetry();
timeseries.write(device_id, telemetry).await?;
shadow_store.update_reported(device_id, data.payload).await?;
}).await?;
gateway.start().await?;
Protocol Adapter Comparison
| Protocol | Applicable Device | Data Model | Security Mechanism |
|---|---|---|---|
| MQTT | Smart meters, charging piles | Topic + JSON | TLS + certificate |
| OPC UA | PLC, DCS | Information model | Security policy |
| Modbus | RTU, sensors | Register mapping | Network segment isolation |
| IEC 104 | Traditional RTU | Telemetry/signaling | Application layer authentication |
| CoAP | Low-power devices | RESTful | DTLS |
Device Discovery
// Auto-discover devices in LAN
let discovered = gateway.discover()
.protocol(Protocol::Mqtt)
.subnet("10.20.30.0/24")
.timeout(Duration::seconds(10))
.execute().await?;
for device in &discovered {
println!("Discovered device: {} ({:?}) @ {}",
device.id, device.protocol, device.address);
// Auto-register to device registry
gateway.auto_register(device).await?;
}
Improvements
- Timeseries Engine: Added IoT data-specific write path, small batch write throughput improved 3x
- Topology Engine: Supports automatic construction of low-voltage distribution network topology from IoT devices
- Agent Runtime: Added IoTAgent type, specifically for device interaction
- Security Gateway: IoT device access enforces device certificate authentication
- Observability: IoT device metrics integrated into central monitoring
Bug Fixes
- Fixed
eneros-iot-mqttbroker crash when large numbers of clients connect simultaneously (#3603) - Fixed
eneros-iot-opcuasubscriptions not auto-recovering after server restart (#3610) - Fixed
eneros-iot-shadowversion number conflicts during concurrent updates (#3616) - Fixed
eneros-iot-batchnot rolling back when batch operations fail midway (#3622) - Fixed
eneros-iotdata loss due to resource competition with concurrent multi-protocol (#3628)
Breaking Changes
DeviceShadow::update: Split intoupdate_reportedandupdate_desiredDeviceGateway::register_adapter: ReturnsResult<()>, conflicts must be handledBatchManager: All methods changed to async
Upgrade Guide
- Update the
enerosdependency inCargo.tomlto0.36.0 - Run
eneros iot initto initialize the IoT gateway - Configure MQTT/OPC UA protocol parameters in
eneros.toml - Run
eneros iot import devices.csvto batch import devices
Acknowledgments
Thanks to the 35 contributors who submitted 520+ commits, and to the IoT protocol community for compatibility testing.