From 8d1730143317bd6f191544b70933b559c9601503 Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Tue, 8 Oct 2019 17:46:03 +0200 Subject: [PATCH] Redesign light driver phase 3 Redesign light driver phase 3 --- sonoff/sonoff.h | 8 +- sonoff/xdrv_04_light.ino | 136 ++---------- .../{xplg_ws2812.ino => xlgt_01_ws2812.ino} | 210 +++++++++++++++--- 3 files changed, 191 insertions(+), 163 deletions(-) rename sonoff/{xplg_ws2812.ino => xlgt_01_ws2812.ino} (71%) diff --git a/sonoff/sonoff.h b/sonoff/sonoff.h index c1c19f3fe..dbdeadc0c 100644 --- a/sonoff/sonoff.h +++ b/sonoff/sonoff.h @@ -168,8 +168,6 @@ const uint32_t LOOP_SLEEP_DELAY = 50; // Lowest number of milliseconds to #define NEO_RGBW 5 // Neopixel RGBW leds #define NEO_GRBW 6 // Neopixel GRBW leds -#define LT_SM16716 16 // Lights that use SM16716 will have this bit set in light_type - #define RGB_REMAP_RGBW 0 #define RGB_REMAP_RBGW 6 #define RGB_REMAP_GRBW 24 @@ -267,9 +265,7 @@ enum Ws2812Color { WS_RED, WS_GREEN, WS_BLUE }; enum LightSubtypes { LST_NONE, LST_SINGLE, LST_COLDWARM, LST_RGB, LST_RGBW, LST_RGBWC, LST_MAX=5 }; // Do not insert new fields enum LightTypes { LT_BASIC, LT_PWM1, LT_PWM2, LT_PWM3, LT_PWM4, LT_PWM5, LT_PWM6, LT_PWM7, - LT_NU8, LT_SERIAL1, LT_SERIAL2, LT_WS2812, LT_RGBW, LT_RGBWC, LT_NU14, LT_NU15 }; // Do not insert new fields - -enum LightSchemes {LS_POWER, LS_WAKEUP, LS_CYCLEUP, LS_CYCLEDN, LS_RANDOM, LS_MAX}; + LT_NU8, LT_SERIAL1, LT_SERIAL2, LT_RGB, LT_RGBW, LT_RGBWC, LT_NU14, LT_NU15 }; // Do not insert new fields enum XsnsFunctions {FUNC_SETTINGS_OVERRIDE, FUNC_PIN_STATE, FUNC_MODULE_INIT, FUNC_PRE_INIT, FUNC_INIT, FUNC_LOOP, FUNC_EVERY_50_MSECOND, FUNC_EVERY_100_MSECOND, FUNC_EVERY_200_MSECOND, FUNC_EVERY_250_MSECOND, FUNC_EVERY_300_MSECOND, FUNC_EVERY_SECOND, @@ -279,7 +275,7 @@ enum XsnsFunctions {FUNC_SETTINGS_OVERRIDE, FUNC_PIN_STATE, FUNC_MODULE_INIT, FU FUNC_SET_POWER, FUNC_SET_DEVICE_POWER, FUNC_SHOW_SENSOR, FUNC_ENERGY_EVERY_SECOND, FUNC_ENERGY_RESET, FUNC_RULES_PROCESS, FUNC_SERIAL, FUNC_FREE_MEM, FUNC_BUTTON_PRESSED, - FUNC_WEB_ADD_BUTTON, FUNC_WEB_ADD_MAIN_BUTTON, FUNC_WEB_ADD_HANDLER, FUNC_SET_CHANNELS}; + FUNC_WEB_ADD_BUTTON, FUNC_WEB_ADD_MAIN_BUTTON, FUNC_WEB_ADD_HANDLER, FUNC_SET_CHANNELS, FUNC_SET_SCHEME}; enum AddressConfigSteps { ADDR_IDLE, ADDR_RECEIVE, ADDR_SEND }; diff --git a/sonoff/xdrv_04_light.ino b/sonoff/xdrv_04_light.ino index 237c2da8f..b8ccb46fe 100644 --- a/sonoff/xdrv_04_light.ino +++ b/sonoff/xdrv_04_light.ino @@ -31,12 +31,9 @@ * 5 PWM5 RGBCW yes (H801, Arilux LC11) * 9 reserved no * 10 reserved yes - * 11 +WS2812 RGB(W) no (One WS2812 RGB or RGBW ledstrip) + * 11 +WS2812 RGB 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 * ------------ ------ --------- ---------- ----------------- @@ -56,7 +53,6 @@ * \*********************************************************************************************/ - /*********************************************************************************************\ * * Light management has been refactored to provide a cleaner class-based interface. @@ -128,21 +124,16 @@ #define XDRV_04 4 // #define DEBUG_LIGHT +enum LightSchemes { LS_POWER, LS_WAKEUP, LS_CYCLEUP, LS_CYCLEDN, LS_RANDOM, LS_MAX }; + const uint8_t LIGHT_COLOR_SIZE = 25; // Char array scolor size -const uint8_t WS2812_SCHEMES = 7; // Number of additional WS2812 schemes supported by xdrv_ws2812.ino const char kLightCommands[] PROGMEM = "|" // No prefix -#ifdef USE_WS2812 - D_CMND_LED "|" D_CMND_PIXELS "|" D_CMND_ROTATION "|" D_CMND_WIDTH "|" -#endif // USE_WS2812 D_CMND_COLOR "|" D_CMND_COLORTEMPERATURE "|" D_CMND_DIMMER "|" D_CMND_LEDTABLE "|" D_CMND_FADE "|" D_CMND_RGBWWTABLE "|" D_CMND_SCHEME "|" D_CMND_SPEED "|" D_CMND_WAKEUP "|" D_CMND_WAKEUPDURATION "|" D_CMND_WHITE "|" D_CMND_CHANNEL "|" D_CMND_HSBCOLOR "|UNDOCA" ; void (* const LightCommand[])(void) PROGMEM = { -#ifdef USE_WS2812 - &CmndLed, &CmndPixels, &CmndRotation, &CmndWidth, -#endif // USE_WS2812 &CmndColor, &CmndColorTemperature, &CmndDimmer, &CmndLedTable, &CmndFade, &CmndRgbwwTable, &CmndScheme, &CmndSpeed, &CmndWakeup, &CmndWakeupDuration, &CmndWhite, &CmndChannel, &CmndHsbColor, &CmndUndocA }; @@ -258,6 +249,7 @@ struct LIGHT { uint8_t wakeup_dimmer = 0; uint8_t fixed_color_index = 1; uint8_t pwm_offset = 0; // Offset in color buffer + uint8_t max_scheme = LS_MAX -1; bool update = true; bool pwm_multi_channels = false; // SetOption68, treat each PWM channel as an independant dimmer @@ -1224,11 +1216,7 @@ bool LightModuleInit(void) } light_type = LT_PWM2; } -#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); @@ -1241,18 +1229,10 @@ bool LightModuleInit(void) void LightInit(void) { - uint8_t max_scheme = LS_MAX -1; - Light.device = devices_present; Light.subtype = (light_type & 7) > LST_MAX ? LST_MAX : (light_type & 7); // Always 0 - LST_MAX (5) Light.pwm_multi_channels = Settings.flag3.pwm_multi_channels; -#if defined(USE_WS2812) && (USE_WS2812_CTYPE > NEO_3LED) - if (LT_WS2812 == light_type) { - Light.subtype++; // from RGB to RGBW - } -#endif - if ((LST_SINGLE < Light.subtype) && Light.pwm_multi_channels) { // we treat each PWM channel as an independant one, hence we switch to light_controller.setPWMMultiChannel(true); @@ -1283,25 +1263,8 @@ void LightInit(void) } } } -#ifdef USE_WS2812 // ************************************************************************ - else if (LT_WS2812 == light_type) { - Ws2812Init(); - max_scheme = LS_MAX + WS2812_SCHEMES; - } -#endif // USE_WS2812 ************************************************************************ -/* - else { - light_pdi_pin = pin[GPIO_DI]; - light_pdcki_pin = pin[GPIO_DCKI]; - pinMode(light_pdi_pin, OUTPUT); - pinMode(light_pdcki_pin, OUTPUT); - digitalWrite(light_pdi_pin, LOW); - digitalWrite(light_pdcki_pin, LOW); - - LightMy92x1Init(); - } -*/ + uint32_t max_scheme = Light.max_scheme; if (Light.subtype < LST_RGB) { max_scheme = LS_POWER; } @@ -1480,7 +1443,7 @@ void LightState(uint8_t append) if (Light.subtype >= LST_RGB) { ResponseAppend_P(PSTR(",\"" D_CMND_SCHEME "\":%d"), Settings.light_scheme); } - if (LT_WS2812 == light_type) { + if (Light.max_scheme > LS_MAX) { ResponseAppend_P(PSTR(",\"" D_CMND_WIDTH "\":%d"), Settings.light_width); } ResponseAppend_P(PSTR(",\"" D_CMND_FADE "\":\"%s\",\"" D_CMND_SPEED "\":%d,\"" D_CMND_LEDTABLE "\":\"%s\""), @@ -1736,12 +1699,8 @@ void LightAnimate(void) case LS_RANDOM: LightRandomColor(); break; -#ifdef USE_WS2812 // ************************************************************************ default: - if (LT_WS2812 == light_type) { - Ws2812ShowScheme(Settings.light_scheme -LS_MAX); - } -#endif // USE_WS2812 ************************************************************************ + XlgtCall(FUNC_SET_SCHEME); } } @@ -1860,11 +1819,6 @@ void LightAnimate(void) else if (XdrvCall(FUNC_SET_CHANNELS)) { // Serviced } -#ifdef USE_WS2812 // ************************************************************************ - else if (LT_WS2812 == light_type) { - Ws2812SetColor(0, cur_col[0], cur_col[1], cur_col[2], cur_col[3]); - } -#endif // USE_ES2812 ************************************************************************ XdrvMailbox.data = tmp_data; XdrvMailbox.data_len = tmp_data_len; } @@ -1882,7 +1836,7 @@ void calcGammaCTPwm(uint8_t cur_col[5], uint16_t cur_col_10bits[5]) { // overall brightness uint16_t pxBri = cur_col[cw0] + cur_col[cw1]; if (pxBri > 255) { pxBri = 255; } - cur_col[cw1] = changeUIntScale(cold, 0, cold + warm, 0, 255); // + cur_col[cw1] = changeUIntScale(cold, 0, cold + warm, 0, 255); // cur_col_10bits[cw1] = changeUIntScale(cur_col[cw1], 0, 255, 0, 1023); // channel 0=intensity, channel1=temperature if (Settings.light_correction) { // gamma correction @@ -2169,76 +2123,11 @@ void CmndHsbColor(void) } } -#ifdef USE_WS2812 // *********************************************************************** -void CmndLed(void) -{ - if ((LT_WS2812 == light_type) && (XdrvMailbox.index > 0) && (XdrvMailbox.index <= Settings.light_pixels)) { - if (XdrvMailbox.data_len > 0) { - char *p; - uint16_t idx = XdrvMailbox.index; - Ws2812ForceSuspend(); - for (char *color = strtok_r(XdrvMailbox.data, " ", &p); color; color = strtok_r(nullptr, " ", &p)) { - if (LightColorEntry(color, strlen(color))) { - Ws2812SetColor(idx, Light.entry_color[0], Light.entry_color[1], Light.entry_color[2], Light.entry_color[3]); - idx++; - if (idx > Settings.light_pixels) { break; } - } else { - break; - } - } - - Ws2812ForceUpdate(); - } - char scolor[LIGHT_COLOR_SIZE]; - ResponseCmndIdxChar(Ws2812GetColor(XdrvMailbox.index, scolor)); - } -} - -void CmndPixels(void) -{ - if (LT_WS2812 == light_type) { - if ((XdrvMailbox.payload > 0) && (XdrvMailbox.payload <= WS2812_MAX_LEDS)) { - Settings.light_pixels = XdrvMailbox.payload; - Settings.light_rotation = 0; - Ws2812Clear(); - Light.update = true; - } - ResponseCmndNumber(Settings.light_pixels); - } -} - -void CmndRotation(void) -{ - if (LT_WS2812 == light_type) { - if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload < Settings.light_pixels)) { - Settings.light_rotation = XdrvMailbox.payload; - } - ResponseCmndNumber(Settings.light_rotation); - } -} - -void CmndWidth(void) -{ - if ((LT_WS2812 == light_type) && (XdrvMailbox.index > 0) && (XdrvMailbox.index <= 4)) { - if (1 == XdrvMailbox.index) { - if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 4)) { - Settings.light_width = XdrvMailbox.payload; - } - ResponseCmndNumber(Settings.light_width); - } else { - if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload < 32)) { - Settings.ws_width[XdrvMailbox.index -2] = XdrvMailbox.payload; - } - ResponseCmndIdxNumber(Settings.ws_width[XdrvMailbox.index -2]); - } - } -} -#endif // USE_WS2812 ************************************************************************ - void CmndScheme(void) { if (Light.subtype >= LST_RGB) { - uint32_t max_scheme = (LT_WS2812 == light_type) ? LS_MAX + WS2812_SCHEMES : LS_MAX -1; + uint32_t max_scheme = Light.max_scheme; + if (1 == XdrvMailbox.data_len) { if (('+' == XdrvMailbox.data[0]) && (Settings.light_scheme < max_scheme)) { XdrvMailbox.payload = Settings.light_scheme + ((0 == Settings.light_scheme) ? 2 : 1); // Skip wakeup @@ -2435,6 +2324,9 @@ bool Xdrv04(uint8_t function) break; case FUNC_COMMAND: result = DecodeCommand(kLightCommands, LightCommand); + if (!result) { + result = XlgtCall(FUNC_COMMAND); + } break; case FUNC_PRE_INIT: LightInit(); diff --git a/sonoff/xplg_ws2812.ino b/sonoff/xlgt_01_ws2812.ino similarity index 71% rename from sonoff/xplg_ws2812.ino rename to sonoff/xlgt_01_ws2812.ino index 8b15d4286..2c0a11f0b 100644 --- a/sonoff/xplg_ws2812.ino +++ b/sonoff/xlgt_01_ws2812.ino @@ -1,7 +1,7 @@ /* - xplg_ws2812.ino - ws2812 led string support for Sonoff-Tasmota + xlgt_01_ws2812.ino - led string support for Sonoff-Tasmota - Copyright (C) 2019 Heiko Krupp and Theo Arends + 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 @@ -21,8 +21,30 @@ #ifdef USE_WS2812 /*********************************************************************************************\ * WS2812 RGB / RGBW Leds using NeopixelBus library + * + * light_scheme WS2812 3+ Colors 1+2 Colors Effect + * ------------ ------ --------- ---------- ----------------- + * 0 yes no no Clock + * 1 yes no no Incandescent + * 2 yes no no RGB + * 3 yes no no Christmas + * 4 yes no no Hanukkah + * 5 yes no no Kwanzaa + * 6 yes no no Rainbow + * 7 yes no no Fire + * \*********************************************************************************************/ +#define XLGT_01 1 + +const uint8_t WS2812_SCHEMES = 8; // Number of WS2812 schemes + +const char kWs2812Commands[] PROGMEM = "|" // No prefix + D_CMND_LED "|" D_CMND_PIXELS "|" D_CMND_ROTATION "|" D_CMND_WIDTH ; + +void (* const Ws2812Command[])(void) PROGMEM = { + &CmndLed, &CmndPixels, &CmndRotation, &CmndWidth }; + #include #if (USE_WS2812_CTYPE == NEO_GRB) @@ -83,7 +105,7 @@ WsColor kHanukkah[2] = { 0,0,255, 255,255,255 }; WsColor kwanzaa[3] = { 255,0,0, 0,0,0, 0,255,0 }; WsColor kRainbow[7] = { 255,0,0, 255,128,0, 255,255,0, 0,255,0, 0,0,255, 128,0,255, 255,0,255 }; WsColor kFire[3] = { 255,0,0, 255,102,0, 255,192,0 }; -ColorScheme kSchemes[WS2812_SCHEMES] = { +ColorScheme kSchemes[WS2812_SCHEMES -1] = { // Skip clock scheme kIncandescent, 2, kRgb, 3, kChristmas, 2, @@ -105,8 +127,11 @@ uint8_t kWsRepeat[5] = { 2, // Largest 1 }; // All -uint8_t ws_show_next = 1; -bool ws_suspend_update = false; +struct WS2812 { + uint8_t show_next = 1; + uint8_t scheme_offset = 0; + bool suspend_update = false; +} Ws2812; /********************************************************************************************/ @@ -313,23 +338,11 @@ void Ws2812Bars(uint32_t schemenr) Ws2812StripShow(); } -/*********************************************************************************************\ - * Public -\*********************************************************************************************/ - -void Ws2812Init(void) -{ - // For DMA, the Pin is ignored as it uses GPIO3 due to DMA hardware use. - strip = new NeoPixelBus(WS2812_MAX_LEDS, pin[GPIO_WS2812]); - strip->Begin(); - Ws2812Clear(); -} - void Ws2812Clear(void) { strip->ClearTo(0); strip->Show(); - ws_show_next = 1; + Ws2812.show_next = 1; } void Ws2812SetColor(uint32_t led, uint8_t red, uint8_t green, uint8_t blue, uint8_t white) @@ -353,22 +366,12 @@ void Ws2812SetColor(uint32_t led, uint8_t red, uint8_t green, uint8_t blue, uint } } - if (!ws_suspend_update) { + if (!Ws2812.suspend_update) { strip->Show(); - ws_show_next = 1; + Ws2812.show_next = 1; } } -void Ws2812ForceSuspend (void) { - ws_suspend_update = true; -} - -void Ws2812ForceUpdate (void) { - ws_suspend_update = false; - strip->Show(); - ws_show_next = 1; -} - char* Ws2812GetColor(uint32_t led, char* scolor) { uint8_t sl_ledcolor[4]; @@ -393,13 +396,42 @@ char* Ws2812GetColor(uint32_t led, char* scolor) return scolor; } -void Ws2812ShowScheme(uint32_t scheme) +/*********************************************************************************************\ + * Public - used by scripter only +\*********************************************************************************************/ + +void Ws2812ForceSuspend (void) { + Ws2812.suspend_update = true; +} + +void Ws2812ForceUpdate (void) +{ + Ws2812.suspend_update = false; + strip->Show(); + Ws2812.show_next = 1; +} + +/********************************************************************************************/ + +bool Ws2812SetChannels(void) +{ + uint8_t *cur_col = (uint8_t*)XdrvMailbox.data; + + Ws2812SetColor(0, cur_col[0], cur_col[1], cur_col[2], cur_col[3]); + + return true; +} + +void Ws2812ShowScheme(void) +{ + uint32_t scheme = Settings.light_scheme - Ws2812.scheme_offset; + switch (scheme) { case 0: // Clock - if ((1 == state_250mS) || (ws_show_next)) { + if ((1 == state_250mS) || (Ws2812.show_next)) { Ws2812Clock(); - ws_show_next = 0; + Ws2812.show_next = 0; } break; default: @@ -408,10 +440,118 @@ void Ws2812ShowScheme(uint32_t scheme) } else { Ws2812Bars(scheme -1); } - ws_show_next = 1; + Ws2812.show_next = 1; break; } } +void Ws2812ModuleSelected(void) +{ + if (pin[GPIO_WS2812] < 99) { // RGB led + + // For DMA, the Pin is ignored as it uses GPIO3 due to DMA hardware use. + strip = new NeoPixelBus(WS2812_MAX_LEDS, pin[GPIO_WS2812]); + strip->Begin(); + + Ws2812Clear(); + + Ws2812.scheme_offset = Light.max_scheme +1; + Light.max_scheme += WS2812_SCHEMES; + +#if (USE_WS2812_CTYPE > NEO_3LED) + light_type = LT_RGBW; +#else + light_type = LT_RGB; +#endif + light_flg = XLGT_01; + } +} + +/********************************************************************************************/ + +void CmndLed(void) +{ + if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= Settings.light_pixels)) { + if (XdrvMailbox.data_len > 0) { + char *p; + uint16_t idx = XdrvMailbox.index; + Ws2812ForceSuspend(); + for (char *color = strtok_r(XdrvMailbox.data, " ", &p); color; color = strtok_r(nullptr, " ", &p)) { + if (LightColorEntry(color, strlen(color))) { + Ws2812SetColor(idx, Light.entry_color[0], Light.entry_color[1], Light.entry_color[2], Light.entry_color[3]); + idx++; + if (idx > Settings.light_pixels) { break; } + } else { + break; + } + } + Ws2812ForceUpdate(); + } + char scolor[LIGHT_COLOR_SIZE]; + ResponseCmndIdxChar(Ws2812GetColor(XdrvMailbox.index, scolor)); + } +} + +void CmndPixels(void) +{ + if ((XdrvMailbox.payload > 0) && (XdrvMailbox.payload <= WS2812_MAX_LEDS)) { + Settings.light_pixels = XdrvMailbox.payload; + Settings.light_rotation = 0; + Ws2812Clear(); + Light.update = true; + } + ResponseCmndNumber(Settings.light_pixels); +} + +void CmndRotation(void) +{ + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload < Settings.light_pixels)) { + Settings.light_rotation = XdrvMailbox.payload; + } + ResponseCmndNumber(Settings.light_rotation); +} + +void CmndWidth(void) +{ + if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= 4)) { + if (1 == XdrvMailbox.index) { + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 4)) { + Settings.light_width = XdrvMailbox.payload; + } + ResponseCmndNumber(Settings.light_width); + } else { + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload < 32)) { + Settings.ws_width[XdrvMailbox.index -2] = XdrvMailbox.payload; + } + ResponseCmndIdxNumber(Settings.ws_width[XdrvMailbox.index -2]); + } + } +} + +/*********************************************************************************************\ + * Interface +\*********************************************************************************************/ + +bool Xlgt01(uint8_t function) +{ + bool result = false; + + switch (function) { + case FUNC_SET_CHANNELS: + result = Ws2812SetChannels(); + break; + case FUNC_SET_SCHEME: + Ws2812ShowScheme(); + break; + case FUNC_COMMAND: + result = DecodeCommand(kWs2812Commands, Ws2812Command); + break; + case FUNC_MODULE_INIT: + Ws2812ModuleSelected(); + break; + } + return result; +} + #endif // USE_WS2812 -#endif // USE_LIGHT +#endif // USE_LIGHT \ No newline at end of file