added rgb_order configuration

This commit is contained in:
2026-10-30 10:36:50 +01:00
parent 8c7cfc1fa9
commit 2962fb2ce7
4 changed files with 96 additions and 7 deletions
+4
View File
@@ -51,6 +51,9 @@ The component exposes an RGB light and supports all
- **chipset** (*Optional*, string): `APA102` or `WS2801`. Defaults to `APA102`. - **chipset** (*Optional*, string): `APA102` or `WS2801`. Defaults to `APA102`.
- **num_leds** (*Optional*, int): Number of LEDs in the strip. Defaults to `1`. - **num_leds** (*Optional*, int): Number of LEDs in the strip. Defaults to `1`.
- **rgb_order** (*Optional*, string): The order in which the color bytes of each LED are sent.
One of `RGB`, `RBG`, `GRB`, `GBR`, `BGR`, `BRG`. Defaults to `BGR` for `APA102` and `RGB` for `WS2801`.
Change this if the colors on your strip come out swapped.
- **data_rate** (*Optional*): SPI clock frequency. Defaults to `1MHz`. - **data_rate** (*Optional*): SPI clock frequency. Defaults to `1MHz`.
- **spi_id** (*Optional*, ID): The SPI bus to use, if more than one is configured. - **spi_id** (*Optional*, ID): The SPI bus to use, if more than one is configured.
- All other options from [Light](https://esphome.io/components/light/#config-light). - All other options from [Light](https://esphome.io/components/light/#config-light).
@@ -69,6 +72,7 @@ light:
name: "WS2801 Strip" name: "WS2801 Strip"
chipset: WS2801 chipset: WS2801
num_leds: 32 num_leds: 32
rgb_order: RGB
data_rate: 1MHz data_rate: 1MHz
effects: effects:
- addressable_rainbow: - addressable_rainbow:
+32 -3
View File
@@ -1,7 +1,12 @@
import esphome.codegen as cg import esphome.codegen as cg
from esphome.components import light, spi from esphome.components import light, spi
import esphome.config_validation as cv import esphome.config_validation as cv
from esphome.const import CONF_CHIPSET, CONF_NUM_LEDS, CONF_OUTPUT_ID from esphome.const import (
CONF_CHIPSET,
CONF_NUM_LEDS,
CONF_OUTPUT_ID,
CONF_RGB_ORDER,
)
from esphome.types import ConfigType from esphome.types import ConfigType
spi_led_strip_ns = cg.esphome_ns.namespace("spi_led_strip") spi_led_strip_ns = cg.esphome_ns.namespace("spi_led_strip")
@@ -9,6 +14,7 @@ SpiLedStrip = spi_led_strip_ns.class_(
"SpiLedStrip", light.AddressableLight, spi.SPIDevice "SpiLedStrip", light.AddressableLight, spi.SPIDevice
) )
SpiLedChipset = spi_led_strip_ns.enum("SpiLedChipset", is_class=True) SpiLedChipset = spi_led_strip_ns.enum("SpiLedChipset", is_class=True)
RGBOrder = spi_led_strip_ns.enum("RGBOrder", is_class=True)
CHIPSET_APA102 = "APA102" CHIPSET_APA102 = "APA102"
CHIPSET_WS2801 = "WS2801" CHIPSET_WS2801 = "WS2801"
@@ -18,6 +24,15 @@ CHIPSETS = {
CHIPSET_WS2801: SpiLedChipset.SPI_LED_CHIPSET_WS2801, CHIPSET_WS2801: SpiLedChipset.SPI_LED_CHIPSET_WS2801,
} }
RGB_ORDERS = {
"RGB": RGBOrder.ORDER_RGB,
"RBG": RGBOrder.ORDER_RBG,
"GRB": RGBOrder.ORDER_GRB,
"GBR": RGBOrder.ORDER_GBR,
"BGR": RGBOrder.ORDER_BGR,
"BRG": RGBOrder.ORDER_BRG,
}
BASE_SCHEMA = light.ADDRESSABLE_LIGHT_SCHEMA.extend( BASE_SCHEMA = light.ADDRESSABLE_LIGHT_SCHEMA.extend(
{ {
cv.GenerateID(CONF_OUTPUT_ID): cv.declare_id(SpiLedStrip), cv.GenerateID(CONF_OUTPUT_ID): cv.declare_id(SpiLedStrip),
@@ -25,11 +40,24 @@ BASE_SCHEMA = light.ADDRESSABLE_LIGHT_SCHEMA.extend(
} }
) )
def rgb_order_schema(default: str) -> cv.Schema:
return cv.Schema(
{
cv.Optional(CONF_RGB_ORDER, default=default): cv.enum(
RGB_ORDERS, upper=True
),
}
)
CONFIG_SCHEMA = cv.typed_schema( CONFIG_SCHEMA = cv.typed_schema(
{ {
CHIPSET_APA102: BASE_SCHEMA.extend(spi.spi_device_schema(False, "1MHz")), CHIPSET_APA102: BASE_SCHEMA.extend(rgb_order_schema("BGR")).extend(
spi.spi_device_schema(False, "1MHz")
),
# WS2801 latches the data when the clock stays low, so the clock must idle low # WS2801 latches the data when the clock stays low, so the clock must idle low
CHIPSET_WS2801: BASE_SCHEMA.extend( CHIPSET_WS2801: BASE_SCHEMA.extend(rgb_order_schema("RGB")).extend(
spi.spi_device_schema(False, "1MHz", "MODE0") spi.spi_device_schema(False, "1MHz", "MODE0")
), ),
}, },
@@ -45,6 +73,7 @@ async def to_code(config: ConfigType) -> None:
if chipset == CHIPSET_WS2801: if chipset == CHIPSET_WS2801:
cg.add_define("USE_SPI_LED_STRIP_WS2801") cg.add_define("USE_SPI_LED_STRIP_WS2801")
var = cg.new_Pvariable(config[CONF_OUTPUT_ID], config[CONF_NUM_LEDS], chipset) var = cg.new_Pvariable(config[CONF_OUTPUT_ID], config[CONF_NUM_LEDS], chipset)
cg.add(var.set_rgb_order(config[CONF_RGB_ORDER]))
await light.register_light(var, config) await light.register_light(var, config)
await spi.register_spi_device(var, config) await spi.register_spi_device(var, config)
await cg.register_component(var, config) await cg.register_component(var, config)
+47 -4
View File
@@ -3,6 +3,25 @@
namespace esphome::spi_led_strip { namespace esphome::spi_led_strip {
static const LogString *rgb_order_to_string(RGBOrder rgb_order) {
switch (rgb_order) {
case RGBOrder::ORDER_RGB:
return LOG_STR("RGB");
case RGBOrder::ORDER_RBG:
return LOG_STR("RBG");
case RGBOrder::ORDER_GRB:
return LOG_STR("GRB");
case RGBOrder::ORDER_GBR:
return LOG_STR("GBR");
case RGBOrder::ORDER_BGR:
return LOG_STR("BGR");
case RGBOrder::ORDER_BRG:
return LOG_STR("BRG");
default:
return LOG_STR("UNKNOWN");
}
}
SpiLedStrip::SpiLedStrip(uint16_t num_leds, SpiLedChipset chipset) : num_leds_(num_leds), chipset_(chipset) { SpiLedStrip::SpiLedStrip(uint16_t num_leds, SpiLedChipset chipset) : num_leds_(num_leds), chipset_(chipset) {
RAMAllocator<uint8_t> allocator; RAMAllocator<uint8_t> allocator;
#ifdef USE_SPI_LED_STRIP_WS2801 #ifdef USE_SPI_LED_STRIP_WS2801
@@ -50,9 +69,10 @@ void SpiLedStrip::dump_config() {
TAG, TAG,
"SPI LED Strip:\n" "SPI LED Strip:\n"
" Chipset: %s\n" " Chipset: %s\n"
" LEDs: %d", " LEDs: %d\n"
" RGB order: %s",
this->chipset_ == SpiLedChipset::SPI_LED_CHIPSET_WS2801 ? LOG_STR_LITERAL("WS2801") : LOG_STR_LITERAL("APA102"), this->chipset_ == SpiLedChipset::SPI_LED_CHIPSET_WS2801 ? LOG_STR_LITERAL("WS2801") : LOG_STR_LITERAL("APA102"),
this->num_leds_); this->num_leds_, LOG_STR_ARG(rgb_order_to_string(this->rgb_order_)));
if (this->data_rate_ >= spi::DATA_RATE_1MHZ) { if (this->data_rate_ >= spi::DATA_RATE_1MHZ) {
esph_log_config(TAG, " Data rate: %uMHz", (unsigned) (this->data_rate_ / 1000000)); esph_log_config(TAG, " Data rate: %uMHz", (unsigned) (this->data_rate_ / 1000000));
} else { } else {
@@ -75,15 +95,38 @@ void SpiLedStrip::write_state(light::LightState *state) {
this->disable(); this->disable();
} }
light::ESPColorView SpiLedStrip::get_view_internal(int32_t index) const { light::ESPColorView SpiLedStrip::get_view_internal(int32_t index) const {
// byte offsets of red, green and blue within the color bytes of one LED
uint8_t r, g, b;
switch (this->rgb_order_) {
case RGBOrder::ORDER_RGB:
r = 0, g = 1, b = 2;
break;
case RGBOrder::ORDER_RBG:
r = 0, g = 2, b = 1;
break;
case RGBOrder::ORDER_GRB:
r = 1, g = 0, b = 2;
break;
case RGBOrder::ORDER_GBR:
r = 2, g = 0, b = 1;
break;
case RGBOrder::ORDER_BRG:
r = 1, g = 2, b = 0;
break;
case RGBOrder::ORDER_BGR:
default:
r = 2, g = 1, b = 0;
break;
}
#ifdef USE_SPI_LED_STRIP_WS2801 #ifdef USE_SPI_LED_STRIP_WS2801
if (this->chipset_ == SpiLedChipset::SPI_LED_CHIPSET_WS2801) { if (this->chipset_ == SpiLedChipset::SPI_LED_CHIPSET_WS2801) {
size_t pos = index * 3; size_t pos = index * 3;
return {this->buf_ + pos + 0, this->buf_ + pos + 1, this->buf_ + pos + 2, nullptr, return {this->buf_ + pos + r, this->buf_ + pos + g, this->buf_ + pos + b, nullptr,
this->effect_data_ + index, &this->correction_}; this->effect_data_ + index, &this->correction_};
} }
#endif #endif
size_t pos = index * 4 + 5; size_t pos = index * 4 + 5;
return {this->buf_ + pos + 2, this->buf_ + pos + 1, this->buf_ + pos + 0, nullptr, return {this->buf_ + pos + r, this->buf_ + pos + g, this->buf_ + pos + b, nullptr,
this->effect_data_ + index, &this->correction_}; this->effect_data_ + index, &this->correction_};
} }
} // namespace esphome::spi_led_strip } // namespace esphome::spi_led_strip
+13
View File
@@ -16,6 +16,16 @@ enum class SpiLedChipset : uint8_t {
SPI_LED_CHIPSET_WS2801, SPI_LED_CHIPSET_WS2801,
}; };
// Order in which the color bytes of one LED are sent over the wire
enum class RGBOrder : uint8_t {
ORDER_RGB,
ORDER_RBG,
ORDER_GRB,
ORDER_GBR,
ORDER_BGR,
ORDER_BRG,
};
class SpiLedStrip final : public light::AddressableLight, class SpiLedStrip final : public light::AddressableLight,
public spi::SPIDevice<spi::BIT_ORDER_MSB_FIRST, spi::CLOCK_POLARITY_HIGH, public spi::SPIDevice<spi::BIT_ORDER_MSB_FIRST, spi::CLOCK_POLARITY_HIGH,
spi::CLOCK_PHASE_TRAILING, spi::DATA_RATE_1MHZ> { spi::CLOCK_PHASE_TRAILING, spi::DATA_RATE_1MHZ> {
@@ -24,6 +34,8 @@ class SpiLedStrip final : public light::AddressableLight,
void setup() override; void setup() override;
float get_setup_priority() const override { return setup_priority::IO; } float get_setup_priority() const override { return setup_priority::IO; }
void set_rgb_order(RGBOrder rgb_order) { this->rgb_order_ = rgb_order; }
int32_t size() const override { return this->num_leds_; } int32_t size() const override { return this->num_leds_; }
light::LightTraits get_traits() override; light::LightTraits get_traits() override;
@@ -42,6 +54,7 @@ class SpiLedStrip final : public light::AddressableLight,
uint8_t *buf_{nullptr}; uint8_t *buf_{nullptr};
uint16_t num_leds_; uint16_t num_leds_;
SpiLedChipset chipset_; SpiLedChipset chipset_;
RGBOrder rgb_order_{RGBOrder::ORDER_BGR};
}; };
} // namespace esphome::spi_led_strip } // namespace esphome::spi_led_strip