[lp5562] Add LP5562 4-channel I2C LED driver output component (#18453)

Co-authored-by: J. Nick Koston <nick@koston.org>
Co-authored-by: J. Nick Koston <nick@home-assistant.io>
This commit is contained in:
Dennis Gaida
2026-10-12 12:12:11 -10:00
committed by GitHub
co-authored by J. Nick Koston J. Nick Koston
parent b88eafcd00
commit 3ddbf8b3ef
9 changed files with 205 additions and 0 deletions
+1
View File
@@ -331,6 +331,7 @@ esphome/components/ln882x/* @lamauny
esphome/components/lock/* @esphome/core
esphome/components/logger/* @esphome/core
esphome/components/logger/select/* @clydebarrow
esphome/components/lp5562/* @DennisGaida
esphome/components/lps22/* @nagisa
esphome/components/lsm6ds/* @clydebarrow
esphome/components/ltr390/* @latonita @sjtrny
+29
View File
@@ -0,0 +1,29 @@
import esphome.codegen as cg
from esphome.components import i2c
import esphome.config_validation as cv
from esphome.const import CONF_ID
from esphome.types import ConfigType
CODEOWNERS = ["@DennisGaida"]
DEPENDENCIES = ["i2c"]
DOMAIN = "lp5562"
MULTI_CONF = True
lp5562_ns = cg.esphome_ns.namespace("lp5562")
LP5562Output = lp5562_ns.class_("LP5562Output", cg.Component, i2c.I2CDevice)
CONFIG_SCHEMA = (
cv.Schema(
{
cv.GenerateID(): cv.declare_id(LP5562Output),
}
)
.extend(cv.COMPONENT_SCHEMA)
.extend(i2c.i2c_device_schema(0x30))
)
async def to_code(config: ConfigType) -> None:
var = cg.new_Pvariable(config[CONF_ID])
await cg.register_component(var, config)
await i2c.register_i2c_device(var, config)
@@ -0,0 +1,72 @@
#include "lp5562_output.h"
#include "esphome/core/hal.h"
#include "esphome/core/log.h"
namespace esphome::lp5562 {
ESPHOME_LOG_TAG(TAG, "lp5562");
// see table 26 in the datasheet https://web.archive.org/web/20240327023921/https://www.ti.com/lit/ds/symlink/lp5562.pdf
static constexpr uint8_t LP5562_REG_ENABLE = 0x00;
static constexpr uint8_t LP5562_REG_CONFIG = 0x08;
static constexpr uint8_t LP5562_REG_RESET = 0x0d;
static constexpr uint8_t LP5562_REG_LED_MAP = 0x70;
static constexpr uint8_t LP5562_RESET_VALUE = 0xff;
static constexpr uint8_t LP5562_ENABLE_CHIP_EN = 0x40;
static constexpr uint8_t LP5562_CONFIG_INT_CLK_EN = 0x01;
static constexpr uint8_t LP5562_LED_MAP_ALL_PWM_REGISTERS = 0x00; // every channel driven by its PWM register
// PWM duty-cycle registers, indexed the same way as the LP5562_CHANNELS enum in output.py
static const uint8_t LP5562_REG_PWM[4] PROGMEM = {
0x02, // blue
0x03, // green
0x04, // red
0x0e, // white
};
void LP5562Output::setup() {
// clear any state left over from before a warm reboot; the chip may not ACK while resetting, so ignore the result
this->write_byte(LP5562_REG_RESET, LP5562_RESET_VALUE);
delay(1);
// power up the chip and enable the internal 32 kHz oscillator, see
// https://github.com/m5stack/M5GFX/blob/b2422cdb4735159209c2097d425c0756505b9a81/src/M5GFX.cpp#L566
if (!this->write_byte(LP5562_REG_ENABLE, LP5562_ENABLE_CHIP_EN)) {
this->mark_failed();
return;
}
delay(1);
// use the internal clock, and drive every channel directly from its PWM register instead of the
// internal blink/fade engines
if (!this->write_byte(LP5562_REG_CONFIG, LP5562_CONFIG_INT_CLK_EN) ||
!this->write_byte(LP5562_REG_LED_MAP, LP5562_LED_MAP_ALL_PWM_REGISTERS)) {
this->mark_failed();
}
}
void LP5562Output::dump_config() {
ESP_LOGCONFIG(TAG, "LP5562:");
LOG_I2C_DEVICE(this);
if (this->is_failed()) {
ESP_LOGE(TAG, "Setting up LP5562 failed!");
}
}
void LP5562Output::set_channel_value_(uint8_t channel, uint8_t value) {
if (this->is_failed())
return;
if (!this->write_byte(progmem_read_byte(&LP5562_REG_PWM[channel]), value)) {
this->status_set_warning();
return;
}
this->status_clear_warning();
}
void LP5562Channel::write_state(float state) {
const uint8_t max_duty = 255;
const auto duty = static_cast<uint8_t>(roundf(state * max_duty));
this->parent_->set_channel_value_(this->channel_, duty);
}
} // namespace esphome::lp5562
+35
View File
@@ -0,0 +1,35 @@
#pragma once
#include "esphome/core/component.h"
#include "esphome/core/helpers.h"
#include "esphome/components/i2c/i2c.h"
#include "esphome/components/output/float_output.h"
namespace esphome::lp5562 {
class LP5562Output;
class LP5562Channel final : public output::FloatOutput, public Parented<LP5562Output> {
public:
void set_channel(uint8_t channel) { this->channel_ = channel; }
protected:
void write_state(float state) override;
uint8_t channel_{0};
};
/// LP5562 4-channel (RGBW) I2C LED driver.
class LP5562Output final : public Component, public i2c::I2CDevice {
public:
void setup() override;
void dump_config() override;
float get_setup_priority() const override { return setup_priority::HARDWARE; }
protected:
friend class LP5562Channel;
void set_channel_value_(uint8_t channel, uint8_t value);
};
} // namespace esphome::lp5562
+35
View File
@@ -0,0 +1,35 @@
import esphome.codegen as cg
from esphome.components import output
import esphome.config_validation as cv
from esphome.const import CONF_CHANNEL, CONF_ID
from esphome.types import ConfigType
from . import LP5562Output, lp5562_ns
DEPENDENCIES = ["lp5562"]
LP5562Channel = lp5562_ns.class_("LP5562Channel", output.FloatOutput)
CONF_LP5562_ID = "lp5562_id"
# Indices match the LP5562_REG_PWM lookup table in lp5562_output.cpp
LP5562_CHANNELS = {
"blue": 0,
"green": 1,
"red": 2,
"white": 3,
}
CONFIG_SCHEMA = output.FLOAT_OUTPUT_SCHEMA.extend(
{
cv.Required(CONF_ID): cv.declare_id(LP5562Channel),
cv.GenerateID(CONF_LP5562_ID): cv.use_id(LP5562Output),
cv.Required(CONF_CHANNEL): cv.enum(LP5562_CHANNELS, lower=True),
}
)
async def to_code(config: ConfigType) -> None:
var = cg.new_Pvariable(config[CONF_ID])
await cg.register_parented(var, config[CONF_LP5562_ID])
cg.add(var.set_channel(config[CONF_CHANNEL]))
await output.register_output(var, config)
+21
View File
@@ -0,0 +1,21 @@
lp5562:
- address: 0x30
id: lp5562_1
output:
- platform: lp5562
id: lp5562_red
channel: red
lp5562_id: lp5562_1
- platform: lp5562
id: lp5562_green
channel: green
lp5562_id: lp5562_1
- platform: lp5562
id: lp5562_blue
channel: blue
lp5562_id: lp5562_1
- platform: lp5562
id: lp5562_white
channel: white
lp5562_id: lp5562_1
@@ -0,0 +1,4 @@
packages:
i2c: !include ../../test_build_components/common/i2c/esp32-idf.yaml
<<: !include common.yaml
@@ -0,0 +1,4 @@
packages:
i2c: !include ../../test_build_components/common/i2c/esp8266-ard.yaml
<<: !include common.yaml
@@ -0,0 +1,4 @@
packages:
i2c: !include ../../test_build_components/common/i2c/rp2040-ard.yaml
<<: !include common.yaml