From c858710aef435b6487b6a8cce7c53e9e78f1e692 Mon Sep 17 00:00:00 2001 From: Andreas Schultz Date: Tue, 15 Oct 2019 16:41:43 +0200 Subject: [PATCH 1/2] fix pwm multi channel (Option68) With Option68 enabled, the pwm channels are independent. The channel setting code was indirectly using setRGB and setCW. Those methods recalculate the channel values to adjust for brightness, thereby modifying the channel values. This change add raw channel set/get methods that do not modify the channels values and uses those when pwm_multichannel is in effect. --- sonoff/_changelog.ino | 1 + sonoff/xdrv_04_light.ino | 47 ++++++++++++++++++++++++++++------------ 2 files changed, 34 insertions(+), 14 deletions(-) diff --git a/sonoff/_changelog.ino b/sonoff/_changelog.ino index e12f18640..7d3a871b6 100644 --- a/sonoff/_changelog.ino +++ b/sonoff/_changelog.ino @@ -7,6 +7,7 @@ * Add define USE_SONOFF_RF to enable/disable Sonoff Rf support (#6648) * Add incremental beeps to Ifan03 remote control fan speed buttons (#6636) * Add rule support after every command execution like Fanspeed#Data=2 (#6636) + * Fix handling of ligth channels when pwm_multichannel (Option68) is enabled * * 6.6.0.17 20191009 * Add command SetOption34 0..255 to set backlog delay. Default value is 200 (mSeconds) (#6562) diff --git a/sonoff/xdrv_04_light.ino b/sonoff/xdrv_04_light.ino index 2fafd2e6c..603e95f30 100644 --- a/sonoff/xdrv_04_light.ino +++ b/sonoff/xdrv_04_light.ino @@ -420,6 +420,14 @@ class LightStateClass { getActualRGBCW(&channels[0], &channels[1], &channels[2], &channels[3], &channels[4]); } + void getChannelsRaw(uint8_t *channels) { + channels[0] = _r; + channels[1] = _g; + channels[2] = _b; + channels[3] = _wc; + channels[4] = _ww; + } + void getHSB(uint16_t *hue, uint8_t *sat, uint8_t *bri) { if (hue) { *hue = _hue; } if (sat) { *sat = _sat; } @@ -602,6 +610,16 @@ class LightStateClass { #endif } + // set all 5 channels at once, don't modify the values in ANY way + // Channels are: R G B CW WW + void setChannelsRaw(uint8_t *channels) { + _r = channels[0]; + _g = channels[1]; + _b = channels[2]; + _wc = channels[3]; + _ww = channels[4]; +} + // set all 5 channels at once. // Channels are: R G B CW WW // Brightness is automatically recalculated to adjust channels to the desired values @@ -838,10 +856,13 @@ public: AddLog_P2(LOG_LEVEL_DEBUG_MORE, "LightControllerClass::loadSettings light_type/sub (%d %d)", light_type, Light.subtype); #endif - // first try setting CW, if zero, it select RGB mode - _state->setCW(Settings.light_color[3], Settings.light_color[4], true); - _state->setRGB(Settings.light_color[0], Settings.light_color[1], Settings.light_color[2]); - if (!_pwm_multi_channels) { + if (_pwm_multi_channels) { + _state->setChannelsRaw(Settings.light_color); + } else { + // first try setting CW, if zero, it select RGB mode + _state->setCW(Settings.light_color[3], Settings.light_color[4], true); + _state->setRGB(Settings.light_color[0], Settings.light_color[1], Settings.light_color[2]); + // only if non-multi channel // We apply dimmer in priority to RGB uint8_t bri = _state->DimmerToBri(Settings.light_dimmer); @@ -893,16 +914,13 @@ public: // calculate the levels for each channel void calcLevels() { uint8_t r,g,b,c,w,briRGB,briCT; - _state->getActualRGBCW(&r,&g,&b,&c,&w); if (_pwm_multi_channels) { // if PWM multi channel, no more transformation required - Light.current_color[0] = r; - Light.current_color[1] = g; - Light.current_color[2] = b; - Light.current_color[3] = c; - Light.current_color[4] = w; + _state->getChannelsRaw(Light.current_color); return; } + + _state->getActualRGBCW(&r,&g,&b,&c,&w); briRGB = _state->getBriRGB(); briCT = _state->getBriCT(); @@ -948,9 +966,7 @@ public: void saveSettings() { if (Light.pwm_multi_channels) { // simply save each channel - _state->getActualRGBCW(&Settings.light_color[0], &Settings.light_color[1], - &Settings.light_color[2], &Settings.light_color[3], - &Settings.light_color[4]); + _state->getChannelsRaw(Settings.light_color); Settings.light_dimmer = 100; // arbitrary value, unused in this mode } else { uint8_t cm = _state->getColorMode(); @@ -980,13 +996,16 @@ public: // Channels are: R G B CW WW // Brightness is automatically recalculated to adjust channels to the desired values void changeChannels(uint8_t *channels) { - if (LST_COLDWARM == Light.subtype) { + if (Light.pwm_multi_channels) { + _state->setChannelsRaw(channels); + } else if (LST_COLDWARM == Light.subtype) { // remap channels 0-1 to 3-4 if cold/warm uint8_t remapped_channels[5] = {0,0,0,channels[0],channels[1]}; _state->setChannels(remapped_channels); } else { _state->setChannels(channels); } + saveSettings(); calcLevels(); } From e1448edb252cf013efc3b3ef2024d1796d0cae5d Mon Sep 17 00:00:00 2001 From: Andreas Schultz Date: Tue, 15 Oct 2019 16:41:53 +0200 Subject: [PATCH 2/2] add Web UI for PWM multi channel (Option68) --- sonoff/_changelog.ino | 1 + sonoff/xdrv_01_webserver.ino | 83 +++++++++++++++++------------------- 2 files changed, 41 insertions(+), 43 deletions(-) diff --git a/sonoff/_changelog.ino b/sonoff/_changelog.ino index 7d3a871b6..882d09787 100644 --- a/sonoff/_changelog.ino +++ b/sonoff/_changelog.ino @@ -8,6 +8,7 @@ * Add incremental beeps to Ifan03 remote control fan speed buttons (#6636) * Add rule support after every command execution like Fanspeed#Data=2 (#6636) * Fix handling of ligth channels when pwm_multichannel (Option68) is enabled + * Add WebUI for multiple, independent PWM channels * * 6.6.0.17 20191009 * Add command SetOption34 0..255 to set backlog delay. Default value is 200 (mSeconds) (#6562) diff --git a/sonoff/xdrv_01_webserver.ino b/sonoff/xdrv_01_webserver.ino index 474ce1289..936b46c28 100644 --- a/sonoff/xdrv_01_webserver.ino +++ b/sonoff/xdrv_01_webserver.ino @@ -153,39 +153,17 @@ const char HTTP_SCRIPT_ROOT[] PROGMEM = #endif // USE_SCRIPT_WEB_DISPLAY #ifdef USE_JAVASCRIPT_ES6 - "lb=p=>la('&d='+p);" // Dark - Bright &d related to lb(value) and WebGetArg("d", tmp, sizeof(tmp)); - "lc=p=>la('&t='+p);" // Cold - Warm &t related to lc(value) and WebGetArg("t", tmp, sizeof(tmp)); + "lb=(v,p)=>la(`&${v}=${p}`);" + "lc=(v,i,p)=>la(`&${v}${i}=${p}`);" #else - "function lb(p){" - "la('&d='+p);" // &d related to WebGetArg("d", tmp, sizeof(tmp)); + "function lb(v,p){" + "la('&'+v+'='+p);" "}" - "function lc(p){" - "la('&t='+p);" // &t related to WebGetArg("t", tmp, sizeof(tmp)); + "function lc(v,i,p){" + "la('&'+v+i+'='+p);" "}" #endif // USE_JAVASCRIPT_ES6 -#ifdef USE_SHUTTER -#ifdef USE_JAVASCRIPT_ES6 - "ld1=p=>la('&u1='+p);" - "ld2=p=>la('&u2='+p);" - "ld3=p=>la('&u3='+p);" - "ld4=p=>la('&u4='+p);" -#else - "function ld1(p){" - "la('&u1='+p);" - "}" - "function ld2(p){" - "la('&u2='+p);" - "}" - "function ld3(p){" - "la('&u3='+p);" - "}" - "function ld4(p){" - "la('&u4='+p);" - "}" -#endif // USE_JAVASCRIPT_ES6 -#endif // USE_SHUTTER - "wl(la);"; const char HTTP_SCRIPT_WIFI[] PROGMEM = @@ -395,16 +373,11 @@ const char HTTP_HEAD_STYLE3[] PROGMEM = "

%s

"; const char HTTP_MSG_SLIDER1[] PROGMEM = - "
" D_COLDLIGHT "" D_WARMLIGHT "
" - "
"; + "
%s%s
" + "
"; const char HTTP_MSG_SLIDER2[] PROGMEM = - "
" D_DARKLIGHT "" D_BRIGHTLIGHT "
" - "
"; -#ifdef USE_SHUTTER -const char HTTP_MSG_SLIDER3[] PROGMEM = - "
" D_CLOSE "" D_OPEN "
" - "
"; -#endif // USE_SHUTTER + "
%s%s
" + "
"; const char HTTP_MSG_RSTRT[] PROGMEM = "
" D_DEVICE_WILL_RESTART "

"; @@ -1012,16 +985,31 @@ void HandleRoot(void) if (devices_present) { #ifdef USE_LIGHT if (light_type) { - if ((LST_COLDWARM == (light_type &7)) || (LST_RGBWC == (light_type &7))) { - WSContentSend_P(HTTP_MSG_SLIDER1, LightGetColorTemp()); - } - WSContentSend_P(HTTP_MSG_SLIDER2, Settings.light_dimmer); + if (!Settings.flag3.pwm_multi_channels) { + if ((LST_COLDWARM == (light_type &7)) || (LST_RGBWC == (light_type &7))) { + // Cold - Warm &t related to lb("t", value) and WebGetArg("t", tmp, sizeof(tmp)); + WSContentSend_P(HTTP_MSG_SLIDER1, F(D_COLDLIGHT), F(D_WARMLIGHT), + 153, 500, LightGetColorTemp(), 't'); + } + // Dark - Bright &d related to lb("d", value) and WebGetArg("d", tmp, sizeof(tmp)); + WSContentSend_P(HTTP_MSG_SLIDER1, F(D_DARKLIGHT), F(D_BRIGHTLIGHT), + 1, 100, Settings.light_dimmer, 'd'); + } else { // Settings.flag3.pwm_multi_channels + uint32_t pwm_channels = (light_type & 7) > LST_MAX ? LST_MAX : (light_type & 7); + for (uint32_t i = 0; i < pwm_channels; i++) { + snprintf_P(stemp, sizeof(stemp), PSTR("c%d"), i); + WSContentSend_P(HTTP_MSG_SLIDER2, stemp, FPSTR("100%"), + 1, 100, + changeUIntScale(Settings.light_color[i], 0, 255, 0, 100), 'd', i+1); + } + } // Settings.flag3.pwm_multi_channels } #endif #ifdef USE_SHUTTER if (Settings.flag3.shutter_mode) { for (uint32_t i = 0; i < shutters_present; i++) { - WSContentSend_P(HTTP_MSG_SLIDER3, Settings.shutter_position[i], i+1); + WSContentSend_P(HTTP_MSG_SLIDER2, F(D_CLOSE), F(D_OPEN), + 0, 100, Settings.shutter_position[i], 'u', i+1); } } #endif // USE_SHUTTER @@ -1097,6 +1085,7 @@ bool HandleRootStatusRefresh(void) char tmp[8]; // WebGetArg numbers only char svalue[32]; // Command and number parameter + char webindex[5]; // WebGetArg name WebGetArg("o", tmp, sizeof(tmp)); // 1 - 16 Device number for button Toggle or Fanspeed if (strlen(tmp)) { @@ -1122,13 +1111,21 @@ bool HandleRootStatusRefresh(void) snprintf_P(svalue, sizeof(svalue), PSTR(D_CMND_DIMMER " %s"), tmp); ExecuteWebCommand(svalue, SRC_WEBGUI); } + uint32_t pwm_channels = (light_type & 7) > LST_MAX ? LST_MAX : (light_type & 7); + for (uint32_t j = 1; j <= pwm_channels; j++) { + snprintf_P(webindex, sizeof(webindex), PSTR("d%d"), j); + WebGetArg(webindex, tmp, sizeof(tmp)); // 0 - 100 percent + if (strlen(tmp)) { + snprintf_P(svalue, sizeof(svalue), PSTR(D_CMND_CHANNEL "%d %s"), j, tmp); + ExecuteWebCommand(svalue, SRC_WEBGUI); + } + } WebGetArg("t", tmp, sizeof(tmp)); // 153 - 500 Color temperature if (strlen(tmp)) { snprintf_P(svalue, sizeof(svalue), PSTR(D_CMND_COLORTEMPERATURE " %s"), tmp); ExecuteWebCommand(svalue, SRC_WEBGUI); } #ifdef USE_SHUTTER - char webindex[5]; // WebGetArg name for (uint32_t j = 1; j <= shutters_present; j++) { snprintf_P(webindex, sizeof(webindex), PSTR("u%d"), j); WebGetArg(webindex, tmp, sizeof(tmp)); // 0 - 100 percent