diff --git a/esphome/components/lcd_base/__init__.py b/esphome/components/lcd_base/__init__.py index 0ed2036c55..92fd0b5563 100644 --- a/esphome/components/lcd_base/__init__.py +++ b/esphome/components/lcd_base/__init__.py @@ -1,7 +1,9 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome.components import display -from esphome.const import CONF_DIMENSIONS +from esphome.const import CONF_DIMENSIONS, CONF_POSITION, CONF_DATA + +CONF_USER_CHARACTERS = "user_characters" lcd_base_ns = cg.esphome_ns.namespace("lcd_base") LCDDisplay = lcd_base_ns.class_("LCDDisplay", cg.PollingComponent) @@ -16,9 +18,35 @@ def validate_lcd_dimensions(value): return value +def validate_user_characters(value): + positions = set() + for conf in value: + if conf[CONF_POSITION] in positions: + raise cv.Invalid( + f"Duplicate user defined character at position {conf[CONF_POSITION]}" + ) + positions.add(conf[CONF_POSITION]) + return value + + LCD_SCHEMA = display.BASIC_DISPLAY_SCHEMA.extend( { cv.Required(CONF_DIMENSIONS): validate_lcd_dimensions, + cv.Optional(CONF_USER_CHARACTERS): cv.All( + cv.ensure_list( + cv.Schema( + { + cv.Required(CONF_POSITION): cv.int_range(min=0, max=7), + cv.Required(CONF_DATA): cv.All( + cv.ensure_list(cv.int_range(min=0, max=31)), + cv.Length(min=8, max=8), + ), + } + ), + ), + cv.Length(max=8), + validate_user_characters, + ), } ).extend(cv.polling_component_schema("1s")) @@ -27,3 +55,6 @@ async def setup_lcd_display(var, config): await cg.register_component(var, config) await display.register_display(var, config) cg.add(var.set_dimensions(config[CONF_DIMENSIONS][0], config[CONF_DIMENSIONS][1])) + if CONF_USER_CHARACTERS in config: + for usr in config[CONF_USER_CHARACTERS]: + cg.add(var.set_user_defined_char(usr[CONF_POSITION], usr[CONF_DATA])) diff --git a/esphome/components/lcd_base/lcd_display.cpp b/esphome/components/lcd_base/lcd_display.cpp index ddd7d6a6b3..b937e36c6c 100644 --- a/esphome/components/lcd_base/lcd_display.cpp +++ b/esphome/components/lcd_base/lcd_display.cpp @@ -65,6 +65,13 @@ void LCDDisplay::setup() { this->command_(LCD_DISPLAY_COMMAND_FUNCTION_SET | display_function); } + // store user defined characters + for (auto &user_defined_char : this->user_defined_chars_) { + this->command_(LCD_DISPLAY_COMMAND_SET_CGRAM_ADDR | (user_defined_char.first << 3)); + for (auto data : user_defined_char.second) + this->send(data, true); + } + this->command_(LCD_DISPLAY_COMMAND_FUNCTION_SET | display_function); uint8_t display_control = LCD_DISPLAY_DISPLAY_ON; this->command_(LCD_DISPLAY_COMMAND_DISPLAY_CONTROL | display_control); diff --git a/esphome/components/lcd_base/lcd_display.h b/esphome/components/lcd_base/lcd_display.h index ee150059c6..0c9e59758c 100644 --- a/esphome/components/lcd_base/lcd_display.h +++ b/esphome/components/lcd_base/lcd_display.h @@ -7,6 +7,8 @@ #include "esphome/components/time/real_time_clock.h" #endif +#include + namespace esphome { namespace lcd_base { @@ -19,6 +21,8 @@ class LCDDisplay : public PollingComponent { this->rows_ = rows; } + void set_user_defined_char(uint8_t pos, const std::vector &data) { this->user_defined_chars_[pos] = data; } + void setup() override; float get_setup_priority() const override; void update() override; @@ -58,6 +62,7 @@ class LCDDisplay : public PollingComponent { uint8_t columns_; uint8_t rows_; uint8_t *buffer_{nullptr}; + std::map > user_defined_chars_; }; } // namespace lcd_base diff --git a/tests/test1.yaml b/tests/test1.yaml index bd4c919e67..181f62d3f4 100644 --- a/tests/test1.yaml +++ b/tests/test1.yaml @@ -2218,6 +2218,17 @@ display: - platform: lcd_pcf8574 dimensions: 18x4 address: 0x3F + user_characters: + - position: 0 + data: + - 0b00000 + - 0b01010 + - 0b00000 + - 0b00100 + - 0b00100 + - 0b10001 + - 0b01110 + - 0b00000 lambda: |- it.print("Hello World!"); i2c_id: i2c_bus