From 4676db2ede644d1c71d07ccd4f273a811cd13a97 Mon Sep 17 00:00:00 2001 From: s-hadinger <49731213+s-hadinger@users.noreply.github.com> Date: Tue, 18 Nov 2025 23:05:02 +0100 Subject: [PATCH] WS2812 and Berry animation support for reverse-order LED strip (#24138) --- CHANGELOG.md | 1 + .../TasmotaLED/TASMOTALED_DOCUMENTATION.md | 30 ++++++++++--------- lib/lib_basic/TasmotaLED/src/TasmotaLED.cpp | 12 ++++++-- lib/lib_basic/TasmotaLED/src/TasmotaLED.h | 3 ++ .../xlgt_01_ws2812_esp32.ino | 3 ++ 5 files changed, 32 insertions(+), 17 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2dbc264d1..1f47837b1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ All notable changes to this project will be documented in this file. ## [15.1.0.2] ### Added +- WS2812 and Berry animation support for reverse-order LED strip ### Breaking Changed diff --git a/lib/lib_basic/TasmotaLED/TASMOTALED_DOCUMENTATION.md b/lib/lib_basic/TasmotaLED/TASMOTALED_DOCUMENTATION.md index f7f5c0dfa..215ec817a 100644 --- a/lib/lib_basic/TasmotaLED/TASMOTALED_DOCUMENTATION.md +++ b/lib/lib_basic/TasmotaLED/TASMOTALED_DOCUMENTATION.md @@ -57,10 +57,10 @@ TasmotaLED is designed with the following principles: ▼ ┌─────────────────────────────────────────────────────────────┐ │ TasmotaLED Class │ -│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │ -│ │ _buf_work │ │ _buf_show │ │ Pixel Format │ │ -│ │ (editable) │─▶│ (internal) │ │ Conversion │ │ -│ └──────────────┘ └──────────────┘ └──────────────┘ │ +│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │ +│ │ _buf_work │ │ _buf_show │ │ Pixel Format │ │ +│ │ (editable) │─▶│ (internal) │ │ Conversion │ │ +│ └──────────────┘ └──────────────┘ └──────────────┘ │ └────────────────────┬────────────────────────────────────────┘ │ ▼ @@ -116,6 +116,7 @@ TasmotaLED uses a dual-buffer system: │ - _type: uint16_t │ │ - _pixel_order: uint8_t │ │ - _w_before: bool │ +│ - _pixel_reverse: bool │ │ - _timing: uint8_t │ │ - _started: bool │ │ - _dirty: bool │ @@ -144,6 +145,7 @@ TasmotaLED uses a dual-buffer system: │ + GetType(): uint8_t │ │ + IsDirty(): bool │ │ + Dirty(): void │ +│ + SetPixelReverse(bool): void │ │ + SetRawFormat(raw): void │ └─────────────────────────────────────────────────────────────┘ │ @@ -171,18 +173,18 @@ TasmotaLED uses a dual-buffer system: │ ┌─────────────────┼─────────────────┐ │ │ │ -┌─────────┴──────────┐ ┌────┴────────┐ ┌─────┴──────────┐ -│ TasmotaLEDPusherRMT│ │TasmotaLED │ │ TasmotaLED │ -│ │ │PusherSPI │ │PusherI2S │ -├────────────────────┤ ├─────────────┤ ├────────────────┤ +┌─────────┴──────────┐ ┌────┴─────────┐ ┌─────┴──────────┐ +│ TasmotaLEDPusherRMT│ │TasmotaLED │ │ TasmotaLED │ +│ │ │PusherSPI │ │PusherI2S │ +├────────────────────┤ ├──────────────┤ ├────────────────┤ │ - _pin: int8_t │ │- _pin: int8_t│ │(Future) │ -│ - _channel: handle │ │- _spi_strip │ │ │ -│ - _led_encoder │ │- _with_dma │ │ │ -│ - _tx_config │ │ │ │ │ -├────────────────────┤ ├─────────────┤ ├────────────────┤ +│ - _channel: handle │ │- _spi_strip │ │ │ +│ - _led_encoder │ │- _with_dma │ │ │ +│ - _tx_config │ │ │ │ │ +├────────────────────┤ ├──────────────┤ ├────────────────┤ │ + Push(): bool │ │+ Push(): bool│ │ │ -│ + CanShow(): bool │ │+ CanShow() │ │ │ -└────────────────────┘ └─────────────┘ └────────────────┘ +│ + CanShow(): bool │ │+ CanShow() │ │ │ +└────────────────────┘ └──────────────┘ └────────────────┘ ``` diff --git a/lib/lib_basic/TasmotaLED/src/TasmotaLED.cpp b/lib/lib_basic/TasmotaLED/src/TasmotaLED.cpp index 1e5c982c7..6729e80b4 100644 --- a/lib/lib_basic/TasmotaLED/src/TasmotaLED.cpp +++ b/lib/lib_basic/TasmotaLED/src/TasmotaLED.cpp @@ -63,6 +63,7 @@ enum LoggingLevels {LOG_LEVEL_NONE, LOG_LEVEL_ERROR, LOG_LEVEL_INFO, LOG_LEVEL_D TasmotaLED::TasmotaLED(uint16_t type, uint16_t num_leds) : _type(type), + _pixel_reverse(false), _timing((type >> 8) & 0xFF), _started(false), _dirty(true), @@ -175,6 +176,11 @@ void TasmotaLED::Show(void) { } else { uint8_t *buf_from = _buf_work; uint8_t *buf_to = _buf_show; + int32_t pixel_incr = _pixel_size; // will be set to negative if reverse + if (_pixel_reverse) { + buf_from += (_pixel_count - 1) * _pixel_size; + pixel_incr = -pixel_incr; + } if (_pixel_size == 3) { // copying with swapping 512 pixels (1536 bytes) takes 124 microseconds to copy, so it's negligeable for (uint32_t i = 0; i < _pixel_count; i++) { @@ -182,7 +188,7 @@ void TasmotaLED::Show(void) { buf_to[(*_pixel_matrix)[1]] = buf_from[1]; // G buf_to[(*_pixel_matrix)[2]] = buf_from[2]; // B buf_to += 3; - buf_from += 3; + buf_from += pixel_incr; } } else if (_pixel_size == 4) { for (uint32_t i = 0; i < _pixel_count; i++) { @@ -191,8 +197,8 @@ void TasmotaLED::Show(void) { buf_to[(*_pixel_matrix)[1]] = buf_from[1]; // G buf_to[(*_pixel_matrix)[2]] = buf_from[2]; // B if (!_w_before) { *buf_to++ = buf_from[3]; } - buf_to += 3; // one increment already happened - buf_from += 4; + buf_to += 4; // one increment already happened + buf_from += pixel_incr; } } } diff --git a/lib/lib_basic/TasmotaLED/src/TasmotaLED.h b/lib/lib_basic/TasmotaLED/src/TasmotaLED.h index 6df882361..5bb608a7a 100644 --- a/lib/lib_basic/TasmotaLED/src/TasmotaLED.h +++ b/lib/lib_basic/TasmotaLED/src/TasmotaLED.h @@ -96,6 +96,8 @@ public: void SetPixelSubType(uint8_t type); // change only Pixel order and pixel size void _adjustSubType(void); + inline void SetPixelReverse(bool reverse) { _pixel_reverse = reverse; } + bool Begin(void); void SetPusher(TasmotaLEDPusher *pusher); // needs to be called before `Begin()`, sets the hardware implementation void Show(void); // pushes the pixels to the LED strip @@ -118,6 +120,7 @@ protected: uint16_t _type; // the composite type uint8_t _pixel_order; // permutation between RGB and position of W bool _w_before; // true if W channel comes first (4 channels only) + bool _pixel_reverse; // display LED strip in reverse order uint8_t _timing; // timing code for strip, 0=WS2812, 1=SK6812... bool _started; // true if the hardware implementation is configured bool _dirty; // for NeoPixelBus compatibility, but ignored by `Push()` diff --git a/tasmota/tasmota_xlgt_light/xlgt_01_ws2812_esp32.ino b/tasmota/tasmota_xlgt_light/xlgt_01_ws2812_esp32.ino index c75e43ea4..73913254a 100644 --- a/tasmota/tasmota_xlgt_light/xlgt_01_ws2812_esp32.ino +++ b/tasmota/tasmota_xlgt_light/xlgt_01_ws2812_esp32.ino @@ -636,6 +636,7 @@ bool Ws2812InitStrip(void) } uint16_t led_type = Ws2812SettingsToLedType(); strip = new TasmotaLED(led_type, Settings->light_pixels); + strip->SetPixelReverse(Settings->light_pixels_reverse); strip->SetPusher(pusher); strip->Begin(); @@ -651,6 +652,7 @@ bool Ws2812ChangePixelCount(void) return true; } strip->SetPixelCount(Settings->light_pixels); + strip->SetPixelReverse(Settings->light_pixels_reverse); Ws2812Clear(); return true; } @@ -662,6 +664,7 @@ bool Ws2812ChangePixelType(bool clear) } uint16_t led_type = Ws2812SettingsToLedType(); strip->SetPixelSubType(led_type & 0xFF); // just submit the lower part + strip->SetPixelReverse(Settings->light_pixels_reverse); if (clear) { Ws2812Clear(); } else {