Fix DALI protocol errors

This commit is contained in:
Theo Arends 2025-11-19 10:44:21 +01:00
parent 4676db2ede
commit 4de92d2e7a
4 changed files with 213 additions and 69 deletions

View File

@ -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

View File

@ -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

View File

@ -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.

View File

@ -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 <broadcast>|<device>|<group>,<command>
*/
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 <broadcast>|<device>|<group>,<command>,<dtr0>
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 <broadcast>|<device>|<group>,<command>
*/
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 <broadcast>|<device>|<group>,<command>,<dtr0_data>
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 <broadcast>|<device>|<group>,<command>,<dtr0_1_data>
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 <broadcast>|<device>|<group>,<command>,<dtr2_data>
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 <broadcast>|<device>|<group>,<command>,<dtr0_1_data>,<dtr2_data>
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 <broadcast>|<device>|<group>,<command>,<dtr0_data>,<dtr1_data>,<dtr2_data>
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();