From 4de92d2e7a67a88c5b4f47192b02cc292254dda0 Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Wed, 19 Nov 2025 10:44:21 +0100 Subject: [PATCH] Fix DALI protocol errors --- CHANGELOG.md | 4 +- RELEASENOTES.md | 2 + tasmota/include/xdrv_75_dali.h | 11 +- tasmota/tasmota_xdrv_driver/xdrv_75_dali.ino | 265 ++++++++++++++----- 4 files changed, 213 insertions(+), 69 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1f47837b1..fd2087b9c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,7 +5,8 @@ All notable changes to this project will be documented in this file. ## [15.1.0.2] ### Added -- WS2812 and Berry animation support for reverse-order LED strip +- WS2812 and Berry animation support for reverse-order LED strip (#24138) +- DALI persistence for `DaliTarget` if filesystem is present ### Breaking Changed @@ -14,6 +15,7 @@ All notable changes to this project will be documented in this file. - JPEGDEC library from v1.8.3 to v1.8.4 (#24120) ### Fixed +- DALI protocol errors ### Removed diff --git a/RELEASENOTES.md b/RELEASENOTES.md index bc4667384..a5d2d336b 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -120,6 +120,7 @@ The latter links can be used for OTA upgrades too like ``OtaUrl https://ota.tasm - Scripter array transfer via UFS [#24060](https://github.com/arendst/Tasmota/issues/24060) - NeoPool command `NPReadLSB`, `NPReadMSB`, `NPWriteLSB`, `NWriteMSB` for directly read/write LSB/MSB of 16-bit register [#24083](https://github.com/arendst/Tasmota/issues/24083) - TLS enabled ECDSA by default for ESP8266 [#24009](https://github.com/arendst/Tasmota/issues/24009) +- WS2812 and Berry animation support for reverse-order LED strip [#24138](https://github.com/arendst/Tasmota/issues/24138) - Berry `cb.free_cb` for extension manager [#24014](https://github.com/arendst/Tasmota/issues/24014) - Berry `light.get()` direct access to values [#24033](https://github.com/arendst/Tasmota/issues/24033) - Berry `gc_heap` and `gc_time` to `tasmota.memory()` [#24054](https://github.com/arendst/Tasmota/issues/24054) @@ -139,6 +140,7 @@ The latter links can be used for OTA upgrades too like ``OtaUrl https://ota.tasm - InfluxDb receives IPAddress as a value regression from v15.0.1.3 [#24031](https://github.com/arendst/Tasmota/issues/24031) - Scripter UDP and switch case [#24060](https://github.com/arendst/Tasmota/issues/24060) - TuyaMCU v1 soft lock when WIFI_SELECT / WIFI_RESET is initiated [#24063](https://github.com/arendst/Tasmota/issues/24063) +- DALI protocol errors - TLS fix ECDSA and add `SetOption165 1` to enable ECDSA in addition to RSA [#24000](https://github.com/arendst/Tasmota/issues/24000) - Extension Manager exception when `OtaUrl` is not defined or invalid - Extension Manager Light Theme support and Extensions input field control diff --git a/tasmota/include/xdrv_75_dali.h b/tasmota/include/xdrv_75_dali.h index b98cde304..a8e91e2be 100644 --- a/tasmota/include/xdrv_75_dali.h +++ b/tasmota/include/xdrv_75_dali.h @@ -26,6 +26,9 @@ #define DALI_GROUP_ADDRESS15 0x9E // 0b10011110 15 - Last group address #define DALI_BROADCAST_DP 0xFE // 0b11111110 254 - Broadcast address +// Address selector bit - Send with first byte +#define DALI_SELECTOR_BIT 0x01 // Mark second byte as standard/extended command + /*-------------------------------------------------------------------------------------------*\ * DALI Commands for IEC62386 part 102 = Control gears - Send as first byte \*-------------------------------------------------------------------------------------------*/ @@ -121,7 +124,7 @@ // If the lamp is off it shall be ignited with this command. #define DALI_102_STEP_DOWN_AND_OFF 0x07 // 7 - Decrements the lighting control level and turns off lighting if the level is at the minimum (without fade). #define DALI_102_ON_AND_STEP_UP 0x08 // 8 - Increments the lighting control level and turns on lighting if lighting is off (with fade). -#define DALI_102_DIRECT_ARC_POWER_CONTROL 0x09 // 9 - Enable DAPC Sequence +#define DALI_102_DIRECT_ARC_POWER_CONTROL 0x09 // 9 Deprecated - Enable DAPC Sequence // Indicates the start of a command iteration of DAPC(level) commands. // The control gear shall temporarily use a fade time of 200ms while the command iteration is active independent of the actual fade/extended fade time. // The DAPC sequence shall end if 200ms elapse without the control gear receiving a DAPC(level) command. @@ -711,7 +714,7 @@ // No change shall occur if ‘COLOUR TEMPERATURE TC’ is already at ‘COLOUR TEMPERATURE TC WARMEST’. // If the new colour value does not correspond to a colour temperature attainable by the control gear, this shall be indicated by // the ‘Colour temperature TC out of range’ bit, bit 1 of the ‘COLOUR STATUS’. -#define DALI_209_SET_TEMPORARY_PRIMARY_N_DIMLEVEL 0xEA // 234 - Set temporary primary N dimlevel (Uses DTR0 (LSB), DTR1 (MSB) and DTR2 (N)) +#define DALI_209_SET_TEMPORARY_PRIMARY_N_DIMLEVEL 0xEA // 234 Deprecated - Set temporary primary N dimlevel (Uses DTR0 (LSB), DTR1 (MSB) and DTR2 (N)) // The value is expressed in units of 1/65536. // The maximum ‘PRIMARY N DIMLEVEL’ value is 0,99997 and shall be interpreted on a linear scale. // N depends on DTR2 and shall be in the range from 0 to 5 depending upon the available number of primaries. @@ -745,13 +748,13 @@ // Application extended configuration commands - Send as second byte with repeat #define DALI_209_RESERVED239 0xEF // 239 - [Reserved] -#define DALI_209_STORE_TY_PRIMARY_N 0xF0 // 240 - Store TY Primary N via DTR0/1/2 +#define DALI_209_STORE_TY_PRIMARY_N 0xF0 // 240 Deprecated - Store TY Primary N via DTR0/1/2 // The value is expressed in units of 0,5 lumen resulting in a possible range of TYmin = 0 lumen, to TYmax = 32767 lumen. // A value of 65535 (“MASK”) means unknown. // N depends on DTR2 and shall be in the range from 0 to 5 depending upon the available number of primaries. // For any other value of DTR2 the command shall be ignored. // A value of “MASK” means that this primary is undefined and calibration is needed. -#define DALI_209_STORE_XY_COORDINATE_PRIMARY_N 0xF1 // 241 - Store XY coord primary channel N via DTR2 +#define DALI_209_STORE_XY_COORDINATE_PRIMARY_N 0xF1 // 241 Deprecated - Store XY coord primary channel N via DTR2 // The ‘TEMPORARY x-COORDINATE’ and the ‘TEMPORARY y-COORDINATE’, given by command 224 and command 225 shall be stored as ‘x-COORDINATE PRIMARY N’ // respectively ‘y-COORDINATE PRIMARY N’ of primary N given by the value of DTR2, and shall be in the range from 0 to 5 depending upon the available number of primaries. // For any other value of DTR2 the command shall be ignored. diff --git a/tasmota/tasmota_xdrv_driver/xdrv_75_dali.ino b/tasmota/tasmota_xdrv_driver/xdrv_75_dali.ino index 3678e8c80..5f4b83459 100644 --- a/tasmota/tasmota_xdrv_driver/xdrv_75_dali.ino +++ b/tasmota/tasmota_xdrv_driver/xdrv_75_dali.ino @@ -58,6 +58,7 @@ -------------------------------------------------------------------------------------------- Version yyyymmdd Action Description -------------------------------------------------------------------------------------------- + 1.2.0.0 20251116 update - Add persistence for `DaliTarget` if filesystem is present 1.1.0.4 20251115 fix - Tasmota light control using non-broadcast address 1.1.0.3 20251112 remove - Remove optional repeat for commands `DaliSend` and `DaliQuery` Send twice is now based on DALI defined commands type @@ -128,23 +129,14 @@ /*********************************************************************************************/ -const char kDALICommands[] PROGMEM = D_PRFX_DALI "|" // Prefix - "|" D_CMND_POWER "|" D_CMND_DIMMER "|Target" -#ifdef USE_LIGHT - "|Light" -#endif // USE_LIGHT - "|Send|Query|Scan|Group" - "|GroupSliders|Gear"; - -void (* const DALICommand[])(void) PROGMEM = { - &CmndDali, &CmndDaliPower, &CmndDaliDimmer, &CmndDaliTarget, -#ifdef USE_LIGHT - &CmndDaliLight, -#endif // USE_LIGHT - &CmndDaliSend, &CmndDaliQuery, &CmndDaliScan, &CmndDaliGroup, - &CmndDaliGroupSliders, &CmndDaliGear }; +typedef struct DliSettings_t { + uint32_t crc32; // To detect file changes + uint8_t target; + uint8_t light_type; +} DliSettings_t; struct DALI { + DliSettings_t Settings; // Persistent settings uint32_t bit_cycles; uint32_t last_activity; uint32_t received_dali_data; // Data received from DALI bus @@ -156,7 +148,6 @@ struct DALI { uint8_t last_dimmer; uint8_t dimmer[DALI_MAX_STORED]; uint8_t web_dimmer[DALI_MAX_STORED]; - uint8_t target; uint8_t target_rgbwaf; uint8_t device_type; bool allow_light; @@ -170,6 +161,87 @@ struct DALI { bool invert_tx; } *Dali = nullptr; +/*********************************************************************************************\ + * Driver Settings load and save +\*********************************************************************************************/ + +#ifdef USE_UFILESYS +#define XDRV_75_KEY "drvset75" + +bool DaliLoadData(void) { + char key[] = XDRV_75_KEY; + String json = UfsJsonSettingsRead(key); + if (json.length() == 0) { return false; } + + // {"Crc":1882268982,"Target":0,"LightType":3} + JsonParser parser((char*)json.c_str()); + JsonParserObject root = parser.getRootObject(); + if (!root) { return false; } + + Dali->Settings.crc32 = root.getUInt(PSTR("Crc"), Dali->Settings.crc32); + Dali->Settings.target = root.getUInt(PSTR("Target"), Dali->Settings.target); + Dali->Settings.light_type = root.getUInt(PSTR("LightType"), Dali->Settings.light_type); + + return true; +} + +bool DaliSaveData(void) { + Response_P(PSTR("{\"" XDRV_75_KEY "\":{\"Crc\":%u,\"Target\":%u,\"LightType\":%u}}"), + Dali->Settings.crc32, + Dali->Settings.target, + Dali->Settings.light_type); + + return UfsJsonSettingsWrite(ResponseData()); +} + +void DaliDeleteData(void) { + char key[] = XDRV_75_KEY; + UfsJsonSettingsDelete(key); // Use defaults +} +#endif // USE_UFILESYS + +/*********************************************************************************************/ + +void DaliSettingsLoad(bool erase) { + // Called from FUNC_MODULE_INIT/FUNC_PRE_INIT (erase = 0) once at restart + // Called from FUNC_RESET_SETTINGS (erase = 1) after command reset 4, 5, or 6 + + // *** Start init default values in case key is not found *** + memset(&Dali->Settings, 0x00, sizeof(DliSettings_t)); + Dali->Settings.light_type = LT_RGB; // Default RGB channel + // *** End Init default values *** + +#ifndef USE_UFILESYS + AddLog(LOG_LEVEL_DEBUG, PSTR("DLI: Use default cfg as file system not enabled")); +#else + // Try to load key + if (erase) { + DaliDeleteData(); + } + else if (DaliLoadData()) { + AddLog(LOG_LEVEL_DEBUG, PSTR("DLI: Cfg loaded from file")); + } + else { + AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("DLI: Use default cfg as file system not ready or key not found")); + } +#endif // USE_UFILESYS +} + +void DaliSettingsSave(void) { + // Called from FUNC_SAVE_SETTINGS every SaveData second and at restart +#ifdef USE_UFILESYS + uint32_t crc32 = GetCfgCrc32((uint8_t*)&Dali->Settings +4, sizeof(DliSettings_t) -4); // Skip crc32 + if (crc32 != Dali->Settings.crc32) { + Dali->Settings.crc32 = crc32; + if (DaliSaveData()) { + AddLog(LOG_LEVEL_DEBUG, PSTR("DLI: Cfg saved to file")); + } else { + AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("DLI: ERROR File system not ready or unable to save file")); + } + } +#endif // USE_UFILESYS +} + /*********************************************************************************************\ * DALI low level \*********************************************************************************************/ @@ -507,12 +579,13 @@ bool DaliSetDTR(uint32_t dtr, uint32_t adr, uint32_t value) { if (dtr > 2) { dtr = 0; } DaliSendData(dtr_set[dtr], value); // Store value in DTR - int result = DaliSendWaitResponse(adr, dtr_query[dtr]); // Get DTR value + int result = DaliSendWaitResponse(adr | DALI_SELECTOR_BIT, dtr_query[dtr]); // Get DTR value return (result == value); } bool DaliSetValue(uint32_t adr, uint32_t getcmd, uint32_t setcmd, uint32_t v) { // Set a parameter value, returns true on success + adr |= DALI_SELECTOR_BIT; // Enable Selector bit int current_v = DaliSendWaitResponse(adr, getcmd); // Get current parameter value if (current_v == v) { return true; } // Already set if (!DaliSetDTR(0, adr, v)) { return false; } @@ -544,15 +617,35 @@ bool DaliSetPowerOnLevel(uint32_t adr, uint32_t v) { int DaliQueryExtendedVersionNumber(uint32_t adr, uint32_t device_type) { DaliSendData(DALI_102_ENABLE_DEVICE_TYPE_X, device_type); // Enable Extended command - return DaliSendWaitResponse(adr, 255); // DALI_xxx_QUERY_EXTENDED_VERSION_NUMBER + 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; - if (DaliQueryExtendedVersionNumber(adr, 8) >= 0) { // Colour device + + adr |= DALI_SELECTOR_BIT; // Enable Selector bit + int result = DaliSendWaitResponse(adr, DALI_102_QUERY_DEVICE_TYPE); + // If the device does not implement any part 2xx device type then the response will be 254; + // If the device implements one part 2xx device type then the response will be the device type number; + // If the device implements multiple part 2xx device types then the response will be MASK (0xff). + // In all other cases returns NO (no response). + while ((result >= 0) && (result != 8) && (result != 254)) { + result = DaliSendWaitResponse(adr, DALI_102_QUERY_NEXT_DEVICE_TYPE); + // DALI2: If directly preceded by DALI_102_QUERY_DEVICE_TYPE and more than one device type is supported, returns the first and lowest device type number. + // DALI2: If directly preceded by DALI_102_QUERY_NEXT_DEVICE_TYPE and not all device types have been reported, returns the next lowest device type number. + // DALI2: If directly preceded by DALI_102_QUERY_NEXT_DEVICE_TYPE and all device types have been reported, returns 254. + // In all other cases returns NO (no response). + if (result < 0) { + if (DaliQueryExtendedVersionNumber(adr, 8) >= 0) { // Colour device + result = 8; + } + } + } + if (8 == result) { DaliSendData(DALI_102_ENABLE_DEVICE_TYPE_X, 8); // Enable Extended command - int result = DaliSendWaitResponse(adr, DALI_209_QUERY_COLOUR_TYPE_FEATURES); + result = DaliSendWaitResponse(adr, DALI_209_QUERY_COLOUR_TYPE_FEATURES); if (result >= 0) { rgbwaf_channels = (result >> 5) & 0x07; // RGBWAF channels in bits 5..7 } @@ -566,7 +659,7 @@ uint32_t DaliQueryRGBWAF(uint32_t adr) { uint32_t DaliGearPresent(void) { uint32_t count = 0; for (uint32_t sa = 0; sa < Dali->max_short_address; sa++) { // Scanning 64 addresses takes about 2500 ms - if (DaliSendWaitResponse(sa << 1 | 1, DALI_102_QUERY_CONTROL_GEAR_PRESENT, 20) >= 0) { + if (DaliSendWaitResponse((sa << 1) | DALI_SELECTOR_BIT, DALI_102_QUERY_CONTROL_GEAR_PRESENT, 20) >= 0) { count++; } } @@ -577,12 +670,13 @@ uint32_t DaliGearPresent(void) { void DaliInitLight(void) { // Taken from Shelly Dali Dimmer ;-) + uint32_t adr = DALI_BROADCAST_DP | DALI_SELECTOR_BIT; DaliSendData(DALI_102_SET_DTR0, DALI_INIT_FADE); // Fade x second - DaliSendData(0xFF, DALI_102_SET_FADE_TIME); + DaliSendData(adr, DALI_102_SET_FADE_TIME); DaliSendData(DALI_102_SET_DTR0, 0); // Power off after gear power restore - DaliSendData(0xFF, DALI_102_SET_POWER_ON_LEVEL); + DaliSendData(adr, DALI_102_SET_POWER_ON_LEVEL); DaliSendData(DALI_102_SET_DTR0, 0xFE); // Reset all but short circuit - DaliSendData(0xFF, DALI_102_SET_SYSTEM_FAILURE_LEVEL); + DaliSendData(adr, DALI_102_SET_SYSTEM_FAILURE_LEVEL); } /*********************************************************************************************\ @@ -656,7 +750,7 @@ uint32_t DaliFindAddress(void) { void DaliProgramShortAddress(uint8_t shortadr) { // The slave shall store the received 6-bit address (AAAAAA) as a short address if it is selected. - DaliSendData(DALI_102_PROGRAM_SHORT_ADDRESS, (shortadr << 1) | 0x01); + DaliSendData(DALI_102_PROGRAM_SHORT_ADDRESS, (shortadr << 1) | DALI_SELECTOR_BIT); AddLog(LOG_LEVEL_INFO, PSTR("DLI: Set short address %d"), shortadr +1); } @@ -669,7 +763,7 @@ uint32_t DaliCommission(uint8_t init_arg) { // init_arg=00000000 : all // init_arg=0AAAAAA1 : only for this shortadr // returns number of new short addresses assigned - DaliSendData(0xFF, DALI_102_RESET); // Turns ON all lights + DaliSendData(DALI_BROADCAST_DP | DALI_SELECTOR_BIT, DALI_102_RESET); // Turns ON all lights uint8_t arr[64]; uint32_t sa; for (sa = 0; sa < 64; sa++) { @@ -677,7 +771,7 @@ uint32_t DaliCommission(uint8_t init_arg) { } delay(450); // It is not guaranteed that any commands will be received properly within the next 300ms DaliSendData(DALI_102_SET_DTR0, 0xFF); - DaliSendData(0xFF, DALI_102_SET_SHORT_ADDRESS); + DaliSendData(DALI_BROADCAST_DP | DALI_SELECTOR_BIT, DALI_102_SET_SHORT_ADDRESS); DaliSendData(DALI_102_TERMINATE, 0x00); // Terminate the DALI_102_INITIALISE command delay(15); // Start commissioning @@ -693,15 +787,14 @@ uint32_t DaliCommission(uint8_t init_arg) { if (0 == arr[sa]) { break; } } if (sa >= 64) { break; } // All 64 short addresses assigned -> exit + arr[sa] = 1; // Mark short address as used cnt++; - DaliProgramShortAddress(sa); // Assign short address DaliSendData(DALI_102_WITHDRAW, 0x00); // Remove the device from the search - DaliSendData(sa << 1, DALI_102_OFF); // Turns OFF latest short address light - - delay(1); + delay(100); OsWatchLoop(); // Feed blocked-loop watchdog + DaliSendData((sa << 1) | DALI_SELECTOR_BIT, DALI_102_OFF); // Turns OFF latest short address light } delay(100); @@ -711,7 +804,7 @@ uint32_t DaliCommission(uint8_t init_arg) { #ifdef USE_LIGHT DaliInitLight(); if (Settings->sbflag1.dali_light) { // DaliLight 1 - address = DaliTarget2Address(Dali->target); + address = DaliTarget2Address(Dali->Settings.target); } #endif // USE_LIGHT DaliSendData(address, Dali->power[0]); // Restore lights @@ -753,7 +846,7 @@ void DaliLoop(void) { bool show_response = true; #ifdef USE_LIGHT - if (Dali->allow_light && (DaliTarget2Address(Dali->target) == Dali->address)) { + if (Dali->allow_light && (DaliTarget2Address(Dali->Settings.target) == Dali->address)) { if (Settings->sbflag1.dali_light) { // DaliLight 1 uint8_t dim_old = changeUIntScale(Dali->last_dimmer, 0, 254, 0, 100); uint8_t dim_new = changeUIntScale(Dali->dimmer[index], 0, 254, 0, 100); @@ -802,10 +895,11 @@ bool DaliSetChannels(void) { for (uint32_t i = 0; i < 5; i++) { if (255 == cur_col[i]) { cur_col[1] = 254; } // Max Dali value } - uint32_t adr = DaliTarget2Address(Dali->target); + uint32_t adr = DaliTarget2Address(Dali->Settings.target); #ifdef DALI_LIGHT_COLOR_SUPPORT if (Dali->target_rgbwaf > 0) { // Colour control + adr |= DALI_SELECTOR_BIT; // Enable Selector bit if (!DaliSetDTR(0, adr, 0x7F)) { return true; } // Linked Channel control DaliSendData(DALI_102_ENABLE_DEVICE_TYPE_X, 8); // Enable Extended command DaliSendData(adr, DALI_209_SET_TEMPORARY_RGBWAF_CONTROL); @@ -862,6 +956,7 @@ bool DaliInit(uint32_t function) { Dali = (DALI*)calloc(sizeof(DALI), 1); // Need calloc to reset registers to 0/false if (!Dali) { return false; } + DaliSettingsLoad(0); Dali->pin_tx = pin_tx; Dali->invert_tx = invert_tx; @@ -902,12 +997,12 @@ bool DaliInit(uint32_t function) { Settings->light_fade = 0; // Use Dali fading instead Settings->light_correction = 0; // Use Dali light correction UpdateDevicesPresent(1); - TasmotaGlobal.light_type = LT_SERIAL1; // Single channel + TasmotaGlobal.light_type = LT_SERIAL1; // Single channel #ifdef DALI_LIGHT_COLOR_SUPPORT - Dali->target_rgbwaf = DaliQueryRGBWAF(DaliTarget2Address(Dali->target)); + Dali->target_rgbwaf = DaliQueryRGBWAF(DaliTarget2Address(Dali->Settings.target)); if (Dali->target_rgbwaf > 0) { - TasmotaGlobal.light_type = LT_RGB; // RGB channel (TBD) + TasmotaGlobal.light_type = Dali->Settings.light_type; } #endif // DALI_LIGHT_COLOR_SUPPORT @@ -921,6 +1016,20 @@ bool DaliInit(uint32_t function) { * Commands \*********************************************************************************************/ +const char kDALICommands[] PROGMEM = D_PRFX_DALI "|" // Prefix + "|" D_CMND_POWER "|" D_CMND_DIMMER "|Target" +#ifdef USE_LIGHT + "|Light|Channels" +#endif // USE_LIGHT + "|Send|Query|Scan|Group|GroupSliders|Gear"; + +void (* const DALICommand[])(void) PROGMEM = { + &CmndDali, &CmndDaliPower, &CmndDaliDimmer, &CmndDaliTarget, +#ifdef USE_LIGHT + &CmndDaliLight, &CmndDaliChannels, +#endif // USE_LIGHT + &CmndDaliSend, &CmndDaliQuery, &CmndDaliScan, &CmndDaliGroup, &CmndDaliGroupSliders, &CmndDaliGear }; + bool DaliJsonParse(void) { // {"addr":254,"cmd":100} // {"addr":2} @@ -994,12 +1103,12 @@ void CmndDaliTarget(void) { if (((XdrvMailbox.payload >= 1) && (XdrvMailbox.payload <= 64)) || ((XdrvMailbox.payload >= 101) && (XdrvMailbox.payload <= 116)) || (XdrvMailbox.payload == 0)) { - Dali->target = XdrvMailbox.payload; + Dali->Settings.target = XdrvMailbox.payload; } #ifdef DALI_LIGHT_COLOR_SUPPORT - Dali->target_rgbwaf = DaliQueryRGBWAF(DaliTarget2Address(Dali->target)); + Dali->target_rgbwaf = DaliQueryRGBWAF(DaliTarget2Address(Dali->Settings.target)); #endif // DALI_LIGHT_COLOR_SUPPORT - ResponseCmndNumber(Dali->target); + ResponseCmndNumber(Dali->Settings.target); } /*-------------------------------------------------------------------------------------------*/ @@ -1087,7 +1196,7 @@ void CmndDaliGroup(void) { if (sa < 64) { snprintf_P(temp, sizeof(temp), PSTR("%s%s%d"), temp, (more)?",":"", sa +1); more = true; - DaliSendData(sa << 1 | 1, command); + DaliSendData((sa << 1) | DALI_SELECTOR_BIT, command); } } ResponseCmndIdxChar(temp); @@ -1100,7 +1209,7 @@ void CmndDaliGroup(void) { bitmask = 1 << group - 8; } for (uint32_t sa = 0; sa < Dali->max_short_address; sa++) { // Scanning 64 addresses takes about 2500 ms - int result = DaliSendWaitResponse(sa << 1 | 1, command, 20); + int result = DaliSendWaitResponse((sa << 1) | DALI_SELECTOR_BIT, command, 20); if ((result >= 0) && (result & bitmask)) { snprintf_P(temp, sizeof(temp), PSTR("%s%s%d"), temp, (more)?",":"", sa +1); more = true; @@ -1148,13 +1257,14 @@ void CmndDaliSend(void) { DaliSend6 ||, */ if ((params >= 2) && (values[1] >= 224) && (values[1] <= 255)) { // DT6 extended command + uint32_t adr = values[0] | DALI_SELECTOR_BIT; if ((DALI_207_SELECT_DIMMING_CURVE == values[1]) || (DALI_207_SET_FAST_FADE_TIME == values[1])) { // DaliSend6 ||,, - if (!DaliSetDTR(0, values[0], values[2])) { return; } + if (!DaliSetDTR(0, adr, values[2])) { return; } } DaliSendData(DALI_102_ENABLE_DEVICE_TYPE_X, 6); // Enable Extended command - DaliSendData(values[0], values[1]); + DaliSendData(adr, values[1]); ResponseCmndDone(); return; } @@ -1168,43 +1278,44 @@ void CmndDaliSend(void) { DaliSend8 ||, */ if ((params >= 2) && (values[1] >= 224) && (values[1] <= 255)) { // DT8 extended command + uint32_t adr = values[0] | DALI_SELECTOR_BIT; // params == 3 if ((DALI_209_SET_TEMPORARY_RGBWAF_CONTROL == values[1]) || (DALI_209_STORE_GEAR_FEATURES_STATUS == values[1]) || (DALI_209_ASSIGN_COLOUR_TO_LINKED_COMMAND == values[1])) { // DaliSend8 ||,, - if (!DaliSetDTR(0, values[0], values[2])) { return; } + if (!DaliSetDTR(0, adr, values[2])) { return; } } else if ((DALI_209_SET_TEMPORARY_X_COORDINATE == values[1]) || (DALI_209_SET_TEMPORARY_Y_COORDINATE == values[1]) || (DALI_209_SET_TEMPORARY_COLOUR_TEMP_TC == values[1])) { // DaliSend8 ||,, - if (!DaliSetDTR(0, values[0], values[2] % 256)) { return; } - if (!DaliSetDTR(1, values[0], values[2] / 256)) { return; } + if (!DaliSetDTR(0, adr, values[2] % 256)) { return; } + if (!DaliSetDTR(1, adr, values[2] / 256)) { return; } } else if (DALI_209_STORE_XY_COORDINATE_PRIMARY_N == values[1]) { // DaliSend8 ||,, - if (!DaliSetDTR(2, values[0], values[2])) { return; } + if (!DaliSetDTR(2, adr, values[2])) { return; } } // params == 4 else if ((DALI_209_SET_TEMPORARY_PRIMARY_N_DIMLEVEL == values[1]) || (DALI_209_STORE_TY_PRIMARY_N == values[1]) || (DALI_209_STORE_COLOUR_TEMP_TC_LIMIT == values[1])) { // DaliSend8 ||,,, - if (!DaliSetDTR(0, values[0], values[2] % 256)) { return; } - if (!DaliSetDTR(1, values[0], values[2] / 256)) { return; } - if (!DaliSetDTR(2, values[0], values[3])) { return; } + if (!DaliSetDTR(0, adr, values[2] % 256)) { return; } + if (!DaliSetDTR(1, adr, values[2] / 256)) { return; } + if (!DaliSetDTR(2, adr, values[3])) { return; } } // params == 5 else if ((DALI_209_SET_TEMPORARY_RGB_DIMLEVEL == values[1]) || (DALI_209_SET_TEMPORARY_WAF_DIMLEVEL == values[1])) { // DaliSend8 ||,,,, - if (!DaliSetDTR(0, values[0], values[2])) { return; } - if (!DaliSetDTR(1, values[0], values[3])) { return; } - if (!DaliSetDTR(2, values[0], values[4])) { return; } + if (!DaliSetDTR(0, adr, values[2])) { return; } + if (!DaliSetDTR(1, adr, values[3])) { return; } + if (!DaliSetDTR(2, adr, values[4])) { return; } } DaliSendData(DALI_102_ENABLE_DEVICE_TYPE_X, 8); // Enable Extended command - DaliSendData(values[0], values[1]); + DaliSendData(adr, values[1]); ResponseCmndDone(); return; } @@ -1226,17 +1337,19 @@ void CmndDaliSend(void) { } } if (3 == params) { // Set extended command mode - DaliSendWaitResponse(DALI_102_ENABLE_DEVICE_TYPE_X, values[0] &0xFF); // Enable Extended command + DaliSendData(DALI_102_ENABLE_DEVICE_TYPE_X, values[0]); // Enable Extended command values[0] = values[1]; values[1] = values[2]; params = 2; } + uint32_t adr = values[0]; + if (XdrvMailbox.index > 1) { adr |= DALI_SELECTOR_BIT; } if (2 == params) { - DaliSendData(values[0], values[1]); + DaliSendData(adr, values[1]); ResponseCmndDone(); } else if (4 == params) { - if (DaliSetValue(values[0] &0x1FF, values[1] &0xFF, values[2] &0xFF, values[3] &0xFF)) { + if (DaliSetValue(adr, values[1], values[2], values[3])) { ResponseCmndDone(); } else { ResponseCmndFailed(); @@ -1256,23 +1369,25 @@ void CmndDaliQuery(void) { if (6 == XdrvMailbox.index) { // DaliQuery6 - DT6 = 207 = Extended LED commands 224...236 if ((params >= 2) && (values[1] >= 224) && (values[1] <= 255)) { // DT6 extended command + uint32_t adr = values[0] | DALI_SELECTOR_BIT; DaliSendData(DALI_102_ENABLE_DEVICE_TYPE_X, 6); // Enable Extended command - int result = DaliSendWaitResponse(values[0], values[1]); + int result = DaliSendWaitResponse(adr, values[1]); ResponseCmndNumber(result); return; } } if (8 == XdrvMailbox.index) { // DaliQuery8 - DT8 = 209 = Extended colour commands 224...246 if ((params >= 2) && (values[1] >= 224) && (values[1] <= 255)) { // DT8 extended command + uint32_t adr = values[0] | DALI_SELECTOR_BIT; if (DALI_209_QUERY_COLOUR_VALUE == values[1]) { - if (!DaliSetDTR(0, values[0], values[2])) { return; } + if (!DaliSetDTR(0, adr, values[2])) { return; } } DaliSendData(DALI_102_ENABLE_DEVICE_TYPE_X, 8); // Enable Extended command - int result = DaliSendWaitResponse(values[0], values[1]); + int result = DaliSendWaitResponse(adr, values[1]); if (DALI_209_QUERY_COLOUR_VALUE == values[1]) { if (result >= 0) { uint32_t result2 = result << 8; - int result = DaliSendWaitResponse(values[0], DALI_102_QUERY_CONTENT_DTR0); + int result = DaliSendWaitResponse(adr, DALI_102_QUERY_CONTENT_DTR0); if (result >= 0) { result = result2 | result; } @@ -1291,14 +1406,14 @@ void CmndDaliQuery(void) { } } if (3 == params) { -// DaliSendWaitResponse(DALI_102_ENABLE_DEVICE_TYPE_X, values[0] &0xFF); // Enable Extended command DaliSendData(DALI_102_ENABLE_DEVICE_TYPE_X, values[0]); // Enable Extended command values[0] = values[1]; values[1] = values[2]; params = 2; } if (2 == params) { - int result = DaliSendWaitResponse(values[0] &0x1FF, values[1] &0xFF); + uint32_t adr = values[0] | DALI_SELECTOR_BIT; + int result = DaliSendWaitResponse(adr, values[1]); ResponseCmndNumber(result); } } @@ -1342,6 +1457,22 @@ void CmndDaliLight(void) { } ResponseCmndStateText(Settings->sbflag1.dali_light); // DaliLight 0/1 } + +/*-------------------------------------------------------------------------------------------*/ + +void CmndDaliChannels(void) { + // DaliChannels - Show amount of color channels + // DaliChannels 1 - Set amount of color channels to R + // DaliChannels 2 - Set amount of color channels to RG + // DaliChannels 3 - Set amount of color channels to RGB + // DaliChannels 4 - Set amount of color channels to RGBW + // DaliChannels 5 - Set amount of color channels to RGBWC + if (Dali->allow_light && ((XdrvMailbox.payload >= 1) && (XdrvMailbox.payload <= 5))) { + Dali->Settings.light_type = XdrvMailbox.payload + 8; + TasmotaGlobal.restart_flag = 2; // Restart to update GUI + } + ResponseCmndNumber(Dali->Settings.light_type -8); +} #endif // USE_LIGHT /*********************************************************************************************\ @@ -1445,6 +1576,12 @@ bool Xdrv75(uint32_t function) { case FUNC_EVERY_SECOND: DaliEverySecond(); break; + case FUNC_RESET_SETTINGS: + DaliSettingsLoad(1); + break; + case FUNC_SAVE_SETTINGS: + DaliSettingsSave(); + break; #ifdef USE_LIGHT case FUNC_SET_CHANNELS: result = DaliSetChannels();