diff --git a/CODEOWNERS b/CODEOWNERS index 3d6ea5cd32..c006db2a6a 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -111,6 +111,7 @@ esphome/components/hte501/* @Stock-M esphome/components/hydreon_rgxx/* @functionpointer esphome/components/i2c/* @esphome/core esphome/components/i2s_audio/* @jesserockz +esphome/components/ili9xxx/* @nielsnl68 esphome/components/improv_base/* @esphome/core esphome/components/improv_serial/* @esphome/core esphome/components/ina260/* @MrEditor97 diff --git a/esphome/components/bme680/bme680.cpp b/esphome/components/bme680/bme680.cpp index 64770ac0d5..e5704a8f9f 100644 --- a/esphome/components/bme680/bme680.cpp +++ b/esphome/components/bme680/bme680.cpp @@ -117,18 +117,24 @@ void BME680Component::setup() { this->calibration_.gh2 = cal2[12] << 8 | cal2[13]; this->calibration_.gh3 = cal2[15]; - if (!this->read_byte(0x02, &this->calibration_.res_heat_range)) { + uint8_t temp_var = 0; + if (!this->read_byte(0x02, &temp_var)) { this->mark_failed(); return; } - if (!this->read_byte(0x00, &this->calibration_.res_heat_val)) { + this->calibration_.res_heat_range = ((temp_var & 0x30) / 16); + + if (!this->read_byte(0x00, &temp_var)) { this->mark_failed(); return; } - if (!this->read_byte(0x04, &this->calibration_.range_sw_err)) { + this->calibration_.res_heat_val = (int8_t) temp_var; + + if (!this->read_byte(0x04, &temp_var)) { this->mark_failed(); return; } + this->calibration_.range_sw_err = ((int8_t) temp_var & (int8_t) 0xf0) / 16; this->calibration_.ambient_temperature = 25; // prime ambient temperature @@ -181,7 +187,7 @@ void BME680Component::setup() { return; } gas0_control &= ~0b00001000; - gas0_control |= heat_off ? 0b100 : 0b000; + gas0_control |= heat_off << 3; if (!this->write_byte(BME680_REGISTER_CONTROL_GAS0, gas0_control)) { this->mark_failed(); return; @@ -249,12 +255,12 @@ uint8_t BME680Component::calc_heater_resistance_(uint16_t temperature) { if (temperature > 400) temperature = 400; - const uint8_t ambient_temperature = this->calibration_.ambient_temperature; + const int8_t ambient_temperature = this->calibration_.ambient_temperature; const int8_t gh1 = this->calibration_.gh1; const int16_t gh2 = this->calibration_.gh2; const int8_t gh3 = this->calibration_.gh3; const uint8_t res_heat_range = this->calibration_.res_heat_range; - const uint8_t res_heat_val = this->calibration_.res_heat_val; + const int8_t res_heat_val = this->calibration_.res_heat_val; uint8_t heatr_res; int32_t var1; @@ -293,35 +299,57 @@ uint8_t BME680Component::calc_heater_duration_(uint16_t duration) { void BME680Component::read_data_() { uint8_t data[15]; if (!this->read_bytes(BME680_REGISTER_FIELD0, data, 15)) { + if (this->temperature_sensor_ != nullptr) + this->temperature_sensor_->publish_state(NAN); + if (this->pressure_sensor_ != nullptr) + this->pressure_sensor_->publish_state(NAN); + if (this->humidity_sensor_ != nullptr) + this->humidity_sensor_->publish_state(NAN); + if (this->gas_resistance_sensor_ != nullptr) + this->gas_resistance_sensor_->publish_state(NAN); + ESP_LOGW(TAG, "Communication with BME680 failed!"); this->status_set_warning(); return; } + this->status_clear_warning(); uint32_t raw_temperature = (uint32_t(data[5]) << 12) | (uint32_t(data[6]) << 4) | (uint32_t(data[7]) >> 4); uint32_t raw_pressure = (uint32_t(data[2]) << 12) | (uint32_t(data[3]) << 4) | (uint32_t(data[4]) >> 4); uint32_t raw_humidity = (uint32_t(data[8]) << 8) | uint32_t(data[9]); - uint16_t raw_gas = (uint16_t(data[13]) << 2) | (uint16_t(14) >> 6); + uint16_t raw_gas = (uint16_t)((uint32_t) data[13] * 4 | (((uint32_t) data[14]) / 64)); uint8_t gas_range = data[14] & 0x0F; float temperature = this->calc_temperature_(raw_temperature); float pressure = this->calc_pressure_(raw_pressure); float humidity = this->calc_humidity_(raw_humidity); - float gas_resistance = NAN; - if (data[14] & 0x20) { - gas_resistance = this->calc_gas_resistance_(raw_gas, gas_range); - } + float gas_resistance = this->calc_gas_resistance_(raw_gas, gas_range); + + bool gas_valid = (data[14] >> 5) & 1; + bool heat_stable = (data[14] >> 4) & 1; + if (this->heater_temperature_ == 0 || this->heater_duration_ == 0) + heat_stable = true; // Allow reporting gas resistance when heater is disabled ESP_LOGD(TAG, "Got temperature=%.1f°C pressure=%.1fhPa humidity=%.1f%% gas_resistance=%.1fΩ", temperature, pressure, humidity, gas_resistance); + if (!gas_valid) + ESP_LOGW(TAG, "Gas measurement unsuccessful, reading invalid!"); + if (!heat_stable) + ESP_LOGW(TAG, "Heater unstable, reading invalid! (Normal for a few readings after a power cycle)"); + if (this->temperature_sensor_ != nullptr) this->temperature_sensor_->publish_state(temperature); if (this->pressure_sensor_ != nullptr) this->pressure_sensor_->publish_state(pressure); if (this->humidity_sensor_ != nullptr) this->humidity_sensor_->publish_state(humidity); - if (this->gas_resistance_sensor_ != nullptr) - this->gas_resistance_sensor_->publish_state(gas_resistance); - this->status_clear_warning(); + if (this->gas_resistance_sensor_ != nullptr) { + if (gas_valid && heat_stable) { + this->gas_resistance_sensor_->publish_state(gas_resistance); + } else { + this->status_set_warning(); + this->gas_resistance_sensor_->publish_state(NAN); + } + } } float BME680Component::calc_temperature_(uint32_t raw_temperature) { @@ -428,20 +456,22 @@ float BME680Component::calc_humidity_(uint16_t raw_humidity) { return calc_hum; } -uint32_t BME680Component::calc_gas_resistance_(uint16_t raw_gas, uint8_t range) { +float BME680Component::calc_gas_resistance_(uint16_t raw_gas, uint8_t range) { float calc_gas_res; float var1 = 0; float var2 = 0; float var3 = 0; + float raw_gas_f = raw_gas; + float range_f = 1U << range; const float range_sw_err = this->calibration_.range_sw_err; var1 = 1340.0f + (5.0f * range_sw_err); var2 = var1 * (1.0f + BME680_GAS_LOOKUP_TABLE_1[range] / 100.0f); var3 = 1.0f + (BME680_GAS_LOOKUP_TABLE_2[range] / 100.0f); - calc_gas_res = 1.0f / (var3 * 0.000000125f * float(1 << range) * (((float(raw_gas) - 512.0f) / var2) + 1.0f)); + calc_gas_res = 1.0f / (var3 * 0.000000125f * range_f * (((raw_gas_f - 512.0f) / var2) + 1.0f)); - return static_cast(calc_gas_res); + return calc_gas_res; } uint32_t BME680Component::calc_meas_duration_() { uint32_t tph_dur; // Calculate in us diff --git a/esphome/components/bme680/bme680.h b/esphome/components/bme680/bme680.h index 6446449742..cfa7aaca20 100644 --- a/esphome/components/bme680/bme680.h +++ b/esphome/components/bme680/bme680.h @@ -59,11 +59,11 @@ struct BME680CalibrationData { int8_t gh3; uint8_t res_heat_range; - uint8_t res_heat_val; - uint8_t range_sw_err; + int8_t res_heat_val; + int8_t range_sw_err; float tfine; - uint8_t ambient_temperature; + int8_t ambient_temperature; }; class BME680Component : public PollingComponent, public i2c::I2CDevice { @@ -117,7 +117,7 @@ class BME680Component : public PollingComponent, public i2c::I2CDevice { /// Calculate the relative humidity in % using the provided raw ADC value. float calc_humidity_(uint16_t raw_humidity); /// Calculate the gas resistance in Ω using the provided raw ADC value. - uint32_t calc_gas_resistance_(uint16_t raw_gas, uint8_t range); + float calc_gas_resistance_(uint16_t raw_gas, uint8_t range); /// Calculate how long the sensor will take until we can retrieve data. uint32_t calc_meas_duration_(); diff --git a/esphome/components/display/display_buffer.cpp b/esphome/components/display/display_buffer.cpp index 85ebd2567b..420801f863 100644 --- a/esphome/components/display/display_buffer.cpp +++ b/esphome/components/display/display_buffer.cpp @@ -256,7 +256,7 @@ void DisplayBuffer::print(int x, int y, Font *font, Color color, TextAlign align if (glyph_n < 0) { // Unknown char, skip ESP_LOGW(TAG, "Encountered character without representation in font: '%c'", text[i]); - if (font->get_glyphs_size() > 0) { + if (!font->get_glyphs().empty()) { uint8_t glyph_width = font->get_glyphs()[0].glyph_data_->width; for (int glyph_x = 0; glyph_x < glyph_width; glyph_x++) { for (int glyph_y = 0; glyph_y < height; glyph_y++) @@ -557,7 +557,7 @@ void Glyph::scan_area(int *x1, int *y1, int *width, int *height) const { } int Font::match_next_glyph(const char *str, int *match_length) { int lo = 0; - int hi = this->glyphs_size_ - 1; + int hi = this->glyphs_.size() - 1; while (lo != hi) { int mid = (lo + hi + 1) / 2; if (this->glyphs_[mid].compare_to(str)) { @@ -583,7 +583,7 @@ void Font::measure(const char *str, int *width, int *x_offset, int *baseline, in int glyph_n = this->match_next_glyph(str + i, &match_length); if (glyph_n < 0) { // Unknown char, skip - if (this->glyphs_size_ > 0) + if (!this->get_glyphs().empty()) x += this->get_glyphs()[0].glyph_data_->width; i++; continue; @@ -604,16 +604,9 @@ void Font::measure(const char *str, int *width, int *x_offset, int *baseline, in *width = x - min_x; } Font::Font(const GlyphData *data, int data_nr, int baseline, int height) : baseline_(baseline), height_(height) { - ExternalRAMAllocator allocator(ExternalRAMAllocator::ALLOW_FAILURE); - this->glyphs_ = allocator.allocate(data_nr); - if (this->glyphs_ == nullptr) { - ESP_LOGE(TAG, "Could not allocate buffer for Glyphs!"); - return; - } - for (int i = 0; i < data_nr; ++i) { - this->glyphs_[i] = Glyph(data + i); - } - this->glyphs_size_ = data_nr; + glyphs_.reserve(data_nr); + for (int i = 0; i < data_nr; ++i) + glyphs_.emplace_back(&data[i]); } bool Image::get_pixel(int x, int y) const { diff --git a/esphome/components/display/display_buffer.h b/esphome/components/display/display_buffer.h index 815ba8d2e1..0402826594 100644 --- a/esphome/components/display/display_buffer.h +++ b/esphome/components/display/display_buffer.h @@ -526,12 +526,10 @@ class Font { inline int get_baseline() { return this->baseline_; } inline int get_height() { return this->height_; } - Glyph *&get_glyphs() { return this->glyphs_; } - const u_int16_t &get_glyphs_size() const { return this->glyphs_size_; } + const std::vector> &get_glyphs() const { return glyphs_; } protected: - Glyph *glyphs_{nullptr}; - u_int16_t glyphs_size_; + std::vector> glyphs_; int baseline_; int height_; }; diff --git a/esphome/components/ethernet/__init__.py b/esphome/components/ethernet/__init__.py index a0f8b557b0..f6ca376681 100644 --- a/esphome/components/ethernet/__init__.py +++ b/esphome/components/ethernet/__init__.py @@ -36,12 +36,25 @@ ETHERNET_TYPES = { "JL1101": EthernetType.ETHERNET_TYPE_JL1101, } +emac_rmii_clock_mode_t = cg.global_ns.enum("emac_rmii_clock_mode_t") emac_rmii_clock_gpio_t = cg.global_ns.enum("emac_rmii_clock_gpio_t") CLK_MODES = { - "GPIO0_IN": emac_rmii_clock_gpio_t.EMAC_CLK_IN_GPIO, - "GPIO0_OUT": emac_rmii_clock_gpio_t.EMAC_APPL_CLK_OUT_GPIO, - "GPIO16_OUT": emac_rmii_clock_gpio_t.EMAC_CLK_OUT_GPIO, - "GPIO17_OUT": emac_rmii_clock_gpio_t.EMAC_CLK_OUT_180_GPIO, + "GPIO0_IN": ( + emac_rmii_clock_mode_t.EMAC_CLK_EXT_IN, + emac_rmii_clock_gpio_t.EMAC_CLK_IN_GPIO, + ), + "GPIO0_OUT": ( + emac_rmii_clock_mode_t.EMAC_CLK_OUT, + emac_rmii_clock_gpio_t.EMAC_APPL_CLK_OUT_GPIO, + ), + "GPIO16_OUT": ( + emac_rmii_clock_mode_t.EMAC_CLK_OUT, + emac_rmii_clock_gpio_t.EMAC_CLK_OUT_GPIO, + ), + "GPIO17_OUT": ( + emac_rmii_clock_mode_t.EMAC_CLK_OUT, + emac_rmii_clock_gpio_t.EMAC_CLK_OUT_180_GPIO, + ), } @@ -114,7 +127,7 @@ async def to_code(config): cg.add(var.set_mdc_pin(config[CONF_MDC_PIN])) cg.add(var.set_mdio_pin(config[CONF_MDIO_PIN])) cg.add(var.set_type(config[CONF_TYPE])) - cg.add(var.set_clk_mode(CLK_MODES[config[CONF_CLK_MODE]])) + cg.add(var.set_clk_mode(*CLK_MODES[config[CONF_CLK_MODE]])) cg.add(var.set_use_address(config[CONF_USE_ADDRESS])) if CONF_POWER_PIN in config: diff --git a/esphome/components/ethernet/ethernet_component.cpp b/esphome/components/ethernet/ethernet_component.cpp index 9152b33a14..7120223cc9 100644 --- a/esphome/components/ethernet/ethernet_component.cpp +++ b/esphome/components/ethernet/ethernet_component.cpp @@ -47,8 +47,8 @@ void EthernetComponent::setup() { mac_config.smi_mdc_gpio_num = this->mdc_pin_; mac_config.smi_mdio_gpio_num = this->mdio_pin_; - mac_config.clock_config.rmii.clock_mode = this->clk_mode_ == EMAC_CLK_IN_GPIO ? EMAC_CLK_EXT_IN : EMAC_CLK_OUT; - mac_config.clock_config.rmii.clock_gpio = this->clk_mode_; + mac_config.clock_config.rmii.clock_mode = this->clk_mode_; + mac_config.clock_config.rmii.clock_gpio = this->clk_gpio_; esp_eth_mac_t *mac = esp_eth_mac_new_esp32(&mac_config); @@ -315,7 +315,10 @@ void EthernetComponent::set_power_pin(int power_pin) { this->power_pin_ = power_ void EthernetComponent::set_mdc_pin(uint8_t mdc_pin) { this->mdc_pin_ = mdc_pin; } void EthernetComponent::set_mdio_pin(uint8_t mdio_pin) { this->mdio_pin_ = mdio_pin; } void EthernetComponent::set_type(EthernetType type) { this->type_ = type; } -void EthernetComponent::set_clk_mode(emac_rmii_clock_gpio_t clk_mode) { this->clk_mode_ = clk_mode; } +void EthernetComponent::set_clk_mode(emac_rmii_clock_mode_t clk_mode, emac_rmii_clock_gpio_t clk_gpio) { + this->clk_mode_ = clk_mode; + this->clk_gpio_ = clk_gpio; +} void EthernetComponent::set_manual_ip(const ManualIP &manual_ip) { this->manual_ip_ = manual_ip; } std::string EthernetComponent::get_use_address() const { diff --git a/esphome/components/ethernet/ethernet_component.h b/esphome/components/ethernet/ethernet_component.h index a538a5c77d..872ed17044 100644 --- a/esphome/components/ethernet/ethernet_component.h +++ b/esphome/components/ethernet/ethernet_component.h @@ -50,7 +50,7 @@ class EthernetComponent : public Component { void set_mdc_pin(uint8_t mdc_pin); void set_mdio_pin(uint8_t mdio_pin); void set_type(EthernetType type); - void set_clk_mode(emac_rmii_clock_gpio_t clk_mode); + void set_clk_mode(emac_rmii_clock_mode_t clk_mode, emac_rmii_clock_gpio_t clk_gpio); void set_manual_ip(const ManualIP &manual_ip); network::IPAddress get_ip_address(); @@ -70,7 +70,8 @@ class EthernetComponent : public Component { uint8_t mdc_pin_{23}; uint8_t mdio_pin_{18}; EthernetType type_{ETHERNET_TYPE_LAN8720}; - emac_rmii_clock_gpio_t clk_mode_{EMAC_CLK_IN_GPIO}; + emac_rmii_clock_mode_t clk_mode_{EMAC_CLK_EXT_IN}; + emac_rmii_clock_gpio_t clk_gpio_{EMAC_CLK_IN_GPIO}; optional manual_ip_{}; bool started_{false}; diff --git a/esphome/components/ili9341/display.py b/esphome/components/ili9341/display.py index 0b87a0c4eb..6ab6a5e8ef 100644 --- a/esphome/components/ili9341/display.py +++ b/esphome/components/ili9341/display.py @@ -1,153 +1,5 @@ -import esphome.codegen as cg import esphome.config_validation as cv -from esphome import core, pins -from esphome.components import display, spi -from esphome.const import ( - CONF_COLOR_PALETTE, - CONF_DC_PIN, - CONF_ID, - CONF_LAMBDA, - CONF_MODEL, - CONF_PAGES, - CONF_RAW_DATA_ID, - CONF_RESET_PIN, + +CONFIG_SCHEMA = cv.invalid( + "The ili9341 platform component has been renamed to ili9xxx." ) -from esphome.core import CORE, HexInt - -DEPENDENCIES = ["spi"] - -CONF_COLOR_PALETTE_IMAGES = "color_palette_images" -CONF_LED_PIN = "led_pin" - -ili9341_ns = cg.esphome_ns.namespace("ili9341") -ili9341 = ili9341_ns.class_( - "ILI9341Display", cg.PollingComponent, spi.SPIDevice, display.DisplayBuffer -) -ILI9341M5Stack = ili9341_ns.class_("ILI9341M5Stack", ili9341) -ILI9341TFT24 = ili9341_ns.class_("ILI9341TFT24", ili9341) -ILI9341TFT24R = ili9341_ns.class_("ILI9341TFT24R", ili9341) - -ILI9341Model = ili9341_ns.enum("ILI9341Model") -ILI9341ColorMode = ili9341_ns.enum("ILI9341ColorMode") - -MODELS = { - "M5STACK": ILI9341Model.M5STACK, - "TFT_2.4": ILI9341Model.TFT_24, - "TFT_2.4R": ILI9341Model.TFT_24R, -} - -ILI9341_MODEL = cv.enum(MODELS, upper=True, space="_") - -COLOR_PALETTE = cv.one_of("NONE", "GRAYSCALE", "IMAGE_ADAPTIVE") - - -def _validate(config): - if config.get(CONF_COLOR_PALETTE) == "IMAGE_ADAPTIVE" and not config.get( - CONF_COLOR_PALETTE_IMAGES - ): - raise cv.Invalid( - "Color palette in IMAGE_ADAPTIVE mode requires at least one 'color_palette_images' entry to generate palette" - ) - if ( - config.get(CONF_COLOR_PALETTE_IMAGES) - and config.get(CONF_COLOR_PALETTE) != "IMAGE_ADAPTIVE" - ): - raise cv.Invalid( - "Providing color palette images requires palette mode to be 'IMAGE_ADAPTIVE'" - ) - return config - - -CONFIG_SCHEMA = cv.All( - display.FULL_DISPLAY_SCHEMA.extend( - { - cv.GenerateID(): cv.declare_id(ili9341), - cv.Required(CONF_MODEL): ILI9341_MODEL, - cv.Required(CONF_DC_PIN): pins.gpio_output_pin_schema, - cv.Optional(CONF_RESET_PIN): pins.gpio_output_pin_schema, - cv.Optional(CONF_LED_PIN): pins.gpio_output_pin_schema, - cv.Optional(CONF_COLOR_PALETTE, default="NONE"): COLOR_PALETTE, - cv.Optional(CONF_COLOR_PALETTE_IMAGES, default=[]): cv.ensure_list( - cv.file_ - ), - cv.GenerateID(CONF_RAW_DATA_ID): cv.declare_id(cg.uint8), - } - ) - .extend(cv.polling_component_schema("1s")) - .extend(spi.spi_device_schema(False)), - cv.has_at_most_one_key(CONF_PAGES, CONF_LAMBDA), - _validate, -) - - -async def to_code(config): - if config[CONF_MODEL] == "M5STACK": - lcd_type = ILI9341M5Stack - if config[CONF_MODEL] == "TFT_2.4": - lcd_type = ILI9341TFT24 - if config[CONF_MODEL] == "TFT_2.4R": - lcd_type = ILI9341TFT24R - rhs = lcd_type.new() - var = cg.Pvariable(config[CONF_ID], rhs) - - await cg.register_component(var, config) - await display.register_display(var, config) - await spi.register_spi_device(var, config) - cg.add(var.set_model(config[CONF_MODEL])) - dc = await cg.gpio_pin_expression(config[CONF_DC_PIN]) - cg.add(var.set_dc_pin(dc)) - - if CONF_LAMBDA in config: - lambda_ = await cg.process_lambda( - config[CONF_LAMBDA], [(display.DisplayBufferRef, "it")], return_type=cg.void - ) - cg.add(var.set_writer(lambda_)) - if CONF_RESET_PIN in config: - reset = await cg.gpio_pin_expression(config[CONF_RESET_PIN]) - cg.add(var.set_reset_pin(reset)) - if CONF_LED_PIN in config: - led_pin = await cg.gpio_pin_expression(config[CONF_LED_PIN]) - cg.add(var.set_led_pin(led_pin)) - - rhs = None - if config[CONF_COLOR_PALETTE] == "GRAYSCALE": - cg.add(var.set_buffer_color_mode(ILI9341ColorMode.BITS_8_INDEXED)) - rhs = [] - for x in range(256): - rhs.extend([HexInt(x), HexInt(x), HexInt(x)]) - elif config[CONF_COLOR_PALETTE] == "IMAGE_ADAPTIVE": - cg.add(var.set_buffer_color_mode(ILI9341ColorMode.BITS_8_INDEXED)) - from PIL import Image - - def load_image(filename): - path = CORE.relative_config_path(filename) - try: - return Image.open(path) - except Exception as e: - raise core.EsphomeError(f"Could not load image file {path}: {e}") - - # make a wide horizontal combined image. - images = [load_image(x) for x in config[CONF_COLOR_PALETTE_IMAGES]] - total_width = sum(i.width for i in images) - max_height = max(i.height for i in images) - - ref_image = Image.new("RGB", (total_width, max_height)) - x = 0 - for i in images: - ref_image.paste(i, (x, 0)) - x = x + i.width - - # reduce the colors on combined image to 256. - converted = ref_image.convert("P", palette=Image.ADAPTIVE, colors=256) - # if you want to verify how the images look use - # ref_image.save("ref_in.png") - # converted.save("ref_out.png") - palette = converted.getpalette() - assert len(palette) == 256 * 3 - rhs = palette - else: - cg.add(var.set_buffer_color_mode(ILI9341ColorMode.BITS_8)) - - if rhs is not None: - prog_arr = cg.progmem_array(config[CONF_RAW_DATA_ID], rhs) - cg.add(var.set_palette(prog_arr)) diff --git a/esphome/components/ili9341/ili9341_defines.h b/esphome/components/ili9341/ili9341_defines.h deleted file mode 100644 index 6b3d4c0dcf..0000000000 --- a/esphome/components/ili9341/ili9341_defines.h +++ /dev/null @@ -1,83 +0,0 @@ -#pragma once - -namespace esphome { -namespace ili9341 { - -// Color definitions -// clang-format off -static const uint8_t MADCTL_MY = 0x80; ///< Bit 7 Bottom to top -static const uint8_t MADCTL_MX = 0x40; ///< Bit 6 Right to left -static const uint8_t MADCTL_MV = 0x20; ///< Bit 5 Reverse Mode -static const uint8_t MADCTL_ML = 0x10; ///< Bit 4 LCD refresh Bottom to top -static const uint8_t MADCTL_RGB = 0x00; ///< Bit 3 Red-Green-Blue pixel order -static const uint8_t MADCTL_BGR = 0x08; ///< Bit 3 Blue-Green-Red pixel order -static const uint8_t MADCTL_MH = 0x04; ///< Bit 2 LCD refresh right to left -// clang-format on - -static const uint16_t ILI9341_TFTWIDTH = 320; ///< ILI9341 max TFT width -static const uint16_t ILI9341_TFTHEIGHT = 240; ///< ILI9341 max TFT height - -// All ILI9341 specific commands some are used by init() -static const uint8_t ILI9341_NOP = 0x00; -static const uint8_t ILI9341_SWRESET = 0x01; -static const uint8_t ILI9341_RDDID = 0x04; -static const uint8_t ILI9341_RDDST = 0x09; - -static const uint8_t ILI9341_SLPIN = 0x10; -static const uint8_t ILI9341_SLPOUT = 0x11; -static const uint8_t ILI9341_PTLON = 0x12; -static const uint8_t ILI9341_NORON = 0x13; - -static const uint8_t ILI9341_RDMODE = 0x0A; -static const uint8_t ILI9341_RDMADCTL = 0x0B; -static const uint8_t ILI9341_RDPIXFMT = 0x0C; -static const uint8_t ILI9341_RDIMGFMT = 0x0A; -static const uint8_t ILI9341_RDSELFDIAG = 0x0F; - -static const uint8_t ILI9341_INVOFF = 0x20; -static const uint8_t ILI9341_INVON = 0x21; -static const uint8_t ILI9341_GAMMASET = 0x26; -static const uint8_t ILI9341_DISPOFF = 0x28; -static const uint8_t ILI9341_DISPON = 0x29; - -static const uint8_t ILI9341_CASET = 0x2A; -static const uint8_t ILI9341_PASET = 0x2B; -static const uint8_t ILI9341_RAMWR = 0x2C; -static const uint8_t ILI9341_RAMRD = 0x2E; - -static const uint8_t ILI9341_PTLAR = 0x30; -static const uint8_t ILI9341_VSCRDEF = 0x33; -static const uint8_t ILI9341_MADCTL = 0x36; -static const uint8_t ILI9341_VSCRSADD = 0x37; -static const uint8_t ILI9341_PIXFMT = 0x3A; - -static const uint8_t ILI9341_WRDISBV = 0x51; -static const uint8_t ILI9341_RDDISBV = 0x52; -static const uint8_t ILI9341_WRCTRLD = 0x53; - -static const uint8_t ILI9341_FRMCTR1 = 0xB1; -static const uint8_t ILI9341_FRMCTR2 = 0xB2; -static const uint8_t ILI9341_FRMCTR3 = 0xB3; -static const uint8_t ILI9341_INVCTR = 0xB4; -static const uint8_t ILI9341_DFUNCTR = 0xB6; - -static const uint8_t ILI9341_PWCTR1 = 0xC0; -static const uint8_t ILI9341_PWCTR2 = 0xC1; -static const uint8_t ILI9341_PWCTR3 = 0xC2; -static const uint8_t ILI9341_PWCTR4 = 0xC3; -static const uint8_t ILI9341_PWCTR5 = 0xC4; -static const uint8_t ILI9341_VMCTR1 = 0xC5; -static const uint8_t ILI9341_VMCTR2 = 0xC7; - -static const uint8_t ILI9341_RDID4 = 0xD3; -static const uint8_t ILI9341_RDINDEX = 0xD9; -static const uint8_t ILI9341_RDID1 = 0xDA; -static const uint8_t ILI9341_RDID2 = 0xDB; -static const uint8_t ILI9341_RDID3 = 0xDC; -static const uint8_t ILI9341_RDIDX = 0xDD; // TBC - -static const uint8_t ILI9341_GMCTRP1 = 0xE0; -static const uint8_t ILI9341_GMCTRN1 = 0xE1; - -} // namespace ili9341 -} // namespace esphome diff --git a/esphome/components/ili9341/ili9341_display.cpp b/esphome/components/ili9341/ili9341_display.cpp deleted file mode 100644 index 9f9edcf21f..0000000000 --- a/esphome/components/ili9341/ili9341_display.cpp +++ /dev/null @@ -1,308 +0,0 @@ -#include "ili9341_display.h" -#include "esphome/core/log.h" -#include "esphome/core/application.h" -#include "esphome/core/helpers.h" -#include "esphome/core/hal.h" - -namespace esphome { -namespace ili9341 { - -static const char *const TAG = "ili9341"; - -void ILI9341Display::setup_pins_() { - this->dc_pin_->setup(); // OUTPUT - this->dc_pin_->digital_write(false); - if (this->reset_pin_ != nullptr) { - this->reset_pin_->setup(); // OUTPUT - this->reset_pin_->digital_write(true); - } - if (this->led_pin_ != nullptr) { - this->led_pin_->setup(); - this->led_pin_->digital_write(true); - } - this->spi_setup(); - - this->reset_(); -} - -void ILI9341Display::dump_config() { - LOG_DISPLAY("", "ili9341", this); - LOG_PIN(" Reset Pin: ", this->reset_pin_); - LOG_PIN(" DC Pin: ", this->dc_pin_); - LOG_PIN(" Busy Pin: ", this->busy_pin_); - LOG_UPDATE_INTERVAL(this); -} - -float ILI9341Display::get_setup_priority() const { return setup_priority::HARDWARE; } - -void ILI9341Display::command(uint8_t value) { - this->start_command_(); - this->write_byte(value); - this->end_command_(); -} - -void ILI9341Display::reset_() { - if (this->reset_pin_ != nullptr) { - this->reset_pin_->digital_write(false); - delay(10); - this->reset_pin_->digital_write(true); - delay(10); - } -} - -void ILI9341Display::data(uint8_t value) { - this->start_data_(); - this->write_byte(value); - this->end_data_(); -} - -void ILI9341Display::send_command(uint8_t command_byte, const uint8_t *data_bytes, uint8_t num_data_bytes) { - this->command(command_byte); // Send the command byte - this->start_data_(); - this->write_array(data_bytes, num_data_bytes); - this->end_data_(); -} - -uint8_t ILI9341Display::read_command(uint8_t command_byte, uint8_t index) { - uint8_t data = 0x10 + index; - this->send_command(0xD9, &data, 1); // Set Index Register - uint8_t result; - this->start_command_(); - this->write_byte(command_byte); - this->start_data_(); - do { - result = this->read_byte(); - } while (index--); - this->end_data_(); - return result; -} - -void ILI9341Display::update() { - this->do_update_(); - this->display_(); -} - -void ILI9341Display::display_() { - // we will only update the changed window to the display - uint16_t w = this->x_high_ - this->x_low_ + 1; - uint16_t h = this->y_high_ - this->y_low_ + 1; - uint32_t start_pos = ((this->y_low_ * this->width_) + x_low_); - - // check if something was displayed - if ((this->x_high_ < this->x_low_) || (this->y_high_ < this->y_low_)) { - return; - } - - set_addr_window_(this->x_low_, this->y_low_, w, h); - - ESP_LOGVV("ILI9341", "Start ILI9341Display::display_(xl:%d, xh:%d, yl:%d, yh:%d, w:%d, h:%d, start_pos:%d)", - this->x_low_, this->x_high_, this->y_low_, this->y_high_, w, h, start_pos); - - this->start_data_(); - for (uint16_t row = 0; row < h; row++) { - uint32_t pos = start_pos + (row * width_); - uint32_t rem = w; - - while (rem > 0) { - uint32_t sz = buffer_to_transfer_(pos, rem); - this->write_array(transfer_buffer_, 2 * sz); - pos += sz; - rem -= sz; - App.feed_wdt(); - } - App.feed_wdt(); - } - this->end_data_(); - - // invalidate watermarks - this->x_low_ = this->width_; - this->y_low_ = this->height_; - this->x_high_ = 0; - this->y_high_ = 0; -} - -void ILI9341Display::fill(Color color) { - uint8_t color332 = 0; - if (this->buffer_color_mode_ == BITS_8) { - color332 = display::ColorUtil::color_to_332(color); - } else { // if (this->buffer_color_mode_ == BITS_8_INDEXED) - color332 = display::ColorUtil::color_to_index8_palette888(color, this->palette_); - } - memset(this->buffer_, color332, this->get_buffer_length_()); - this->x_low_ = 0; - this->y_low_ = 0; - this->x_high_ = this->get_width_internal() - 1; - this->y_high_ = this->get_height_internal() - 1; -} - -void ILI9341Display::fill_internal_(uint8_t color) { - memset(transfer_buffer_, color, sizeof(transfer_buffer_)); - - uint32_t rem = (this->get_buffer_length_() * 2); - - this->set_addr_window_(0, 0, this->get_width_internal(), this->get_height_internal()); - this->start_data_(); - - while (rem > 0) { - size_t sz = rem <= sizeof(transfer_buffer_) ? rem : sizeof(transfer_buffer_); - this->write_array(transfer_buffer_, sz); - rem -= sz; - } - - this->end_data_(); - - memset(buffer_, color, this->get_buffer_length_()); -} - -void ILI9341Display::rotate_my_(uint8_t m) { - uint8_t rotation = m & 3; // can't be higher than 3 - switch (rotation) { - case 0: - m = (MADCTL_MX | MADCTL_BGR); - // _width = ILI9341_TFTWIDTH; - // _height = ILI9341_TFTHEIGHT; - break; - case 1: - m = (MADCTL_MV | MADCTL_BGR); - // _width = ILI9341_TFTHEIGHT; - // _height = ILI9341_TFTWIDTH; - break; - case 2: - m = (MADCTL_MY | MADCTL_BGR); - // _width = ILI9341_TFTWIDTH; - // _height = ILI9341_TFTHEIGHT; - break; - case 3: - m = (MADCTL_MX | MADCTL_MY | MADCTL_MV | MADCTL_BGR); - // _width = ILI9341_TFTHEIGHT; - // _height = ILI9341_TFTWIDTH; - break; - } - - this->command(ILI9341_MADCTL); - this->data(m); -} - -void HOT ILI9341Display::draw_absolute_pixel_internal(int x, int y, Color color) { - if (x >= this->get_width_internal() || x < 0 || y >= this->get_height_internal() || y < 0) - return; - - uint32_t pos = (y * width_) + x; - uint8_t new_color; - - if (this->buffer_color_mode_ == BITS_8) { - new_color = display::ColorUtil::color_to_332(color, display::ColorOrder::COLOR_ORDER_RGB); - } else { // if (this->buffer_color_mode_ == BITS_8_INDEXED) { - new_color = display::ColorUtil::color_to_index8_palette888(color, this->palette_); - } - - if (buffer_[pos] != new_color) { - buffer_[pos] = new_color; - // low and high watermark may speed up drawing from buffer - this->x_low_ = (x < this->x_low_) ? x : this->x_low_; - this->y_low_ = (y < this->y_low_) ? y : this->y_low_; - this->x_high_ = (x > this->x_high_) ? x : this->x_high_; - this->y_high_ = (y > this->y_high_) ? y : this->y_high_; - } -} - -// should return the total size: return this->get_width_internal() * this->get_height_internal() * 2 // 16bit color -// values per bit is huge -uint32_t ILI9341Display::get_buffer_length_() { return this->get_width_internal() * this->get_height_internal(); } - -void ILI9341Display::start_command_() { - this->dc_pin_->digital_write(false); - this->enable(); -} - -void ILI9341Display::end_command_() { this->disable(); } -void ILI9341Display::start_data_() { - this->dc_pin_->digital_write(true); - this->enable(); -} -void ILI9341Display::end_data_() { this->disable(); } - -void ILI9341Display::init_lcd_(const uint8_t *init_cmd) { - uint8_t cmd, x, num_args; - const uint8_t *addr = init_cmd; - while ((cmd = progmem_read_byte(addr++)) > 0) { - x = progmem_read_byte(addr++); - num_args = x & 0x7F; - send_command(cmd, addr, num_args); - addr += num_args; - if (x & 0x80) - delay(150); // NOLINT - } -} - -void ILI9341Display::set_addr_window_(uint16_t x1, uint16_t y1, uint16_t w, uint16_t h) { - uint16_t x2 = (x1 + w - 1), y2 = (y1 + h - 1); - this->command(ILI9341_CASET); // Column address set - this->start_data_(); - this->write_byte(x1 >> 8); - this->write_byte(x1); - this->write_byte(x2 >> 8); - this->write_byte(x2); - this->end_data_(); - this->command(ILI9341_PASET); // Row address set - this->start_data_(); - this->write_byte(y1 >> 8); - this->write_byte(y1); - this->write_byte(y2 >> 8); - this->write_byte(y2); - this->end_data_(); - this->command(ILI9341_RAMWR); // Write to RAM -} - -void ILI9341Display::invert_display_(bool invert) { this->command(invert ? ILI9341_INVON : ILI9341_INVOFF); } - -int ILI9341Display::get_width_internal() { return this->width_; } -int ILI9341Display::get_height_internal() { return this->height_; } - -uint32_t ILI9341Display::buffer_to_transfer_(uint32_t pos, uint32_t sz) { - uint8_t *src = buffer_ + pos; - uint8_t *dst = transfer_buffer_; - - if (sz > sizeof(transfer_buffer_) / 2) { - sz = sizeof(transfer_buffer_) / 2; - } - - for (uint32_t i = 0; i < sz; ++i) { - uint16_t color; - if (this->buffer_color_mode_ == BITS_8) { - color = display::ColorUtil::color_to_565(display::ColorUtil::rgb332_to_color(*src++)); - } else { // if (this->buffer_color_mode == BITS_8_INDEXED) { - Color col = display::ColorUtil::index8_to_color_palette888(*src++, this->palette_); - color = display::ColorUtil::color_to_565(col); - } - *dst++ = (uint8_t)(color >> 8); - *dst++ = (uint8_t) color; - } - - return sz; -} - -// M5Stack display -void ILI9341M5Stack::initialize() { - this->init_lcd_(INITCMD_M5STACK); - this->width_ = 320; - this->height_ = 240; - this->invert_display_(true); -} - -// 24_TFT display -void ILI9341TFT24::initialize() { - this->init_lcd_(INITCMD_TFT); - this->width_ = 240; - this->height_ = 320; -} - -// 24_TFT rotated display -void ILI9341TFT24R::initialize() { - this->init_lcd_(INITCMD_TFT); - this->width_ = 320; - this->height_ = 240; -} - -} // namespace ili9341 -} // namespace esphome diff --git a/esphome/components/ili9341/ili9341_init.h b/esphome/components/ili9341/ili9341_init.h deleted file mode 100644 index b4f67ff19a..0000000000 --- a/esphome/components/ili9341/ili9341_init.h +++ /dev/null @@ -1,70 +0,0 @@ -#pragma once -#include "esphome/core/helpers.h" - -namespace esphome { -namespace ili9341 { - -// clang-format off -static const uint8_t PROGMEM INITCMD_M5STACK[] = { - 0xEF, 3, 0x03, 0x80, 0x02, - 0xCF, 3, 0x00, 0xC1, 0x30, - 0xED, 4, 0x64, 0x03, 0x12, 0x81, - 0xE8, 3, 0x85, 0x00, 0x78, - 0xCB, 5, 0x39, 0x2C, 0x00, 0x34, 0x02, - 0xF7, 1, 0x20, - 0xEA, 2, 0x00, 0x00, - ILI9341_PWCTR1 , 1, 0x23, // Power control VRH[5:0] - ILI9341_PWCTR2 , 1, 0x10, // Power control SAP[2:0];BT[3:0] - ILI9341_VMCTR1 , 2, 0x3e, 0x28, // VCM control - ILI9341_VMCTR2 , 1, 0x86, // VCM control2 - ILI9341_MADCTL , 1, MADCTL_BGR, // Memory Access Control - ILI9341_VSCRSADD, 1, 0x00, // Vertical scroll zero - ILI9341_PIXFMT , 1, 0x55, - ILI9341_FRMCTR1 , 2, 0x00, 0x13, - ILI9341_DFUNCTR , 3, 0x08, 0x82, 0x27, // Display Function Control - 0xF2, 1, 0x00, // 3Gamma Function Disable - ILI9341_GAMMASET , 1, 0x01, // Gamma curve selected - ILI9341_GMCTRP1 , 15, 0x0F, 0x31, 0x2B, 0x0C, 0x0E, 0x08, // Set Gamma - 0x4E, 0xF1, 0x37, 0x07, 0x10, 0x03, - 0x0E, 0x09, 0x00, - ILI9341_GMCTRN1 , 15, 0x00, 0x0E, 0x14, 0x03, 0x11, 0x07, // Set Gamma - 0x31, 0xC1, 0x48, 0x08, 0x0F, 0x0C, - 0x31, 0x36, 0x0F, - ILI9341_SLPOUT , 0x80, // Exit Sleep - ILI9341_DISPON , 0x80, // Display on - 0x00 // End of list -}; - -static const uint8_t PROGMEM INITCMD_TFT[] = { - 0xEF, 3, 0x03, 0x80, 0x02, - 0xCF, 3, 0x00, 0xC1, 0x30, - 0xED, 4, 0x64, 0x03, 0x12, 0x81, - 0xE8, 3, 0x85, 0x00, 0x78, - 0xCB, 5, 0x39, 0x2C, 0x00, 0x34, 0x02, - 0xF7, 1, 0x20, - 0xEA, 2, 0x00, 0x00, - ILI9341_PWCTR1 , 1, 0x23, // Power control VRH[5:0] - ILI9341_PWCTR2 , 1, 0x10, // Power control SAP[2:0];BT[3:0] - ILI9341_VMCTR1 , 2, 0x3e, 0x28, // VCM control - ILI9341_VMCTR2 , 1, 0x86, // VCM control2 - ILI9341_MADCTL , 1, 0x48, // Memory Access Control - ILI9341_VSCRSADD, 1, 0x00, // Vertical scroll zero - ILI9341_PIXFMT , 1, 0x55, - ILI9341_FRMCTR1 , 2, 0x00, 0x18, - ILI9341_DFUNCTR , 3, 0x08, 0x82, 0x27, // Display Function Control - 0xF2, 1, 0x00, // 3Gamma Function Disable - ILI9341_GAMMASET , 1, 0x01, // Gamma curve selected - ILI9341_GMCTRP1 , 15, 0x0F, 0x31, 0x2B, 0x0C, 0x0E, 0x08, // Set Gamma - 0x4E, 0xF1, 0x37, 0x07, 0x10, 0x03, - 0x0E, 0x09, 0x00, - ILI9341_GMCTRN1 , 15, 0x00, 0x0E, 0x14, 0x03, 0x11, 0x07, // Set Gamma - 0x31, 0xC1, 0x48, 0x08, 0x0F, 0x0C, - 0x31, 0x36, 0x0F, - ILI9341_SLPOUT , 0x80, // Exit Sleep - ILI9341_DISPON , 0x80, // Display on - 0x00 // End of list -}; - -// clang-format on -} // namespace ili9341 -} // namespace esphome diff --git a/esphome/components/ili9xxx/__init__.py b/esphome/components/ili9xxx/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/esphome/components/ili9xxx/display.py b/esphome/components/ili9xxx/display.py new file mode 100644 index 0000000000..437fc93b89 --- /dev/null +++ b/esphome/components/ili9xxx/display.py @@ -0,0 +1,159 @@ +import esphome.codegen as cg +import esphome.config_validation as cv +from esphome import core, pins +from esphome.components import display, spi +from esphome.core import CORE, HexInt +from esphome.const import ( + CONF_COLOR_PALETTE, + CONF_DC_PIN, + CONF_ID, + CONF_LAMBDA, + CONF_MODEL, + CONF_RAW_DATA_ID, + CONF_PAGES, + CONF_RESET_PIN, + CONF_DIMENSIONS, +) + +DEPENDENCIES = ["spi"] +AUTO_LOAD = ["psram"] + +CODEOWNERS = ["@nielsnl68"] + +ili9XXX_ns = cg.esphome_ns.namespace("ili9xxx") +ili9XXXSPI = ili9XXX_ns.class_( + "ILI9XXXDisplay", cg.PollingComponent, spi.SPIDevice, display.DisplayBuffer +) + +ILI9XXXColorMode = ili9XXX_ns.enum("ILI9XXXColorMode") + +MODELS = { + "M5STACK": ili9XXX_ns.class_("ILI9XXXM5Stack", ili9XXXSPI), + "M5CORE": ili9XXX_ns.class_("ILI9XXXM5CORE", ili9XXXSPI), + "TFT_2.4": ili9XXX_ns.class_("ILI9XXXILI9341", ili9XXXSPI), + "TFT_2.4R": ili9XXX_ns.class_("ILI9XXXILI9342", ili9XXXSPI), + "ILI9341": ili9XXX_ns.class_("ILI9XXXILI9341", ili9XXXSPI), + "ILI9342": ili9XXX_ns.class_("ILI9XXXILI9342", ili9XXXSPI), + "ILI9481": ili9XXX_ns.class_("ILI9XXXILI9481", ili9XXXSPI), + "ILI9486": ili9XXX_ns.class_("ILI9XXXILI9486", ili9XXXSPI), + "ILI9488": ili9XXX_ns.class_("ILI9XXXILI9488", ili9XXXSPI), + "ST7796": ili9XXX_ns.class_("ILI9XXXST7796", ili9XXXSPI), +} + +COLOR_PALETTE = cv.one_of("NONE", "GRAYSCALE", "IMAGE_ADAPTIVE") + +CONF_LED_PIN = "led_pin" +CONF_COLOR_PALETTE_IMAGES = "color_palette_images" + + +def _validate(config): + if config.get(CONF_COLOR_PALETTE) == "IMAGE_ADAPTIVE" and not config.get( + CONF_COLOR_PALETTE_IMAGES + ): + raise cv.Invalid( + "Color palette in IMAGE_ADAPTIVE mode requires at least one 'color_palette_images' entry to generate palette" + ) + if ( + config.get(CONF_COLOR_PALETTE_IMAGES) + and config.get(CONF_COLOR_PALETTE) != "IMAGE_ADAPTIVE" + ): + raise cv.Invalid( + "Providing color palette images requires palette mode to be 'IMAGE_ADAPTIVE'" + ) + return config + + +CONFIG_SCHEMA = cv.All( + display.FULL_DISPLAY_SCHEMA.extend( + { + cv.GenerateID(): cv.declare_id(ili9XXXSPI), + cv.Required(CONF_MODEL): cv.enum(MODELS, upper=True, space="_"), + cv.Optional(CONF_DIMENSIONS): cv.dimensions, + cv.Required(CONF_DC_PIN): pins.gpio_output_pin_schema, + cv.Optional(CONF_RESET_PIN): pins.gpio_output_pin_schema, + cv.Optional(CONF_LED_PIN): cv.invalid( + "This property is removed. To use the backlight use proper light component." + ), + cv.Optional(CONF_COLOR_PALETTE, default="NONE"): COLOR_PALETTE, + cv.GenerateID(CONF_RAW_DATA_ID): cv.declare_id(cg.uint8), + cv.Optional(CONF_COLOR_PALETTE_IMAGES, default=[]): cv.ensure_list( + cv.file_ + ), + } + ) + .extend(cv.polling_component_schema("1s")) + .extend(spi.spi_device_schema(False)), + cv.has_at_most_one_key(CONF_PAGES, CONF_LAMBDA), + _validate, +) + + +async def to_code(config): + rhs = MODELS[config[CONF_MODEL]].new() + var = cg.Pvariable(config[CONF_ID], rhs) + + await cg.register_component(var, config) + await display.register_display(var, config) + await spi.register_spi_device(var, config) + dc = await cg.gpio_pin_expression(config[CONF_DC_PIN]) + cg.add(var.set_dc_pin(dc)) + + if CONF_LAMBDA in config: + lambda_ = await cg.process_lambda( + config[CONF_LAMBDA], [(display.DisplayBufferRef, "it")], return_type=cg.void + ) + cg.add(var.set_writer(lambda_)) + + if CONF_RESET_PIN in config: + reset = await cg.gpio_pin_expression(config[CONF_RESET_PIN]) + cg.add(var.set_reset_pin(reset)) + + if CONF_DIMENSIONS in config: + cg.add( + var.set_dimentions(config[CONF_DIMENSIONS][0], config[CONF_DIMENSIONS][1]) + ) + + rhs = None + if config[CONF_COLOR_PALETTE] == "GRAYSCALE": + cg.add(var.set_buffer_color_mode(ILI9XXXColorMode.BITS_8_INDEXED)) + rhs = [] + for x in range(256): + rhs.extend([HexInt(x), HexInt(x), HexInt(x)]) + prog_arr = cg.progmem_array(config[CONF_RAW_DATA_ID], rhs) + cg.add(var.set_palette(prog_arr)) + elif config[CONF_COLOR_PALETTE] == "IMAGE_ADAPTIVE": + cg.add(var.set_buffer_color_mode(ILI9XXXColorMode.BITS_8_INDEXED)) + from PIL import Image + + def load_image(filename): + path = CORE.relative_config_path(filename) + try: + return Image.open(path) + except Exception as e: + raise core.EsphomeError(f"Could not load image file {path}: {e}") + + # make a wide horizontal combined image. + images = [load_image(x) for x in config[CONF_COLOR_PALETTE_IMAGES]] + total_width = sum(i.width for i in images) + max_height = max(i.height for i in images) + + ref_image = Image.new("RGB", (total_width, max_height)) + x = 0 + for i in images: + ref_image.paste(i, (x, 0)) + x = x + i.width + + # reduce the colors on combined image to 256. + converted = ref_image.convert("P", palette=Image.ADAPTIVE, colors=256) + # if you want to verify how the images look use + # ref_image.save("ref_in.png") + # converted.save("ref_out.png") + palette = converted.getpalette() + assert len(palette) == 256 * 3 + rhs = palette + else: + cg.add(var.set_buffer_color_mode(ILI9XXXColorMode.BITS_16)) + + if rhs is not None: + prog_arr = cg.progmem_array(config[CONF_RAW_DATA_ID], rhs) + cg.add(var.set_palette(prog_arr)) diff --git a/esphome/components/ili9xxx/ili9xxx_defines.h b/esphome/components/ili9xxx/ili9xxx_defines.h new file mode 100644 index 0000000000..29483ee15e --- /dev/null +++ b/esphome/components/ili9xxx/ili9xxx_defines.h @@ -0,0 +1,96 @@ +#pragma once + +namespace esphome { +namespace ili9xxx { + +// Color definitions +// clang-format off +static const uint8_t MADCTL_MY = 0x80; ///< Bit 7 Bottom to top +static const uint8_t MADCTL_MX = 0x40; ///< Bit 6 Right to left +static const uint8_t MADCTL_MV = 0x20; ///< Bit 5 Reverse Mode +static const uint8_t MADCTL_ML = 0x10; ///< Bit 4 LCD refresh Bottom to top +static const uint8_t MADCTL_RGB = 0x00; ///< Bit 3 Red-Green-Blue pixel order +static const uint8_t MADCTL_BGR = 0x08; ///< Bit 3 Blue-Green-Red pixel order +static const uint8_t MADCTL_MH = 0x04; ///< Bit 2 LCD refresh right to left +// clang-format on + +// All ILI9XXX specific commands some are used by init() +static const uint8_t ILI9XXX_NOP = 0x00; +static const uint8_t ILI9XXX_SWRESET = 0x01; +static const uint8_t ILI9XXX_RDDID = 0x04; +static const uint8_t ILI9XXX_RDDST = 0x09; + +static const uint8_t ILI9XXX_SLPIN = 0x10; +static const uint8_t ILI9XXX_SLPOUT = 0x11; +static const uint8_t ILI9XXX_PTLON = 0x12; +static const uint8_t ILI9XXX_NORON = 0x13; + +static const uint8_t ILI9XXX_RDMODE = 0x0A; +static const uint8_t ILI9XXX_RDMADCTL = 0x0B; +static const uint8_t ILI9XXX_RDPIXFMT = 0x0C; +static const uint8_t ILI9XXX_RDIMGFMT = 0x0D; +static const uint8_t ILI9XXX_RDSELFDIAG = 0x0F; + +static const uint8_t ILI9XXX_INVOFF = 0x20; +static const uint8_t ILI9XXX_INVON = 0x21; +static const uint8_t ILI9XXX_GAMMASET = 0x26; +static const uint8_t ILI9XXX_DISPOFF = 0x28; +static const uint8_t ILI9XXX_DISPON = 0x29; + +static const uint8_t ILI9XXX_CASET = 0x2A; +static const uint8_t ILI9XXX_PASET = 0x2B; +static const uint8_t ILI9XXX_RAMWR = 0x2C; +static const uint8_t ILI9XXX_RAMRD = 0x2E; + +static const uint8_t ILI9XXX_PTLAR = 0x30; +static const uint8_t ILI9XXX_VSCRDEF = 0x33; +static const uint8_t ILI9XXX_MADCTL = 0x36; +static const uint8_t ILI9XXX_VSCRSADD = 0x37; +static const uint8_t ILI9XXX_IDMOFF = 0x38; +static const uint8_t ILI9XXX_IDMON = 0x39; +static const uint8_t ILI9XXX_PIXFMT = 0x3A; +static const uint8_t ILI9XXX_COLMOD = 0x3A; + +static const uint8_t ILI9XXX_GETSCANLINE = 0x45; + +static const uint8_t ILI9XXX_WRDISBV = 0x51; +static const uint8_t ILI9XXX_RDDISBV = 0x52; +static const uint8_t ILI9XXX_WRCTRLD = 0x53; + +static const uint8_t ILI9XXX_IFMODE = 0xB0; +static const uint8_t ILI9XXX_FRMCTR1 = 0xB1; +static const uint8_t ILI9XXX_FRMCTR2 = 0xB2; +static const uint8_t ILI9XXX_FRMCTR3 = 0xB3; +static const uint8_t ILI9XXX_INVCTR = 0xB4; +static const uint8_t ILI9XXX_DFUNCTR = 0xB6; +static const uint8_t ILI9XXX_ETMOD = 0xB7; + +static const uint8_t ILI9XXX_PWCTR1 = 0xC0; +static const uint8_t ILI9XXX_PWCTR2 = 0xC1; +static const uint8_t ILI9XXX_PWCTR3 = 0xC2; +static const uint8_t ILI9XXX_PWCTR4 = 0xC3; +static const uint8_t ILI9XXX_PWCTR5 = 0xC4; +static const uint8_t ILI9XXX_VMCTR1 = 0xC5; +static const uint8_t ILI9XXX_IFCTR = 0xC6; +static const uint8_t ILI9XXX_VMCTR2 = 0xC7; +static const uint8_t ILI9XXX_GMCTR = 0xC8; +static const uint8_t ILI9XXX_SETEXTC = 0xC8; + +static const uint8_t ILI9XXX_PWSET = 0xD0; +static const uint8_t ILI9XXX_VMCTR = 0xD1; +static const uint8_t ILI9XXX_PWSETN = 0xD2; +static const uint8_t ILI9XXX_RDID4 = 0xD3; +static const uint8_t ILI9XXX_RDINDEX = 0xD9; +static const uint8_t ILI9XXX_RDID1 = 0xDA; +static const uint8_t ILI9XXX_RDID2 = 0xDB; +static const uint8_t ILI9XXX_RDID3 = 0xDC; +static const uint8_t ILI9XXX_RDIDX = 0xDD; // TBC + +static const uint8_t ILI9XXX_GMCTRP1 = 0xE0; +static const uint8_t ILI9XXX_GMCTRN1 = 0xE1; + +static const uint8_t ILI9XXX_CSCON = 0xF0; +static const uint8_t ILI9XXX_ADJCTL3 = 0xF7; + +} // namespace ili9xxx +} // namespace esphome diff --git a/esphome/components/ili9xxx/ili9xxx_display.cpp b/esphome/components/ili9xxx/ili9xxx_display.cpp new file mode 100644 index 0000000000..0091a2aabc --- /dev/null +++ b/esphome/components/ili9xxx/ili9xxx_display.cpp @@ -0,0 +1,416 @@ +#include "ili9xxx_display.h" +#include "esphome/core/log.h" +#include "esphome/core/application.h" +#include "esphome/core/helpers.h" +#include "esphome/core/hal.h" + +namespace esphome { +namespace ili9xxx { + +static const char *const TAG = "ili9xxx"; + +void ILI9XXXDisplay::setup() { + this->setup_pins_(); + this->initialize(); + + this->x_low_ = this->width_; + this->y_low_ = this->height_; + this->x_high_ = 0; + this->y_high_ = 0; + if (this->buffer_color_mode_ == BITS_16) { + this->init_internal_(this->get_buffer_length_() * 2); + if (this->buffer_ != nullptr) { + return; + } + this->buffer_color_mode_ = BITS_8; + } + this->init_internal_(this->get_buffer_length_()); + if (this->buffer_ == nullptr) { + this->mark_failed(); + } +} + +void ILI9XXXDisplay::setup_pins_() { + this->dc_pin_->setup(); // OUTPUT + this->dc_pin_->digital_write(false); + if (this->reset_pin_ != nullptr) { + this->reset_pin_->setup(); // OUTPUT + this->reset_pin_->digital_write(true); + } + + this->spi_setup(); + + this->reset_(); +} + +void ILI9XXXDisplay::dump_config() { + LOG_DISPLAY("", "ili9xxx", this); + switch (this->buffer_color_mode_) { + case BITS_8_INDEXED: + ESP_LOGCONFIG(TAG, " Color mode: 8bit Indexed"); + break; + case BITS_16: + ESP_LOGCONFIG(TAG, " Color mode: 16bit"); + break; + default: + ESP_LOGCONFIG(TAG, " Color mode: 8bit 332 mode"); + break; + } + if (this->is_18bitdisplay_) { + ESP_LOGCONFIG(TAG, " 18-Bit Mode: YES"); + } + + LOG_PIN(" Reset Pin: ", this->reset_pin_); + LOG_PIN(" DC Pin: ", this->dc_pin_); + LOG_PIN(" Busy Pin: ", this->busy_pin_); + + if (this->is_failed()) { + ESP_LOGCONFIG(TAG, " => Failed to init Memory: YES!"); + } + LOG_UPDATE_INTERVAL(this); +} + +float ILI9XXXDisplay::get_setup_priority() const { return setup_priority::HARDWARE; } + +void ILI9XXXDisplay::fill(Color color) { + uint16_t new_color = 0; + this->x_low_ = 0; + this->y_low_ = 0; + this->x_high_ = this->get_width_internal() - 1; + this->y_high_ = this->get_height_internal() - 1; + switch (this->buffer_color_mode_) { + case BITS_8_INDEXED: + new_color = display::ColorUtil::color_to_index8_palette888(color, this->palette_); + break; + case BITS_16: + new_color = display::ColorUtil::color_to_565(color); + for (uint32_t i = 0; i < this->get_buffer_length_() * 2; i = i + 2) { + this->buffer_[i] = (uint8_t)(new_color >> 8); + this->buffer_[i + 1] = (uint8_t) new_color; + } + return; + break; + default: + new_color = display::ColorUtil::color_to_332(color, display::ColorOrder::COLOR_ORDER_RGB); + break; + } + memset(this->buffer_, (uint8_t) new_color, this->get_buffer_length_()); +} + +void HOT ILI9XXXDisplay::draw_absolute_pixel_internal(int x, int y, Color color) { + if (x >= this->get_width_internal() || x < 0 || y >= this->get_height_internal() || y < 0) { + return; + } + uint32_t pos = (y * width_) + x; + uint16_t new_color; + bool updated = false; + switch (this->buffer_color_mode_) { + case BITS_8_INDEXED: + new_color = display::ColorUtil::color_to_index8_palette888(color, this->palette_); + break; + case BITS_16: + pos = pos * 2; + new_color = display::ColorUtil::color_to_565(color, display::ColorOrder::COLOR_ORDER_RGB); + if (this->buffer_[pos] != (uint8_t)(new_color >> 8)) { + this->buffer_[pos] = (uint8_t)(new_color >> 8); + updated = true; + } + pos = pos + 1; + new_color = new_color & 0xFF; + break; + default: + new_color = display::ColorUtil::color_to_332(color, display::ColorOrder::COLOR_ORDER_RGB); + break; + } + + if (this->buffer_[pos] != new_color) { + this->buffer_[pos] = new_color; + updated = true; + } + if (updated) { + // low and high watermark may speed up drawing from buffer + this->x_low_ = (x < this->x_low_) ? x : this->x_low_; + this->y_low_ = (y < this->y_low_) ? y : this->y_low_; + this->x_high_ = (x > this->x_high_) ? x : this->x_high_; + this->y_high_ = (y > this->y_high_) ? y : this->y_high_; + // ESP_LOGVV(TAG, "=>>> pixel (x:%d, y:%d) (xl:%d, xh:%d, yl:%d, yh:%d", x, y, this->x_low_, this->x_high_, + // this->y_low_, this->y_high_); + } +} + +void ILI9XXXDisplay::update() { + if (this->prossing_update_) { + this->need_update_ = true; + return; + } + do { + this->prossing_update_ = true; + this->need_update_ = false; + if (!this->need_update_) { + this->do_update_(); + } + } while (this->need_update_); + this->prossing_update_ = false; + this->display_(); +} + +void ILI9XXXDisplay::display_() { + // we will only update the changed window to the display + uint16_t w = this->x_high_ - this->x_low_ + 1; // NOLINT + uint16_t h = this->y_high_ - this->y_low_ + 1; // NOLINT + uint32_t start_pos = ((this->y_low_ * this->width_) + x_low_); + + // check if something was displayed + if ((this->x_high_ < this->x_low_) || (this->y_high_ < this->y_low_)) { + ESP_LOGV(TAG, "Nothing to display"); + return; + } + + set_addr_window_(this->x_low_, this->y_low_, w, h); + + ESP_LOGV(TAG, + "Start display(xlow:%d, ylow:%d, xhigh:%d, yhigh:%d, width:%d, " + "heigth:%d, start_pos:%d)", + this->x_low_, this->y_low_, this->x_high_, this->y_high_, w, h, start_pos); + + this->start_data_(); + for (uint16_t row = 0; row < h; row++) { + uint32_t pos = start_pos + (row * width_); + uint32_t rem = w; + + while (rem > 0) { + uint32_t sz = std::min(rem, ILI9XXX_TRANSFER_BUFFER_SIZE); + // ESP_LOGVV(TAG, "Send to display(pos:%d, rem:%d, zs:%d)", pos, rem, sz); + buffer_to_transfer_(pos, sz); + if (this->is_18bitdisplay_) { + for (uint32_t i = 0; i < sz; ++i) { + uint16_t color_val = transfer_buffer_[i]; + + uint8_t red = color_val & 0x1F; + uint8_t green = (color_val & 0x7E0) >> 5; + uint8_t blue = (color_val & 0xF800) >> 11; + + uint8_t pass_buff[3]; + + pass_buff[2] = (uint8_t)((red / 32.0) * 64) << 2; + pass_buff[1] = (uint8_t) green << 2; + pass_buff[0] = (uint8_t)((blue / 32.0) * 64) << 2; + + this->write_array(pass_buff, sizeof(pass_buff)); + } + } else { + this->write_array16(transfer_buffer_, sz); + } + pos += sz; + rem -= sz; + } + App.feed_wdt(); + } + this->end_data_(); + + // invalidate watermarks + this->x_low_ = this->width_; + this->y_low_ = this->height_; + this->x_high_ = 0; + this->y_high_ = 0; +} + +uint32_t ILI9XXXDisplay::buffer_to_transfer_(uint32_t pos, uint32_t sz) { + for (uint32_t i = 0; i < sz; ++i) { + switch (this->buffer_color_mode_) { + case BITS_8_INDEXED: + transfer_buffer_[i] = display::ColorUtil::color_to_565( + display::ColorUtil::index8_to_color_palette888(this->buffer_[pos + i], this->palette_)); + break; + case BITS_16: + transfer_buffer_[i] = ((uint16_t) this->buffer_[(pos + i) * 2] << 8) | this->buffer_[((pos + i) * 2) + 1]; + continue; + break; + default: + transfer_buffer_[i] = + display::ColorUtil::color_to_565(display::ColorUtil::rgb332_to_color(this->buffer_[pos + i])); + break; + } + } + return sz; +} + +// should return the total size: return this->get_width_internal() * this->get_height_internal() * 2 // 16bit color +// values per bit is huge +uint32_t ILI9XXXDisplay::get_buffer_length_() { return this->get_width_internal() * this->get_height_internal(); } + +void ILI9XXXDisplay::command(uint8_t value) { + this->start_command_(); + this->write_byte(value); + this->end_command_(); +} + +void ILI9XXXDisplay::data(uint8_t value) { + this->start_data_(); + this->write_byte(value); + this->end_data_(); +} + +void ILI9XXXDisplay::send_command(uint8_t command_byte, const uint8_t *data_bytes, uint8_t num_data_bytes) { + this->command(command_byte); // Send the command byte + this->start_data_(); + this->write_array(data_bytes, num_data_bytes); + this->end_data_(); +} + +uint8_t ILI9XXXDisplay::read_command(uint8_t command_byte, uint8_t index) { + uint8_t data = 0x10 + index; + this->send_command(0xD9, &data, 1); // Set Index Register + uint8_t result; + this->start_command_(); + this->write_byte(command_byte); + this->start_data_(); + do { + result = this->read_byte(); + } while (index--); + this->end_data_(); + return result; +} + +void ILI9XXXDisplay::start_command_() { + this->dc_pin_->digital_write(false); + this->enable(); +} +void ILI9XXXDisplay::start_data_() { + this->dc_pin_->digital_write(true); + this->enable(); +} + +void ILI9XXXDisplay::end_command_() { this->disable(); } +void ILI9XXXDisplay::end_data_() { this->disable(); } + +void ILI9XXXDisplay::reset_() { + if (this->reset_pin_ != nullptr) { + this->reset_pin_->digital_write(false); + delay(10); + this->reset_pin_->digital_write(true); + delay(10); + } +} + +void ILI9XXXDisplay::init_lcd_(const uint8_t *init_cmd) { + uint8_t cmd, x, num_args; + const uint8_t *addr = init_cmd; + while ((cmd = progmem_read_byte(addr++)) > 0) { + x = progmem_read_byte(addr++); + num_args = x & 0x7F; + send_command(cmd, addr, num_args); + addr += num_args; + if (x & 0x80) + delay(150); // NOLINT + } +} + +void ILI9XXXDisplay::set_addr_window_(uint16_t x1, uint16_t y1, uint16_t w, uint16_t h) { + uint16_t x2 = (x1 + w - 1), y2 = (y1 + h - 1); + this->command(ILI9XXX_CASET); // Column address set + this->start_data_(); + this->write_byte(x1 >> 8); + this->write_byte(x1); + this->write_byte(x2 >> 8); + this->write_byte(x2); + this->end_data_(); + this->command(ILI9XXX_PASET); // Row address set + this->start_data_(); + this->write_byte(y1 >> 8); + this->write_byte(y1); + this->write_byte(y2 >> 8); + this->write_byte(y2); + this->end_data_(); + this->command(ILI9XXX_RAMWR); // Write to RAM +} + +void ILI9XXXDisplay::invert_display_(bool invert) { this->command(invert ? ILI9XXX_INVON : ILI9XXX_INVOFF); } + +int ILI9XXXDisplay::get_width_internal() { return this->width_; } +int ILI9XXXDisplay::get_height_internal() { return this->height_; } + +// M5Stack display +void ILI9XXXM5Stack::initialize() { + this->init_lcd_(INITCMD_M5STACK); + if (this->width_ == 0) + this->width_ = 320; + if (this->height_ == 0) + this->height_ = 240; + this->invert_display_(true); +} + +// M5CORE display // Based on the configuration settings of M5stact's M5GFX code. +void ILI9XXXM5CORE::initialize() { + this->init_lcd_(INITCMD_M5CORE); + if (this->width_ == 0) + this->width_ = 320; + if (this->height_ == 0) + this->height_ = 240; + this->invert_display_(true); +} + +// 24_TFT display +void ILI9XXXILI9341::initialize() { + this->init_lcd_(INITCMD_ILI9341); + if (this->width_ == 0) + this->width_ = 240; + if (this->height_ == 0) + this->height_ = 320; +} +// 24_TFT rotated display +void ILI9XXXILI9342::initialize() { + this->init_lcd_(INITCMD_ILI9341); + if (this->width_ == 0) { + this->width_ = 320; + } + if (this->height_ == 0) { + this->height_ = 240; + } +} + +// 35_TFT display +void ILI9XXXILI9481::initialize() { + this->init_lcd_(INITCMD_ILI9481); + if (this->width_ == 0) { + this->width_ = 480; + } + if (this->height_ == 0) { + this->height_ = 320; + } +} + +// 35_TFT display +void ILI9XXXILI9486::initialize() { + this->init_lcd_(INITCMD_ILI9486); + if (this->width_ == 0) { + this->width_ = 480; + } + if (this->height_ == 0) { + this->height_ = 320; + } +} +// 40_TFT display +void ILI9XXXILI9488::initialize() { + this->init_lcd_(INITCMD_ILI9488); + if (this->width_ == 0) { + this->width_ = 480; + } + if (this->height_ == 0) { + this->height_ = 320; + } + this->is_18bitdisplay_ = true; +} +// 40_TFT display +void ILI9XXXST7796::initialize() { + this->init_lcd_(INITCMD_ST7796); + if (this->width_ == 0) { + this->width_ = 320; + } + if (this->height_ == 0) { + this->height_ = 480; + } +} + +} // namespace ili9xxx +} // namespace esphome diff --git a/esphome/components/ili9341/ili9341_display.h b/esphome/components/ili9xxx/ili9xxx_display.h similarity index 51% rename from esphome/components/ili9341/ili9341_display.h rename to esphome/components/ili9xxx/ili9xxx_display.h index 547c608ae8..8a8cd4bb44 100644 --- a/esphome/components/ili9341/ili9341_display.h +++ b/esphome/components/ili9xxx/ili9xxx_display.h @@ -1,27 +1,21 @@ #pragma once - -#include "esphome/core/component.h" #include "esphome/components/spi/spi.h" #include "esphome/components/display/display_buffer.h" -#include "ili9341_defines.h" -#include "ili9341_init.h" -#include "esphome/core/log.h" +#include "ili9xxx_defines.h" +#include "ili9xxx_init.h" namespace esphome { -namespace ili9341 { +namespace ili9xxx { -enum ILI9341Model { - M5STACK = 0, - TFT_24, - TFT_24R, +const uint32_t ILI9XXX_TRANSFER_BUFFER_SIZE = 64; + +enum ILI9XXXColorMode { + BITS_8 = 0x08, + BITS_8_INDEXED = 0x09, + BITS_16 = 0x10, }; -enum ILI9341ColorMode { - BITS_8, - BITS_8_INDEXED, -}; - -class ILI9341Display : public PollingComponent, +class ILI9XXXDisplay : public PollingComponent, public display::DisplayBuffer, public spi::SPIDevice { @@ -29,59 +23,46 @@ class ILI9341Display : public PollingComponent, void set_dc_pin(GPIOPin *dc_pin) { dc_pin_ = dc_pin; } float get_setup_priority() const override; void set_reset_pin(GPIOPin *reset) { this->reset_pin_ = reset; } - void set_led_pin(GPIOPin *led) { this->led_pin_ = led; } - void set_model(ILI9341Model model) { this->model_ = model; } void set_palette(const uint8_t *palette) { this->palette_ = palette; } - void set_buffer_color_mode(ILI9341ColorMode color_mode) { this->buffer_color_mode_ = color_mode; } - + void set_buffer_color_mode(ILI9XXXColorMode color_mode) { this->buffer_color_mode_ = color_mode; } + void set_dimentions(int16_t width, int16_t height) { + this->height_ = height; + this->width_ = width; + } void command(uint8_t value); void data(uint8_t value); void send_command(uint8_t command_byte, const uint8_t *data_bytes, uint8_t num_data_bytes); uint8_t read_command(uint8_t command_byte, uint8_t index); - virtual void initialize() = 0; void update() override; void fill(Color color) override; void dump_config() override; - void setup() override { - this->setup_pins_(); - this->initialize(); - - this->x_low_ = this->width_; - this->y_low_ = this->height_; - this->x_high_ = 0; - this->y_high_ = 0; - - this->init_internal_(this->get_buffer_length_()); - this->fill_internal_(0x00); - } + void setup() override; display::DisplayType get_display_type() override { return display::DisplayType::DISPLAY_TYPE_COLOR; } protected: void draw_absolute_pixel_internal(int x, int y, Color color) override; void setup_pins_(); + virtual void initialize() = 0; + void display_(); void init_lcd_(const uint8_t *init_cmd); void set_addr_window_(uint16_t x, uint16_t y, uint16_t w, uint16_t h); void invert_display_(bool invert); void reset_(); - void fill_internal_(uint8_t color); - void display_(); - void rotate_my_(uint8_t m); - ILI9341Model model_; - int16_t width_{320}; ///< Display width as modified by current rotation - int16_t height_{240}; ///< Display height as modified by current rotation + int16_t width_{0}; ///< Display width as modified by current rotation + int16_t height_{0}; ///< Display height as modified by current rotation uint16_t x_low_{0}; uint16_t y_low_{0}; uint16_t x_high_{0}; uint16_t y_high_{0}; const uint8_t *palette_; - ILI9341ColorMode buffer_color_mode_{BITS_8}; + ILI9XXXColorMode buffer_color_mode_{BITS_16}; uint32_t get_buffer_length_(); int get_width_internal() override; @@ -92,33 +73,66 @@ class ILI9341Display : public PollingComponent, void start_data_(); void end_data_(); - uint8_t transfer_buffer_[64]; + uint16_t transfer_buffer_[ILI9XXX_TRANSFER_BUFFER_SIZE]; uint32_t buffer_to_transfer_(uint32_t pos, uint32_t sz); GPIOPin *reset_pin_{nullptr}; - GPIOPin *led_pin_{nullptr}; - GPIOPin *dc_pin_; + GPIOPin *dc_pin_{nullptr}; GPIOPin *busy_pin_{nullptr}; + + bool prossing_update_ = false; + bool need_update_ = false; + bool is_18bitdisplay_ = false; }; //----------- M5Stack display -------------- -class ILI9341M5Stack : public ILI9341Display { - public: +class ILI9XXXM5Stack : public ILI9XXXDisplay { + protected: void initialize() override; }; -//----------- ILI9341_24_TFT display -------------- -class ILI9341TFT24 : public ILI9341Display { - public: +//----------- M5Stack display -------------- +class ILI9XXXM5CORE : public ILI9XXXDisplay { + protected: void initialize() override; }; -//----------- ILI9341_24_TFT rotated display -------------- -class ILI9341TFT24R : public ILI9341Display { - public: +//----------- ILI9XXX_24_TFT display -------------- +class ILI9XXXILI9341 : public ILI9XXXDisplay { + protected: void initialize() override; }; -} // namespace ili9341 +//----------- ILI9XXX_24_TFT rotated display -------------- +class ILI9XXXILI9342 : public ILI9XXXDisplay { + protected: + void initialize() override; +}; + +//----------- ILI9XXX_??_TFT rotated display -------------- +class ILI9XXXILI9481 : public ILI9XXXDisplay { + protected: + void initialize() override; +}; + +//----------- ILI9XXX_35_TFT rotated display -------------- +class ILI9XXXILI9486 : public ILI9XXXDisplay { + protected: + void initialize() override; +}; + +//----------- ILI9XXX_35_TFT rotated display -------------- +class ILI9XXXILI9488 : public ILI9XXXDisplay { + protected: + void initialize() override; +}; + +//----------- ILI9XXX_35_TFT rotated display -------------- +class ILI9XXXST7796 : public ILI9XXXDisplay { + protected: + void initialize() override; +}; + +} // namespace ili9xxx } // namespace esphome diff --git a/esphome/components/ili9xxx/ili9xxx_init.h b/esphome/components/ili9xxx/ili9xxx_init.h new file mode 100644 index 0000000000..e8d3614a1d --- /dev/null +++ b/esphome/components/ili9xxx/ili9xxx_init.h @@ -0,0 +1,174 @@ +#pragma once +#include "esphome/core/helpers.h" + +namespace esphome { +namespace ili9xxx { + +// clang-format off +static const uint8_t PROGMEM INITCMD_M5STACK[] = { + 0xEF, 3, 0x03, 0x80, 0x02, + 0xCF, 3, 0x00, 0xC1, 0x30, + 0xED, 4, 0x64, 0x03, 0x12, 0x81, + 0xE8, 3, 0x85, 0x00, 0x78, + 0xCB, 5, 0x39, 0x2C, 0x00, 0x34, 0x02, + 0xF7, 1, 0x20, + 0xEA, 2, 0x00, 0x00, + ILI9XXX_PWCTR1 , 1, 0x23, // Power control VRH[5:0] + ILI9XXX_PWCTR2 , 1, 0x10, // Power control SAP[2:0];BT[3:0] + ILI9XXX_VMCTR1 , 2, 0x3e, 0x28, // VCM control + ILI9XXX_VMCTR2 , 1, 0x86, // VCM control2 + ILI9XXX_MADCTL , 1, MADCTL_BGR, // Memory Access Control + ILI9XXX_VSCRSADD, 1, 0x00, // Vertical scroll zero + ILI9XXX_PIXFMT , 1, 0x55, + ILI9XXX_FRMCTR1 , 2, 0x00, 0x13, + ILI9XXX_DFUNCTR , 3, 0x08, 0x82, 0x27, // Display Function Control + 0xF2, 1, 0x00, // 3Gamma Function Disable + ILI9XXX_GAMMASET , 1, 0x01, // Gamma curve selected + ILI9XXX_GMCTRP1 , 15, 0x0F, 0x31, 0x2B, 0x0C, 0x0E, 0x08, // Set Gamma + 0x4E, 0xF1, 0x37, 0x07, 0x10, 0x03, + 0x0E, 0x09, 0x00, + ILI9XXX_GMCTRN1 , 15, 0x00, 0x0E, 0x14, 0x03, 0x11, 0x07, // Set Gamma + 0x31, 0xC1, 0x48, 0x08, 0x0F, 0x0C, + 0x31, 0x36, 0x0F, + ILI9XXX_SLPOUT , 0x80, // Exit Sleep + ILI9XXX_DISPON , 0x80, // Display on + 0x00 // End of list +}; + +static const uint8_t PROGMEM INITCMD_M5CORE[] = { + ILI9XXX_SETEXTC, 3, 0xFF,0x93,0x42, // Turn on the external command + ILI9XXX_PWCTR1 , 2, 0x12, 0x12, + ILI9XXX_PWCTR2 , 1, 0x03, + ILI9XXX_VMCTR1 , 1, 0xF2, + ILI9XXX_IFMODE , 1, 0xE0, + 0xF6 , 3, 0x01, 0x00, 0x00, + ILI9XXX_GMCTRP1,15, 0x00,0x0C,0x11,0x04,0x11,0x08,0x37,0x89,0x4C,0x06,0x0C,0x0A,0x2E,0x34,0x0F, + ILI9XXX_GMCTRN1,15, 0x00,0x0B,0x11,0x05,0x13,0x09,0x33,0x67,0x48,0x07,0x0E,0x0B,0x2E,0x33,0x0F, + ILI9XXX_DFUNCTR, 4, 0x08,0x82,0x1D,0x04, + ILI9XXX_IDMOFF , 0, + ILI9XXX_DISPON , 0x80, // Display on + ILI9XXX_SLPOUT , 0x80, // Exit Sleep + + 0x00 // End of list +}; + + + +static const uint8_t PROGMEM INITCMD_ILI9341[] = { + 0xEF, 3, 0x03, 0x80, 0x02, + 0xCF, 3, 0x00, 0xC1, 0x30, + 0xED, 4, 0x64, 0x03, 0x12, 0x81, + 0xE8, 3, 0x85, 0x00, 0x78, + 0xCB, 5, 0x39, 0x2C, 0x00, 0x34, 0x02, + 0xF7, 1, 0x20, + 0xEA, 2, 0x00, 0x00, + ILI9XXX_PWCTR1 , 1, 0x23, // Power control VRH[5:0] + ILI9XXX_PWCTR2 , 1, 0x10, // Power control SAP[2:0];BT[3:0] + ILI9XXX_VMCTR1 , 2, 0x3e, 0x28, // VCM control + ILI9XXX_VMCTR2 , 1, 0x86, // VCM control2 + ILI9XXX_MADCTL , 1, 0x48, // Memory Access Control + ILI9XXX_VSCRSADD, 1, 0x00, // Vertical scroll zero + ILI9XXX_PIXFMT , 1, 0x55, + ILI9XXX_FRMCTR1 , 2, 0x00, 0x18, + ILI9XXX_DFUNCTR , 3, 0x08, 0x82, 0x27, // Display Function Control + 0xF2, 1, 0x00, // 3Gamma Function Disable + ILI9XXX_GAMMASET , 1, 0x01, // Gamma curve selected + ILI9XXX_GMCTRP1 , 15, 0x0F, 0x31, 0x2B, 0x0C, 0x0E, 0x08, // Set Gamma + 0x4E, 0xF1, 0x37, 0x07, 0x10, 0x03, + 0x0E, 0x09, 0x00, + ILI9XXX_GMCTRN1 , 15, 0x00, 0x0E, 0x14, 0x03, 0x11, 0x07, // Set Gamma + 0x31, 0xC1, 0x48, 0x08, 0x0F, 0x0C, + 0x31, 0x36, 0x0F, + ILI9XXX_SLPOUT , 0x80, // Exit Sleep + ILI9XXX_DISPON , 0x80, // Display on + 0x00 // End of list +}; + +static const uint8_t PROGMEM INITCMD_ILI9481[] = { + ILI9XXX_SLPOUT , 0x80, // Exit sleep mode + ILI9XXX_PWSET , 3, 0x07, 0x41, 0x1D, + ILI9XXX_VMCTR , 3, 0x00, 0x1C, 0x1F, + ILI9XXX_PWSETN , 2, 0x01, 0x11, + ILI9XXX_PWCTR1 , 5, 0x10, 0x3B, 0x00, 0x02, 0x11, + ILI9XXX_VMCTR1 , 1, 0x03, + ILI9XXX_IFCTR , 1, 0x83, + ILI9XXX_GMCTR ,12, 0x00, 0x26, 0x21, 0x00, 0x00, 0x1F, 0x65, 0x23, 0x77, 0x00, 0x0F, 0x00, + ILI9XXX_IFMODE , 1, 0x00, // CommandAccessProtect + 0xE4 , 1, 0xA0, + ILI9XXX_CSCON , 1, 0x01, + ILI9XXX_DISPON, 0x80, // Set display on + 0x00 // end +}; + +static const uint8_t PROGMEM INITCMD_ILI9486[] = { + ILI9XXX_SLPOUT, 0x80, + ILI9XXX_PIXFMT, 1, 0x55, + ILI9XXX_PWCTR3, 1, 0x44, + ILI9XXX_VMCTR1, 4, 0x00, 0x00, 0x00, 0x00, + ILI9XXX_GMCTRP1, 15, 0x0f,0x1f,0x1c,0x0c,0x0f,0x08,0x48,0x98,0x37,0x0a,0x13,0x04,0x11,0x0d,0x00, + ILI9XXX_GMCTRN1, 15, 0x0f,0x32,0x2e,0x0b,0x0d,0x05,0x47,0x75,0x37,0x06,0x10,0x03,0x24,0x20,0x00, + ILI9XXX_INVOFF, 0x80, + ILI9XXX_MADCTL, 1, 0x48, + ILI9XXX_DISPON, 0x80, + + // ILI9XXX_MADCTL, 1, MADCTL_BGR | MADCTL_MV, //hardware rotation + 0x00 // End of list +}; + +static const uint8_t PROGMEM INITCMD_ILI9488[] = { + ILI9XXX_GMCTRP1,15, 0x00, 0x03, 0x09, 0x08, 0x16, 0x0A, 0x3F, 0x78, 0x4C, 0x09, 0x0A, 0x08, 0x16, 0x1A, 0x0F, + ILI9XXX_GMCTRN1,15, 0x00, 0x16, 0x19, 0x03, 0x0F, 0x05, 0x32, 0x45, 0x46, 0x04, 0x0E, 0x0D, 0x35, 0x37, 0x0F, + + ILI9XXX_PWCTR1, 2, 0x17, 0x15, // VRH1 VRH2 + ILI9XXX_PWCTR2, 1, 0x41, // VGH, VGL + ILI9XXX_VMCTR1, 3, 0x00, 0x12, 0x80, // nVM VCM_REG VCM_REG_EN + + ILI9XXX_IFMODE, 1, 0x00, + ILI9XXX_FRMCTR1, 1, 0xA0, // Frame rate = 60Hz + ILI9XXX_INVCTR, 1, 0x02, // Display Inversion Control = 2dot + + ILI9XXX_DFUNCTR, 2, 0x02, 0x02, // Nomal scan + + 0xE9, 1, 0x00, // Set Image Functio. Disable 24 bit data + + ILI9XXX_ADJCTL3, 4, 0xA9, 0x51, 0x2C, 0x82, // Adjust Control 3 + + ILI9XXX_MADCTL, 1, 0x28, + //ILI9XXX_PIXFMT, 1, 0x55, // Interface Pixel Format = 16bit + ILI9XXX_PIXFMT, 1, 0x66, //ILI9488 only supports 18-bit pixel format in 4/3 wire SPI mode + + + + // 5 frames + //ILI9XXX_ETMOD, 1, 0xC6, // + + + ILI9XXX_SLPOUT, 0x80, // Exit sleep mode + //ILI9XXX_INVON , 0, + ILI9XXX_DISPON, 0x80, // Set display on + 0x00 // end +}; + +static const uint8_t PROGMEM INITCMD_ST7796[] = { + // This ST7796S initilization routine was copied from https://github.com/prenticedavid/Adafruit_ST7796S_kbv/blob/master/Adafruit_ST7796S_kbv.cpp + ILI9XXX_SWRESET, 0x80, // Soft reset, then delay 150 ms + ILI9XXX_CSCON, 1, 0xC3, // ?? Unlock Manufacturer + ILI9XXX_CSCON, 1, 0x96, + ILI9XXX_VMCTR1, 1, 0x1C, //VCOM Control 1 [1C] + ILI9XXX_MADCTL, 1, 0x48, //Memory Access [00] + ILI9XXX_PIXFMT, 1, 0x55, //565 + ILI9XXX_IFMODE, 1, 0x80, //Interface [00] + ILI9XXX_INVCTR, 1, 0x01, //Inversion Control [01] + ILI9XXX_DFUNCTR, 3, 0x80, 0x02, 0x3B, // Display Function Control [80 02 3B] .kbv SS=1, NL=480 + ILI9XXX_ETMOD, 1, 0xC6, //Entry Mode [06] + + ILI9XXX_CSCON, 1, 0x69, //?? lock manufacturer commands + ILI9XXX_CSCON, 1, 0x3C, // + ILI9XXX_SLPOUT, 0x80, // Exit Sleep, then delay 150 ms + ILI9XXX_DISPON, 0x80, // Main screen turn on, delay 150 ms + 0x00 // End of list +}; + +// clang-format on +} // namespace ili9xxx +} // namespace esphome diff --git a/esphome/components/neopixelbus/_methods.py b/esphome/components/neopixelbus/_methods.py index a290257d6b..4059d040d0 100644 --- a/esphome/components/neopixelbus/_methods.py +++ b/esphome/components/neopixelbus/_methods.py @@ -13,8 +13,8 @@ from esphome.const import ( from esphome.components.esp32 import get_esp32_variant from esphome.components.esp32.const import ( VARIANT_ESP32, - VARIANT_ESP32S2, VARIANT_ESP32C3, + VARIANT_ESP32S2, VARIANT_ESP32S3, ) from esphome.core import CORE @@ -58,9 +58,9 @@ SPI_SPEEDS = [40e6, 20e6, 10e6, 5e6, 2e6, 1e6, 500e3] def _esp32_rmt_default_channel(): return { + VARIANT_ESP32C3: 1, VARIANT_ESP32S2: 1, VARIANT_ESP32S3: 1, - VARIANT_ESP32C3: 1, }.get(get_esp32_variant(), 6) @@ -71,9 +71,9 @@ def _validate_esp32_rmt_channel(value): value = cv.int_(value) variant_channels = { VARIANT_ESP32: [0, 1, 2, 3, 4, 5, 6, 7, CHANNEL_DYNAMIC], + VARIANT_ESP32C3: [0, 1, CHANNEL_DYNAMIC], VARIANT_ESP32S2: [0, 1, 2, 3, CHANNEL_DYNAMIC], VARIANT_ESP32S3: [0, 1, 2, 3, CHANNEL_DYNAMIC], - VARIANT_ESP32C3: [0, 1, CHANNEL_DYNAMIC], } variant = get_esp32_variant() if variant not in variant_channels: diff --git a/esphome/components/neopixelbus/light.py b/esphome/components/neopixelbus/light.py index 072a565eda..9bd9215936 100644 --- a/esphome/components/neopixelbus/light.py +++ b/esphome/components/neopixelbus/light.py @@ -17,6 +17,7 @@ from esphome.const import ( from esphome.components.esp32 import get_esp32_variant from esphome.components.esp32.const import ( VARIANT_ESP32C3, + VARIANT_ESP32S3, ) from esphome.core import CORE from ._methods import ( @@ -96,7 +97,7 @@ def _choose_default_method(config): config[CONF_METHOD] = _validate_method(METHOD_BIT_BANG) if CORE.is_esp32: - if get_esp32_variant() == VARIANT_ESP32C3: + if get_esp32_variant() in (VARIANT_ESP32C3, VARIANT_ESP32S3): config[CONF_METHOD] = _validate_method(METHOD_ESP32_RMT) else: config[CONF_METHOD] = _validate_method(METHOD_ESP32_I2S) diff --git a/esphome/const.py b/esphome/const.py index a78fb6948f..a66dfbc0fd 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -1,6 +1,6 @@ """Constants used by esphome.""" -__version__ = "2023.3.0b1" +__version__ = "2023.3.0b2" ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_" diff --git a/esphome/core/log.h b/esphome/core/log.h index b1b1cf9115..bef5e5c633 100644 --- a/esphome/core/log.h +++ b/esphome/core/log.h @@ -144,19 +144,12 @@ int esp_idf_log_vprintf_(const char *format, va_list args); // NOLINT #endif #define ESP_LOGE(tag, ...) esph_log_e(tag, __VA_ARGS__) -#define LOG_E(tag, ...) ESP_LOGE(tag, __VA__ARGS__) #define ESP_LOGW(tag, ...) esph_log_w(tag, __VA_ARGS__) -#define LOG_W(tag, ...) ESP_LOGW(tag, __VA__ARGS__) #define ESP_LOGI(tag, ...) esph_log_i(tag, __VA_ARGS__) -#define LOG_I(tag, ...) ESP_LOGI(tag, __VA__ARGS__) #define ESP_LOGD(tag, ...) esph_log_d(tag, __VA_ARGS__) -#define LOG_D(tag, ...) ESP_LOGD(tag, __VA__ARGS__) #define ESP_LOGCONFIG(tag, ...) esph_log_config(tag, __VA_ARGS__) -#define LOG_CONFIG(tag, ...) ESP_LOGCONFIG(tag, __VA__ARGS__) #define ESP_LOGV(tag, ...) esph_log_v(tag, __VA_ARGS__) -#define LOG_V(tag, ...) ESP_LOGV(tag, __VA__ARGS__) #define ESP_LOGVV(tag, ...) esph_log_vv(tag, __VA_ARGS__) -#define LOG_VV(tag, ...) ESP_LOGVV(tag, __VA__ARGS__) #define BYTE_TO_BINARY_PATTERN "%c%c%c%c%c%c%c%c" #define BYTE_TO_BINARY(byte) \ diff --git a/platformio.ini b/platformio.ini index 65dccde3b9..3df6446ff1 100644 --- a/platformio.ini +++ b/platformio.ini @@ -34,7 +34,7 @@ build_flags = [common] lib_deps = esphome/noise-c@0.1.4 ; api - makuna/NeoPixelBus@2.6.9 ; neopixelbus + makuna/NeoPixelBus@2.7.3 ; neopixelbus esphome/Improv@1.2.3 ; improv_serial / esp32_improv bblanchon/ArduinoJson@6.18.5 ; json wjtje/qr-code-generator-library@1.7.0 ; qr_code diff --git a/tests/test1.yaml b/tests/test1.yaml index b599eb2666..21cdb6bc6f 100644 --- a/tests/test1.yaml +++ b/tests/test1.yaml @@ -2693,24 +2693,18 @@ display: row_start: 0 lambda: |- it.rectangle(0, 0, it.get_width(), it.get_height()); - - platform: ili9341 + - platform: ili9xxx model: TFT 2.4 cs_pin: GPIO5 dc_pin: GPIO4 reset_pin: GPIO22 - led_pin: - number: GPIO15 - inverted: true lambda: |- it.rectangle(0, 0, it.get_width(), it.get_height()); - - platform: ili9341 + - platform: ili9xxx model: TFT 2.4 cs_pin: GPIO5 dc_pin: GPIO4 reset_pin: GPIO22 - led_pin: - number: GPIO15 - inverted: true auto_clear_enabled: false rotation: 90 lambda: |-