- Added `bridge.cpp` to handle DALI bridge operations including model management, command execution, and response formatting. - Introduced `bridge_model.cpp` for defining bridge models, value transformations, and JSON serialization/deserialization. - Created `bridge_provisioning.cpp` for managing bridge configuration storage and retrieval using NVS on ESP platform. - Enhanced `gateway_cloud.cpp` to integrate DALI bridge requests and responses with cloud communication. - Introduced `modbus_bridge.cpp` to handle Modbus-specific operations and register management. - Implemented utility functions for converting between DaliValue and cJSON formats. - Added error handling and metadata management in bridge responses.
DALI ESP-IDF Component
A lightweight C++ implementation of the DALI stack used in lib/dali/*.dart for gateway type 1 (USB UART) traffic. The component mirrors Dart method names so it can be used as an embedded replacement target.
Quick Start
- Add the component to your ESP-IDF project (e.g., via
EXTRA_COMPONENT_DIRS). - Provide UART callbacks when constructing
DaliComm:
DaliComm comm(
/* send */ [](const uint8_t* data, size_t len) {
// write bytes to the gateway UART
return my_uart_write(data, len) == ESP_OK;
},
/* read (optional) */ [](size_t len, uint32_t timeoutMs) -> std::vector<uint8_t> {
return my_uart_read(len, timeoutMs);
},
/* transact */ [](const uint8_t* data, size_t len) -> std::vector<uint8_t> {
my_uart_write(data, len);
return my_uart_read_response(); // should return the raw gateway reply
});
Dali dali(comm);
- Use the API just like the Dart version:
dali.base.setBright(5, 200); // direct arc power control
dali.base.off(5);
dali.base.dtSelect(8);
dali.dt8.setColorTemperature(5, 4000); // Kelvin
std::vector<int> rgb = dali.dt8.getColourRGB(5);
Behaviour Parity
- Frame formats match the Dart implementation:
[0x10, addr, cmd](send),[0x11, addr, cmd](extended),[0x12, addr, cmd](query with[0xFF, data]response). - Address encoding matches Dart helpers:
dec*2for direct arc,dec*2+1for command/query addresses. - Colour conversion utilities (
rgb2xy,xy2rgb, XYZ/LAB helpers) are ported fromlib/dali/color.dart. - Public APIs from
base.dart,dt8.dart,dt1.dart,addr.dart, anddecode.dartare exposed with matching method names. - App-side model parity modules are included for
device.dart,sequence.dart, andsequence_store.dartviadevice.hpp,sequence.hpp, andsequence_store.hpp. - Utility APIs from
errors.dart,log.dart,query_scheduler.dart, andbus_monitor.dartare available as embedded-friendly C++ headers.
Generic Bridge Layer
The component now includes a protocol-agnostic bridge layer for mapping external fieldbus models into DALI operations.
Bridge Building Blocks
bridge_model.hppdefines the strongly typed mapping model: protocol kind, external point, DALI target, default operation, and value transform.bridge.hppprovidesDaliBridgeEngine, which resolves models and dispatches requests intoDaliComm,DaliBase, andDaliDT8.bridge_provisioning.hppprovidesBridgeProvisioningStorefor persisting bridge models and protocol config in ESP-IDF NVS.modbus_bridge.hppprovides a Modbus skeleton adapter keyed by holding register bindings.bacnet_bridge.hppprovides a BACnet skeleton adapter keyed by object type, instance, and property bindings.
Example Model Mapping
BridgeModel modbusBrightness;
modbusBrightness.id = "modbus-light-1";
modbusBrightness.name = "Line 1 brightness";
modbusBrightness.protocol = BridgeProtocolKind::modbus;
modbusBrightness.external.objectType = BridgeObjectType::holdingRegister;
modbusBrightness.external.registerAddress = 40001;
modbusBrightness.dali.shortAddress = 1;
modbusBrightness.operation = BridgeOperation::setBrightness;
modbusBrightness.valueTransform.clampMin = 0;
modbusBrightness.valueTransform.clampMax = 254;
DaliBridgeEngine engine(comm);
engine.upsertModel(modbusBrightness);
DaliModbusBridge modbus(engine);
modbus.handleHoldingRegisterWrite(40001, 180);
Supported Bridge Operations
sendsend_extqueryset_brightnessset_brightness_percentonoffrecall_max_levelrecall_min_levelset_color_temperatureget_brightnessget_statusget_color_temperatureget_color_statusget_emergency_levelget_emergency_statusget_emergency_failure_statusstart_emergency_function_teststop_emergency_test
Query-style operations return data when available and may include decoded flags in meta.
Notes
- Optional query support: provide a
transactcallback that returns the gateway reply; otherwise, query methods returnstd::nullopt. Dalifacade ininclude/dali.hppmirrorslib/dali/dali.dartand wiresbase,decode,dt1,dt8, andaddrtogether.- The
t,d, andgparameters in Dart are not required here; timing/gateway selection is driven by your callbacks. DaliCloudBridgenow uses the shared bridge engine internally, so MQTT downlinks can target either raw DALI frames or registered bridge models.
Bridge Provisioning via NVS
Use BridgeProvisioningStore to persist bridge models and protocol-specific config for Modbus and BACnet:
BridgeRuntimeConfig runtime;
runtime.models.push_back(modbusBrightness);
ModbusBridgeConfig modbus;
modbus.transport = "tcp-server";
modbus.port = 1502;
runtime.modbus = modbus;
BridgeProvisioningStore store;
store.save(runtime);
BridgeRuntimeConfig loaded;
if (store.load(&loaded) == ESP_OK) {
// register loaded.models with DaliBridgeEngine
}
Cloud Bridge (ESP32 Gateway)
The component now includes DaliCloudBridge in include/gateway_cloud.hpp to connect ESP32 gateways to the backend MQTT broker.
Topics
- Downlink:
devices/<deviceID>/down - Uplink:
devices/<deviceID>/up - Status:
devices/<deviceID>/status - Register:
devices/<deviceID>/register
Downlink JSON Envelope
{
"type": "dali_cmd",
"seq": "123",
"model": "modbus-light-1",
"op": "send|send_ext|query",
"addr": 5,
"cmd": 160,
"shortAddress": 1,
"value": 180
}
model, shortAddress, and value are optional. If model is provided, the bridge resolves its mapped DALI target and default operation.
Successful query responses may also include a meta object with decoded status flags.
Uplink JSON Envelope
{
"type": "dali_resp",
"seq": "123",
"op": "query",
"ok": true,
"data": 255
}
Usage
GatewayCloudConfig cfg;
cfg.brokerURI = "mqtt://192.168.1.100:1883";
cfg.deviceID = "A1B2C3D4E5F6";
cfg.username = "device";
cfg.password = "A1B2C3D4E5F6";
DaliCloudBridge bridge(comm);
if (bridge.start(cfg)) {
bridge.publishStatus("online");
}
Provisioning via NVS
Use GatewayProvisioningStore to persist cloud connection settings:
GatewayProvisioningStore store;
GatewayCloudConfig cfg;
cfg.brokerURI = "mqtt://192.168.1.100:1883";
cfg.deviceID = "A1B2C3D4E5F6";
cfg.username = "device";
cfg.password = "A1B2C3D4E5F6";
store.save(cfg);
GatewayCloudConfig loaded;
if (store.load(&loaded) == ESP_OK) {
DaliCloudBridge bridge(comm);
bridge.start(loaded);
}
ESP32-S3 Example Project
A standalone ESP-IDF example app is available at examples/esp32s3_bridge/.
Environment Setup
Source the helper script from your shell so the exported ESP-IDF variables stay in your session:
. ./scripts/export_esp_idf.sh
The helper script sources ~/esp/v5.5.2/esp-idf/export.sh.
Build the Example
cd examples/esp32s3_bridge
idf.py set-target esp32s3
idf.py build
The example persists its bridge config in NVS, starts a real Modbus TCP listener, registers Modbus and BACnet bridge models, and routes Modbus writes through the shared bridge engine. The DALI gateway callbacks are still placeholders where you should connect your UART or transport driver.