feat: Add DALI raw frame handling and USB setup bridge

- Introduced DaliRawFrame structure to encapsulate raw frame data.
- Enhanced DaliDomainService to manage raw frame sinks and processing.
- Implemented raw frame task for asynchronous handling of incoming DALI frames.
- Integrated raw frame handling in GatewayBleBridge and GatewayNetworkService.
- Added GatewayUsbSetupBridge to facilitate USB Serial/JTAG communication with DALI.
- Configured ESP-NOW for wireless communication and setup management.
- Updated GatewayRuntime to support clearing wireless credentials on boot button long press.
- Enhanced CMakeLists to include new components and dependencies.

Co-authored-by: Copilot <copilot@github.com>
This commit is contained in:
Tony
2026-04-30 04:15:05 +08:00
parent 3d8d00c3dd
commit 4ce3513dd2
20 changed files with 984 additions and 25 deletions
+94 -1
View File
@@ -112,7 +112,8 @@ struct DaliDomainService::DaliChannel {
std::optional<DaliSerialBusConfig> serial_bus;
};
DaliDomainService::DaliDomainService() = default;
DaliDomainService::DaliDomainService()
: raw_frame_sink_lock_(xSemaphoreCreateMutex()) {}
DaliDomainService::~DaliDomainService() = default;
@@ -172,6 +173,10 @@ esp_err_t DaliDomainService::bindHardwareBus(const DaliHardwareBusConfig& config
channel->phy_kind = DaliPhyKind::kNativeHardware;
channel->hardware_bus = config;
}
err = startRawFrameTask();
if (err != ESP_OK) {
ESP_LOGW(kTag, "failed to start raw frame task: %s", esp_err_to_name(err));
}
ESP_LOGI(kTag, "bound channel=%u gateway=%u hardware bus=%u tx=%u rx=%u baudrate=%lu",
config.channel_index, config.gateway_id, config.bus_id, config.tx_pin, config.rx_pin,
static_cast<unsigned long>(config.baudrate));
@@ -290,6 +295,19 @@ std::vector<DaliChannelInfo> DaliDomainService::channelInfo() const {
return info;
}
void DaliDomainService::addRawFrameSink(std::function<void(const DaliRawFrame& frame)> sink) {
if (!sink) {
return;
}
if (raw_frame_sink_lock_ != nullptr) {
xSemaphoreTake(raw_frame_sink_lock_, portMAX_DELAY);
}
raw_frame_sinks_.push_back(std::move(sink));
if (raw_frame_sink_lock_ != nullptr) {
xSemaphoreGive(raw_frame_sink_lock_);
}
}
bool DaliDomainService::resetBus(uint8_t gateway_id) const {
const auto* channel = findChannelByGateway(gateway_id);
return channel != nullptr && channel->comm != nullptr && channel->comm->resetBus();
@@ -446,6 +464,81 @@ DaliDomainService::DaliChannel* DaliDomainService::findChannelByIndex(uint8_t ch
return it == channels_.end() ? nullptr : it->get();
}
const DaliDomainService::DaliChannel* DaliDomainService::findChannelByHardwareBus(
uint8_t bus_id) const {
const auto it = std::find_if(channels_.begin(), channels_.end(), [bus_id](const auto& channel) {
return channel->hardware_bus.has_value() && channel->hardware_bus->bus_id == bus_id;
});
return it == channels_.end() ? nullptr : it->get();
}
esp_err_t DaliDomainService::startRawFrameTask() {
if (raw_frame_task_handle_ != nullptr) {
return ESP_OK;
}
QueueHandle_t queue = dali_hal_raw_receive_queue();
if (queue == nullptr) {
return ESP_ERR_INVALID_STATE;
}
const BaseType_t created = xTaskCreate(&DaliDomainService::RawFrameTaskEntry,
"dali_raw_rx", 4096, this, 4,
&raw_frame_task_handle_);
if (created != pdPASS) {
raw_frame_task_handle_ = nullptr;
return ESP_ERR_NO_MEM;
}
return ESP_OK;
}
void DaliDomainService::RawFrameTaskEntry(void* arg) {
static_cast<DaliDomainService*>(arg)->rawFrameTaskLoop();
}
void DaliDomainService::rawFrameTaskLoop() {
QueueHandle_t queue = dali_hal_raw_receive_queue();
Dali_msg_t message = {};
while (true) {
if (queue == nullptr) {
vTaskDelay(pdMS_TO_TICKS(100));
queue = dali_hal_raw_receive_queue();
continue;
}
if (xQueueReceive(queue, &message, portMAX_DELAY) != pdTRUE) {
continue;
}
if (message.status != DALI_FRAME_OK) {
continue;
}
const auto* channel = findChannelByHardwareBus(message.id);
if (channel == nullptr) {
continue;
}
size_t byte_count = (static_cast<size_t>(message.length) + 7U) / 8U;
if (byte_count > DALI_MAX_BYTES) {
byte_count = DALI_MAX_BYTES;
}
DaliRawFrame frame;
frame.channel_index = channel->config.channel_index;
frame.gateway_id = channel->config.gateway_id;
frame.phy_kind = channel->phy_kind;
frame.data.assign(message.data, message.data + byte_count);
notifyRawFrameSinks(frame);
}
}
void DaliDomainService::notifyRawFrameSinks(const DaliRawFrame& frame) {
if (raw_frame_sink_lock_ != nullptr) {
xSemaphoreTake(raw_frame_sink_lock_, portMAX_DELAY);
}
auto sinks = raw_frame_sinks_;
if (raw_frame_sink_lock_ != nullptr) {
xSemaphoreGive(raw_frame_sink_lock_);
}
for (const auto& sink : sinks) {
sink(frame);
}
}
bool DaliDomainService::hasSerialPort(int uart_port) const {
return std::any_of(channels_.begin(), channels_.end(), [uart_port](const auto& channel) {
return channel->serial_bus.has_value() && channel->serial_bus->uart_port == uart_port;