From feab61627751c685bd4081330db201d05b806b97 Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Wed, 1 Sep 2021 11:14:30 +0200 Subject: [PATCH] Fix Sonoff L1 (lite) smoother color transitions --- CHANGELOG.md | 1 + RELEASENOTES.md | 1 + tasmota/xlgt_05_sonoff_l1.ino | 115 +++++++++++++++++++++++++--------- 3 files changed, 86 insertions(+), 31 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0a6e3102d..ca682600c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -25,6 +25,7 @@ All notable changes to this project will be documented in this file. - Hass and Tasmota discovery prefix topic notifications (#12972) - Unable to disable MusicSync mode on Sonoff L1 Lite regression from 9.3.0 (#12930) - Shelly Dimmer 2 Energy usage (#12815) +- Sonoff L1 (lite) smoother color transitions ## [9.5.0.6] 20210820 ### Added diff --git a/RELEASENOTES.md b/RELEASENOTES.md index efd6c2d38..7d1db9225 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -157,6 +157,7 @@ The latter links can be used for OTA upgrades too like ``OtaUrl http://ota.tasmo - ESP32 core v2.0.0 setting hostname - ESP32-C3 settings layout for configuration backup and restore - ESP32-Solo OTA upgrade +- Sonoff L1 (lite) smoother color transitions - DDS238-2 wrong reactive power value [#12283](https://github.com/arendst/Tasmota/issues/12283) - ESP32 Webcam add boundary marker before sending mjpeg image [#12376](https://github.com/arendst/Tasmota/issues/12376) - NO VALID JSON regression from may 4th [#12440](https://github.com/arendst/Tasmota/issues/12440) diff --git a/tasmota/xlgt_05_sonoff_l1.ino b/tasmota/xlgt_05_sonoff_l1.ino index 6be1205f2..080bc9c7a 100644 --- a/tasmota/xlgt_05_sonoff_l1.ino +++ b/tasmota/xlgt_05_sonoff_l1.ino @@ -26,9 +26,11 @@ #define XLGT_05 5 #define SONOFF_L1_START_DELAY // Sync Nuvotron power state with Tasmota on power up -//#define SONOFF_L1_ALLOW_REMOTE_INTERRUPT // During schemes 2..4 +//#define SONOFF_L1_ALLOW_REMOTE_INTERRUPT // During schemes 2..4 or if fade is active #define SONOFF_L1_DEBUG1 // Add send and receive logging +#define SONOFF_L1_BUSY 200 // Time in milliseconds to handle a serial request + #define SONOFF_L1_BUFFER_SIZE 170 #define SONOFF_L1_MODE_COLORFUL 1 // [Color key] Colorful (static color) @@ -46,10 +48,10 @@ struct SNFL1 { char *buffer; -#ifdef SONOFF_L1_ALLOW_REMOTE_INTERRUPT + uint32_t process_time = 0; uint32_t unlock = 0; - bool receive_ready = true; -#endif + uint32_t busy = SONOFF_L1_BUSY; + uint16_t sequence; uint8_t color[3]; uint8_t dimmer; uint8_t power; @@ -75,18 +77,29 @@ void SnfL1SendDelayed(void) { } #endif // SONOFF_L1_START_DELAY +#include +Ticker SnfL1Backlog; + +void SnfL1SendBacklog(void) { +// AddLog(LOG_LEVEL_DEBUG, PSTR("SL1: ++Exec backlog")); + + SnfL1SetChannels(true); +} + void SnfL1Send(void) { #ifdef SONOFF_L1_DEBUG1 - AddLog(LOG_LEVEL_DEBUG, PSTR("SL1: Send %s"), Snfl1.buffer); + AddLog(LOG_LEVEL_DEBUG, PSTR("SL1: Send '%s'"), Snfl1.buffer); #endif Serial.print(Snfl1.buffer); Serial.write(0x1B); Serial.flush(); + + Snfl1.process_time = millis(); + Snfl1.unlock = Snfl1.process_time + Snfl1.busy; // Wait for 'AT+RESULT="sequence":"1630250830439"' } -void SnfL1SerialSendOk(void) { +void SnfL1SendOk(void) { snprintf_P(Snfl1.buffer, SONOFF_L1_BUFFER_SIZE, PSTR("AT+SEND=ok")); - SnfL1Send(); } @@ -101,16 +114,47 @@ bool SnfL1SerialInput(void) { } else { TasmotaGlobal.serial_in_buffer[TasmotaGlobal.serial_in_byte_counter++] = 0x00; - // AT+RESULT="sequence":"1554682835320" - // AT+UPDATE="sequence":"34906","switch":"on","light_type":1,"colorR":0,"colorG":16,"colorB":0,"bright":6,"mode":1 + // AT+RESULT="sequence":"0458" = L1 + // AT+RESULT="sequence":"0458","switch" = L1 lite - just returns part of receive buffer + // AT+RESULT="sequence":"1554682835320" = L1 both + // AT+UPDATE="sequence":"1554682835320","switch":"on","light_type":1,"colorR":0,"colorG":16,"colorB":0,"bright":6,"mode":1 // AT+UPDATE="switch":"on","light_type":1,"colorR":255,"colorG":0,"colorB":0,"bright":6,"mode":1,"speed":100,"sensitive":10 #ifdef SONOFF_L1_DEBUG1 - AddLog(LOG_LEVEL_DEBUG, PSTR("SL1: Rcvd %s"), TasmotaGlobal.serial_in_buffer); + AddLog(LOG_LEVEL_DEBUG, PSTR("SL1: Rcvd '%s'"), TasmotaGlobal.serial_in_buffer); #endif if (!strncmp(TasmotaGlobal.serial_in_buffer +3, "RESULT", 6)) { #ifdef SONOFF_L1_ALLOW_REMOTE_INTERRUPT - Snfl1.receive_ready = true; -#endif + Snfl1.busy = 500; +#else +/* + // Read sequence number and calculate time it took from send of same sequence + // this indicates Nuvoton processing speed (50-60 for L1, 30-40 for L1 lite) + // Important for constant color change schemes and fade + char *end_str; + char *string = TasmotaGlobal.serial_in_buffer +10; + char *token = strtok_r(string, ",", &end_str); + if (token) { + char* end_token; + char* token2 = strtok_r(token, ":", &end_token); + char* token3 = strtok_r(nullptr, ":", &end_token); + if (!strncmp(token2, "\"sequence\"", 10) && (strlen(token3) > 3)) { + token3 = token3 + strlen(token3) - 4; // Last three digits + if (Snfl1.sequence == atoi(token3)) { + Snfl1.busy = (millis() - Snfl1.process_time) + 30; // Add some scatter time + if (Snfl1.busy > SONOFF_L1_BUSY) { + Snfl1.busy = SONOFF_L1_BUSY; + } + } + } + } +*/ + Snfl1.busy = 80; +#endif // SONOFF_L1_ALLOW_REMOTE_INTERRUPT +// AddLog(LOG_LEVEL_DEBUG, PSTR("Sl1: ++Busy %d"), Snfl1.busy); + + SnfL1SendOk(); + return true; + } else if (!strncmp(TasmotaGlobal.serial_in_buffer +3, "UPDATE", 6)) { char cmnd_dimmer[20]; @@ -190,6 +234,8 @@ bool SnfL1SerialInput(void) { token = strtok_r(nullptr, ",", &end_str); } + SnfL1SendOk(); + if (is_power_change) { if (Settings->light_scheme > 0) { if (!switch_state) { // If power off RC button pressed stop schemes @@ -197,9 +243,10 @@ bool SnfL1SerialInput(void) { snprintf_P(cmnd_scheme, sizeof(cmnd_scheme), PSTR(D_CMND_SCHEME " 0")); ExecuteCommand(cmnd_scheme, SRC_REMOTE); } - } else { - ExecuteCommandPower(1, switch_state, SRC_REMOTE); } +// else { + ExecuteCommandPower(1, switch_state, SRC_REMOTE); +// } } else if (is_brightness_change) { ExecuteCommand(cmnd_dimmer, SRC_REMOTE); @@ -221,8 +268,6 @@ bool SnfL1SerialInput(void) { } } - SnfL1SerialSendOk(); - return true; } TasmotaGlobal.serial_in_byte = 0; @@ -231,10 +276,10 @@ bool SnfL1SerialInput(void) { /********************************************************************************************/ -bool SnfL1SetChannels(void) { -#ifdef SONOFF_L1_ALLOW_REMOTE_INTERRUPT - if (Snfl1.receive_ready || TimeReached(Snfl1.unlock)) { -#endif +void SnfL1SetChannels(bool backlog) { + // Takes about 100ms at 9600 bps + + if (!backlog) { uint8_t power = Light.power; bool power_changed = (Snfl1.power != power); Snfl1.power = power; @@ -247,17 +292,23 @@ bool SnfL1SetChannels(void) { bool color_changed = false; if (!power_changed) { for (uint32_t i = 0; i < 3; i++) { - if ((Snfl1.color[i] < scale_col[i] -5) || (Snfl1.color[i] > scale_col[i] +5)) { - color_changed = true; // Allow scale-up margins of +/-5 + if (Snfl1.color[i] != scale_col[i]) { + color_changed = true; } Snfl1.color[i] = scale_col[i]; } } - if (!power_changed && !dimmer_changed && !color_changed && (Snfl1.old_music_sync == Settings->sbflag1.sonoff_l1_music_sync)) { return true; } + if (!power_changed && !dimmer_changed && !color_changed && (Snfl1.old_music_sync == Settings->sbflag1.sonoff_l1_music_sync)) { return; } + } + + if (TimeReached(Snfl1.unlock)) { + +// AddLog(LOG_LEVEL_DEBUG, PSTR("SL1: ++SC snd")); uint32_t mode = (Settings->sbflag1.sonoff_l1_music_sync) ? SONOFF_L1_MODE_SYNC_TO_MUSIC : SONOFF_L1_MODE_COLORFUL; + Snfl1.sequence = millis()%1000; snprintf_P(Snfl1.buffer, SONOFF_L1_BUFFER_SIZE, PSTR("AT+UPDATE=\"sequence\":\"%d%03d\",\"switch\":\"%s\",\"light_type\":1,\"colorR\":%d,\"colorG\":%d,\"colorB\":%d,\"bright\":%d,\"mode\":%d"), - LocalTime(), millis()%1000, + LocalTime(), Snfl1.sequence, Snfl1.power ? "on" : "off", Snfl1.color[0], Snfl1.color[1], Snfl1.color[2], Snfl1.dimmer, @@ -277,13 +328,14 @@ bool SnfL1SetChannels(void) { } else #endif // SONOFF_L1_START_DELAY SnfL1Send(); + } else { + if (Settings->light_scheme == 0) { + // Fix last fade state +// AddLog(LOG_LEVEL_DEBUG, PSTR("SL1: ++SC bck")); -#ifdef SONOFF_L1_ALLOW_REMOTE_INTERRUPT - Snfl1.unlock = millis() + 500; // Allow time for the RC - Snfl1.receive_ready = false; + SnfL1Backlog.once_ms(SONOFF_L1_BUSY, SnfL1SendBacklog); // Set backlog + } } -#endif - return true; } bool SnfL1SetChannelsFromFunc(void) { @@ -293,7 +345,8 @@ bool SnfL1SetChannelsFromFunc(void) { } else { Settings->sbflag1.sonoff_l1_music_sync = 0; // Disable MusicSync on user color change } - return SnfL1SetChannels(); + SnfL1SetChannels(false); + return true; } bool SnfL1ModuleSelected(void) { @@ -336,7 +389,7 @@ void CmndMusicSync(void) { if ((parm[2] > 0) && (parm[2] < 101)) { Snfl1.speed = parm[2]; // 1..100 } - SnfL1SetChannels(); + SnfL1SetChannels(false); } Response_P(PSTR("{\"%s\":{\"Mode\":\"%s\",\"Sensitive\":%d,\"Speed\":%d}}"), XdrvMailbox.command, GetStateText(Settings->sbflag1.sonoff_l1_music_sync), Snfl1.sensitive, Snfl1.speed);