Fix and refactor DALI GUI multi controller sync

This commit is contained in:
Theo Arends 2025-12-06 17:58:41 +01:00
parent 8feff1148a
commit d716dec667
3 changed files with 140 additions and 134 deletions

View File

@ -799,7 +799,6 @@
#define USE_KNX // Enable KNX IP Protocol Support (+23k code, +3k3 mem)
#endif
#define USE_DALI // Add support for DALI gateway (+5k code)
#define DALI_LIGHT_COLOR_SUPPORT // Add support for DALI DT8 RGBWAF color control (+0k7 code)
#define USE_ESP32_TWAI // Add support for TWAI/CAN interface (+7k code)
#endif // FIRMWARE_TASMOTA32

View File

@ -941,7 +941,6 @@
//#define USE_WOOLIIS // Add support for Wooliis Hall Effect Coulometer or Battery capacity monitor (+1k6 code)
//#define USE_DALI // Add support for DALI gateway (+7k6 code)
// #define DALI_POWER_OFF_NO_FADE // Power off immediatly without fading (+0k1 code)
// #define DALI_LIGHT_COLOR_SUPPORT // Add support for DALI DT8 RGBWAF color control (+0k7 code)
// #define DALI_LIGHT_NO_READ_AFTER_WRITE // Use no DTR read-after-write for smooth color transitions saving 55ms / channel (-0k1 code)
// -- Power monitoring sensors --------------------

View File

