From 15a5ef30c408247f9b6a504ed594d85e85cc9411 Mon Sep 17 00:00:00 2001 From: s-hadinger <49731213+s-hadinger@users.noreply.github.com> Date: Sun, 12 May 2024 21:11:14 +0200 Subject: [PATCH 1/4] Fixed Berry `gpio.dac_voltage()` (#21403) --- CHANGELOG.md | 1 + .../xdrv_52_3_berry_gpio.ino | 99 ++++--------------- 2 files changed, 19 insertions(+), 81 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b0ec099f9..99aee1988 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -53,6 +53,7 @@ All notable changes to this project will be documented in this file. - Avoid unwanted OTA upgrade when safeboot starts for the first time (#21360) - Matter broken NOCStruct types preventing pairing with HA (#21365) - jpeg compile core3 (#21387) +- Berry `gpio.dac_voltage()` ### Removed - LVGL disabled vector graphics (#21242) diff --git a/tasmota/tasmota_xdrv_driver/xdrv_52_3_berry_gpio.ino b/tasmota/tasmota_xdrv_driver/xdrv_52_3_berry_gpio.ino index cb222d808..4c1f0f711 100644 --- a/tasmota/tasmota_xdrv_driver/xdrv_52_3_berry_gpio.ino +++ b/tasmota/tasmota_xdrv_driver/xdrv_52_3_berry_gpio.ino @@ -40,6 +40,9 @@ extern "C" { #include "berry/include/be_gpio_defines.h" +#if defined(CONFIG_IDF_TARGET_ESP32) || defined(CONFIG_IDF_TARGET_ESP32S2) + #include "soc/dac_channel.h" +#endif // defined(CONFIG_IDF_TARGET_ESP32) || defined(CONFIG_IDF_TARGET_ESP32S2) // virtual member int gp_member(bvm *vm); @@ -62,44 +65,11 @@ extern "C" { // synthetic mode if (-1 == mode) { // DAC -#if defined(CONFIG_IDF_TARGET_ESP32) - if (25 == pin || 26 == pin) { -#if ESP_IDF_VERSION_MAJOR >= 5 - dac_oneshot_handle_t channel_handle; - const dac_channel_t channel = (25 == pin) ? DAC_CHAN_0 : DAC_CHAN_1; - dac_oneshot_config_t channel_cfg = { - .chan_id = channel, - }; - esp_err_t err = dac_oneshot_new_channel(&channel_cfg, &channel_handle); -#else - dac_channel_t channel = (25 == pin) ? DAC_CHANNEL_1 : DAC_CHANNEL_2; - esp_err_t err = dac_output_enable(channel); -#endif - if (err) { - be_raisef(vm, "value_error", "Error: dac_output_enable(%i) -> %i", channel, err); - } - } else { - be_raise(vm, "value_error", "DAC only supported on GPIO25-26"); - } -#elif defined(CONFIG_IDF_TARGET_ESP32S2) - if (17 == pin || 18 == pin) { -#if ESP_IDF_VERSION_MAJOR >= 5 - dac_oneshot_handle_t channel_handle; - const dac_channel_t channel = (17 == pin) ? DAC_CHAN_0 : DAC_CHAN_1; - dac_oneshot_config_t channel_cfg = { - .chan_id = channel, - }; - esp_err_t err = dac_oneshot_new_channel(&channel_cfg, &channel_handle); -#else - dac_channel_t channel = (17 == pin) ? DAC_CHANNEL_1 : DAC_CHANNEL_2; - esp_err_t err = dac_output_enable(channel); -#endif - if (err) { - be_raisef(vm, "value_error", "Error: dac_output_enable(%i) -> %i", channel, err); - } - } else { - be_raise(vm, "value_error", "DAC only supported on GPIO17-18"); +#if defined(CONFIG_IDF_TARGET_ESP32) || defined(CONFIG_IDF_TARGET_ESP32S2) + if (pin != DAC_CHAN0_GPIO_NUM && pin != DAC_CHAN1_GPIO_NUM) { + be_raisef(vm, "value_error", "DAC only supported on GPIO%i-%i", DAC_CHAN0_GPIO_NUM, DAC_CHAN1_GPIO_NUM); } + // DEPRECATED - this is not needed anymore, the GPIO is configured when first write occurs #else be_raise(vm, "value_error", "DAC unsupported in this chip"); #endif @@ -143,59 +113,26 @@ extern "C" { int gp_dac_voltage(bvm *vm); int gp_dac_voltage(bvm *vm) { +#if defined(CONFIG_IDF_TARGET_ESP32) || defined(CONFIG_IDF_TARGET_ESP32S2) int32_t argc = be_top(vm); // Get the number of arguments - if (argc == 2 && be_isint(vm, 1) && be_isint(vm, 2)) { + if (argc == 2 && be_isint(vm, 1) && be_isnumber(vm, 2)) { int32_t pin = be_toint(vm, 1); int32_t mV = be_toint(vm, 2); + if (pin != DAC_CHAN0_GPIO_NUM && pin != DAC_CHAN1_GPIO_NUM) { + be_raisef(vm, "value_error", "DAC only supported on GPIO%i-%i", DAC_CHAN0_GPIO_NUM, DAC_CHAN1_GPIO_NUM); + } if (mV < 0) { mV = 0; } uint32_t dac_value = changeUIntScale(mV, 0, 3300, 0, 255); // convert from 0..3300 ms to 0..255 -#if defined(CONFIG_IDF_TARGET_ESP32) - if (25 == pin || 26 == pin) { -#if ESP_IDF_VERSION_MAJOR >= 5 - dac_oneshot_handle_t channel_handle; - const dac_channel_t channel = (25 == pin) ? DAC_CHAN_0 : DAC_CHAN_1; - dac_oneshot_config_t channel_cfg = { - .chan_id = channel, - }; - esp_err_t err = dac_oneshot_new_channel(&channel_cfg, &channel_handle); -#else - dac_channel_t channel = (25 == pin) ? DAC_CHANNEL_1 : DAC_CHANNEL_2; - esp_err_t err = dac_output_voltage(channel, dac_value); - // err = dac_output_enable(channel); -#endif - if (err) { - be_raisef(vm, "internal_error", "Error: esp_err_tdac_output_voltage(%i, %i) -> %i", channel, dac_value, err); - } - } else { - be_raise(vm, "value_error", "DAC only supported on GPIO25-26"); + if (!dacWrite(pin, dac_value)) { + be_raise(vm, "value_error", "Error: dacWrite failed"); } -#elif defined(CONFIG_IDF_TARGET_ESP32S2) - if (17 == pin || 18 == pin) { -#if ESP_IDF_VERSION_MAJOR >= 5 - dac_oneshot_handle_t channel_handle; - const dac_channel_t channel = (17 == pin) ? DAC_CHAN_0 : DAC_CHAN_1; - dac_oneshot_config_t channel_cfg = { - .chan_id = channel, - }; - esp_err_t err = dac_oneshot_new_channel(&channel_cfg, &channel_handle); -#else - dac_channel_t channel = (17 == pin) ? DAC_CHANNEL_1 : DAC_CHANNEL_2; - esp_err_t err = dac_output_voltage(channel, dac_value); - // err = dac_output_enable(channel); -#endif - if (err) { - be_raisef(vm, "internal_error", "Error: esp_err_tdac_output_voltage(%i, %i) -> %i", channel, dac_value, err); - } - } else { - be_raise(vm, "value_error", "DAC only supported on GPIO17-18"); - } -#else - be_raise(vm, "value_error", "DAC unsupported in this chip"); -#endif - be_pushint(vm, changeUIntScale(dac_value, 0, 255, 0, 3300)); + be_pushint(vm, changeUIntScale(dac_value, 0, 255, 0, 3300)); // convert back to mV to indicate the actual value be_return(vm); } be_raise(vm, kTypeError, nullptr); +#else // defined(CONFIG_IDF_TARGET_ESP32) || defined(CONFIG_IDF_TARGET_ESP32S2) + be_raise(vm, "value_error", "DAC unsupported in this chip"); +#endif // defined(CONFIG_IDF_TARGET_ESP32) || defined(CONFIG_IDF_TARGET_ESP32S2) } // Tasmota specific From 2865630309c1cfe979e55ceacec355f862010b8f Mon Sep 17 00:00:00 2001 From: s-hadinger <49731213+s-hadinger@users.noreply.github.com> Date: Sun, 12 May 2024 21:12:08 +0200 Subject: [PATCH 2/4] Prepare for ESP32 WDT (#21383) * Prepare for ESP32 WDT * make `sleep()` reset WDT --- platformio_tasmota32.ini | 1 + tasmota/my_user_config.h | 2 ++ tasmota/tasmota.ino | 4 ++++ tasmota/tasmota_support/support.ino | 24 +++++++++++++++++++ .../xdrv_52_1_berry_native.ino | 1 + .../xdrv_52_3_berry_tasmota.ino | 3 ++- 6 files changed, 34 insertions(+), 1 deletion(-) diff --git a/platformio_tasmota32.ini b/platformio_tasmota32.ini index de7725220..4f1aab3a1 100644 --- a/platformio_tasmota32.ini +++ b/platformio_tasmota32.ini @@ -36,6 +36,7 @@ build_flags = ${esp_defaults.build_flags} -Wl,--wrap=panicHandler -Wl,--wrap=xt_unhandled_exception -Wl,--wrap=_Z11analogWritehi ; `analogWrite(unsigned char, int)` use the Tasmota version of analogWrite for deeper integration and phase control -Wl,--wrap=ledcReadFreq ; `uint32_t ledcReadFreq(uint8_t chan)` + -Wl,--wrap=delay ; void delay(uint32_t ms) lib_ignore = HTTPUpdateServer USB diff --git a/tasmota/my_user_config.h b/tasmota/my_user_config.h index 532682faf..faf3791c8 100644 --- a/tasmota/my_user_config.h +++ b/tasmota/my_user_config.h @@ -1122,6 +1122,8 @@ https://rya.nc/tasmota-fingerprint.html" #ifdef ESP32 +// #define USE_ESP32_WDT // Enable Watchdog for ESP32, trigger a restart if loop has not responded for 5s, and if `yield();` was not called + #define SET_ESP32_STACK_SIZE (8 * 1024) // Set the stack size for Tasmota. The default value is 8192 for Arduino, some builds might need to increase it #ifdef SOC_TOUCH_VERSION_1 // ESP32 diff --git a/tasmota/tasmota.ino b/tasmota/tasmota.ino index ced7f4fd3..122ae3eef 100644 --- a/tasmota/tasmota.ino +++ b/tasmota/tasmota.ino @@ -411,6 +411,10 @@ void setup(void) { #endif // CONFIG_IDF_TARGET_ESP32 #endif // ESP32 +#ifdef USE_ESP32_WDT + enableLoopWDT(); // enabled WDT Watchdog on Arduino `loop()` - must return before 5s or called `feedLoopWDT();` - included in `yield()` +#endif // USE_ESP32_WDT + RtcPreInit(); SettingsInit(); diff --git a/tasmota/tasmota_support/support.ino b/tasmota/tasmota_support/support.ino index 0064e70fa..f1c979a38 100755 --- a/tasmota/tasmota_support/support.ino +++ b/tasmota/tasmota_support/support.ino @@ -21,6 +21,30 @@ extern "C" { extern struct rst_info resetInfo; } +/*********************************************************************************************\ + * ESP32 Watchdog +\*********************************************************************************************/ +#ifdef ESP32 +// Watchdog - yield() resets the watchdog +#ifdef USE_ESP32_WDT + +extern "C" +void yield(void) { + vPortYield(); // was originally in `__yield` + feedLoopWDT(); +} + +// patching delay(uint32_t ms) +extern "C" void __real_delay(uint32_t ms); + +extern "C" void __wrap_delay(uint32_t ms) { + __real_delay(ms); + feedLoopWDT(); +} + +#endif // USE_ESP32_WDT +#endif // ESP32 + /*********************************************************************************************\ * Watchdog extension (https://github.com/esp8266/Arduino/issues/1532) \*********************************************************************************************/ diff --git a/tasmota/tasmota_xdrv_driver/xdrv_52_1_berry_native.ino b/tasmota/tasmota_xdrv_driver/xdrv_52_1_berry_native.ino index b0809c8d3..db11ac3e4 100644 --- a/tasmota/tasmota_xdrv_driver/xdrv_52_1_berry_native.ino +++ b/tasmota/tasmota_xdrv_driver/xdrv_52_1_berry_native.ino @@ -110,6 +110,7 @@ void BrTimeoutStart(void) { if (0 == berry.timeout) { berry.timeout = 1; // rare case when value accidentally computes to zero } + yield(); } void BrTimeoutYield(void) { diff --git a/tasmota/tasmota_xdrv_driver/xdrv_52_3_berry_tasmota.ino b/tasmota/tasmota_xdrv_driver/xdrv_52_3_berry_tasmota.ino index e23511bb2..eb87fede7 100644 --- a/tasmota/tasmota_xdrv_driver/xdrv_52_3_berry_tasmota.ino +++ b/tasmota/tasmota_xdrv_driver/xdrv_52_3_berry_tasmota.ino @@ -404,7 +404,8 @@ extern "C" { // ESP object int32_t l_yield(bvm *vm); int32_t l_yield(bvm *vm) { - return be_call_c_func(vm, (void*) &BrTimeoutYield, NULL, "-"); + BrTimeoutYield(); + be_return_nil(vm); } // Berry: tasmota.scale_uint(int * 5) -> int From 4cda2d7dc24adddcdf7f520e427e5e3e9be538ce Mon Sep 17 00:00:00 2001 From: s-hadinger <49731213+s-hadinger@users.noreply.github.com> Date: Mon, 13 May 2024 22:21:58 +0200 Subject: [PATCH 3/4] Berry `Leds` uses native WS2812 driver by default (#21406) --- CHANGELOG.md | 1 + .../berry_tasmota/src/embedded/leds.be | 38 +++-- .../src/solidify/solidified_leds.h | 151 +++++++++++------- .../xdrv_04_light_artnet.ino | 2 +- .../xdrv_52_3_berry_leds.ino | 93 ++++++++--- tasmota/tasmota_xlgt_light/xlgt_01_ws2812.ino | 123 ++++++++++++-- 6 files changed, 299 insertions(+), 109 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 99aee1988..40e4bbfab 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -37,6 +37,7 @@ All notable changes to this project will be documented in this file. - TLS Letsencrypt replace R3 CA with long-term ISRG_Root_X1 CA, which works with R3 and R10-R14 (#21352) - GPIOViewer from v1.5.0 to v1.5.2 - ESP32 Core3 platform update from 2024.05.10 to 2024.05.11 (#21381) +- Berry `Leds` uses native WS2812 driver by default ### Fixed - HASPmota `align` attribute and expand PNG cache (#21228) diff --git a/lib/libesp32/berry_tasmota/src/embedded/leds.be b/lib/libesp32/berry_tasmota/src/embedded/leds.be index 1d9e505ac..9442f8787 100644 --- a/lib/libesp32/berry_tasmota/src/embedded/leds.be +++ b/lib/libesp32/berry_tasmota/src/embedded/leds.be @@ -33,23 +33,27 @@ class Leds : Leds_ntv # typ:int (optional) = Type of LED, defaults to WS2812 RGB # rmt:int (optional) = RMT hardware channel to use, leave default unless you have a good reason def init(leds, gpio_phy, typ, rmt) # rmt is optional + import gpio self.gamma = true # gamma is enabled by default, it should be disabled explicitly if needed - self.leds = int(leds) - self.bri = 127 # 50% brightness by default + if (gpio_phy == nil) || (gpio_phy == gpio.pin(gpio.WS2812, 0)) + # use native driver + self.ctor() # no parameters + self.leds = self.pixel_count() + import light + self.bri = light.get()['bri'] + else + # use pure Berry driver + self.leds = int(leds) + self.bri = 127 # 50% brightness by default - # if no GPIO, abort - if gpio_phy == nil - raise "valuer_error", "no GPIO specified for neopixelbus" + # initialize the structure + self.ctor(self.leds, gpio_phy, typ, rmt) end - # initialize the structure - self.ctor(self.leds, gpio_phy, typ, rmt) - if self._p == nil raise "internal_error", "couldn't not initialize noepixelbus" end # call begin self.begin() - end # assign RMT @@ -106,13 +110,17 @@ class Leds : Leds_ntv end def ctor(leds, gpio_phy, typ, rmt) - if typ == nil - typ = self.WS2812_GRB + if gpio_phy == nil + self.call_native(0) # native driver + else + if typ == nil + typ = self.WS2812_GRB + end + if rmt == nil + rmt = self.assign_rmt(gpio_phy) + end + self.call_native(0, leds, gpio_phy, typ, rmt) end - if rmt == nil - rmt = self.assign_rmt(gpio_phy) - end - self.call_native(0, leds, gpio_phy, typ, rmt) end def begin() self.call_native(1) diff --git a/lib/libesp32/berry_tasmota/src/solidify/solidified_leds.h b/lib/libesp32/berry_tasmota/src/solidify/solidified_leds.h index 3e0ef7846..4ea0f61f7 100644 --- a/lib/libesp32/berry_tasmota/src/solidify/solidified_leds.h +++ b/lib/libesp32/berry_tasmota/src/solidify/solidified_leds.h @@ -649,7 +649,7 @@ be_local_closure(Leds_clear, /* name */ ********************************************************************/ be_local_closure(Leds_init, /* name */ be_nested_proto( - 11, /* nstack */ + 12, /* nstack */ 5, /* argc */ 2, /* varg */ 0, /* has upvals */ @@ -657,47 +657,69 @@ be_local_closure(Leds_init, /* name */ 0, /* has sup protos */ NULL, /* no sub protos */ 1, /* has constants */ - ( &(const bvalue[10]) { /* constants */ - /* K0 */ be_nested_str(gamma), - /* K1 */ be_nested_str(leds), - /* K2 */ be_nested_str(bri), - /* K3 */ be_nested_str(valuer_error), - /* K4 */ be_nested_str(no_X20GPIO_X20specified_X20for_X20neopixelbus), + ( &(const bvalue[15]) { /* constants */ + /* K0 */ be_nested_str(gpio), + /* K1 */ be_nested_str(gamma), + /* K2 */ be_nested_str(pin), + /* K3 */ be_nested_str(WS2812), + /* K4 */ be_const_int(0), /* K5 */ be_nested_str(ctor), - /* K6 */ be_nested_str(_p), - /* K7 */ be_nested_str(internal_error), - /* K8 */ be_nested_str(couldn_X27t_X20not_X20initialize_X20noepixelbus), - /* K9 */ be_nested_str(begin), + /* K6 */ be_nested_str(leds), + /* K7 */ be_nested_str(pixel_count), + /* K8 */ be_nested_str(light), + /* K9 */ be_nested_str(bri), + /* K10 */ be_nested_str(get), + /* K11 */ be_nested_str(_p), + /* K12 */ be_nested_str(internal_error), + /* K13 */ be_nested_str(couldn_X27t_X20not_X20initialize_X20noepixelbus), + /* K14 */ be_nested_str(begin), }), &be_const_str_init, &be_const_str_solidified, - ( &(const binstruction[26]) { /* code */ - 0x50140200, // 0000 LDBOOL R5 1 0 - 0x90020005, // 0001 SETMBR R0 K0 R5 - 0x60140009, // 0002 GETGBL R5 G9 - 0x5C180200, // 0003 MOVE R6 R1 - 0x7C140200, // 0004 CALL R5 1 - 0x90020205, // 0005 SETMBR R0 K1 R5 - 0x5416007E, // 0006 LDINT R5 127 - 0x90020405, // 0007 SETMBR R0 K2 R5 - 0x4C140000, // 0008 LDNIL R5 - 0x1C140405, // 0009 EQ R5 R2 R5 - 0x78160000, // 000A JMPF R5 #000C - 0xB0060704, // 000B RAISE 1 K3 K4 - 0x8C140105, // 000C GETMET R5 R0 K5 - 0x881C0101, // 000D GETMBR R7 R0 K1 - 0x5C200400, // 000E MOVE R8 R2 - 0x5C240600, // 000F MOVE R9 R3 - 0x5C280800, // 0010 MOVE R10 R4 - 0x7C140A00, // 0011 CALL R5 5 - 0x88140106, // 0012 GETMBR R5 R0 K6 - 0x4C180000, // 0013 LDNIL R6 - 0x1C140A06, // 0014 EQ R5 R5 R6 - 0x78160000, // 0015 JMPF R5 #0017 - 0xB0060F08, // 0016 RAISE 1 K7 K8 - 0x8C140109, // 0017 GETMET R5 R0 K9 - 0x7C140200, // 0018 CALL R5 1 - 0x80000000, // 0019 RET 0 + ( &(const binstruction[43]) { /* code */ + 0xA4160000, // 0000 IMPORT R5 K0 + 0x50180200, // 0001 LDBOOL R6 1 0 + 0x90020206, // 0002 SETMBR R0 K1 R6 + 0x4C180000, // 0003 LDNIL R6 + 0x1C180406, // 0004 EQ R6 R2 R6 + 0x741A0005, // 0005 JMPT R6 #000C + 0x8C180B02, // 0006 GETMET R6 R5 K2 + 0x88200B03, // 0007 GETMBR R8 R5 K3 + 0x58240004, // 0008 LDCONST R9 K4 + 0x7C180600, // 0009 CALL R6 3 + 0x1C180406, // 000A EQ R6 R2 R6 + 0x781A000A, // 000B JMPF R6 #0017 + 0x8C180105, // 000C GETMET R6 R0 K5 + 0x7C180200, // 000D CALL R6 1 + 0x8C180107, // 000E GETMET R6 R0 K7 + 0x7C180200, // 000F CALL R6 1 + 0x90020C06, // 0010 SETMBR R0 K6 R6 + 0xA41A1000, // 0011 IMPORT R6 K8 + 0x8C1C0D0A, // 0012 GETMET R7 R6 K10 + 0x7C1C0200, // 0013 CALL R7 1 + 0x941C0F09, // 0014 GETIDX R7 R7 K9 + 0x90021207, // 0015 SETMBR R0 K9 R7 + 0x7002000B, // 0016 JMP #0023 + 0x60180009, // 0017 GETGBL R6 G9 + 0x5C1C0200, // 0018 MOVE R7 R1 + 0x7C180200, // 0019 CALL R6 1 + 0x90020C06, // 001A SETMBR R0 K6 R6 + 0x541A007E, // 001B LDINT R6 127 + 0x90021206, // 001C SETMBR R0 K9 R6 + 0x8C180105, // 001D GETMET R6 R0 K5 + 0x88200106, // 001E GETMBR R8 R0 K6 + 0x5C240400, // 001F MOVE R9 R2 + 0x5C280600, // 0020 MOVE R10 R3 + 0x5C2C0800, // 0021 MOVE R11 R4 + 0x7C180A00, // 0022 CALL R6 5 + 0x8818010B, // 0023 GETMBR R6 R0 K11 + 0x4C1C0000, // 0024 LDNIL R7 + 0x1C180C07, // 0025 EQ R6 R6 R7 + 0x781A0000, // 0026 JMPF R6 #0028 + 0xB006190D, // 0027 RAISE 1 K12 K13 + 0x8C18010E, // 0028 GETMET R6 R0 K14 + 0x7C180200, // 0029 CALL R6 1 + 0x80000000, // 002A RET 0 }) ) ); @@ -2061,33 +2083,40 @@ be_local_closure(Leds_ctor, /* name */ NULL, /* no sub protos */ 1, /* has constants */ ( &(const bvalue[ 4]) { /* constants */ - /* K0 */ be_nested_str(WS2812_GRB), - /* K1 */ be_nested_str(assign_rmt), - /* K2 */ be_nested_str(call_native), - /* K3 */ be_const_int(0), + /* K0 */ be_nested_str(call_native), + /* K1 */ be_const_int(0), + /* K2 */ be_nested_str(WS2812_GRB), + /* K3 */ be_nested_str(assign_rmt), }), &be_const_str_ctor, &be_const_str_solidified, - ( &(const binstruction[19]) { /* code */ + ( &(const binstruction[26]) { /* code */ 0x4C140000, // 0000 LDNIL R5 - 0x1C140605, // 0001 EQ R5 R3 R5 - 0x78160000, // 0002 JMPF R5 #0004 - 0x880C0100, // 0003 GETMBR R3 R0 K0 - 0x4C140000, // 0004 LDNIL R5 - 0x1C140805, // 0005 EQ R5 R4 R5 - 0x78160003, // 0006 JMPF R5 #000B - 0x8C140101, // 0007 GETMET R5 R0 K1 - 0x5C1C0400, // 0008 MOVE R7 R2 - 0x7C140400, // 0009 CALL R5 2 - 0x5C100A00, // 000A MOVE R4 R5 - 0x8C140102, // 000B GETMET R5 R0 K2 - 0x581C0003, // 000C LDCONST R7 K3 - 0x5C200200, // 000D MOVE R8 R1 - 0x5C240400, // 000E MOVE R9 R2 - 0x5C280600, // 000F MOVE R10 R3 - 0x5C2C0800, // 0010 MOVE R11 R4 - 0x7C140C00, // 0011 CALL R5 6 - 0x80000000, // 0012 RET 0 + 0x1C140405, // 0001 EQ R5 R2 R5 + 0x78160003, // 0002 JMPF R5 #0007 + 0x8C140100, // 0003 GETMET R5 R0 K0 + 0x581C0001, // 0004 LDCONST R7 K1 + 0x7C140400, // 0005 CALL R5 2 + 0x70020011, // 0006 JMP #0019 + 0x4C140000, // 0007 LDNIL R5 + 0x1C140605, // 0008 EQ R5 R3 R5 + 0x78160000, // 0009 JMPF R5 #000B + 0x880C0102, // 000A GETMBR R3 R0 K2 + 0x4C140000, // 000B LDNIL R5 + 0x1C140805, // 000C EQ R5 R4 R5 + 0x78160003, // 000D JMPF R5 #0012 + 0x8C140103, // 000E GETMET R5 R0 K3 + 0x5C1C0400, // 000F MOVE R7 R2 + 0x7C140400, // 0010 CALL R5 2 + 0x5C100A00, // 0011 MOVE R4 R5 + 0x8C140100, // 0012 GETMET R5 R0 K0 + 0x581C0001, // 0013 LDCONST R7 K1 + 0x5C200200, // 0014 MOVE R8 R1 + 0x5C240400, // 0015 MOVE R9 R2 + 0x5C280600, // 0016 MOVE R10 R3 + 0x5C2C0800, // 0017 MOVE R11 R4 + 0x7C140C00, // 0018 CALL R5 6 + 0x80000000, // 0019 RET 0 }) ) ); diff --git a/tasmota/tasmota_xdrv_driver/xdrv_04_light_artnet.ino b/tasmota/tasmota_xdrv_driver/xdrv_04_light_artnet.ino index 49ebf6ddf..d0d3b61ff 100644 --- a/tasmota/tasmota_xdrv_driver/xdrv_04_light_artnet.ino +++ b/tasmota/tasmota_xdrv_driver/xdrv_04_light_artnet.ino @@ -384,7 +384,7 @@ bool ArtNetStart(void) { if ((Settings->light_pixels != artnet_conf.rows * artnet_conf.cols + artnet_conf.offs) || (Settings->light_rotation != 0)) { Settings->light_pixels = artnet_conf.rows * artnet_conf.cols + artnet_conf.offs; Settings->light_rotation = 0; - Ws2812ReinitStrip(); + Ws2812InitStrip(); } else { Ws2812Clear(); } diff --git a/tasmota/tasmota_xdrv_driver/xdrv_52_3_berry_leds.ino b/tasmota/tasmota_xdrv_driver/xdrv_52_3_berry_leds.ino index 08f287567..0984cb14b 100644 --- a/tasmota/tasmota_xdrv_driver/xdrv_52_3_berry_leds.ino +++ b/tasmota/tasmota_xdrv_driver/xdrv_52_3_berry_leds.ino @@ -41,6 +41,23 @@ typedef NeoPixelBus neopixel_ws2812_gr typedef NeoPixelBus neopixel_sk6812_grbw_t; #endif +/*********************************************************************************************\ + * Functions from Tasmota WS2812 driver + * +\*********************************************************************************************/ +extern void *Ws2812GetStrip(void); +extern void Ws2812Begin(void); +extern void Ws2812Show(void); +extern uint32_t Ws2812PixelsSize(void); +extern bool Ws2812CanShow(void); +extern bool Ws2812IsDirty(void); +extern void Ws2812Dirty(void); +extern uint8_t * Ws2812Pixels(void); +extern size_t Ws2812PixelSize(void); +extern size_t Ws2812PixelCount(void); +extern void Ws2812ClearTo(uint8_t r, uint8_t g, uint8_t b, uint8_t w, int32_t from, int32_t to); +extern void Ws2812SetPixelColor(uint32_t idx, uint8_t r, uint8_t g, uint8_t b, uint8_t w); +extern uint32_t Ws2812GetPixelColor(uint32_t idx); /*********************************************************************************************\ * Native functions mapped to Berry functions @@ -82,7 +99,7 @@ extern "C" { be_getmember(vm, 1, "_t"); int32_t type = be_toint(vm, -1); be_pop(vm, 1); - if (type <= 0 || type >= neopixel_type_end) { + if (type < 0 || type >= neopixel_type_end) { be_raise(vm, "internal_error", "invalid leds type"); } return type; @@ -95,30 +112,46 @@ extern "C" { int32_t cmd = be_toint(vm, 2); if (0 == cmd) { // 00 : ctor (leds:int, gpio:int) -> void - if (!(argc >= 6 && be_isint(vm, 3) && be_isint(vm, 4) && be_isint(vm, 5) && be_isint(vm, 6))) { + if ((argc != 2) && !(argc >= 6 && be_isint(vm, 3) && be_isint(vm, 4) && be_isint(vm, 5) && be_isint(vm, 6))) { be_raise(vm, "value_error", "bad arguments for neopixelbus:ctor"); } - int32_t leds = be_toint(vm, 3); - int32_t gpio = be_toint(vm, 4); - int32_t neopixel_type = be_toint(vm, 5); - int32_t rmt = be_toint(vm, 6); - if (neopixel_type < 1) { neopixel_type = 1; } - if (neopixel_type >= neopixel_type_end) { neopixel_type = neopixel_type_end - 1; } - if (rmt < 0) { rmt = 0; } - if (rmt >= MAX_RMT) { rmt = MAX_RMT - 1; } + int32_t leds = -1; + int32_t gpio = -1; + int32_t neopixel_type = 0; + int32_t rmt = 0; + void * strip = nullptr; + if (argc > 2) { + leds = be_toint(vm, 3); + gpio = be_toint(vm, 4); + neopixel_type = be_toint(vm, 5); + rmt = be_toint(vm, 6); + } + + if (-1 == gpio) { + // if GPIO is '-1' + neopixel_type = 0; + Ws2812InitStrip(); // ensure the NeoPixelbus object is initialized, because Berry code runs before the driver is initialized + strip = Ws2812GetStrip(); + } else { + // allocate a new RMT + if (neopixel_type < 1) { neopixel_type = 1; } + if (neopixel_type >= neopixel_type_end) { neopixel_type = neopixel_type_end - 1; } + if (rmt < 0) { rmt = 0; } + if (rmt >= MAX_RMT) { rmt = MAX_RMT - 1; } + + switch (neopixel_type) { + case ws2812_grb: strip = new neopixel_ws2812_grb_t(leds, gpio, (NeoBusChannel) rmt); + break; + case sk6812_grbw: strip = new neopixel_sk6812_grbw_t(leds, gpio, (NeoBusChannel) rmt); + break; + } + } // store type in attribute `_t` be_pushint(vm, neopixel_type); be_setmember(vm, 1, "_t"); be_pop(vm, 1); - void * strip = nullptr; - switch (neopixel_type) { - case ws2812_grb: strip = new neopixel_ws2812_grb_t(leds, gpio, (NeoBusChannel) rmt); - break; - case sk6812_grbw: strip = new neopixel_sk6812_grbw_t(leds, gpio, (NeoBusChannel) rmt); - break; - } be_pushcomptr(vm, (void*) strip); be_setmember(vm, 1, "_p"); be_pop(vm, 1); @@ -130,11 +163,14 @@ extern "C" { // initialize all possible variants neopixel_ws2812_grb_t * s_ws2812_grb = (leds_type == ws2812_grb) ? (neopixel_ws2812_grb_t*) s : nullptr; neopixel_sk6812_grbw_t * s_sk6812_grbw = (leds_type == sk6812_grbw) ? (neopixel_sk6812_grbw_t*) s : nullptr; + // detect native driver + bool native = (leds_type == 0); be_pushnil(vm); // push a default `nil` return value switch (cmd) { case 1: // # 01 : begin void -> void + if (native) Ws2812Begin(); if (s_ws2812_grb) s_ws2812_grb->Begin(); if (s_sk6812_grbw) s_sk6812_grbw->Begin(); break; @@ -159,6 +195,7 @@ extern "C" { } } uint32_t pixels_size; // number of bytes to push + if (native) { Ws2812Show(); pixels_size = Ws2812PixelsSize(); } if (s_ws2812_grb) { s_ws2812_grb->Show(); pixels_size = s_ws2812_grb->PixelsSize(); } if (s_sk6812_grbw) { s_sk6812_grbw->Show(); pixels_size = s_sk6812_grbw->PixelsSize(); } @@ -168,20 +205,24 @@ extern "C" { } break; case 3: // # 03 : CanShow void -> bool + if (native) be_pushbool(vm, Ws2812CanShow()); if (s_ws2812_grb) be_pushbool(vm, s_ws2812_grb->CanShow()); if (s_sk6812_grbw) be_pushbool(vm, s_sk6812_grbw->CanShow()); break; case 4: // # 04 : IsDirty void -> bool + if (native) be_pushbool(vm, Ws2812IsDirty()); if (s_ws2812_grb) be_pushbool(vm, s_ws2812_grb->IsDirty()); if (s_sk6812_grbw) be_pushbool(vm, s_sk6812_grbw->IsDirty()); break; case 5: // # 05 : Dirty void -> void + if (native) Ws2812Dirty(); if (s_ws2812_grb) s_ws2812_grb->Dirty(); if (s_sk6812_grbw) s_sk6812_grbw->Dirty(); break; case 6: // # 06 : Pixels void -> bytes() (mapped to the buffer) { uint8_t * pixels; + if (native) pixels = Ws2812Pixels(); if (s_ws2812_grb) pixels = s_ws2812_grb->Pixels(); if (s_sk6812_grbw) pixels = s_sk6812_grbw->Pixels(); @@ -189,10 +230,12 @@ extern "C" { } break; case 7: // # 07 : PixelSize void -> int + if (native) be_pushint(vm, Ws2812PixelSize()); if (s_ws2812_grb) be_pushint(vm, s_ws2812_grb->PixelSize()); if (s_sk6812_grbw) be_pushint(vm, s_sk6812_grbw->PixelSize()); break; case 8: // # 08 : PixelCount void -> int + if (native) be_pushint(vm, Ws2812PixelCount()); if (s_ws2812_grb) be_pushint(vm, s_ws2812_grb->PixelCount()); if (s_sk6812_grbw) be_pushint(vm, s_sk6812_grbw->PixelCount()); break; @@ -204,11 +247,16 @@ extern "C" { uint8_t g = (rgbw >> 8) & 0xFF; uint8_t b = (rgbw ) & 0xFF; if (argc >= 5 && be_isint(vm, 4) && be_isint(vm, 5)) { - uint32_t from = be_toint(vm, 4); - uint32_t len = be_toint(vm, 5); + int32_t from = be_toint(vm, 4); + int32_t len = be_toint(vm, 5); + if (from < 0) { from = 0; } + if (len < 0) { len = 0; } + + if (native) Ws2812ClearTo(r, g, b, w, from, from + len - 1); if (s_ws2812_grb) s_ws2812_grb->ClearTo(RgbColor(r, g, b), from, from + len - 1); if (s_sk6812_grbw) s_sk6812_grbw->ClearTo(RgbwColor(r, g, b, w), from, from + len - 1); } else { + if (native) Ws2812ClearTo(r, g, b, w, -1, -1); if (s_ws2812_grb) s_ws2812_grb->ClearTo(RgbColor(r, g, b)); if (s_sk6812_grbw) s_sk6812_grbw->ClearTo(RgbwColor(r, g, b, w)); } @@ -222,6 +270,7 @@ extern "C" { uint8_t r = (rgbw >> 16) & 0xFF; uint8_t g = (rgbw >> 8) & 0xFF; uint8_t b = (rgbw ) & 0xFF; + if (native) Ws2812SetPixelColor(idx, r, g, b, w); if (s_ws2812_grb) s_ws2812_grb->SetPixelColor(idx, RgbColor(r, g, b)); if (s_sk6812_grbw) s_sk6812_grbw->SetPixelColor(idx, RgbwColor(r, g, b, w)); } @@ -229,15 +278,17 @@ extern "C" { case 11: // # 11 : GetPixelColor (idx:int) -> color:?? { int32_t idx = be_toint(vm, 3); - RgbColor rgb; + if (native) { + be_pushint(vm, Ws2812GetPixelColor(idx)); + } if (s_ws2812_grb) { RgbColor rgb = s_ws2812_grb->GetPixelColor(idx); be_pushint(vm, (rgb.R << 16) | (rgb.G << 8) | rgb.B); } if (s_sk6812_grbw) { RgbwColor rgbw = s_sk6812_grbw->GetPixelColor(idx); - be_pushint(vm, (rgbw.W << 24) | (rgb.R << 16) | (rgb.G << 8) | rgb.B); + be_pushint(vm, (rgbw.W << 24) | (rgbw.R << 16) | (rgbw.G << 8) | rgbw.B); } } break; diff --git a/tasmota/tasmota_xlgt_light/xlgt_01_ws2812.ino b/tasmota/tasmota_xlgt_light/xlgt_01_ws2812.ino index e87d393f7..dd4f425da 100644 --- a/tasmota/tasmota_xlgt_light/xlgt_01_ws2812.ino +++ b/tasmota/tasmota_xlgt_light/xlgt_01_ws2812.ino @@ -681,16 +681,10 @@ void Ws2812ShowScheme(void) } } -bool Ws2812ReinitStrip(void) +bool Ws2812InitStrip(void) { if (strip != nullptr) { - Ws2812Clear(); - if (!strip->CanShow()) { - // we're doing DMA, so wait for a decent amount of time - delay(10); - } - delete strip; - strip = nullptr; + return true; } #if (USE_WS2812_HARDWARE == NEO_HW_P9813) @@ -711,7 +705,7 @@ bool Ws2812ReinitStrip(void) void Ws2812ModuleSelected(void) { - if (Ws2812ReinitStrip()) { + if (Ws2812InitStrip()) { Ws2812.scheme_offset = Light.max_scheme +1; Light.max_scheme += WS2812_SCHEMES; @@ -728,6 +722,112 @@ void Ws2812ModuleSelected(void) } } +/********************************************************************************************/ +// Callbacks for Berry driver +// +// Since we dont' want to export all the template stuff, we need to encapsulate the calls +// in plain functions +// +void *Ws2812GetStrip(void) { + return strip; +} + +void Ws2812Begin(void) { + if (strip) { strip->Begin(); } +} + +void Ws2812Show(void) { + if (strip) { strip->Show(); } +} + +uint32_t Ws2812PixelsSize(void) { + if (strip) { return strip->PixelCount(); } + return 0; +} + +bool Ws2812CanShow(void) { + if (strip) { return strip->CanShow(); } + return false; +} + +bool Ws2812IsDirty(void) { + if (strip) { return strip->IsDirty(); } + return false; +} + +void Ws2812Dirty(void) { + if (strip) { strip->Dirty(); } +} + +uint8_t * Ws2812Pixels(void) { + if (strip) { return strip->Pixels(); } + return nullptr; +} + +size_t Ws2812PixelSize(void) { + if (strip) { return strip->PixelSize(); } + return 0; +} + +size_t Ws2812PixelCount(void) { + if (strip) { return strip->PixelCount(); } + return 0; +} + +void Ws2812ClearTo(uint8_t r, uint8_t g, uint8_t b, uint8_t w, int32_t from, int32_t to) { +#if (USE_WS2812_CTYPE > NEO_3LED) + RgbwColor lcolor; + lcolor.W = w; +#else + RgbColor lcolor; +#endif + + lcolor.R = r; + lcolor.G = g; + lcolor.B = b; + if (strip) { + if (from < 0) { + strip->ClearTo(lcolor); + } else { + strip->ClearTo(lcolor, from, to); + } + } +} + +void Ws2812SetPixelColor(uint32_t idx, uint8_t r, uint8_t g, uint8_t b, uint8_t w) +{ +#if (USE_WS2812_CTYPE > NEO_3LED) + RgbwColor lcolor; + lcolor.W = w; +#else + RgbColor lcolor; +#endif + + lcolor.R = r; + lcolor.G = g; + lcolor.B = b; + if (strip) { + strip->SetPixelColor(idx, lcolor); + } +} + +uint32_t Ws2812GetPixelColor(uint32_t idx) { +#if (USE_WS2812_CTYPE > NEO_3LED) + RgbwColor lcolor; +#else + RgbColor lcolor; +#endif + if (strip) { + lcolor = strip->GetPixelColor(idx); +#if (USE_WS2812_CTYPE > NEO_3LED) + return (lcolor.W << 24) | (lcolor.R << 16) | (lcolor.G << 8) | lcolor.B; +#else + return (lcolor.R << 16) | (lcolor.G << 8) | lcolor.B; +#endif + } + return 0; +} + /********************************************************************************************/ void CmndLed(void) @@ -758,7 +858,8 @@ void CmndPixels(void) if ((XdrvMailbox.payload > 0) && (XdrvMailbox.payload <= WS2812_MAX_LEDS)) { Settings->light_pixels = XdrvMailbox.payload; Settings->light_rotation = 0; - Ws2812ReinitStrip(); + // Ws2812ReinitStrip(); -- does not work + TasmotaGlobal.restart_flag = 2; // reboot instead Light.update = true; } ResponseCmndNumber(Settings->light_pixels); @@ -768,7 +869,7 @@ void CmndStepPixels(void) { if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 255)) { Settings->light_step_pixels = (XdrvMailbox.payload > WS2812_MAX_LEDS) ? WS2812_MAX_LEDS : XdrvMailbox.payload; - Ws2812ReinitStrip(); + // Ws2812ReinitStrip(); -- not sure it's actually needed Light.update = true; } ResponseCmndNumber(Settings->light_step_pixels); From f9ca7d4821863cc02fd54cf825196405832eb4e8 Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Tue, 14 May 2024 10:59:23 +0200 Subject: [PATCH 4/4] Update changelogs --- CHANGELOG.md | 5 +++-- RELEASENOTES.md | 7 +++++-- tasmota/tasmota_xlgt_light/xlgt_01_ws2812.ino | 14 ++++++++++++-- 3 files changed, 20 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 40e4bbfab..67f9c6bda 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -37,7 +37,8 @@ All notable changes to this project will be documented in this file. - TLS Letsencrypt replace R3 CA with long-term ISRG_Root_X1 CA, which works with R3 and R10-R14 (#21352) - GPIOViewer from v1.5.0 to v1.5.2 - ESP32 Core3 platform update from 2024.05.10 to 2024.05.11 (#21381) -- Berry `Leds` uses native WS2812 driver by default +- Berry `Leds` uses native WS2812 driver by default (#21406) +- Command ``Pixels`` initiates a restart before activation due to changed NeoPixelBus library (#21406) ### Fixed - HASPmota `align` attribute and expand PNG cache (#21228) @@ -54,7 +55,7 @@ All notable changes to this project will be documented in this file. - Avoid unwanted OTA upgrade when safeboot starts for the first time (#21360) - Matter broken NOCStruct types preventing pairing with HA (#21365) - jpeg compile core3 (#21387) -- Berry `gpio.dac_voltage()` +- Berry `gpio.dac_voltage()` (#21403) ### Removed - LVGL disabled vector graphics (#21242) diff --git a/RELEASENOTES.md b/RELEASENOTES.md index 4084153e1..70702bb3d 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -40,10 +40,10 @@ This release will be supported from ESP32/Arduino library Core version **3.0.0** Support of ESP8266 Core versions before 2.7.6 and ESP32 Core versions before 3.0.0 have been removed. -### Known issues +### Known issues with v14.0.0 Due to the change from ESP32 Arduino Core2/IDF4 to Arduino Core3/IDF5 not all functionality has been restored. The following features are known not to work on ESP32: -- Wifi Range Extender +- Wifi Range Extender [#21200](https://github.com/arendst/Tasmota/issues/21200) ## Support of TLS @@ -177,6 +177,7 @@ The latter links can be used for OTA upgrades too like ``OtaUrl https://ota.tasm - GPIOViewer from v1.5.0 to v1.5.2 - Seriallog set to `SERIAL_LOG_LEVEL` at boot [#21363](https://github.com/arendst/Tasmota/issues/21363) - TLS Letsencrypt replace R3 CA with long-term ISRG_Root_X1 CA, which works with R3 and R10-R14 [#21352](https://github.com/arendst/Tasmota/issues/21352) +- Command ``Pixels`` initiates a restart before activation due to changed NeoPixelBus library [#21406](https://github.com/arendst/Tasmota/issues/21406) - Command ``EthType`` option selection [#21317](https://github.com/arendst/Tasmota/issues/21317) - Refactor Platformio script `post_esp32.py` [#20966](https://github.com/arendst/Tasmota/issues/20966) - SGP4x Domoticz air quality value from raw to computed [#18880](https://github.com/arendst/Tasmota/issues/18880) @@ -194,6 +195,7 @@ The latter links can be used for OTA upgrades too like ``OtaUrl https://ota.tasm - ESP32 WiFi phy modes 11n and 11ax represented as HT20, HT40 and HE20 [#19350](https://github.com/arendst/Tasmota/issues/19350) - berry.exe (pre-compiled for Windows) updated to latest Berry patches [#21024](https://github.com/arendst/Tasmota/issues/21024) - Berry class `int64` made immutable [#20727](https://github.com/arendst/Tasmota/issues/20727) +- Berry `Leds` uses native WS2812 driver by default [#21406](https://github.com/arendst/Tasmota/issues/21406) - Matter reduce memory usage when reading with wildcards [#20809](https://github.com/arendst/Tasmota/issues/20809) - LVGL make lv_touch_3_buttons more responsive [#20728](https://github.com/arendst/Tasmota/issues/20728) - LVGL optimize fonts and add icons [#20880](https://github.com/arendst/Tasmota/issues/20880) @@ -228,6 +230,7 @@ The latter links can be used for OTA upgrades too like ``OtaUrl https://ota.tasm - Berry walrus bug when assigning to self [#21015](https://github.com/arendst/Tasmota/issues/21015) - Berry `web_add_handler` called before `Webserver` is initialized [#21272](https://github.com/arendst/Tasmota/issues/21272) - Berry `math.inf`, `math.isinf()` and fixed json ouput for `inf` and `nan` [#21304](https://github.com/arendst/Tasmota/issues/21304) +- Berry `gpio.dac_voltage()` [#21403](https://github.com/arendst/Tasmota/issues/21403) - Matter broken NOCStruct types preventing pairing with HA [#21365](https://github.com/arendst/Tasmota/issues/21365) - LVGL restore `lv_palette` functions [#21232](https://github.com/arendst/Tasmota/issues/21232) - LVGL fix memory allocation of flush buffers [#21256](https://github.com/arendst/Tasmota/issues/21256) diff --git a/tasmota/tasmota_xlgt_light/xlgt_01_ws2812.ino b/tasmota/tasmota_xlgt_light/xlgt_01_ws2812.ino index dd4f425da..b05d15e8a 100644 --- a/tasmota/tasmota_xlgt_light/xlgt_01_ws2812.ino +++ b/tasmota/tasmota_xlgt_light/xlgt_01_ws2812.ino @@ -722,6 +722,8 @@ void Ws2812ModuleSelected(void) } } +#ifdef ESP32 +#ifdef USE_BERRY /********************************************************************************************/ // Callbacks for Berry driver // @@ -828,6 +830,9 @@ uint32_t Ws2812GetPixelColor(uint32_t idx) { return 0; } +#endif // ESP32 +#endif // USE_BERRY + /********************************************************************************************/ void CmndLed(void) @@ -856,11 +861,16 @@ void CmndLed(void) void CmndPixels(void) { if ((XdrvMailbox.payload > 0) && (XdrvMailbox.payload <= WS2812_MAX_LEDS)) { +/* Settings->light_pixels = XdrvMailbox.payload; Settings->light_rotation = 0; - // Ws2812ReinitStrip(); -- does not work - TasmotaGlobal.restart_flag = 2; // reboot instead + Ws2812ReinitStrip(); -- does not work with latest NeoPixelBus driver Light.update = true; +*/ + Ws2812Clear(); // Clear all known pixels + Settings->light_pixels = XdrvMailbox.payload; + Settings->light_rotation = 0; + TasmotaGlobal.restart_flag = 2; // reboot instead } ResponseCmndNumber(Settings->light_pixels); }