diff --git a/sonoff/language/bg-BG.h b/sonoff/language/bg-BG.h index 03c1b9f32..6c3f4556a 100644 --- a/sonoff/language/bg-BG.h +++ b/sonoff/language/bg-BG.h @@ -546,6 +546,7 @@ #define D_SENSOR_MAX31855_DO "MAX31855 DO" #define D_SENSOR_SM16716_CLK "SM16716 CLK" #define D_SENSOR_SM16716_DAT "SM16716 DAT" +#define D_SENSOR_SM16716_SEL "SM16716 SEL" // Units #define D_UNIT_AMPERE "A" diff --git a/sonoff/language/cs-CZ.h b/sonoff/language/cs-CZ.h index c87260f71..38c80d158 100644 --- a/sonoff/language/cs-CZ.h +++ b/sonoff/language/cs-CZ.h @@ -546,6 +546,7 @@ #define D_SENSOR_MAX31855_DO "MAX31855 DO" #define D_SENSOR_SM16716_CLK "SM16716 CLK" #define D_SENSOR_SM16716_DAT "SM16716 DAT" +#define D_SENSOR_SM16716_SEL "SM16716 SEL" // Units #define D_UNIT_AMPERE "A" diff --git a/sonoff/language/de-DE.h b/sonoff/language/de-DE.h index 692c74e17..607c781da 100644 --- a/sonoff/language/de-DE.h +++ b/sonoff/language/de-DE.h @@ -546,6 +546,7 @@ #define D_SENSOR_MAX31855_DO "MAX31855 DO" #define D_SENSOR_SM16716_CLK "SM16716 CLK" #define D_SENSOR_SM16716_DAT "SM16716 DAT" +#define D_SENSOR_SM16716_SEL "SM16716 SEL" // Units #define D_UNIT_AMPERE "A" diff --git a/sonoff/language/el-GR.h b/sonoff/language/el-GR.h index 81a017a0b..0a385a966 100644 --- a/sonoff/language/el-GR.h +++ b/sonoff/language/el-GR.h @@ -546,6 +546,7 @@ #define D_SENSOR_MAX31855_DO "MAX31855 DO" #define D_SENSOR_SM16716_CLK "SM16716 CLK" #define D_SENSOR_SM16716_DAT "SM16716 DAT" +#define D_SENSOR_SM16716_SEL "SM16716 SEL" // Units #define D_UNIT_AMPERE "A" diff --git a/sonoff/language/en-GB.h b/sonoff/language/en-GB.h index 9644e8b4d..34dc71c4a 100644 --- a/sonoff/language/en-GB.h +++ b/sonoff/language/en-GB.h @@ -546,6 +546,7 @@ #define D_SENSOR_MAX31855_DO "MAX31855 DO" #define D_SENSOR_SM16716_CLK "SM16716 CLK" #define D_SENSOR_SM16716_DAT "SM16716 DAT" +#define D_SENSOR_SM16716_SEL "SM16716 SEL" // Units #define D_UNIT_AMPERE "A" diff --git a/sonoff/language/es-AR.h b/sonoff/language/es-AR.h index a2f8bcf8a..597be23db 100644 --- a/sonoff/language/es-AR.h +++ b/sonoff/language/es-AR.h @@ -546,6 +546,7 @@ #define D_SENSOR_MAX31855_DO "MAX31855 DO" #define D_SENSOR_SM16716_CLK "SM16716 CLK" #define D_SENSOR_SM16716_DAT "SM16716 DAT" +#define D_SENSOR_SM16716_SEL "SM16716 SEL" // Units #define D_UNIT_AMPERE "A" diff --git a/sonoff/language/fr-FR.h b/sonoff/language/fr-FR.h index b9fa0d423..41da8c895 100644 --- a/sonoff/language/fr-FR.h +++ b/sonoff/language/fr-FR.h @@ -546,6 +546,7 @@ #define D_SENSOR_MAX31855_DO "MAX31855 DO" #define D_SENSOR_SM16716_CLK "SM16716 CLK" #define D_SENSOR_SM16716_DAT "SM16716 DAT" +#define D_SENSOR_SM16716_SEL "SM16716 SEL" // Units #define D_UNIT_AMPERE "A" diff --git a/sonoff/language/he-HE.h b/sonoff/language/he-HE.h index 22f9677b5..96c14b0e5 100644 --- a/sonoff/language/he-HE.h +++ b/sonoff/language/he-HE.h @@ -546,6 +546,7 @@ #define D_SENSOR_MAX31855_DO "MAX31855 DO" #define D_SENSOR_SM16716_CLK "SM16716 CLK" #define D_SENSOR_SM16716_DAT "SM16716 DAT" +#define D_SENSOR_SM16716_SEL "SM16716 SEL" // Units #define D_UNIT_AMPERE "A" diff --git a/sonoff/language/hu-HU.h b/sonoff/language/hu-HU.h index b6dd5b55a..628a12770 100644 --- a/sonoff/language/hu-HU.h +++ b/sonoff/language/hu-HU.h @@ -546,6 +546,7 @@ #define D_SENSOR_MAX31855_DO "MAX31855 DO" #define D_SENSOR_SM16716_CLK "SM16716 CLK" #define D_SENSOR_SM16716_DAT "SM16716 DAT" +#define D_SENSOR_SM16716_SEL "SM16716 SEL" // Units #define D_UNIT_AMPERE "A" diff --git a/sonoff/language/it-IT.h b/sonoff/language/it-IT.h index fa3c0b533..90a967057 100644 --- a/sonoff/language/it-IT.h +++ b/sonoff/language/it-IT.h @@ -546,6 +546,7 @@ #define D_SENSOR_MAX31855_DO "MAX31855 DO" #define D_SENSOR_SM16716_CLK "SM16716 CLK" #define D_SENSOR_SM16716_DAT "SM16716 DAT" +#define D_SENSOR_SM16716_SEL "SM16716 SEL" // Units #define D_UNIT_AMPERE "A" diff --git a/sonoff/language/nl-NL.h b/sonoff/language/nl-NL.h index 684545acf..cb3c9913c 100644 --- a/sonoff/language/nl-NL.h +++ b/sonoff/language/nl-NL.h @@ -546,6 +546,7 @@ #define D_SENSOR_MAX31855_DO "MAX31855 DO" #define D_SENSOR_SM16716_CLK "SM16716 CLK" #define D_SENSOR_SM16716_DAT "SM16716 DAT" +#define D_SENSOR_SM16716_SEL "SM16716 SEL" // Units #define D_UNIT_AMPERE "A" diff --git a/sonoff/language/pl-PL.h b/sonoff/language/pl-PL.h index 8e677b554..1e50d2f6b 100644 --- a/sonoff/language/pl-PL.h +++ b/sonoff/language/pl-PL.h @@ -546,6 +546,7 @@ #define D_SENSOR_MAX31855_DO "MAX31855 DO" #define D_SENSOR_SM16716_CLK "SM16716 CLK" #define D_SENSOR_SM16716_DAT "SM16716 DAT" +#define D_SENSOR_SM16716_SEL "SM16716 SEL" // Units #define D_UNIT_AMPERE "A" diff --git a/sonoff/language/pt-BR.h b/sonoff/language/pt-BR.h index 1d708f072..f86addf8e 100644 --- a/sonoff/language/pt-BR.h +++ b/sonoff/language/pt-BR.h @@ -546,6 +546,7 @@ #define D_SENSOR_MAX31855_DO "MAX31855 DO" #define D_SENSOR_SM16716_CLK "SM16716 CLK" #define D_SENSOR_SM16716_DAT "SM16716 DAT" +#define D_SENSOR_SM16716_SEL "SM16716 SEL" // Units #define D_UNIT_AMPERE "A" diff --git a/sonoff/language/pt-PT.h b/sonoff/language/pt-PT.h index baa0e28a5..8d8f1bb8d 100644 --- a/sonoff/language/pt-PT.h +++ b/sonoff/language/pt-PT.h @@ -546,6 +546,7 @@ #define D_SENSOR_MAX31855_DO "MAX31855 DO" #define D_SENSOR_SM16716_CLK "SM16716 CLK" #define D_SENSOR_SM16716_DAT "SM16716 DAT" +#define D_SENSOR_SM16716_SEL "SM16716 SEL" // Units #define D_UNIT_AMPERE "A" diff --git a/sonoff/language/ru-RU.h b/sonoff/language/ru-RU.h index 19c09a7fc..9064b1407 100644 --- a/sonoff/language/ru-RU.h +++ b/sonoff/language/ru-RU.h @@ -546,6 +546,7 @@ #define D_SENSOR_MAX31855_DO "MAX31855 DO" #define D_SENSOR_SM16716_CLK "SM16716 CLK" #define D_SENSOR_SM16716_DAT "SM16716 DAT" +#define D_SENSOR_SM16716_SEL "SM16716 SEL" // Units #define D_UNIT_AMPERE "А" diff --git a/sonoff/language/sk-SK.h b/sonoff/language/sk-SK.h index e2551f9bc..b778d45a5 100644 --- a/sonoff/language/sk-SK.h +++ b/sonoff/language/sk-SK.h @@ -546,6 +546,7 @@ #define D_SENSOR_MAX31855_DO "MAX31855 DO" #define D_SENSOR_SM16716_CLK "SM16716 CLK" #define D_SENSOR_SM16716_DAT "SM16716 DAT" +#define D_SENSOR_SM16716_SEL "SM16716 SEL" // Units #define D_UNIT_AMPERE "A" diff --git a/sonoff/language/sv-SE.h b/sonoff/language/sv-SE.h index 2b7b672a4..495564051 100644 --- a/sonoff/language/sv-SE.h +++ b/sonoff/language/sv-SE.h @@ -546,6 +546,7 @@ #define D_SENSOR_MAX31855_DO "MAX31855 DO" #define D_SENSOR_SM16716_CLK "SM16716 CLK" #define D_SENSOR_SM16716_DAT "SM16716 DAT" +#define D_SENSOR_SM16716_SEL "SM16716 SEL" // Units #define D_UNIT_AMPERE "A" diff --git a/sonoff/language/tr-TR.h b/sonoff/language/tr-TR.h index bdab28afb..d55b02899 100755 --- a/sonoff/language/tr-TR.h +++ b/sonoff/language/tr-TR.h @@ -546,6 +546,7 @@ #define D_SENSOR_MAX31855_DO "MAX31855 DO" #define D_SENSOR_SM16716_CLK "SM16716 CLK" #define D_SENSOR_SM16716_DAT "SM16716 DAT" +#define D_SENSOR_SM16716_SEL "SM16716 SEL" // Units #define D_UNIT_AMPERE "A" diff --git a/sonoff/language/uk-UK.h b/sonoff/language/uk-UK.h index 4e3c255d5..8c6d82a10 100644 --- a/sonoff/language/uk-UK.h +++ b/sonoff/language/uk-UK.h @@ -546,6 +546,7 @@ #define D_SENSOR_MAX31855_DO "MAX31855 DO" #define D_SENSOR_SM16716_CLK "SM16716 CLK" #define D_SENSOR_SM16716_DAT "SM16716 DAT" +#define D_SENSOR_SM16716_SEL "SM16716 SEL" // Units #define D_UNIT_AMPERE "А" diff --git a/sonoff/language/zh-CN.h b/sonoff/language/zh-CN.h index d4ff8eba8..0d8a4012d 100644 --- a/sonoff/language/zh-CN.h +++ b/sonoff/language/zh-CN.h @@ -546,6 +546,7 @@ #define D_SENSOR_MAX31855_DO "MAX31855 DO" #define D_SENSOR_SM16716_CLK "SM16716 CLK" #define D_SENSOR_SM16716_DAT "SM16716 DAT" +#define D_SENSOR_SM16716_SEL "SM16716 SEL" // Units #define D_UNIT_AMPERE "安" diff --git a/sonoff/language/zh-TW.h b/sonoff/language/zh-TW.h index 186c5bae3..cd1e503ec 100644 --- a/sonoff/language/zh-TW.h +++ b/sonoff/language/zh-TW.h @@ -546,6 +546,7 @@ #define D_SENSOR_MAX31855_DO "MAX31855 DO" #define D_SENSOR_SM16716_CLK "SM16716 CLK" #define D_SENSOR_SM16716_DAT "SM16716 DAT" +#define D_SENSOR_SM16716_SEL "SM16716 SEL" // Units #define D_UNIT_AMPERE "安" diff --git a/sonoff/sonoff.ino b/sonoff/sonoff.ino index 936f7930e..fbf256134 100755 --- a/sonoff/sonoff.ino +++ b/sonoff/sonoff.ino @@ -2422,6 +2422,12 @@ void GpioInit(void) light_type = LT_WS2812; } #endif // USE_WS2812 +#ifdef USE_SM16716 + if (SM16716_ModuleSelected()) { + light_type += 3; + light_type |= 16; + } +#endif // ifdef USE_SM16716 if (!light_type) { for (uint8_t i = 0; i < MAX_PWMS; i++) { // Basic PWM control only if (pin[GPIO_PWM1 +i] < 99) { diff --git a/sonoff/sonoff_template.h b/sonoff/sonoff_template.h index e050dc882..0ad920b27 100644 --- a/sonoff/sonoff_template.h +++ b/sonoff/sonoff_template.h @@ -154,8 +154,9 @@ enum UserSelectablePins { GPIO_KEY2_INV_NP, GPIO_KEY3_INV_NP, GPIO_KEY4_INV_NP, - GPIO_SM16716_CLK, // SM16716 CLK - GPIO_SM16716_DAT, // SM16716 DAT + GPIO_SM16716_CLK, // SM16716 CLOCK + GPIO_SM16716_DAT, // SM16716 DATA + GPIO_SM16716_SEL, // SM16716 SELECT GPIO_SENSOR_END }; // Programmer selectable GPIO functionality offset by user selectable GPIOs @@ -221,8 +222,8 @@ const char kSensorNames[] PROGMEM = D_SENSOR_AZ_TX "|" D_SENSOR_AZ_RX "|" D_SENSOR_MAX31855_CS "|" D_SENSOR_MAX31855_CLK "|" D_SENSOR_MAX31855_DO "|" D_SENSOR_BUTTON "1i|" D_SENSOR_BUTTON "2i|" D_SENSOR_BUTTON "3i|" D_SENSOR_BUTTON "4i|" - D_SENSOR_BUTTON "1in|" D_SENSOR_BUTTON "2in|" D_SENSOR_BUTTON "3in|" D_SENSOR_BUTTON "4in" - D_SENSOR_SM16716_CLK "|" D_SENSOR_SM16716_DAT + D_SENSOR_BUTTON "1in|" D_SENSOR_BUTTON "2in|" D_SENSOR_BUTTON "3in|" D_SENSOR_BUTTON "4in|" + D_SENSOR_SM16716_CLK "|" D_SENSOR_SM16716_DAT "|" D_SENSOR_SM16716_SEL ; /********************************************************************************************/ @@ -530,8 +531,9 @@ const uint8_t kGpioNiceList[] PROGMEM = { GPIO_MAX31855DO, // MAX31855 Serial interface #endif #ifdef USE_SM16716 - GPIO_SM16716_CLK, // SM16716 CLK - GPIO_SM16716_DAT, // SM16716 DAT + GPIO_SM16716_CLK, // SM16716 CLOCK + GPIO_SM16716_DAT, // SM16716 DATA + GPIO_SM16716_SEL, // SM16716 SELECT #endif // USE_SM16716 }; diff --git a/sonoff/xdrv_04_light.ino b/sonoff/xdrv_04_light.ino index 9d8740ccb..9cb22d698 100644 --- a/sonoff/xdrv_04_light.ino +++ b/sonoff/xdrv_04_light.ino @@ -32,6 +32,9 @@ * 11 +WS2812 RGB(W) no (One WS2812 RGB or RGBW ledstrip) * 12 AiLight RGBW no * 13 Sonoff B1 RGBCW yes + * 19 SM16716 RGB no + * 20 SM16716+W RGBW no + * 21 SM16716+CW RGBCW yes * * light_scheme WS2812 3+ Colors 1+2 Colors Effect * ------------ ------ --------- ---------- ----------------- @@ -354,6 +357,110 @@ 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 + * +\*********************************************************************************************/ + +// Enable this for debug logging +#define D_LOG_SM16716 "SM16716: " + +uint8_t sm16716_pin_clk = 100; +uint8_t sm16716_pin_dat = 100; +uint8_t sm16716_pin_sel = 100; + +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) { + if (duty_r | duty_g | duty_b) { +#ifdef D_LOG_SM16716 + snprintf_P(log_data, sizeof(log_data), PSTR(D_LOG_SM16716 "turning color on")); + AddLog(LOG_LEVEL_DEBUG); +#endif // D_LOG_SM16716 + digitalWrite(sm16716_pin_sel, HIGH); + delayMicroseconds(20); + SM16716_Init(); + } else { +#ifdef D_LOG_SM16716 + snprintf_P(log_data, sizeof(log_data), PSTR(D_LOG_SM16716 "turning color off")); + AddLog(LOG_LEVEL_DEBUG); +#endif // D_LOG_SM16716 + digitalWrite(sm16716_pin_sel, LOW); + } + } +#ifdef D_LOG_SM16716 + snprintf_P(log_data, sizeof(log_data), + PSTR(D_LOG_SM16716 "Update; rgb=%02x%02x%02x"), + duty_r, duty_g, duty_b); + AddLog(LOG_LEVEL_DEBUG); +#endif // D_LOG_SM16716 + + // send start bit + SM16716_SendBit(1); + // send 24-bit rgb data + 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]; +#ifdef D_LOG_SM16716 + snprintf_P(log_data, sizeof(log_data), + PSTR(D_LOG_SM16716 "ModuleSelected; clk_pin=%d, dat_pin=%d)"), + sm16716_pin_clk, sm16716_pin_dat); + AddLog(LOG_LEVEL_DEBUG); +#endif // D_LOG_SM16716 + return (sm16716_pin_clk < 99) && (sm16716_pin_dat < 99); +} + +void SM16716_Init(void) +{ + for (uint8_t t_init = 0; t_init < 50; ++t_init) { + SM16716_SendBit(0); + } +} + +#endif // ifdef USE_SM16716 + /********************************************************************************************/ void LightInit(void) @@ -402,6 +509,30 @@ void LightInit(void) max_scheme = LS_MAX + WS2812_SCHEMES; } #endif // USE_WS2812 ************************************************************************ +#ifdef USE_SM16716 + else if (16 & light_type) { + // init PWM + for (uint8_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); + } + + SM16716_Init(); + } +#endif // ifdef USE_SM16716 else { light_pdi_pin = pin[GPIO_DI]; light_pdcki_pin = pin[GPIO_DCKI]; @@ -844,6 +975,22 @@ 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 (16 & light_type) { + for (uint8_t i = 3; i < light_subtype; i++) { + if (pin[GPIO_PWM1 +i-3] < 99) { + if (cur_col[i] > 0xFC) { + cur_col[i] = 0xFC; // Fix unwanted blinking and PWM watchdog errors for values close to pwm_range (H801, Arilux and BN-SZ01) + } + uint16_t curcol = cur_col[i] * (Settings.pwm_range / 255); +// snprintf_P(log_data, sizeof(log_data), PSTR(D_LOG_APPLICATION "Cur_Col%d %d, CurCol %d"), i, cur_col[i], curcol); +// AddLog(LOG_LEVEL_DEBUG); + analogWrite(pin[GPIO_PWM1 +i-3], bitRead(pwm_inverted, i-3) ? Settings.pwm_range - curcol : curcol); + } + } + SM16716_Update(cur_col[0], cur_col[1], cur_col[2]); + } +#endif // ifdef USE_SM16716 else if (light_type > LT_WS2812) { LightMy92x1Duty(cur_col[0], cur_col[1], cur_col[2], cur_col[3], cur_col[4]); } diff --git a/sonoff/xdrv_20_sm16717.ino b/sonoff/xdrv_20_sm16717.ino deleted file mode 100644 index 1dada4c9c..000000000 --- a/sonoff/xdrv_20_sm16717.ino +++ /dev/null @@ -1,400 +0,0 @@ -/* - xdrv_20_sm16716.ino - SM16716 RGB led controller support for Sonoff-Tasmota - - Copyright (C) 2019 Gabor Simon - - 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_SM16716 - -/*********************************************************************************************\ - * SM16716 - Controlling RGB over a synchronous serial line - * - * Source: https://community.home-assistant.io/t/cheap-uk-wifi-bulbs-with-tasmota-teardown-help-tywe3s/40508/27 - * -\*********************************************************************************************/ - -#define XDRV_20 20 - -// Enable this for debug logging -//#define D_LOG_SM16716 "SM16716: " - -enum SM16716_Commands { - CMND_SM16716_COLOR }; - -const char k_SM16716_Commands[] PROGMEM = - D_CMND_COLOR ; - -uint8_t sm16716_pin_clk = 100; -uint8_t sm16716_pin_dat = 100; - -bool sm16716_is_selected = false; -bool sm16716_is_on = false; -uint8_t sm16716_color[3] = {0, 0, 0}; -int8_t sm16716_color_preset = 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_Append_JSON(void) -{ - snprintf_P(mqtt_data, sizeof(mqtt_data), - PSTR("%s,\"SM16716\":{\"Red\":%d,\"Green\":%d,\"Blue\":%d}"), - mqtt_data, sm16716_color[0], sm16716_color[1], sm16716_color[2]); -} - - -/*********************************************************************************************/ -bool SM16716_Show_State(void) -{ - snprintf_P(mqtt_data, sizeof(mqtt_data), - PSTR("{\"" D_CMND_COLOR "\":\"%02x%02x%02x\"}"), - sm16716_color[0], sm16716_color[1], sm16716_color[2]); - return true; -} - - -/*********************************************************************************************/ -void SM16716_Update() -{ -#ifdef D_LOG_SM16716 - snprintf_P(log_data, sizeof(log_data), - PSTR(D_LOG_SM16716 "Update; pwr=%02x, rgb=%02x%02x%02x"), - sm16716_is_on, sm16716_color[0], sm16716_color[1], sm16716_color[2]); - AddLog(LOG_LEVEL_DEBUG); -#endif // D_LOG_SM16716 - - // send start bit - SM16716_SendBit(1); - // send 24-bit rgb data - if (sm16716_is_on) { - SM16716_SendByte(sm16716_color[0]); - SM16716_SendByte(sm16716_color[1]); - SM16716_SendByte(sm16716_color[2]); - } - else { - SM16716_SendByte(0); - SM16716_SendByte(0); - SM16716_SendByte(0); - } - // 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_SendBit(0); - SM16716_SendByte(0); - SM16716_SendByte(0); - SM16716_SendByte(0); - SM16716_Show_State(); -} - - -/*********************************************************************************************/ -bool SM16716_ModuleSelected(void) -{ - sm16716_pin_clk = pin[GPIO_SM16716_CLK]; - sm16716_pin_dat = pin[GPIO_SM16716_DAT]; -#ifdef D_LOG_SM16716 - snprintf_P(log_data, sizeof(log_data), - PSTR(D_LOG_SM16716 "ModuleSelected; clk_pin=%d, dat_pin=%d)"), - sm16716_pin_clk, sm16716_pin_dat); - AddLog(LOG_LEVEL_DEBUG); -#endif // D_LOG_SM16716 - sm16716_is_selected = (sm16716_pin_clk < 99) && (sm16716_pin_dat < 99); - return sm16716_is_selected; -} - - -/*********************************************************************************************/ -bool SM16716_Init(void) -{ - uint8_t t_init; - - if (!SM16716_ModuleSelected()) { - return false; - } - - pinMode(sm16716_pin_clk, OUTPUT); - digitalWrite(sm16716_pin_clk, LOW); - - pinMode(sm16716_pin_dat, OUTPUT); - digitalWrite(sm16716_pin_dat, LOW); - - for (t_init = 0; t_init < 50; ++t_init) { - SM16716_SendBit(0); - } - - return true; -} - - -/*********************************************************************************************/ -/* Try to parse a string as 'RRGGBB' hex-encoded color value - * Accept only exact match (i.e. there can be no leftover chars) - */ -bool SM16716_Parse_RRGGBB(const char *data, int data_len) { - char component[3]; - char *endptr = NULL; - uint8_t candidate[3]; - int i; - -#ifdef D_LOG_SM16716 - snprintf_P(log_data, sizeof(log_data), - PSTR(D_LOG_SM16716 "Parse_RRGGBB; data='%s', data_len=%d"), - data, data_len); - AddLog(LOG_LEVEL_DEBUG); -#endif // D_LOG_SM16716 - - if (data_len != 6) { - // too long or too short - return false; - } - - // a component is exactly 3 chars, so terminate the string now - component[2] = '\0'; - - // try to parse 3 components - for (i = 0; i < 3; ++i) { - // copy the value to the temp string - component[0] = data[0]; - component[1] = data[1]; - // try to interpret it as a hex number - candidate[i] = (uint8_t)strtoul(component, &endptr, 16); - if (!endptr || *endptr) { - // not a valid hex number - return false; - } - // advance to the next 2 characters - data += 2; - } - - // now that we have all 3, we may change sm16716_color[] - sm16716_color[0] = candidate[0]; - sm16716_color[1] = candidate[1]; - sm16716_color[2] = candidate[2]; - return true; -} - - -/*********************************************************************************************/ -bool SM16716_Parse_Color(char *data, int data_len) { - /* NOTE: Very similar to 'LightColorEntry', but can't reuse it here, because - * 'light_type' must be PWM, and then it won't parse the color components. */ - -#ifdef D_LOG_SM16716 - snprintf_P(log_data, sizeof(log_data), - PSTR(D_LOG_SM16716 "Parse_Color; data='%s', data_len=%d"), - data, data_len); - AddLog(LOG_LEVEL_DEBUG); -#endif // D_LOG_SM16716 - - if (data_len < 3) { // too short for color literal: try to interpret as a color preset - // check for '+' and '-' commands first - switch (data[0]) { - case '+': // advance to the next preset, handle wrap-around - ++sm16716_color_preset; - if (sm16716_color_preset >= MAX_FIXED_COLOR) { - sm16716_color_preset = 0; - } - break; - - case '-': // return to the previous preset, handle wrap-around - --sm16716_color_preset; - if (sm16716_color_preset < 0) { - sm16716_color_preset = MAX_FIXED_COLOR - 1; - } - break; - - default: - // try to interpret it as a decimal integer - { - char *endptr = NULL; - uint8_t candidate = (uint8_t)strtoul(data, &endptr, 10); - if (!endptr || *endptr || (candidate < 0) || (MAX_FIXED_COLOR <= candidate)) { - // it's not a valid integer (and nothing else), or the number is not a valid preset index - return false; - } - // we have the requested preset index - sm16716_color_preset = candidate; - } - break; - } - -#ifdef D_LOG_SM16716 - snprintf_P(log_data, sizeof(log_data), - PSTR(D_LOG_SM16716 "Parse_Color; preset=%d"), - sm16716_color_preset); - AddLog(LOG_LEVEL_DEBUG); -#endif // D_LOG_SM16716 - - // copy the requested preset values - memcpy_P(sm16716_color, &kFixedColor[sm16716_color_preset], 3); - } - else if (data[0] == '#') { // starts with #, so try to interpret as #RRGGBB - if (!SM16716_Parse_RRGGBB(data + 1, data_len - 1)) { - // not a valid RRGGBB after the # - return false; - } - } - // two more formats left: RRGGBB (without #) and comma separated color components - else if (SM16716_Parse_RRGGBB(data, data_len)) { // try to interpret as RRGGBB - // parsed successfully as RRGGBB, nothing more to do - } - else { // try to interpret as rrr,g,bb - uint8_t candidate[3]; - char *tok, *last, *endptr = NULL; - int i; - - // try to parse the first 3 comma-separated tokens - for (i = 0; i < 3; ++i) { - // try to isolate the next token - tok = strtok_r(data, ",", &last); - if (!tok) { - // we're beyond the end of string: there were too few tokens - return false; - } - // try to interpret the token as integer - candidate[i] = (uint8_t)strtoul(tok, &endptr, 0); - if (!endptr || *endptr) { - // not a valid integer - return false; - } - // in the next cycle just continue this token-processing session - data = NULL; - } - // there shouldn't be any leftover characters (i.e. we need exactly 3 components) - tok = strtok_r(NULL, ",", &last); - if (tok) { - // too many components - return false; - } - // now that we have all 3 components, we may change sm16716_color - sm16716_color[0] = candidate[0]; - sm16716_color[1] = candidate[1]; - sm16716_color[2] = candidate[2]; - } - return true; -} - - -/*********************************************************************************************/ -bool SM16716_Command(void) -{ - char command [CMDSZ]; - int command_code = GetCommandCode(command, sizeof(command), XdrvMailbox.topic, k_SM16716_Commands); - -#ifdef D_LOG_SM16716 - snprintf_P(log_data, sizeof(log_data), - PSTR(D_LOG_SM16716 "Command; topic='%s', data_len=%d, data='%s', code=%d"), - XdrvMailbox.topic, XdrvMailbox.data_len, XdrvMailbox.data, command_code); - AddLog(LOG_LEVEL_DEBUG); -#endif // D_LOG_SM16716 - - switch (command_code) { - case CMND_SM16716_COLOR: - if (XdrvMailbox.data_len == 0) { - return SM16716_Show_State(); - } - if (!SM16716_Parse_Color(XdrvMailbox.data, XdrvMailbox.data_len)) { - return false; - } - SM16716_Update(); - return true; - } - return false; // Unknown command -} - - -/*********************************************************************************************\ - * Interface -\*********************************************************************************************/ -bool Xdrv20(uint8_t function) -{ - if (function == FUNC_MODULE_INIT) { - return SM16716_ModuleSelected(); - } - - if (!sm16716_is_selected) { - return false; - } - - switch (function) { - case FUNC_INIT: -#ifdef D_LOG_SM16716 - snprintf_P(log_data, sizeof(log_data), - PSTR(D_LOG_SM16716 "Entry; function=FUNC_INIT")); - AddLog(LOG_LEVEL_DEBUG); -#endif - return SM16716_Init(); - - case FUNC_COMMAND: -#ifdef D_LOG_SM16716 - snprintf_P(log_data, sizeof(log_data), - PSTR(D_LOG_SM16716 "Entry; function=FUNC_COMMAND")); - AddLog(LOG_LEVEL_DEBUG); -#endif - return SM16716_Command(); - - //case FUNC_SET_POWER: - case FUNC_SET_DEVICE_POWER: -#ifdef D_LOG_SM16716 - snprintf_P(log_data, sizeof(log_data), - PSTR(D_LOG_SM16716 "Entry; function=FUNC_SET_DEVICE_POWER, index=%02x, payload=%02x"), - XdrvMailbox.index, XdrvMailbox.payload); - AddLog(LOG_LEVEL_DEBUG); -#endif - sm16716_is_on = (XdrvMailbox.index != 0); - SM16716_Update(); - return false; // don't catch the event - - case FUNC_JSON_APPEND: -#ifdef D_LOG_SM16716 - snprintf_P(log_data, sizeof(log_data), - PSTR(D_LOG_SM16716 "Entry; function=FUNC_JSON_APPEND")); - AddLog(LOG_LEVEL_DEBUG); -#endif - SM16716_Append_JSON(); - break; - } - return false; -} - -#endif // USE_SM16716 -// vim: set ft=c sw=2 ts=2 et: