From e52961b3b46b5dc0846d1eb93913c92e87cc5bef Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Tue, 30 Jun 2020 16:58:36 +0200 Subject: [PATCH] Add rotary encoder support - Add rotary encoder support for light dimmer and optional color temperature if button1 still pressed (#8670) - Fix Mi Desk Lamp brightness control (#8748) --- RELEASENOTES.md | 1 + tasmota/CHANGELOG.md | 1 + tasmota/my_user_config.h | 2 +- tasmota/support_rotary.ino | 107 ++++++++++--------------------- tasmota/tasmota_configurations.h | 2 +- tasmota/xdrv_04_light.ino | 26 +++++++- 6 files changed, 62 insertions(+), 77 deletions(-) diff --git a/RELEASENOTES.md b/RELEASENOTES.md index d8c865fdbd69..45a026fe5f96 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -91,3 +91,4 @@ The following binary downloads have been compiled with ESP8266/Arduino library c - Add support for single wire LMT01 temperature Sensor by justifiably (#8713) - Add compile time interlock parameters (#8759) - Add compile time user template (#8766) +- Add rotary encoder support for light dimmer and optional color temperature if button1 still pressed (#8670) diff --git a/tasmota/CHANGELOG.md b/tasmota/CHANGELOG.md index 95fdf9efd16b..f40ebd895c6b 100644 --- a/tasmota/CHANGELOG.md +++ b/tasmota/CHANGELOG.md @@ -8,6 +8,7 @@ - Add support for single wire LMT01 temperature Sensor by justifiably (#8713) - Add compile time interlock parameters (#8759) - Add compile time user template (#8766) +- Add rotary encoder support for light dimmer and optional color temperature if button1 still pressed (#8670) - Fix exception or watchdog on rule re-entry (#8757) - Change ESP32 USER GPIO template representation decreasing template message size - Change define USE_TASMOTA_SLAVE into USE_TASMOTA_CLIENT diff --git a/tasmota/my_user_config.h b/tasmota/my_user_config.h index e1e75e4b1111..fa1d90afd459 100644 --- a/tasmota/my_user_config.h +++ b/tasmota/my_user_config.h @@ -429,7 +429,7 @@ // #define SUPPORT_MQTT_EVENT // Support trigger event with MQTT subscriptions (+3k5 code) // -- Optional modules ---------------------------- -//#define ROTARY_V1 // Add support for MI Desk Lamp +#define ROTARY_V1 // Add support for Rotary Encoder as used in MI Desk Lamp (+0k8 code) #define USE_SONOFF_RF // Add support for Sonoff Rf Bridge (+3k2 code) #define USE_RF_FLASH // Add support for flashing the EFM8BB1 chip on the Sonoff RF Bridge. C2CK must be connected to GPIO4, C2D to GPIO5 on the PCB (+2k7 code) #define USE_SONOFF_SC // Add support for Sonoff Sc (+1k1 code) diff --git a/tasmota/support_rotary.ino b/tasmota/support_rotary.ino index 713ab4871079..ec804173e886 100644 --- a/tasmota/support_rotary.ino +++ b/tasmota/support_rotary.ino @@ -18,7 +18,6 @@ */ #ifdef USE_LIGHT -//#define ROTARY_V1 #ifdef ROTARY_V1 /*********************************************************************************************\ * Rotary support @@ -36,49 +35,40 @@ struct ROTARY { /********************************************************************************************/ -#ifndef ARDUINO_ESP8266_RELEASE_2_3_0 // Fix core 2.5.x ISR not in IRAM Exception void update_rotary(void) ICACHE_RAM_ATTR; -#endif // ARDUINO_ESP8266_RELEASE_2_3_0 - -void update_rotary(void) -{ - if (MI_DESK_LAMP == my_module_type) { - if (LightPowerIRAM() && !Rotary.busy) { - /* - * https://github.com/PaulStoffregen/Encoder/blob/master/Encoder.h - */ - - uint8_t s = Rotary.state & 3; - if (digitalRead(Pin(GPIO_ROT1A))) { s |= 4; } - if (digitalRead(Pin(GPIO_ROT1B))) { s |= 8; } - switch (s) { - case 0: case 5: case 10: case 15: - break; - case 1: case 7: case 8: case 14: - Rotary.position++; break; - case 2: case 4: case 11: case 13: - Rotary.position--; break; - case 3: case 12: - Rotary.position = Rotary.position + 2; break; - default: - Rotary.position = Rotary.position - 2; break; - } - Rotary.state = (s >> 2); - } +void update_rotary(void) { + if (Rotary.busy || !LightPowerIRAM()) { return; } + + /* + * https://github.com/PaulStoffregen/Encoder/blob/master/Encoder.h + */ + uint8_t s = Rotary.state & 3; + if (digitalRead(Pin(GPIO_ROT1A))) { s |= 4; } + if (digitalRead(Pin(GPIO_ROT1B))) { s |= 8; } + switch (s) { + case 0: case 5: case 10: case 15: + break; + case 1: case 7: case 8: case 14: + Rotary.position++; break; + case 2: case 4: case 11: case 13: + Rotary.position--; break; + case 3: case 12: + Rotary.position = Rotary.position + 2; break; + default: + Rotary.position = Rotary.position - 2; break; } + Rotary.state = (s >> 2); } -bool RotaryButtonPressed(void) -{ - if ((MI_DESK_LAMP == my_module_type) && (Rotary.changed) && LightPower()) { +bool RotaryButtonPressed(void) { + if (Rotary.changed && LightPower()) { Rotary.changed = 0; // Color temp changed, no need to turn of the light return true; } return false; } -void RotaryInit(void) -{ +void RotaryInit(void) { Rotary.present = 0; if (PinUsed(GPIO_ROT1A) && PinUsed(GPIO_ROT1B)) { Rotary.present++; @@ -93,57 +83,30 @@ void RotaryInit(void) * Rotary handler \*********************************************************************************************/ -void RotaryHandler(void) -{ +void RotaryHandler(void) { if (Rotary.last_position != Rotary.position) { Rotary.busy = true; int rotary_position = Rotary.position - Rotary.last_position; - if (MI_DESK_LAMP == my_module_type) { // Mi Desk lamp - if (Button.hold_timer[0]) { - Rotary.changed = 1; - // button1 is pressed: set color temperature - int16_t t = LightGetColorTemp(); - t = t + ((rotary_position) * 4); - if (t < 153) { - t = 153; - } - if (t > 500) { - t = 500; - } - DEBUG_CORE_LOG(PSTR("ROT: " D_CMND_COLORTEMPERATURE " %d"), rotary_position); - LightSetColorTemp((uint16_t)t); - -// char scmnd[20]; -// snprintf_P(scmnd, sizeof(scmnd), PSTR(D_CMND_COLORTEMPERATURE " %d"), t); -// ExecuteCommand(scmnd, SRC_SWITCH); - } else { - int8_t d = Settings.light_dimmer; - d = d + rotary_position; - if (d < 1) { - d = 1; - } - if (d > 100) { - d = 100; - } - DEBUG_CORE_LOG(PSTR("ROT: " D_CMND_DIMMER " %d"), rotary_position); - - char scmnd[20]; - snprintf_P(scmnd, sizeof(scmnd), PSTR(D_CMND_DIMMER "0 %d"), d); - ExecuteCommand(scmnd, SRC_SWITCH); - } + if (Button.hold_timer[0]) { // Button1 is pressed: set color temperature +// AddLog_P2(LOG_LEVEL_DEBUG, PSTR("ROT: " D_CMND_COLORTEMPERATURE " %d"), rotary_position); + Rotary.changed = 1; + LightColorTempOffset(rotary_position * 4); + } else { +// AddLog_P2(LOG_LEVEL_DEBUG, PSTR("ROT: " D_CMND_DIMMER " %d"), rotary_position); + LightDimmerOffset(rotary_position); } + Rotary.last_position = 128; Rotary.position = 128; Rotary.busy = false; } } -void RotaryLoop(void) -{ +void RotaryLoop(void) { if (Rotary.present) { if (TimeReached(Rotary.debounce)) { - SetNextTimeInterval(Rotary.debounce, Settings.button_debounce); // Using button_debounce setting for this as well + SetNextTimeInterval(Rotary.debounce, Settings.button_debounce); // Using button_debounce setting for this as well RotaryHandler(); } } diff --git a/tasmota/tasmota_configurations.h b/tasmota/tasmota_configurations.h index be5f3a4f12a8..58d08d543d09 100644 --- a/tasmota/tasmota_configurations.h +++ b/tasmota/tasmota_configurations.h @@ -33,7 +33,7 @@ #undef USE_DISCOVERY // Disable mDNS (+8k code or +23.5k code with core 2_5_x, +0.3k mem) // -- Optional modules ---------------------------- -//#define ROTARY_V1 // Add support for MI Desk Lamp +#define ROTARY_V1 // Add support for Rotary Encoder as used in MI Desk Lamp #define USE_SONOFF_RF // Add support for Sonoff Rf Bridge (+3k2 code) #define USE_RF_FLASH // Add support for flashing the EFM8BB1 chip on the Sonoff RF Bridge. C2CK must be connected to GPIO4, C2D to GPIO5 on the PCB (+2k7 code) #define USE_SONOFF_SC // Add support for Sonoff Sc (+1k1 code) diff --git a/tasmota/xdrv_04_light.ino b/tasmota/xdrv_04_light.ino index a47787f90783..9755d39040d4 100644 --- a/tasmota/xdrv_04_light.ino +++ b/tasmota/xdrv_04_light.ino @@ -895,7 +895,7 @@ void LightStateClass::XyToRgb(float x, float y, uint8_t *rr, uint8_t *rg, uint8_ 0.0557f, -0.2040f, 1.0570f }; mat3x3(rgb_factors, XYZ, rgb); float max = (rgb[0] > rgb[1] && rgb[0] > rgb[2]) ? rgb[0] : (rgb[1] > rgb[2]) ? rgb[1] : rgb[2]; - + for (uint32_t i = 0; i < 3; i++) { rgb[i] = rgb[i] / max; // normalize to max == 1.0 rgb[i] = (rgb[i] <= 0.0031308f) ? 12.92f * rgb[i] : 1.055f * POW(rgb[i], (1.0f / 2.4f)) - 0.055f; // gamma @@ -1478,12 +1478,22 @@ void LightSetBri(uint8_t device, uint8_t bri) { } } +void LightColorTempOffset(int32_t offset) { + int32_t ct = LightGetColorTemp(); + if (0 == ct) { return; } // CT not supported + ct += offset; + if (ct < CT_MIN) { ct = CT_MIN; } + else if (ct > CT_MAX) { ct = CT_MAX; } + + LightSetColorTemp(ct); +} + void LightSetColorTemp(uint16_t ct) { /* Color Temperature (https://developers.meethue.com/documentation/core-concepts) * - * ct = 153 = 6500K = Cold = CCWW = FF00 - * ct = 600 = 2000K = Warm = CCWW = 00FF + * ct = 153 mirek = 6500K = Cold = CCWW = FF00 + * ct = 500 mirek = 2000K = Warm = CCWW = 00FF */ // don't set CT if not supported if ((LST_COLDWARM != Light.subtype) && (LST_RGBCW != Light.subtype)) { @@ -2745,6 +2755,16 @@ void CmndColorTemperature(void) } } +void LightDimmerOffset(int32_t offset) { + int32_t dimmer = light_state.getDimmer() + offset; + if (dimmer < 1) { dimmer = 1; } + if (dimmer > 100) { dimmer = 100; } + + XdrvMailbox.index = 0; + XdrvMailbox.payload = dimmer; + CmndDimmer(); +} + void CmndDimmer(void) { // Dimmer - Show current Dimmer state