Command 'PixelType' to change the WS2812 color order and channel number (#22876)

This commit is contained in:
s-hadinger 2025-01-24 21:21:35 +01:00 committed by GitHub
parent a91771e0cf
commit beb967ccd4
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 95 additions and 23 deletions

View File

@ -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

View File

@ -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) {

View File

@ -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_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

View File

@ -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"

View File

@ -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 <Compile>/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 <RGB>W
uint32_t spare09 : 1; // bit 9
uint32_t spare10 : 1; // bit 10
uint32_t spare11 : 1; // bit 11

View File

@ -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 <TasmotaLED.h>
@ -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)) {