diff --git a/sonoff/_changelog.ino b/sonoff/_changelog.ino
index ede627571..b2badf70f 100644
--- a/sonoff/_changelog.ino
+++ b/sonoff/_changelog.ino
@@ -2,6 +2,7 @@
* 6.6.0.20 20191018
* Add command SetOption65 0/1 to disable (1) fast power cycle detection fixing unwanted brownout trigger
* Add absolute PowerDelta using command PowerDelta 101..32000 where 101 = 101-100 = 1W, 202 = 202-100 = 102W (#5901)
+ * Add support for EX-Store WiFi Dimmer V4 (#5856)
*
* 6.6.0.19 20191018
* Replace obsolete xsns_23_sdm120 with xnrg_08_sdm120 and consolidate define USE_SDM120
diff --git a/sonoff/language/bg-BG.h b/sonoff/language/bg-BG.h
index 29e932cfc..a24e95d70 100644
--- a/sonoff/language/bg-BG.h
+++ b/sonoff/language/bg-BG.h
@@ -626,6 +626,7 @@
#define D_SENSOR_SM2135_CLK "SM2135 Clk"
#define D_SENSOR_SM2135_DAT "SM2135 Dat"
#define D_SENSOR_DEEPSLEEP "DeepSleep"
+#define D_SENSOR_EXS_MCU_RESET "EXS Reset"
// Units
#define D_UNIT_AMPERE "A"
diff --git a/sonoff/language/cs-CZ.h b/sonoff/language/cs-CZ.h
index 948853f3d..31b23dac9 100644
--- a/sonoff/language/cs-CZ.h
+++ b/sonoff/language/cs-CZ.h
@@ -626,6 +626,7 @@
#define D_SENSOR_SM2135_CLK "SM2135 Clk"
#define D_SENSOR_SM2135_DAT "SM2135 Dat"
#define D_SENSOR_DEEPSLEEP "DeepSleep"
+#define D_SENSOR_EXS_MCU_RESET "EXS Reset"
// Units
#define D_UNIT_AMPERE "A"
diff --git a/sonoff/language/de-DE.h b/sonoff/language/de-DE.h
index 2627e3986..849d356af 100644
--- a/sonoff/language/de-DE.h
+++ b/sonoff/language/de-DE.h
@@ -626,6 +626,7 @@
#define D_SENSOR_SM2135_CLK "SM2135 Clk"
#define D_SENSOR_SM2135_DAT "SM2135 Dat"
#define D_SENSOR_DEEPSLEEP "DeepSleep"
+#define D_SENSOR_EXS_MCU_RESET "EXS Reset"
// Units
#define D_UNIT_AMPERE "A"
diff --git a/sonoff/language/el-GR.h b/sonoff/language/el-GR.h
index 7dc1bdad1..325558b83 100644
--- a/sonoff/language/el-GR.h
+++ b/sonoff/language/el-GR.h
@@ -626,6 +626,7 @@
#define D_SENSOR_SM2135_CLK "SM2135 Clk"
#define D_SENSOR_SM2135_DAT "SM2135 Dat"
#define D_SENSOR_DEEPSLEEP "DeepSleep"
+#define D_SENSOR_EXS_MCU_RESET "EXS Reset"
// Units
#define D_UNIT_AMPERE "A"
diff --git a/sonoff/language/en-GB.h b/sonoff/language/en-GB.h
index 1ce9f12b9..5cee90b5c 100644
--- a/sonoff/language/en-GB.h
+++ b/sonoff/language/en-GB.h
@@ -626,6 +626,7 @@
#define D_SENSOR_SM2135_CLK "SM2135 Clk"
#define D_SENSOR_SM2135_DAT "SM2135 Dat"
#define D_SENSOR_DEEPSLEEP "DeepSleep"
+#define D_SENSOR_EXS_MCU_RESET "EXS Reset"
// Units
#define D_UNIT_AMPERE "A"
diff --git a/sonoff/language/es-ES.h b/sonoff/language/es-ES.h
index f9ce932a3..f5f31ffc6 100644
--- a/sonoff/language/es-ES.h
+++ b/sonoff/language/es-ES.h
@@ -626,6 +626,7 @@
#define D_SENSOR_SM2135_CLK "SM2135 Clk"
#define D_SENSOR_SM2135_DAT "SM2135 Dat"
#define D_SENSOR_DEEPSLEEP "DeepSleep"
+#define D_SENSOR_EXS_MCU_RESET "EXS Reset"
// Units
#define D_UNIT_AMPERE "A"
diff --git a/sonoff/language/fr-FR.h b/sonoff/language/fr-FR.h
index a0cee0290..b52156447 100644
--- a/sonoff/language/fr-FR.h
+++ b/sonoff/language/fr-FR.h
@@ -626,6 +626,7 @@
#define D_SENSOR_SM2135_CLK "SM2135 Clk"
#define D_SENSOR_SM2135_DAT "SM2135 Dat"
#define D_SENSOR_DEEPSLEEP "DeepSleep"
+#define D_SENSOR_EXS_MCU_RESET "EXS Reset"
// Units
#define D_UNIT_AMPERE "A"
diff --git a/sonoff/language/he-HE.h b/sonoff/language/he-HE.h
index c609b5ac6..52c9d8cf9 100644
--- a/sonoff/language/he-HE.h
+++ b/sonoff/language/he-HE.h
@@ -626,6 +626,7 @@
#define D_SENSOR_SM2135_CLK "SM2135 Clk"
#define D_SENSOR_SM2135_DAT "SM2135 Dat"
#define D_SENSOR_DEEPSLEEP "DeepSleep"
+#define D_SENSOR_EXS_MCU_RESET "EXS Reset"
// Units
#define D_UNIT_AMPERE "A"
diff --git a/sonoff/language/hu-HU.h b/sonoff/language/hu-HU.h
index b014b531a..635756447 100644
--- a/sonoff/language/hu-HU.h
+++ b/sonoff/language/hu-HU.h
@@ -626,6 +626,7 @@
#define D_SENSOR_SM2135_CLK "SM2135 Clk"
#define D_SENSOR_SM2135_DAT "SM2135 Dat"
#define D_SENSOR_DEEPSLEEP "DeepSleep"
+#define D_SENSOR_EXS_MCU_RESET "EXS Reset"
// Units
#define D_UNIT_AMPERE "A"
diff --git a/sonoff/language/it-IT.h b/sonoff/language/it-IT.h
index 46ab48e52..7a35a4dfc 100644
--- a/sonoff/language/it-IT.h
+++ b/sonoff/language/it-IT.h
@@ -626,6 +626,7 @@
#define D_SENSOR_SM2135_CLK "SM2135 Clk"
#define D_SENSOR_SM2135_DAT "SM2135 Dat"
#define D_SENSOR_DEEPSLEEP "DeepSleep"
+#define D_SENSOR_EXS_MCU_RESET "EXS Reset"
// Units
#define D_UNIT_AMPERE "A"
diff --git a/sonoff/language/ko-KO.h b/sonoff/language/ko-KO.h
index 6da901543..6ae84f9a7 100644
--- a/sonoff/language/ko-KO.h
+++ b/sonoff/language/ko-KO.h
@@ -626,6 +626,7 @@
#define D_SENSOR_SM2135_CLK "SM2135 Clk"
#define D_SENSOR_SM2135_DAT "SM2135 Dat"
#define D_SENSOR_DEEPSLEEP "DeepSleep"
+#define D_SENSOR_EXS_MCU_RESET "EXS Reset"
// Units
#define D_UNIT_AMPERE "A"
diff --git a/sonoff/language/nl-NL.h b/sonoff/language/nl-NL.h
index 728b5249e..fd94dbe09 100644
--- a/sonoff/language/nl-NL.h
+++ b/sonoff/language/nl-NL.h
@@ -626,6 +626,7 @@
#define D_SENSOR_SM2135_CLK "SM2135 Clk"
#define D_SENSOR_SM2135_DAT "SM2135 Dat"
#define D_SENSOR_DEEPSLEEP "DeepSleep"
+#define D_SENSOR_EXS_MCU_RESET "EXS Reset"
// Units
#define D_UNIT_AMPERE "A"
diff --git a/sonoff/language/pl-PL.h b/sonoff/language/pl-PL.h
index a1610bef0..b871a18ea 100644
--- a/sonoff/language/pl-PL.h
+++ b/sonoff/language/pl-PL.h
@@ -626,6 +626,7 @@
#define D_SENSOR_SM2135_CLK "SM2135 Clk"
#define D_SENSOR_SM2135_DAT "SM2135 Dat"
#define D_SENSOR_DEEPSLEEP "DeepSleep"
+#define D_SENSOR_EXS_MCU_RESET "EXS Reset"
// Units
#define D_UNIT_AMPERE "A"
diff --git a/sonoff/language/pt-BR.h b/sonoff/language/pt-BR.h
index 737e785a4..b4048fe8c 100644
--- a/sonoff/language/pt-BR.h
+++ b/sonoff/language/pt-BR.h
@@ -626,6 +626,7 @@
#define D_SENSOR_SM2135_CLK "SM2135 Clk"
#define D_SENSOR_SM2135_DAT "SM2135 Dat"
#define D_SENSOR_DEEPSLEEP "DeepSleep"
+#define D_SENSOR_EXS_MCU_RESET "EXS Reset"
// Units
#define D_UNIT_AMPERE "A"
diff --git a/sonoff/language/pt-PT.h b/sonoff/language/pt-PT.h
index f6f106bce..80ac976b7 100644
--- a/sonoff/language/pt-PT.h
+++ b/sonoff/language/pt-PT.h
@@ -626,6 +626,7 @@
#define D_SENSOR_SM2135_CLK "SM2135 Clk"
#define D_SENSOR_SM2135_DAT "SM2135 Dat"
#define D_SENSOR_DEEPSLEEP "DeepSleep"
+#define D_SENSOR_EXS_MCU_RESET "EXS Reset"
// Units
#define D_UNIT_AMPERE "A"
diff --git a/sonoff/language/ru-RU.h b/sonoff/language/ru-RU.h
index c03ae5feb..543d8a4f1 100644
--- a/sonoff/language/ru-RU.h
+++ b/sonoff/language/ru-RU.h
@@ -626,6 +626,7 @@
#define D_SENSOR_SM2135_CLK "SM2135 Clk"
#define D_SENSOR_SM2135_DAT "SM2135 Dat"
#define D_SENSOR_DEEPSLEEP "DeepSleep"
+#define D_SENSOR_EXS_MCU_RESET "EXS Reset"
// Units
#define D_UNIT_AMPERE "А"
diff --git a/sonoff/language/sk-SK.h b/sonoff/language/sk-SK.h
index 6ca306f54..207764960 100644
--- a/sonoff/language/sk-SK.h
+++ b/sonoff/language/sk-SK.h
@@ -626,6 +626,7 @@
#define D_SENSOR_SM2135_CLK "SM2135 Clk"
#define D_SENSOR_SM2135_DAT "SM2135 Dat"
#define D_SENSOR_DEEPSLEEP "DeepSleep"
+#define D_SENSOR_EXS_MCU_RESET "EXS Reset"
// Units
#define D_UNIT_AMPERE "A"
diff --git a/sonoff/language/sv-SE.h b/sonoff/language/sv-SE.h
index f03aa1539..79cf9dedc 100644
--- a/sonoff/language/sv-SE.h
+++ b/sonoff/language/sv-SE.h
@@ -626,6 +626,7 @@
#define D_SENSOR_SM2135_CLK "SM2135 Clk"
#define D_SENSOR_SM2135_DAT "SM2135 Dat"
#define D_SENSOR_DEEPSLEEP "DeepSleep"
+#define D_SENSOR_EXS_MCU_RESET "EXS Reset"
// Units
#define D_UNIT_AMPERE "A"
diff --git a/sonoff/language/tr-TR.h b/sonoff/language/tr-TR.h
index 39881132e..f28f951d9 100755
--- a/sonoff/language/tr-TR.h
+++ b/sonoff/language/tr-TR.h
@@ -626,6 +626,7 @@
#define D_SENSOR_SM2135_CLK "SM2135 Clk"
#define D_SENSOR_SM2135_DAT "SM2135 Dat"
#define D_SENSOR_DEEPSLEEP "DeepSleep"
+#define D_SENSOR_EXS_MCU_RESET "EXS Reset"
// Units
#define D_UNIT_AMPERE "A"
diff --git a/sonoff/language/uk-UK.h b/sonoff/language/uk-UK.h
index 02ac21a24..0ea4127c7 100644
--- a/sonoff/language/uk-UK.h
+++ b/sonoff/language/uk-UK.h
@@ -626,6 +626,7 @@
#define D_SENSOR_SM2135_CLK "SM2135 Clk"
#define D_SENSOR_SM2135_DAT "SM2135 Dat"
#define D_SENSOR_DEEPSLEEP "DeepSleep"
+#define D_SENSOR_EXS_MCU_RESET "EXS Reset"
// Units
#define D_UNIT_AMPERE "А"
diff --git a/sonoff/language/zh-CN.h b/sonoff/language/zh-CN.h
index 264964b2e..22ac1f9cf 100644
--- a/sonoff/language/zh-CN.h
+++ b/sonoff/language/zh-CN.h
@@ -626,6 +626,7 @@
#define D_SENSOR_SM2135_CLK "SM2135 Clk"
#define D_SENSOR_SM2135_DAT "SM2135 Dat"
#define D_SENSOR_DEEPSLEEP "DeepSleep"
+#define D_SENSOR_EXS_MCU_RESET "EXS Reset"
// Units
#define D_UNIT_AMPERE "安"
diff --git a/sonoff/language/zh-TW.h b/sonoff/language/zh-TW.h
index 3c3afd739..437656457 100644
--- a/sonoff/language/zh-TW.h
+++ b/sonoff/language/zh-TW.h
@@ -626,6 +626,7 @@
#define D_SENSOR_SM2135_CLK "SM2135 Clk"
#define D_SENSOR_SM2135_DAT "SM2135 Dat"
#define D_SENSOR_DEEPSLEEP "DeepSleep"
+#define D_SENSOR_EXS_MCU_RESET "EXS Reset"
// Units
#define D_UNIT_AMPERE "安"
diff --git a/sonoff/my_user_config.h b/sonoff/my_user_config.h
index ad25e3b3a..33a3f9208 100644
--- a/sonoff/my_user_config.h
+++ b/sonoff/my_user_config.h
@@ -321,6 +321,8 @@
#define USE_ARILUX_RF // Add support for Arilux RF remote controller (+0k8 code, 252 iram (non 2.3.0))
//#define USE_SHUTTER // Add Shutter support for up to 4 shutter with different motortypes (+6k code)
//#define USE_DEEPSLEEP // Add support for deepsleep (+1k code)
+//#define USE_EXS_DIMMER // Add support for ES-Store WiFi Dimmer (+1k5 code)
+// #define EXS_MCU_CMNDS // Add command to send MCU commands (+0k8 code)
// -- Optional light modules ----------------------
#define USE_WS2812 // WS2812 Led string using library NeoPixelBus (+5k code, +1k mem, 232 iram) - Disable by //
diff --git a/sonoff/sonoff_post.h b/sonoff/sonoff_post.h
index 1446d8946..8e0e95cf4 100644
--- a/sonoff/sonoff_post.h
+++ b/sonoff/sonoff_post.h
@@ -101,6 +101,7 @@ char* ToHex_P(const unsigned char * in, size_t insz, char * out, size_t outsz, c
#define USE_ARILUX_RF // Add support for Arilux RF remote controller (+0k8 code, 252 iram (non 2.3.0))
//#define USE_SHUTTER // Add Shutter support for up to 4 shutter with different motortypes (+6k code)
//#define USE_DEEPSLEEP // Add support for deepsleep (+1k code)
+//#define USE_EXS_DIMMER // Add support for EX-Store WiFi Dimmer
// -- Optional light modules ----------------------
#define USE_LIGHT // Add Dimmer/Light support
@@ -256,6 +257,7 @@ char* ToHex_P(const unsigned char * in, size_t insz, char * out, size_t outsz, c
#undef USE_ARILUX_RF // Disable support for Arilux RF remote controller
#undef USE_SHUTTER // Disable Shutter support for up to 4 shutter with different motortypes (+6k code)
#undef USE_DEEPSLEEP // Disable support for deepsleep (+1k code)
+#undef USE_EXS_DIMMER // Disable support for EX-Store WiFi Dimmer
// -- Optional light modules ----------------------
//#undef USE_LIGHT // Disable Dimmer/Light support
@@ -364,6 +366,7 @@ char* ToHex_P(const unsigned char * in, size_t insz, char * out, size_t outsz, c
#undef USE_ARILUX_RF // Disable support for Arilux RF remote controller
#undef USE_SHUTTER // Disable Shutter support for up to 4 shutter with different motortypes (+6k code)
#undef USE_DEEPSLEEP // Disable support for deepsleep (+1k code)
+#undef USE_EXS_DIMMER // Disable support for EX-Store WiFi Dimmer
#undef USE_ENERGY_SENSOR // Disable energy sensors (-14k code)
#undef USE_PZEM004T // Disable PZEM004T energy sensor
@@ -435,6 +438,7 @@ char* ToHex_P(const unsigned char * in, size_t insz, char * out, size_t outsz, c
#undef USE_ARILUX_RF // Disable support for Arilux RF remote controller
#undef USE_SHUTTER // Disable Shutter support for up to 4 shutter with different motortypes (+6k code)
#undef USE_DEEPSLEEP // Disable support for deepsleep (+1k code)
+#undef USE_EXS_DIMMER // Disable support for EX-Store WiFi Dimmer
// -- Optional light modules ----------------------
//#undef USE_LIGHT // Also disable all Dimmer/Light support
@@ -532,6 +536,7 @@ char* ToHex_P(const unsigned char * in, size_t insz, char * out, size_t outsz, c
#undef USE_ARILUX_RF // Disable support for Arilux RF remote controller
#undef USE_SHUTTER // Disable Shutter support for up to 4 shutter with different motortypes (+6k code)
#undef USE_DEEPSLEEP // Disable support for deepsleep (+1k code)
+#undef USE_EXS_DIMMER // Disable support for EX-Store WiFi Dimmer
// -- Optional light modules ----------------------
//#undef USE_LIGHT // Also disable all Dimmer/Light support
@@ -631,6 +636,7 @@ char* ToHex_P(const unsigned char * in, size_t insz, char * out, size_t outsz, c
#undef USE_ARILUX_RF // Disable support for Arilux RF remote controller
#undef USE_SHUTTER // Disable Shutter support for up to 4 shutter with different motortypes (+6k code)
#undef USE_DEEPSLEEP // Disable support for deepsleep (+1k code)
+#undef USE_EXS_DIMMER // Disable support for EX-Store WiFi Dimmer
// -- Optional light modules ----------------------
#undef USE_LIGHT // Disable support for lights
diff --git a/sonoff/sonoff_template.h b/sonoff/sonoff_template.h
index 2a3ddcaca..c6ed63361 100644
--- a/sonoff/sonoff_template.h
+++ b/sonoff/sonoff_template.h
@@ -207,6 +207,7 @@ enum UserSelectablePins {
GPIO_SM2135_CLK, // SM2135 Clk
GPIO_SM2135_DAT, // SM2135 Dat
GPIO_DEEPSLEEP, // Kill switch for deepsleep
+ GPIO_EXS_MCU_RESET, // EXS MCU Reset
GPIO_SENSOR_END };
// Programmer selectable GPIO functionality
@@ -284,7 +285,7 @@ const char kSensorNames[] PROGMEM =
D_SENSOR_DDS2382_TX "|" D_SENSOR_DDS2382_RX "|"
D_SENSOR_DDSU666_TX "|" D_SENSOR_DDSU666_RX "|"
D_SENSOR_SM2135_CLK "|" D_SENSOR_SM2135_DAT "|"
- D_SENSOR_DEEPSLEEP "|"
+ D_SENSOR_DEEPSLEEP "|" D_SENSOR_EXS_MCU_RESET "|"
;
const char kSensorNamesFixed[] PROGMEM =
@@ -392,6 +393,7 @@ enum SupportedModules {
SYF05,
SONOFF_L1,
SONOFF_IFAN03,
+ EXS_DIMMER,
MAXMODULE};
#define USER_MODULE 255
@@ -573,6 +575,9 @@ const uint8_t kGpioNiceList[] PROGMEM = {
GPIO_TUYA_TX, // Tuya Serial interface
GPIO_TUYA_RX, // Tuya Serial interface
#endif
+#ifdef USE_EXS_DIMMER
+ GPIO_EXS_MCU_RESET, // EXS MCU Reset
+#endif
#endif // USE_LIGHT
#if defined(USE_IR_REMOTE) || defined(USE_IR_REMOTE_FULL)
@@ -803,6 +808,9 @@ const uint8_t kModuleNiceList[] PROGMEM = {
#endif
#ifdef USE_PS_16_DZ
PS_16_DZ,
+#endif
+#ifdef USE_EXS_DIMMER
+ EXS_DIMMER,
#endif
H801, // Light Devices
MAGICHOME,
@@ -2122,6 +2130,27 @@ const mytmplt kModules[MAXMODULE] PROGMEM = {
GPIO_REL2, // GPIO14 WIFI_O1 Relay 2 (0 = Off, 1 = On) controlling the fan
GPIO_REL4, // GPIO15 WIFI_O3 Relay 4 (0 = Off, 1 = On) controlling the fan
0, 0
+ },
+ { "EXS Dimmer", // EXS_DIMMER - EX-Stroe WiFi Dimmer v4, two channel (ESP8266 w/ separate MCU dimmer)
+ // https://ex-store.de/2-Kanal-RS232-WiFi-WLan-Dimmer-Modul-V4-fuer-Unterputzmontage-230V-3A
+ // https://ex-store.de/2-Kanal-RS232-WiFi-WLan-Dimmer-Modul-V4-fuer-Unterputzmontage-230V-3A-ESP8266-V12-Stift-und-Buchsenleisten
+ 0,
+ GPIO_TXD, // GPIO01 MCU serial control
+ 0,
+ GPIO_RXD, // GPIO03 MCU serial control
+ GPIO_USER, // GPIO04
+ GPIO_USER, // GPIO05
+ // GPIO06 (SD_CLK Flash)
+ // GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT)
+ // GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT)
+ 0, // GPIO09 (SD_DATA2 Flash QIO or ESP8285)
+ 0, // GPIO10 (SD_DATA3 Flash QIO or ESP8285)
+ // GPIO11 (SD_CMD Flash)
+ GPIO_USER, // GPIO12
+ GPIO_EXS_MCU_RESET, // GPIO13 EXS MCU Reset
+ GPIO_USER, // GPIO14
+ 0, // GPIO15
+ 0, 0
}
};
diff --git a/sonoff/support_features.ino b/sonoff/support_features.ino
index 6ee9afe21..a159377d8 100644
--- a/sonoff/support_features.ino
+++ b/sonoff/support_features.ino
@@ -472,9 +472,11 @@ void GetFeatures(void)
feature5 |= 0x00002000; // xdrv_06_snfbridge.ino
#endif
#ifdef USE_SONOFF_L1
- feature5 |= 0x00004000;
+ feature5 |= 0x00004000; // xlgt_05_sonoff_l1.ino
+#endif
+#ifdef USE_EXS_DIMMER
+ feature5 |= 0x00008000; // xdrv_30_exs_dimmer.ino
#endif
-// feature5 |= 0x00008000;
// feature5 |= 0x00010000;
// feature5 |= 0x00020000;
diff --git a/sonoff/xdrv_30_exs_dimmer.ino b/sonoff/xdrv_30_exs_dimmer.ino
new file mode 100644
index 000000000..63b489b56
--- /dev/null
+++ b/sonoff/xdrv_30_exs_dimmer.ino
@@ -0,0 +1,624 @@
+/*
+ xdrv_30_exs_dimmer.ino - ex-store dimmer support for Sonoff-Tasmota
+
+ Copyright (C) 2019 Andreas Schultz
+
+ 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 .
+*/
+
+#ifdef USE_LIGHT
+#ifdef USE_EXS_DIMMER
+/*********************************************************************************************\
+ * EX-Store WiFi Dimmer V4
+ * https://ex-store.de/2-Kanal-RS232-WiFi-WLan-Dimmer-Modul-V4-fuer-Unterputzmontage-230V-3A
+ * https://ex-store.de/2-Kanal-RS232-WiFi-WLan-Dimmer-Modul-V4-fuer-Unterputzmontage-230V-3A-ESP8266-V12-Stift-und-Buchsenleisten
+\*********************************************************************************************/
+//#define EXS_DEBUG
+
+#define XDRV_30 30
+
+#define EXS_GATE_1_ON 0x20
+#define EXS_GATE_1_OFF 0x21
+#define EXS_DIMM_1_ON 0x22
+#define EXS_DIMM_1_OFF 0x23
+#define EXS_DIMM_1_TBL 0x24
+#define EXS_DIMM_1_VAL 0x25
+#define EXS_GATE_2_ON 0x30
+#define EXS_GATE_2_OFF 0x31
+#define EXS_DIMM_2_ON 0x32
+#define EXS_DIMM_2_OFF 0x33
+#define EXS_DIMM_2_TBL 0x34
+#define EXS_DIMM_2_VAL 0x35
+#define EXS_GATES_ON 0x40
+#define EXS_GATES_OFF 0x41
+#define EXS_DIMMS_ON 0x50
+#define EXS_DIMMS_OFF 0x51
+#define EXS_CH_LOCK 0x60
+#define EXS_GET_VALUES 0xFA
+#define EXS_WRITE_EE 0xFC
+#define EXS_READ_EE 0xFD
+#define EXS_GET_VERSION 0xFE
+#define EXS_RESET 0xFF
+
+#define EXS_BUFFER_SIZE 256
+#define EXS_ACK_TIMEOUT 200 // 200 ms ACK timeout
+
+#include
+
+TasmotaSerial *ExsSerial = nullptr;
+
+typedef struct
+{
+ uint8_t on = 0;
+ uint8_t bright_tbl = 0;
+ uint8_t dimm = 0;
+ uint8_t impuls_start = 0;
+ uint32_t impuls_len = 0;
+} CHANNEL;
+
+typedef struct
+{
+ uint8_t version_major = 0;
+ uint8_t version_minor = 0;
+ CHANNEL channel[2];
+ uint8_t gate_lock = 0;
+} DIMMER;
+
+struct EXS
+{
+ uint8_t *buffer = nullptr; // Serial receive buffer
+ int byte_counter = 0; // Index in serial receive buffer
+ int cmd_status = 0;
+ uint8_t power = 0;
+ uint8_t dimm[2] = {0, 0};
+ DIMMER dimmer;
+} Exs;
+
+/*
+ * Internal Functions
+ */
+
+uint8_t crc8(const uint8_t *p, uint8_t len)
+{
+ const uint8_t table[] = {
+ 0x00, 0x07, 0x0E, 0x09, 0x1C, 0x1B, 0x12, 0x15,
+ 0x38, 0x3F, 0x36, 0x31, 0x24, 0x23, 0x2A, 0x2D};
+
+ const uint8_t table_rev[] = {
+ 0x00, 0x70, 0xE0, 0x90, 0xC1, 0xB1, 0x21, 0x51,
+ 0x83, 0xF3, 0x63, 0x13, 0x42, 0x32, 0xA2, 0xD2};
+
+ uint8_t offset;
+ uint8_t temp, crc8_temp;
+ uint8_t crc8 = 0;
+
+ for (int i = 0; i < len; i++)
+ {
+ temp = *(p + i);
+ offset = temp ^ crc8;
+ offset >>= 4;
+ crc8_temp = crc8 & 0x0f;
+ crc8 = crc8_temp ^ table_rev[offset];
+ offset = crc8 ^ temp;
+ offset &= 0x0f;
+ crc8_temp = crc8 & 0xf0;
+ crc8 = crc8_temp ^ table[offset];
+ }
+ return crc8 ^ 0x55;
+}
+
+void ExsSerialSend(const uint8_t data[] = nullptr, uint16_t len = 0)
+{
+ int retries = 3;
+ char rc;
+
+#ifdef EXS_DEBUG
+ snprintf_P(log_data, sizeof(log_data), PSTR("EXS: Tx Packet: \""));
+ for (uint32_t i = 0; i < len; i++)
+ {
+ snprintf_P(log_data, sizeof(log_data), PSTR("%s%02x"), log_data, data[i]);
+ }
+ snprintf_P(log_data, sizeof(log_data), PSTR("%s\""), log_data);
+ AddLog(LOG_LEVEL_DEBUG_MORE);
+#endif
+
+ while (retries)
+ {
+ retries--;
+
+ ExsSerial->write(data, len);
+ ExsSerial->flush();
+
+ // wait for any response
+ uint32_t snd_time = millis();
+ while ((TimePassedSince(snd_time) < EXS_ACK_TIMEOUT) &&
+ (!ExsSerial->available()))
+ ;
+
+ if (!ExsSerial->available())
+ {
+ // timeout
+#ifdef EXS_DEBUG
+ AddLog_P(LOG_LEVEL_DEBUG, PSTR("ESX: serial send timeout"));
+#endif
+ continue;
+ }
+
+ rc = ExsSerial->read();
+ if (rc == 0xFF)
+ break;
+ }
+}
+
+void ExsSendCmd(uint8_t cmd, uint8_t value)
+{
+ uint8_t buffer[8];
+ uint16_t len;
+
+ buffer[0] = 0x7b;
+ buffer[3] = cmd;
+
+ switch (cmd)
+ {
+ case EXS_GATE_1_ON:
+ case EXS_GATE_1_OFF:
+ case EXS_DIMM_1_ON:
+ case EXS_DIMM_1_OFF:
+ case EXS_GATE_2_ON:
+ case EXS_GATE_2_OFF:
+ case EXS_DIMM_2_ON:
+ case EXS_DIMM_2_OFF:
+ case EXS_GATES_ON:
+ case EXS_GATES_OFF:
+ case EXS_DIMMS_ON:
+ case EXS_DIMMS_OFF:
+ case EXS_GET_VALUES:
+ case EXS_GET_VERSION:
+ case EXS_RESET:
+ buffer[2] = 1;
+ len = 4;
+ break;
+
+ case EXS_CH_LOCK:
+ case EXS_DIMM_1_TBL:
+ case EXS_DIMM_1_VAL:
+ case EXS_DIMM_2_TBL:
+ case EXS_DIMM_2_VAL:
+ buffer[2] = 2;
+ buffer[4] = value;
+ len = 5;
+ break;
+ }
+ buffer[1] = crc8(&buffer[3], buffer[2]);
+
+ ExsSerialSend(buffer, len);
+}
+
+uint8_t ExsSetPower(uint8_t device, uint8_t power)
+{
+ Exs.dimmer.channel[device].dimm = power;
+ ExsSendCmd(EXS_DIMM_1_ON + 0x10 * device + power ^ 1, 0);
+}
+
+uint8_t ExsSetBri(uint8_t device, uint8_t bri)
+{
+ Exs.dimmer.channel[device].bright_tbl = bri;
+ ExsSendCmd(EXS_DIMM_1_TBL + 0x10 * device, bri);
+}
+
+uint8_t ExsSyncState(uint8_t device)
+{
+#ifdef EXS_DEBUG
+ AddLog_P2(LOG_LEVEL_DEBUG, PSTR("EXS: Channel %d Power Want %d, Is %d"),
+ device, bitRead(Exs.power, device), Exs.dimmer.channel[device].dimm);
+ AddLog_P2(LOG_LEVEL_DEBUG, PSTR("EXS: Set Channel %d Brightness Want %d, Is %d"),
+ device, Exs.dimm[device], Exs.dimmer.channel[device].bright_tbl);
+#endif
+
+ if (bitRead(Exs.power, device) &&
+ Exs.dimm[device] != Exs.dimmer.channel[device].bright_tbl) {
+ ExsSetBri(device, Exs.dimm[device]);
+ }
+
+ if (!Exs.dimm[device]) {
+ Exs.dimmer.channel[device].dimm = 0;
+ } else if (Exs.dimmer.channel[device].dimm != bitRead(Exs.power, device)) {
+ ExsSetPower(device, bitRead(Exs.power, device));
+ }
+}
+
+bool ExsSyncState()
+{
+#ifdef EXS_DEBUG
+ AddLog_P2(LOG_LEVEL_DEBUG, PSTR("EXS: Serial %p, Cmd %d"), ExsSerial, Exs.cmd_status);
+#endif
+
+ if (!ExsSerial || Exs.cmd_status != 0)
+ return false;
+
+ ExsSyncState(0);
+ ExsSyncState(1);
+}
+
+static inline void ExsSetChannelV10(uint8_t channel, const uint8_t *buffer)
+{
+ Exs.dimmer.channel[channel].on = buffer[0];
+ Exs.dimmer.channel[channel].dimm = buffer[1];
+ Exs.dimmer.channel[channel].bright_tbl = 0;
+}
+
+static inline void ExsSetChannel(uint8_t channel, const uint8_t *buffer)
+{
+ Exs.dimmer.channel[channel].on = buffer[0];
+ Exs.dimmer.channel[channel].dimm = buffer[1];
+ Exs.dimmer.channel[channel].bright_tbl = buffer[2];
+}
+
+void ExsDebugState()
+{
+#ifdef EXS_DEBUG
+ AddLog_P2(LOG_LEVEL_DEBUG, PSTR("EXS: MCU v%d.%d, c0: On:%d,Dim:%d,Tbl:%d(%d%%), c1: On:%d,Dim:%d,Tbl:%d(%d%%), ChLock: %d"),
+ Exs.dimmer.version_major, Exs.dimmer.version_minor,
+ Exs.dimmer.channel[0].on, Exs.dimmer.channel[0].dimm,
+ Exs.dimmer.channel[0].bright_tbl,
+ changeUIntScale(Exs.dimmer.channel[0].bright_tbl, 0, 255, 0, 100),
+ Exs.dimmer.channel[1].on, Exs.dimmer.channel[1].dimm,
+ Exs.dimmer.channel[1].bright_tbl,
+ changeUIntScale(Exs.dimmer.channel[1].bright_tbl, 0, 255, 0, 100),
+ Exs.dimmer.gate_lock);
+#endif
+}
+
+void ExsPacketProcess(void)
+{
+ uint8_t len = Exs.buffer[1];
+ uint8_t cmd = Exs.buffer[2];
+
+ switch (cmd)
+ {
+ case EXS_GET_VALUES:
+ if (len > 9)
+ {
+ Exs.dimmer.version_major = Exs.buffer[3];
+ Exs.dimmer.version_minor = Exs.buffer[4];
+ ExsSetChannel(0, &Exs.buffer[5]);
+ ExsSetChannel(1, &Exs.buffer[8]);
+ Exs.dimmer.gate_lock = Exs.buffer[11];
+ }
+ else
+ {
+ Exs.dimmer.version_major = 1;
+ Exs.dimmer.version_minor = 0;
+ ExsSetChannel(0, &Exs.buffer[3]);
+ ExsSetChannel(1, &Exs.buffer[5]);
+ Exs.dimmer.gate_lock = Exs.buffer[8];
+ }
+
+ ExsDebugState();
+ ExsSyncState();
+ ExsDebugState();
+ break;
+ default:
+ break;
+ }
+}
+
+/*
+ * API Functions
+ */
+bool ExsModuleSelected(void)
+{
+ Settings.light_correction = 0;
+ Settings.flag.mqtt_serial = 0;
+ Settings.flag3.pwm_multi_channels = 1;
+ SetSeriallog(LOG_LEVEL_NONE);
+
+ devices_present = +2;
+ light_type = LT_SERIAL2;
+ return true;
+}
+
+bool ExsSetChannels(void)
+{
+#ifdef EXS_DEBUG
+ snprintf_P(log_data, sizeof(log_data), PSTR("EXS: SetChannels: \""));
+ for (int i = 0; i < XdrvMailbox.data_len; i++)
+ {
+ snprintf_P(log_data, sizeof(log_data), PSTR("%s%02x"), log_data, ((uint8_t *)XdrvMailbox.data)[i]);
+ }
+ snprintf_P(log_data, sizeof(log_data), PSTR("%s\""), log_data);
+ AddLog(LOG_LEVEL_DEBUG_MORE);
+#endif
+
+ Exs.dimm[0] = ((uint8_t *)XdrvMailbox.data)[0];
+ Exs.dimm[1] = ((uint8_t *)XdrvMailbox.data)[1];
+ return ExsSyncState();
+}
+
+bool ExsSetPower(void)
+{
+ AddLog_P2(LOG_LEVEL_INFO, PSTR("EXS: Set Power, Device %d, Power 0x%02x"),
+ active_device, XdrvMailbox.index);
+
+ Exs.power = XdrvMailbox.index;
+ return ExsSyncState();
+}
+
+void EsxMcuStart(void)
+{
+ int retries = 3;
+
+#ifdef EXS_DEBUG
+ AddLog_P2(LOG_LEVEL_DEBUG, PSTR("EXS: Request MCU configuration, PIN %d to Low"), pin[GPIO_EXS_MCU_RESET]);
+#endif
+
+ pinMode(pin[GPIO_EXS_MCU_RESET], OUTPUT);
+ digitalWrite(pin[GPIO_EXS_MCU_RESET], LOW);
+
+ delay(1); // wait 1ms fot the MCU to come online
+
+ while (ExsSerial->available())
+ {
+ // clear in the receive buffer
+ ExsSerial->read();
+ }
+}
+
+void ExsInit(void)
+{
+#ifdef EXS_DEBUG
+ AddLog_P2(LOG_LEVEL_INFO, PSTR("EXS: Starting Tx %d Rx %d"), pin[GPIO_TXD], pin[GPIO_RXD]);
+#endif
+
+ Exs.buffer = (uint8_t *)malloc(EXS_BUFFER_SIZE);
+ if (Exs.buffer != nullptr)
+ {
+ ExsSerial = new TasmotaSerial(pin[GPIO_RXD], pin[GPIO_TXD], 2);
+ if (ExsSerial->begin(9600))
+ {
+ if (ExsSerial->hardwareSerial())
+ {
+ ClaimSerial();
+ }
+ ExsSerial->flush();
+ EsxMcuStart();
+ ExsSendCmd(EXS_CH_LOCK, 0);
+ ExsSendCmd(EXS_GET_VALUES, 0);
+ }
+ }
+}
+
+void ExsSerialInput(void)
+{
+ while (ExsSerial->available())
+ {
+ yield();
+ uint8_t serial_in_byte = ExsSerial->read();
+
+ AddLog_P2(LOG_LEVEL_INFO, PSTR("EXS: Serial In Byte 0x%02x"), serial_in_byte);
+
+ if (Exs.cmd_status == 0 &&
+ serial_in_byte == 0x7B)
+ {
+ Exs.cmd_status = 1;
+ Exs.byte_counter = 0;
+ }
+ else if (Exs.byte_counter >= EXS_BUFFER_SIZE)
+ {
+ Exs.cmd_status = 0;
+ }
+ else if (Exs.cmd_status == 1)
+ {
+ Exs.buffer[Exs.byte_counter++] = serial_in_byte;
+
+ if (Exs.byte_counter > 2 && Exs.byte_counter == Exs.buffer[1] + 2)
+ {
+ uint8_t crc = crc8(&Exs.buffer[2], Exs.buffer[1]);
+
+ // all read
+ Exs.cmd_status = 0;
+
+#ifdef EXS_DEBUG
+ snprintf_P(log_data, sizeof(log_data), PSTR("EXS: RX Packet: \""));
+ for (uint32_t i = 0; i < Exs.byte_counter; i++)
+ {
+ snprintf_P(log_data, sizeof(log_data), PSTR("%s%02x"), log_data, Exs.buffer[i]);
+ }
+ snprintf_P(log_data, sizeof(log_data), PSTR("%s\", CRC: 0x%02x"), log_data, crc);
+ AddLog(LOG_LEVEL_DEBUG_MORE);
+#endif
+
+ if (Exs.buffer[0] == crc)
+ {
+ ExsSerial->write(0xFF); //send ACK
+ ExsPacketProcess();
+ }
+ else
+ {
+ ExsSerial->write(0x00); //send NO-ACK
+ }
+
+ }
+ }
+ }
+}
+
+/*
+ * Commands
+ */
+
+#ifdef EXS_MCU_CMNDS
+
+#define D_PRFX_EXS "Exs"
+#define D_CMND_EXS_GATE "Gate"
+#define D_CMND_EXS_DIMM "Dimm"
+#define D_CMND_EXS_DIMM_TBL "DimmTbl"
+#define D_CMND_EXS_DIMM_VAL "DimmVal"
+#define D_CMND_EXS_GATES "Gates"
+#define D_CMND_EXS_DIMMS "Dimms"
+#define D_CMND_EXS_CH_LOCK "ChLock"
+#define D_CMND_EXS_STATE "State"
+
+const char kExsCommands[] PROGMEM = D_PRFX_EXS "|"
+ D_CMND_EXS_GATE "|"
+ D_CMND_EXS_DIMM "|" D_CMND_EXS_DIMM_TBL "|" D_CMND_EXS_DIMM_VAL "|"
+ D_CMND_EXS_GATES "|" D_CMND_EXS_DIMMS "|" D_CMND_EXS_CH_LOCK "|"
+ D_CMND_EXS_STATE;
+
+void (* const ExsCommand[])(void) PROGMEM =
+ { &CmndExsGate,
+ &CmndExsDimm, &CmndExsDimmTbl, &CmndExsDimmVal,
+ &CmndExsGates, &CmndExsDimms, &CmndExsChLock,
+ &CmndExsState };
+
+void CmndExsGate(void)
+{
+ if ((XdrvMailbox.index == 1 || XdrvMailbox.index == 2) &&
+ (XdrvMailbox.payload == 0 || XdrvMailbox.payload == 1)) {
+ ExsSendCmd(EXS_GATE_1_ON + 0x10 * (XdrvMailbox.index - 1) +
+ XdrvMailbox.payload ^ 1, 0);
+ }
+ CmndExsState();
+}
+
+void CmndExsDimm(void)
+{
+ if ((XdrvMailbox.index == 1 || XdrvMailbox.index == 2) &&
+ (XdrvMailbox.payload == 0 || XdrvMailbox.payload == 1)) {
+ ExsSendCmd(EXS_DIMM_1_ON + 0x10 * (XdrvMailbox.index - 1) +
+ XdrvMailbox.payload ^ 1, 0);
+ }
+ CmndExsState();
+}
+
+void CmndExsDimmTbl(void)
+{
+ if ((XdrvMailbox.index == 1 || XdrvMailbox.index == 2) &&
+ (XdrvMailbox.payload > 0 || XdrvMailbox.payload <= 255)) {
+ ExsSendCmd(EXS_DIMM_1_TBL + 0x10 * (XdrvMailbox.index - 1),
+ XdrvMailbox.payload);
+ }
+ CmndExsState();
+}
+
+void CmndExsDimmVal(void)
+{
+ if ((XdrvMailbox.index == 1 || XdrvMailbox.index == 2) &&
+ (XdrvMailbox.payload > 0 || XdrvMailbox.payload <= 255)) {
+ ExsSendCmd(EXS_DIMM_1_VAL + 0x10 * (XdrvMailbox.index - 1),
+ XdrvMailbox.payload);
+ }
+ CmndExsState();
+}
+
+void CmndExsGates(void)
+{
+ if (XdrvMailbox.payload == 0 || XdrvMailbox.payload == 1) {
+ ExsSendCmd(EXS_GATES_ON + XdrvMailbox.payload ^ 1, 0);
+ }
+ CmndExsState();
+}
+
+void CmndExsDimms(void)
+{
+ if (XdrvMailbox.payload == 0 || XdrvMailbox.payload == 1) {
+ ExsSendCmd(EXS_DIMMS_ON + XdrvMailbox.payload ^ 1, 0);
+ }
+ CmndExsState();
+}
+
+void CmndExsChLock(void)
+{
+ if (XdrvMailbox.payload == 0 || XdrvMailbox.payload == 1) {
+ ExsSendCmd(EXS_CH_LOCK, XdrvMailbox.payload);
+ }
+ CmndExsState();
+}
+
+void CmndExsState(void)
+{
+ ExsSendCmd(EXS_GET_VALUES, 0);
+
+ // wait for data
+ uint32_t snd_time = millis();
+ while ((TimePassedSince(snd_time) < EXS_ACK_TIMEOUT) &&
+ (!ExsSerial->available()))
+ ;
+ ExsSerialInput();
+
+ Response_P(PSTR("{\"" D_CMND_EXS_STATE "\":{"));
+ ResponseAppend_P(PSTR("\"McuVersion\":\"%d.%d\","
+ "\"Channels\":["),
+ Exs.dimmer.version_major, Exs.dimmer.version_minor);
+
+ for (uint32_t i = 0; i < 2; i++) {
+ if (i != 0) {
+ ResponseAppend_P(PSTR(","));
+ }
+ ResponseAppend_P(PSTR("{\"On\":\"%d\","
+ "\"BrightProz\":\"%d\","
+ "\"BrightTab\":\"%d\","
+ "\"Dimm\":\"%d\"}"),
+ Exs.dimmer.channel[i].on,
+ changeUIntScale(Exs.dimmer.channel[i].bright_tbl, 0, 255, 0, 100),
+ Exs.dimmer.channel[i].bright_tbl,
+ Exs.dimmer.channel[i].dimm);
+ }
+ ResponseAppend_P(PSTR("],"));
+ ResponseAppend_P(PSTR("\"GateLock\":\"%d\""), Exs.dimmer.gate_lock);
+ ResponseJsonEndEnd();
+}
+
+#endif
+
+/*
+ * Interface
+ */
+
+bool Xdrv30(uint8_t function)
+{
+ bool result = false;
+
+ if (EXS_DIMMER == my_module_type)
+ {
+ switch (function)
+ {
+ case FUNC_LOOP:
+ if (ExsSerial)
+ ExsSerialInput();
+ break;
+ case FUNC_MODULE_INIT:
+ result = ExsModuleSelected();
+ break;
+ case FUNC_INIT:
+ ExsInit();
+ break;
+ case FUNC_SET_DEVICE_POWER:
+ result = ExsSetPower();
+ break;
+ case FUNC_SET_CHANNELS:
+ result = ExsSetChannels();
+ break;
+#ifdef EXS_MCU_CMNDS
+ case FUNC_COMMAND:
+ result = DecodeCommand(kExsCommands, ExsCommand);
+ break;
+#endif
+ }
+ }
+ return result;
+}
+
+#endif // USE_EXS_DIMMER
+#endif // USE_LIGHT
\ No newline at end of file
diff --git a/tools/decode-status.py b/tools/decode-status.py
index ce20da571..7b8bce742 100755
--- a/tools/decode-status.py
+++ b/tools/decode-status.py
@@ -172,7 +172,7 @@ a_features = [[
"USE_BUZZER","USE_RDM6300","USE_IBEACON","USE_SML_M",
"USE_INA226","USE_A4988_STEPPER","USE_DDS2382","USE_SM2135",
"USE_SHUTTER","USE_PCF8574","USE_DDSU666","USE_DEEPSLEEP",
- "USE_SONOFF_SC","USE_SONOFF_RF","USE_SONOFF_L1","",
+ "USE_SONOFF_SC","USE_SONOFF_RF","USE_SONOFF_L1","USE_EXS_DIMMER",
"","","","",
"","","","",
"","","","",