Files
dali_cpp/README.md
T

7.1 KiB

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

  1. Add the component to your ESP-IDF project (e.g., via EXTRA_COMPONENT_DIRS).
  2. 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);
  1. 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*2 for direct arc, dec*2+1 for command/query addresses.
  • Colour conversion utilities (rgb2xy, xy2rgb, XYZ/LAB helpers) are ported from lib/dali/color.dart.
  • Public APIs from base.dart, dt8.dart, dt1.dart, addr.dart, and decode.dart are exposed with matching method names.
  • App-side model parity modules are included for device.dart, sequence.dart, and sequence_store.dart via device.hpp, sequence.hpp, and sequence_store.hpp.
  • Utility APIs from errors.dart, log.dart, query_scheduler.dart, and bus_monitor.dart are 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.hpp defines the strongly typed mapping model: protocol kind, external point, DALI target, default operation, and value transform.
  • bridge.hpp provides DaliBridgeEngine, which resolves models and dispatches requests into DaliComm, DaliBase, and DaliDT8.
  • bridge_provisioning.hpp provides BridgeProvisioningStore for persisting bridge models in ESP-IDF NVS.
  • Modbus and BACnet runtime support are owned by the native gateway project in gateway/components/gateway_modbus and gateway/components/gateway_bacnet.

Example Model Mapping

BridgeModel brightness;
brightness.id = "line-1-brightness";
brightness.name = "Line 1 brightness";
brightness.dali.shortAddress = 1;
brightness.operation = BridgeOperation::setBrightness;
brightness.valueTransform.clampMin = 0;
brightness.valueTransform.clampMax = 254;

DaliBridgeEngine engine(comm);
engine.upsertModel(brightness);

DaliBridgeRequest request;
request.modelID = "line-1-brightness";
request.value = 180;
engine.execute(request);

Supported Bridge Operations

  • send
  • send_ext
  • query
  • set_brightness
  • set_brightness_percent
  • on
  • off
  • recall_max_level
  • recall_min_level
  • set_color_temperature
  • get_brightness
  • get_status
  • get_color_temperature
  • get_color_status
  • get_emergency_level
  • get_emergency_status
  • get_emergency_failure_status
  • start_emergency_function_test
  • stop_emergency_test

Query-style operations return data when available and may include decoded flags in meta.

Notes

  • Query support works with either a read callback or a transact callback. When read is available, queries return as soon as the reply bytes arrive instead of waiting behind a fixed pre-read delay.
  • Dali facade in include/dali.hpp mirrors lib/dali/dali.dart and wires base, decode, dt1, dt8, and addr together.
  • The t, d, and g parameters in Dart are not required here; timing/gateway selection is driven by your callbacks.
  • DaliCloudBridge now 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:

BridgeRuntimeConfig runtime;
runtime.models.push_back(brightness);

BridgeProvisioningStore store;
store.save(runtime);

BridgeRuntimeConfig loaded;
if (store.load(&loaded) == ESP_OK) {
  // register loaded.models with DaliBridgeEngine
}

The gateway project stores Modbus TCP settings in the same persisted JSON shape, but parses and applies that section in gateway/components/gateway_modbus rather than in this standalone DALI component.

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
{
  "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.

{
  "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, registers generic bridge models, and routes requests through the shared bridge engine. The DALI gateway callbacks are still placeholders where you should connect your UART or transport driver. Use gateway/apps/gateway to exercise the gateway-owned Modbus and BACnet runtimes.