mirror of
https://github.com/esphome/esphome.git
synced 2026-10-14 02:02:51 +00:00
[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:
co-authored by
J. Nick Koston
J. Nick Koston
parent
b88eafcd00
commit
3ddbf8b3ef
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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)
|
||||
@@ -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
|
||||
Reference in New Issue
Block a user