@ -65,6 +65,8 @@
--------------------------------------------------------------------------------------------
Version yyyymmdd Action Description
--------------------------------------------------------------------------------------------
1.5.0.0 20251206 update - Fix WAF GUI sync
- Refactor GUI sync
1.4.1.0 20251130 update - Add options to `DaliGear` and DaliGroup` to toggle specific outputs
- Make max number of devices persistent to speed up scan response
1.4.0.0 20251126 update - Change to TasmotaDali library
@ -134,9 +136,7 @@
#define DALI_TIMEOUT 20 // DALI backward frame receive timeout (ms) - Protocol = >7Te and <22Te (22 * 417us)
#endif
//#define DALI_LIGHT_COLOR_SUPPORT // Support DALI DT8 RGBWAF
//#define DALI_LIGHT_NO_READ_AFTER_WRITE // Use no DTR read-after-write for smooth color transitions (saves 55ms / channel)
//#define DALI_POWER_OFF_NO_FADE // Power off immediatly without fading
//#define DALI_DEBUG
@ -162,6 +162,7 @@ typedef struct DliSettings_t {
struct DALI {
DliSettings_t Settings; // Persistent settings
TasmotaDali *dali;
uint32_t light_sync;
uint8_t address;
uint8_t command;
uint8_t last_dimmer;
@ -178,7 +179,6 @@ struct DALI {
bool allow_light;
bool last_power;
bool power[DALI_MAX_STORED];
bool light_sync;
} *Dali = nullptr;
/*********************************************************************************************\
@ -486,7 +486,6 @@ int DaliQueryExtendedVersionNumber(uint32_t adr, uint32_t device_type) {
return DaliSendWaitResponse(adr | DALI_SELECTOR_BIT, 255); // DALI_xxx_QUERY_EXTENDED_VERSION_NUMBER
}
#ifdef DALI_LIGHT_COLOR_SUPPORT
uint32_t DaliQueryRGBWAF(uint32_t adr) {
// https://www.dali-alliance.org/tech-notes/device-type-discovery.html
uint32_t rgbwaf_channels = 0;
@ -528,7 +527,6 @@ uint32_t DaliQueryRGBWAF(uint32_t adr) {
}
return rgbwaf_channels;
}
#endif // DALI_LIGHT_COLOR_SUPPORT
/*-------------------------------------------------------------------------------------------*/
@ -713,6 +711,55 @@ void ResponseDali(uint32_t index) {
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
#ifdef USE_LIGHT
bool DaliLoopSync(uint32_t channels) {
// Sync local light settings with DALI bus data
if (!Dali->allow_light ||
!Settings->sbflag1.dali_light || // DaliLight 1
(DaliTarget2Address(Dali->Settings.target) != Dali->address)) {
return false;
}
if (Settings->save_data) {
// Postpone save_data during fast color changes which results in exception 0 on ESP8266
TasmotaGlobal.save_data_counter = 4;
}
uint32_t rgb = 1; // RGB channel(s) present but not sure if powered ON
uint32_t waf = (channels > 3) ? 1 : 0;
for (uint32_t i = 0; i < channels; i++) {
if (Dali->color[i] > 0) {
if (i < 3) {
rgb = 2; // At least one RGB channel is powered ON
} else {
waf = 2; // At least one WAF (CCT) channel is powered ON
}
}
}
if ((2 == rgb) || (2 == waf)) {
uint8_t color[LST_MAX] = {}; // Init as 0
for (uint32_t i = 0; i < channels; i++) {
if (i < 3) {
color[i] = (2 == rgb) ? Dali->color[i] : Light.current_color[i];
} else {
color[i] = (2 == waf) ? Dali->color[i] : Light.current_color[i];
}
}
Dali->light_sync = millis(); // Block local loop
light_controller.changeChannels(color);
}
Dali->light_sync = millis(); // Block local loop
ExecuteCommandPower(LightDevice(), (2 == rgb) ? 9 : 8, SRC_SWITCH);
if (waf) {
Dali->light_sync = millis(); // Block local loop
ExecuteCommandPower(LightDevice() +1, (2 == waf) ? 9 : 8, SRC_SWITCH);
}
return true;
}
#endif // USE_LIGHT
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
void DaliLoop(void) {
while (Dali->dali->available()) {
uint32_t queue = Dali->dali->available();
@ -732,8 +779,7 @@ void DaliLoop(void) {
Dali->address = (frame.data >> 8) &0xFF;
Dali->command = frame.data &0xFF;
#ifdef USE_LIGHT
#ifdef DALI_LIGHT_COLOR_SUPPORT
#ifdef USE_LIGHT
if (DALI_102_SET_DTR0 == Dali->address) { Dali->dtr[0] = Dali->command; } // Might be Red / White
else if (DALI_102_SET_DTR1 == Dali->address) { Dali->dtr[1] = Dali->command; } // Might be Green / Amber
else if (DALI_102_SET_DTR2 == Dali->address) { Dali->dtr[2] = Dali->command; } // Might be Blue
@ -742,7 +788,7 @@ void DaliLoop(void) {
Dali->color[1] = Dali->dtr[1]; // Green
Dali->color[2] = Dali->dtr[2]; // Blue
}
else if (DALI_209_SET_TEMPORARY_RGB_DIMLEVEL == Dali->command) {
else if (DALI_209_SET_TEMPORARY_WAF_DIMLEVEL == Dali->command) {
Dali->color[3] = Dali->dtr[0]; // Cold White
Dali->color[4] = Dali->dtr[1]; // Warm White (Amber)
}
@ -750,56 +796,19 @@ void DaliLoop(void) {
uint32_t channels = Dali->Settings.light_type -8;
if ((Dali->target_rgbwaf > 0) && (channels > 0)) { // Color control
Dali->address &= 0xFE; // Reset DALI_SELECTOR_BIT set
if (Dali->allow_light && (DaliTarget2Address(Dali->Settings.target) == Dali->address)) {
if (Settings->sbflag1.dali_light) { // DaliLight 1
uint32_t any_color = 0;
char scolors[20];
scolors[0] = 0;
for (uint32_t i = 0; i < channels; i++) {
any_color += Dali->color[i];
snprintf_P(scolors, sizeof(scolors), PSTR("%s%02X"), scolors, Dali->color[i]);
}
Dali->light_sync = true; // Block local loop
if (any_color) {
if (Settings->save_data) {
// Postpone save_data during fast color changes which results in exception 0 on ESP8266
TasmotaGlobal.save_data_counter = 4;
}
char scmnd[20];
snprintf_P(scmnd, sizeof(scmnd), PSTR(D_CMND_COLOR " %s"), scolors);
ExecuteCommand(scmnd, SRC_SWITCH);
} else {
ExecuteCommandPower(LightDevice(), 0, SRC_SWITCH);
}
}
}
DaliLoopSync(channels); // Sync local light settings with DALI bus data
}
} else
#endif // DALI_LIGHT_COLOR_SUPPORT
#endif // USE_LIGHT
if (!(Dali->address & DALI_SELECTOR_BIT)) { // Address
#endif // USE_LIGHT
if (!(Dali->address & DALI_SELECTOR_BIT)) { // Address = DAPC command
uint32_t index = DaliSaveState(Dali->address, Dali->command); // Update dimmer and power
bool show_response = true;
#ifdef USE_LIGHT
if (Dali->allow_light && (DaliTarget2Address(Dali->Settings.target) == Dali->address)) {
if (Settings->sbflag1.dali_light) { // DaliLight 1
// Sync local light settings with DALI bus data
uint8_t dim_old = changeUIntScale(Dali->last_dimmer, 0, 254, 0, 100);
uint8_t dim_new = changeUIntScale(Dali->dimmer[index], 0, 254, 0, 100);
if (Dali->last_power != Dali->power[index]) {
Dali->light_sync = true; // Block local loop
ExecuteCommandPower(LightDevice(), Dali->power[index], SRC_SWITCH);
}
else if (dim_old != dim_new) {
char scmnd[20];
snprintf_P(scmnd, sizeof(scmnd), PSTR(D_CMND_DIMMER " %d"), dim_new);
Dali->light_sync = true; // Block local loop
ExecuteCommand(scmnd, SRC_SWITCH);
}
#ifdef USE_LIGHT
Dali->color[0] = Dali->command; // Contains "dimmer" value 0..254
if (DaliLoopSync(1)) { // Sync local light settings with DALI bus data
show_response = false;
}
}
#endif // USE_LIGHT
#endif // USE_LIGHT
if (show_response) {
ResponseDali(index);
MqttPublishPrefixTopicRulesProcess_P(RESULT_OR_TELE, PSTR(D_PRFX_DALI));
@ -828,16 +837,22 @@ bool DaliSetChannels(void) {
if (Settings->sbflag1.dali_light) { // DaliLight 1
Settings->light_fade = 0; // Use Dali fading
Settings->light_correction = 0; // Use Dali light correction
if (Dali->light_sync) { // Block local loop
Dali->light_sync = false;
} else {
uint8_t *cur_col = (uint8_t*)XdrvMailbox.data;
AddLog(LOG_LEVEL_DEBUG, PSTR("DBG: SetChannels Sync %d cur_col %02X %02X %02X %02X %02X"), Dali->light_sync, cur_col[0], cur_col[1], cur_col[2], cur_col[3], cur_col[4]);
if (Dali->light_sync) { // Block local loop
uint32_t light_sync = Dali->light_sync;
Dali->light_sync = 0;
if (TimePassedSince(light_sync) < 200) { // Time it can take to call DaliSetChannels() from Dali received data
return true;
}
}
// cur_col[0] = Red, cur_col[1] = Green, cur_col[2] = Blue, cur_col[3] = Cold = White, cur_col[4] = Warm = Amber
for (uint32_t i = 0; i < 5; i++) {
if (255 == cur_col[i]) { cur_col[i] = 254; } // Max Dali value
}
uint32_t adr = DaliTarget2Address(Dali->Settings.target);
#ifdef DALI_LIGHT_COLOR_SUPPORT
uint32_t channels = Dali->Settings.light_type -8;
if ((Dali->target_rgbwaf > 0) && (channels > 0)) { // Color control
adr |= DALI_SELECTOR_BIT; // Enable Selector bit
@ -891,13 +906,11 @@ bool DaliSetChannels(void) {
DaliSendData(DALI_102_ENABLE_DEVICE_TYPE_X, DALI_209_DEVICE_TYPE); // Enable Extended command
DaliSendData(adr, DALI_209_SET_TEMPORARY_WAF_DIMLEVEL);
}
#endif // DALI_LIGHT_NO_READ_AFTER_WRITE
DaliSendData(DALI_102_ENABLE_DEVICE_TYPE_X, DALI_209_DEVICE_TYPE); // Enable Extended command
DaliSendData(adr, DALI_209_ACTIVATE);
return true;
}
#endif // DALI_LIGHT_COLOR_SUPPORT
#ifdef DALI_POWER_OFF_NO_FADE
if (!cur_col[0]) {
@ -907,7 +920,6 @@ bool DaliSetChannels(void) {
#endif // DALI_POWER_OFF_NO_FADE
DaliSendData(adr, cur_col[0]); // DAPC command - dim level
}
}
return true;
}
#endif // USE_LIGHT
@ -970,7 +982,6 @@ bool DaliInit(uint32_t function) {
UpdateDevicesPresent(1);
TasmotaGlobal.light_type = LT_W; // Single channel
#ifdef DALI_LIGHT_COLOR_SUPPORT
Dali->target_rgbwaf = DaliQueryRGBWAF(DaliTarget2Address(Dali->Settings.target));
if (Dali->target_rgbwaf > 1) {
TasmotaGlobal.light_type = Dali->Settings.light_type;
@ -980,7 +991,6 @@ bool DaliInit(uint32_t function) {
UpdateDevicesPresent(1); // We manage RGB and W separately, hence adding a device
}
}
#endif // DALI_LIGHT_COLOR_SUPPORT
return true;
#else
@ -1090,9 +1100,7 @@ void CmndDaliTarget(void) {
(XdrvMailbox.payload == 0)) {
Dali->Settings.target = XdrvMailbox.payload;
}
#ifdef DALI_LIGHT_COLOR_SUPPORT
Dali->target_rgbwaf = DaliQueryRGBWAF(DaliTarget2Address(Dali->Settings.target));
#endif // DALI_LIGHT_COLOR_SUPPORT
ResponseCmndNumber(Dali->Settings.target);
}