diff --git a/CHANGELOG.md b/CHANGELOG.md
index cd25deabf..e2b378aa8 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -6,11 +6,12 @@ All notable changes to this project will be documented in this file.
 ## [14.2.0.6]
 ### Added
 - Support for Sonoff SPM v1.3.0 (#13447)
-- LVGL port `colorwheel` from LVGL 8
-- HASPmota `cpicker` and `msgbox`
+- LVGL port `colorwheel` from LVGL 8 (#22244)
+- HASPmota `cpicker` and `msgbox` (#22244)
+- Support for DALI 1 on ESP8266
 
 ### Breaking Changed
-- HASPmota `delete` instead of `delete()`
+- HASPmota `delete` instead of `delete()` (#22245)
 
 ### Changed
 - ESP32 platform update from 2024.09.10 to 2024.09.30 and Framework (Arduino Core) from v3.0.5 to v3.1.0.240926 (#22203)
@@ -20,6 +21,7 @@ All notable changes to this project will be documented in this file.
 - HASPmota error when page '1' is not defined (#22220)
 - ESP32-S3 uDisplay force cache writes to RGB display (#22222)
 - ESP32 Dali compile error with core 3.x (#22214)
+- Dali received data decoding
 
 ### Removed
 
diff --git a/CODE_OWNERS.md b/CODE_OWNERS.md
index c6b33cdf6..f70f2b8ff 100644
--- a/CODE_OWNERS.md
+++ b/CODE_OWNERS.md
@@ -86,7 +86,7 @@ In addition to @arendst the following code is mainly owned by:
 | xdrv_72_pipsolar             | @chefpro
 | xdrv_73_lora                 | @arendst
 | xdrv_74                      |
-| xdrv_75                      |
+| xdrv_75_dali                 | @eeak, @arendst
 | xdrv_76                      |
 | xdrv_77                      |
 | xdrv_78                      |
@@ -98,7 +98,7 @@ In addition to @arendst the following code is mainly owned by:
 | xdrv_86_esp32_sonoff_spm     | @arendst
 | xdrv_87_esp32_sonoff_tm1621  | @arendst
 | xdrv_88_esp32_shelly_pro     | @arendst
-| xdrv_89_esp32_dali           | @eeak
+| xdrv_89_                     |
 | xdrv_90_esp32_dingtian_relay | @barbudor
 | xdrv_91_                     |
 | xdrv_92_                     |
diff --git a/RELEASENOTES.md b/RELEASENOTES.md
index d5c5a41c9..254ea05bf 100644
--- a/RELEASENOTES.md
+++ b/RELEASENOTES.md
@@ -121,6 +121,7 @@ The latter links can be used for OTA upgrades too like ``OtaUrl https://ota.tasm
 - ESP8266 support for one-wire M1601 temperature sensor on DS18x20 GPIO [#21376](https://github.com/arendst/Tasmota/issues/21376)
 - ESP8266 support for I2C CLK on GPIO16 [#22199](https://github.com/arendst/Tasmota/issues/22199)
 - Support for I2C M5Unit (Mini)Scales using HX711 driver
+- Support for DALI 1 on ESP8266
 - Support for RX8010 RTC as used in IOTTIMER [#21376](https://github.com/arendst/Tasmota/issues/21376)
 - Support for BL0906 up to 6 channel energy monitor as used in Athom EM2/EM6 [#22167](https://github.com/arendst/Tasmota/issues/22167)
 - Support for Sonoff SPM v1.3.0 [#13447](https://github.com/arendst/Tasmota/issues/13447)
@@ -136,6 +137,8 @@ The latter links can be used for OTA upgrades too like ``OtaUrl https://ota.tasm
 - Berry Zigbee improvements to prepare Matter [#22083](https://github.com/arendst/Tasmota/issues/22083)
 - Berry virtual Energy driver [#22134](https://github.com/arendst/Tasmota/issues/22134)
 - Berry improve `int64` constructor [#22172](https://github.com/arendst/Tasmota/issues/22172)
+- LVGL port `colorwheel` from LVGL 8 [#22244](https://github.com/arendst/Tasmota/issues/22244)
+- HASPmota `cpicker` and `msgbox` [#22244](https://github.com/arendst/Tasmota/issues/22244)
 - Matter support for Zigbee Temperature, Humidity and Pressure sensors [#22084](https://github.com/arendst/Tasmota/issues/22084)
 - Matter support for Zigbee Occupancy and Light 0/1/2 (OnOff / Dimmer / White Color Temperature) [#22110](https://github.com/arendst/Tasmota/issues/22110)
 
@@ -165,6 +168,7 @@ The latter links can be used for OTA upgrades too like ``OtaUrl https://ota.tasm
 - PZEM continue energy monitoring when one phase fails [#21968](https://github.com/arendst/Tasmota/issues/21968)
 - BearSSL panic on ESP8266 in rare conditions [#22017](https://github.com/arendst/Tasmota/issues/22017)
 - ModbusBridge request and response logic [#22075](https://github.com/arendst/Tasmota/issues/22075)
+- Dali received data decoding
 - Autoconf prevent 'init.bat' from stopping on empty lines [#22158](https://github.com/arendst/Tasmota/issues/22158)
 - Zigbee extend timeout for MCU reboot from 5s to 10s [#22009](https://github.com/arendst/Tasmota/issues/22009)
 - Zigbee avoid disabling console serial on ESP32 and improved log messages [#22082](https://github.com/arendst/Tasmota/issues/22082)
diff --git a/tasmota/include/tasmota_template.h b/tasmota/include/tasmota_template.h
index 0bcb2adfd..59f108fe1 100644
--- a/tasmota/include/tasmota_template.h
+++ b/tasmota/include/tasmota_template.h
@@ -592,7 +592,7 @@ const uint16_t kGpioNiceList[] PROGMEM = {
  * Protocol specifics
 \*-------------------------------------------------------------------------------------------*/
 
-#if defined(USE_DALI) && defined(ESP32)
+#ifdef USE_DALI
   AGPIO(GPIO_DALI_RX),                  // DALI RX
   AGPIO(GPIO_DALI_TX),                  // DALI TX
 #endif  // USE_DALI
diff --git a/tasmota/my_user_config.h b/tasmota/my_user_config.h
index 21b9da449..e9d8128bb 100644
--- a/tasmota/my_user_config.h
+++ b/tasmota/my_user_config.h
@@ -1078,6 +1078,10 @@
 
 //#define USE_FLOWRATEMETER                        // Add support for water flow meter YF-DN50 and similary (+1k7 code)
 
+// #define USE_DALI                                // Add support for DALI 1 bridge (+2k1 code)
+  #define DALI_IN_INVERT   0                     // DALI RX inverted
+  #define DALI_OUT_INVERT  0                     // DALI TX inverted
+
 // -- Thermostat control ----------------------------
 //#define USE_THERMOSTAT                           // Add support for Thermostat
   #define THERMOSTAT_CONTROLLER_OUTPUTS         1         // Number of outputs to be controlled independently
@@ -1139,11 +1143,6 @@
 #define USE_ESP32_SENSORS                        // Add support for ESP32 temperature and optional hall effect sensor
 #define USE_GPIO_VIEWER                          // Enable GPIO Viewer to see realtime GPIO states (+5k6 code)
 
-// #define USE_DALI                              // Add support for DALI
-    #define DALI_IN_INVERT  0                 // DALI RX inverted ?
-    #define DALI_OUT_INVERT  0                // DALI TX inverted ?
-    #define DALI_TIMER 0                      // ESP32 hardware timer number 0-3 !!! timer 3 used in xdrv_10_scripter.ino !!!
-
 //#define USE_SONOFF_SPM                           // Add support for ESP32 based Sonoff Smart Stackable Power Meter (+11k code)
 //#define USE_DISPLAY_TM1621_SONOFF                // Add support for TM1621 dsiplay driver used by Sonoff POWR3xxD and THR3xxD
 
diff --git a/tasmota/tasmota_support/support_features.ino b/tasmota/tasmota_support/support_features.ino
index bc86f8edb..2d8104524 100644
--- a/tasmota/tasmota_support/support_features.ino
+++ b/tasmota/tasmota_support/support_features.ino
@@ -823,7 +823,7 @@ constexpr uint32_t feature[] = {
   0x00000020 |  // xdrv_88_esp32_shelly_pro.ino
 #endif
 #ifdef USE_DALI
-  0x00000040 |  // xdrv_89_esp32_dali.ino
+  0x00000040 |  // xdrv_75_dali.ino
 #endif
 #if defined(USE_LIGHT) && defined(USE_BP1658CJ)
   0x00000080 |  // xlgt_10_bp1658cj.ino
diff --git a/tasmota/tasmota_xdrv_driver/xdrv_75_dali.ino b/tasmota/tasmota_xdrv_driver/xdrv_75_dali.ino
new file mode 100644
index 000000000..2614ffe0b
--- /dev/null
+++ b/tasmota/tasmota_xdrv_driver/xdrv_75_dali.ino
@@ -0,0 +1,411 @@
+/*
+  xdrv_75_dali.ino - DALI support for Tasmota
+
+  Copyright (C) 2022  Andrei Kazmirtsuk aka eeak and Theo Arends
+
+  This program is free software: you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation, either version 3 of the License, or
+  (at your option) any later version.
+
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+  You should have received a copy of the GNU General Public License
+  along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+  --------------------------------------------------------------------------------------------
+  Version yyyymmdd  Action    Description
+  --------------------------------------------------------------------------------------------
+  0.1.0.0 20241006  rewrite   - Add support for ESP8266
+                              - Fix decoding of received Dali 1 data
+                              - Refactor command `DaliPower 0..254` controlling Broadcast devices
+                              - Add command `DaliDimmer 0..254` controlling Broadcast devices
+
+  0.0.0.1 20221027  publish   - Initial version
+*/
+
+#ifdef USE_DALI
+
+/*********************************************************************************************\
+ * DALI support for Tasmota
+\*********************************************************************************************/
+
+#define XDRV_75             75
+
+#ifndef DALI_IN_INVERT
+#define DALI_IN_INVERT      0           // DALI RX inverted ?
+#endif
+#ifndef DALI_OUT_INVERT
+#define DALI_OUT_INVERT     0           // DALI TX inverted ?
+#endif
+
+//#define DALI_DEBUG
+#ifndef DALI_DEBUG_PIN
+#define DALI_DEBUG_PIN      27
+#endif
+
+#define BROADCAST_DP        0b11111110  // 0xFE = 254
+
+#define DALI_TOPIC "DALI"
+// http and json defines
+#define D_NAME_DALI "DALI"
+#define D_PRFX_DALI "Dali"
+
+const char kDALICommands[] PROGMEM = D_PRFX_DALI "|"  // Prefix
+  "|" D_CMND_POWER "|" D_CMND_DIMMER;
+
+void (* const DALICommand[])(void) PROGMEM = {
+  &CmndDali, &CmndDaliPower, &CmndDaliDimmer };
+
+struct DALI {
+  uint32_t bit_time;
+  uint16_t received_dali_data;  // Data received from DALI bus
+  uint8_t pin_rx;
+  uint8_t pin_tx;
+  uint8_t dimmer;
+  bool power;
+  bool input_ready;
+} *Dali = nullptr;
+
+/*********************************************************************************************\
+ * DALI low level
+\*********************************************************************************************/
+
+void DaliEnableRxInterrupt(void) {
+  attachInterrupt(Dali->pin_rx, DaliReceiveData, FALLING);
+}
+
+void DaliDisableRxInterrupt(void) {
+  detachInterrupt(Dali->pin_rx);
+}
+
+/*************** R E C E I V E * P R O C E D U R E *********/
+
+#define DALI_WAIT_RCV { while (ESP.getCycleCount() < (wait + start)); wait += bit_time; }
+
+void IRAM_ATTR DaliReceiveData(void);
+void DaliReceiveData(void) {
+  if (Dali->input_ready) { return; }
+  uint32_t start = ESP.getCycleCount();
+  uint32_t bit_time = Dali->bit_time;
+  // Advance the starting point for the samples but compensate for the
+  // initial delay which occurs before the interrupt is delivered
+  uint32_t wait = bit_time / 2;
+  int bit_state = 0; 
+  bool dali_read;
+  uint32_t received_dali_data = 0;
+
+  DALI_WAIT_RCV;
+  DALI_WAIT_RCV;                   // Start bit
+  for (uint32_t i = 0; i < 32; i++) {
+    DALI_WAIT_RCV;
+    if (abs(bit_state) <= 2) {     // Manchester encoding max 2 consequtive equal bits
+      dali_read = digitalRead(Dali->pin_rx);
+#ifdef DALI_DEBUG
+      digitalWrite(DALI_DEBUG_PIN, i&1);  // Add LogicAnalyzer poll indication
+#endif  // DALI_DEBUG
+      bit_state += (dali_read) ? 1 : -1;
+      if (i &1) {
+        uint32_t j = i >>1;
+        received_dali_data |= ((DALI_IN_INVERT) ? !dali_read : dali_read << (15 -j));
+      }
+    }
+  }
+  DALI_WAIT_RCV;
+  DALI_WAIT_RCV;                   // Stop bit
+
+  if (abs(bit_state) <= 2) {       // Valid Manchester encoding
+    Dali->received_dali_data = received_dali_data;
+    Dali->input_ready = true;      // Valid data received
+  }
+
+#ifdef ESP8266
+  // Must clear this bit in the interrupt register,
+  // it gets set even when interrupts are disabled
+  GPIO_REG_WRITE(GPIO_STATUS_W1TC_ADDRESS, 1 << Dali->pin_rx);
+#endif  // ESP8266
+}
+
+/*************** S E N D * P R O C E D U R E ***************/
+
+#define DALI_WAIT_SND { while (ESP.getCycleCount() < (wait + start)) optimistic_yield(1); wait += bit_time; }  // Watchdog timeouts
+
+void DaliDigitalWrite(bool pin_value) {
+  digitalWrite(Dali->pin_tx, (pin_value == DALI_OUT_INVERT) ? LOW : HIGH);
+}
+
+void DaliSendData(uint8_t firstByte, uint8_t secondByte) {
+  if (BROADCAST_DP == firstByte) {
+    Dali->power = (secondByte);  // State
+    Dali->dimmer = secondByte;   // Value
+  }
+
+  uint16_t send_dali_data = firstByte << 8;
+  send_dali_data += secondByte & 0xff;
+
+  DaliDisableRxInterrupt();
+
+  uint32_t bit_time = Dali->bit_time;
+  uint32_t wait = bit_time;
+//  digitalWrite(Dali->pin_tx, HIGH);     // already in HIGH mode
+  uint32_t start = ESP.getCycleCount();
+
+  // Settling time between forward and backward frame
+  for (uint32_t i = 0; i < 8; i++) {
+    DALI_WAIT_SND;
+  }
+  // Start bit;
+  DaliDigitalWrite(LOW);
+  DALI_WAIT_SND;
+  DaliDigitalWrite(HIGH);
+  DALI_WAIT_SND;
+  for (uint32_t i = 0; i < 16; i++) {
+    // Bit value (edge) selection
+    bool bit_value = (bool)((send_dali_data >> (15 - i)) & 0x01);  // MSB first
+    // Every half bit -> Manchester coding
+    DaliDigitalWrite(bit_value ? LOW : HIGH);  // Manchester 
+    DALI_WAIT_SND;
+    DaliDigitalWrite(bit_value ? HIGH : LOW);  // Value
+    DALI_WAIT_SND;
+  }
+  // Stop bit
+  DaliDigitalWrite(HIGH);
+  delay(1);
+
+  DaliEnableRxInterrupt();
+}
+
+void DaliPower(uint8_t val) {
+  DaliSendData(BROADCAST_DP, val);
+}
+
+/***********************************************************/
+
+void DaliInput(void) {
+  if (Dali->input_ready) {
+    uint8_t DALIaddr = Dali->received_dali_data >> 8;
+    uint8_t DALIcmnd = Dali->received_dali_data;
+    if (BROADCAST_DP == DALIaddr) {
+      Dali->power = (DALIcmnd);  // State
+      Dali->dimmer = DALIcmnd;   // Value
+    }
+
+//    AddLog(LOG_LEVEL_DEBUG, PSTR("DLI: Received 0x%04X"), Dali->received_dali_data);
+    Response_P(PSTR("{\"" D_NAME_DALI "\":{\"Power\":\"%s\",\"Dimmer\":%d,\"Address\":%d,\"Command\":%d}}"), 
+      GetStateText(Dali->power), Dali->dimmer, DALIaddr, DALIcmnd);
+    MqttPublishPrefixTopicRulesProcess_P(RESULT_OR_TELE, PSTR(D_NAME_DALI));
+
+    Dali->input_ready = false;
+  }
+}
+
+void DaliPreInit(void) {
+  if (!PinUsed(GPIO_DALI_TX) || !PinUsed(GPIO_DALI_RX)) { return; }
+
+  Dali = (DALI*)calloc(sizeof(DALI), 1);
+  if (!Dali) { return; }
+
+  Dali->pin_rx = Pin(GPIO_DALI_RX);
+  Dali->pin_tx = Pin(GPIO_DALI_TX);
+
+  AddLog(LOG_LEVEL_INFO, PSTR("DLI: GPIO%d(RX) and GPIO%d(TX)"), Dali->pin_rx, Dali->pin_tx);
+
+  pinMode(Dali->pin_tx, OUTPUT);
+  digitalWrite(Dali->pin_tx, HIGH);
+  pinMode(Dali->pin_rx, INPUT);
+#ifdef DALI_DEBUG
+  pinMode(DALI_DEBUG_PIN, OUTPUT);
+  digitalWrite(DALI_DEBUG_PIN, HIGH);
+#endif  // DALI_DEBUG
+
+  Dali->bit_time = ESP.getCpuFreqMHz() * 1000000 / 2400;  // Manchester twice 1200 bps
+
+  DaliEnableRxInterrupt();
+}
+
+bool DaliMqtt(void) {
+/*
+  XdrvMailbox.topic = topic;
+  XdrvMailbox.index = strlen(topic);
+  XdrvMailbox.data = (char*)data;
+  XdrvMailbox.data_len = data_len;
+
+  This won't work as there is currently no subscribe done
+*/
+  char stopic[TOPSZ];
+  strncpy(stopic, XdrvMailbox.topic, TOPSZ);
+  XdrvMailbox.topic[TOPSZ - 1] = 0;
+
+  char *items[10];
+  char *p = stopic;
+  int cnt = 0;
+  do {
+    items[cnt] = strtok(p, "/");
+    cnt++;
+    p = nullptr;
+  } while (items[cnt - 1]);
+  cnt--; // represents the number of items
+
+  AddLog(LOG_LEVEL_DEBUG, PSTR("DLI: Cnt %d, Topic '%s', Payload '%s'"), cnt, XdrvMailbox.topic, XdrvMailbox.data);
+
+  if (cnt < 3) { // not for us?
+    AddLog(LOG_LEVEL_INFO, PSTR("DLI: Cnt %d < 3"), cnt);
+    return false;
+  }
+
+  int DALIindex = 0;
+  int ADRindex = 0;
+  int CMDindex = 0;
+  uint8_t DALIaddr = BROADCAST_DP;
+
+  if (strcasecmp_P(items[cnt - 3], PSTR(DALI_TOPIC)) != 0) {      // dali
+    // cmnd
+    if (strcasecmp_P(items[cnt - 2], PSTR(DALI_TOPIC)) != 0) {    // dali
+      // device
+      return false; // not for us
+    } else {
+      // cmnd/dali/percent
+      DALIindex = cnt - 2;
+      CMDindex = cnt - 1;
+    }
+  } else {
+    // dali/percent/2 20 
+    DALIindex = cnt - 3;
+    CMDindex = cnt - 2;
+    ADRindex = cnt - 1;
+    DALIaddr = ((int)CharToFloat(items[ADRindex]))  << 1;
+  }
+
+  uint8_t level;
+  uint8_t value = (uint8_t)CharToFloat(XdrvMailbox.data);
+  if (strcasecmp_P(items[CMDindex], PSTR("percent")) == 0) {
+    // dali/percent/
+    float percent = (float)(254 * value * 0.01);
+    level = (uint8_t)percent;
+  }
+  else if (strcasecmp_P(items[CMDindex], PSTR("level")) == 0) {
+    level = value;
+  }
+  else {
+    AddLog(LOG_LEVEL_INFO,PSTR("DLI: Command not recognized: %s"), items[CMDindex]);
+    return false; // not for us
+  }
+
+  AddLog(LOG_LEVEL_INFO,PSTR("DLI: Dali value %d on address %d"), value, DALIaddr);
+  DaliSendData(DALIaddr, level);
+
+  return true;
+}
+
+bool DaliJsonParse(void) {
+  // {"addr":254,"cmd":100}
+  // {"addr":2}
+  // {"dim":3}
+
+  bool served = false;
+  JsonParser parser((char *)XdrvMailbox.data);
+  JsonParserObject root = parser.getRootObject();
+  if (root) {
+    int DALIindex = 0;
+    int ADRindex = 0;
+    int8_t DALIdim = -1;
+    uint8_t DALIaddr = BROADCAST_DP;
+
+    JsonParserToken val = root[PSTR("cmd")];
+    if (val) {
+      uint8_t cmd = val.getUInt();
+      val = root[PSTR("addr")];
+      if (val) {
+        uint8_t addr = val.getUInt();
+        AddLog(LOG_LEVEL_DEBUG, PSTR("DLI: cmd = %d, addr = %d"), cmd, addr);
+        DaliSendData(addr, cmd);
+        return true;
+      } else {
+        return false;
+      }
+    }
+    val = root[PSTR("addr")];
+    if (val) {
+      uint8_t addr = val.getUInt();
+      if ((addr >= 0) && (addr < 64)) {
+        DALIaddr = addr  << 1;
+      }
+    }
+    val = root[PSTR("dim")];
+    if (val) {
+      uint8_t dim = val.getUInt();
+      if (dim < 255) {
+        DALIdim = dim;
+      }
+    }
+    DaliSendData(DALIaddr, DALIdim);
+    served = true;
+  }
+  return served;
+}
+
+/*********************************************************************************************\
+ * Commands
+\*********************************************************************************************/
+
+void CmndDali(void) {
+  if (XdrvMailbox.data_len > 0) {
+    if (DaliJsonParse()) {
+      ResponseCmndDone();
+    }
+  }
+}
+
+void CmndDaliPower(void) {
+  if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 254)) {
+    DaliPower(XdrvMailbox.payload);
+  }
+  ResponseCmndStateText(Dali->power);
+}
+
+void CmndDaliDimmer(void) {
+  if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 254)) {
+    DaliPower(XdrvMailbox.payload);
+  }
+  ResponseCmndNumber(Dali->dimmer);
+}
+
+/*********************************************************************************************\
+ * Presentation
+\*********************************************************************************************/
+
+
+
+/*********************************************************************************************\
+ * Interface
+\*********************************************************************************************/
+
+bool Xdrv75(uint32_t function) {
+  bool result = false;
+
+  if (FUNC_INIT == function) {
+    DaliPreInit();
+  }
+  else if (Dali) {
+    switch (function) {
+      case FUNC_LOOP:
+        DaliInput();
+        break;
+      case FUNC_MQTT_DATA:
+        result = DaliMqtt();
+        break;
+      case FUNC_COMMAND:
+        result = DecodeCommand(kDALICommands, DALICommand);
+        break;
+      case FUNC_ACTIVE:
+        result = true;
+        break;
+    }
+  }
+  return result;
+}
+
+#endif  // USE_DALI
diff --git a/tasmota/tasmota_xdrv_driver/xdrv_89_esp32_dali.ino b/tasmota/tasmota_xdrv_driver/xdrv_89_esp32_dali.ino
deleted file mode 100644
index a739f46bd..000000000
--- a/tasmota/tasmota_xdrv_driver/xdrv_89_esp32_dali.ino
+++ /dev/null
@@ -1,601 +0,0 @@
-/*
-  xdrv_89_esp32_dali.ino - DALI support for Tasmota
-
-  Copyright (C) 2022  Andrei Kazmirtsuk aka eeak
-
-  This program is free software: you can redistribute it and/or modify
-  it under the terms of the GNU General Public License as published by
-  the Free Software Foundation, either version 3 of the License, or
-  (at your option) any later version.
-
-  This program is distributed in the hope that it will be useful,
-  but WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-  GNU General Public License for more details.
-
-  You should have received a copy of the GNU General Public License
-  along with this program.  If not, see <http://www.gnu.org/licenses/>.
-
-  --------------------------------------------------------------------------------------------
-  Version yyyymmdd  Action    Description
-  --------------------------------------------------------------------------------------------
-  0.0.0.1 20221027  publish - initial version
-*/
-
-#ifdef ESP32
-#ifdef USE_DALI
-
-/*********************************************************************************************\
- * DALI support for Tasmota
-\*********************************************************************************************/
-
-#define XDRV_89              89
-
-#ifndef DALI_TIMER
-    #define DALI_TIMER 0    // Default timer
-#endif
-
-#define BROADCAST_DP        0b11111110 // 0xFE
-#define DALI_TOPIC          "DALI"
-
-enum
-{
-    DALI_NO_ACTION,
-    DALI_SENDING_DATA,
-    DALI_RECEIVING_DATA,
-    DALI_ERROR
-};
-
-// http and json defines
-#define D_NAME_DALI "DALI"
-
-const char S_JSON_DALI_COMMAND_NVALUE[] PROGMEM = "{\"" D_NAME_DALI "\":{\"%s\":%d}}";
-const char kDALI_Commands[] PROGMEM  = D_CMND_DALI_POWER "|" D_CMND_DALI_DIMMER;
-
-enum DALI_Commands {         // commands for Console
-  CMND_DALI_PWR,
-  CMND_DALI_DIM,
-};
-
-struct DALI {
-    uint16_t send_dali_data;     // data to send to DALI bus
-    uint16_t received_dali_data; // data received from DALI bus
-    uint8_t flag;                // DALI status flag
-    uint8_t bit_count;           // nr of rec/send bits
-    uint16_t tick_count;         // nr of ticks of the timer
-    bool former_val;             // bit value in previous tick of timer
-    hw_timer_t *timer;           // hardware timer
-} *Dali = nullptr;
-
-
-
-/*********************************************************************************************\
- * DALI low level
-\*********************************************************************************************/
-
-/**
-* @brief  This function handles hardware timer Handler.
-* @param  None
-* @retval None
-*/
-void IRAM_ATTR DALI_Tick_Handler(void);
-void DALI_Tick_Handler(void)
-{
-    if (getDaliFlag() == DALI_RECEIVING_DATA)
-    {
-        receive_tick();
-    }
-    else if (getDaliFlag() == DALI_SENDING_DATA)
-    {
-        send_tick();
-    }
-}
-
-/**
-* @brief  This function enable data transfer start interrupt.
-* @param  None
-* @retval None
-*/
-void enableDaliRxInterrupt() {
-    Dali->flag = DALI_NO_ACTION;
-//    timerAlarmDisable(Dali->timer);
-    timerStop(Dali->timer);
-    attachInterrupt(Pin(GPIO_DALI_RX), receiveDaliData, FALLING);
-}
-
-/**
-* @brief  This function disable data transfer start interrupt.
-* @param  None
-* @retval None
-*/
-void disableRxInterrupt() {
-//    timerAlarmEnable(Dali->timer);
-    timerStart(Dali->timer);
-	detachInterrupt(Pin(GPIO_DALI_RX));
-}
-
-/**
-* @brief   receiving flag status
-* @param  None
-* @retval uint8_t flag
-*/
-uint8_t getDaliFlag(void)
-{
-  return Dali->flag;
-}
-
-/**
-* @brief   DALI data received callback
-* @param  None
-* @retval uint8_t flag
-*/
-void DataReceivedCallback() {
-    AddLog(LOG_LEVEL_DEBUG, PSTR("DLI: Received: %d %d"), Dali->received_dali_data>>9, Dali->received_dali_data&0xff);
-}
-
-/*************** R E C E I V E * P R O C E D U R E S *******/
-
-/**
-* @brief  receive data from DALI bus
-* @param  None
-* @retval None
-*/
-void receiveDaliData()
-{
-    // null variables
-    Dali->received_dali_data = 0;
-    Dali->bit_count = 0;
-    Dali->tick_count = 0;
-    Dali->former_val = true;
-
-    Dali->flag = DALI_RECEIVING_DATA;
-    
-	disableRxInterrupt();
-}
-
-/**
-* @brief  Get state of DALIIN pin
-* @param  None
-* @retval bool status
-*/
-bool get_DALIIN(void)
-{
-    bool dali_read = digitalRead(Pin(GPIO_DALI_RX));
-    return (false == DALI_IN_INVERT) ? dali_read : !dali_read;
-}
-
-/**
-* @brief   receiving data from DALI bus
-* @param  None
-* @retval None
-*
-* |--------|----|---------------------------|----|
-* 0        24   32                          160  176
-*    wait   start      data                  stop
-*/
-void receive_tick(void)
-{
-    // four ticks per bit
-    bool actual_val = get_DALIIN();
-    Dali->tick_count++;
-    
-    // edge detected
-    if(actual_val != Dali->former_val)
-    {
-        switch(Dali->bit_count)
-        {
-            case 0:
-                if (Dali->tick_count > 2)
-                {
-                    Dali->tick_count = 0;
-                    Dali->bit_count  = 1; // start bit
-                }
-                break;
-            case 17:      // 1st stop bit
-                if(Dali->tick_count > 6) { // stop bit error, no edge should exist
-                    Dali->flag = DALI_ERROR;
-				}
-                break;
-            default:      // other bits
-                if(Dali->tick_count > 6)
-                {
-                    Dali->received_dali_data |= (actual_val << (16-Dali->bit_count));
-                    Dali->bit_count++;
-                    Dali->tick_count = 0;
-                }
-                break;
-        }
-    }else // voltage level stable
-    {
-        switch(Dali->bit_count)
-        {
-            case 0:
-                if(Dali->tick_count==8) {	// too long start bit
-                    Dali->flag = DALI_ERROR;
-				}
-                break;
-            case 17:
-                // First stop bit
-                if (Dali->tick_count==8)
-                {
-                    if (actual_val==0) // wrong level of stop bit
-                    {
-                        Dali->flag = DALI_ERROR;
-                    }
-                    else
-                    {
-                        Dali->bit_count++;
-                        Dali->tick_count = 0;
-                    }
-                }
-                break;
-            case 18:
-                // Second stop bit
-                if (Dali->tick_count==8)
-                {
-                    enableDaliRxInterrupt();
-                    DataReceivedCallback();
-					
-                }
-                break;
-            default: // normal bits
-                if(Dali->tick_count==10)
-                { // too long delay before edge
-                    Dali->flag = DALI_ERROR;
-                }
-                break;
-        }
-    }
-    Dali->former_val = actual_val;
-    if(getDaliFlag() == DALI_ERROR)
-    {
-		enableDaliRxInterrupt();
-    }
-}
-
-
-/*************** S E N D * P R O C E D U R E S *************/
-
-/**
-* @brief   Set value to the DALIOUT pin
-* @param  bool
-* @retval None
-*/
-void set_DALIOUT(bool pin_value)
-{
-    digitalWrite(Pin(GPIO_DALI_TX), pin_value == DALI_OUT_INVERT ? LOW : HIGH);
-}
-
-/**
-* @brief   gets state of the DALIOUT pin
-* @param   None
-* @retval bool state of the DALIOUT pin
-*/
-bool get_DALIOUT(void)
-{
-    bool dali_read = digitalRead(Pin(GPIO_DALI_TX));
-    return (false == DALI_OUT_INVERT) ? dali_read : !dali_read;
-}
-
-/**
-* @brief   Send data to DALI bus
-* @param   byteToSend
-* @retval None
-*/
-void sendDaliData(uint8_t firstByte, uint8_t secondByte)
-{
-	Dali->send_dali_data = firstByte << 8;
-	Dali->send_dali_data += secondByte & 0xff;
-	Dali->bit_count = 0;
-	Dali->tick_count = 0;
-
-    Dali->flag = DALI_SENDING_DATA;
-
-	disableRxInterrupt();
-}
-
-/**
-* @brief   DALI protocol physical layer for slave device
-* @param   None
-* @retval  None
-*
-* |--------|----|---------------------------|----|
-* 0        24   32                          160  176
-*    wait   start      data                  stop
-*/
-void send_tick(void)
-{
-	// access to the routine just every 4 ticks = every half bit
-	if ((Dali->tick_count & 0x03) == 0)
-	{
-		if (Dali->tick_count < 160)
-		{
-			// settling time between forward and backward frame
-			if (Dali->tick_count < 24)
-			{
-				Dali->tick_count++;
-				return;
-			}
-
-			// start of the start bit
-			if (Dali->tick_count == 24)
-			{
-				//   GPIOB->ODR ^= GPIO_ODR_7;
-				set_DALIOUT(false);
-				Dali->tick_count++;
-				return;
-			}
-
-			// edge of the start bit
-			// 28 ticks = 28/9600 = 2,92ms = delay between forward and backward message frame
-			if (Dali->tick_count == 28)
-			{
-				set_DALIOUT(true);
-				Dali->tick_count++;
-				return;
-			}
-
-			// bit value (edge) selection
-			bool bit_value = (bool)((Dali->send_dali_data >> (15 - Dali->bit_count)) & 0x01);
-
-			// Every half bit -> Manchester coding
-			if (!((Dali->tick_count - 24) & 0x0007))
-			{									// div by 8
-				if (get_DALIOUT() == bit_value) // former value of bit = new value of bit
-					set_DALIOUT((bool)(1 - bit_value));
-			}
-
-			// Generate edge for actual bit
-			if (!((Dali->tick_count - 28) & 0x0007))
-			{
-				set_DALIOUT(bit_value);
-				Dali->bit_count++;
-			}
-		}
-		else
-		{ // end of data byte, start of stop bits
-			if (Dali->tick_count == 160)
-			{
-				set_DALIOUT(true); // start of stop bit
-			}
-
-			// end of stop bits, no settling time
-			if (Dali->tick_count == 176)
-			{
-				enableDaliRxInterrupt();
-			}
-		}
-	}
-	Dali->tick_count++;
-
-	return;
-}
-
-/***********************************************************/
-
-void DaliPreInit() {
-    if (!PinUsed(GPIO_DALI_TX) || !PinUsed(GPIO_DALI_RX)) { return; }
-    AddLog(LOG_LEVEL_INFO, PSTR("DLI: Init - RX-pin: %d, TX-pin: %d"), Pin(GPIO_DALI_RX), Pin(GPIO_DALI_TX));
-    // pinMode(LED, OUTPUT);
-	pinMode(Pin(GPIO_DALI_TX), OUTPUT);
-	digitalWrite(Pin(GPIO_DALI_TX), HIGH);
-	pinMode(Pin(GPIO_DALI_RX), INPUT);
-
-	Dali = (DALI*)calloc(1,sizeof(DALI));
-    if (!Dali) {
-        AddLog(LOG_LEVEL_INFO, PSTR("DLI: Memory allocation error"));
-        return;
-    }
-//  Arduino Core < 3
-//    Dali->timer = timerBegin(DALI_TIMER, 13, true);
-//    timerAttachInterrupt(Dali->timer, &DALI_Tick_Handler, true);
-//    timerAlarmWrite(Dali->timer, 641, true);
-
-//  Arduino Core > 3
-    Dali->timer = timerBegin(6153846);  // 80MHz / 13
-    if (nullptr == Dali->timer) {
-        AddLog(LOG_LEVEL_INFO, PSTR("DLI: No timer available"));
-        free(Dali);
-        Dali = nullptr;
-        return;
-    }
-    timerAttachInterrupt(Dali->timer, &DALI_Tick_Handler);
-    timerAlarm(Dali->timer, 641, true, 0);
-    
-    attachInterrupt(Pin(GPIO_DALI_RX), receiveDaliData, FALLING);
-	enableDaliRxInterrupt();
-}
-
-void DaliPwr(uint8_t val){
-    sendDaliData(BROADCAST_DP, val);
-}
-
-bool DaliCmd(void)
-{
-    char command[CMDSZ];
-    uint8_t name_len = strlen(D_NAME_DALI);
-    if (!strncasecmp_P(XdrvMailbox.topic, PSTR(D_NAME_DALI), name_len))
-    {
-        uint32_t command_code = GetCommandCode(command, sizeof(command), XdrvMailbox.topic + name_len, kDALI_Commands);
-        switch (command_code)
-        {
-        case CMND_DALI_PWR:
-            if (XdrvMailbox.data_len)
-            {
-                if (254 >= XdrvMailbox.payload)
-                {
-                    DaliPwr(XdrvMailbox.payload);
-                }
-            }
-            Response_P(S_JSON_DALI_COMMAND_NVALUE, command, XdrvMailbox.payload);
-            break;
-        default:
-            return false;
-        }
-        return true;
-    }
-    else
-    {
-        return false;
-    }
-}
-
-
-bool DaliMqtt()
-{
-    char stopic[TOPSZ];
-    strncpy(stopic, XdrvMailbox.topic, TOPSZ);
-    XdrvMailbox.topic[TOPSZ - 1] = 0;
-
-    char *items[10];
-    char *p = stopic;
-    int cnt = 0;
-    do
-    {
-        items[cnt] = strtok(p, "/");
-        cnt++;
-        p = nullptr;
-    } while (items[cnt - 1]);
-    cnt--; // repreents the number of items
-
-    if (cnt < 3)
-    { // not for us?
-        AddLog(LOG_LEVEL_INFO,PSTR("cnt: %d < 3"), cnt);
-        return false;
-    }
-
-    int DALIindex = 0;
-    int ADRindex = 0;
-    int CMDindex = 0;
-    uint8_t DALIaddr = BROADCAST_DP;
-    if (strcasecmp_P(items[cnt - 3], PSTR(DALI_TOPIC)) != 0)
-    {
-        if (strcasecmp_P(items[cnt - 2], PSTR(DALI_TOPIC)) != 0)
-        {
-            if (strcasecmp_P(items[cnt - 1], PSTR(DALI_TOPIC)) != 0)
-            {
-                return false; // not for us
-            }
-            else
-            {
-                if (true == DaliJsonParse()) { return true; }
-            }
-        }
-        else
-        {
-            DALIindex = cnt - 2;
-            CMDindex = cnt - 1;
-        }
-    }
-    else
-    {
-        DALIindex = cnt - 3;
-        CMDindex = cnt - 2;
-        ADRindex = cnt - 1;
-        DALIaddr = ((int)CharToFloat(items[ADRindex]))  << 1;
-        
-    }
-
-    uint8_t level;
-    uint8_t value = (uint8_t)CharToFloat(XdrvMailbox.data);
-    if (strcasecmp_P(items[CMDindex], PSTR("percent")) == 0) {
-        float percent = (float)(254 * value * 0.01);
-        level = (uint8_t)percent;
-    }
-    else if (strcasecmp_P(items[CMDindex], PSTR("level")) == 0) {
-        level = value;
-    }
-    else {
-        AddLog(LOG_LEVEL_INFO,PSTR("command not recognized: %s"), items[CMDindex]);
-        return false; // not for us
-    }
-
-    AddLog(LOG_LEVEL_INFO,PSTR("Dali value %d on address %d"), value, DALIaddr);
-    sendDaliData(DALIaddr, level);
-
-    return true;
-}
-
-bool DaliJsonParse()
-{
-    bool served = false;
-    JsonParser parser((char *)XdrvMailbox.data);
-    JsonParserObject root = parser.getRootObject();
-    if (root)
-    {
-        int DALIindex = 0;
-        int ADRindex = 0;
-        int8_t DALIdim = -1;
-        uint8_t DALIaddr = BROADCAST_DP;
-
-        JsonParserToken val = root[PSTR("cmd")];
-        if (val)
-        {
-            uint8_t cmd = val.getUInt();
-            val = root[PSTR("addr")];
-            if (val)
-            {
-                uint8_t addr = val.getUInt();
-                AddLog(LOG_LEVEL_DEBUG, PSTR("DLI: cmd = %d, addr = %d"), cmd, addr);
-                sendDaliData(addr, cmd);
-                return true;
-            }
-            else
-            {
-                return false;
-            }
-        }
-        val = root[PSTR("addr")];
-        if (val)
-        {
-            uint8_t addr = val.getUInt();
-            if ((addr >= 0) && (addr < 64))
-                DALIaddr = addr  << 1;
-        }
-        val = root[PSTR("dim")];
-        if (val)
-        {
-            uint8_t dim = val.getUInt();
-            if (dim < 255)
-                DALIdim = dim;
-        }
-
-        sendDaliData(DALIaddr, DALIdim);
-        served = true;
-    }
-    
-    return served;
-}
-
-/*********************************************************************************************\
- * Interface
-\*********************************************************************************************/
-
-bool Xdrv89(uint32_t function)
-{
-    bool result = false;
-
-    if (FUNC_INIT == function)
-    {
-        DaliPreInit();
-    }
-    else if (Dali)
-    {
-        switch (function)
-        {
-        case FUNC_MQTT_DATA:
-            result = DaliMqtt();
-            break;
-        case FUNC_COMMAND:
-            result = DaliCmd();
-            break;
-        case FUNC_ACTIVE:
-            result = true;
-            break;
-        }
-    }
-    return result;
-}
-
-#endif  // USE_DALI
-#endif  // ESP32
\ No newline at end of file