diff --git a/CHANGELOG.md b/CHANGELOG.md index 8cc3dcf2f..e7ef515ed 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ All notable changes to this project will be documented in this file. - Berry `serial.read()` read only `n` bytes (#22835) - Display template for Waveshare ESP32-C6 LCD 1.47 (#22863) - Berry `tasmota.global.tele_period` and `tasmota.settings.tele_period` (#22865) +- Command `PixelType` to change the WS2812 color order and channel number ### Breaking Changed diff --git a/lib/lib_basic/TasmotaLED/src/TasmotaLED.cpp b/lib/lib_basic/TasmotaLED/src/TasmotaLED.cpp index c64aff9db..1e5c982c7 100644 --- a/lib/lib_basic/TasmotaLED/src/TasmotaLED.cpp +++ b/lib/lib_basic/TasmotaLED/src/TasmotaLED.cpp @@ -24,13 +24,15 @@ #include "TasmotaLED.h" // DRAM_ATTR to force in IRAM because we use this in show loop -static const DRAM_ATTR uint8_t TASMOTALED_CHANNEL_ORDERS[6][3] = { - {1, 0, 2}, // GRB (0) - {2, 0, 1}, // GBR (1) +static const DRAM_ATTR uint8_t TASMOTALED_CHANNEL_ORDERS[8][3] = { + {1, 0, 2}, // Def=GRB (0) + {1, 0, 2}, // GRB (1) {0, 1, 2}, // RGB (2) {0, 2, 1}, // RBG (3) {2, 1, 0}, // BRG (4) - {1, 2, 0} // BGR (5) + {1, 2, 0}, // BGR (5) + {2, 0, 1}, // GBR (6) + {1, 0, 2} // GRB (7) // fallback if erroneous value }; static const TasmotaLED_Timing TasmotaLED_Timings[] = { @@ -61,8 +63,6 @@ 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_order((type >> 4) & 0x07), - _w_before(type & 0x08), _timing((type >> 8) & 0xFF), _started(false), _dirty(true), @@ -70,19 +70,17 @@ TasmotaLED::TasmotaLED(uint16_t type, uint16_t num_leds) : _pixel_count(num_leds), _buf_work(nullptr), _buf_show(nullptr), - _pixel_matrix(&TASMOTALED_CHANNEL_ORDERS[0]), _pusher(nullptr) { + _adjustSubType(); // compute values for _pixel_order, _w_before, _pixel_matrix if (_timing > (TasmotaLed_TimingEnd >> 8)) { _timing = 0; } switch (_type & 0x0F) { - // case TasmotaLed_1_W: - // _pixel_size = 1; - // break; case TasmotaLed_4_WRGB: _pixel_size = 4; break; + case TasmotaLed_1_Def: case TasmotaLed_3_RGB: default: // fallback _pixel_size = 3; @@ -109,6 +107,13 @@ TasmotaLED::~TasmotaLED() { _buf_show = nullptr; } +// Adjust all internal parameters accouring to sub-type +void TasmotaLED::_adjustSubType(void) { + _pixel_order = (_type >> 4) & 0x07; + _pixel_matrix = &TASMOTALED_CHANNEL_ORDERS[_pixel_order]; + _w_before = _type & 0x08; +} + void TasmotaLED::SetPixelCount(uint16_t num_leds) { if (num_leds != _pixel_count) { _pixel_count = num_leds; @@ -124,6 +129,12 @@ void TasmotaLED::SetPixelCount(uint16_t num_leds) { } } +void TasmotaLED::SetPixelSubType(uint8_t subtype) { + // subtype is only the 8 lower bits of _type + _type = (_type & 0xFF00) | (subtype & 0xFF); + _adjustSubType(); +} + // Color is passed as 0xWWRRGGBB and copied as WWRRGGBB in _buf_work void TasmotaLED::ClearTo(uint32_t wrgb, int32_t first, int32_t last) { diff --git a/lib/lib_basic/TasmotaLED/src/TasmotaLED.h b/lib/lib_basic/TasmotaLED/src/TasmotaLED.h index e31c8b826..6df882361 100644 --- a/lib/lib_basic/TasmotaLED/src/TasmotaLED.h +++ b/lib/lib_basic/TasmotaLED/src/TasmotaLED.h @@ -22,16 +22,17 @@ enum TasmotaLEDTypesEncoding : uint16_t { // bits 0..3 encode for number of bytes per pixel - TasmotaLed_1_W = 0x0, // 1 byte per pixel (not used yet) - TasmotaLed_3_RGB = 0x1, // 3 bytes per pixel - TasmotaLed_4_WRGB = 0x2, // 4 bytes per pixel + TasmotaLed_1_Def = 0x0, // Default value - identical to TasmotaLed_3_RGB + TasmotaLed_3_RGB = 0x1, // 3 bytes per pixel + TasmotaLed_4_WRGB = 0x2, // 4 bytes per pixel // bits 4..6 encode for pixel order - TasmotaLed_GRB = 0b000 << 4, - TasmotaLed_GBR = 0b001 << 4, + TasmotaLed_Def = 0b000 << 4, // Default value - identical to TasmotaLed_GRB + TasmotaLed_GRB = 0b001 << 4, TasmotaLed_RGB = 0b010 << 4, TasmotaLed_RBG = 0b011 << 4, TasmotaLed_BRG = 0b100 << 4, TasmotaLed_BGR = 0b101 << 4, + TasmotaLed_GBR = 0b110 << 4, // bit 7 sets the position for W channel TasmotaLed_xxxW = 0b0 << 7, // W channel after color TasmotaLed_Wxxx = 0b1 << 7, // W channel before color @@ -92,6 +93,8 @@ public: ~TasmotaLED(); void SetPixelCount(uint16_t num_leds); + void SetPixelSubType(uint8_t type); // change only Pixel order and pixel size + void _adjustSubType(void); bool Begin(void); void SetPusher(TasmotaLEDPusher *pusher); // needs to be called before `Begin()`, sets the hardware implementation diff --git a/tasmota/include/i18n.h b/tasmota/include/i18n.h index 77fd48a49..40a095967 100644 --- a/tasmota/include/i18n.h +++ b/tasmota/include/i18n.h @@ -532,6 +532,7 @@ #define D_CMND_PALETTE "Palette" #define D_CMND_PIXELS "Pixels" #define D_CMND_STEPPIXELS "StepPixels" +#define D_CMND_PIXELTYPE "PixelType" #define D_CMND_ARTNET "ArtNet" #define D_CMND_ARTNET_CONFIG "ArtNetConfig" #define D_SO_ARTNET_AUTORUN "ArtNetAutorun" diff --git a/tasmota/include/tasmota_types.h b/tasmota/include/tasmota_types.h index 93805669e..5e325fe08 100644 --- a/tasmota/include/tasmota_types.h +++ b/tasmota/include/tasmota_types.h @@ -242,11 +242,9 @@ typedef union { uint32_t data; // Allow bit manipulation struct { uint32_t log_file_idx : 4; // bit 0.3 (v14.4.1.2) - FileLog log rotate index - uint32_t spare04 : 1; // bit 4 - uint32_t spare05 : 1; // bit 5 - uint32_t spare06 : 1; // bit 6 - uint32_t spare07 : 1; // bit 7 - uint32_t spare08 : 1; // bit 8 + uint32_t light_pixels_order : 3; // bit 4.6 (v14.4.1.3) - LED light order /GRB/RGB/RBG/BRG/BGR/GBR, high bit indicates W before (for RGBW) + uint32_t light_pixels_rgbw : 1; // bit 7 (v14.4.1.3) - LED true is 4 channels RGBW, false is 3 channels RGB + uint32_t light_pixels_w_first : 1; // bit 8 (v14.4.1.3) - LED true if W channel comes first, default is W uint32_t spare09 : 1; // bit 9 uint32_t spare10 : 1; // bit 10 uint32_t spare11 : 1; // bit 11 diff --git a/tasmota/tasmota_xlgt_light/xlgt_01_ws2812_esp32.ino b/tasmota/tasmota_xlgt_light/xlgt_01_ws2812_esp32.ino index f2ed0d10e..c75e43ea4 100644 --- a/tasmota/tasmota_xlgt_light/xlgt_01_ws2812_esp32.ino +++ b/tasmota/tasmota_xlgt_light/xlgt_01_ws2812_esp32.ino @@ -44,10 +44,10 @@ const uint8_t WS2812_SCHEMES = 10; // Number of WS2812 schemes const char kWs2812Commands[] PROGMEM = "|" // No prefix - D_CMND_LED "|" D_CMND_PIXELS "|" D_CMND_ROTATION "|" D_CMND_WIDTH "|" D_CMND_STEPPIXELS ; + D_CMND_LED "|" D_CMND_PIXELS "|" D_CMND_ROTATION "|" D_CMND_WIDTH "|" D_CMND_STEPPIXELS "|" D_CMND_PIXELTYPE ; void (* const Ws2812Command[])(void) PROGMEM = { - &CmndLed, &CmndPixels, &CmndRotation, &CmndWidth, &CmndStepPixels }; + &CmndLed, &CmndPixels, &CmndRotation, &CmndWidth, &CmndStepPixels, &CmndPixelType }; #include @@ -610,6 +610,17 @@ void Ws2812ShowScheme(void) } } +// convert the Settings to a Led type compatible with TasmotaLED +uint16_t Ws2812SettingsToLedType(void) { + uint16_t led_type = kTasLed_Type; // default value from compile options + if (Settings->mbflag2.light_pixels_order != 0) { + led_type = (led_type & 0xFF00) | (Settings->mbflag2.light_pixels_order << 4) + | (Settings->mbflag2.light_pixels_w_first ? TasmotaLed_Wxxx : 0) + | (Settings->mbflag2.light_pixels_rgbw ? TasmotaLed_4_WRGB : TasmotaLed_3_RGB); + } + return led_type; +} + bool Ws2812InitStrip(void) { if (strip != nullptr) { @@ -623,7 +634,8 @@ bool Ws2812InitStrip(void) AddLog(LOG_LEVEL_ERROR, "LED: No hardware supported"); return false; } - strip = new TasmotaLED(kTasLed_Type, Settings->light_pixels); + uint16_t led_type = Ws2812SettingsToLedType(); + strip = new TasmotaLED(led_type, Settings->light_pixels); strip->SetPusher(pusher); strip->Begin(); @@ -643,6 +655,21 @@ bool Ws2812ChangePixelCount(void) return true; } +bool Ws2812ChangePixelType(bool clear) +{ + if (strip == nullptr) { + return true; + } + uint16_t led_type = Ws2812SettingsToLedType(); + strip->SetPixelSubType(led_type & 0xFF); // just submit the lower part + if (clear) { + Ws2812Clear(); + } else { + Ws2812LibStripShow(); + } + return true; +} + void Ws2812ModuleSelected(void) { if (Ws2812InitStrip()) { @@ -809,6 +836,37 @@ void CmndPixels(void) Settings->light_pixels, Settings->light_pixels_reverse, Settings->light_pixels_height_1 + 1, Settings->light_pixels_alternate); } +void CmndPixelType(void) +{ + if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload < 32)) { + // Value is: + // 0 = use compile-time option (default value) + // 1..6 = Pixel Order: 1=GRB 2=RGB 3=RBG 4=BRG 5=BGR 6=GBR + // Modifiers: + // +8 = 4 channels RGBW strip - default is 3 channels RGB + // +16 = W channel is sent first - default W channel is sent last + uint32_t pixels_order = XdrvMailbox.payload & 0x07; + uint32_t pixels_w_first = (XdrvMailbox.payload & 0x08) ? 1 : 0; + uint32_t pixels_rgbw = (XdrvMailbox.payload & 0x10) ? 1 : 0; + // changing number of channels requires a reboot + bool reboot = pixels_rgbw != Settings->mbflag2.light_pixels_rgbw; + if (reboot) { + TasmotaGlobal.restart_flag = 2; // force restart if number of channels changed + } + + Settings->mbflag2.light_pixels_order = pixels_order; + Settings->mbflag2.light_pixels_w_first = pixels_w_first; + Settings->mbflag2.light_pixels_rgbw = (XdrvMailbox.payload & 0x10) ? 1 : 0; + Ws2812ChangePixelType(reboot); + } + uint32_t pixel_type = 0; + if (Settings->mbflag2.light_pixels_order != 0) { + pixel_type = Settings->mbflag2.light_pixels_order | (Settings->mbflag2.light_pixels_w_first ? 0x08 : 0) + | (Settings->mbflag2.light_pixels_rgbw ? 0x10 : 0); + } + ResponseCmndNumber(pixel_type); +} + void CmndStepPixels(void) { if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 255)) {