fix(gateway): update DALI configuration parameters for improved performance and reliability

This commit add comat parameter for some old DALI-1 control gear.

Signed-off-by: Tony <tonylu@tony-cloud.com>
This commit is contained in:
Tony
2026-05-21 17:03:35 +08:00
parent 0827befb06
commit 20642e5ec3
8 changed files with 239 additions and 73 deletions
+4 -4
View File
@@ -2781,10 +2781,10 @@ CONFIG_DALI_TIMER_RESOLUTION_HZ=3636363
CONFIG_DALI_CUSTOM_HALF_BIT_TIME_X100_US=0
CONFIG_DALI_TX_STOP_CONDITION_US=0
CONFIG_DALI_RX_STOP_CONDITION_US=0
CONFIG_DALI_QUERY_RESPONSE_TIMEOUT_MS=25
CONFIG_DALI_DOUBLE_SEND_DELAY_MS=15
CONFIG_DALI_FORWARD_ACTIVITY_WAIT_MS=15
CONFIG_DALI_FORWARD_AFTER_BACKWARD_WAIT_MS=5
CONFIG_DALI_QUERY_RESPONSE_TIMEOUT_MS=30
CONFIG_DALI_DOUBLE_SEND_DELAY_MS=20
CONFIG_DALI_FORWARD_ACTIVITY_WAIT_MS=20
CONFIG_DALI_FORWARD_AFTER_BACKWARD_WAIT_MS=10
CONFIG_DALI_FORWARD_MAX_WAIT_MS=50
CONFIG_DALI_BACKWARD_IDLE_TIMEOUT_MS=10
CONFIG_DALI_BUS_POWER_CHECK_INTERVAL_MS=500
+3 -2
View File
@@ -623,7 +623,8 @@ CONFIG_GATEWAY_CHANNEL1_NATIVE_BAUDRATE=1200
CONFIG_GATEWAY_CACHE_SUPPORTED=y
CONFIG_GATEWAY_CACHE_START_ENABLED=y
# CONFIG_GATEWAY_CACHE_RECONCILIATION_ENABLED is not set
CONFIG_GATEWAY_CACHE_FLUSH_INTERVAL_MS=60000
CONFIG_GATEWAY_CACHE_FLUSH_INTERVAL_MS=10000
CONFIG_GATEWAY_CACHE_REFRESH_INTERVAL_MS=120000
# end of Gateway Cache
# CONFIG_GATEWAY_ENABLE_DALI_BUS is not set
@@ -674,7 +675,7 @@ CONFIG_GATEWAY_KNX_BRIDGE_SUPPORTED=y
CONFIG_GATEWAY_START_KNX_BRIDGE_ENABLED=y
CONFIG_GATEWAY_KNX_DATA_SECURE_SUPPORTED=y
# CONFIG_GATEWAY_KNX_IP_SECURE_SUPPORTED is not set
# CONFIG_GATEWAY_KNX_SECURITY_DEV_ENDPOINTS is not set
CONFIG_GATEWAY_KNX_SECURITY_DEV_ENDPOINTS=y
CONFIG_GATEWAY_KNX_SECURITY_PLAIN_NVS=y
CONFIG_GATEWAY_KNX_OEM_MANUFACTURER_ID=0x01e5
CONFIG_GATEWAY_KNX_OEM_HARDWARE_ID=0xa401
+11 -11
View File
@@ -17,11 +17,11 @@ config DALI_DEFAULT_BAUDRATE
config DALI_TIMER_RESOLUTION_HZ
int "Native DALI timer resolution Hz"
range 1000000 8000000
default 3000000
default 3636363
help
GPTimer resolution for native DALI Manchester timing. The default 3 MHz
allows a 1200 bps half-bit period of 416.67 us to be represented as
1250 timer ticks.
GPTimer resolution for native DALI Manchester timing. The default
3.636363 MHz timing matches the gateway's tuned 1200 bps behavior for
older control gear that needs more recovery margin between commands.
config DALI_CUSTOM_HALF_BIT_TIME_X100_US
int "Custom native DALI half-bit time x100 us"
@@ -51,7 +51,7 @@ config DALI_RX_STOP_CONDITION_US
config DALI_QUERY_RESPONSE_TIMEOUT_MS
int "DALI query response timeout ms"
range 10 100
default 25
default 30
help
Time to wait for a complete backward frame after a forward query has
finished transmitting. DALI backward frames start 5.5-10.5 ms after the
@@ -61,7 +61,7 @@ config DALI_QUERY_RESPONSE_TIMEOUT_MS
config DALI_DOUBLE_SEND_DELAY_MS
int "Double-send delay ms"
range 0 100
default 10
default 20
help
Delay between the two frames sent by dali_send_double(), measured after
the first frame has completed. Exposed for development tuning.
@@ -69,7 +69,7 @@ config DALI_DOUBLE_SEND_DELAY_MS
config DALI_FORWARD_ACTIVITY_WAIT_MS
int "Forward-frame wait after bus activity ms"
range 0 1000
default 25
default 20
help
Minimum delay before sending a 2- to 4-byte forward frame after normal
bus activity. This is the native queue anti-collision wait.
@@ -77,7 +77,7 @@ config DALI_FORWARD_ACTIVITY_WAIT_MS
config DALI_FORWARD_AFTER_BACKWARD_WAIT_MS
int "Forward-frame wait after backward frame ms"
range 0 1000
default 5
default 10
help
Minimum delay before sending a 2- to 4-byte forward frame after the last
valid 1-byte backward frame received by the native bus.
@@ -94,7 +94,7 @@ config DALI_FORWARD_MAX_WAIT_MS
config DALI_BACKWARD_IDLE_TIMEOUT_MS
int "Backward-frame idle wait timeout ms"
range 0 1000
default 9
default 10
help
Time a 1-byte backward frame waits for an idle bus before being sent
anyway. Backward frame sends are not echo-verified because collisions
@@ -103,7 +103,7 @@ config DALI_BACKWARD_IDLE_TIMEOUT_MS
config DALI_BUS_POWER_CHECK_INTERVAL_MS
int "Bus power-down check interval ms"
range 10 5000
default 100
default 500
help
Interval used to resample the RX pin while the native DALI bus is marked
power-down. This lets the HAL recover when the bus was already powered
@@ -112,7 +112,7 @@ config DALI_BUS_POWER_CHECK_INTERVAL_MS
config DALI_BUS_ABNORMAL_REPORT_INTERVAL_MS
int "Legacy bus abnormal report interval ms"
range 0 60000
default 1000
default 3000
help
Interval for publishing the legacy two-byte FF FD bus-abnormal raw frame
while the native DALI bus is power-down. Set to 0 to disable the report.
+3 -3
View File
@@ -53,13 +53,13 @@ Use `menuconfig` under `DALI Component` to configure:
The native bus monitor uses `CONFIG_DALI_BUS_POWER_CHECK_INTERVAL_MS` to resample RX while
power-down and `CONFIG_DALI_BUS_ABNORMAL_REPORT_INTERVAL_MS` to publish legacy `FF FD`
raw frames while down. The report interval defaults to 1000 ms; set it to 0 to disable the
raw frames while down. The report interval defaults to 3000 ms; set it to 0 to disable the
compatibility report.
Native timing defaults target standard 1200 bps DALI: a 416.67 us half-bit period is
generated by the default 3 MHz timer as 1250 ticks. `CONFIG_DALI_CUSTOM_HALF_BIT_TIME_X100_US`
generated by the default 3.636363 MHz timer. `CONFIG_DALI_CUSTOM_HALF_BIT_TIME_X100_US`
can override the half-bit period for development; keep it at 0 for baudrate-derived timing.
Query no-response timeout defaults to 25 ms, which covers the 5.5-10.5 ms backward-frame
Query no-response timeout defaults to 30 ms, which covers the 5.5-10.5 ms backward-frame
start window plus the approximately 9.95 ms backward frame duration.
Native TX queue arbitration uses frame length as the frame type signal. Two- to four-byte
+2 -2
View File
@@ -7,11 +7,11 @@
#include "freertos/semphr.h"
#ifndef CONFIG_DALI_QUERY_RESPONSE_TIMEOUT_MS
#define CONFIG_DALI_QUERY_RESPONSE_TIMEOUT_MS 25
#define CONFIG_DALI_QUERY_RESPONSE_TIMEOUT_MS 30
#endif
#ifndef CONFIG_DALI_DOUBLE_SEND_DELAY_MS
#define CONFIG_DALI_DOUBLE_SEND_DELAY_MS 10
#define CONFIG_DALI_DOUBLE_SEND_DELAY_MS 20
#endif
static SemaphoreHandle_t s_dali_core_lock;
+6 -6
View File
@@ -64,7 +64,7 @@
#endif
#ifndef CONFIG_DALI_TIMER_RESOLUTION_HZ
#define CONFIG_DALI_TIMER_RESOLUTION_HZ 3000000
#define CONFIG_DALI_TIMER_RESOLUTION_HZ 3636363
#endif
#ifndef CONFIG_DALI_CUSTOM_HALF_BIT_TIME_X100_US
@@ -80,19 +80,19 @@
#endif
#ifndef CONFIG_DALI_BUS_POWER_CHECK_INTERVAL_MS
#define CONFIG_DALI_BUS_POWER_CHECK_INTERVAL_MS 100
#define CONFIG_DALI_BUS_POWER_CHECK_INTERVAL_MS 500
#endif
#ifndef CONFIG_DALI_BUS_ABNORMAL_REPORT_INTERVAL_MS
#define CONFIG_DALI_BUS_ABNORMAL_REPORT_INTERVAL_MS 1000
#define CONFIG_DALI_BUS_ABNORMAL_REPORT_INTERVAL_MS 3000
#endif
#ifndef CONFIG_DALI_FORWARD_ACTIVITY_WAIT_MS
#define CONFIG_DALI_FORWARD_ACTIVITY_WAIT_MS 25
#define CONFIG_DALI_FORWARD_ACTIVITY_WAIT_MS 20
#endif
#ifndef CONFIG_DALI_FORWARD_AFTER_BACKWARD_WAIT_MS
#define CONFIG_DALI_FORWARD_AFTER_BACKWARD_WAIT_MS 5
#define CONFIG_DALI_FORWARD_AFTER_BACKWARD_WAIT_MS 10
#endif
#ifndef CONFIG_DALI_FORWARD_MAX_WAIT_MS
@@ -100,7 +100,7 @@
#endif
#ifndef CONFIG_DALI_BACKWARD_IDLE_TIMEOUT_MS
#define CONFIG_DALI_BACKWARD_IDLE_TIMEOUT_MS 9
#define CONFIG_DALI_BACKWARD_IDLE_TIMEOUT_MS 10
#endif
#define WITHIN_RANGE(x, min, max) ((x) > (min) && (x) < (max))
+123 -31
View File
@@ -247,6 +247,121 @@ void GatewayKnxBridge::runCommissioningScanTask() {
}
while (!is_cancelled()) {
auto log_and_record_ballast = [&](const GatewayKnxCommissioningBallast& ballast) {
record_ballast(ballast);
ESP_LOGI(kTag, "REG1-Dali scan found random=0x%02X%02X%02X short=%u",
ballast.high, ballast.middle, ballast.low,
static_cast<unsigned>(ballast.short_address));
};
auto withdraw_selected = [&]() {
return SendRaw(engine_, DALI_CMD_SPECIAL_WITHDRAW, DALI_CMD_OFF,
"knx-function-scan-withdraw");
};
auto program_selected_ballast =
[&](uint8_t short_address,
uint32_t fallback_random_address) -> std::optional<GatewayKnxCommissioningBallast> {
if (!ProgramShortAddressAndConfirm(engine_, short_address)) {
return std::nullopt;
}
const auto random_address =
QueryRandomAddressForShortAddress(engine_, short_address).value_or(fallback_random_address);
GatewayKnxCommissioningBallast ballast;
ballast.high = static_cast<uint8_t>((random_address >> 16) & 0xff);
ballast.middle = static_cast<uint8_t>((random_address >> 8) & 0xff);
ballast.low = static_cast<uint8_t>(random_address & 0xff);
ballast.short_address = short_address;
return ballast;
};
if (options.assign) {
const auto next_address = NextFreeShortAddress(used_addresses);
if (!next_address.has_value()) {
ESP_LOGW(kTag, "REG1-Dali scan has no free short address left");
break;
}
const auto compare_base = FindSelectedCommissioningCompareBase(engine_);
if (!compare_base.has_value()) {
break;
}
const auto first_ballast =
program_selected_ballast(next_address.value(), compare_base.value());
if (!first_ballast.has_value()) {
ESP_LOGW(kTag, "REG1-Dali scan failed to program short address %u",
static_cast<unsigned>(next_address.value()));
break;
}
used_addresses[next_address.value()] = true;
log_and_record_ballast(first_ballast.value());
if (is_cancelled()) {
clear_results = true;
break;
}
if (!withdraw_selected()) {
ESP_LOGW(kTag, "REG1-Dali scan failed while withdrawing matched device");
break;
}
auto compare_cursor = IncrementRandomAddress(compare_base.value());
bool compare_multi_failed = false;
while (!is_cancelled() && compare_cursor.has_value()) {
const auto contiguous_short = NextFreeShortAddress(used_addresses);
if (!contiguous_short.has_value()) {
break;
}
const auto next_search = IncrementRandomAddress(compare_cursor.value());
if (!next_search.has_value()) {
break;
}
const auto matched = CompareSelectedSearchAddress(
engine_, next_search.value(), "knx-function-scan-compare-multi");
if (!matched.has_value()) {
compare_multi_failed = true;
break;
}
if (!matched.value()) {
break;
}
compare_cursor = next_search.value();
const auto ballast =
program_selected_ballast(contiguous_short.value(), compare_cursor.value());
if (!ballast.has_value()) {
ESP_LOGW(kTag, "REG1-Dali scan failed to program short address %u",
static_cast<unsigned>(contiguous_short.value()));
compare_multi_failed = true;
break;
}
used_addresses[contiguous_short.value()] = true;
log_and_record_ballast(ballast.value());
if (is_cancelled()) {
clear_results = true;
break;
}
if (!withdraw_selected()) {
ESP_LOGW(kTag, "REG1-Dali scan failed while withdrawing matched device");
compare_multi_failed = true;
break;
}
}
if (compare_multi_failed) {
break;
}
if (is_cancelled()) {
clear_results = true;
break;
}
continue;
}
const auto random_address = FindLowestSelectedRandomAddress(engine_);
if (!random_address.has_value()) {
break;
@@ -258,38 +373,15 @@ void GatewayKnxBridge::runCommissioningScanTask() {
ballast.low = static_cast<uint8_t>(random_address.value() & 0xff);
ballast.short_address = 0xff;
if (options.assign) {
const auto next_address = NextFreeShortAddress(used_addresses);
if (!next_address.has_value()) {
ESP_LOGW(kTag, "REG1-Dali scan has no free short address left for 0x%06x",
static_cast<unsigned>(random_address.value()));
break;
}
if (!SendRaw(engine_, DALI_CMD_SPECIAL_PROGRAM_SHORT_ADDRESS,
DaliComm::toCmdAddr(next_address.value()),
"knx-function-scan-program-short") ||
!VerifyShortAddress(engine_, next_address.value())) {
ESP_LOGW(kTag, "REG1-Dali scan failed to program short address %u",
static_cast<unsigned>(next_address.value()));
break;
}
used_addresses[next_address.value()] = true;
ballast.short_address = next_address.value();
} else {
ballast.short_address = QuerySelectedShortAddress(engine_).value_or(0xff);
}
ballast.short_address = QuerySelectedShortAddress(engine_).value_or(0xff);
record_ballast(ballast);
ESP_LOGI(kTag, "REG1-Dali scan found random=0x%02X%02X%02X short=%u",
ballast.high, ballast.middle, ballast.low,
static_cast<unsigned>(ballast.short_address));
log_and_record_ballast(ballast);
if (is_cancelled()) {
clear_results = true;
break;
}
if (!SendRaw(engine_, DALI_CMD_SPECIAL_WITHDRAW, DALI_CMD_OFF,
"knx-function-scan-withdraw")) {
if (!withdraw_selected()) {
ESP_LOGW(kTag, "REG1-Dali scan failed while withdrawing matched device");
break;
}
@@ -662,12 +754,12 @@ bool GatewayKnxBridge::handleReg1AssignCommand(const uint8_t* data, size_t len,
const uint8_t short_address = data[1] == 99 ? 0xff : data[1];
const bool ok = SendRawExt(engine_, DALI_CMD_SPECIAL_INITIALIZE, 0x00,
"knx-function-assign-init") &&
SendRaw(engine_, DALI_CMD_SPECIAL_SEARCHADDRH, data[2],
"knx-function-assign-search-h") &&
SendRaw(engine_, DALI_CMD_SPECIAL_SEARCHADDRM, data[3],
"knx-function-assign-search-m") &&
SendRaw(engine_, DALI_CMD_SPECIAL_SEARCHADDRL, data[4],
"knx-function-assign-search-l") &&
"knx-function-assign-search-l") &&
SendRaw(engine_, DALI_CMD_SPECIAL_SEARCHADDRM, data[3],
"knx-function-assign-search-m") &&
SendRaw(engine_, DALI_CMD_SPECIAL_SEARCHADDRH, data[2],
"knx-function-assign-search-h") &&
SendRaw(engine_, DALI_CMD_SPECIAL_PROGRAM_SHORT_ADDRESS,
short_address == 0xff ? 0xff : DaliComm::toCmdAddr(short_address),
"knx-function-assign-program") &&
@@ -969,19 +969,16 @@ DaliBridgeResult IgnoredResult(uint16_t group_address, uint16_t group_object_num
}
bool SetSearchAddress(DaliBridgeEngine& engine, uint32_t search_address, const char* sequence) {
return SendRaw(engine, DALI_CMD_SPECIAL_SEARCHADDRH,
static_cast<uint8_t>((search_address >> 16) & 0xff), sequence) &&
return SendRaw(engine, DALI_CMD_SPECIAL_SEARCHADDRL,
static_cast<uint8_t>(search_address & 0xff), sequence) &&
SendRaw(engine, DALI_CMD_SPECIAL_SEARCHADDRM,
static_cast<uint8_t>((search_address >> 8) & 0xff), sequence) &&
SendRaw(engine, DALI_CMD_SPECIAL_SEARCHADDRL,
static_cast<uint8_t>(search_address & 0xff), sequence);
SendRaw(engine, DALI_CMD_SPECIAL_SEARCHADDRH,
static_cast<uint8_t>((search_address >> 16) & 0xff), sequence);
}
std::optional<bool> CompareSelectedSearchAddress(DaliBridgeEngine& engine, uint32_t search_address,
const char* sequence) {
if (!SetSearchAddress(engine, search_address, sequence)) {
return std::nullopt;
}
std::optional<bool> CompareCurrentSearchAddress(DaliBridgeEngine& engine,
const char* sequence) {
const auto result = ExecuteRaw(engine, BridgeOperation::query, DALI_CMD_SPECIAL_COMPARE,
DALI_CMD_OFF, sequence);
if (result.ok && result.data.has_value()) {
@@ -989,14 +986,14 @@ std::optional<bool> CompareSelectedSearchAddress(DaliBridgeEngine& engine, uint3
}
if (DaliQueryResultHasStatus(result, "noResponse") ||
DaliQueryResultHasStatus(result, "timeout")) {
ESP_LOGD(kTag, "DALI compare no match seq=%s search=0x%06x status=%s",
sequence == nullptr ? "" : sequence, static_cast<unsigned>(search_address),
ESP_LOGD(kTag, "DALI compare no match seq=%s status=%s",
sequence == nullptr ? "" : sequence,
DaliQueryResultHasStatus(result, "timeout") ? "timeout" : "noResponse");
return false;
}
if (!result.error.empty()) {
ESP_LOGW(kTag, "DALI compare failed seq=%s search=0x%06x error=%s",
sequence == nullptr ? "" : sequence, static_cast<unsigned>(search_address),
ESP_LOGW(kTag, "DALI compare failed seq=%s error=%s",
sequence == nullptr ? "" : sequence,
result.error.c_str());
}
if (!result.ok || !result.data.has_value()) {
@@ -1005,6 +1002,53 @@ std::optional<bool> CompareSelectedSearchAddress(DaliBridgeEngine& engine, uint3
return true;
}
std::optional<bool> CompareSelectedSearchAddress(DaliBridgeEngine& engine, uint32_t search_address,
const char* sequence) {
if (!SetSearchAddress(engine, search_address, sequence)) {
return std::nullopt;
}
return CompareCurrentSearchAddress(engine, sequence);
}
bool SetSearchAddressComponent(DaliBridgeEngine& engine, int typ, uint8_t value,
const char* sequence) {
switch (typ) {
case 1:
return SendRaw(engine, DALI_CMD_SPECIAL_SEARCHADDRH, value, sequence);
case 2:
return SendRaw(engine, DALI_CMD_SPECIAL_SEARCHADDRM, value, sequence);
case 3:
return SendRaw(engine, DALI_CMD_SPECIAL_SEARCHADDRL, value, sequence);
default:
return false;
}
}
std::optional<bool> CompareSelectedSearchComponent(DaliBridgeEngine& engine, int typ, int value,
const char* sequence) {
if (!SetSearchAddressComponent(engine, typ, static_cast<uint8_t>(value & 0xff), sequence)) {
return std::nullopt;
}
return CompareCurrentSearchAddress(engine, sequence);
}
std::optional<uint32_t> IncrementRandomAddress(uint32_t random_address) {
if (random_address >= 0x00ffffffu) {
return std::nullopt;
}
return random_address + 1u;
}
std::optional<uint32_t> FindLowestSelectedRandomAddress(DaliBridgeEngine& engine);
std::optional<uint32_t> FindSelectedCommissioningCompareBase(DaliBridgeEngine& engine) {
const auto lowest = FindLowestSelectedRandomAddress(engine);
if (!lowest.has_value()) {
return std::nullopt;
}
return lowest.value() == 0 ? 0u : lowest.value() - 1u;
}
std::optional<uint32_t> FindLowestSelectedRandomAddress(DaliBridgeEngine& engine) {
const auto any = CompareSelectedSearchAddress(engine, 0x00ffffffu,
"knx-function-scan-compare-any");
@@ -1042,13 +1086,42 @@ std::optional<uint8_t> QuerySelectedShortAddress(DaliBridgeEngine& engine) {
return static_cast<uint8_t>((raw.value() >> 1) & 0x3f);
}
bool VerifyShortAddress(DaliBridgeEngine& engine, uint8_t short_address) {
bool VerifySelectedShortAddress(DaliBridgeEngine& engine, uint8_t short_address) {
const auto raw = ExecuteRawQuery(engine, DALI_CMD_SPECIAL_VERIFY_SHORT_ADDRESS,
DaliComm::toCmdAddr(short_address),
"knx-function-scan-verify-short");
return raw.has_value() && raw.value() == 0xff;
}
bool ProgramShortAddressAndConfirm(DaliBridgeEngine& engine, uint8_t short_address) {
if (!SendRaw(engine, DALI_CMD_SPECIAL_PROGRAM_SHORT_ADDRESS,
DaliComm::toCmdAddr(short_address),
"knx-function-scan-program-short")) {
return false;
}
if (VerifySelectedShortAddress(engine, short_address)) {
return true;
}
const auto confirmed = QuerySelectedShortAddress(engine);
return confirmed.has_value() && confirmed.value() == short_address;
}
std::optional<uint32_t> QueryRandomAddressForShortAddress(DaliBridgeEngine& engine,
uint8_t short_address) {
const auto high = QueryShort(engine, short_address, DALI_CMD_QUERY_RANDOM_ADDRESS_H,
"knx-function-scan-query-random-h");
const auto middle = QueryShort(engine, short_address, DALI_CMD_QUERY_RANDOM_ADDRESS_M,
"knx-function-scan-query-random-m");
const auto low = QueryShort(engine, short_address, DALI_CMD_QUERY_RANDOM_ADDRESS_L,
"knx-function-scan-query-random-l");
if (!high.has_value() || !middle.has_value() || !low.has_value()) {
return std::nullopt;
}
return (static_cast<uint32_t>(high.value() & 0xff) << 16) |
(static_cast<uint32_t>(middle.value() & 0xff) << 8) |
static_cast<uint32_t>(low.value() & 0xff);
}
std::array<bool, 64> QueryUsedShortAddresses(DaliBridgeEngine& engine) {
std::array<bool, 64> used{};
for (int short_address = 0; short_address < static_cast<int>(used.size()); ++short_address) {