From 019dc767403ab45f3e7fd83644ec4f9be9a14254 Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Sun, 6 Oct 2019 17:19:05 +0200 Subject: [PATCH] Redesign light driver phase 1 Redesign light driver phase 1 --- sonoff/my_user_config.h | 16 +- sonoff/sonoff.ino | 80 ++----- sonoff/xdrv_04_light.ino | 226 ++++++------------ sonoff/xlgt_03_sm16716.ino | 200 ++++++++++++++++ ...{xdrv_26_sm2135.ino => xlgt_04_sm2135.ino} | 55 ++--- sonoff/xlgt_interface.ino | 114 +++++++++ 6 files changed, 438 insertions(+), 253 deletions(-) create mode 100644 sonoff/xlgt_03_sm16716.ino rename sonoff/{xdrv_26_sm2135.ino => xlgt_04_sm2135.ino} (81%) create mode 100644 sonoff/xlgt_interface.ino diff --git a/sonoff/my_user_config.h b/sonoff/my_user_config.h index 631ddfc7c..a4864c61f 100644 --- a/sonoff/my_user_config.h +++ b/sonoff/my_user_config.h @@ -315,9 +315,16 @@ #define USE_ARMTRONIX_DIMMERS // Add support for Armtronix Dimmers (+1k4 code) #define USE_PS_16_DZ // Add support for PS-16-DZ Dimmer and Sonoff L1 (+2k code) //#define ROTARY_V1 // Add support for MI Desk Lamp -#define USE_SM2135 // Add support for SM2135 RGBCW led control (+0k6 code) //#define USE_SHUTTER // Add Shutter support for up to 4 shutter with different motortypes (+6k code) +// -- Optional light modules ---------------------- +#define USE_WS2812 // WS2812 Led string using library NeoPixelBus (+5k code, +1k mem, 232 iram) - Disable by // +// #define USE_WS2812_DMA // DMA supports only GPIO03 (= Serial RXD) (+1k mem). When USE_WS2812_DMA is enabled expect Exceptions on Pow + #define USE_WS2812_HARDWARE NEO_HW_WS2812 // Hardware type (NEO_HW_WS2812, NEO_HW_WS2812X, NEO_HW_WS2813, NEO_HW_SK6812, NEO_HW_LC8812, NEO_HW_APA106) + #define USE_WS2812_CTYPE NEO_GRB // Color type (NEO_RGB, NEO_GRB, NEO_BRG, NEO_RBG, NEO_RGBW, NEO_GRBW) +#define USE_SM16716 // Add support for SM16716 RGB LED controller (+0k7 code) +#define USE_SM2135 // Add support for SM2135 RGBCW led control as used in Action LSC (+0k6 code) + // -- Counter input ------------------------------- #define USE_COUNTER // Enable inputs as counter (+0k8 code) @@ -513,11 +520,6 @@ // ------------------------------------------------ -#define USE_WS2812 // WS2812 Led string using library NeoPixelBus (+5k code, +1k mem, 232 iram) - Disable by // -// #define USE_WS2812_DMA // DMA supports only GPIO03 (= Serial RXD) (+1k mem). When USE_WS2812_DMA is enabled expect Exceptions on Pow - #define USE_WS2812_HARDWARE NEO_HW_WS2812 // Hardware type (NEO_HW_WS2812, NEO_HW_WS2812X, NEO_HW_WS2813, NEO_HW_SK6812, NEO_HW_LC8812, NEO_HW_APA106) - #define USE_WS2812_CTYPE NEO_GRB // Color type (NEO_RGB, NEO_GRB, NEO_BRG, NEO_RBG, NEO_RGBW, NEO_GRBW) - #define USE_ARILUX_RF // Add support for Arilux RF remote controller (+0k8 code, 252 iram (non 2.3.0)) #define USE_SR04 // Add support for HC-SR04 ultrasonic devices (+1k code) @@ -536,8 +538,6 @@ // #define USE_THEO_V2 // Add support for decoding Theo V2 sensors as documented on https://sidweb.nl using 434MHz RF sensor receiver (+1k4 code) // #define USE_ALECTO_V2 // Add support for decoding Alecto V2 sensors like ACH2010, WS3000 and DKW2012 weather stations using 868MHz RF sensor receiver (+1k7 code) -#define USE_SM16716 // Add support for SM16716 RGB LED controller (+0k7 code) - //#define USE_HRE // Add support for Badger HR-E Water Meter (+1k4 code) //#define USE_A4988_Stepper // Add support for A4988 stepper-motor-driver-circuit (+10k5 code) diff --git a/sonoff/sonoff.ino b/sonoff/sonoff.ino index 8f0627b1b..df7de51ec 100755 --- a/sonoff/sonoff.ino +++ b/sonoff/sonoff.ino @@ -130,6 +130,7 @@ uint8_t led_power = 0; // LED power state uint8_t ledlnk_inverted = 0; // Link LED inverted flag (1 = (0 = On, 1 = Off)) uint8_t pwm_inverted = 0; // PWM inverted flag (1 = inverted) uint8_t energy_flg = 0; // Energy monitor configured +uint8_t light_flg = 0; // Light module configured uint8_t light_type = 0; // Light types uint8_t serial_in_byte; // Received byte uint8_t ota_retry_counter = OTA_ATTEMPTS; // OTA retry counter @@ -1391,14 +1392,6 @@ void GpioInit(void) devices_present = 1; light_type = LT_BASIC; // Use basic PWM control if SetOption15 = 0 -#ifdef USE_LIGHT - if (Settings.flag.pwm_control) { - for (uint32_t i = 0; i < MAX_PWMS; i++) { - if (pin[GPIO_PWM1 +i] < 99) { light_type++; } // Use Dimmer/Color control for all PWM as SetOption15 = 1 - } - } -#endif // USE_LIGHT - if (XdrvCall(FUNC_MODULE_INIT)) { // Serviced } @@ -1420,30 +1413,24 @@ void GpioInit(void) devices_present = 0; baudrate = 19200; } -#ifdef USE_LIGHT - else if (SONOFF_BN == my_module_type) { // PWM Single color led (White) - light_type = LT_PWM1; + + if (!light_type) { + devices_present = 0; + for (uint32_t i = 0; i < MAX_PWMS; i++) { // Basic PWM control only + if (pin[GPIO_PWM1 +i] < 99) { + pwm_present = true; + pinMode(pin[GPIO_PWM1 +i], OUTPUT); + analogWrite(pin[GPIO_PWM1 +i], bitRead(pwm_inverted, i) ? Settings.pwm_range - Settings.pwm_value[i] : Settings.pwm_value[i]); + } + } } - else if (SONOFF_LED == my_module_type) { // PWM Dual color led (White warm and cold) - light_type = LT_PWM2; - } - else if (AILIGHT == my_module_type) { // RGBW led - light_type = LT_RGBW; - } - else if (SONOFF_B1 == my_module_type) { // RGBWC led - light_type = LT_RGBWC; - } -#endif // USE_LIGHT - else { - if (!light_type) { devices_present = 0; } - for (uint32_t i = 0; i < MAX_RELAYS; i++) { - if (pin[GPIO_REL1 +i] < 99) { - pinMode(pin[GPIO_REL1 +i], OUTPUT); - devices_present++; - if (EXS_RELAY == my_module_type) { - digitalWrite(pin[GPIO_REL1 +i], bitRead(rel_inverted, i) ? 1 : 0); - if (i &1) { devices_present--; } - } + for (uint32_t i = 0; i < MAX_RELAYS; i++) { + if (pin[GPIO_REL1 +i] < 99) { + pinMode(pin[GPIO_REL1 +i], OUTPUT); + devices_present++; + if (EXS_RELAY == my_module_type) { + digitalWrite(pin[GPIO_REL1 +i], bitRead(rel_inverted, i) ? 1 : 0); + if (i &1) { devices_present--; } } } } @@ -1475,37 +1462,6 @@ void GpioInit(void) RotaryInit(); #endif -#ifdef USE_LIGHT -#ifdef USE_WS2812 - if (!light_type && (pin[GPIO_WS2812] < 99)) { // RGB led - devices_present++; - light_type = LT_WS2812; - } -#endif // USE_WS2812 -#ifdef USE_SM16716 - if (SM16716_ModuleSelected()) { - light_type += 3; - light_type |= LT_SM16716; - } -#endif // USE_SM16716 - - // post-process for lights - if (Settings.flag3.pwm_multi_channels) { - uint32_t pwm_channels = (light_type & 7) > LST_MAX ? LST_MAX : (light_type & 7); - if (0 == pwm_channels) { pwm_channels = 1; } - devices_present += pwm_channels - 1; // add the pwm channels controls at the end - } -#endif // USE_LIGHT - if (!light_type) { - for (uint32_t i = 0; i < MAX_PWMS; i++) { // Basic PWM control only - if (pin[GPIO_PWM1 +i] < 99) { - pwm_present = true; - pinMode(pin[GPIO_PWM1 +i], OUTPUT); - analogWrite(pin[GPIO_PWM1 +i], bitRead(pwm_inverted, i) ? Settings.pwm_range - Settings.pwm_value[i] : Settings.pwm_value[i]); - } - } - } - SetLedPower(Settings.ledstate &8); SetLedLink(Settings.ledstate &8); diff --git a/sonoff/xdrv_04_light.ino b/sonoff/xdrv_04_light.ino index e1cc82418..0b9df7205 100644 --- a/sonoff/xdrv_04_light.ino +++ b/sonoff/xdrv_04_light.ino @@ -257,6 +257,7 @@ struct LIGHT { uint8_t wakeup_active = 0; uint8_t wakeup_dimmer = 0; uint8_t fixed_color_index = 1; + uint8_t pwm_offset = 0; // Offset in color buffer bool update = true; bool pwm_multi_channels = false; // SetOption68, treat each PWM channel as an independant dimmer @@ -1269,102 +1270,66 @@ void LightMy92x1Duty(uint8_t duty_r, uint8_t duty_g, uint8_t duty_b, uint8_t dut os_delay_us(12); // TStop > 12us. } -#ifdef USE_SM16716 -/*********************************************************************************************\ - * SM16716 - Controlling RGB over a synchronous serial line - * Copyright (C) 2019 Gabor Simon - * - * Source: https://community.home-assistant.io/t/cheap-uk-wifi-bulbs-with-tasmota-teardown-help-tywe3s/40508/27 - * -\*********************************************************************************************/ - -#define D_LOG_SM16716 "SM16716: " - -uint8_t sm16716_pin_clk = 100; -uint8_t sm16716_pin_dat = 100; -uint8_t sm16716_pin_sel = 100; -uint8_t sm16716_enabled = 0; - -void SM16716_SendBit(uint8_t v) -{ - /* NOTE: - * According to the spec sheet, max freq is 30 MHz, that is 16.6 ns per high/low half of the - * clk square wave. That is less than the overhead of 'digitalWrite' at this clock rate, - * so no additional delays are needed yet. */ - - digitalWrite(sm16716_pin_dat, (v != 0) ? HIGH : LOW); - //delayMicroseconds(1); - digitalWrite(sm16716_pin_clk, HIGH); - //delayMicroseconds(1); - digitalWrite(sm16716_pin_clk, LOW); -} - -void SM16716_SendByte(uint8_t v) -{ - uint8_t mask; - - for (mask = 0x80; mask; mask >>= 1) { - SM16716_SendBit(v & mask); - } -} - -void SM16716_Update(uint8_t duty_r, uint8_t duty_g, uint8_t duty_b) -{ - if (sm16716_pin_sel < 99) { - uint8_t sm16716_should_enable = (duty_r | duty_g | duty_b); - if (!sm16716_enabled && sm16716_should_enable) { - DEBUG_DRIVER_LOG(PSTR(D_LOG_SM16716 "turning color on")); - sm16716_enabled = 1; - digitalWrite(sm16716_pin_sel, HIGH); - // in testing I found it takes a minimum of ~380us to wake up the chip - // tested on a Merkury RGBW with an SM726EB - delayMicroseconds(1000); - SM16716_Init(); - } - else if (sm16716_enabled && !sm16716_should_enable) { - DEBUG_DRIVER_LOG(PSTR(D_LOG_SM16716 "turning color off")); - sm16716_enabled = 0; - digitalWrite(sm16716_pin_sel, LOW); - } - } - DEBUG_DRIVER_LOG(PSTR(D_LOG_SM16716 "Update; rgb=%02x%02x%02x"), duty_r, duty_g, duty_b); - - // send start bit - SM16716_SendBit(1); - SM16716_SendByte(duty_r); - SM16716_SendByte(duty_g); - SM16716_SendByte(duty_b); - - // send a 'do it' pulse - // (if multiple chips are chained, each one processes the 1st '1rgb' 25-bit block and - // passes on the rest, right until the one starting with 0) - //SM16716_Init(); - SM16716_SendBit(0); - SM16716_SendByte(0); - SM16716_SendByte(0); - SM16716_SendByte(0); -} - -bool SM16716_ModuleSelected(void) -{ - sm16716_pin_clk = pin[GPIO_SM16716_CLK]; - sm16716_pin_dat = pin[GPIO_SM16716_DAT]; - sm16716_pin_sel = pin[GPIO_SM16716_SEL]; - DEBUG_DRIVER_LOG(PSTR(D_LOG_SM16716 "ModuleSelected; clk_pin=%d, dat_pin=%d)"), sm16716_pin_clk, sm16716_pin_dat); - return (sm16716_pin_clk < 99) && (sm16716_pin_dat < 99); -} - -void SM16716_Init(void) -{ - for (uint32_t t_init = 0; t_init < 50; ++t_init) { - SM16716_SendBit(0); - } -} - -#endif // ifdef USE_SM16716 - /********************************************************************************************/ +void LightPwmOffset(uint32_t offset) +{ + Light.pwm_offset = offset; +} + +bool LightModuleInit(void) +{ + light_type = LT_BASIC; // Use basic PWM control if SetOption15 = 0 + + if (Settings.flag.pwm_control) { + for (uint32_t i = 0; i < MAX_PWMS; i++) { + if (pin[GPIO_PWM1 +i] < 99) { light_type++; } // Use Dimmer/Color control for all PWM as SetOption15 = 1 + } + } + + light_flg = 0; + if (XlgtCall(FUNC_MODULE_INIT)) { + // serviced + } + else if (SONOFF_BN == my_module_type) { // PWM Single color led (White) + light_type = LT_PWM1; + } + else if (SONOFF_LED == my_module_type) { // PWM Dual color led (White warm and cold) + if (!my_module.io[4]) { // Fix Sonoff Led instabilities + pinMode(4, OUTPUT); // Stop floating outputs + digitalWrite(4, LOW); + } + if (!my_module.io[5]) { + pinMode(5, OUTPUT); // Stop floating outputs + digitalWrite(5, LOW); + } + if (!my_module.io[14]) { + pinMode(14, OUTPUT); // Stop floating outputs + digitalWrite(14, LOW); + } + light_type = LT_PWM2; + } + else if (AILIGHT == my_module_type) { // RGBW led + light_type = LT_RGBW; + } + else if (SONOFF_B1 == my_module_type) { // RGBWC led + light_type = LT_RGBWC; + } +#ifdef USE_WS2812 + if (!light_type && (pin[GPIO_WS2812] < 99)) { // RGB led + light_type = LT_WS2812; + } +#endif // USE_WS2812 + // post-process for lights + if (Settings.flag3.pwm_multi_channels) { + uint32_t pwm_channels = (light_type & 7) > LST_MAX ? LST_MAX : (light_type & 7); + if (0 == pwm_channels) { pwm_channels = 1; } + devices_present += pwm_channels - 1; // add the pwm channels controls at the end + } + + return (light_type > 0); +} + void LightInit(void) { uint8_t max_scheme = LS_MAX -1; @@ -1402,20 +1367,6 @@ void LightInit(void) pinMode(pin[GPIO_PWM1 +i], OUTPUT); } } - if (SONOFF_LED == my_module_type) { // Fix Sonoff Led instabilities - if (!my_module.io[4]) { - pinMode(4, OUTPUT); // Stop floating outputs - digitalWrite(4, LOW); - } - if (!my_module.io[5]) { - pinMode(5, OUTPUT); // Stop floating outputs - digitalWrite(5, LOW); - } - if (!my_module.io[14]) { - pinMode(14, OUTPUT); // Stop floating outputs - digitalWrite(14, LOW); - } - } if (pin[GPIO_ARIRFRCV] < 99) { if (pin[GPIO_ARIRFSEL] < 99) { pinMode(pin[GPIO_ARIRFSEL], OUTPUT); @@ -1429,32 +1380,6 @@ void LightInit(void) max_scheme = LS_MAX + WS2812_SCHEMES; } #endif // USE_WS2812 ************************************************************************ -#ifdef USE_SM16716 - else if (LT_SM16716 == light_type - Light.subtype) { - // init PWM - for (uint32_t i = 0; i < Light.subtype; i++) { - Settings.pwm_value[i] = 0; // Disable direct PWM control - if (pin[GPIO_PWM1 +i] < 99) { - pinMode(pin[GPIO_PWM1 +i], OUTPUT); - } - } - // init sm16716 - pinMode(sm16716_pin_clk, OUTPUT); - digitalWrite(sm16716_pin_clk, LOW); - - pinMode(sm16716_pin_dat, OUTPUT); - digitalWrite(sm16716_pin_dat, LOW); - - if (sm16716_pin_sel < 99) { - pinMode(sm16716_pin_sel, OUTPUT); - digitalWrite(sm16716_pin_sel, LOW); - // no need to call SM16716_Init here, it will be called after sel goes HIGH - } else { - // no sel pin means you have an 'always on' chip, so init right away - SM16716_Init(); - } - } -#endif // ifdef USE_SM16716 else { light_pdi_pin = pin[GPIO_DI]; light_pdcki_pin = pin[GPIO_DCKI]; @@ -2001,10 +1926,10 @@ void LightAnimate(void) // now apply the actual PWM values, adjusted and remapped 10-bits range if (light_type < LT_PWM6) { // only for direct PWM lights, not for Tuya, Armtronix... - for (uint32_t i = 0; i < Light.subtype; i++) { + for (uint32_t i = 0; i < (Light.subtype - Light.pwm_offset); i++) { if (pin[GPIO_PWM1 +i] < 99) { //AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_APPLICATION "Cur_Col%d 10 bits %d, Pwm%d %d"), i, cur_col_10bits[i], i+1, cur_col[i]); - analogWrite(pin[GPIO_PWM1 +i], bitRead(pwm_inverted, i) ? Settings.pwm_range - cur_col_10bits[i] : cur_col_10bits[i]); + analogWrite(pin[GPIO_PWM1 +i], bitRead(pwm_inverted, i) ? Settings.pwm_range - cur_col_10bits[(i + Light.pwm_offset)] : cur_col_10bits[(i + Light.pwm_offset)]); } } } @@ -2018,7 +1943,10 @@ void LightAnimate(void) AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_DEBUG "R%d G%d B%d, C%d W%d"), cur_col[0], cur_col[1], cur_col[2], cur_col[3], cur_col[4]); - if (XdrvCall(FUNC_SET_CHANNELS)) { + if (XlgtCall(FUNC_SET_CHANNELS)) { + // Serviced + } + else if (XdrvCall(FUNC_SET_CHANNELS)) { // Serviced } #ifdef USE_WS2812 // ************************************************************************ @@ -2026,19 +1954,6 @@ void LightAnimate(void) Ws2812SetColor(0, cur_col[0], cur_col[1], cur_col[2], cur_col[3]); } #endif // USE_ES2812 ************************************************************************ -#ifdef USE_SM16716 - else if (LT_SM16716 == light_type - Light.subtype) { - // handle any PWM pins, skipping the first 3 values for sm16716 - for (uint32_t i = 3; i < Light.subtype; i++) { - if (pin[GPIO_PWM1 +i-3] < 99) { - //AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_APPLICATION "Cur_Col%d 10 bits %d, Pwm%d %d"), i, cur_col[i], i+1, curcol); - analogWrite(pin[GPIO_PWM1 +i-3], bitRead(pwm_inverted, i-3) ? Settings.pwm_range - cur_col_10bits[i] : cur_col_10bits[i]); - } - } - // handle sm16716 update - SM16716_Update(cur_col[0], cur_col[1], cur_col[2]); - } -#endif // ifdef USE_SM16716 else if (light_type > LT_WS2812) { //AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_APPLICATION "Cur_Col %d,%d,%d,%d,%d"), cur_col[0], cur_col[1], cur_col[2], cur_col[3], cur_col[4]); LightMy92x1Duty(cur_col[0], cur_col[1], cur_col[2], cur_col[3], cur_col[4]); @@ -2588,11 +2503,11 @@ bool Xdrv04(uint8_t function) { bool result = false; - if (light_type) { + if (FUNC_MODULE_INIT == function) { + return LightModuleInit(); + } + else if (light_type) { switch (function) { - case FUNC_PRE_INIT: - LightInit(); - break; case FUNC_EVERY_50_MSECOND: LightAnimate(); #ifdef USE_ARILUX_RF @@ -2610,6 +2525,9 @@ bool Xdrv04(uint8_t function) case FUNC_COMMAND: result = DecodeCommand(kLightCommands, LightCommand); break; + case FUNC_PRE_INIT: + LightInit(); + break; } } return result; diff --git a/sonoff/xlgt_03_sm16716.ino b/sonoff/xlgt_03_sm16716.ino new file mode 100644 index 000000000..d12636e85 --- /dev/null +++ b/sonoff/xlgt_03_sm16716.ino @@ -0,0 +1,200 @@ +/* + xlgt_03_sm16716.ino - sm16716 three channel led support for Sonoff-Tasmota + + Copyright (C) 2019 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 . +*/ + +#ifdef USE_LIGHT +#ifdef USE_SM16716 +/*********************************************************************************************\ + * SM16716 - Controlling RGB over a synchronous serial line + * Copyright (C) 2019 Gabor Simon + * + * Source: https://community.home-assistant.io/t/cheap-uk-wifi-bulbs-with-tasmota-teardown-help-tywe3s/40508/27 +\*********************************************************************************************/ + +#define XLGT_03 3 + +#define D_LOG_SM16716 "SM16716: " + +struct SM16716 { + uint8_t pin_clk = 0; + uint8_t pin_dat = 0; + uint8_t pin_sel = 0; + bool enabled = false; +} Sm16716; + +void SM16716_SendBit(uint8_t v) +{ + /* NOTE: + * According to the spec sheet, max freq is 30 MHz, that is 16.6 ns per high/low half of the + * clk square wave. That is less than the overhead of 'digitalWrite' at this clock rate, + * so no additional delays are needed yet. */ + + digitalWrite(Sm16716.pin_dat, (v != 0) ? HIGH : LOW); + //delayMicroseconds(1); + digitalWrite(Sm16716.pin_clk, HIGH); + //delayMicroseconds(1); + digitalWrite(Sm16716.pin_clk, LOW); +} + +void SM16716_SendByte(uint8_t v) +{ + uint8_t mask; + + for (mask = 0x80; mask; mask >>= 1) { + SM16716_SendBit(v & mask); + } +} + +void SM16716_Update(uint8_t duty_r, uint8_t duty_g, uint8_t duty_b) +{ + if (Sm16716.pin_sel < 99) { + bool should_enable = (duty_r | duty_g | duty_b); + if (!Sm16716.enabled && should_enable) { + DEBUG_DRIVER_LOG(PSTR(D_LOG_SM16716 "turning color on")); + Sm16716.enabled = true; + digitalWrite(Sm16716.pin_sel, HIGH); + // in testing I found it takes a minimum of ~380us to wake up the chip + // tested on a Merkury RGBW with an SM726EB + delayMicroseconds(1000); + SM16716_Init(); + } + else if (Sm16716.enabled && !should_enable) { + DEBUG_DRIVER_LOG(PSTR(D_LOG_SM16716 "turning color off")); + Sm16716.enabled = false; + digitalWrite(Sm16716.pin_sel, LOW); + } + } + DEBUG_DRIVER_LOG(PSTR(D_LOG_SM16716 "Update; rgb=%02x%02x%02x"), duty_r, duty_g, duty_b); + + // send start bit + SM16716_SendBit(1); + SM16716_SendByte(duty_r); + SM16716_SendByte(duty_g); + SM16716_SendByte(duty_b); + + // send a 'do it' pulse + // (if multiple chips are chained, each one processes the 1st '1rgb' 25-bit block and + // passes on the rest, right until the one starting with 0) + //SM16716_Init(); + SM16716_SendBit(0); + SM16716_SendByte(0); + SM16716_SendByte(0); + SM16716_SendByte(0); +} + +/* +bool SM16716_ModuleSelected(void) +{ + Sm16716.pin_clk = pin[GPIO_SM16716_CLK]; + Sm16716.pin_dat = pin[GPIO_SM16716_DAT]; + Sm16716.pin_sel = pin[GPIO_SM16716_SEL]; + DEBUG_DRIVER_LOG(PSTR(D_LOG_SM16716 "ModuleSelected; clk_pin=%d, dat_pin=%d)"), Sm16716.pin_clk, Sm16716.pin_dat); + return (Sm16716.pin_clk < 99) && (Sm16716.pin_dat < 99); +} +*/ + +void SM16716_Init(void) +{ + for (uint32_t t_init = 0; t_init < 50; ++t_init) { + SM16716_SendBit(0); + } +} + +/********************************************************************************************/ + +bool Sm16716SetChannels(void) +{ +/* + // handle any PWM pins, skipping the first 3 values for sm16716 + for (uint32_t i = 3; i < Light.subtype; i++) { + if (pin[GPIO_PWM1 +i-3] < 99) { + //AddLog_P2(LOG_LEVEL_DEBUG, PSTR(D_LOG_APPLICATION "Cur_Col%d 10 bits %d, Pwm%d %d"), i, cur_col[i], i+1, curcol); + analogWrite(pin[GPIO_PWM1 +i-3], bitRead(pwm_inverted, i-3) ? Settings.pwm_range - cur_col_10bits[i] : cur_col_10bits[i]); + } + } +*/ + // handle sm16716 update + uint8_t *cur_col = (uint8_t*)XdrvMailbox.data; + + SM16716_Update(cur_col[0], cur_col[1], cur_col[2]); + + return true; +} + +void Sm16716ModuleSelected(void) +{ + if ((pin[GPIO_SM16716_CLK] < 99) && (pin[GPIO_SM16716_DAT] < 99)) { + Sm16716.pin_clk = pin[GPIO_SM16716_CLK]; + Sm16716.pin_dat = pin[GPIO_SM16716_DAT]; + Sm16716.pin_sel = pin[GPIO_SM16716_SEL]; + +/* + // init PWM + for (uint32_t i = 0; i < Light.subtype; i++) { + Settings.pwm_value[i] = 0; // Disable direct PWM control + if (pin[GPIO_PWM1 +i] < 99) { + pinMode(pin[GPIO_PWM1 +i], OUTPUT); + } + } +*/ + + // init sm16716 + pinMode(Sm16716.pin_clk, OUTPUT); + digitalWrite(Sm16716.pin_clk, LOW); + + pinMode(Sm16716.pin_dat, OUTPUT); + digitalWrite(Sm16716.pin_dat, LOW); + + if (Sm16716.pin_sel < 99) { + pinMode(Sm16716.pin_sel, OUTPUT); + digitalWrite(Sm16716.pin_sel, LOW); + // no need to call SM16716_Init here, it will be called after sel goes HIGH + } else { + // no sel pin means you have an 'always on' chip, so init right away + SM16716_Init(); + } + + LightPwmOffset(LST_RGB); // Handle any PWM pins, skipping the first 3 color values for sm16716 + light_type += LST_RGB; // Add RGB to be controlled by sm16716 + light_flg = XLGT_03; + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("DBG: SM16716 Found")); + } +} + +/*********************************************************************************************\ + * Interface +\*********************************************************************************************/ + +bool Xlgt03(uint8_t function) +{ + bool result = false; + + switch (function) { + case FUNC_SET_CHANNELS: + result = Sm16716SetChannels(); + break; + case FUNC_MODULE_INIT: + Sm16716ModuleSelected(); + break; + } + return result; +} + +#endif // USE_SM16716 +#endif // USE_LIGHT + diff --git a/sonoff/xdrv_26_sm2135.ino b/sonoff/xlgt_04_sm2135.ino similarity index 81% rename from sonoff/xdrv_26_sm2135.ino rename to sonoff/xlgt_04_sm2135.ino index a54a6ccb8..21755d656 100644 --- a/sonoff/xdrv_26_sm2135.ino +++ b/sonoff/xlgt_04_sm2135.ino @@ -1,5 +1,5 @@ /* - xdrv_26_sm2135.ino - sm2135 five channel led support for Sonoff-Tasmota + xlgt_04_sm2135.ino - sm2135 five channel led support for Sonoff-Tasmota Copyright (C) 2019 Theo Arends @@ -25,7 +25,7 @@ * {"NAME":"LSC RGBCW LED","GPIO":[0,0,0,0,0,0,0,0,181,0,180,0,0],"FLAG":0,"BASE":18} \*********************************************************************************************/ -#define XDRV_26 26 +#define XLGT_04 4 #define SM2135_ADDR_MC 0xC0 // Max current register #define SM2135_ADDR_CH 0xC1 // RGB or CW channel select register @@ -56,7 +56,6 @@ const uint8_t SM2135_CURRENT = (SM2135_20MA << 4) | SM2135_10MA; struct SM2135 { uint8_t clk = 0; uint8_t data = 0; - bool found = true; } Sm2135; uint8_t Sm2135Write(uint8_t data) @@ -87,19 +86,21 @@ void Sm2135Send(uint8_t *buffer, uint8_t size) digitalWrite(Sm2135.data, HIGH); } +/********************************************************************************************/ + bool Sm2135SetChannels(void) { - char *buffer = XdrvMailbox.data; - uint8_t data[8]; + uint8_t *cur_col = (uint8_t*)XdrvMailbox.data; + uint8_t data[6]; - if (('\0' == buffer[0]) && ('\0' == buffer[1]) && ('\0' == buffer[2])) { + if ((0 == cur_col[0]) && (0 == cur_col[1]) && (0 == cur_col[2])) { // No color so must be Cold/Warm /* - if ((buffer[3] + buffer[4]) >= (1 * 256)) { + if ((cur_col[3] + cur_col[4]) >= (1 * 256)) { // Scale down to 255 total to fix max power usage of 9W (=40mA) -// buffer[3] >>= 1; // Divide by 2 -// buffer[4] >>= 1; // Divide by 2 +// cur_col[3] >>= 1; // Divide by 2 +// cur_col[4] >>= 1; // Divide by 2 } */ data[0] = SM2135_ADDR_MC; @@ -108,13 +109,13 @@ bool Sm2135SetChannels(void) Sm2135Send(data, 3); delay(1); data[0] = SM2135_ADDR_C; - data[1] = buffer[4]; // Warm - data[2] = buffer[3]; // Cold + data[1] = cur_col[4]; // Warm + data[2] = cur_col[3]; // Cold Sm2135Send(data, 3); } else { // Color /* - if ((buffer[0] + buffer[1] + buffer[2]) >= (3 * 256)) { + if ((cur_col[0] + cur_col[1] + cur_col[2]) >= (3 * 256)) { // Scale down to 765 total to fix max power usage of 9W // Currently not needed with setting 3 x 15mA = 45mA = 11W = 765 } @@ -122,16 +123,16 @@ bool Sm2135SetChannels(void) data[0] = SM2135_ADDR_MC; data[1] = SM2135_CURRENT; data[2] = SM2135_RGB; - data[3] = buffer[1]; // Green - data[4] = buffer[0]; // Red - data[5] = buffer[2]; // Blue + data[3] = cur_col[1]; // Green + data[4] = cur_col[0]; // Red + data[5] = cur_col[2]; // Blue Sm2135Send(data, 6); } return true; } -bool Sm2135ModuleSelected(void) +void Sm2135ModuleSelected(void) { if ((pin[GPIO_SM2135_CLK] < 99) && (pin[GPIO_SM2135_DAT] < 99)) { Sm2135.clk = pin[GPIO_SM2135_CLK]; @@ -143,30 +144,26 @@ bool Sm2135ModuleSelected(void) digitalWrite(Sm2135.clk, HIGH); light_type = LT_RGBWC; + light_flg = XLGT_04; AddLog_P2(LOG_LEVEL_DEBUG, PSTR("DBG: SM2135 Found")); - } else { - Sm2135.found = false; } - return Sm2135.found; } /*********************************************************************************************\ * Interface \*********************************************************************************************/ -bool Xdrv26(uint8_t function) +bool Xlgt04(uint8_t function) { bool result = false; - if (Sm2135.found) { - switch (function) { - case FUNC_SET_CHANNELS: - result = Sm2135SetChannels(); - break; - case FUNC_MODULE_INIT: - result = Sm2135ModuleSelected(); - break; - } + switch (function) { + case FUNC_SET_CHANNELS: + result = Sm2135SetChannels(); + break; + case FUNC_MODULE_INIT: + Sm2135ModuleSelected(); + break; } return result; } diff --git a/sonoff/xlgt_interface.ino b/sonoff/xlgt_interface.ino new file mode 100644 index 000000000..cb963dc5c --- /dev/null +++ b/sonoff/xlgt_interface.ino @@ -0,0 +1,114 @@ +/* + xlgt_interface.ino - Light driver interface support for Sonoff-Tasmota + + Copyright (C) 2019 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 . +*/ + +#ifdef USE_LIGHT + +#ifdef XFUNC_PTR_IN_ROM +bool (* const xlgt_func_ptr[])(uint8_t) PROGMEM = { // Light driver Function Pointers +#else +bool (* const xlgt_func_ptr[])(uint8_t) = { // Light driver Function Pointers +#endif + +#ifdef XLGT_01 + &Xlgt01, +#endif + +#ifdef XLGT_02 + &Xlgt02, +#endif + +#ifdef XLGT_03 + &Xlgt03, +#endif + +#ifdef XLGT_04 + &Xlgt04, +#endif + +#ifdef XLGT_05 + &Xlgt05, +#endif + +#ifdef XLGT_06 + &Xlgt06, +#endif + +#ifdef XLGT_07 + &Xlgt07, +#endif + +#ifdef XLGT_08 + &Xlgt08, +#endif + +#ifdef XLGT_09 + &Xlgt09, +#endif + +#ifdef XLGT_10 + &Xlgt10, +#endif + +#ifdef XLGT_11 + &Xlgt11, +#endif + +#ifdef XLGT_12 + &Xlgt12, +#endif + +#ifdef XLGT_13 + &Xlgt13, +#endif + +#ifdef XLGT_14 + &Xlgt14, +#endif + +#ifdef XLGT_15 + &Xlgt15, +#endif + +#ifdef XLGT_16 + &Xlgt16 +#endif +}; + +const uint8_t xlgt_present = sizeof(xlgt_func_ptr) / sizeof(xlgt_func_ptr[0]); // Number of drivers found + +uint8_t xlgt_active = 0; + +bool XlgtCall(uint8_t function) +{ + if (FUNC_MODULE_INIT == function) { + for (uint32_t x = 0; x < xlgt_present; x++) { + xlgt_func_ptr[x](function); + if (light_flg) { + xlgt_active = x; + return true; // Stop further driver investigation + } + } + } + else if (light_flg) { + return xlgt_func_ptr[xlgt_active](function); + } + return false; +} + +#endif // USE_LIGHT