Implement DALI Bridge Engine and Model Management

- 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.
This commit is contained in:
Tony
2026-04-22 10:14:42 +08:00
parent ec5f36b581
commit 21afc6b942
22 changed files with 2045 additions and 35 deletions
+112 -1
View File
@@ -42,11 +42,91 @@ std::vector<int> rgb = dali.dt8.getColourRGB(5);
- 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 and protocol config in ESP-IDF NVS.
- `modbus_bridge.hpp` provides a Modbus skeleton adapter keyed by holding register bindings.
- `bacnet_bridge.hpp` provides a BACnet skeleton adapter keyed by object type, instance, and property bindings.
### Example Model Mapping
```cpp
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
- `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
- Optional query support: provide a `transact` callback that returns the gateway reply; otherwise, query methods return `std::nullopt`.
- `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 and protocol-specific config for Modbus and BACnet:
```cpp
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)
@@ -65,12 +145,19 @@ The component now includes `DaliCloudBridge` in `include/gateway_cloud.hpp` to c
{
"type": "dali_cmd",
"seq": "123",
"model": "modbus-light-1",
"op": "send|send_ext|query",
"addr": 5,
"cmd": 160
"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
```json
@@ -118,3 +205,27 @@ if (store.load(&loaded) == ESP_OK) {
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:
```bash
. ./scripts/export_esp_idf.sh
```
The helper script sources `~/esp/v5.5.2/esp-idf/export.sh`.
### Build the Example
```bash
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.