diff --git a/AGENTS.md b/AGENTS.md index e00888b5f77..5581a255dba 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -225,6 +225,15 @@ file does, and it is the authority when they disagree. The most useful starting stays inside the `#ifdef`. * **Failures after `setup()`:** `mark_failed()` is for `setup()` only. A runtime read or write failure sets `status_set_warning()` and the next success clears it; writes skip work when `is_failed()`. + * **`dump_config()` already prints the failure reason:** for a failed component, core prints + ` is marked FAILED: ` right after the component's `dump_config()`, using the reason + passed to `mark_failed()`. So `dump_config()` never checks `is_failed()` to log a failure, and no + `error_code_` member exists only to pick that message. Pass the reason where setup fails instead: + `this->mark_failed(LOG_STR(ESP_LOG_MSG_COMM_FAIL))` for a failed bus transfer, a short specific reason + otherwise (`LOG_STR("Wrong chip ID")`). Core also logs it at that moment, so no `ESP_LOGE` repeats it + before `mark_failed()`; keep a separate line only when it prints a value read at run time, such as the + chip ID that did not match. Only the first reason is kept, so do not call `status_set_error()` before + `mark_failed()` and do not mark the component failed again after a helper already did. * **Fixed platform facts compile to nothing:** gate on `#ifdef USE_ESP8266` and friends, not on a runtime check of something the build already knows. * **Logging string literals:** wrap literals passed as `%s` arguments in `LOG_STR_LITERAL()` so they diff --git a/docker/Dockerfile b/docker/Dockerfile index c9aa3dba62c..658b8c06302 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -22,7 +22,7 @@ RUN \ -r /requirements.txt # Install the ESPHome Device Builder dashboard. -RUN uv pip install --no-cache-dir esphome-device-builder==1.23.1 +RUN uv pip install --no-cache-dir esphome-device-builder==1.24.0 RUN \ platformio settings set enable_telemetry No \ diff --git a/esphome/components/ads1115/ads1115.cpp b/esphome/components/ads1115/ads1115.cpp index b75e1b73046..03714390fea 100644 --- a/esphome/components/ads1115/ads1115.cpp +++ b/esphome/components/ads1115/ads1115.cpp @@ -11,7 +11,7 @@ static const uint8_t ADS1115_REGISTER_CONFIG = 0x01; void ADS1115Component::setup() { uint16_t value; if (!this->read_byte_16(ADS1115_REGISTER_CONVERSION, &value)) { - this->mark_failed(); + this->mark_failed(LOG_STR(ESP_LOG_MSG_COMM_FAIL)); return; } @@ -58,7 +58,7 @@ void ADS1115Component::setup() { config |= 0b0000000000000011; if (!this->write_byte_16(ADS1115_REGISTER_CONFIG, config)) { - this->mark_failed(); + this->mark_failed(LOG_STR(ESP_LOG_MSG_COMM_FAIL)); return; } this->prev_config_ = config; @@ -66,9 +66,6 @@ void ADS1115Component::setup() { void ADS1115Component::dump_config() { ESP_LOGCONFIG(TAG, "ADS1115:"); LOG_I2C_DEVICE(this); - if (this->is_failed()) { - ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL); - } } float ADS1115Component::request_measurement(ADS1115Multiplexer multiplexer, ADS1115Gain gain, ADS1115Resolution resolution, ADS1115Samplerate samplerate) { diff --git a/esphome/components/aht10/aht10.cpp b/esphome/components/aht10/aht10.cpp index 83ab55e6576..41167033701 100644 --- a/esphome/components/aht10/aht10.cpp +++ b/esphome/components/aht10/aht10.cpp @@ -52,8 +52,7 @@ void AHT10Component::setup() { break; } if (error_code != i2c::ERROR_OK) { - ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL); - this->mark_failed(); + this->mark_failed(LOG_STR(ESP_LOG_MSG_COMM_FAIL)); return; } uint8_t cal_attempts = 0; @@ -61,20 +60,17 @@ void AHT10Component::setup() { while (data & AHT10_STATUS_BUSY) { delay(AHT10_DEFAULT_DELAY); if (this->read(&data, 1) != i2c::ERROR_OK) { - ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL); - this->mark_failed(); + this->mark_failed(LOG_STR(ESP_LOG_MSG_COMM_FAIL)); return; } ++cal_attempts; if (cal_attempts > AHT10_INIT_ATTEMPTS) { - ESP_LOGE(TAG, "Initialization timed out"); - this->mark_failed(); + this->mark_failed(LOG_STR("Initialization timed out")); return; } } if ((data & 0x68) != 0x08) { // Bit[6:5] = 0b00, NORMAL mode and Bit[3] = 0b1, CALIBRATED - ESP_LOGE(TAG, "Initialization failed"); - this->mark_failed(); + this->mark_failed(LOG_STR("Initialization failed")); return; } } @@ -152,9 +148,6 @@ void AHT10Component::update() { void AHT10Component::dump_config() { ESP_LOGCONFIG(TAG, "AHT10:"); LOG_I2C_DEVICE(this); - if (this->is_failed()) { - ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL); - } LOG_SENSOR(" ", "Temperature", this->temperature_sensor_); LOG_SENSOR(" ", "Humidity", this->humidity_sensor_); } diff --git a/esphome/components/aic3204/aic3204.cpp b/esphome/components/aic3204/aic3204.cpp index 892a6c56ac0..23902035eb8 100644 --- a/esphome/components/aic3204/aic3204.cpp +++ b/esphome/components/aic3204/aic3204.cpp @@ -10,8 +10,7 @@ ESPHOME_LOG_TAG(TAG, "aic3204"); #define ERROR_CHECK(err, msg) \ if (!(err)) { \ - ESP_LOGE(TAG, msg); \ - this->mark_failed(); \ + this->mark_failed(LOG_STR(msg)); \ return; \ } @@ -108,10 +107,6 @@ void AIC3204::setup() { void AIC3204::dump_config() { ESP_LOGCONFIG(TAG, "AIC3204:"); LOG_I2C_DEVICE(this); - - if (this->is_failed()) { - ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL); - } } bool AIC3204::set_mute_off() { diff --git a/esphome/components/am2315c/am2315c.cpp b/esphome/components/am2315c/am2315c.cpp index d34061889be..9b350560ee9 100644 --- a/esphome/components/am2315c/am2315c.cpp +++ b/esphome/components/am2315c/am2315c.cpp @@ -42,21 +42,18 @@ bool AM2315C::reset_register_(uint8_t reg) { data[2] = 0; ESP_LOGD(TAG, "Reset register: 0x%02x", reg); if (this->write(data, 3) != i2c::ERROR_OK) { - ESP_LOGE(TAG, "Write failed!"); - this->mark_failed(); + this->mark_failed(LOG_STR(ESP_LOG_MSG_COMM_FAIL)); return false; } delay(5); if (this->read(data, 3) != i2c::ERROR_OK) { - ESP_LOGE(TAG, "Read failed!"); - this->mark_failed(); + this->mark_failed(LOG_STR(ESP_LOG_MSG_COMM_FAIL)); return false; } delay(10); data[0] = 0xB0 | reg; if (this->write(data, 3) != i2c::ERROR_OK) { - ESP_LOGE(TAG, "Write failed!"); - this->mark_failed(); + this->mark_failed(LOG_STR(ESP_LOG_MSG_COMM_FAIL)); return false; } delay(5); @@ -76,8 +73,7 @@ void AM2315C::setup() { // get status uint8_t status = 0; if (this->read(&status, 1) != i2c::ERROR_OK) { - ESP_LOGE(TAG, "Read failed!"); - this->mark_failed(); + this->mark_failed(LOG_STR(ESP_LOG_MSG_COMM_FAIL)); return; } @@ -86,18 +82,9 @@ void AM2315C::setup() { // never required during testing if ((status & 0x18) != 0x18) { ESP_LOGD(TAG, "Resetting AM2315C registers"); - if (!this->reset_register_(0x1B)) { - this->mark_failed(); + // reset_register_() marks the component failed with its own reason + if (!this->reset_register_(0x1B) || !this->reset_register_(0x1C) || !this->reset_register_(0x1E)) return; - } - if (!this->reset_register_(0x1C)) { - this->mark_failed(); - return; - } - if (!this->reset_register_(0x1E)) { - this->mark_failed(); - return; - } } } @@ -168,9 +155,6 @@ void AM2315C::update() { void AM2315C::dump_config() { ESP_LOGCONFIG(TAG, "AM2315C:"); LOG_I2C_DEVICE(this); - if (this->is_failed()) { - ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL); - } LOG_SENSOR(" ", "Temperature", this->temperature_sensor_); LOG_SENSOR(" ", "Humidity", this->humidity_sensor_); } diff --git a/esphome/components/am2320/am2320.cpp b/esphome/components/am2320/am2320.cpp index 94053ab5dc8..1b3761fdac5 100644 --- a/esphome/components/am2320/am2320.cpp +++ b/esphome/components/am2320/am2320.cpp @@ -37,16 +37,13 @@ void AM2320Component::setup() { data[0] = 0; data[1] = 4; if (!this->read_data_(data)) { - this->mark_failed(); + this->mark_failed(LOG_STR(ESP_LOG_MSG_COMM_FAIL)); return; } } void AM2320Component::dump_config() { ESP_LOGD(TAG, "AM2320:"); LOG_I2C_DEVICE(this); - if (this->is_failed()) { - ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL); - } LOG_SENSOR(" ", "Temperature", this->temperature_sensor_); LOG_SENSOR(" ", "Humidity", this->humidity_sensor_); } diff --git a/esphome/components/animation/animation.cpp b/esphome/components/animation/animation.cpp index 2a293adf1d8..1a2307520d3 100644 --- a/esphome/components/animation/animation.cpp +++ b/esphome/components/animation/animation.cpp @@ -5,8 +5,8 @@ namespace esphome::animation { Animation::Animation(const uint8_t *data_start, int width, int height, uint32_t animation_frame_count, - image::ImageType type, image::Transparency transparent) - : Image(data_start, width, height, type, transparent), + image::ImageType type, image::Transparency transparent, bool big_endian) + : Image(data_start, width, height, type, transparent, big_endian), animation_data_start_(data_start), current_frame_(0), animation_frame_count_(animation_frame_count), diff --git a/esphome/components/animation/animation.h b/esphome/components/animation/animation.h index 4ee060c1213..0cee54c3607 100644 --- a/esphome/components/animation/animation.h +++ b/esphome/components/animation/animation.h @@ -6,7 +6,7 @@ namespace esphome::animation { class Animation final : public image::Image { public: Animation(const uint8_t *data_start, int width, int height, uint32_t animation_frame_count, image::ImageType type, - image::Transparency transparent); + image::Transparency transparent, bool big_endian = false); uint32_t get_animation_frame_count() const; int get_current_frame() const; diff --git a/esphome/components/animation/image.py b/esphome/components/animation/image.py index 09e0e8753cf..b8280f47cd9 100644 --- a/esphome/components/animation/image.py +++ b/esphome/components/animation/image.py @@ -1,6 +1,6 @@ from esphome import automation import esphome.codegen as cg -from esphome.components.const import CONF_LOOP +from esphome.components.const import CONF_BYTE_ORDER, CONF_LOOP from esphome.components.file import image as file_image from esphome.components.file.image import image_schema, write_image from esphome.components.image import Image_, validate_settings @@ -90,6 +90,7 @@ async def setup_animation(config: ConfigType) -> None: frame_count, image_type, trans_value, + config.get(CONF_BYTE_ORDER) == "BIG_ENDIAN", ) if loop_config := config.get(CONF_LOOP): start = loop_config[CONF_START_FRAME] diff --git a/esphome/components/apds9306/apds9306.cpp b/esphome/components/apds9306/apds9306.cpp index daf6bb6b338..af123d8c4f3 100644 --- a/esphome/components/apds9306/apds9306.cpp +++ b/esphome/components/apds9306/apds9306.cpp @@ -34,8 +34,7 @@ enum { // APDS9306 registers #define APDS9306_ERROR_CHECK(func, error) \ if (!(func)) { \ - ESP_LOGE(TAG, error); \ - this->mark_failed(); \ + this->mark_failed(LOG_STR(error)); \ return; \ } #define APDS9306_WARNING_CHECK(func, warning) \ @@ -48,21 +47,19 @@ enum { // APDS9306 registers ESP_LOGV(TAG, "Writing 0x%02x to 0x%02x", value, reg); \ if (!this->write_byte(reg, value)) { \ ESP_LOGE(TAG, "Failed writing 0x%02x to 0x%02x", value, reg); \ - this->mark_failed(); \ + this->mark_failed(LOG_STR(ESP_LOG_MSG_COMM_FAIL)); \ return; \ } void APDS9306::setup() { uint8_t id; if (!this->read_byte(APDS9306_PART_ID, &id)) { // Part ID register - this->error_code_ = COMMUNICATION_FAILED; - this->mark_failed(); + this->mark_failed(LOG_STR(ESP_LOG_MSG_COMM_FAIL)); return; } if (id != 0xB1 && id != 0xB3) { // 0xB1 for APDS9306 0xB3 for APDS9306-065 - this->error_code_ = WRONG_ID; - this->mark_failed(); + this->mark_failed(LOG_STR("APDS9306 has invalid id")); return; } @@ -89,20 +86,6 @@ void APDS9306::dump_config() { LOG_SENSOR("", "APDS9306", this); LOG_I2C_DEVICE(this); - if (this->is_failed()) { - switch (this->error_code_) { - case COMMUNICATION_FAILED: - ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL); - break; - case WRONG_ID: - ESP_LOGE(TAG, "APDS9306 has invalid id!"); - break; - default: - ESP_LOGE(TAG, "Setting up APDS9306 registers failed!"); - break; - } - } - ESP_LOGCONFIG(TAG, " Gain: %u\n" " Measurement rate: %u\n" diff --git a/esphome/components/apds9306/apds9306.h b/esphome/components/apds9306/apds9306.h index f971290cdd5..1f0a695c4ff 100644 --- a/esphome/components/apds9306/apds9306.h +++ b/esphome/components/apds9306/apds9306.h @@ -50,12 +50,6 @@ class APDS9306 final : public sensor::Sensor, public PollingComponent, public i2 void set_ambient_light_gain(AmbientLightGain gain) { this->gain_ = gain; } protected: - enum ErrorCode { - NONE = 0, - COMMUNICATION_FAILED, - WRONG_ID, - } error_code_{NONE}; - MeasurementBitWidth bit_width_; MeasurementRate measurement_rate_; AmbientLightGain gain_; diff --git a/esphome/components/apds9960/apds9960.cpp b/esphome/components/apds9960/apds9960.cpp index ef5d43e2cba..c003e7a8bc9 100644 --- a/esphome/components/apds9960/apds9960.cpp +++ b/esphome/components/apds9960/apds9960.cpp @@ -8,7 +8,7 @@ ESPHOME_LOG_TAG(TAG, "apds9960"); #define APDS9960_ERROR_CHECK(func) \ if (!(func)) { \ - this->mark_failed(); \ + this->mark_failed(LOG_STR("Setting up APDS9960 registers failed")); \ return; \ } #define APDS9960_WRITE_BYTE(reg, value) APDS9960_ERROR_CHECK(this->write_byte(reg, value)); @@ -16,14 +16,12 @@ ESPHOME_LOG_TAG(TAG, "apds9960"); void APDS9960::setup() { uint8_t id; if (!this->read_byte(0x92, &id)) { // ID register - this->error_code_ = COMMUNICATION_FAILED; - this->mark_failed(); + this->mark_failed(LOG_STR(ESP_LOG_MSG_COMM_FAIL)); return; } if (id != 0xAB && id != 0x9C && id != 0xA8 && id != 0x9E) { // APDS9960 all should have one of these IDs - this->error_code_ = WRONG_ID; - this->mark_failed(); + this->mark_failed(LOG_STR("APDS9960 has invalid id")); return; } @@ -135,20 +133,6 @@ void APDS9960::dump_config() { LOG_SENSOR(" ", "Clear channel", this->clear_sensor_); LOG_SENSOR(" ", "Proximity", this->proximity_sensor_); #endif - - if (this->is_failed()) { - switch (this->error_code_) { - case COMMUNICATION_FAILED: - ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL); - break; - case WRONG_ID: - ESP_LOGE(TAG, "APDS9960 has invalid id!"); - break; - default: - ESP_LOGE(TAG, "Setting up APDS9960 registers failed!"); - break; - } - } } #define APDS9960_WARNING_CHECK(func, warning) \ diff --git a/esphome/components/apds9960/apds9960.h b/esphome/components/apds9960/apds9960.h index bfa64bcc745..5e84a6d3d9d 100644 --- a/esphome/components/apds9960/apds9960.h +++ b/esphome/components/apds9960/apds9960.h @@ -58,11 +58,6 @@ class APDS9960 final : public PollingComponent, public i2c::I2CDevice { uint8_t gesture_gain_; uint8_t gesture_wait_time_; - enum ErrorCode { - NONE = 0, - COMMUNICATION_FAILED, - WRONG_ID, - } error_code_{NONE}; bool gesture_up_started_{false}; bool gesture_down_started_{false}; bool gesture_left_started_{false}; diff --git a/esphome/components/as5600/as5600.cpp b/esphome/components/as5600/as5600.cpp index fc768b383b0..279e4051660 100644 --- a/esphome/components/as5600/as5600.cpp +++ b/esphome/components/as5600/as5600.cpp @@ -23,7 +23,7 @@ static const uint8_t REGISTER_MAGNITUDE = 0x1B; // 16 bytes / R void AS5600Component::setup() { if (!this->read_byte(REGISTER_STATUS).has_value()) { - this->mark_failed(); + this->mark_failed(LOG_STR(ESP_LOG_MSG_COMM_FAIL)); return; } @@ -54,7 +54,7 @@ void AS5600Component::setup() { // write config to config register if (!this->write_byte_16(REGISTER_CONF, config)) { - this->mark_failed(); + this->mark_failed(LOG_STR(ESP_LOG_MSG_COMM_FAIL)); return; } @@ -87,10 +87,8 @@ void AS5600Component::dump_config() { ESP_LOGCONFIG(TAG, "AS5600:"); LOG_I2C_DEVICE(this); - if (this->is_failed()) { - ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL); + if (this->is_failed()) return; - } ESP_LOGCONFIG(TAG, " Watchdog: %d\n" diff --git a/esphome/components/as7341/as7341.cpp b/esphome/components/as7341/as7341.cpp index 9ebc7e95b2b..6f8ba12d839 100644 --- a/esphome/components/as7341/as7341.cpp +++ b/esphome/components/as7341/as7341.cpp @@ -14,14 +14,13 @@ void AS7341Component::setup() { this->read_byte(AS7341_ID, &id); ESP_LOGCONFIG(TAG, " Read ID: 0x%X", id); if ((id & 0xFC) != (AS7341_CHIP_ID << 2)) { - this->mark_failed(); + this->mark_failed(LOG_STR("Wrong chip ID")); return; } // Power on (enter IDLE state) if (!this->enable_power(true)) { - ESP_LOGE(TAG, " Power on failed!"); - this->mark_failed(); + this->mark_failed(LOG_STR("Power on failed")); return; } @@ -35,9 +34,6 @@ void AS7341Component::setup() { void AS7341Component::dump_config() { ESP_LOGCONFIG(TAG, "AS7341:"); LOG_I2C_DEVICE(this); - if (this->is_failed()) { - ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL); - } LOG_UPDATE_INTERVAL(this); ESP_LOGCONFIG(TAG, " Gain: %u\n" diff --git a/esphome/components/as734x/as734x.cpp b/esphome/components/as734x/as734x.cpp index 4759d1a2d5c..208fe24bdf5 100644 --- a/esphome/components/as734x/as734x.cpp +++ b/esphome/components/as734x/as734x.cpp @@ -350,9 +350,6 @@ void AS734XComponent::dump_config() { #ifdef USE_AS734X_RGB LOG_TEXT_SENSOR(" ", "RGB hex", this->rgb_hex_sensor_); #endif - if (this->is_failed()) { - ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL); - } } void AS734XComponent::enable_led(bool enable) { diff --git a/esphome/components/atm90e26/atm90e26.cpp b/esphome/components/atm90e26/atm90e26.cpp index 8ec611357c8..d0b734ddcb5 100644 --- a/esphome/components/atm90e26/atm90e26.cpp +++ b/esphome/components/atm90e26/atm90e26.cpp @@ -52,7 +52,7 @@ void ATM90E26Component::setup() { uint16_t read = this->read16_(ATM90E26_REGISTER_LASTDATA); if (read != 0x0030) { ESP_LOGW(TAG, "Could not initialize ATM90E26 IC, check SPI settings: %d", read); - this->mark_failed(); + this->mark_failed(LOG_STR(ESP_LOG_MSG_COMM_FAIL)); return; } // TODO: 100 * * sqrt(2) * / (4 * gain_voltage/32768) @@ -120,21 +120,18 @@ void ATM90E26Component::setup() { ESP_LOGW(TAG, "Could not initialize ATM90E26 IC: CS1 was incorrect, expected: 0x%04X", this->read16_(ATM90E26_REGISTER_CS1)); - this->mark_failed(); + this->mark_failed(LOG_STR("Checksum mismatch")); } if (sys_status & 0x3000) { // Checksum 2 Error ESP_LOGW(TAG, "Could not initialize ATM90E26 IC: CS2 was incorrect, expected: 0x%04X", this->read16_(ATM90E26_REGISTER_CS2)); - this->mark_failed(); + this->mark_failed(LOG_STR("Checksum mismatch")); } } void ATM90E26Component::dump_config() { ESP_LOGCONFIG("", "ATM90E26:"); LOG_PIN(" CS Pin: ", this->cs_); - if (this->is_failed()) { - ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL); - } LOG_UPDATE_INTERVAL(this); LOG_SENSOR(" ", "Voltage A", this->voltage_sensor_); LOG_SENSOR(" ", "Current A", this->current_sensor_); diff --git a/esphome/components/atm90e32/atm90e32.cpp b/esphome/components/atm90e32/atm90e32.cpp index 0c4f095ab57..0cdf9918db9 100644 --- a/esphome/components/atm90e32/atm90e32.cpp +++ b/esphome/components/atm90e32/atm90e32.cpp @@ -168,7 +168,7 @@ void ATM90E32Component::setup() { this->write16_(ATM90E32_REGISTER_CFGREGACCEN, 0x55AA); // enable register config access if (!this->validate_spi_read_(0x55AA, "setup()")) { ESP_LOGW(TAG, "Could not initialize ATM90E32 IC, check SPI settings"); - this->mark_failed(); + this->mark_failed(LOG_STR(ESP_LOG_MSG_COMM_FAIL)); return; } @@ -411,9 +411,6 @@ void ATM90E32Component::log_calibration_status_() { void ATM90E32Component::dump_config() { ESP_LOGCONFIG("", "ATM90E32:"); LOG_PIN(" CS Pin: ", this->cs_); - if (this->is_failed()) { - ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL); - } LOG_UPDATE_INTERVAL(this); LOG_SENSOR(" ", "Voltage A", this->phase_[PHASEA].voltage_sensor_); LOG_SENSOR(" ", "Current A", this->phase_[PHASEA].current_sensor_); diff --git a/esphome/components/bh1750/bh1750.cpp b/esphome/components/bh1750/bh1750.cpp index 2d9709f4228..94ee60ea8d3 100644 --- a/esphome/components/bh1750/bh1750.cpp +++ b/esphome/components/bh1750/bh1750.cpp @@ -65,7 +65,7 @@ MTreg: void BH1750Sensor::setup() { uint8_t turn_on = BH1750_COMMAND_POWER_ON; if (this->write(&turn_on, 1) != i2c::ERROR_OK) { - this->mark_failed(); + this->mark_failed(LOG_STR(ESP_LOG_MSG_COMM_FAIL)); return; } this->state_ = IDLE; @@ -74,9 +74,6 @@ void BH1750Sensor::setup() { void BH1750Sensor::dump_config() { LOG_SENSOR("", "BH1750", this); LOG_I2C_DEVICE(this); - if (this->is_failed()) { - ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL_FOR, this->get_name().c_str()); - } LOG_UPDATE_INTERVAL(this); } diff --git a/esphome/components/bluetooth_connection/bluetooth_connection_bluedroid.cpp b/esphome/components/bluetooth_connection/bluetooth_connection_bluedroid.cpp index 5fe1460c661..b642c544c56 100644 --- a/esphome/components/bluetooth_connection/bluetooth_connection_bluedroid.cpp +++ b/esphome/components/bluetooth_connection/bluetooth_connection_bluedroid.cpp @@ -62,7 +62,8 @@ void BluedroidGattClient::loop() { auto ret = esp_ble_gattc_app_register(this->app_id); if (ret) { ESP_LOGE(TAG, "gattc app register failed: app_id=%d code=%d", this->app_id, ret); - this->mark_failed(); + this->mark_failed( + LOG_STR("Registration failed; if the error was ESP_GATT_NO_RESOURCES, reduce the connection slots")); } // Do not wait for REG_EVT; connect() rejects until it lands. this->set_idle_(); @@ -108,12 +109,7 @@ void BluedroidGattClient::ble_before_disabled_event_handler() { this->enable_loop(); } -void BluedroidGattClient::dump_config() { - ESP_LOGCONFIG(TAG, "Bluedroid GATT client %d", this->connection_index_); - if (this->is_failed()) { - ESP_LOGE(TAG, " Registration failed; if the error was ESP_GATT_NO_RESOURCES, reduce the connection slots"); - } -} +void BluedroidGattClient::dump_config() { ESP_LOGCONFIG(TAG, "Bluedroid GATT client %d", this->connection_index_); } // ---- contract ops ---- @@ -680,7 +676,8 @@ bool BluedroidGattClient::gattc_event_handler(esp_gattc_cb_event_t event, esp_ga this->gattc_if_ = esp_gattc_if; } else { ESP_LOGE(TAG, "[%d] gattc app registration failed, status=%d", this->connection_index_, param->reg.status); - this->mark_failed(); + this->mark_failed( + LOG_STR("Registration failed; if the error was ESP_GATT_NO_RESOURCES, reduce the connection slots")); } break; } diff --git a/esphome/components/bluetooth_connection/bluetooth_connection_rp2.cpp b/esphome/components/bluetooth_connection/bluetooth_connection_rp2.cpp index c6e2703c48b..6af2fba62bb 100644 --- a/esphome/components/bluetooth_connection/bluetooth_connection_rp2.cpp +++ b/esphome/components/bluetooth_connection/bluetooth_connection_rp2.cpp @@ -76,16 +76,14 @@ void RP2GattClient::setup() { // Pre-create every pool entry so the packet handlers' allocate() calls are // always a free-list pop -- the IRQ path must never reach malloc(). if (!this->event_pool_.warm() || !this->notify_pool_.warm()) { - ESP_LOGE(TAG, "GATT event pool warm-up failed"); - this->mark_failed(); + this->mark_failed(LOG_STR("GATT event pool warm-up failed")); return; } // Register this engine for IRQ-context event routing. if (instance_count >= ESPHOME_BLE_GATT_CLIENT_COUNT) { // Cannot happen with codegen-sized storage; refuse loudly if it ever does. - ESP_LOGE(TAG, "GATT client registry full"); - this->mark_failed(); + this->mark_failed(LOG_STR("GATT client registry full")); return; } { diff --git a/esphome/components/bm8563/bm8563.cpp b/esphome/components/bm8563/bm8563.cpp index de6b51ab1f5..f83b98af115 100644 --- a/esphome/components/bm8563/bm8563.cpp +++ b/esphome/components/bm8563/bm8563.cpp @@ -21,7 +21,7 @@ static constexpr uint32_t MAX_TIMER_DURATION_S = 255 * 60; void BM8563::setup() { if (!this->write_byte_16(CONTROL_STATUS_1_REG, 0)) { - this->mark_failed(); + this->mark_failed(LOG_STR(ESP_LOG_MSG_COMM_FAIL)); return; } } @@ -31,9 +31,6 @@ void BM8563::update() { this->read_time(); } void BM8563::dump_config() { ESP_LOGCONFIG(TAG, "BM8563:"); LOG_I2C_DEVICE(this); - if (this->is_failed()) { - ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL); - } } void BM8563::start_timer(uint32_t duration_s) { diff --git a/esphome/components/bme280_base/bme280_base.cpp b/esphome/components/bme280_base/bme280_base.cpp index 96bb56f345e..4b74a15c0a6 100644 --- a/esphome/components/bme280_base/bme280_base.cpp +++ b/esphome/components/bme280_base/bme280_base.cpp @@ -7,8 +7,6 @@ #include #include -#define BME280_ERROR_WRONG_CHIP_ID "Wrong chip ID or no response" - namespace esphome::bme280_base { ESPHOME_LOG_TAG(TAG, "bme280.sensor"); @@ -100,13 +98,11 @@ void BME280Component::setup() { } if (!this->read_byte(BME280_REGISTER_CHIPID, &chip_id)) { - this->error_code_ = COMMUNICATION_FAILED; this->mark_failed(LOG_STR(ESP_LOG_MSG_COMM_FAIL)); return; } if (chip_id != 0x60) { - this->error_code_ = WRONG_CHIP_ID; - this->mark_failed(LOG_STR(BME280_ERROR_WRONG_CHIP_ID)); + this->mark_failed(LOG_STR("Wrong chip ID or no response")); return; } @@ -182,17 +178,6 @@ void BME280Component::setup() { } void BME280Component::dump_config() { ESP_LOGCONFIG(TAG, "BME280:"); - switch (this->error_code_) { - case COMMUNICATION_FAILED: - ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL); - break; - case WRONG_CHIP_ID: - ESP_LOGE(TAG, BME280_ERROR_WRONG_CHIP_ID); - break; - case NONE: - default: - break; - } ESP_LOGCONFIG(TAG, " IIR Filter: %s", iir_filter_to_str(this->iir_filter_)); LOG_UPDATE_INTERVAL(this); diff --git a/esphome/components/bme280_base/bme280_base.h b/esphome/components/bme280_base/bme280_base.h index 8b4906b7b77..d1a441ccebb 100644 --- a/esphome/components/bme280_base/bme280_base.h +++ b/esphome/components/bme280_base/bme280_base.h @@ -101,11 +101,6 @@ class BME280Component : public PollingComponent { sensor::Sensor *temperature_sensor_{nullptr}; sensor::Sensor *pressure_sensor_{nullptr}; sensor::Sensor *humidity_sensor_{nullptr}; - enum ErrorCode { - NONE = 0, - COMMUNICATION_FAILED, - WRONG_CHIP_ID, - } error_code_{NONE}; }; } // namespace esphome::bme280_base diff --git a/esphome/components/bme680/bme680.cpp b/esphome/components/bme680/bme680.cpp index 8695e6a0e21..2996cd206d1 100644 --- a/esphome/components/bme680/bme680.cpp +++ b/esphome/components/bme680/bme680.cpp @@ -72,22 +72,24 @@ constexpr float BME680_GAS_LOOKUP_TABLE_2[16] PROGMEM = {0.0, 0.0, 0.0, 0.0, 0. } void BME680Component::setup() { + if (!this->setup_registers_()) + this->mark_failed(LOG_STR(ESP_LOG_MSG_COMM_FAIL)); +} + +bool BME680Component::setup_registers_() { uint8_t chip_id; if (!this->read_byte(BME680_REGISTER_CHIPID, &chip_id) || chip_id != 0x61) { - this->mark_failed(); - return; + return false; } // Read calibration uint8_t coeff1[25]; if (!this->read_bytes(BME680_REGISTER_COEFF1, coeff1, 25)) { - this->mark_failed(); - return; + return false; } uint8_t coeff2[16]; if (!this->read_bytes(BME680_REGISTER_COEFF2, coeff2, 16)) { - this->mark_failed(); - return; + return false; } this->calibration_.t1 = coeff2[9] << 8 | coeff2[8]; @@ -119,20 +121,17 @@ void BME680Component::setup() { uint8_t temp_var = 0; if (!this->read_byte(0x02, &temp_var)) { - this->mark_failed(); - return; + return false; } this->calibration_.res_heat_range = ((temp_var & 0x30) / 16); if (!this->read_byte(0x00, &temp_var)) { - this->mark_failed(); - return; + return false; } this->calibration_.res_heat_val = (int8_t) temp_var; if (!this->read_byte(0x04, &temp_var)) { - this->mark_failed(); - return; + return false; } this->calibration_.range_sw_err = ((int8_t) temp_var & (int8_t) 0xf0) / 16; @@ -140,54 +139,46 @@ void BME680Component::setup() { // Config register if (!this->update_register(BME680_REGISTER_CONFIG, 0b00011100, (this->iir_filter_ & 0b111) << 2)) { - this->mark_failed(); - return; + return false; } // Humidity control register if (!this->update_register(BME680_REGISTER_CONTROL_HUMIDITY, 0b00000111, this->humidity_oversampling_ & 0b111)) { - this->mark_failed(); - return; + return false; } // Gas 1 control register: run gas, heater profile 0 if (!this->update_register(BME680_REGISTER_CONTROL_GAS1, 0b00011111, 1 << 4)) { - this->mark_failed(); - return; + return false; } const bool heat_off = this->heater_temperature_ == 0 || this->heater_duration_ == 0; // Gas 0 control register if (!this->update_register(BME680_REGISTER_CONTROL_GAS0, 0b00001000, heat_off << 3)) { - this->mark_failed(); - return; + return false; } if (!heat_off) { // Gas Heater Temperature uint8_t temperature = this->calc_heater_resistance_(this->heater_temperature_); if (!this->write_byte(BME680_REGISTER_HEATER_HEAT0, temperature)) { - this->mark_failed(); - return; + return false; } // Gas Heater Duration uint8_t duration = this->calc_heater_duration_(this->heater_duration_); if (!this->write_byte(BME680_REGISTER_HEATER_WAIT0, duration)) { - this->mark_failed(); - return; + return false; } } + return true; } void BME680Component::dump_config() { ESP_LOGCONFIG(TAG, "BME680:"); LOG_I2C_DEVICE(this); - if (this->is_failed()) { - ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL); - } ESP_LOGCONFIG(TAG, " IIR Filter: %s", iir_filter_to_str(this->iir_filter_)); LOG_UPDATE_INTERVAL(this); diff --git a/esphome/components/bme680/bme680.h b/esphome/components/bme680/bme680.h index e401d036595..64fa20f2fb1 100644 --- a/esphome/components/bme680/bme680.h +++ b/esphome/components/bme680/bme680.h @@ -101,6 +101,8 @@ class BME680Component final : public PollingComponent, public i2c::I2CDevice { void update() override; protected: + /// Configure the sensor registers; false on an I2C failure. + bool setup_registers_(); /// Calculate the heater resistance value to send to the BME680 register. uint8_t calc_heater_resistance_(uint16_t temperature); /// Calculate the heater duration value to send to the BME680 register. diff --git a/esphome/components/bmi160/bmi160.cpp b/esphome/components/bmi160/bmi160.cpp index a90d490ae18..b97baae06ae 100644 --- a/esphome/components/bmi160/bmi160.cpp +++ b/esphome/components/bmi160/bmi160.cpp @@ -117,18 +117,21 @@ const uint8_t BMI160_REGISTER_DATA_TEMP_MSB = 0x21; const float GRAVITY_EARTH = 9.80665f; void BMI160Component::internal_setup_(int stage) { + if (!this->setup_stage_(stage)) + this->mark_failed(LOG_STR(ESP_LOG_MSG_COMM_FAIL)); +} + +bool BMI160Component::setup_stage_(int stage) { switch (stage) { case 0: uint8_t chipid; if (!this->read_byte(BMI160_REGISTER_CHIPID, &chipid) || (chipid != 0b11010001)) { - this->mark_failed(); - return; + return false; } ESP_LOGV(TAG, " Bringing accelerometer out of sleep"); if (!this->write_byte(BMI160_REGISTER_CMD, (uint8_t) Cmd::ACCL_SET_PMU_MODE | (uint8_t) AcclPmuMode::NORMAL)) { - this->mark_failed(); - return; + return false; } ESP_LOGV(TAG, " Waiting for accelerometer to wake up"); // need to wait (max delay in datasheet) because we can't send commands while another is in progress @@ -139,8 +142,7 @@ void BMI160Component::internal_setup_(int stage) { case 1: ESP_LOGV(TAG, " Bringing gyroscope out of sleep"); if (!this->write_byte(BMI160_REGISTER_CMD, (uint8_t) Cmd::GYRO_SET_PMU_MODE | (uint8_t) GyroPmuMode::NORMAL)) { - this->mark_failed(); - return; + return false; } ESP_LOGV(TAG, " Waiting for gyroscope to wake up"); // wait between 51 & 81ms, doing 100 to be safe @@ -152,15 +154,13 @@ void BMI160Component::internal_setup_(int stage) { uint8_t gyro_config = (uint8_t) GyroBandwidth::OSR4 | (uint8_t) GyroOuputDataRate::HZ_25; ESP_LOGV(TAG, " Output gyro_config: 0b" BYTE_TO_BINARY_PATTERN, BYTE_TO_BINARY(gyro_config)); if (!this->write_byte(BMI160_REGISTER_GYRO_CONFIG, gyro_config)) { - this->mark_failed(); - return; + return false; } ESP_LOGV(TAG, " Setting up Gyro Range"); uint8_t gyro_range = (uint8_t) GyroRange::RANGE_2000_DPS; ESP_LOGV(TAG, " Output gyro_range: 0b" BYTE_TO_BINARY_PATTERN, BYTE_TO_BINARY(gyro_range)); if (!this->write_byte(BMI160_REGISTER_GYRO_RANGE, gyro_range)) { - this->mark_failed(); - return; + return false; } ESP_LOGV(TAG, " Setting up Accel Config"); @@ -168,28 +168,24 @@ void BMI160Component::internal_setup_(int stage) { (uint8_t) AcclFilterMode::PERF | (uint8_t) AcclBandwidth::RES_AVG16 | (uint8_t) AccelOutputDataRate::HZ_25; ESP_LOGV(TAG, " Output accel_config: 0b" BYTE_TO_BINARY_PATTERN, BYTE_TO_BINARY(accel_config)); if (!this->write_byte(BMI160_REGISTER_ACCEL_CONFIG, accel_config)) { - this->mark_failed(); - return; + return false; } ESP_LOGV(TAG, " Setting up Accel Range"); uint8_t accel_range = (uint8_t) AccelRange::RANGE_16G; ESP_LOGV(TAG, " Output accel_range: 0b" BYTE_TO_BINARY_PATTERN, BYTE_TO_BINARY(accel_range)); if (!this->write_byte(BMI160_REGISTER_ACCEL_RANGE, accel_range)) { - this->mark_failed(); - return; + return false; } this->setup_complete_ = true; } + return true; } void BMI160Component::setup() { this->internal_setup_(0); } void BMI160Component::dump_config() { ESP_LOGCONFIG(TAG, "BMI160:"); LOG_I2C_DEVICE(this); - if (this->is_failed()) { - ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL); - } LOG_UPDATE_INTERVAL(this); LOG_SENSOR(" ", "Acceleration X", this->accel_x_sensor_); LOG_SENSOR(" ", "Acceleration Y", this->accel_y_sensor_); diff --git a/esphome/components/bmi160/bmi160.h b/esphome/components/bmi160/bmi160.h index 8af25a09add..53704965cd9 100644 --- a/esphome/components/bmi160/bmi160.h +++ b/esphome/components/bmi160/bmi160.h @@ -31,6 +31,8 @@ class BMI160Component final : public PollingComponent, public i2c::I2CDevice { sensor::Sensor *gyro_z_sensor_{nullptr}; void internal_setup_(int stage); + /// Run one setup stage; false on an I2C failure. + bool setup_stage_(int stage); bool setup_complete_{false}; /** reads `len` 16-bit little-endian integers from the given i2c register */ diff --git a/esphome/components/bmi270/bmi270.cpp b/esphome/components/bmi270/bmi270.cpp index 6f14598a5f4..e3c6d83a722 100644 --- a/esphome/components/bmi270/bmi270.cpp +++ b/esphome/components/bmi270/bmi270.cpp @@ -80,28 +80,26 @@ void BMI270Component::setup() { // 1. Verify chip ID uint8_t chip_id = 0; if (!this->read_byte(BMI270_REG_CHIP_ID, &chip_id)) { - ESP_LOGE(TAG, "Failed to read chip ID – check wiring / address"); - this->mark_failed(); + this->mark_failed(LOG_STR(ESP_LOG_MSG_COMM_FAIL)); return; } if (chip_id != BMI270_CHIP_ID_VALUE) { ESP_LOGE(TAG, "Wrong chip ID: 0x%02X (expected 0x%02X)", chip_id, BMI270_CHIP_ID_VALUE); - this->mark_failed(); + this->mark_failed(LOG_STR("Wrong chip ID")); return; } ESP_LOGD(TAG, "Chip ID: 0x%02X", chip_id); // 2. Soft-reset via CMD register (0x7E = 0xB6) if (!this->write_byte(BMI270_REG_CMD, BMI270_CMD_SOFT_RESET)) { - this->mark_failed(); + this->mark_failed(LOG_STR(ESP_LOG_MSG_COMM_FAIL)); return; } delay(20); // 4. Upload the configuration blob if (!load_config_file_()) { - ESP_LOGE(TAG, "Config file upload failed"); - this->mark_failed(); + this->mark_failed(LOG_STR("Config file upload failed")); return; } ESP_LOGD(TAG, "Config blob uploaded ✓"); @@ -111,11 +109,11 @@ void BMI270Component::setup() { uint8_t acc_conf = (uint8_t) (accel_odr_) | (BMI270_CONF_BWP_NORMAL << BMI270_CONF_BWP_SHIFT) | BMI270_ACC_CONF_PERF_MODE; if (!this->write_byte(BMI270_REG_ACC_CONF, acc_conf)) { - this->mark_failed(); + this->mark_failed(LOG_STR(ESP_LOG_MSG_COMM_FAIL)); return; } if (!this->write_byte(BMI270_REG_ACC_RANGE, (uint8_t) accel_range_)) { - this->mark_failed(); + this->mark_failed(LOG_STR(ESP_LOG_MSG_COMM_FAIL)); return; } @@ -124,18 +122,18 @@ void BMI270Component::setup() { uint8_t gyr_conf = (uint8_t) (gyro_odr_) | (BMI270_CONF_BWP_NORMAL << BMI270_CONF_BWP_SHIFT) | BMI270_GYR_CONF_NOISE_PERF | BMI270_GYR_CONF_FILTER_PERF; if (!this->write_byte(BMI270_REG_GYR_CONF, gyr_conf)) { - this->mark_failed(); + this->mark_failed(LOG_STR(ESP_LOG_MSG_COMM_FAIL)); return; } if (!this->write_byte(BMI270_REG_GYR_RANGE, (uint8_t) gyro_range_)) { - this->mark_failed(); + this->mark_failed(LOG_STR(ESP_LOG_MSG_COMM_FAIL)); return; } // 7. Enable accelerometer, gyroscope, and temperature sensor // PWR_CTRL bits: temp_en[3] | gyr_en[2] | acc_en[1] if (!this->write_byte(BMI270_REG_PWR_CTRL, BMI270_PWR_CTRL_IMU)) { - this->mark_failed(); + this->mark_failed(LOG_STR(ESP_LOG_MSG_COMM_FAIL)); return; } delay(5); @@ -143,7 +141,7 @@ void BMI270Component::setup() { // 8. Re-enable advanced power save (optional; keeps current low between reads) // Disabled here for simplicity – leave in performance mode if (!this->write_byte(BMI270_REG_PWR_CONF, BMI270_PWR_CONF_FIFO_SELF_WAKEUP)) { // bit1 = fifo_self_wakeup - this->mark_failed(); + this->mark_failed(LOG_STR(ESP_LOG_MSG_COMM_FAIL)); return; } @@ -272,10 +270,8 @@ bool BMI270Component::setup_magnetometer_() { void BMI270Component::dump_config() { ESP_LOGCONFIG(TAG, "BMI270 IMU:"); LOG_I2C_DEVICE(this); - if (this->is_failed()) { - ESP_LOGE(TAG, " Communication failed!"); + if (this->is_failed()) return; - } ESP_LOGCONFIG(TAG, " Accel range : %s", LOG_STR_ARG(AccelRangeStrings::get_log_str(this->accel_range_, AccelRangeStrings::LAST_INDEX))); diff --git a/esphome/components/bmp085/bmp085.cpp b/esphome/components/bmp085/bmp085.cpp index b989ed877b6..f9ec710a9b6 100644 --- a/esphome/components/bmp085/bmp085.cpp +++ b/esphome/components/bmp085/bmp085.cpp @@ -24,7 +24,7 @@ void BMP085Component::update() { void BMP085Component::setup() { uint8_t data[22]; if (!this->read_bytes(BMP085_REGISTER_AC1_H, data, 22)) { - this->mark_failed(); + this->mark_failed(LOG_STR(ESP_LOG_MSG_COMM_FAIL)); return; } @@ -44,9 +44,6 @@ void BMP085Component::setup() { void BMP085Component::dump_config() { ESP_LOGCONFIG(TAG, "BMP085:"); LOG_I2C_DEVICE(this); - if (this->is_failed()) { - ESP_LOGE(TAG, "Connection with BMP085 failed!"); - } LOG_UPDATE_INTERVAL(this); LOG_SENSOR(" ", "Temperature", this->temperature_); diff --git a/esphome/components/bmp280_base/bmp280_base.cpp b/esphome/components/bmp280_base/bmp280_base.cpp index 9cb023c6884..d82f80ccd43 100644 --- a/esphome/components/bmp280_base/bmp280_base.cpp +++ b/esphome/components/bmp280_base/bmp280_base.cpp @@ -2,8 +2,6 @@ #include "esphome/core/hal.h" #include "esphome/core/log.h" -#define BMP280_ERROR_WRONG_CHIP_ID "Wrong chip ID or no response" - namespace esphome::bmp280_base { ESPHOME_LOG_TAG(TAG, "bmp280.sensor"); @@ -65,18 +63,15 @@ void BMP280Component::setup() { // Read the chip id twice, to work around a bug where the first read is 0. // https://community.st.com/t5/stm32-mcus-products/issue-with-reading-bmp280-chip-id-using-spi/td-p/691855 if (!this->bmp_read_byte(0xD0, &chip_id)) { - this->error_code_ = COMMUNICATION_FAILED; this->mark_failed(LOG_STR(ESP_LOG_MSG_COMM_FAIL)); return; } if (!this->bmp_read_byte(0xD0, &chip_id)) { - this->error_code_ = COMMUNICATION_FAILED; this->mark_failed(LOG_STR(ESP_LOG_MSG_COMM_FAIL)); return; } if (chip_id != 0x58) { - this->error_code_ = WRONG_CHIP_ID; - this->mark_failed(LOG_STR(BMP280_ERROR_WRONG_CHIP_ID)); + this->mark_failed(LOG_STR("Wrong chip ID or no response")); return; } @@ -130,17 +125,6 @@ void BMP280Component::setup() { } void BMP280Component::dump_config() { ESP_LOGCONFIG(TAG, "BMP280:"); - switch (this->error_code_) { - case COMMUNICATION_FAILED: - ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL); - break; - case WRONG_CHIP_ID: - ESP_LOGE(TAG, BMP280_ERROR_WRONG_CHIP_ID); - break; - case NONE: - default: - break; - } ESP_LOGCONFIG(TAG, " IIR Filter: %s", iir_filter_to_str(this->iir_filter_)); LOG_UPDATE_INTERVAL(this); diff --git a/esphome/components/bmp280_base/bmp280_base.h b/esphome/components/bmp280_base/bmp280_base.h index 860fff6b4b1..1c3fc9cf6cf 100644 --- a/esphome/components/bmp280_base/bmp280_base.h +++ b/esphome/components/bmp280_base/bmp280_base.h @@ -85,11 +85,6 @@ class BMP280Component : public PollingComponent { BMP280IIRFilter iir_filter_{BMP280_IIR_FILTER_OFF}; sensor::Sensor *temperature_sensor_{nullptr}; sensor::Sensor *pressure_sensor_{nullptr}; - enum ErrorCode { - NONE = 0, - COMMUNICATION_FAILED, - WRONG_CHIP_ID, - } error_code_{NONE}; }; } // namespace esphome::bmp280_base diff --git a/esphome/components/bmp3xx_base/bmp3xx_base.cpp b/esphome/components/bmp3xx_base/bmp3xx_base.cpp index b10a7a7a951..f59abc61ec3 100644 --- a/esphome/components/bmp3xx_base/bmp3xx_base.cpp +++ b/esphome/components/bmp3xx_base/bmp3xx_base.cpp @@ -43,35 +43,27 @@ static const LogString *iir_filter_to_str(IIRFilter filter) { } void BMP3XXComponent::setup() { - this->error_code_ = NONE; // Call the Device base class "initialise" function if (!reset()) { - ESP_LOGE(TAG, "Failed to reset"); - this->error_code_ = ERROR_SENSOR_RESET; - this->mark_failed(); + this->mark_failed(LOG_STR("Failed to reset")); + return; } if (!read_byte(BMP388_CHIP_ID, &this->chip_id_.reg)) { - ESP_LOGE(TAG, "Can't read chip id"); - this->error_code_ = ERROR_COMMUNICATION_FAILED; - this->mark_failed(); + this->mark_failed(LOG_STR(ESP_LOG_MSG_COMM_FAIL)); return; } ESP_LOGCONFIG(TAG, "Chip %s Id 0x%X", LOG_STR_ARG(chip_type_to_str(this->chip_id_.reg)), this->chip_id_.reg); if (chip_id_.reg != BMP388_ID && chip_id_.reg != BMP390_ID) { - ESP_LOGE(TAG, "Unknown chip id - is this really a BMP388 or BMP390?"); - this->error_code_ = ERROR_WRONG_CHIP_ID; - this->mark_failed(); + this->mark_failed(LOG_STR("Wrong chip ID - please check if you are really using a BMP 388 or BMP 390")); return; } // set sensor in sleep mode stop_conversion(); // Read the calibration parameters into the params structure if (!read_bytes(BMP388_TRIM_PARAMS, (uint8_t *) &compensation_params_, sizeof(compensation_params_))) { - ESP_LOGE(TAG, "Can't read calibration data"); - this->error_code_ = ERROR_COMMUNICATION_FAILED; - this->mark_failed(); + this->mark_failed(LOG_STR("Can't read calibration data")); return; } compensation_float_params_.param_T1 = @@ -92,9 +84,7 @@ void BMP3XXComponent::setup() { // Initialise the BMP388 IIR filter register if (!set_iir_filter(this->iir_filter_)) { - ESP_LOGE(TAG, "Failed to set IIR filter"); - this->error_code_ = ERROR_COMMUNICATION_FAILED; - this->mark_failed(); + this->mark_failed(LOG_STR("Failed to set IIR filter")); return; } @@ -113,9 +103,7 @@ void BMP3XXComponent::setup() { } // Initialise the BMP388 oversampling register if (!set_oversampling_register(this->pressure_oversampling_, this->temperature_oversampling_)) { - ESP_LOGE(TAG, "Failed to set oversampling register"); - this->error_code_ = ERROR_COMMUNICATION_FAILED; - this->mark_failed(); + this->mark_failed(LOG_STR("Failed to set oversampling register")); return; } } @@ -125,23 +113,6 @@ void BMP3XXComponent::dump_config() { "BMP3XX:\n" " Type: %s (0x%X)", LOG_STR_ARG(chip_type_to_str(this->chip_id_.reg)), this->chip_id_.reg); - switch (this->error_code_) { - case NONE: - break; - case ERROR_COMMUNICATION_FAILED: - ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL); - break; - case ERROR_WRONG_CHIP_ID: - ESP_LOGE(TAG, "Wrong chip ID (reported id: 0x%X) - please check if you are really using a BMP 388 or BMP 390", - this->chip_id_.reg); - break; - case ERROR_SENSOR_RESET: - ESP_LOGE(TAG, "Failed to reset"); - break; - default: - ESP_LOGE(TAG, "Error code %d", (int) this->error_code_); - break; - } ESP_LOGCONFIG(TAG, " IIR Filter: %s", LOG_STR_ARG(iir_filter_to_str(this->iir_filter_))); LOG_UPDATE_INTERVAL(this); if (this->temperature_sensor_) { @@ -165,8 +136,7 @@ void BMP3XXComponent::update() { meas_time += 2.02f * oversampling_to_time(this->pressure_oversampling_) + 0.392f; meas_time += 0.234f; if (!set_mode(FORCED_MODE)) { - ESP_LOGE(TAG, "Failed start forced mode"); - this->mark_failed(); + this->mark_failed(LOG_STR("Failed start forced mode")); return; } diff --git a/esphome/components/bmp3xx_base/bmp3xx_base.h b/esphome/components/bmp3xx_base/bmp3xx_base.h index cd70b2fb163..c3e30890411 100644 --- a/esphome/components/bmp3xx_base/bmp3xx_base.h +++ b/esphome/components/bmp3xx_base/bmp3xx_base.h @@ -124,13 +124,6 @@ class BMP3XXComponent : public PollingComponent { OperationMode operation_mode_{FORCED_MODE}; sensor::Sensor *temperature_sensor_{nullptr}; sensor::Sensor *pressure_sensor_{nullptr}; - enum ErrorCode { - NONE = 0, - ERROR_COMMUNICATION_FAILED, - ERROR_WRONG_CHIP_ID, - ERROR_SENSOR_STATUS, - ERROR_SENSOR_RESET, - } error_code_{NONE}; struct { // The BMP388 compensation trim parameters (coefficients) uint16_t param_T1; diff --git a/esphome/components/bmp581_base/bmp581_base.cpp b/esphome/components/bmp581_base/bmp581_base.cpp index 29f67fadc51..7bc743977b2 100644 --- a/esphome/components/bmp581_base/bmp581_base.cpp +++ b/esphome/components/bmp581_base/bmp581_base.cpp @@ -38,29 +38,6 @@ static const LogString *iir_filter_to_str(IIRFilter filter) { void BMP581Component::dump_config() { ESP_LOGCONFIG(TAG, "BMP581:"); - switch (this->error_code_) { - case NONE: - break; - case ERROR_COMMUNICATION_FAILED: - ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL); - break; - case ERROR_WRONG_CHIP_ID: - ESP_LOGE(TAG, "Unknown chip ID"); - break; - case ERROR_SENSOR_RESET: - ESP_LOGE(TAG, "Reset failed"); - break; - case ERROR_SENSOR_STATUS: - ESP_LOGE(TAG, "Get status failed"); - break; - case ERROR_PRIME_IIR_FAILED: - ESP_LOGE(TAG, "IIR Filter failed to prime with initial measurement"); - break; - default: - ESP_LOGE(TAG, "Error %d", (int) this->error_code_); - break; - } - LOG_UPDATE_INTERVAL(this); ESP_LOGCONFIG(TAG, " Measurement conversion time: %ums", this->conversion_time_); @@ -96,17 +73,13 @@ void BMP581Component::setup() { * 6) Configure and prime IIR Filter(s), if enabled */ - this->error_code_ = NONE; //////////////////// // 1) Soft reboot // //////////////////// // Power-On-Reboot bit is asserted if sensor successfully reset if (!this->reset_()) { - ESP_LOGE(TAG, "Reset failed"); - - this->error_code_ = ERROR_SENSOR_RESET; - this->mark_failed(); + this->mark_failed(LOG_STR("Reset failed")); return; } @@ -119,20 +92,14 @@ void BMP581Component::setup() { // read chip id from sensor if (!this->bmp_read_byte(BMP581_CHIP_ID, &chip_id)) { - ESP_LOGE(TAG, "Read chip ID failed"); - - this->error_code_ = ERROR_COMMUNICATION_FAILED; - this->mark_failed(); + this->mark_failed(LOG_STR("Read chip ID failed")); return; } // verify id if (chip_id != BMP581_ASIC_ID && chip_id != BMP585_ASIC_ID) { - ESP_LOGE(TAG, "Unknown chip ID"); - - this->error_code_ = ERROR_WRONG_CHIP_ID; - this->mark_failed(); + this->mark_failed(LOG_STR("Unknown chip ID")); return; } @@ -142,30 +109,21 @@ void BMP581Component::setup() { //////////////////////////////////////////////////// if (!this->bmp_read_byte(BMP581_STATUS, &this->status_.reg)) { - ESP_LOGE(TAG, "Failed to read status register"); - - this->error_code_ = ERROR_COMMUNICATION_FAILED; - this->mark_failed(); + this->mark_failed(LOG_STR("Failed to read status register")); return; } // verify status_nvm_rdy bit (it is asserted if boot was successful) if (!(this->status_.bit.status_nvm_rdy)) { - ESP_LOGE(TAG, "NVM not ready"); - - this->error_code_ = ERROR_SENSOR_STATUS; - this->mark_failed(); + this->mark_failed(LOG_STR("NVM not ready")); return; } // verify status_nvm_err bit (it is asserted if an error is detected) if (this->status_.bit.status_nvm_err) { - ESP_LOGE(TAG, "NVM error detected"); - - this->error_code_ = ERROR_SENSOR_STATUS; - this->mark_failed(); + this->mark_failed(LOG_STR("NVM error detected")); return; } @@ -176,10 +134,7 @@ void BMP581Component::setup() { // enable the data ready interrupt source if (!this->write_interrupt_source_settings_(true)) { - ESP_LOGE(TAG, "Failed to write interrupt source register"); - - this->error_code_ = ERROR_COMMUNICATION_FAILED; - this->mark_failed(); + this->mark_failed(LOG_STR("Failed to write interrupt source register")); return; } @@ -198,10 +153,7 @@ void BMP581Component::setup() { // write oversampling settings if (!this->write_oversampling_settings_(this->temperature_oversampling_, this->pressure_oversampling_)) { - ESP_LOGE(TAG, "Failed to write oversampling register"); - - this->error_code_ = ERROR_COMMUNICATION_FAILED; - this->mark_failed(); + this->mark_failed(LOG_STR("Failed to write oversampling register")); return; } @@ -218,19 +170,13 @@ void BMP581Component::setup() { if ((this->iir_temperature_level_ != IIR_FILTER_OFF) || (this->iir_pressure_level_ != IIR_FILTER_OFF)) { if (!this->write_iir_settings_(this->iir_temperature_level_, this->iir_pressure_level_)) { - ESP_LOGE(TAG, "Failed to write IIR configuration registers"); - - this->error_code_ = ERROR_COMMUNICATION_FAILED; - this->mark_failed(); + this->mark_failed(LOG_STR("Failed to write IIR configuration registers")); return; } if (!this->prime_iir_filter_()) { - ESP_LOGE(TAG, "Failed to prime the IIR filter with an initial measurement"); - - this->error_code_ = ERROR_PRIME_IIR_FAILED; - this->mark_failed(); + this->mark_failed(LOG_STR("Failed to prime the IIR filter with an initial measurement")); return; } diff --git a/esphome/components/bmp581_base/bmp581_base.h b/esphome/components/bmp581_base/bmp581_base.h index 1a73a91558c..abd7f64b4d2 100644 --- a/esphome/components/bmp581_base/bmp581_base.h +++ b/esphome/components/bmp581_base/bmp581_base.h @@ -133,15 +133,6 @@ class BMP581Component : public PollingComponent { // Sets the power mode on the BMP581 by writing to the ODR register bool write_power_mode_(OperationMode mode); - enum ErrorCode { - NONE = 0, - ERROR_COMMUNICATION_FAILED, - ERROR_WRONG_CHIP_ID, - ERROR_SENSOR_STATUS, - ERROR_SENSOR_RESET, - ERROR_PRIME_IIR_FAILED - } error_code_{NONE}; - // BMP581's interrupt source register (address 0x15) to configure which interrupts are enabled (page 54 of datasheet) union { struct { diff --git a/esphome/components/bq27220/bq27220.cpp b/esphome/components/bq27220/bq27220.cpp index d2f7961e020..b7912b930e6 100644 --- a/esphome/components/bq27220/bq27220.cpp +++ b/esphome/components/bq27220/bq27220.cpp @@ -48,7 +48,7 @@ void BQ27220Component::setup() { } if (device_number != DEVICE_NUMBER) { ESP_LOGE(TAG, "Unexpected device number 0x%04X (expected 0x%04X)", device_number, DEVICE_NUMBER); - this->mark_failed(); + this->mark_failed(LOG_STR("Unexpected device number")); } } @@ -76,9 +76,6 @@ void BQ27220Component::update() { void BQ27220Component::dump_config() { ESP_LOGCONFIG(TAG, "BQ27220 Battery Fuel Gauge:"); LOG_I2C_DEVICE(this); - if (this->is_failed()) { - ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL); - } LOG_UPDATE_INTERVAL(this); LOG_SENSOR(" ", "Voltage", this->voltage_sensor_); LOG_SENSOR(" ", "Current", this->current_sensor_); diff --git a/esphome/components/cap1188/cap1188.cpp b/esphome/components/cap1188/cap1188.cpp index a5cb791c2e6..88ebdf44d33 100644 --- a/esphome/components/cap1188/cap1188.cpp +++ b/esphome/components/cap1188/cap1188.cpp @@ -37,8 +37,7 @@ void CAP1188Component::finish_setup_() { this->read_byte(CAP1188_REVISION, &this->cap1188_revision_); if ((this->cap1188_product_id_ != 0x50) || (this->cap1188_manufacture_id_ != 0x5D)) { - this->error_code_ = COMMUNICATION_FAILED; - this->mark_failed(); + this->mark_failed(LOG_STR("Product ID or Manufacture ID of the connected device does not match a known CAP1188")); return; } @@ -68,15 +67,6 @@ void CAP1188Component::dump_config() { this->cap1188_product_id_, this->cap1188_manufacture_id_, this->cap1188_revision_); LOG_I2C_DEVICE(this); LOG_PIN(" Reset Pin: ", this->reset_pin_); - - switch (this->error_code_) { - case COMMUNICATION_FAILED: - ESP_LOGE(TAG, "Product ID or Manufacture ID of the connected device does not match a known CAP1188."); - break; - case NONE: - default: - break; - } } void CAP1188Component::loop() { diff --git a/esphome/components/cap1188/cap1188.h b/esphome/components/cap1188/cap1188.h index a4abb270e70..daa2c4015f6 100644 --- a/esphome/components/cap1188/cap1188.h +++ b/esphome/components/cap1188/cap1188.h @@ -59,11 +59,6 @@ class CAP1188Component final : public Component, public i2c::I2CDevice { uint8_t cap1188_product_id_{0}; uint8_t cap1188_manufacture_id_{0}; uint8_t cap1188_revision_{0}; - - enum ErrorCode { - NONE = 0, - COMMUNICATION_FAILED, - } error_code_{NONE}; }; } // namespace esphome::cap1188 diff --git a/esphome/components/ccs811/ccs811.cpp b/esphome/components/ccs811/ccs811.cpp index 126ed0049d1..1cae581194f 100644 --- a/esphome/components/ccs811/ccs811.cpp +++ b/esphome/components/ccs811/ccs811.cpp @@ -10,29 +10,28 @@ ESPHOME_LOG_TAG(TAG, "ccs811"); // based on // - https://cdn.sparkfun.com/datasheets/BreakoutBoards/CCS811_Programming_Guide.pdf -#define CHECK_TRUE(f, error_code) \ +#define CHECK_TRUE(f, message) \ if (!(f)) { \ - this->mark_failed(); \ - this->error_code_ = (error_code); \ + this->mark_failed(LOG_STR(message)); \ return; \ } -#define CHECKED_IO(f) CHECK_TRUE(f, COMMUNICATION_FAILED) +#define CHECKED_IO(f) CHECK_TRUE(f, ESP_LOG_MSG_COMM_FAIL) void CCS811Component::setup() { // page 9 programming guide - hwid is always 0x81 uint8_t hw_id; CHECKED_IO(this->read_byte(0x20, &hw_id)) - CHECK_TRUE(hw_id == 0x81, INVALID_ID) + CHECK_TRUE(hw_id == 0x81, "Sensor reported an invalid ID. Is this a CCS811?") // software reset, page 3 - allowed to fail this->write_bytes(0xFF, {0x11, 0xE5, 0x72, 0x8A}); delay(5); // page 10, APP_START - CHECK_TRUE(!this->status_has_error_(), SENSOR_REPORTED_ERROR) - CHECK_TRUE(this->status_app_is_valid_(), APP_INVALID) - CHECK_TRUE(this->write_bytes(0xF4, {}), APP_START_FAILED) + CHECK_TRUE(!this->status_has_error_(), "Sensor reported internal error") + CHECK_TRUE(this->status_app_is_valid_(), "Sensor reported invalid APP installed") + CHECK_TRUE(this->write_bytes(0xF4, {}), "Sensor reported APP start failed") // App setup, wait for it to load delay(1); @@ -160,29 +159,6 @@ void CCS811Component::dump_config() { } else { ESP_LOGCONFIG(TAG, " Baseline: NOT SET"); } - if (this->is_failed()) { - switch (this->error_code_) { - case COMMUNICATION_FAILED: - ESP_LOGW(TAG, ESP_LOG_MSG_COMM_FAIL); - break; - case INVALID_ID: - ESP_LOGW(TAG, "Sensor reported an invalid ID. Is this a CCS811?"); - break; - case SENSOR_REPORTED_ERROR: - ESP_LOGW(TAG, "Sensor reported internal error"); - break; - case APP_INVALID: - ESP_LOGW(TAG, "Sensor reported invalid APP installed."); - break; - case APP_START_FAILED: - ESP_LOGW(TAG, "Sensor reported APP start failed."); - break; - case UNKNOWN: - default: - ESP_LOGW(TAG, "Unknown setup error!"); - break; - } - } } } // namespace esphome::ccs811 diff --git a/esphome/components/ccs811/ccs811.h b/esphome/components/ccs811/ccs811.h index fb83f842fd9..d7a8f22f116 100644 --- a/esphome/components/ccs811/ccs811.h +++ b/esphome/components/ccs811/ccs811.h @@ -31,15 +31,6 @@ class CCS811Component final : public PollingComponent, public i2c::I2CDevice { bool status_has_data_() { return this->read_status_().value_or(0) & (1 << 3); } void send_env_data_(); - enum ErrorCode { - UNKNOWN, - COMMUNICATION_FAILED, - INVALID_ID, - SENSOR_REPORTED_ERROR, - APP_INVALID, - APP_START_FAILED, - } error_code_{UNKNOWN}; - sensor::Sensor *co2_{nullptr}; sensor::Sensor *tvoc_{nullptr}; text_sensor::TextSensor *version_{nullptr}; diff --git a/esphome/components/ch422g/ch422g.cpp b/esphome/components/ch422g/ch422g.cpp index 28b355a4f1d..e76801ff77a 100644 --- a/esphome/components/ch422g/ch422g.cpp +++ b/esphome/components/ch422g/ch422g.cpp @@ -18,7 +18,7 @@ void CH422GComponent::setup() { // Set mode and check for errors if (!this->set_mode_(this->mode_value_) || !this->read_inputs_()) { ESP_LOGE(TAG, "CH422G not detected at 0x%02X", this->address_); - this->mark_failed(); + this->mark_failed(LOG_STR(ESP_LOG_MSG_COMM_FAIL)); return; } @@ -34,9 +34,6 @@ void CH422GComponent::loop() { void CH422GComponent::dump_config() { ESP_LOGCONFIG(TAG, "CH422G:"); LOG_I2C_DEVICE(this) - if (this->is_failed()) { - ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL); - } } void CH422GComponent::pin_mode(uint8_t pin, gpio::Flags flags) { diff --git a/esphome/components/ch423/ch423.cpp b/esphome/components/ch423/ch423.cpp index b7e9d224faf..3a57f646d4e 100644 --- a/esphome/components/ch423/ch423.cpp +++ b/esphome/components/ch423/ch423.cpp @@ -24,8 +24,7 @@ void CH423Component::setup() { success = this->read_inputs_(); } if (!success) { - ESP_LOGE(TAG, "CH423 not detected"); - this->mark_failed(); + this->mark_failed(LOG_STR("CH423 not detected")); return; } @@ -38,12 +37,7 @@ void CH423Component::loop() { this->pin_read_flags_ = 0x00; } -void CH423Component::dump_config() { - ESP_LOGCONFIG(TAG, "CH423:"); - if (this->is_failed()) { - ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL); - } -} +void CH423Component::dump_config() { ESP_LOGCONFIG(TAG, "CH423:"); } void CH423Component::pin_mode(uint8_t pin, gpio::Flags flags) { if (pin < 8) { diff --git a/esphome/components/cm1106/cm1106.cpp b/esphome/components/cm1106/cm1106.cpp index edfcd0f7309..55b7556cda5 100644 --- a/esphome/components/cm1106/cm1106.cpp +++ b/esphome/components/cm1106/cm1106.cpp @@ -27,8 +27,7 @@ uint8_t cm1106_checksum(const uint8_t *response, size_t len) { void CM1106Component::setup() { uint8_t response[8] = {0}; if (!this->cm1106_write_command_(C_M1106_CMD_GET_CO2, sizeof(C_M1106_CMD_GET_CO2), response, sizeof(response))) { - ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL); - this->mark_failed(); + this->mark_failed(LOG_STR(ESP_LOG_MSG_COMM_FAIL)); return; } @@ -139,9 +138,6 @@ void CM1106Component::dump_config() { " ABC baseline: %uppm", ONOFF(this->abc_boot_logic_ == CM1106_ABC_ENABLED), this->abc_cycle_, this->abc_baseline_); } - if (this->is_failed()) { - ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL); - } } } // namespace esphome::cm1106 diff --git a/esphome/components/cse7761/cse7761.cpp b/esphome/components/cse7761/cse7761.cpp index 4ba93af01f1..0c56c741fe6 100644 --- a/esphome/components/cse7761/cse7761.cpp +++ b/esphome/components/cse7761/cse7761.cpp @@ -67,15 +67,12 @@ void CSE7761Component::setup() { ESP_LOGD(TAG, "CSE7761 found"); this->data_.ready = true; } else { - this->mark_failed(); + this->mark_failed(LOG_STR(ESP_LOG_MSG_COMM_FAIL)); } } void CSE7761Component::dump_config() { ESP_LOGCONFIG(TAG, "CSE7761:"); - if (this->is_failed()) { - ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL); - } ESP_LOGCONFIG(TAG, " Current input: %s", LOG_STR_ARG(this->is_current_transformer_() ? LOG_STR("current transformer") : LOG_STR("shunt"))); LOG_UPDATE_INTERVAL(this); diff --git a/esphome/components/dac7678/dac7678_output.cpp b/esphome/components/dac7678/dac7678_output.cpp index d1a076733aa..de61964a8ff 100644 --- a/esphome/components/dac7678/dac7678_output.cpp +++ b/esphome/components/dac7678/dac7678_output.cpp @@ -23,8 +23,7 @@ void DAC7678Output::setup() { // Reset device if (!this->write_byte_16(DAC7678_REG_SOFTWARE_RESET, 0x0000)) { - ESP_LOGE(TAG, "Reset failed"); - this->mark_failed(); + this->mark_failed(LOG_STR("Reset failed")); return; } else { ESP_LOGV(TAG, "Reset succeeded"); @@ -35,8 +34,7 @@ void DAC7678Output::setup() { // Set internal reference if (this->internal_reference_) { if (!this->write_byte_16(DAC7678_REG_INTERNAL_REF_0, 1 << 4)) { - ESP_LOGE(TAG, "Set internal reference failed"); - this->mark_failed(); + this->mark_failed(LOG_STR("Set internal reference failed")); return; } else { ESP_LOGV(TAG, "Internal reference enabled"); @@ -45,9 +43,7 @@ void DAC7678Output::setup() { } void DAC7678Output::dump_config() { - if (this->is_failed()) { - ESP_LOGE(TAG, "Setting up DAC7678 failed!"); - } else { + if (!this->is_failed()) { ESP_LOGCONFIG(TAG, "DAC7678 initialised"); } } diff --git a/esphome/components/dht12/dht12.cpp b/esphome/components/dht12/dht12.cpp index e099b65d5b7..353546dba3a 100644 --- a/esphome/components/dht12/dht12.cpp +++ b/esphome/components/dht12/dht12.cpp @@ -35,16 +35,13 @@ void DHT12Component::update() { void DHT12Component::setup() { uint8_t data[5]; if (!this->read_data_(data)) { - this->mark_failed(); + this->mark_failed(LOG_STR(ESP_LOG_MSG_COMM_FAIL)); return; } } void DHT12Component::dump_config() { ESP_LOGD(TAG, "DHT12:"); LOG_I2C_DEVICE(this); - if (this->is_failed()) { - ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL); - } LOG_SENSOR(" ", "Temperature", this->temperature_sensor_); LOG_SENSOR(" ", "Humidity", this->humidity_sensor_); } diff --git a/esphome/components/display/display.h b/esphome/components/display/display.h index f17b7d86f71..b43a3221518 100644 --- a/esphome/components/display/display.h +++ b/esphome/components/display/display.h @@ -364,6 +364,10 @@ class Display : public PollingComponent { * \param x_pad How many pixels are in each line after the end of the pixels to be copied. * * The length of each source buffer line (stride) will be x_offset + w + x_pad. + * + * An override that copies the block as is must send any other format, byte order, a software + * rotation or an active clipping rectangle through this base version, which draws per pixel. + * The block must lie on the screen. */ virtual void draw_pixels_at(int x_start, int y_start, int w, int h, const uint8_t *ptr, ColorOrder order, ColorBitness bitness, bool big_endian, int x_offset, int y_offset, int x_pad); diff --git a/esphome/components/dps310/dps310.cpp b/esphome/components/dps310/dps310.cpp index 080f101747f..6ab786e7c71 100644 --- a/esphome/components/dps310/dps310.cpp +++ b/esphome/components/dps310/dps310.cpp @@ -9,13 +9,17 @@ ESPHOME_LOG_TAG(TAG, "dps310"); static constexpr uint32_t READ_RETRY_TIMEOUT_ID = 0; void DPS310Component::setup() { + if (!this->setup_registers_()) + this->mark_failed(LOG_STR(ESP_LOG_MSG_COMM_FAIL)); +} + +bool DPS310Component::setup_registers_() { uint8_t coef_data_raw[DPS310_NUM_COEF_REGS]; auto timer = DPS310_INIT_TIMEOUT; uint8_t reg = 0; // first, reset the sensor if (!this->write_byte(DPS310_REG_RESET, DPS310_CMD_RESET)) { - this->mark_failed(); - return; + return false; } delay(10); // wait for the sensor and its coefficients to be ready @@ -25,40 +29,32 @@ void DPS310Component::setup() { } if (!(reg & DPS310_BIT_SENSOR_RDY) || !(reg & DPS310_BIT_COEF_RDY)) { // the flags were not set in time - this->mark_failed(); - return; + return false; } // read device ID if (!this->read_byte(DPS310_REG_PROD_REV_ID, &this->prod_rev_id_)) { - this->mark_failed(); - return; + return false; } // read in coefficients used to calculate the compensated pressure and temperature values if (!this->read_bytes(DPS310_REG_COEF, coef_data_raw, DPS310_NUM_COEF_REGS)) { - this->mark_failed(); - return; + return false; } // read in coefficients source register, too -- we need this a few lines down if (!this->read_byte(DPS310_REG_TMP_COEF_SRC, ®)) { - this->mark_failed(); - return; + return false; } // set up operational stuff if (!this->write_byte(DPS310_REG_PRS_CFG, DPS310_VAL_PRS_CFG)) { - this->mark_failed(); - return; + return false; } if (!this->write_byte(DPS310_REG_TMP_CFG, DPS310_VAL_TMP_CFG | (reg & DPS310_BIT_TMP_COEF_SRC))) { - this->mark_failed(); - return; + return false; } if (!this->write_byte(DPS310_REG_CFG, DPS310_VAL_REG_CFG)) { - this->mark_failed(); - return; + return false; } if (!this->write_byte(DPS310_REG_MEAS_CFG, 0x07)) { // enable background mode - this->mark_failed(); - return; + return false; } this->c0_ = // we only ever use c0/2, so just divide by 2 here to save time later @@ -82,6 +78,7 @@ void DPS310Component::setup() { this->c20_ = int16_t(((uint16_t) coef_data_raw[12] << 8) | (uint16_t) coef_data_raw[13]); this->c21_ = int16_t(((uint16_t) coef_data_raw[14] << 8) | (uint16_t) coef_data_raw[15]); this->c30_ = int16_t(((uint16_t) coef_data_raw[16] << 8) | (uint16_t) coef_data_raw[17]); + return true; } void DPS310Component::dump_config() { @@ -91,9 +88,6 @@ void DPS310Component::dump_config() { " Revision ID: %u", this->prod_rev_id_ & 0x0F, (this->prod_rev_id_ >> 4) & 0x0F); LOG_I2C_DEVICE(this); - if (this->is_failed()) { - ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL); - } LOG_UPDATE_INTERVAL(this); LOG_SENSOR(" ", "Temperature", this->temperature_sensor_); LOG_SENSOR(" ", "Pressure", this->pressure_sensor_); diff --git a/esphome/components/dps310/dps310.h b/esphome/components/dps310/dps310.h index 4dd23985d75..d761940ca99 100644 --- a/esphome/components/dps310/dps310.h +++ b/esphome/components/dps310/dps310.h @@ -45,6 +45,8 @@ class DPS310Component final : public PollingComponent, public i2c::I2CDevice { void set_pressure_sensor(sensor::Sensor *pressure_sensor) { pressure_sensor_ = pressure_sensor; } protected: + /// Configure the sensor; false on a failure. + bool setup_registers_(); void read_(); void read_pressure_(); void read_temperature_(); diff --git a/esphome/components/ds1307/ds1307.cpp b/esphome/components/ds1307/ds1307.cpp index 4ddef04f413..485e0f4b5c1 100644 --- a/esphome/components/ds1307/ds1307.cpp +++ b/esphome/components/ds1307/ds1307.cpp @@ -10,7 +10,7 @@ ESPHOME_LOG_TAG(TAG, "ds1307"); void DS1307Component::setup() { if (!this->read_rtc_()) { - this->mark_failed(); + this->mark_failed(LOG_STR(ESP_LOG_MSG_COMM_FAIL)); } } @@ -19,9 +19,6 @@ void DS1307Component::update() { this->read_time(); } void DS1307Component::dump_config() { ESP_LOGCONFIG(TAG, "DS1307:"); LOG_I2C_DEVICE(this); - if (this->is_failed()) { - ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL); - } RealTimeClock::dump_config(); } diff --git a/esphome/components/ds3231/ds3231.cpp b/esphome/components/ds3231/ds3231.cpp index 4f7396fcbdd..72cae0e3832 100644 --- a/esphome/components/ds3231/ds3231.cpp +++ b/esphome/components/ds3231/ds3231.cpp @@ -29,10 +29,6 @@ void DS3231Component::setup() { void DS3231Component::dump_config() { ESP_LOGCONFIG(TAG, "DS3231:"); LOG_I2C_DEVICE(this); - if (this->is_failed()) { - ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL); - return; - } } bool DS3231Component::read_temperature(float &out) { diff --git a/esphome/components/emc2101/emc2101.cpp b/esphome/components/emc2101/emc2101.cpp index 6a21b4e7b8e..c6ae5169e4c 100644 --- a/esphome/components/emc2101/emc2101.cpp +++ b/esphome/components/emc2101/emc2101.cpp @@ -60,7 +60,7 @@ void Emc2101Component::setup() { uint8_t chip_id = reg(EMC2101_REGISTER_WHOAMI).get(); if ((chip_id != EMC2101_CHIP_ID) && (chip_id != EMC2101_ALT_CHIP_ID)) { ESP_LOGE(TAG, "Wrong chip ID %02X", chip_id); - this->mark_failed(); + this->mark_failed(LOG_STR("Wrong chip ID")); return; } @@ -90,9 +90,6 @@ void Emc2101Component::setup() { void Emc2101Component::dump_config() { ESP_LOGCONFIG(TAG, "Emc2101 component:"); LOG_I2C_DEVICE(this); - if (this->is_failed()) { - ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL); - } ESP_LOGCONFIG(TAG, " Mode: %s", this->dac_mode_ ? LOG_STR_LITERAL("DAC") : LOG_STR_LITERAL("PWM")); if (this->dac_mode_) { ESP_LOGCONFIG(TAG, " DAC Conversion Rate: %X", this->dac_conversion_rate_); diff --git a/esphome/components/ens160_base/ens160_base.cpp b/esphome/components/ens160_base/ens160_base.cpp index b06182e59c0..b18a44f7523 100644 --- a/esphome/components/ens160_base/ens160_base.cpp +++ b/esphome/components/ens160_base/ens160_base.cpp @@ -62,20 +62,17 @@ void ENS160Component::setup() { // check part_id uint16_t part_id; if (!this->read_bytes(ENS160_REG_PART_ID, reinterpret_cast(&part_id), 2)) { - this->error_code_ = COMMUNICATION_FAILED; - this->mark_failed(); + this->mark_failed(LOG_STR(ESP_LOG_MSG_COMM_FAIL)); return; } if (part_id != ENS160_PART_ID) { - this->error_code_ = INVALID_ID; - this->mark_failed(); + this->mark_failed(LOG_STR("Sensor reported an invalid ID. Is this a ENS160?")); return; } // set mode to reset if (!this->write_byte(ENS160_REG_OPMODE, ENS160_OPMODE_RESET)) { - this->error_code_ = WRITE_FAILED; - this->mark_failed(); + this->mark_failed(LOG_STR(ESP_LOG_MSG_COMM_FAIL)); return; } delay(ENS160_BOOTING); @@ -83,51 +80,44 @@ void ENS160Component::setup() { // check status uint8_t status_value; if (!this->read_byte(ENS160_REG_DATA_STATUS, &status_value)) { - this->error_code_ = READ_FAILED; - this->mark_failed(); + this->mark_failed(LOG_STR(ESP_LOG_MSG_COMM_FAIL)); return; } this->validity_flag_ = static_cast((ENS160_DATA_STATUS_VALIDITY & status_value) >> 2); if (this->validity_flag_ == INVALID_OUTPUT) { - this->error_code_ = VALIDITY_INVALID; - this->mark_failed(); + this->mark_failed(LOG_STR("Invalid Device Status - No valid output")); return; } // set mode to idle if (!this->write_byte(ENS160_REG_OPMODE, ENS160_OPMODE_IDLE)) { - this->error_code_ = WRITE_FAILED; - this->mark_failed(); + this->mark_failed(LOG_STR(ESP_LOG_MSG_COMM_FAIL)); return; } delay(ENS160_BOOTING); // clear command if (!this->write_byte(ENS160_REG_COMMAND, ENS160_COMMAND_NOP)) { - this->error_code_ = WRITE_FAILED; - this->mark_failed(); + this->mark_failed(LOG_STR(ESP_LOG_MSG_COMM_FAIL)); return; } if (!this->write_byte(ENS160_REG_COMMAND, ENS160_COMMAND_CLRGPR)) { - this->error_code_ = WRITE_FAILED; - this->mark_failed(); + this->mark_failed(LOG_STR(ESP_LOG_MSG_COMM_FAIL)); return; } delay(ENS160_BOOTING); // read firmware version if (!this->write_byte(ENS160_REG_COMMAND, ENS160_COMMAND_GET_APPVER)) { - this->error_code_ = WRITE_FAILED; - this->mark_failed(); + this->mark_failed(LOG_STR(ESP_LOG_MSG_COMM_FAIL)); return; } delay(ENS160_BOOTING); uint8_t version_data[3]; if (!this->read_bytes(ENS160_REG_GPR_READ_4, version_data, 3)) { - this->error_code_ = READ_FAILED; - this->mark_failed(); + this->mark_failed(LOG_STR(ESP_LOG_MSG_COMM_FAIL)); return; } this->firmware_ver_major_ = version_data[0]; @@ -136,22 +126,19 @@ void ENS160Component::setup() { // set mode to standard if (!this->write_byte(ENS160_REG_OPMODE, ENS160_OPMODE_STD)) { - this->error_code_ = WRITE_FAILED; - this->mark_failed(); + this->mark_failed(LOG_STR(ESP_LOG_MSG_COMM_FAIL)); return; } // read opmode and check standard mode is achieved before finishing Setup uint8_t op_mode; if (!this->read_byte(ENS160_REG_OPMODE, &op_mode)) { - this->error_code_ = READ_FAILED; - this->mark_failed(); + this->mark_failed(LOG_STR(ESP_LOG_MSG_COMM_FAIL)); return; } if (op_mode != ENS160_OPMODE_STD) { - this->error_code_ = STD_OPMODE_FAILED; - this->mark_failed(); + this->mark_failed(LOG_STR("Device failed to achieve Standard Operating Mode")); return; } } @@ -291,29 +278,8 @@ void ENS160Component::send_env_data_() { void ENS160Component::dump_config() { ESP_LOGCONFIG(TAG, "ENS160:"); - - switch (this->error_code_) { - case COMMUNICATION_FAILED: - ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL); - break; - case READ_FAILED: - ESP_LOGE(TAG, "Error reading from register"); - break; - case WRITE_FAILED: - ESP_LOGE(TAG, "Error writing to register"); - break; - case INVALID_ID: - ESP_LOGE(TAG, "Sensor reported an invalid ID. Is this a ENS160?"); - break; - case VALIDITY_INVALID: - ESP_LOGE(TAG, "Invalid Device Status - No valid output"); - break; - case STD_OPMODE_FAILED: - ESP_LOGE(TAG, "Device failed to achieve Standard Operating Mode"); - break; - case NONE: - ESP_LOGD(TAG, "Setup successful"); - break; + if (!this->is_failed()) { + ESP_LOGD(TAG, "Setup successful"); } ESP_LOGI(TAG, "Firmware Version: %d.%d.%d", this->firmware_ver_major_, this->firmware_ver_minor_, this->firmware_ver_build_); diff --git a/esphome/components/ens160_base/ens160_base.h b/esphome/components/ens160_base/ens160_base.h index f42272684ed..d844dccb02c 100644 --- a/esphome/components/ens160_base/ens160_base.h +++ b/esphome/components/ens160_base/ens160_base.h @@ -21,16 +21,6 @@ class ENS160Component : public PollingComponent, public sensor::Sensor { protected: void send_env_data_(); - enum ErrorCode { - NONE = 0, - COMMUNICATION_FAILED, - INVALID_ID, - VALIDITY_INVALID, - READ_FAILED, - WRITE_FAILED, - STD_OPMODE_FAILED, - } error_code_{NONE}; - enum ValidityFlag { NORMAL_OPERATION = 0, WARMING_UP, diff --git a/esphome/components/ens210/ens210.cpp b/esphome/components/ens210/ens210.cpp index 5815fc90382..2251e0a574b 100644 --- a/esphome/components/ens210/ens210.cpp +++ b/esphome/components/ens210/ens210.cpp @@ -55,8 +55,6 @@ static const LogString *ens210_status_to_human(int status) { return LOG_STR("Invalid data"); case ENS210Component::ENS210_STATUS_OK: return LOG_STR("Status OK"); - case ENS210Component::ENS210_WRONG_CHIP_ID: - return LOG_STR("ENS210 has wrong chip ID! Is it a ENS210?"); default: return LOG_STR("Unknown"); } @@ -93,8 +91,7 @@ void ENS210Component::setup() { // Reset if (!this->write_byte(ENS210_REGISTER_SYS_CTRL, 0x80)) { this->write_byte(ENS210_REGISTER_SYS_CTRL, 0x80); - this->error_code_ = ENS210_STATUS_I2C_ERROR; - this->mark_failed(); + this->mark_failed(LOG_STR(ESP_LOG_MSG_COMM_FAIL)); return; } // Wait to boot after reset @@ -103,24 +100,24 @@ void ENS210Component::setup() { if (!set_low_power_(false)) { // Try to go back to default mode (low power enabled) set_low_power_(true); - this->error_code_ = ENS210_STATUS_I2C_ERROR; - this->mark_failed(); + this->mark_failed(LOG_STR(ESP_LOG_MSG_COMM_FAIL)); return; } // Read the PART_ID if (!this->read_bytes(ENS210_REGISTER_PART_ID, data, 2)) { // Try to go back to default mode (low power enabled) set_low_power_(true); - this->error_code_ = ENS210_STATUS_I2C_ERROR; - this->mark_failed(); + this->mark_failed(LOG_STR(ESP_LOG_MSG_COMM_FAIL)); return; } // Pack bytes into partid part_id = data[1] * 256U + data[0] * 1U; // Check expected part id of the ENS210 if (part_id != ENS210_PART_ID) { - this->error_code_ = ENS210_WRONG_CHIP_ID; - this->mark_failed(); + // Try to go back to default mode (low power enabled) + set_low_power_(true); + this->mark_failed(LOG_STR("Wrong chip ID")); + return; } // Set default power mode (low power enabled) set_low_power_(true); @@ -129,9 +126,6 @@ void ENS210Component::setup() { void ENS210Component::dump_config() { ESP_LOGCONFIG(TAG, "ENS210:"); LOG_I2C_DEVICE(this); - if (this->is_failed()) { - ESP_LOGE(TAG, "%s", LOG_STR_ARG(ens210_status_to_human(this->error_code_))); - } LOG_UPDATE_INTERVAL(this); LOG_SENSOR(" ", "Temperature", this->temperature_sensor_); LOG_SENSOR(" ", "Humidity", this->humidity_sensor_); diff --git a/esphome/components/ens210/ens210.h b/esphome/components/ens210/ens210.h index fca20133b82..803d3b9cedf 100644 --- a/esphome/components/ens210/ens210.h +++ b/esphome/components/ens210/ens210.h @@ -22,8 +22,7 @@ class ENS210Component final : public PollingComponent, public i2c::I2CDevice { // not yet finished) ENS210_STATUS_CRC_ERROR, // The value was read, but the CRC over the payload (valid and data) does not match ENS210_STATUS_I2C_ERROR, // There was an I2C communication error - ENS210_WRONG_CHIP_ID // The read PART_ID is not the expected part id of the ENS210 - } error_code_{ENS210_STATUS_OK}; + }; protected: bool set_low_power_(bool enable); diff --git a/esphome/components/es7210/es7210.cpp b/esphome/components/es7210/es7210.cpp index 9c3dee69506..84af004c481 100644 --- a/esphome/components/es7210/es7210.cpp +++ b/esphome/components/es7210/es7210.cpp @@ -13,7 +13,7 @@ static const size_t MCLK_DIV_FRE = 256; // Mark the component as failed; use only in setup #define ES7210_ERROR_FAILED(func) \ if (!(func)) { \ - this->mark_failed(); \ + this->mark_failed(LOG_STR("Failed to initialize")); \ return; \ } @@ -31,11 +31,6 @@ void ES7210::dump_config() { " Channel Gains: %.1f dB, %.1f dB, %.1f dB, %.1f dB", this->bits_per_sample_, this->sample_rate_, this->channel_gains_[0], this->channel_gains_[1], this->channel_gains_[2], this->channel_gains_[3]); - - if (this->is_failed()) { - ESP_LOGE(TAG, " Failed to initialize"); - return; - } } void ES7210::setup() { diff --git a/esphome/components/es7243e/es7243e.cpp b/esphome/components/es7243e/es7243e.cpp index 1bfa6933ddc..c43ed38cd57 100644 --- a/esphome/components/es7243e/es7243e.cpp +++ b/esphome/components/es7243e/es7243e.cpp @@ -13,7 +13,7 @@ ESPHOME_LOG_TAG(TAG, "es7243e"); // Mark the component as failed; use only in setup #define ES7243E_ERROR_FAILED(func) \ if (!(func)) { \ - this->mark_failed(); \ + this->mark_failed(LOG_STR("Failed to initialize")); \ return; \ } @@ -23,14 +23,7 @@ ESPHOME_LOG_TAG(TAG, "es7243e"); return false; \ } -void ES7243E::dump_config() { - ESP_LOGCONFIG(TAG, "ES7243E audio ADC:"); - - if (this->is_failed()) { - ESP_LOGE(TAG, " Failed to initialize"); - return; - } -} +void ES7243E::dump_config() { ESP_LOGCONFIG(TAG, "ES7243E audio ADC:"); } void ES7243E::setup() { ES7243E_ERROR_FAILED(this->write_byte(ES7243E_CLOCK_MGR_REG01, 0x3A)); diff --git a/esphome/components/es8156/es8156.cpp b/esphome/components/es8156/es8156.cpp index ac1e378222c..930e912988d 100644 --- a/esphome/components/es8156/es8156.cpp +++ b/esphome/components/es8156/es8156.cpp @@ -11,7 +11,7 @@ ESPHOME_LOG_TAG(TAG, "es8156"); // Mark the component as failed; use only in setup #define ES8156_ERROR_FAILED(func) \ if (!(func)) { \ - this->mark_failed(); \ + this->mark_failed(LOG_STR("Failed to initialize")); \ return; \ } @@ -74,14 +74,7 @@ void ES8156::setup() { ES8156_ERROR_FAILED(this->write_byte(ES8156_REG25_ANALOG_SYS5, 0x20)); } -void ES8156::dump_config() { - ESP_LOGCONFIG(TAG, "ES8156 Audio Codec:"); - - if (this->is_failed()) { - ESP_LOGCONFIG(TAG, " Failed to initialize"); - return; - } -} +void ES8156::dump_config() { ESP_LOGCONFIG(TAG, "ES8156 Audio Codec:"); } bool ES8156::set_volume(float volume) { volume = clamp(volume, 0.0f, 1.0f); diff --git a/esphome/components/es8311/es8311.cpp b/esphome/components/es8311/es8311.cpp index 8c82716f9f0..05edf6417ec 100644 --- a/esphome/components/es8311/es8311.cpp +++ b/esphome/components/es8311/es8311.cpp @@ -11,7 +11,7 @@ ESPHOME_LOG_TAG(TAG, "es8311"); // Mark the component as failed; use only in setup #define ES8311_ERROR_FAILED(func) \ if (!(func)) { \ - this->mark_failed(); \ + this->mark_failed(LOG_STR("Failed to initialize")); \ return; \ } // Return false; use outside of setup @@ -56,11 +56,6 @@ void ES8311::dump_config() { " DAC Bits per Sample: %" PRIu8 "\n" " Sample Rate: %" PRIu32, YESNO(this->use_mclk_), YESNO(this->use_mic_), this->resolution_out_, this->sample_frequency_); - - if (this->is_failed()) { - ESP_LOGCONFIG(TAG, " Failed to initialize!"); - return; - } } bool ES8311::set_volume(float volume) { diff --git a/esphome/components/es8388/es8388.cpp b/esphome/components/es8388/es8388.cpp index 56ee1ac4a59..d7aedd9d9e5 100644 --- a/esphome/components/es8388/es8388.cpp +++ b/esphome/components/es8388/es8388.cpp @@ -11,7 +11,7 @@ ESPHOME_LOG_TAG(TAG, "es8388"); // Mark the component as failed; use only in setup #define ES8388_ERROR_FAILED(func) \ if (!(func)) { \ - this->mark_failed(); \ + this->mark_failed(LOG_STR("Failed to initialize")); \ return; \ } @@ -154,11 +154,6 @@ void ES8388::dump_config() { LOG_SELECT(" ", "DacOutputSelect", this->dac_output_select_); LOG_SELECT(" ", "ADCInputMicSelect", this->adc_input_mic_select_); #endif - - if (this->is_failed()) { - ESP_LOGCONFIG(TAG, " Failed to initialize"); - return; - } } bool ES8388::set_volume(float volume) { diff --git a/esphome/components/esp32_camera_web_server/camera_web_server.cpp b/esphome/components/esp32_camera_web_server/camera_web_server.cpp index 36c1cdfa45b..a248b901204 100644 --- a/esphome/components/esp32_camera_web_server/camera_web_server.cpp +++ b/esphome/components/esp32_camera_web_server/camera_web_server.cpp @@ -42,7 +42,7 @@ CameraWebServer::~CameraWebServer() {} void CameraWebServer::setup() { if (!camera::Camera::instance() || camera::Camera::instance()->is_failed()) { - this->mark_failed(); + this->mark_failed(LOG_STR("Camera not available")); return; } @@ -56,7 +56,7 @@ void CameraWebServer::setup() { config.lru_purge_enable = true; if (httpd_start(&this->httpd_, &config) != ESP_OK) { - mark_failed(); + this->mark_failed(LOG_STR("Failed to start the web server")); return; } @@ -97,10 +97,6 @@ void CameraWebServer::dump_config() { } else { ESP_LOGCONFIG(TAG, " Mode: snapshot"); } - - if (this->is_failed()) { - ESP_LOGE(TAG, " Setup Failed"); - } } float CameraWebServer::get_setup_priority() const { return setup_priority::LATE; } diff --git a/esphome/components/ezo/ezo.cpp b/esphome/components/ezo/ezo.cpp index d18e6e67cb1..c9a5087bf5c 100644 --- a/esphome/components/ezo/ezo.cpp +++ b/esphome/components/ezo/ezo.cpp @@ -13,9 +13,6 @@ static const char *const EZO_CALIBRATION_TYPE_STRINGS[] = {"LOW", "MID", "HIGH"} void EZOSensor::dump_config() { LOG_SENSOR("", "EZO", this); LOG_I2C_DEVICE(this); - if (this->is_failed()) { - ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL); - } LOG_UPDATE_INTERVAL(this); } diff --git a/esphome/components/ezo_pmp/ezo_pmp.cpp b/esphome/components/ezo_pmp/ezo_pmp.cpp index 8db57670aea..393ba66fdf0 100644 --- a/esphome/components/ezo_pmp/ezo_pmp.cpp +++ b/esphome/components/ezo_pmp/ezo_pmp.cpp @@ -39,9 +39,6 @@ static const std::string DOSING_MODE_CONTINUOUS = "Continuous"; void EzoPMP::dump_config() { LOG_I2C_DEVICE(this); - if (this->is_failed()) { - ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL); - } LOG_UPDATE_INTERVAL(this); } diff --git a/esphome/components/file/image.py b/esphome/components/file/image.py index 20eda9a3278..5e4fd145a44 100644 --- a/esphome/components/file/image.py +++ b/esphome/components/file/image.py @@ -254,7 +254,13 @@ async def new_image(config: ConfigType) -> MockObj: """ prog_arr, width, height, image_type, trans_value, _ = await write_image(config) var = cg.new_Pvariable( - config[CONF_ID], prog_arr, width, height, image_type, trans_value + config[CONF_ID], + prog_arr, + width, + height, + image_type, + trans_value, + config.get(CONF_BYTE_ORDER) == "BIG_ENDIAN", ) add_metadata( config[CONF_ID], width, height, config[CONF_TYPE], config[CONF_TRANSPARENCY] diff --git a/esphome/components/gdk101/gdk101.cpp b/esphome/components/gdk101/gdk101.cpp index 3c7e8d21613..a84d5ba06fb 100644 --- a/esphome/components/gdk101/gdk101.cpp +++ b/esphome/components/gdk101/gdk101.cpp @@ -82,9 +82,6 @@ bool GDK101Component::try_reset_() { void GDK101Component::dump_config() { ESP_LOGCONFIG(TAG, "GDK101:"); LOG_I2C_DEVICE(this); - if (this->is_failed()) { - ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL); - } #ifdef USE_SENSOR LOG_SENSOR(" ", "Average Radaition Dose per 1 minute", this->rad_1m_sensor_); LOG_SENSOR(" ", "Average Radaition Dose per 10 minutes", this->rad_10m_sensor_); diff --git a/esphome/components/grove_gas_mc_v2/grove_gas_mc_v2.cpp b/esphome/components/grove_gas_mc_v2/grove_gas_mc_v2.cpp index b01dd119e63..1acf8076af4 100644 --- a/esphome/components/grove_gas_mc_v2/grove_gas_mc_v2.cpp +++ b/esphome/components/grove_gas_mc_v2/grove_gas_mc_v2.cpp @@ -23,7 +23,6 @@ bool GroveGasMultichannelV2Component::read_sensor_(uint8_t address, sensor::Sens uint32_t value = 0; if (!this->read_bytes(address, (uint8_t *) &value, 4)) { ESP_LOGW(TAG, "Reading Grove Gas Sensor data failed!"); - this->error_code_ = COMMUNICATION_FAILED; this->status_set_warning(); return false; } @@ -34,8 +33,7 @@ bool GroveGasMultichannelV2Component::read_sensor_(uint8_t address, sensor::Sens void GroveGasMultichannelV2Component::setup() { // Before reading sensor values, must preheat sensor if (!(this->write_bytes(GROVE_GAS_MC_V2_HEAT_ON, {}))) { - this->mark_failed(); - this->error_code_ = APP_START_FAILED; + this->mark_failed(LOG_STR(ESP_LOG_MSG_COMM_FAIL)); } } @@ -61,24 +59,6 @@ void GroveGasMultichannelV2Component::dump_config() { LOG_SENSOR(" ", "Ethanol", this->ethanol_sensor_); LOG_SENSOR(" ", "Carbon Monoxide", this->carbon_monoxide_sensor_); LOG_SENSOR(" ", "TVOC", this->tvoc_sensor_); - - if (this->is_failed()) { - switch (this->error_code_) { - case COMMUNICATION_FAILED: - ESP_LOGW(TAG, ESP_LOG_MSG_COMM_FAIL); - break; - case APP_INVALID: - ESP_LOGW(TAG, "Sensor reported invalid APP installed"); - break; - case APP_START_FAILED: - ESP_LOGW(TAG, "Sensor reported APP start failed"); - break; - case UNKNOWN: - default: - ESP_LOGW(TAG, "Unknown setup error"); - break; - } - } } } // namespace esphome::grove_gas_mc_v2 diff --git a/esphome/components/grove_gas_mc_v2/grove_gas_mc_v2.h b/esphome/components/grove_gas_mc_v2/grove_gas_mc_v2.h index 545b6df97a9..8aafdea8e7d 100644 --- a/esphome/components/grove_gas_mc_v2/grove_gas_mc_v2.h +++ b/esphome/components/grove_gas_mc_v2/grove_gas_mc_v2.h @@ -22,13 +22,6 @@ class GroveGasMultichannelV2Component final : public PollingComponent, public i2 void dump_config() override; protected: - enum ErrorCode { - UNKNOWN, - COMMUNICATION_FAILED, - APP_INVALID, - APP_START_FAILED, - } error_code_{UNKNOWN}; - bool read_sensor_(uint8_t address, sensor::Sensor *sensor); }; diff --git a/esphome/components/hdc1080/hdc1080.cpp b/esphome/components/hdc1080/hdc1080.cpp index be0de1bae6e..c61feb52d57 100644 --- a/esphome/components/hdc1080/hdc1080.cpp +++ b/esphome/components/hdc1080/hdc1080.cpp @@ -24,9 +24,6 @@ void HDC1080Component::setup() { void HDC1080Component::dump_config() { ESP_LOGCONFIG(TAG, "HDC1080:"); LOG_I2C_DEVICE(this); - if (this->is_failed()) { - ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL); - } LOG_UPDATE_INTERVAL(this); LOG_SENSOR(" ", "Temperature", this->temperature_); LOG_SENSOR(" ", "Humidity", this->humidity_); diff --git a/esphome/components/hdc2010/hdc2010.cpp b/esphome/components/hdc2010/hdc2010.cpp index 825d7d3da96..471afe515a1 100644 --- a/esphome/components/hdc2010/hdc2010.cpp +++ b/esphome/components/hdc2010/hdc2010.cpp @@ -42,9 +42,6 @@ void HDC2010Component::setup() { void HDC2010Component::dump_config() { ESP_LOGCONFIG(TAG, "HDC2010:"); LOG_I2C_DEVICE(this); - if (this->is_failed()) { - ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL); - } LOG_UPDATE_INTERVAL(this); LOG_SENSOR(" ", "Temperature", this->temperature_sensor_); LOG_SENSOR(" ", "Humidity", this->humidity_sensor_); diff --git a/esphome/components/hdc2080/hdc2080.cpp b/esphome/components/hdc2080/hdc2080.cpp index 8210733b4cb..beb84f30746 100644 --- a/esphome/components/hdc2080/hdc2080.cpp +++ b/esphome/components/hdc2080/hdc2080.cpp @@ -33,9 +33,6 @@ void HDC2080Component::dump_config() { LOG_UPDATE_INTERVAL(this); LOG_SENSOR(" ", "Temperature", this->temperature_sensor_); LOG_SENSOR(" ", "Humidity", this->humidity_sensor_); - if (this->is_failed()) { - ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL); - } } void HDC2080Component::update() { diff --git a/esphome/components/hmc5883l/hmc5883l.cpp b/esphome/components/hmc5883l/hmc5883l.cpp index c747e7968ed..d1abe8c1fdb 100644 --- a/esphome/components/hmc5883l/hmc5883l.cpp +++ b/esphome/components/hmc5883l/hmc5883l.cpp @@ -27,8 +27,7 @@ void HMC5883LComponent::setup() { if (!this->read_byte(HMC5883L_REGISTER_IDENTIFICATION_A, &id[0]) || !this->read_byte(HMC5883L_REGISTER_IDENTIFICATION_B, &id[1]) || !this->read_byte(HMC5883L_REGISTER_IDENTIFICATION_C, &id[2])) { - this->error_code_ = COMMUNICATION_FAILED; - this->mark_failed(); + this->mark_failed(LOG_STR(ESP_LOG_MSG_COMM_FAIL)); return; } @@ -37,8 +36,7 @@ void HMC5883LComponent::setup() { } if (id[0] != 0x48 || id[1] != 0x34 || id[2] != 0x33) { - this->error_code_ = ID_REGISTERS; - this->mark_failed(); + this->mark_failed(LOG_STR("The ID registers don't match - Is this really an HMC5883L?")); return; } @@ -47,16 +45,14 @@ void HMC5883LComponent::setup() { config_a |= this->datarate_ << 2; config_a |= 0b0 << 0; // Measurement Mode: Normal(high impedance on load) if (!this->write_byte(HMC5883L_REGISTER_CONFIG_A, config_a)) { - this->error_code_ = COMMUNICATION_FAILED; - this->mark_failed(); + this->mark_failed(LOG_STR(ESP_LOG_MSG_COMM_FAIL)); return; } uint8_t config_b = 0; config_b |= this->range_ << 5; if (!this->write_byte(HMC5883L_REGISTER_CONFIG_B, config_b)) { - this->error_code_ = COMMUNICATION_FAILED; - this->mark_failed(); + this->mark_failed(LOG_STR(ESP_LOG_MSG_COMM_FAIL)); return; } @@ -64,19 +60,13 @@ void HMC5883LComponent::setup() { // Continuous Measurement Mode mode |= 0b00; if (!this->write_byte(HMC5883L_REGISTER_MODE, mode)) { - this->error_code_ = COMMUNICATION_FAILED; - this->mark_failed(); + this->mark_failed(LOG_STR(ESP_LOG_MSG_COMM_FAIL)); return; } } void HMC5883LComponent::dump_config() { ESP_LOGCONFIG(TAG, "HMC5883L:"); LOG_I2C_DEVICE(this); - if (this->error_code_ == COMMUNICATION_FAILED) { - ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL); - } else if (this->error_code_ == ID_REGISTERS) { - ESP_LOGE(TAG, "The ID registers don't match - Is this really an HMC5883L?"); - } LOG_UPDATE_INTERVAL(this); LOG_SENSOR(" ", "X Axis", this->x_sensor_); diff --git a/esphome/components/hmc5883l/hmc5883l.h b/esphome/components/hmc5883l/hmc5883l.h index d23eb1a0f4a..6e5303d8988 100644 --- a/esphome/components/hmc5883l/hmc5883l.h +++ b/esphome/components/hmc5883l/hmc5883l.h @@ -56,11 +56,6 @@ class HMC5883LComponent final : public PollingComponent, public i2c::I2CDevice { sensor::Sensor *y_sensor_{nullptr}; sensor::Sensor *z_sensor_{nullptr}; sensor::Sensor *heading_sensor_{nullptr}; - enum ErrorCode { - NONE = 0, - COMMUNICATION_FAILED, - ID_REGISTERS, - } error_code_{NONE}; HighFrequencyLoopRequester high_freq_; }; diff --git a/esphome/components/honeywell_hih_i2c/honeywell_hih.cpp b/esphome/components/honeywell_hih_i2c/honeywell_hih.cpp index 352d3f7f01f..91f3089c729 100644 --- a/esphome/components/honeywell_hih_i2c/honeywell_hih.cpp +++ b/esphome/components/honeywell_hih_i2c/honeywell_hih.cpp @@ -18,8 +18,7 @@ void HoneywellHIComponent::read_sensor_data_() { uint8_t data[4]; if (this->read(data, sizeof(data)) != i2c::ERROR_OK) { - ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL); - this->mark_failed(); + this->mark_failed(LOG_STR(ESP_LOG_MSG_COMM_FAIL)); return; } @@ -38,8 +37,7 @@ void HoneywellHIComponent::read_sensor_data_() { void HoneywellHIComponent::start_measurement_() { if (this->write(REQUEST_CMD, sizeof(REQUEST_CMD)) != i2c::ERROR_OK) { - ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL); - this->mark_failed(); + this->mark_failed(LOG_STR(ESP_LOG_MSG_COMM_FAIL)); return; } @@ -50,8 +48,7 @@ bool HoneywellHIComponent::is_measurement_ready_() { uint8_t data[1]; if (this->read(data, sizeof(data)) != i2c::ERROR_OK) { - ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL); - this->mark_failed(); + this->mark_failed(LOG_STR(ESP_LOG_MSG_COMM_FAIL)); return false; } @@ -60,9 +57,8 @@ bool HoneywellHIComponent::is_measurement_ready_() { } void HoneywellHIComponent::measurement_timeout_() { - ESP_LOGE(TAG, "Honeywell HIH Timeout!"); this->measurement_running_ = false; - this->mark_failed(); + this->mark_failed(LOG_STR("Measurement timeout")); } void HoneywellHIComponent::update() { @@ -84,9 +80,6 @@ void HoneywellHIComponent::loop() { void HoneywellHIComponent::dump_config() { ESP_LOGD(TAG, "Honeywell HIH:"); LOG_I2C_DEVICE(this); - if (this->is_failed()) { - ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL); - } LOG_SENSOR(" ", "Temperature", this->temperature_sensor_); LOG_SENSOR(" ", "Humidity", this->humidity_sensor_); LOG_UPDATE_INTERVAL(this); diff --git a/esphome/components/htu21d/htu21d.cpp b/esphome/components/htu21d/htu21d.cpp index 468a7f8e6a2..4fe502f2a5c 100644 --- a/esphome/components/htu21d/htu21d.cpp +++ b/esphome/components/htu21d/htu21d.cpp @@ -18,7 +18,7 @@ static const uint8_t HTU21D_REG_HTRE_BIT = 0x02; /**< Control Register He void HTU21DComponent::setup() { if (!this->write_bytes(HTU21D_REGISTER_RESET, nullptr, 0)) { - this->mark_failed(); + this->mark_failed(LOG_STR(ESP_LOG_MSG_COMM_FAIL)); return; } @@ -28,9 +28,6 @@ void HTU21DComponent::setup() { void HTU21DComponent::dump_config() { ESP_LOGCONFIG(TAG, "HTU21D:"); LOG_I2C_DEVICE(this); - if (this->is_failed()) { - ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL); - } LOG_UPDATE_INTERVAL(this); LOG_SENSOR(" ", "Temperature", this->temperature_); LOG_SENSOR(" ", "Humidity", this->humidity_); diff --git a/esphome/components/htu31d/htu31d.cpp b/esphome/components/htu31d/htu31d.cpp index 86c9876b2b6..5426ca50845 100644 --- a/esphome/components/htu31d/htu31d.cpp +++ b/esphome/components/htu31d/htu31d.cpp @@ -49,12 +49,12 @@ static const uint8_t HTU31D_DIAGNOSTICS = 0x08; */ void HTU31DComponent::setup() { if (!this->reset_()) { - this->mark_failed(); + this->mark_failed(LOG_STR(ESP_LOG_MSG_COMM_FAIL)); return; } if (this->read_serial_num_() == 0) { - this->mark_failed(); + this->mark_failed(LOG_STR(ESP_LOG_MSG_COMM_FAIL)); return; } } @@ -131,9 +131,6 @@ void HTU31DComponent::update() { void HTU31DComponent::dump_config() { ESP_LOGCONFIG(TAG, "HTU31D:"); LOG_I2C_DEVICE(this); - if (this->is_failed()) { - ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL); - } LOG_UPDATE_INTERVAL(this); LOG_SENSOR(" ", "Temperature", this->temperature_); LOG_SENSOR(" ", "Humidity", this->humidity_); diff --git a/esphome/components/husb238/husb238.cpp b/esphome/components/husb238/husb238.cpp index 8ca841a7e43..d0f9973b2a4 100644 --- a/esphome/components/husb238/husb238.cpp +++ b/esphome/components/husb238/husb238.cpp @@ -39,9 +39,6 @@ void Husb238Component::dump_config() { ESP_LOGCONFIG(TAG, "HUSB238:"); LOG_I2C_DEVICE(this); LOG_UPDATE_INTERVAL(this); - if (this->is_failed()) { - ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL); - } #ifdef USE_BINARY_SENSOR LOG_BINARY_SENSOR(" ", "PD Attached", this->attached_binary_sensor_); LOG_BINARY_SENSOR(" ", "CC Direction", this->cc_direction_binary_sensor_); diff --git a/esphome/components/hydreon_rgxx/hydreon_rgxx.cpp b/esphome/components/hydreon_rgxx/hydreon_rgxx.cpp index 36a7b1b3511..a2f8fc50862 100644 --- a/esphome/components/hydreon_rgxx/hydreon_rgxx.cpp +++ b/esphome/components/hydreon_rgxx/hydreon_rgxx.cpp @@ -15,9 +15,6 @@ static const char *const IGNORE_STRINGS[] = {HYDREON_RGXX_IGNORE_LIST(, HYDREON_ void HydreonRGxxComponent::dump_config() { ESP_LOGCONFIG(TAG, "hydreon_rgxx:"); - if (this->is_failed()) { - ESP_LOGE(TAG, "Connection with hydreon_rgxx failed!"); - } if (model_ == RG9) { ESP_LOGCONFIG(TAG, " Model: RG9\n" @@ -154,7 +151,7 @@ void HydreonRGxxComponent::schedule_reboot_() { sensor->publish_state(NAN); } } - this->mark_failed(); + this->mark_failed(LOG_STR("Sensor did not boot")); } }); } diff --git a/esphome/components/i2s_audio/__init__.py b/esphome/components/i2s_audio/__init__.py index 6e4e35590c1..3d5ec97a633 100644 --- a/esphome/components/i2s_audio/__init__.py +++ b/esphome/components/i2s_audio/__init__.py @@ -19,8 +19,16 @@ from esphome.components.esp32.const import ( VARIANT_ESP32S3, ) from esphome.components.microphone import DOMAIN as MICROPHONE_DOMAIN +from esphome.config_helpers import filter_source_files_from_defines import esphome.config_validation as cv -from esphome.const import CONF_BITS_PER_SAMPLE, CONF_CHANNEL, CONF_ID, CONF_SAMPLE_RATE +from esphome.const import ( + CONF_BITS_PER_SAMPLE, + CONF_CHANNEL, + CONF_ID, + CONF_PLATFORM, + CONF_SAMPLE_RATE, + CONF_SPEAKER, +) from esphome.core import CORE from esphome.cpp_generator import MockObj, MockObjClass import esphome.final_validate as fv @@ -42,6 +50,9 @@ CONF_I2S_BCLK_PIN = "i2s_bclk_pin" CONF_I2S_LRCLK_PIN = "i2s_lrclk_pin" CONF_I2S_AUDIO_ID = "i2s_audio_id" +CONF_FULL_DUPLEX = "full_duplex" +CONF_I2S_COMM_FMT = "i2s_comm_fmt" +CONF_SPDIF_MODE = "spdif_mode" CONF_I2S_MODE = "i2s_mode" CONF_PRIMARY = "primary" @@ -185,7 +196,8 @@ def i2s_audio_component_schema( async def register_i2s_audio_component(var: MockObj, config: ConfigType) -> None: - await cg.register_parented(var, config[CONF_I2S_AUDIO_ID]) + parent = await cg.get_variable(config[CONF_I2S_AUDIO_ID]) + await cg.register_parented(var, parent) cg.add(var.set_i2s_role(I2S_ROLE_OPTIONS[config[CONF_I2S_MODE]])) slot_mode = config[CONF_CHANNEL] if slot_mode != CONF_STEREO: @@ -200,6 +212,12 @@ async def register_i2s_audio_component(var: MockObj, config: ConfigType) -> None cg.add(var.set_use_apll(config[CONF_USE_APLL])) cg.add(var.set_mclk_multiple(I2S_MCLK_MULTIPLE[config[CONF_MCLK_MULTIPLE]])) + if str(config[CONF_I2S_AUDIO_ID]) in _get_data().full_duplex_buses: + if config[CONF_ID].type.inherits_from(I2SAudioIn): + cg.add(parent.set_audio_in(var)) + else: + cg.add(parent.set_audio_out(var)) + CONFIG_SCHEMA = cv.Schema( { @@ -207,6 +225,7 @@ CONFIG_SCHEMA = cv.Schema( cv.Optional(CONF_I2S_LRCLK_PIN): pins.internal_gpio_output_pin_number, cv.Optional(CONF_I2S_BCLK_PIN): pins.internal_gpio_output_pin_number, cv.Optional(CONF_I2S_MCLK_PIN): pins.internal_gpio_output_pin_number, + cv.Optional(CONF_FULL_DUPLEX, default=False): cv.boolean, }, ) @@ -216,6 +235,7 @@ class I2SAudioData: """I2S audio component state stored in CORE.data.""" port_map: dict[str, int] = field(default_factory=dict) + full_duplex_buses: set[str] = field(default_factory=set) def _get_data() -> I2SAudioData: @@ -262,8 +282,56 @@ def _assign_ports() -> None: next_port += 1 +def _bus_devices(full_config: ConfigType, domain: str, bus_id: str) -> list[ConfigType]: + return [ + device + for device in full_config.get(domain, []) + if device.get(CONF_PLATFORM) == DOMAIN + and str(device[CONF_I2S_AUDIO_ID]) == bus_id + ] + + +def _validate_full_duplex(full_config: ConfigType, bus_id: str) -> None: + """Check that a full duplex bus has one microphone and speakers that can share its clocks and TX channel.""" + microphones = _bus_devices(full_config, MICROPHONE_DOMAIN, bus_id) + speakers = _bus_devices(full_config, CONF_SPEAKER, bus_id) + if len(microphones) != 1 or not speakers: + raise cv.Invalid( + f"'{CONF_FULL_DUPLEX}' requires exactly one i2s_audio microphone and at least one " + f"i2s_audio speaker on bus '{bus_id}'" + ) + microphone = microphones[0] + if microphone.get(CONF_PDM): + raise cv.Invalid(f"A PDM microphone cannot use a '{CONF_FULL_DUPLEX}' bus") + for speaker in speakers: + if speaker.get(CONF_SPDIF_MODE): + raise cv.Invalid(f"An SPDIF speaker cannot use a '{CONF_FULL_DUPLEX}' bus") + # Both directions run from the same bit and word clocks + for key in ( + CONF_SAMPLE_RATE, + CONF_BITS_PER_SAMPLE, + CONF_I2S_MODE, + CONF_USE_APLL, + CONF_MCLK_MULTIPLE, + ): + if microphone[key] != speaker[key]: + raise cv.Invalid( + f"The microphone and speaker '{speaker[CONF_ID]}' on '{CONF_FULL_DUPLEX}' bus " + f"'{bus_id}' must use the same '{key}'" + ) + # The TX channel is set up once from one speaker's configuration, and the speakers take turns using it + first = speakers[0] + for speaker in speakers[1:]: + for key in (CONF_I2S_DOUT_PIN, CONF_CHANNEL, CONF_I2S_COMM_FMT): + if speaker.get(key) != first.get(key): + raise cv.Invalid( + f"The speakers on '{CONF_FULL_DUPLEX}' bus '{bus_id}' must use the same '{key}'" + ) + + def _final_validate(_: ConfigType) -> None: - i2s_audio_configs = fv.full_config.get()[DOMAIN] + full_config = fv.full_config.get() + i2s_audio_configs = full_config[DOMAIN] variant = get_esp32_variant() if variant not in I2S_PORTS: raise cv.Invalid(f"Unsupported variant {variant}") @@ -273,6 +341,13 @@ def _final_validate(_: ConfigType) -> None: ) _assign_ports() + data = _get_data() + for config in i2s_audio_configs: + if config[CONF_FULL_DUPLEX]: + bus_id = str(config[CONF_ID]) + _validate_full_duplex(full_config, bus_id) + data.full_duplex_buses.add(bus_id) + FINAL_VALIDATE_SCHEMA = _final_validate @@ -299,3 +374,10 @@ async def to_code(config: ConfigType) -> None: cg.add(var.set_bclk_pin(config[CONF_I2S_BCLK_PIN])) if CONF_I2S_MCLK_PIN in config: cg.add(var.set_mclk_pin(config[CONF_I2S_MCLK_PIN])) + if config[CONF_FULL_DUPLEX]: + cg.add_define("USE_I2S_AUDIO_FULL_DUPLEX") + + +FILTER_SOURCE_FILES = filter_source_files_from_defines( + {"i2s_audio.cpp": "USE_I2S_AUDIO_FULL_DUPLEX"} +) diff --git a/esphome/components/i2s_audio/i2s_audio.cpp b/esphome/components/i2s_audio/i2s_audio.cpp new file mode 100644 index 00000000000..d1ee8d0c73b --- /dev/null +++ b/esphome/components/i2s_audio/i2s_audio.cpp @@ -0,0 +1,94 @@ +#include "i2s_audio.h" + +#if defined(USE_ESP32) && defined(USE_I2S_AUDIO_FULL_DUPLEX) + +#include "esphome/core/log.h" + +namespace esphome::i2s_audio { + +ESPHOME_LOG_TAG(TAG, "i2s_audio"); + +void I2SAudioComponent::setup() { + if (!this->is_full_duplex()) + return; + + // ESP-IDF only shares the bit and word clocks between channels allocated in the same call, and deleting either + // channel ends the pairing. Allocate both once here and keep them for the lifetime of the device. + i2s_chan_config_t chan_cfg{}; + i2s_std_config_t tx_cfg{}; + i2s_std_config_t rx_cfg{}; + this->audio_out_->build_full_duplex_config(chan_cfg, tx_cfg); + this->audio_in_->build_full_duplex_config(rx_cfg); + + esp_err_t err = i2s_new_channel(&chan_cfg, &this->tx_handle_, &this->rx_handle_); + if (err == ESP_OK) { + // The first initialized channel drives the shared clocks. Use RX so the microphone runs on its own. + err = i2s_channel_init_std_mode(this->rx_handle_, &rx_cfg); + } + if (err == ESP_OK) { + err = i2s_channel_init_std_mode(this->tx_handle_, &tx_cfg); + } + if (err != ESP_OK) { + ESP_LOGE(TAG, "Full duplex channel setup failed: %s", esp_err_to_name(err)); + if (this->tx_handle_ != nullptr) { + i2s_del_channel(this->tx_handle_); + this->tx_handle_ = nullptr; + } + if (this->rx_handle_ != nullptr) { + i2s_del_channel(this->rx_handle_); + this->rx_handle_ = nullptr; + } + this->mark_failed(); + } +} + +i2s_chan_handle_t I2SAudioComponent::acquire_(bool &in_use, i2s_chan_handle_t handle) { + if (handle == nullptr) + return nullptr; + in_use = true; + if (!this->update_rx_channel_()) { + in_use = false; + return nullptr; + } + return handle; +} + +i2s_chan_handle_t I2SAudioComponent::acquire_rx_channel() { return this->acquire_(this->rx_in_use_, this->rx_handle_); } + +void I2SAudioComponent::release_rx_channel() { + this->rx_in_use_ = false; + this->update_rx_channel_(); +} + +i2s_chan_handle_t I2SAudioComponent::acquire_tx_channel() { + // Speakers sharing the bus take turns; a second one must not get the channel while the first is playing + if (this->tx_in_use_) + return nullptr; + return this->acquire_(this->tx_in_use_, this->tx_handle_); +} + +void I2SAudioComponent::release_tx_channel() { + // The speaker task may have left the channel disabled already, so an invalid state error is expected here + i2s_channel_disable(this->tx_handle_); + this->tx_in_use_ = false; + this->update_rx_channel_(); +} + +bool I2SAudioComponent::update_rx_channel_() { + const bool needed = this->rx_in_use_ || this->tx_in_use_; + if (needed == this->rx_enabled_) + return true; + + esp_err_t err = needed ? i2s_channel_enable(this->rx_handle_) : i2s_channel_disable(this->rx_handle_); + if (err != ESP_OK) { + ESP_LOGE(TAG, "Failed to %s RX channel: %s", needed ? LOG_STR_LITERAL("enable") : LOG_STR_LITERAL("disable"), + esp_err_to_name(err)); + return false; + } + this->rx_enabled_ = needed; + return true; +} + +} // namespace esphome::i2s_audio + +#endif // USE_ESP32 && USE_I2S_AUDIO_FULL_DUPLEX diff --git a/esphome/components/i2s_audio/i2s_audio.h b/esphome/components/i2s_audio/i2s_audio.h index 00a9705807e..b9adbe45e23 100644 --- a/esphome/components/i2s_audio/i2s_audio.h +++ b/esphome/components/i2s_audio/i2s_audio.h @@ -23,6 +23,15 @@ class I2SAudioBase : public Parented { void set_mclk_multiple(i2s_mclk_multiple_t mclk_multiple) { this->mclk_multiple_ = mclk_multiple; } protected: + i2s_clock_src_t get_clock_source_() const { +#ifdef I2S_CLK_SRC_APLL + if (this->use_apll_) { + return I2S_CLK_SRC_APLL; + } +#endif + return I2S_CLK_SRC_DEFAULT; + } + i2s_role_t i2s_role_{}; i2s_slot_mode_t slot_mode_; i2s_std_slot_mask_t std_slot_mask_; @@ -32,12 +41,51 @@ class I2SAudioBase : public Parented { i2s_mclk_multiple_t mclk_multiple_; }; -class I2SAudioIn : public I2SAudioBase {}; +class I2SAudioIn : public I2SAudioBase { +#ifdef USE_I2S_AUDIO_FULL_DUPLEX + public: + /// @brief Builds the RX configuration the parent uses to set up a full duplex channel pair. + virtual void build_full_duplex_config(i2s_std_config_t &std_cfg) = 0; +#endif +}; -class I2SAudioOut : public I2SAudioBase {}; +class I2SAudioOut : public I2SAudioBase { +#ifdef USE_I2S_AUDIO_FULL_DUPLEX + public: + /// @brief Builds the TX configuration the parent uses to set up a full duplex channel pair. The channel + /// configuration (DMA layout, role, interrupt priority) is shared by both channels. Validation only puts + /// standard speakers on a full duplex bus, so the SPDIF speaker never gets here. + virtual void build_full_duplex_config(i2s_chan_config_t &chan_cfg, i2s_std_config_t &std_cfg) {} +#endif +}; class I2SAudioComponent final : public Component { public: +#ifdef USE_I2S_AUDIO_FULL_DUPLEX + void setup() override; + + void set_audio_in(I2SAudioIn *audio_in) { this->audio_in_ = audio_in; } + void set_audio_out(I2SAudioOut *audio_out) { this->audio_out_ = audio_out; } + + /// @brief True when a microphone and a speaker share this bus at the same time. + bool is_full_duplex() const { return this->audio_in_ != nullptr && this->audio_out_ != nullptr; } + + /// @brief Enables the full duplex RX channel. Main loop only. + /// @return The enabled RX handle, or nullptr if the channel pair is unavailable + i2s_chan_handle_t acquire_rx_channel(); + /// @brief Releases the RX channel; it keeps running while the TX side needs its clocks. Main loop only. + void release_rx_channel(); + + /// @brief Starts the shared clocks and hands over the full duplex TX channel, still disabled, so the caller + /// can register callbacks and preload data before enabling it. Main loop only. + /// @return The TX handle, or nullptr if the channel pair is unavailable or another speaker holds it + i2s_chan_handle_t acquire_tx_channel(); + /// @brief Disables the TX channel and stops the shared clocks if the RX side is idle. Only the speaker that + /// acquired the channel may call this. Main loop only. + void release_tx_channel(); + /// @brief True while another speaker holds the full duplex TX channel + bool is_tx_in_use() const { return this->tx_in_use_; } +#endif i2s_std_gpio_config_t get_pin_config() const { return {.mclk = (gpio_num_t) this->mclk_pin_, .bclk = (gpio_num_t) this->bclk_pin_, @@ -68,8 +116,20 @@ class I2SAudioComponent final : public Component { protected: Mutex lock_; +#ifdef USE_I2S_AUDIO_FULL_DUPLEX + /// @brief Marks one side active and brings the shared clocks up; undoes the mark if that fails. + i2s_chan_handle_t acquire_(bool &in_use, i2s_chan_handle_t handle); + /// @brief Enables the RX channel while either side is active, since it drives the shared clocks. + bool update_rx_channel_(); + I2SAudioIn *audio_in_{nullptr}; I2SAudioOut *audio_out_{nullptr}; + i2s_chan_handle_t rx_handle_{nullptr}; + i2s_chan_handle_t tx_handle_{nullptr}; + bool rx_in_use_{false}; + bool tx_in_use_{false}; + bool rx_enabled_{false}; +#endif int mclk_pin_{I2S_GPIO_UNUSED}; int bclk_pin_{I2S_GPIO_UNUSED}; int lrclk_pin_; diff --git a/esphome/components/i2s_audio/microphone/i2s_audio_microphone.cpp b/esphome/components/i2s_audio/microphone/i2s_audio_microphone.cpp index 1345f0187b3..526ae304ee2 100644 --- a/esphome/components/i2s_audio/microphone/i2s_audio_microphone.cpp +++ b/esphome/components/i2s_audio/microphone/i2s_audio_microphone.cpp @@ -46,6 +46,13 @@ void I2SAudioMicrophone::setup() { return; } +#ifdef USE_I2S_AUDIO_FULL_DUPLEX + if (this->parent_->is_full_duplex() && this->parent_->is_failed()) { + this->mark_failed(LOG_STR("Full duplex bus setup failed")); + return; + } +#endif + this->configure_stream_settings_(); } @@ -94,7 +101,44 @@ void I2SAudioMicrophone::start() { xSemaphoreTake(this->active_listeners_semaphore_, 0); } +void I2SAudioMicrophone::build_std_config_(i2s_std_config_t &std_cfg) const { + i2s_std_slot_config_t std_slot_cfg = + I2S_STD_PHILIPS_SLOT_DEFAULT_CONFIG((i2s_data_bit_width_t) this->slot_bit_width_, this->slot_mode_); + std_slot_cfg.slot_bit_width = this->slot_bit_width_; + std_slot_cfg.slot_mask = this->std_slot_mask_; + + i2s_std_gpio_config_t pin_config = this->parent_->get_pin_config(); + pin_config.din = this->din_pin_; + + std_cfg = { + .clk_cfg = + { + .sample_rate_hz = this->sample_rate_, + .clk_src = this->get_clock_source_(), + .mclk_multiple = this->mclk_multiple_, + }, + .slot_cfg = std_slot_cfg, + .gpio_cfg = pin_config, + }; +} + +#ifdef USE_I2S_AUDIO_FULL_DUPLEX +void I2SAudioMicrophone::build_full_duplex_config(i2s_std_config_t &std_cfg) { this->build_std_config_(std_cfg); } +#endif + bool I2SAudioMicrophone::start_driver_() { +#ifdef USE_I2S_AUDIO_FULL_DUPLEX + if (this->parent_->is_full_duplex()) { + this->rx_handle_ = this->parent_->acquire_rx_channel(); + if (this->rx_handle_ == nullptr) { + ESP_LOGE(TAG, "Full duplex channel unavailable"); + return false; + } + this->configure_stream_settings_(); + return true; + } +#endif + if (!this->parent_->try_lock()) { return false; // Waiting for another i2s to return lock } @@ -115,18 +159,12 @@ bool I2SAudioMicrophone::start_driver_() { return false; } - i2s_clock_src_t clk_src = I2S_CLK_SRC_DEFAULT; -#ifdef I2S_CLK_SRC_APLL - if (this->use_apll_) { - clk_src = I2S_CLK_SRC_APLL; - } -#endif - i2s_std_gpio_config_t pin_config = this->parent_->get_pin_config(); #if SOC_I2S_SUPPORTS_PDM_RX if (this->pdm_) { + i2s_std_gpio_config_t pin_config = this->parent_->get_pin_config(); i2s_pdm_rx_clk_config_t clk_cfg = { .sample_rate_hz = this->sample_rate_, - .clk_src = clk_src, + .clk_src = this->get_clock_source_(), .mclk_multiple = this->mclk_multiple_, .dn_sample_mode = this->pdm_dsr_, }; @@ -162,23 +200,8 @@ bool I2SAudioMicrophone::start_driver_() { } else #endif { - i2s_std_clk_config_t clk_cfg = { - .sample_rate_hz = this->sample_rate_, - .clk_src = clk_src, - .mclk_multiple = this->mclk_multiple_, - }; - i2s_std_slot_config_t std_slot_cfg = - I2S_STD_PHILIPS_SLOT_DEFAULT_CONFIG((i2s_data_bit_width_t) this->slot_bit_width_, this->slot_mode_); - std_slot_cfg.slot_bit_width = this->slot_bit_width_; - std_slot_cfg.slot_mask = this->std_slot_mask_; - - pin_config.din = this->din_pin_; - - i2s_std_config_t std_cfg = { - .clk_cfg = clk_cfg, - .slot_cfg = std_slot_cfg, - .gpio_cfg = pin_config, - }; + i2s_std_config_t std_cfg; + this->build_std_config_(std_cfg); /* Initialize the channel */ err = i2s_channel_init_std_mode(this->rx_handle_, &std_cfg); } @@ -210,6 +233,17 @@ void I2SAudioMicrophone::stop_driver_() { // There is no harm continuing to unload the driver if an error is ever returned by the various functions. This // ensures that we stop/unload the driver when it only partially starts. +#ifdef USE_I2S_AUDIO_FULL_DUPLEX + if (this->parent_->is_full_duplex()) { + // The parent owns the channel pair, so only hand the RX channel back + if (this->rx_handle_ != nullptr) { + this->parent_->release_rx_channel(); + this->rx_handle_ = nullptr; + } + return; + } +#endif + esp_err_t err; if (this->rx_handle_ != nullptr) { /* Have to stop the channel before deleting it */ diff --git a/esphome/components/i2s_audio/microphone/i2s_audio_microphone.h b/esphome/components/i2s_audio/microphone/i2s_audio_microphone.h index 1b17380ed87..c5ed52f6514 100644 --- a/esphome/components/i2s_audio/microphone/i2s_audio_microphone.h +++ b/esphome/components/i2s_audio/microphone/i2s_audio_microphone.h @@ -33,7 +33,14 @@ class I2SAudioMicrophone final : public I2SAudioIn, public microphone::Microphon void set_pdm_dsr(i2s_pdm_dsr_t pdm_dsr) { this->pdm_dsr_ = pdm_dsr; } #endif +#ifdef USE_I2S_AUDIO_FULL_DUPLEX + void build_full_duplex_config(i2s_std_config_t &std_cfg) override; +#endif + protected: + /// @brief Builds the standard (non-PDM) mode configuration from the configured settings. + void build_std_config_(i2s_std_config_t &std_cfg) const; + /// @brief Starts the I2S driver. Updates the ``audio_stream_info_`` member variable with the current setttings. /// @return True if succesful, false otherwise bool start_driver_(); @@ -59,7 +66,7 @@ class I2SAudioMicrophone final : public I2SAudioIn, public microphone::Microphon TaskHandle_t task_handle_{nullptr}; gpio_num_t din_pin_{I2S_GPIO_UNUSED}; - i2s_chan_handle_t rx_handle_; + i2s_chan_handle_t rx_handle_{nullptr}; #if SOC_I2S_SUPPORTS_PDM_RX i2s_pdm_dsr_t pdm_dsr_{I2S_PDM_DSR_8S}; #endif diff --git a/esphome/components/i2s_audio/speaker/__init__.py b/esphome/components/i2s_audio/speaker/__init__.py index e4ea186199d..ddbe08922fe 100644 --- a/esphome/components/i2s_audio/speaker/__init__.py +++ b/esphome/components/i2s_audio/speaker/__init__.py @@ -20,6 +20,7 @@ from esphome.types import ConfigType from .. import ( CONF_I2S_AUDIO_ID, + CONF_I2S_COMM_FMT, CONF_I2S_DOUT_PIN, CONF_I2S_MODE, CONF_LEFT, @@ -27,6 +28,7 @@ from .. import ( CONF_MONO, CONF_PRIMARY, CONF_RIGHT, + CONF_SPDIF_MODE, CONF_STEREO, CONF_USE_APLL, I2SAudioOut, @@ -46,9 +48,7 @@ I2SAudioSpeakerBase = i2s_audio_ns.class_( I2SAudioSpeaker = i2s_audio_ns.class_("I2SAudioSpeaker", I2SAudioSpeakerBase) CONF_DAC_TYPE = "dac_type" -CONF_I2S_COMM_FMT = "i2s_comm_fmt" CONF_KEEP_ALIVE = "keep_alive" -CONF_SPDIF_MODE = "spdif_mode" I2SAudioSpeakerBase = i2s_audio_ns.class_( "I2SAudioSpeakerBase", cg.Component, speaker.Speaker, I2SAudioOut diff --git a/esphome/components/i2s_audio/speaker/i2s_audio_spdif.cpp b/esphome/components/i2s_audio/speaker/i2s_audio_spdif.cpp index 3938d777717..46d6473983d 100644 --- a/esphome/components/i2s_audio/speaker/i2s_audio_spdif.cpp +++ b/esphome/components/i2s_audio/speaker/i2s_audio_spdif.cpp @@ -467,14 +467,6 @@ esp_err_t I2SAudioSpeakerSPDIF::start_i2s_driver(audio::AudioStreamInfo &audio_s return ESP_ERR_INVALID_STATE; } - i2s_clock_src_t clk_src = I2S_CLK_SRC_DEFAULT; - -#if SOC_CLK_APLL_SUPPORTED - if (this->use_apll_) { - clk_src = i2s_clock_src_t::I2S_CLK_SRC_APLL; - } -#endif // SOC_CLK_APLL_SUPPORTED - // SPDIF mode: fixed configuration for BMC encoding // For new driver, dma_frame_num is in I2S frames (8 bytes each for 32-bit stereo) uint32_t dma_buffer_length = SPDIF_BLOCK_I2S_FRAMES; // One SPDIF block = 384 I2S frames = 3072 bytes @@ -496,7 +488,7 @@ esp_err_t I2SAudioSpeakerSPDIF::start_i2s_driver(audio::AudioStreamInfo &audio_s // SPDIF: double sample rate for BMC, 32-bit stereo, only data pin needed i2s_std_clk_config_t clk_cfg = { .sample_rate_hz = this->sample_rate_ * 2, - .clk_src = clk_src, + .clk_src = this->get_clock_source_(), .mclk_multiple = this->mclk_multiple_, }; diff --git a/esphome/components/i2s_audio/speaker/i2s_audio_speaker.cpp b/esphome/components/i2s_audio/speaker/i2s_audio_speaker.cpp index 85c0e6358a2..f14626dd47f 100644 --- a/esphome/components/i2s_audio/speaker/i2s_audio_speaker.cpp +++ b/esphome/components/i2s_audio/speaker/i2s_audio_speaker.cpp @@ -41,6 +41,17 @@ void I2SAudioSpeakerBase::setup() { this->set_volume(this->volume_); this->set_mute_state(this->mute_state_); +#ifdef USE_I2S_AUDIO_FULL_DUPLEX + // The parent set up before us and already routed dout to the shared TX channel, which is never re-initialized. + // Parking would detach the pin for good. SPDIF speakers cannot use a full duplex bus. + if (this->parent_->is_full_duplex()) { + if (this->parent_->is_failed()) { + this->mark_failed(LOG_STR("Full duplex bus setup failed")); + } + return; + } +#endif + // Until the I2S driver first starts, dout sits in its reset state (often pulled high, or a JTAG // function on the ESP32-S3), which keeps a SPDIF optical transmitter lit. Park it low now. this->park_dout_pin_(); @@ -130,7 +141,7 @@ void I2SAudioSpeakerBase::loop() { // Handle the speaker's state switch (this->state_) { - case speaker::STATE_STARTING: + case speaker::STATE_STARTING: { if (this->status_has_error()) { break; } @@ -140,7 +151,21 @@ void I2SAudioSpeakerBase::loop() { break; } - if (this->start_i2s_driver(this->audio_stream_info_) != ESP_OK) { +#ifdef USE_I2S_AUDIO_FULL_DUPLEX + if (this->parent_->is_tx_in_use()) { + // Another speaker on the bus is playing; wait for it to release the channel + break; + } +#endif + const esp_err_t err = this->start_i2s_driver(this->audio_stream_info_); + if (err == ESP_ERR_NOT_SUPPORTED) { + // Retrying cannot help; stay stopped so the audio source gives up instead of waiting forever + ESP_LOGE(TAG, "Stream format not supported; not starting"); + this->rejected_stream_info_ = this->audio_stream_info_; + this->state_ = speaker::STATE_STOPPED; + break; + } + if (err != ESP_OK) { ESP_LOGE(TAG, "Driver failed to start; retrying in 1 second"); this->status_momentary_error(1000); break; @@ -159,6 +184,7 @@ void I2SAudioSpeakerBase::loop() { this->stop_i2s_driver_(false); // Stops the driver to return the lock; will be reloaded in next attempt } break; + } case speaker::STATE_RUNNING: // Intentional fallthrough case speaker::STATE_STOPPING: // Intentional fallthrough case speaker::STATE_STOPPED: @@ -240,6 +266,9 @@ void I2SAudioSpeakerBase::start() { return; if ((this->state_ == speaker::STATE_STARTING) || (this->state_ == speaker::STATE_RUNNING)) return; + // play() calls start() on every write, so without this a rejected stream would be retried and logged nonstop + if (this->rejected_stream_info_.has_value() && this->rejected_stream_info_.value() == this->audio_stream_info_) + return; xEventGroupSetBits(this->event_group_, SpeakerEventGroupBits::COMMAND_START); } @@ -277,6 +306,15 @@ esp_err_t I2SAudioSpeakerBase::init_i2s_channel_(const i2s_chan_config_t &chan_c return err; } + err = this->prepare_event_queues_(event_queue_size); + if (err != ESP_OK) { + this->delete_channel_(); + this->parent_->unlock(); + } + return err; +} + +esp_err_t I2SAudioSpeakerBase::prepare_event_queues_(size_t event_queue_size) { // Both queues are drained by the speaker task before it preloads, whether the channel is new or kept if (this->i2s_event_queue_ == nullptr) { this->i2s_event_queue_ = xQueueCreate(event_queue_size, sizeof(int64_t)); @@ -290,15 +328,41 @@ esp_err_t I2SAudioSpeakerBase::init_i2s_channel_(const i2s_chan_config_t &chan_c if (this->i2s_event_queue_ == nullptr || this->write_records_queue_ == nullptr) { ESP_LOGE(TAG, "Failed to allocate I2S event queue(s)"); - this->delete_channel_(); - this->parent_->unlock(); return ESP_ERR_NO_MEM; } return ESP_OK; } +#ifdef USE_I2S_AUDIO_FULL_DUPLEX +esp_err_t I2SAudioSpeakerBase::acquire_full_duplex_channel_(size_t event_queue_size) { + this->tx_handle_ = this->parent_->acquire_tx_channel(); + if (this->tx_handle_ == nullptr) { + ESP_LOGE(TAG, "Full duplex channel unavailable"); + return ESP_ERR_INVALID_STATE; + } + + esp_err_t err = this->prepare_event_queues_(event_queue_size); + if (err != ESP_OK) { + this->parent_->release_tx_channel(); + this->tx_handle_ = nullptr; + } + return err; +} +#endif + void I2SAudioSpeakerBase::stop_i2s_driver_(bool keep_channel) { +#ifdef USE_I2S_AUDIO_FULL_DUPLEX + if (this->parent_->is_full_duplex()) { + // The parent owns the channel pair and its pins, so only hand the TX channel back + if (this->tx_handle_ != nullptr) { + this->parent_->release_tx_channel(); + this->tx_handle_ = nullptr; + } + return; + } +#endif + if (this->tx_handle_ != nullptr) { i2s_channel_disable(this->tx_handle_); // A kept channel lets the next start skip i2s_new_channel and i2s_channel_init_std_mode, whose GPIO and @@ -372,9 +436,9 @@ void I2SAudioSpeakerBase::apply_software_volume_(uint8_t *data, size_t bytes_rea this->current_stream_info_.bytes_to_samples(bytes_read)); } -bool I2SAudioSpeakerBase::esp32_mono_swap_needed_() const { +bool I2SAudioSpeakerBase::esp32_mono_swap_needed(const audio::AudioStreamInfo &stream_info) { #ifdef USE_ESP32_VARIANT_ESP32 - return this->output_stream_info_.get_channels() == 1 && this->output_stream_info_.get_bits_per_sample() == 16; + return stream_info.get_channels() == 1 && stream_info.get_bits_per_sample() == 16; #else return false; #endif // USE_ESP32_VARIANT_ESP32 diff --git a/esphome/components/i2s_audio/speaker/i2s_audio_speaker.h b/esphome/components/i2s_audio/speaker/i2s_audio_speaker.h index 07b7e5d1905..03213347f0e 100644 --- a/esphome/components/i2s_audio/speaker/i2s_audio_speaker.h +++ b/esphome/components/i2s_audio/speaker/i2s_audio_speaker.h @@ -127,6 +127,18 @@ class I2SAudioSpeakerBase : public I2SAudioOut, public speaker::Speaker, public esp_err_t init_i2s_channel_(const i2s_chan_config_t &chan_cfg, const i2s_std_config_t &std_cfg, size_t event_queue_size); + /// @brief Creates the lockstep queues unless they exist already. + /// @param event_queue_size Size of each queue + /// @return ESP_OK if successful, or ESP_ERR_NO_MEM + esp_err_t prepare_event_queues_(size_t event_queue_size); + +#ifdef USE_I2S_AUDIO_FULL_DUPLEX + /// @brief Takes the TX channel of the parent's full duplex pair and prepares the event queues. + /// @param event_queue_size Size of the event queue + /// @return ESP_OK if successful, or an error code. On failure, hands the channel back to the parent. + esp_err_t acquire_full_duplex_channel_(size_t event_queue_size); +#endif + /// @brief Stops the I2S driver and unlocks the I2S port; keep_channel leaves the disabled channel allocated void stop_i2s_driver_(bool keep_channel); @@ -160,8 +172,10 @@ class I2SAudioSpeakerBase : public I2SAudioOut, public speaker::Speaker, public /// @param bytes_read Number of bytes of audio data void apply_software_volume_(uint8_t *data, size_t bytes_read); + /// @brief Whether 16-bit mono samples in this format need the ESP32 (non-variant) pair swap. + static bool esp32_mono_swap_needed(const audio::AudioStreamInfo &stream_info); /// @brief Whether the output is 16-bit mono on the original ESP32, which sends each pair of samples swapped. - bool esp32_mono_swap_needed_() const; + bool esp32_mono_swap_needed_() const { return esp32_mono_swap_needed(this->output_stream_info_); } /// @brief Swap adjacent 16-bit mono samples for ESP32 (non-variant) hardware quirk. /// Only applies when esp32_mono_swap_needed_() is true. `data` must start on a DMA word and hold whole pairs. @@ -182,6 +196,9 @@ class I2SAudioSpeakerBase : public I2SAudioOut, public speaker::Speaker, public optional timeout_; + // A format start_i2s_driver() refused. Those checks compare against fixed settings, so it can never start. + optional rejected_stream_info_; + bool pause_state_{false}; bool keep_alive_{false}; diff --git a/esphome/components/i2s_audio/speaker/i2s_audio_speaker_standard.cpp b/esphome/components/i2s_audio/speaker/i2s_audio_speaker_standard.cpp index 16abb2f22ef..73ad924fad7 100644 --- a/esphome/components/i2s_audio/speaker/i2s_audio_speaker_standard.cpp +++ b/esphome/components/i2s_audio/speaker/i2s_audio_speaker_standard.cpp @@ -86,7 +86,7 @@ void I2SAudioSpeaker::run_speaker_task() { // The ring buffer holds input-format audio (what play() receives), so size it from the input stream info. const size_t bytes_per_frame = this->current_stream_info_.frames_to_bytes(1); // Hand over whole ESP32 mono sample pairs so writes stay word-aligned - const size_t source_alignment = bytes_per_frame * this->dma_frame_multiple_(); + const size_t source_alignment = bytes_per_frame * dma_frame_multiple(this->output_stream_info_); const bool pair_aligned = source_alignment > bytes_per_frame; // Round the ring buffer size down to a multiple of source_alignment so the wrap boundary avoids splices. const size_t ring_buffer_size = @@ -111,7 +111,7 @@ void I2SAudioSpeaker::run_speaker_task() { } else { // Should not happen for a READY channel; fall back to the requested size. dma_buffer_bytes = this->output_stream_info_.frames_to_bytes( - dma_buffer_frames(this->output_stream_info_, this->dma_frame_multiple_())); + dma_buffer_frames(this->output_stream_info_, dma_frame_multiple(this->output_stream_info_))); } // dma_buffer_bytes counts output-format bytes; convert with the output stream info. const uint32_t frames_per_dma_buffer = this->output_stream_info_.bytes_to_frames(dma_buffer_bytes); @@ -442,6 +442,25 @@ esp_err_t I2SAudioSpeaker::start_i2s_driver(audio::AudioStreamInfo &audio_stream } #endif // USE_ESP32_VARIANT_ESP32 +#ifdef USE_I2S_AUDIO_FULL_DUPLEX + if (this->parent_->is_full_duplex()) { + // The parent set up the channel once from the configured format, so the stream must produce exactly that + const audio::AudioStreamInfo required = this->full_duplex_stream_info_(); + if (this->output_stream_info_ != required) { + ESP_LOGE(TAG, "Full duplex requires %u-bit, %u channel audio at %" PRIu32 " Hz", + (unsigned) required.get_bits_per_sample(), (unsigned) required.get_channels(), + required.get_sample_rate()); + if (this->output_stream_info_.get_channels() != required.get_channels()) { + ESP_LOGE(TAG, "A resampler does not change the channel count; add a mixer speaker with num_channels: %u", + (unsigned) required.get_channels()); + } + return ESP_ERR_NOT_SUPPORTED; + } + // The speaker task will enable the channel after preloading. + return this->acquire_full_duplex_channel_(I2S_EVENT_QUEUE_COUNT); + } +#endif // USE_I2S_AUDIO_FULL_DUPLEX + if (!this->parent_->try_lock()) { ESP_LOGE(TAG, "Parent bus is busy"); return ESP_ERR_INVALID_STATE; @@ -457,25 +476,37 @@ esp_err_t I2SAudioSpeaker::start_i2s_driver(audio::AudioStreamInfo &audio_stream this->current_stream_info_ = audio_stream_info; + i2s_chan_config_t chan_cfg; + i2s_std_config_t std_cfg; + this->build_i2s_config_(this->output_stream_info_, chan_cfg, std_cfg); + + // The speaker task will enable the channel after preloading. + return this->init_i2s_channel_(chan_cfg, std_cfg, I2S_EVENT_QUEUE_COUNT); +} + +#ifdef USE_I2S_AUDIO_FULL_DUPLEX +audio::AudioStreamInfo I2SAudioSpeaker::full_duplex_stream_info_() const { + return audio::AudioStreamInfo(static_cast(this->slot_bit_width_), + this->slot_mode_ == I2S_SLOT_MODE_STEREO ? 2 : 1, this->sample_rate_); +} + +void I2SAudioSpeaker::build_full_duplex_config(i2s_chan_config_t &chan_cfg, i2s_std_config_t &std_cfg) { + this->build_i2s_config_(this->full_duplex_stream_info_(), chan_cfg, std_cfg); +} +#endif // USE_I2S_AUDIO_FULL_DUPLEX + +void I2SAudioSpeaker::build_i2s_config_(const audio::AudioStreamInfo &output_stream_info, i2s_chan_config_t &chan_cfg, + i2s_std_config_t &std_cfg) const { // The DMA buffers hold output-format (post-narrowing) samples, so size them from the output stream info. - uint32_t dma_buffer_length = dma_buffer_frames(this->output_stream_info_, this->dma_frame_multiple_()); - - i2s_role_t i2s_role = this->i2s_role_; - i2s_clock_src_t clk_src = I2S_CLK_SRC_DEFAULT; - -#if SOC_CLK_APLL_SUPPORTED - if (this->use_apll_) { - clk_src = i2s_clock_src_t::I2S_CLK_SRC_APLL; - } -#endif // SOC_CLK_APLL_SUPPORTED + uint32_t dma_buffer_length = dma_buffer_frames(output_stream_info, dma_frame_multiple(output_stream_info)); // Log DMA configuration for debugging ESP_LOGV(TAG, "I2S DMA config: %zu buffers x %lu frames", (size_t) DMA_BUFFERS_COUNT, (unsigned long) dma_buffer_length); - i2s_chan_config_t chan_cfg = { + chan_cfg = { .id = this->parent_->get_port(), - .role = i2s_role, + .role = this->i2s_role_, .dma_desc_num = DMA_BUFFERS_COUNT, .dma_frame_num = dma_buffer_length, .auto_clear = true, @@ -484,22 +515,22 @@ esp_err_t I2SAudioSpeaker::start_i2s_driver(audio::AudioStreamInfo &audio_stream // Build standard I2S clock/slot/gpio configuration i2s_std_clk_config_t clk_cfg = { - .sample_rate_hz = audio_stream_info.get_sample_rate(), - .clk_src = clk_src, + .sample_rate_hz = output_stream_info.get_sample_rate(), + .clk_src = this->get_clock_source_(), .mclk_multiple = this->mclk_multiple_, }; i2s_slot_mode_t slot_mode = this->slot_mode_; i2s_std_slot_mask_t slot_mask = this->std_slot_mask_; - if (audio_stream_info.get_channels() == 1) { + if (output_stream_info.get_channels() == 1) { slot_mode = I2S_SLOT_MODE_MONO; - } else if (audio_stream_info.get_channels() == 2) { + } else if (output_stream_info.get_channels() == 2) { slot_mode = I2S_SLOT_MODE_STEREO; slot_mask = I2S_STD_SLOT_BOTH; } // Configure the data bit width from the output (post-narrowing) format, which is what is clocked out. - const i2s_data_bit_width_t data_bit_width = (i2s_data_bit_width_t) this->output_stream_info_.get_bits_per_sample(); + const i2s_data_bit_width_t data_bit_width = (i2s_data_bit_width_t) output_stream_info.get_bits_per_sample(); i2s_std_slot_config_t slot_cfg; switch (this->i2s_comm_fmt_) { case I2SCommFmt::PCM: @@ -535,20 +566,11 @@ esp_err_t I2SAudioSpeaker::start_i2s_driver(audio::AudioStreamInfo &audio_stream i2s_std_gpio_config_t gpio_cfg = this->parent_->get_pin_config(); gpio_cfg.dout = this->dout_pin_; - i2s_std_config_t std_cfg = { + std_cfg = { .clk_cfg = clk_cfg, .slot_cfg = slot_cfg, .gpio_cfg = gpio_cfg, }; - - esp_err_t err = this->init_i2s_channel_(chan_cfg, std_cfg, I2S_EVENT_QUEUE_COUNT); - if (err != ESP_OK) { - return err; - } - - // The speaker task will enable the channel after preloading. - - return ESP_OK; } } // namespace esphome::i2s_audio diff --git a/esphome/components/i2s_audio/speaker/i2s_audio_speaker_standard.h b/esphome/components/i2s_audio/speaker/i2s_audio_speaker_standard.h index 01390dfdf70..fb1184bb71e 100644 --- a/esphome/components/i2s_audio/speaker/i2s_audio_speaker_standard.h +++ b/esphome/components/i2s_audio/speaker/i2s_audio_speaker_standard.h @@ -20,12 +20,28 @@ class I2SAudioSpeaker final : public I2SAudioSpeakerBase { void set_i2s_comm_fmt(I2SCommFmt fmt) { this->i2s_comm_fmt_ = fmt; } +#ifdef USE_I2S_AUDIO_FULL_DUPLEX + void build_full_duplex_config(i2s_chan_config_t &chan_cfg, i2s_std_config_t &std_cfg) override; +#endif + protected: void run_speaker_task() override; esp_err_t start_i2s_driver(audio::AudioStreamInfo &audio_stream_info) override; + /// @brief Builds the channel and standard mode configuration for the given output format. + /// @param output_stream_info Format clocked out of the I2S peripheral + void build_i2s_config_(const audio::AudioStreamInfo &output_stream_info, i2s_chan_config_t &chan_cfg, + i2s_std_config_t &std_cfg) const; + +#ifdef USE_I2S_AUDIO_FULL_DUPLEX + /// @brief The output format of a full duplex bus, fixed by the configuration since the channel is set up once. + audio::AudioStreamInfo full_duplex_stream_info_() const; +#endif + /// @brief Frame multiple for DMA buffers and writes; 2 keeps ESP32 mono sample pairs word-aligned. - uint32_t dma_frame_multiple_() const { return this->esp32_mono_swap_needed_() ? 2 : 1; } + static uint32_t dma_frame_multiple(const audio::AudioStreamInfo &stream_info) { + return esp32_mono_swap_needed(stream_info) ? 2 : 1; + } I2SCommFmt i2s_comm_fmt_{I2SCommFmt::STANDARD}; }; diff --git a/esphome/components/iaqcore/iaqcore.cpp b/esphome/components/iaqcore/iaqcore.cpp index 48935c24681..73b6252cece 100644 --- a/esphome/components/iaqcore/iaqcore.cpp +++ b/esphome/components/iaqcore/iaqcore.cpp @@ -27,8 +27,7 @@ struct SensorData { void IAQCore::setup() { if (this->write(nullptr, 0) != i2c::ERROR_OK) { - ESP_LOGD(TAG, ESP_LOG_MSG_COMM_FAIL); - this->mark_failed(); + this->mark_failed(LOG_STR(ESP_LOG_MSG_COMM_FAIL)); return; } } @@ -89,9 +88,6 @@ void IAQCore::dump_config() { ESP_LOGCONFIG(TAG, "AMS iAQ Core:"); LOG_I2C_DEVICE(this); LOG_UPDATE_INTERVAL(this); - if (this->is_failed()) { - ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL); - } LOG_SENSOR(" ", "CO2", this->co2_); LOG_SENSOR(" ", "TVOC", this->tvoc_); } diff --git a/esphome/components/icm20689/icm20689.cpp b/esphome/components/icm20689/icm20689.cpp index 7331a3fe4f6..3bca35dc254 100644 --- a/esphome/components/icm20689/icm20689.cpp +++ b/esphome/components/icm20689/icm20689.cpp @@ -38,8 +38,7 @@ void ICM20689Component::setup() { this->spi_setup(); if (!this->spi_is_ready()) { - ESP_LOGE(TAG, "SPI device is not ready"); - this->mark_failed(LOG_STR(ESP_LOG_MSG_COMM_FAIL)); + this->mark_failed(LOG_STR("SPI device is not ready")); return; } @@ -61,7 +60,7 @@ void ICM20689Component::setup() { if (who_am_i != ICM20689_WHO_AM_I_VALUE) { ESP_LOGE(TAG, "Unexpected WHO_AM_I 0x%02X (expected 0x%02X) -- check wiring/CS", who_am_i, ICM20689_WHO_AM_I_VALUE); - this->mark_failed(LOG_STR(ESP_LOG_MSG_COMM_FAIL)); + this->mark_failed(LOG_STR("Wrong chip ID")); return; } @@ -77,10 +76,8 @@ void ICM20689Component::dump_config() { ESP_LOGCONFIG(TAG, "ICM20689 IMU:"); LOG_PIN(" CS Pin: ", this->cs_); - if (this->is_failed()) { - ESP_LOGE(TAG, " " ESP_LOG_MSG_COMM_FAIL); + if (this->is_failed()) return; - } ESP_LOGCONFIG(TAG, " Accel range: ±%dg", 2 << static_cast(this->accel_range_)); ESP_LOGCONFIG(TAG, " Gyro range: ±%d°/s", 250 << static_cast(this->gyro_range_)); diff --git a/esphome/components/ili9xxx/ili9xxx_display.cpp b/esphome/components/ili9xxx/ili9xxx_display.cpp index 0ed18c45da0..d2e1bc127e5 100644 --- a/esphome/components/ili9xxx/ili9xxx_display.cpp +++ b/esphome/components/ili9xxx/ili9xxx_display.cpp @@ -4,6 +4,9 @@ #include "esphome/core/helpers.h" #include "esphome/core/log.h" +#include +#include + namespace esphome::ili9xxx { static const uint16_t SPI_SETUP_US = 100; // estimated fixed overhead in microseconds for an SPI write @@ -67,7 +70,7 @@ void ILI9XXXDisplay::alloc_buffer_() { this->init_internal_(this->get_buffer_length_()); } if (this->buffer_ == nullptr) { - this->mark_failed(); + this->mark_failed(LOG_STR("Failed to allocate the display buffer")); } } @@ -119,9 +122,6 @@ void ILI9XXXDisplay::dump_config() { this->color_order_ == display::COLOR_ORDER_BGR ? LOG_STR_LITERAL("BGR") : LOG_STR_LITERAL("RGB"), YESNO(this->swap_xy_), YESNO(this->mirror_x_), YESNO(this->mirror_y_), YESNO(this->pre_invertcolors_)); - if (this->is_failed()) { - ESP_LOGCONFIG(TAG, " => Failed to init Memory: YES!"); - } LOG_UPDATE_INTERVAL(this); } @@ -313,7 +313,8 @@ void ILI9XXXDisplay::display_() { this->y_high_ = 0; } -// note that this bypasses the buffer and writes directly to the display. +// Outside update() this bypasses the buffer and writes directly to the display. Inside it the block goes into the +// buffer, or the flush at the end of update() would paint the old buffer contents over it. void ILI9XXXDisplay::draw_pixels_at(int x_start, int y_start, int w, int h, const uint8_t *ptr, display::ColorOrder order, display::ColorBitness bitness, bool big_endian, int x_offset, int y_offset, int x_pad) { @@ -322,14 +323,34 @@ void ILI9XXXDisplay::draw_pixels_at(int x_start, int y_start, int w, int h, cons // if color mapping or software rotation is required, hand this off to the parent implementation. This will // do color conversion pixel-by-pixel into the buffer and draw it later. If this is happening the user has not // configured the renderer well. - if (this->rotation_ != display::DISPLAY_ROTATION_0_DEGREES || bitness != display::COLOR_BITNESS_565 || !big_endian) { + if (this->rotation_ != display::DISPLAY_ROTATION_0_DEGREES || bitness != display::COLOR_BITNESS_565 || !big_endian || + order != display::COLOR_ORDER_RGB || this->is_clipping() || + (this->prossing_update_ && this->buffer_color_mode_ != BITS_16)) { display::Display::draw_pixels_at(x_start, y_start, w, h, ptr, order, bitness, big_endian, x_offset, y_offset, x_pad); return; } - this->set_addr_window_(x_start, y_start, x_start + w - 1, y_start + h - 1); // x_ and y_offset are offsets into the source buffer, unrelated to our own offsets into the display. auto stride = x_offset + w + x_pad; + if (this->prossing_update_) { + if (!this->check_buffer_()) + return; + // The buffer holds big endian RGB565, the same layout as the block + const int first_col = std::max(0, -x_start), end_col = std::min(w, this->get_width_internal() - x_start); + const int first_row = std::max(0, -y_start), end_row = std::min(h, this->get_height_internal() - y_start); + if (first_col >= end_col || first_row >= end_row) + return; + for (int y = first_row; y != end_row; y++) { + memcpy(this->buffer_ + ((y_start + y) * this->width_ + x_start + first_col) * 2, + ptr + ((y_offset + y) * stride + x_offset + first_col) * 2, (end_col - first_col) * 2); + } + this->x_low_ = std::min(this->x_low_, x_start + first_col); + this->y_low_ = std::min(this->y_low_, y_start + first_row); + this->x_high_ = std::max(this->x_high_, x_start + end_col - 1); + this->y_high_ = std::max(this->y_high_, y_start + end_row - 1); + return; + } + this->set_addr_window_(x_start, y_start, x_start + w - 1, y_start + h - 1); if (!this->is_18bitdisplay_) { if (x_offset == 0 && x_pad == 0 && y_offset == 0) { // we could deal here with a non-zero y_offset, but if x_offset is zero, y_offset probably will be so don't bother diff --git a/esphome/components/image/image.cpp b/esphome/components/image/image.cpp index d27d110248c..60d81ad62f1 100644 --- a/esphome/components/image/image.cpp +++ b/esphome/components/image/image.cpp @@ -1,5 +1,7 @@ #include "image.h" +#include + #include "esphome/core/hal.h" #include "esphome/core/helpers.h" @@ -7,92 +9,137 @@ ESPHOME_LOG_TAG(TAG, "image"); namespace esphome::image { +// Walks the clipped range with rows (ROWS_OUTER) or columns outer and hands the +// body its coordinates and row major index. The body must be ESPHOME_ALWAYS_INLINE, +// at -Os GCC otherwise calls it once per pixel. +template +static void for_each_pixel(int x_start, int x_end, int y_start, int y_end, int width, F &&f) { + const int outer_start = ROWS_OUTER ? y_start : x_start; + const int outer_end = ROWS_OUTER ? y_end : x_end; + const int inner_start = ROWS_OUTER ? x_start : y_start; + const int inner_end = ROWS_OUTER ? x_end : y_end; + for (int outer = outer_start; outer < outer_end; outer++) { + const int outer_pos = ROWS_OUTER ? outer * width : outer; + for (int inner = inner_start; inner < inner_end; inner++) { + f(ROWS_OUTER ? inner : outer, ROWS_OUTER ? outer : inner, + ROWS_OUTER ? outer_pos + inner : outer_pos + inner * width); + } + } +} + void Image::draw(int x, int y, display::Display *display, Color color_on, Color color_off) { - int img_x0 = 0; - int img_y0 = 0; - int w = width_; - int h = height_; + int x_start = 0; + int y_start = 0; + int x_end = width_; + int y_end = height_; auto clipping = display->get_clipping(); if (clipping.is_set()) { if (clipping.x > x) - img_x0 += clipping.x - x; + x_start += clipping.x - x; if (clipping.y > y) - img_y0 += clipping.y - y; - if (w > clipping.x2() - x) - w = clipping.x2() - x; - if (h > clipping.y2() - y) - h = clipping.y2() - y; + y_start += clipping.y - y; + if (x_end > clipping.x2() - x) + x_end = clipping.x2() - x; + if (y_end > clipping.y2() - y) + y_end = clipping.y2() - y; } - switch (type_) { - case IMAGE_TYPE_BINARY: { - for (int img_x = img_x0; img_x < w; img_x++) { - for (int img_y = img_y0; img_y < h; img_y++) { - if (this->get_binary_pixel_(img_x, img_y)) { - display->draw_pixel_at(x + img_x, y + img_y, color_on); - } else if (!this->transparency_) { - display->draw_pixel_at(x + img_x, y + img_y, color_off); - } - } - } +#ifndef USE_ESP8266 + // Opaque RGB565 and RGB are already laid out for draw_pixels_at(), so the display + // gets the whole window. Not on the ESP8266, whose image bytes need progmem_read_byte(). + if (this->transparency_ == TRANSPARENCY_OPAQUE && + (this->type_ == IMAGE_TYPE_RGB565 || this->type_ == IMAGE_TYPE_RGB)) { + // Keep the block on screen + x_start = std::max(x_start, -x); + y_start = std::max(y_start, -y); + x_end = std::min(x_end, display->get_width() - x); + y_end = std::min(y_end, display->get_height() - y); + if (x_end <= x_start || y_end <= y_start) + return; + const bool rgb565 = this->type_ == IMAGE_TYPE_RGB565; + display->draw_pixels_at(x + x_start, y + y_start, x_end - x_start, y_end - y_start, this->data_start_, + display::COLOR_ORDER_RGB, rgb565 ? display::COLOR_BITNESS_565 : display::COLOR_BITNESS_888, + this->big_endian_, x_start, y_start, this->width_ - x_end); + return; + } +#endif + + // Rows keep the read and the write sequential; when the display swaps the axes + // columns keep the write sequential instead, and a strided write costs more. +#ifdef USE_PSRAM + if (display->pixel_axes_swapped()) { + this->draw_(x, y, display, color_on, color_off, x_start, x_end, y_start, y_end); + return; + } +#endif + // Without PSRAM a strided write is free, so one copy of the loops is enough + this->draw_(x, y, display, color_on, color_off, x_start, x_end, y_start, y_end); +} + +template +void Image::draw_(int x, int y, display::Display *display, Color color_on, Color color_off, int x_start, int x_end, + int y_start, int y_end) { + // Local copies, or the virtual draw_pixel_at() forces a reload every pixel + const Transparency transparency = this->transparency_; + const int width = this->width_; + switch (this->type_) { + case IMAGE_TYPE_BINARY: + for_each_pixel(x_start, x_end, y_start, y_end, width, + [&](int img_x, int img_y, int pos) ESPHOME_ALWAYS_INLINE { + if (this->get_binary_pixel_(img_x, img_y)) { + display->draw_pixel_at(x + img_x, y + img_y, color_on); + } else if (transparency == TRANSPARENCY_OPAQUE) { + display->draw_pixel_at(x + img_x, y + img_y, color_off); + } + }); + break; + case IMAGE_TYPE_RGB111: + for_each_pixel(x_start, x_end, y_start, y_end, width, + [&](int img_x, int img_y, int pos) ESPHOME_ALWAYS_INLINE { + auto color = this->get_rgb111_pixel_(img_x, img_y); + if (color.w >= 0x80) { + display->draw_pixel_at(x + img_x, y + img_y, color); + } + }); + break; + case IMAGE_TYPE_GRAYSCALE: { + const uint8_t *const data = this->data_start_; + for_each_pixel(x_start, x_end, y_start, y_end, width, + [&](int img_x, int img_y, int pos) ESPHOME_ALWAYS_INLINE { + const uint8_t gray = progmem_read_byte(data + pos); + Color color = Color(gray, gray, gray, 0xFF); + if (transparency == TRANSPARENCY_CHROMA_KEY) { + if (gray == 1) { + return; // transparent pixel, skip drawing + } + } else if (transparency == TRANSPARENCY_ALPHA_CHANNEL) { + // gray is the alpha: blend from color_off to color_on, drawn opaque + color = Color(Color::blend_channel(color_off.r, color_on.r, gray), + Color::blend_channel(color_off.g, color_on.g, gray), + Color::blend_channel(color_off.b, color_on.b, gray), 0xFF); + } + display->draw_pixel_at(x + img_x, y + img_y, color); + }); break; } - case IMAGE_TYPE_RGB111: { - for (int img_x = img_x0; img_x < w; img_x++) { - for (int img_y = img_y0; img_y < h; img_y++) { - auto color = this->get_rgb111_pixel_(img_x, img_y); - if (color.w >= 0x80) { - display->draw_pixel_at(x + img_x, y + img_y, color); - } - } - } - break; - } - case IMAGE_TYPE_GRAYSCALE: - for (int img_x = img_x0; img_x < w; img_x++) { - for (int img_y = img_y0; img_y < h; img_y++) { - const uint32_t pos = (img_x + img_y * this->width_); - const uint8_t gray = progmem_read_byte(this->data_start_ + pos); - Color color = Color(gray, gray, gray, 0xFF); - switch (this->transparency_) { - case TRANSPARENCY_CHROMA_KEY: - if (gray == 1) { - continue; // skip drawing - } - break; - case TRANSPARENCY_ALPHA_CHANNEL: - // gray is the alpha: blend from color_off to color_on, drawn opaque - color = Color(Color::blend_channel(color_off.r, color_on.r, gray), - Color::blend_channel(color_off.g, color_on.g, gray), - Color::blend_channel(color_off.b, color_on.b, gray), 0xFF); - break; - default: - break; - } - display->draw_pixel_at(x + img_x, y + img_y, color); - } - } - break; case IMAGE_TYPE_RGB565: - for (int img_x = img_x0; img_x < w; img_x++) { - for (int img_y = img_y0; img_y < h; img_y++) { - auto color = this->get_rgb565_pixel_(img_x, img_y); - if (color.w >= 0x80) { - display->draw_pixel_at(x + img_x, y + img_y, color); - } - } - } + for_each_pixel(x_start, x_end, y_start, y_end, width, + [&](int img_x, int img_y, int pos) ESPHOME_ALWAYS_INLINE { + auto color = this->get_rgb565_pixel_(img_x, img_y); + if (color.w >= 0x80) { + display->draw_pixel_at(x + img_x, y + img_y, color); + } + }); break; case IMAGE_TYPE_RGB: - for (int img_x = img_x0; img_x < w; img_x++) { - for (int img_y = img_y0; img_y < h; img_y++) { - auto color = this->get_rgb_pixel_(img_x, img_y); - if (color.w >= 0x80) { - display->draw_pixel_at(x + img_x, y + img_y, color); - } - } - } + for_each_pixel(x_start, x_end, y_start, y_end, width, + [&](int img_x, int img_y, int pos) ESPHOME_ALWAYS_INLINE { + auto color = this->get_rgb_pixel_(img_x, img_y); + if (color.w >= 0x80) { + display->draw_pixel_at(x + img_x, y + img_y, color); + } + }); break; } } @@ -185,7 +232,7 @@ Color Image::get_rgb111_pixel_(int x, int y) const { return Color(rgb & 4 ? 0xFF : 0x00, rgb & 2 ? 0xFF : 0x00, rgb & 1 ? 0xFF : 0x00, opaque ? 0xFF : 0x00); } Color Image::get_rgb_pixel_(int x, int y) const { - const uint32_t pos = (x + y * this->width_) * this->bpp_ / 8; + const uint32_t pos = (x + y * this->width_) * static_cast(this->bpp_) / 8; Color color = Color(progmem_read_byte(this->data_start_ + pos + 2), progmem_read_byte(this->data_start_ + pos + 1), progmem_read_byte(this->data_start_ + pos + 0), 0xFF); @@ -205,8 +252,10 @@ Color Image::get_rgb_pixel_(int x, int y) const { return color; } Color Image::get_rgb565_pixel_(int x, int y) const { - const uint8_t *pos = this->data_start_ + (x + y * this->width_) * this->bpp_ / 8; - uint16_t rgb565 = encode_uint16(progmem_read_byte(pos + 1), progmem_read_byte(pos)); + const uint8_t *pos = this->data_start_ + (x + y * this->width_) * static_cast(this->bpp_) / 8; + // High byte first when big endian + const uint8_t high = this->big_endian_ ? 0 : 1; + const uint16_t rgb565 = encode_uint16(progmem_read_byte(pos + high), progmem_read_byte(pos + 1 - high)); auto r = (rgb565 & 0xF800) >> 11; auto g = (rgb565 & 0x07E0) >> 5; auto b = rgb565 & 0x001F; @@ -242,8 +291,14 @@ Color Image::get_grayscale_pixel_(int x, int y) const { int Image::get_width() const { return this->width_; } int Image::get_height() const { return this->height_; } ImageType Image::get_type() const { return this->type_; } -Image::Image(const uint8_t *data_start, int width, int height, ImageType type, Transparency transparency) - : width_(width), height_(height), type_(type), data_start_(data_start), transparency_(transparency) { +Image::Image(const uint8_t *data_start, int width, int height, ImageType type, Transparency transparency, + bool big_endian) + : width_(width), + height_(height), + type_(type), + data_start_(data_start), + transparency_(transparency), + big_endian_(big_endian) { switch (this->type_) { case IMAGE_TYPE_BINARY: this->bpp_ = 1; diff --git a/esphome/components/image/image.h b/esphome/components/image/image.h index a94f8137f41..b03befcddb4 100644 --- a/esphome/components/image/image.h +++ b/esphome/components/image/image.h @@ -24,7 +24,9 @@ enum Transparency { class Image : public display::BaseImage { public: - Image(const uint8_t *data_start, int width, int height, ImageType type, Transparency transparency); + /// big_endian is the RGB565 byte order; codegen writes little endian unless configured otherwise. + Image(const uint8_t *data_start, int width, int height, ImageType type, Transparency transparency, + bool big_endian = false); Color get_pixel(int x, int y, Color color_on = display::COLOR_ON, Color color_off = display::COLOR_OFF) const; int get_width() const override; int get_height() const override; @@ -44,6 +46,10 @@ class Image : public display::BaseImage { lv_image_dsc_t *get_lv_image_dsc(); #endif protected: + /// Per-pixel draw of the clipped range, rows (true) or columns (false) outer. + template + void draw_(int x, int y, display::Display *display, Color color_on, Color color_off, int x_start, int x_end, + int y_start, int y_end); bool get_binary_pixel_(int x, int y) const; Color get_rgb111_pixel_(int x, int y) const; Color get_rgb_pixel_(int x, int y) const; @@ -55,7 +61,8 @@ class Image : public display::BaseImage { ImageType type_; const uint8_t *data_start_; Transparency transparency_; - size_t bpp_{}; + bool big_endian_; + uint8_t bpp_{}; #ifdef USE_LVGL lv_img_dsc_t dsc_{}; #endif diff --git a/esphome/components/ina219/ina219.cpp b/esphome/components/ina219/ina219.cpp index a675099b4dc..48c9a06f504 100644 --- a/esphome/components/ina219/ina219.cpp +++ b/esphome/components/ina219/ina219.cpp @@ -36,7 +36,7 @@ void INA219Component::setup() { // Config Register // 0bx000000000000000 << 15 RESET Bit (1 -> trigger reset) if (!this->write_byte_16(INA219_REGISTER_CONFIG, 0x8000)) { - this->mark_failed(); + this->mark_failed(LOG_STR(ESP_LOG_MSG_COMM_FAIL)); return; } @@ -101,7 +101,7 @@ void INA219Component::setup() { config |= shunt_gain << 11; ESP_LOGCONFIG(TAG, " Using %dV-Range Shunt Gain=%dmV", bus_32v_range ? 32 : 16, 40 << shunt_gain); if (!this->write_byte_16(INA219_REGISTER_CONFIG, config)) { - this->mark_failed(); + this->mark_failed(LOG_STR(ESP_LOG_MSG_COMM_FAIL)); return; } @@ -122,7 +122,7 @@ void INA219Component::setup() { auto calibration = uint32_t(0.04096f / (0.000001f * lsb * this->shunt_resistance_ohm_)); ESP_LOGV(TAG, " Using LSB=%" PRIu32 " calibration=%" PRIu32, lsb, calibration); if (!this->write_byte_16(INA219_REGISTER_CALIBRATION, calibration)) { - this->mark_failed(); + this->mark_failed(LOG_STR(ESP_LOG_MSG_COMM_FAIL)); return; } } @@ -138,10 +138,8 @@ void INA219Component::dump_config() { ESP_LOGCONFIG(TAG, "INA219:"); LOG_I2C_DEVICE(this); - if (this->is_failed()) { - ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL); + if (this->is_failed()) return; - } LOG_UPDATE_INTERVAL(this); LOG_SENSOR(" ", "Bus Voltage", this->bus_voltage_sensor_); diff --git a/esphome/components/ina226/ina226.cpp b/esphome/components/ina226/ina226.cpp index 97b3caf949b..987ce25d60b 100644 --- a/esphome/components/ina226/ina226.cpp +++ b/esphome/components/ina226/ina226.cpp @@ -40,7 +40,7 @@ void INA226Component::setup() { config.reset = 1; if (!this->write_byte_16(INA226_REGISTER_CONFIG, config.raw)) { - this->mark_failed(); + this->mark_failed(LOG_STR(ESP_LOG_MSG_COMM_FAIL)); return; } delay(1); @@ -61,7 +61,7 @@ void INA226Component::setup() { config.mode = 0b111; if (!this->write_byte_16(INA226_REGISTER_CONFIG, config.raw)) { - this->mark_failed(); + this->mark_failed(LOG_STR(ESP_LOG_MSG_COMM_FAIL)); return; } @@ -75,7 +75,7 @@ void INA226Component::setup() { ESP_LOGV(TAG, " Using LSB=%" PRIu32 " calibration=%" PRIu32, lsb, calibration); if (!this->write_byte_16(INA226_REGISTER_CALIBRATION, calibration)) { - this->mark_failed(); + this->mark_failed(LOG_STR(ESP_LOG_MSG_COMM_FAIL)); return; } } @@ -84,10 +84,8 @@ void INA226Component::dump_config() { ESP_LOGCONFIG(TAG, "INA226:"); LOG_I2C_DEVICE(this); - if (this->is_failed()) { - ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL); + if (this->is_failed()) return; - } LOG_UPDATE_INTERVAL(this); ESP_LOGCONFIG(TAG, diff --git a/esphome/components/ina260/ina260.cpp b/esphome/components/ina260/ina260.cpp index 82a4e4fba14..0600e088390 100644 --- a/esphome/components/ina260/ina260.cpp +++ b/esphome/components/ina260/ina260.cpp @@ -36,8 +36,7 @@ static const uint8_t INA260_REGISTER_DEVICE_ID = 0xFF; void INA260Component::setup() { // Reset device on setup if (!this->write_byte_16(INA260_REGISTER_CONFIG, 0x8000)) { - this->error_code_ = DEVICE_RESET_FAILED; - this->mark_failed(); + this->mark_failed(LOG_STR("Device reset failed - Is the device connected?")); return; } @@ -47,14 +46,12 @@ void INA260Component::setup() { this->read_byte_16(INA260_REGISTER_DEVICE_ID, &this->device_id_); if (this->manufacture_id_ != (uint16_t) 0x5449 || this->device_id_ != (uint16_t) 0x2270) { - this->error_code_ = COMMUNICATION_FAILED; - this->mark_failed(); + this->mark_failed(LOG_STR("Connected device does not match a known INA260 sensor")); return; } if (!this->write_byte_16(INA260_REGISTER_CONFIG, (uint16_t) 0b0000001100000111)) { - this->error_code_ = FAILED_TO_UPDATE_CONFIGURATION; - this->mark_failed(); + this->mark_failed(LOG_STR("Failed to update device configuration")); return; } } @@ -70,21 +67,6 @@ void INA260Component::dump_config() { LOG_SENSOR(" ", "Bus Voltage", this->bus_voltage_sensor_); LOG_SENSOR(" ", "Current", this->current_sensor_); LOG_SENSOR(" ", "Power", this->power_sensor_); - - switch (this->error_code_) { - case COMMUNICATION_FAILED: - ESP_LOGE(TAG, "Connected device does not match a known INA260 sensor"); - break; - case DEVICE_RESET_FAILED: - ESP_LOGE(TAG, "Device reset failed - Is the device connected?"); - break; - case FAILED_TO_UPDATE_CONFIGURATION: - ESP_LOGE(TAG, "Failed to update device configuration"); - break; - case NONE: - default: - break; - } } void INA260Component::update() { diff --git a/esphome/components/ina260/ina260.h b/esphome/components/ina260/ina260.h index bbcb7a7acb2..162a4447e0f 100644 --- a/esphome/components/ina260/ina260.h +++ b/esphome/components/ina260/ina260.h @@ -23,13 +23,6 @@ class INA260Component final : public PollingComponent, public i2c::I2CDevice { sensor::Sensor *bus_voltage_sensor_{nullptr}; sensor::Sensor *current_sensor_{nullptr}; sensor::Sensor *power_sensor_{nullptr}; - - enum ErrorCode { - NONE, - COMMUNICATION_FAILED, - DEVICE_RESET_FAILED, - FAILED_TO_UPDATE_CONFIGURATION, - } error_code_{NONE}; }; } // namespace esphome::ina260 diff --git a/esphome/components/ina2xx_base/ina2xx_base.cpp b/esphome/components/ina2xx_base/ina2xx_base.cpp index 58e0598b06c..4c36870b680 100644 --- a/esphome/components/ina2xx_base/ina2xx_base.cpp +++ b/esphome/components/ina2xx_base/ina2xx_base.cpp @@ -50,15 +50,13 @@ static bool check_model_and_device_match(INAModel model, uint16_t dev_id) { void INA2XX::setup() { if (!this->reset_config_()) { - ESP_LOGE(TAG, "Reset failed, check connection"); - this->mark_failed(); + this->mark_failed(LOG_STR("Reset failed, check connection")); return; } delay(2); if (!this->check_device_model_()) { - ESP_LOGE(TAG, "Device not supported or model selected improperly in yaml file"); - this->mark_failed(); + this->mark_failed(LOG_STR("Device not supported or model selected improperly in yaml file")); return; } delay(1); @@ -197,9 +195,6 @@ void INA2XX::dump_config() { ESP_LOGE(TAG, " Device model mismatch. Found device with ID = %x. Please check your configuration.", this->dev_id_); } - if (this->is_failed()) { - ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL); - } LOG_UPDATE_INTERVAL(this); ESP_LOGCONFIG(TAG, " Shunt resistance = %f Ohm\n" diff --git a/esphome/components/ina3221/ina3221.cpp b/esphome/components/ina3221/ina3221.cpp index e66e2456767..455b4c0c7d2 100644 --- a/esphome/components/ina3221/ina3221.cpp +++ b/esphome/components/ina3221/ina3221.cpp @@ -40,13 +40,13 @@ static constexpr uint32_t READ_TIMEOUT_ID = 0; void INA3221Component::setup() { if (!this->write_byte_16(INA3221_REGISTER_CONFIG, INA3221_CONFIG_RESET)) { - this->mark_failed(); + this->mark_failed(LOG_STR(ESP_LOG_MSG_COMM_FAIL)); return; } delay(1); if (!this->write_byte_16(INA3221_REGISTER_CONFIG, this->config_)) { - this->mark_failed(); + this->mark_failed(LOG_STR(ESP_LOG_MSG_COMM_FAIL)); return; } @@ -57,7 +57,7 @@ void INA3221Component::setup() { !this->write_byte_16(INA3221_REGISTER_CHANNEL1_CRITICAL_ALERT + i, channel.critical_limit_)) || (channel.warning_limit_ != 0 && !this->write_byte_16(INA3221_REGISTER_CHANNEL1_WARNING_ALERT + i, channel.warning_limit_))) { - this->mark_failed(); + this->mark_failed(LOG_STR(ESP_LOG_MSG_COMM_FAIL)); return; } } @@ -67,7 +67,7 @@ void INA3221Component::setup() { // Sum the shunt voltages of every enabled channel if (this->sum_shunt_voltage_sensor_ != nullptr && !this->write_byte_16(INA3221_REGISTER_MASK_ENABLE, this->config_ & INA3221_CONFIG_CHANNEL_MASK)) { - this->mark_failed(); + this->mark_failed(LOG_STR(ESP_LOG_MSG_COMM_FAIL)); return; } #endif @@ -76,9 +76,6 @@ void INA3221Component::setup() { void INA3221Component::dump_config() { ESP_LOGCONFIG(TAG, "INA3221:"); LOG_I2C_DEVICE(this); - if (this->is_failed()) { - ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL); - } LOG_UPDATE_INTERVAL(this); ESP_LOGCONFIG(TAG, " Config register: 0x%04X", this->config_); if (this->single_shot_wait_ms_ != 0) { diff --git a/esphome/components/kamstrup_kmp/kamstrup_kmp.cpp b/esphome/components/kamstrup_kmp/kamstrup_kmp.cpp index 9713c72b613..fcf648c31f3 100644 --- a/esphome/components/kamstrup_kmp/kamstrup_kmp.cpp +++ b/esphome/components/kamstrup_kmp/kamstrup_kmp.cpp @@ -9,9 +9,6 @@ ESPHOME_LOG_TAG(TAG, "kamstrup_kmp"); void KamstrupKMPComponent::dump_config() { ESP_LOGCONFIG(TAG, "kamstrup_kmp:"); - if (this->is_failed()) { - ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL); - } LOG_UPDATE_INTERVAL(this); LOG_SENSOR(" ", "Heat Energy", this->heat_energy_sensor_); diff --git a/esphome/components/lcd_menu/lcd_menu.cpp b/esphome/components/lcd_menu/lcd_menu.cpp index 7ced6e67cec..b500461809b 100644 --- a/esphome/components/lcd_menu/lcd_menu.cpp +++ b/esphome/components/lcd_menu/lcd_menu.cpp @@ -8,7 +8,7 @@ ESPHOME_LOG_TAG(TAG, "lcd_menu"); void LCDCharacterMenuComponent::setup() { if (this->display_->is_failed()) { - this->mark_failed(); + this->mark_failed(LOG_STR("The connected display failed, the menu is disabled")); return; } @@ -24,9 +24,6 @@ void LCDCharacterMenuComponent::dump_config() { " Mark characters: %02x, %02x, %02x, %02x", this->columns_, this->rows_, this->mark_selected_, this->mark_editing_, this->mark_submenu_, this->mark_back_); - if (this->is_failed()) { - ESP_LOGE(TAG, "The connected display failed, the menu is disabled!"); - } } void LCDCharacterMenuComponent::draw_item(const display_menu_base::MenuItem *item, uint8_t row, bool selected) { diff --git a/esphome/components/lcd_pcf8574/pcf8574_display.cpp b/esphome/components/lcd_pcf8574/pcf8574_display.cpp index fc7f5ff2f50..2e2196be4ad 100644 --- a/esphome/components/lcd_pcf8574/pcf8574_display.cpp +++ b/esphome/components/lcd_pcf8574/pcf8574_display.cpp @@ -12,7 +12,7 @@ static const uint8_t LCD_DISPLAY_BACKLIGHT_OFF = 0x00; void PCF8574LCDDisplay::setup() { this->backlight_value_ = LCD_DISPLAY_BACKLIGHT_ON; if (!this->write_bytes(this->backlight_value_, nullptr, 0)) { - this->mark_failed(); + this->mark_failed(LOG_STR(ESP_LOG_MSG_COMM_FAIL)); return; } @@ -25,9 +25,6 @@ void PCF8574LCDDisplay::dump_config() { this->columns_, this->rows_); LOG_I2C_DEVICE(this); LOG_UPDATE_INTERVAL(this); - if (this->is_failed()) { - ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL); - } } void PCF8574LCDDisplay::write_n_bits(uint8_t value, uint8_t n) { if (n == 4) { diff --git a/esphome/components/lis2dw12/lis2dw12.cpp b/esphome/components/lis2dw12/lis2dw12.cpp index 38065aa7859..21e1a07676f 100644 --- a/esphome/components/lis2dw12/lis2dw12.cpp +++ b/esphome/components/lis2dw12/lis2dw12.cpp @@ -83,9 +83,13 @@ static const LogString *filter_bandwidth_to_string(FilterBandwidth bw) { void LIS2DW12Component::setup() { uint8_t who_am_i{0}; - if (!this->read_byte(static_cast(RegisterMap::WHO_AM_I), &who_am_i) || who_am_i != LIS2DW12_WHO_AM_I_VALUE) { + if (!this->read_byte(static_cast(RegisterMap::WHO_AM_I), &who_am_i)) { + this->mark_failed(LOG_STR(ESP_LOG_MSG_COMM_FAIL)); + return; + } + if (who_am_i != LIS2DW12_WHO_AM_I_VALUE) { ESP_LOGE(TAG, "WHO_AM_I check failed. Got 0x%02X, expected 0x%02X", who_am_i, LIS2DW12_WHO_AM_I_VALUE); - this->mark_failed(); + this->mark_failed(LOG_STR("Wrong chip ID")); return; } @@ -171,9 +175,6 @@ bool LIS2DW12Component::configure_registers_() { void LIS2DW12Component::dump_config() { ESP_LOGCONFIG(TAG, "LIS2DW12:"); - if (this->is_failed()) { - ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL); - } ESP_LOGCONFIG(TAG, " Power Mode: %s\n" " Data Rate: %s\n" diff --git a/esphome/components/lis3dh/lis3dh.cpp b/esphome/components/lis3dh/lis3dh.cpp index 22ca7ae56b2..f12c00852cd 100644 --- a/esphome/components/lis3dh/lis3dh.cpp +++ b/esphome/components/lis3dh/lis3dh.cpp @@ -29,19 +29,17 @@ void LIS3DHComponent::setup() { uint8_t who_am_i = 0; if (!this->read_byte(LIS3DH_REG_WHO_AM_I, &who_am_i)) { - ESP_LOGE(TAG, "Failed to read chip ID - check wiring / address"); - this->mark_failed(); + this->mark_failed(LOG_STR("Failed to read chip ID - check wiring / address")); return; } if (who_am_i != LIS3DH_WHO_AM_I_VALUE) { ESP_LOGE(TAG, "Wrong chip ID: 0x%02X (expected 0x%02X)", who_am_i, LIS3DH_WHO_AM_I_VALUE); - this->mark_failed(); + this->mark_failed(LOG_STR("Wrong chip ID")); return; } if (!this->setup_accelerometer_() || !this->setup_temperature_() || !this->setup_interrupt_()) { - ESP_LOGE(TAG, "Failed to configure sensor"); - this->mark_failed(); + this->mark_failed(LOG_STR("Failed to configure sensor")); return; } } @@ -119,10 +117,8 @@ bool LIS3DHComponent::setup_interrupt_() { void LIS3DHComponent::dump_config() { ESP_LOGCONFIG(TAG, "LIS3DH:"); LOG_I2C_DEVICE(this); - if (this->is_failed()) { - ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL); + if (this->is_failed()) return; - } ESP_LOGCONFIG(TAG, " Range: ±%ug\n" diff --git a/esphome/components/lm75b/lm75b.cpp b/esphome/components/lm75b/lm75b.cpp index 56e69faa62f..388f5dfd93a 100644 --- a/esphome/components/lm75b/lm75b.cpp +++ b/esphome/components/lm75b/lm75b.cpp @@ -9,9 +9,6 @@ ESPHOME_LOG_TAG(TAG, "lm75b"); void LM75BComponent::dump_config() { ESP_LOGCONFIG(TAG, "LM75B:"); LOG_I2C_DEVICE(this); - if (this->is_failed()) { - ESP_LOGE(TAG, "Setting up LM75B failed!"); - } LOG_UPDATE_INTERVAL(this); LOG_SENSOR(" ", "Temperature", this); } diff --git a/esphome/components/lp5562/lp5562_output.cpp b/esphome/components/lp5562/lp5562_output.cpp index 356ef2c0669..b55e8b297d2 100644 --- a/esphome/components/lp5562/lp5562_output.cpp +++ b/esphome/components/lp5562/lp5562_output.cpp @@ -33,7 +33,7 @@ void LP5562Output::setup() { // 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(); + this->mark_failed(LOG_STR(ESP_LOG_MSG_COMM_FAIL)); return; } delay(1); @@ -41,16 +41,13 @@ void LP5562Output::setup() { // 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(); + this->mark_failed(LOG_STR(ESP_LOG_MSG_COMM_FAIL)); } } 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) { diff --git a/esphome/components/ltr501/ltr501.cpp b/esphome/components/ltr501/ltr501.cpp index 91fca7fa9ba..a3f082051cc 100644 --- a/esphome/components/ltr501/ltr501.cpp +++ b/esphome/components/ltr501/ltr501.cpp @@ -115,10 +115,6 @@ void LTRAlsPs501Component::dump_config() { LOG_SENSOR(" ", "CH1 Infrared counts", this->infrared_counts_sensor_); LOG_SENSOR(" ", "CH0 Visible+IR counts", this->full_spectrum_counts_sensor_); LOG_SENSOR(" ", "Actual gain", this->actual_gain_sensor_); - - if (this->is_failed()) { - ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL); - } } void LTRAlsPs501Component::update() { @@ -151,7 +147,7 @@ void LTRAlsPs501Component::loop() { err = this->write(nullptr, 0); if (err != i2c::ERROR_OK) { ESP_LOGW(TAG, "i2c connection failed"); - this->mark_failed(); + this->mark_failed(LOG_STR(ESP_LOG_MSG_COMM_FAIL)); } this->configure_reset_(); if (this->is_als_()) { diff --git a/esphome/components/ltr_als_ps/ltr_als_ps.cpp b/esphome/components/ltr_als_ps/ltr_als_ps.cpp index 2d3b3755ad5..743bd68529d 100644 --- a/esphome/components/ltr_als_ps/ltr_als_ps.cpp +++ b/esphome/components/ltr_als_ps/ltr_als_ps.cpp @@ -109,10 +109,6 @@ void LTRAlsPsComponent::dump_config() { LOG_SENSOR(" ", "Proximity counts", this->proximity_counts_sensor_); } LOG_UPDATE_INTERVAL(this); - - if (this->is_failed()) { - ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL); - } } void LTRAlsPsComponent::update() { @@ -142,7 +138,7 @@ void LTRAlsPsComponent::loop() { err = this->write(nullptr, 0); if (err != i2c::ERROR_OK) { ESP_LOGV(TAG, "i2c connection failed"); - this->mark_failed(); + this->mark_failed(LOG_STR(ESP_LOG_MSG_COMM_FAIL)); } this->configure_reset_(); if (this->is_als_()) { diff --git a/esphome/components/max17043/max17043.cpp b/esphome/components/max17043/max17043.cpp index a15658512b2..c64875e67c9 100644 --- a/esphome/components/max17043/max17043.cpp +++ b/esphome/components/max17043/max17043.cpp @@ -55,16 +55,13 @@ void MAX17043Component::setup() { ESP_LOGV(TAG, "MAX17043 CONFIG register reads 0x%X", config_reg); if (config_reg != MAX17043_CONFIG_POWER_UP_DEFAULT) { - ESP_LOGE(TAG, "Device does not appear to be a MAX17043"); - this->status_set_error(LOG_STR("unrecognised")); - this->mark_failed(); + this->mark_failed(LOG_STR("Device does not appear to be a MAX17043")); return; } // need to write back to config register to reset the sleep bit if (!this->write_byte_16(MAX17043_CONFIG, MAX17043_CONFIG_POWER_UP_DEFAULT)) { - this->status_set_error(LOG_STR("sleep reset failed")); - this->mark_failed(); + this->mark_failed(LOG_STR("sleep reset failed")); return; } } @@ -72,9 +69,6 @@ void MAX17043Component::setup() { void MAX17043Component::dump_config() { ESP_LOGCONFIG(TAG, "MAX17043:"); LOG_I2C_DEVICE(this); - if (this->is_failed()) { - ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL); - } LOG_UPDATE_INTERVAL(this); LOG_SENSOR(" ", "Battery Voltage", this->voltage_sensor_); LOG_SENSOR(" ", "Battery Level", this->battery_remaining_sensor_); diff --git a/esphome/components/max17048/max17048.cpp b/esphome/components/max17048/max17048.cpp index 67f9dbaac3d..1caf8ab308b 100644 --- a/esphome/components/max17048/max17048.cpp +++ b/esphome/components/max17048/max17048.cpp @@ -65,9 +65,6 @@ void MAX17048Component::update() { void MAX17048Component::dump_config() { ESP_LOGCONFIG(TAG, "MAX17048:"); LOG_I2C_DEVICE(this); - if (this->is_failed()) { - ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL); - } LOG_UPDATE_INTERVAL(this); LOG_SENSOR(" ", "Battery Voltage", this->battery_voltage_sensor_); LOG_SENSOR(" ", "Battery Level", this->battery_level_sensor_); diff --git a/esphome/components/max44009/max44009.cpp b/esphome/components/max44009/max44009.cpp index b616b9032ee..10df08bbeb4 100644 --- a/esphome/components/max44009/max44009.cpp +++ b/esphome/components/max44009/max44009.cpp @@ -39,15 +39,12 @@ void MAX44009Sensor::setup() { } } if (!state_ok) - this->mark_failed(); + this->mark_failed(LOG_STR(ESP_LOG_MSG_COMM_FAIL)); } void MAX44009Sensor::dump_config() { ESP_LOGCONFIG(TAG, "MAX44009:"); LOG_I2C_DEVICE(this); - if (this->is_failed()) { - ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL); - } } void MAX44009Sensor::update() { diff --git a/esphome/components/mcp4461/mcp4461.cpp b/esphome/components/mcp4461/mcp4461.cpp index b6e8c247021..800ec631e75 100644 --- a/esphome/components/mcp4461/mcp4461.cpp +++ b/esphome/components/mcp4461/mcp4461.cpp @@ -12,7 +12,7 @@ void Mcp4461Component::setup() { auto err = this->write(nullptr, 0); if (err != i2c::ERROR_OK) { this->error_code_ = MCP4461_STATUS_I2C_ERROR; - this->mark_failed(); + this->mark_failed(Mcp4461Component::get_message_string(this->error_code_)); return; } // save WP/WL status @@ -96,9 +96,6 @@ void Mcp4461Component::update_write_protection_status_() { void Mcp4461Component::dump_config() { ESP_LOGCONFIG(TAG, "mcp4461:"); LOG_I2C_DEVICE(this); - if (this->is_failed()) { - ESP_LOGE(TAG, "%s", LOG_STR_ARG(this->get_message_string(this->error_code_))); - } // log wiper status for (uint8_t i = 0; i < 8; ++i) { // terminals only valid for volatile wipers 0-3 - enable/disable is terminal hw @@ -218,7 +215,7 @@ uint8_t Mcp4461Component::get_status_register_() { uint16_t buf; if (!this->read_16_(reg, &buf)) { this->error_code_ = MCP4461_STATUS_REGISTER_ERROR; - this->mark_failed(); + this->mark_failed(Mcp4461Component::get_message_string(this->error_code_)); return 0; } uint8_t msb = buf >> 8; @@ -227,7 +224,7 @@ uint8_t Mcp4461Component::get_status_register_() { // D8, D7 and R1 bits are hardlocked to 1 -> a status msb bit 0 (bit 9 of status register) of 0 or lsb bit 1/7 = 0 // indicate device/communication issues, therefore mark component failed this->error_code_ = MCP4461_STATUS_REGISTER_INVALID; - this->mark_failed(); + this->mark_failed(Mcp4461Component::get_message_string(this->error_code_)); return 0; } this->status_clear_warning(); diff --git a/esphome/components/mcp4725/mcp4725.cpp b/esphome/components/mcp4725/mcp4725.cpp index faa377b8de9..f25765f4a3a 100644 --- a/esphome/components/mcp4725/mcp4725.cpp +++ b/esphome/components/mcp4725/mcp4725.cpp @@ -8,19 +8,12 @@ ESPHOME_LOG_TAG(TAG, "mcp4725"); void MCP4725::setup() { auto err = this->write(nullptr, 0); if (err != i2c::ERROR_OK) { - this->error_code_ = COMMUNICATION_FAILED; - this->mark_failed(); + this->mark_failed(LOG_STR(ESP_LOG_MSG_COMM_FAIL)); return; } } -void MCP4725::dump_config() { - LOG_I2C_DEVICE(this); - - if (this->error_code_ == COMMUNICATION_FAILED) { - ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL); - } -} +void MCP4725::dump_config() { LOG_I2C_DEVICE(this); } // https://learn.sparkfun.com/tutorials/mcp4725-digital-to-analog-converter-hookup-guide?_ga=2.176055202.1402343014.1607953301-893095255.1606753886 void MCP4725::write_state(float state) { diff --git a/esphome/components/mcp4725/mcp4725.h b/esphome/components/mcp4725/mcp4725.h index a0838dc33e6..9c4b95db3f0 100644 --- a/esphome/components/mcp4725/mcp4725.h +++ b/esphome/components/mcp4725/mcp4725.h @@ -16,7 +16,6 @@ class MCP4725 final : public Component, public output::FloatOutput, public i2c:: void write_state(float state) override; protected: - enum ErrorCode { NONE = 0, COMMUNICATION_FAILED } error_code_{NONE}; }; } // namespace esphome::mcp4725 diff --git a/esphome/components/mcp4728/mcp4728.cpp b/esphome/components/mcp4728/mcp4728.cpp index 7259a0a89ec..48336af339d 100644 --- a/esphome/components/mcp4728/mcp4728.cpp +++ b/esphome/components/mcp4728/mcp4728.cpp @@ -10,7 +10,7 @@ ESPHOME_LOG_TAG(TAG, "mcp4728"); void MCP4728Component::setup() { auto err = this->write(nullptr, 0); if (err != i2c::ERROR_OK) { - this->mark_failed(); + this->mark_failed(LOG_STR(ESP_LOG_MSG_COMM_FAIL)); return; } } @@ -18,9 +18,6 @@ void MCP4728Component::setup() { void MCP4728Component::dump_config() { ESP_LOGCONFIG(TAG, "MCP4728:"); LOG_I2C_DEVICE(this); - if (this->is_failed()) { - ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL); - } } void MCP4728Component::loop() { diff --git a/esphome/components/mcp9600/mcp9600.cpp b/esphome/components/mcp9600/mcp9600.cpp index 70e7cb176d4..d99cf63b471 100644 --- a/esphome/components/mcp9600/mcp9600.cpp +++ b/esphome/components/mcp9600/mcp9600.cpp @@ -33,8 +33,7 @@ void MCP9600Component::setup() { // Allows both MCP9600's and MCP9601's to be connected. if (this->device_id_ != (uint8_t) 0x40 && this->device_id_ != (uint8_t) 0x41) { - this->error_code_ = COMMUNICATION_FAILED; - this->mark_failed(); + this->mark_failed(LOG_STR("Connected device does not match a known MCP9600 or MCP9601 sensor")); return; } @@ -55,8 +54,7 @@ void MCP9600Component::setup() { success &= this->write_byte_16(MCP9600_REGISTER_ALERT4_LIMIT, 0x0000); if (!success) { - this->error_code_ = FAILED_TO_UPDATE_CONFIGURATION; - this->mark_failed(); + this->mark_failed(LOG_STR("Failed to update device configuration")); return; } } @@ -70,18 +68,6 @@ void MCP9600Component::dump_config() { LOG_UPDATE_INTERVAL(this); LOG_SENSOR(" ", "Hot Junction Temperature", this->hot_junction_sensor_); LOG_SENSOR(" ", "Cold Junction Temperature", this->cold_junction_sensor_); - - switch (this->error_code_) { - case COMMUNICATION_FAILED: - ESP_LOGE(TAG, "Connected device does not match a known MCP9600 or MCP901 sensor"); - break; - case FAILED_TO_UPDATE_CONFIGURATION: - ESP_LOGE(TAG, "Failed to update device configuration"); - break; - case NONE: - default: - break; - } } void MCP9600Component::update() { diff --git a/esphome/components/mcp9600/mcp9600.h b/esphome/components/mcp9600/mcp9600.h index 523c9ace2f7..88dfd046ceb 100644 --- a/esphome/components/mcp9600/mcp9600.h +++ b/esphome/components/mcp9600/mcp9600.h @@ -36,12 +36,6 @@ class MCP9600Component final : public PollingComponent, public i2c::I2CDevice { sensor::Sensor *cold_junction_sensor_{nullptr}; MCP9600ThermocoupleType thermocouple_type_{MCP9600_THERMOCOUPLE_TYPE_K}; - - enum ErrorCode { - NONE, - COMMUNICATION_FAILED, - FAILED_TO_UPDATE_CONFIGURATION, - } error_code_{NONE}; }; } // namespace esphome::mcp9600 diff --git a/esphome/components/mcp9808/mcp9808.cpp b/esphome/components/mcp9808/mcp9808.cpp index 47df37f4563..309651970ca 100644 --- a/esphome/components/mcp9808/mcp9808.cpp +++ b/esphome/components/mcp9808/mcp9808.cpp @@ -18,24 +18,29 @@ ESPHOME_LOG_TAG(TAG, "mcp9808"); void MCP9808Sensor::setup() { uint16_t manu = 0; - if (!this->read_byte_16(MCP9808_REG_MANUF_ID, &manu) || manu != MCP9808_MANUF_ID) { - this->mark_failed(); + if (!this->read_byte_16(MCP9808_REG_MANUF_ID, &manu)) { + this->mark_failed(LOG_STR(ESP_LOG_MSG_COMM_FAIL)); + return; + } + if (manu != MCP9808_MANUF_ID) { ESP_LOGE(TAG, "Incorrect manufacturer ID (%X) for '%s'", manu, this->name_.c_str()); + this->mark_failed(LOG_STR("Wrong chip ID")); return; } uint16_t dev_id = 0; - if (!this->read_byte_16(MCP9808_REG_DEVICE_ID, &dev_id) || dev_id != MCP9808_DEV_ID) { - this->mark_failed(); + if (!this->read_byte_16(MCP9808_REG_DEVICE_ID, &dev_id)) { + this->mark_failed(LOG_STR(ESP_LOG_MSG_COMM_FAIL)); + return; + } + if (dev_id != MCP9808_DEV_ID) { ESP_LOGE(TAG, "Incorrect device ID (%X) for '%s'", dev_id, this->name_.c_str()); + this->mark_failed(LOG_STR("Wrong chip ID")); return; } } void MCP9808Sensor::dump_config() { ESP_LOGCONFIG(TAG, "%s:", this->name_.c_str()); LOG_I2C_DEVICE(this); - if (this->is_failed()) { - ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL_FOR, this->name_.c_str()); - } LOG_UPDATE_INTERVAL(this); LOG_SENSOR(" ", "Temperature", this); } diff --git a/esphome/components/mipi_dsi/display.py b/esphome/components/mipi_dsi/display.py index 6bf94bf364b..97af7a33243 100644 --- a/esphome/components/mipi_dsi/display.py +++ b/esphome/components/mipi_dsi/display.py @@ -198,6 +198,7 @@ async def to_code(config: ConfigType) -> None: model.get_dimensions(config) ) var = cg.new_Pvariable(config[CONF_ID], width, height, color_depth, pixel_mode) + cg.add(var.set_big_endian(config[CONF_BYTE_ORDER] == BYTE_ORDER_BIG)) sequence = model.get_sequence(config) cg.add(var.set_model(config[CONF_MODEL])) diff --git a/esphome/components/mipi_dsi/mipi_dsi.cpp b/esphome/components/mipi_dsi/mipi_dsi.cpp index 64166012c20..31bf69b178c 100644 --- a/esphome/components/mipi_dsi/mipi_dsi.cpp +++ b/esphome/components/mipi_dsi/mipi_dsi.cpp @@ -3,6 +3,9 @@ #include "mipi_dsi.h" #include "esphome/core/helpers.h" +#include +#include + namespace esphome::mipi_dsi { // Maximum bytes to log for init commands (truncated if larger) @@ -180,6 +183,7 @@ void MipiDsi::update() { if (this->auto_clear_enabled_) { this->clear(); } + this->updating_ = true; if (this->show_test_card_) { this->test_card(); } else if (this->page_ != nullptr) { @@ -189,7 +193,13 @@ void MipiDsi::update() { } else { this->stop_poller(); } - if (this->buffer_ == nullptr || this->x_low_ > this->x_high_ || this->y_low_ > this->y_high_) + this->updating_ = false; + this->flush_dirty_(); +} + +void MipiDsi::flush_dirty_() { + // Inside update() the one flush at its end sends everything + if (this->updating_ || this->buffer_ == nullptr || this->x_low_ > this->x_high_ || this->y_low_ > this->y_high_) return; ESP_LOGV(TAG, "x_low %d, y_low %d, x_high %d, y_high %d", this->x_low_, this->y_low_, this->x_high_, this->y_high_); int w = this->x_high_ - this->x_low_ + 1; @@ -207,11 +217,34 @@ void MipiDsi::draw_pixels_at(int x_start, int y_start, int w, int h, const uint8 display::ColorBitness bitness, bool big_endian, int x_offset, int y_offset, int x_pad) { if (w <= 0 || h <= 0) return; - // if color mapping is required, pass the buck. - // note that endianness is not considered here - it is assumed to match! - if (bitness != this->color_depth_) { + if (bitness != this->color_depth_ || order != display::COLOR_ORDER_RGB || + (bitness == display::COLOR_BITNESS_565 && big_endian != this->big_endian_) || + this->rotation_ != display::DISPLAY_ROTATION_0_DEGREES || this->is_clipping()) { display::Display::draw_pixels_at(x_start, y_start, w, h, ptr, order, bitness, big_endian, x_offset, y_offset, x_pad); + this->mark_dirty_(x_start, y_start, w, h); + this->flush_dirty_(); + return; + } + if (this->buffer_ != nullptr) { + // With a buffer the block goes through it, or a flush of the old contents would paint over it + const int first_col = std::max(0, -x_start), end_col = std::min(w, static_cast(this->width_) - x_start); + const int first_row = std::max(0, -y_start), end_row = std::min(h, static_cast(this->height_) - y_start); + if (first_col >= end_col || first_row >= end_row) + return; + const size_t bytes_per_pixel = 3 - this->color_depth_; + const size_t stride = x_offset + w + x_pad; + for (int y = first_row; y != end_row; y++) { + memcpy(this->buffer_ + ((y_start + y) * this->width_ + x_start + first_col) * bytes_per_pixel, + ptr + ((y_offset + y) * stride + x_offset + first_col) * bytes_per_pixel, + (end_col - first_col) * bytes_per_pixel); + } + if (this->updating_) { + this->mark_dirty_(x_start + first_col, y_start + first_row, end_col - first_col, end_row - first_row); + } else { + this->write_to_display_(x_start + first_col, y_start + first_row, end_col - first_col, end_row - first_row, ptr, + x_offset + first_col, y_offset + first_row, x_pad + w - end_col); + } return; } this->write_to_display_(x_start, y_start, w, h, ptr, x_offset, y_offset, x_pad); @@ -259,26 +292,37 @@ bool MipiDsi::check_buffer_() { return true; } +// Widen the dirty box to the drawn rectangle, even where the buffer already held the same values. +void MipiDsi::mark_dirty_(int x_start, int y_start, int w, int h) { + int x1 = x_start, y1 = y_start; + int x2 = x_start + w - 1, y2 = y_start + h - 1; + this->rotate_(x1, y1); + this->rotate_(x2, y2); + const int right = static_cast(this->width_) - 1; + const int bottom = static_cast(this->height_) - 1; + const int left = std::clamp(std::min(x1, x2), 0, right); + const int top = std::clamp(std::min(y1, y2), 0, bottom); + x2 = std::clamp(std::max(x1, x2), 0, right); + y2 = std::clamp(std::max(y1, y2), 0, bottom); + // An empty box is low above high, so it is replaced rather than widened + if (this->x_low_ > this->x_high_ || this->y_low_ > this->y_high_) { + this->x_low_ = left; + this->x_high_ = x2; + this->y_low_ = top; + this->y_high_ = y2; + return; + } + this->x_low_ = std::min(this->x_low_, left); + this->x_high_ = std::max(this->x_high_, x2); + this->y_low_ = std::min(this->y_low_, top); + this->y_high_ = std::max(this->y_high_, y2); +} + void MipiDsi::draw_pixel_at(int x, int y, Color color) { if (this->is_point_clipped(x, y)) return; - switch (this->rotation_) { - case display::DISPLAY_ROTATION_0_DEGREES: - break; - case display::DISPLAY_ROTATION_90_DEGREES: - std::swap(x, y); - x = this->width_ - x - 1; - break; - case display::DISPLAY_ROTATION_180_DEGREES: - x = this->width_ - x - 1; - y = this->height_ - y - 1; - break; - case display::DISPLAY_ROTATION_270_DEGREES: - std::swap(x, y); - y = this->height_ - y - 1; - break; - } + this->rotate_(x, y); if (x >= this->get_width_internal() || x < 0 || y >= this->get_height_internal() || y < 0) { return; } @@ -288,9 +332,7 @@ void MipiDsi::draw_pixel_at(int x, int y, Color color) { switch (this->color_depth_) { case display::COLOR_BITNESS_565: { auto *ptr_16 = reinterpret_cast(this->buffer_); - uint8_t hi_byte = static_cast(color.r & 0xF8) | (color.g >> 5); - uint8_t lo_byte = static_cast((color.g & 0x1C) << 3) | (color.b >> 3); - uint16_t new_color = lo_byte | (hi_byte << 8); // little endian + const uint16_t new_color = this->to_panel_565_(color); if (ptr_16[pos] == new_color) return; ptr_16[pos] = new_color; @@ -328,9 +370,7 @@ void MipiDsi::fill(Color color) { switch (this->color_depth_) { case display::COLOR_BITNESS_565: { auto *ptr_16 = reinterpret_cast(this->buffer_); - uint8_t hi_byte = static_cast(color.r & 0xF8) | (color.g >> 5); - uint8_t lo_byte = static_cast((color.g & 0x1C) << 3) | (color.b >> 3); - uint16_t new_color = lo_byte | (hi_byte << 8); // little endian + const uint16_t new_color = this->to_panel_565_(color); std::fill_n(ptr_16, this->width_ * this->height_, new_color); break; } diff --git a/esphome/components/mipi_dsi/mipi_dsi.h b/esphome/components/mipi_dsi/mipi_dsi.h index ba3f29ca05a..dd1b0dc5f90 100644 --- a/esphome/components/mipi_dsi/mipi_dsi.h +++ b/esphome/components/mipi_dsi/mipi_dsi.h @@ -6,16 +6,20 @@ // only applicable on ESP32-P4 #ifdef USE_ESP32_VARIANT_ESP32P4 #include "esphome/core/component.h" +#include "esphome/core/helpers.h" #include "esphome/core/application.h" #include "esphome/core/log.h" #include "esphome/core/gpio.h" #include "esphome/components/display/display.h" +#include "esphome/components/display/display_color_utils.h" #include "esp_lcd_panel_ops.h" #include "esp_lcd_panel_io.h" #include "esp_lcd_mipi_dsi.h" +#include + namespace esphome::mipi_dsi { ESPHOME_LOG_TAG(TAG, "display.mipi_dsi"); @@ -39,6 +43,7 @@ class MipiDsi final : public display::Display { public: MipiDsi(size_t width, size_t height, display::ColorBitness color_depth, uint8_t pixel_mode) : width_(width), height_(height), color_depth_(color_depth), pixel_mode_(pixel_mode) {} + void set_big_endian(bool big_endian) { this->big_endian_ = big_endian; } void set_invert_colors(bool invert_colors) { this->invert_colors_ = invert_colors; } display::DisplayType get_display_type() override { return display::DisplayType::DISPLAY_TYPE_COLOR; } @@ -81,6 +86,33 @@ class MipiDsi final : public display::Display { void write_to_display_(int x_start, int y_start, int w, int h, const uint8_t *ptr, int x_offset, int y_offset, int x_pad); bool check_buffer_(); + /// Send the region touched since the last flush to the panel. + void flush_dirty_(); + // Maps drawn coordinates to the buffer the way draw_pixel_at() stores them + void rotate_(int &x, int &y) const { + switch (this->rotation_) { + case display::DISPLAY_ROTATION_0_DEGREES: + break; + case display::DISPLAY_ROTATION_90_DEGREES: + std::swap(x, y); + x = this->width_ - x - 1; + break; + case display::DISPLAY_ROTATION_180_DEGREES: + x = this->width_ - x - 1; + y = this->height_ - y - 1; + break; + case display::DISPLAY_ROTATION_270_DEGREES: + std::swap(x, y); + y = this->height_ - y - 1; + break; + } + } + void mark_dirty_(int x_start, int y_start, int w, int h); + /// Pack a colour as the 565 value the panel's byte order expects in memory. + uint16_t to_panel_565_(Color color) const { + const uint16_t rgb565 = display::ColorUtil::color_to_565(color); + return this->big_endian_ ? convert_big_endian(rgb565) : rgb565; + } GPIOPin *reset_pin_{nullptr}; std::vector enable_pins_{}; size_t width_{}; @@ -101,6 +133,8 @@ class MipiDsi final : public display::Display { bool invert_colors_{}; display::ColorBitness color_depth_; + bool big_endian_{false}; + bool updating_{false}; uint8_t pixel_mode_{}; esp_lcd_panel_handle_t handle_{}; diff --git a/esphome/components/mipi_rgb/display.py b/esphome/components/mipi_rgb/display.py index 71234c2f25b..6e508dd7c44 100644 --- a/esphome/components/mipi_rgb/display.py +++ b/esphome/components/mipi_rgb/display.py @@ -290,6 +290,7 @@ async def to_code(config: ConfigType) -> None: cg.add(var.set_init_sequence(table, len(sequence))) cg.add(var.set_color_mode(COLOR_ORDERS[config[CONF_COLOR_ORDER]])) + cg.add(var.set_big_endian(config[CONF_BYTE_ORDER] == BYTE_ORDER_BIG)) cg.add(var.set_invert_colors(config[CONF_INVERT_COLORS])) cg.add(var.set_hsync_pulse_width(config[CONF_HSYNC_PULSE_WIDTH])) cg.add(var.set_hsync_back_porch(config[CONF_HSYNC_BACK_PORCH])) diff --git a/esphome/components/mipi_rgb/mipi_rgb.cpp b/esphome/components/mipi_rgb/mipi_rgb.cpp index 4928b6f6672..2bc300e5b82 100644 --- a/esphome/components/mipi_rgb/mipi_rgb.cpp +++ b/esphome/components/mipi_rgb/mipi_rgb.cpp @@ -4,6 +4,9 @@ #include "esphome/core/hal.h" #include "esphome/core/helpers.h" #include "esphome/core/log.h" + +#include +#include #include #include #include @@ -185,6 +188,7 @@ void MipiRgb::update() { if (this->auto_clear_enabled_) { this->clear(); } + this->updating_ = true; if (this->show_test_card_) { this->test_card(); } else if (this->page_ != nullptr) { @@ -194,7 +198,13 @@ void MipiRgb::update() { } else { this->stop_poller(); } - if (this->buffer_ == nullptr || this->x_low_ > this->x_high_ || this->y_low_ > this->y_high_) + this->updating_ = false; + this->flush_dirty_(); +} + +void MipiRgb::flush_dirty_() { + // Inside update() the one flush at its end sends everything + if (this->updating_ || this->buffer_ == nullptr || this->x_low_ > this->x_high_ || this->y_low_ > this->y_high_) return; ESP_LOGV(TAG, "x_low %d, y_low %d, x_high %d, y_high %d", this->x_low_, this->y_low_, this->x_high_, this->y_high_); int w = this->x_high_ - this->x_low_ + 1; @@ -212,12 +222,28 @@ void MipiRgb::draw_pixels_at(int x_start, int y_start, int w, int h, const uint8 display::ColorBitness bitness, bool big_endian, int x_offset, int y_offset, int x_pad) { if (w <= 0 || h <= 0 || this->is_failed()) return; - // if color mapping is required, pass the buck. - // note that endianness is not considered here - it is assumed to match! - if (bitness != display::COLOR_BITNESS_565) { + if (bitness != display::COLOR_BITNESS_565 || order != display::COLOR_ORDER_RGB || big_endian != this->big_endian_ || + this->rotation_ != display::DISPLAY_ROTATION_0_DEGREES || this->is_clipping()) { Display::draw_pixels_at(x_start, y_start, w, h, ptr, order, bitness, big_endian, x_offset, y_offset, x_pad); - this->write_to_display_(x_start, y_start, w, h, reinterpret_cast(this->buffer_), x_start, y_start, - this->width_ - w - x_start); + this->mark_dirty_(x_start, y_start, w, h); + this->flush_dirty_(); + } else if (this->buffer_ != nullptr) { + // With a buffer the block goes through it, or a flush of the old contents would paint over it + const int first_col = std::max(0, -x_start), end_col = std::min(w, static_cast(this->width_) - x_start); + const int first_row = std::max(0, -y_start), end_row = std::min(h, static_cast(this->height_) - y_start); + if (first_col >= end_col || first_row >= end_row) + return; + const size_t stride = x_offset + w + x_pad; + for (int y = first_row; y != end_row; y++) { + memcpy(this->buffer_ + (y_start + y) * this->width_ + x_start + first_col, + ptr + ((y_offset + y) * stride + x_offset + first_col) * 2, (end_col - first_col) * 2); + } + if (this->updating_) { + this->mark_dirty_(x_start + first_col, y_start + first_row, end_col - first_col, end_row - first_row); + } else { + this->write_to_display_(x_start + first_col, y_start + first_row, end_col - first_col, end_row - first_row, ptr, + x_offset + first_col, y_offset + first_row, x_pad + w - end_col); + } } else { this->write_to_display_(x_start, y_start, w, h, ptr, x_offset, y_offset, x_pad); } @@ -260,33 +286,44 @@ bool MipiRgb::check_buffer_() { return true; } +// Widen the dirty box to the drawn rectangle, even where the buffer already held the same values. +void MipiRgb::mark_dirty_(int x_start, int y_start, int w, int h) { + int x1 = x_start, y1 = y_start; + int x2 = x_start + w - 1, y2 = y_start + h - 1; + this->rotate_(x1, y1); + this->rotate_(x2, y2); + const int right = static_cast(this->width_) - 1; + const int bottom = static_cast(this->height_) - 1; + const int left = std::clamp(std::min(x1, x2), 0, right); + const int top = std::clamp(std::min(y1, y2), 0, bottom); + x2 = std::clamp(std::max(x1, x2), 0, right); + y2 = std::clamp(std::max(y1, y2), 0, bottom); + // An empty box is low above high, so it is replaced rather than widened + if (this->x_low_ > this->x_high_ || this->y_low_ > this->y_high_) { + this->x_low_ = left; + this->x_high_ = x2; + this->y_low_ = top; + this->y_high_ = y2; + return; + } + this->x_low_ = std::min(this->x_low_, left); + this->x_high_ = std::max(this->x_high_, x2); + this->y_low_ = std::min(this->y_low_, top); + this->y_high_ = std::max(this->y_high_, y2); +} + void MipiRgb::draw_pixel_at(int x, int y, Color color) { if (this->is_point_clipped(x, y) || this->is_failed()) return; - switch (this->rotation_) { - case display::DISPLAY_ROTATION_0_DEGREES: - break; - case display::DISPLAY_ROTATION_90_DEGREES: - std::swap(x, y); - x = this->width_ - x - 1; - break; - case display::DISPLAY_ROTATION_180_DEGREES: - x = this->width_ - x - 1; - y = this->height_ - y - 1; - break; - case display::DISPLAY_ROTATION_270_DEGREES: - std::swap(x, y); - y = this->height_ - y - 1; - break; - } + this->rotate_(x, y); if (x >= this->get_width_internal() || x < 0 || y >= this->get_height_internal() || y < 0) { return; } if (!this->check_buffer_()) return; size_t pos = (y * this->width_) + x; - uint16_t new_color = convert_big_endian(display::ColorUtil::color_to_565(color)); + uint16_t new_color = this->to_panel_565_(color); if (this->buffer_[pos] == new_color) return; this->buffer_[pos] = new_color; @@ -311,7 +348,7 @@ void MipiRgb::fill(Color color) { } auto *ptr_16 = reinterpret_cast(this->buffer_); - uint16_t new_color = convert_big_endian(display::ColorUtil::color_to_565(color)); + uint16_t new_color = this->to_panel_565_(color); std::fill_n(ptr_16, this->width_ * this->height_, new_color); this->x_low_ = 0; this->y_low_ = 0; diff --git a/esphome/components/mipi_rgb/mipi_rgb.h b/esphome/components/mipi_rgb/mipi_rgb.h index 01b1ebee91f..393e052b26b 100644 --- a/esphome/components/mipi_rgb/mipi_rgb.h +++ b/esphome/components/mipi_rgb/mipi_rgb.h @@ -4,7 +4,9 @@ #include "esphome/core/gpio.h" #include "esphome/core/log.h" #include "esphome/components/display/display.h" +#include "esphome/components/display/display_color_utils.h" #include +#include #ifdef USE_SPI #include "esphome/components/spi/spi.h" #endif @@ -39,6 +41,7 @@ class MipiRgb : public display::Display { display::ColorOrder get_color_mode() { return this->color_mode_; } void set_color_mode(display::ColorOrder color_mode) { this->color_mode_ = color_mode; } + void set_big_endian(bool big_endian) { this->big_endian_ = big_endian; } void set_invert_colors(bool invert_colors) { this->invert_colors_ = invert_colors; } void add_data_pin(InternalGPIOPin *data_pin, size_t index) { this->data_pins_[index] = data_pin; }; @@ -71,6 +74,33 @@ class MipiRgb : public display::Display { void write_to_display_(int x_start, int y_start, int w, int h, const uint8_t *ptr, int x_offset, int y_offset, int x_pad); bool check_buffer_(); + /// Pack a colour as the 565 value the panel's byte order expects in memory. + uint16_t to_panel_565_(Color color) const { + const uint16_t rgb565 = display::ColorUtil::color_to_565(color); + return this->big_endian_ ? convert_big_endian(rgb565) : rgb565; + } + /// Send the region touched since the last flush to the panel. + void flush_dirty_(); + // Maps drawn coordinates to the buffer the way draw_pixel_at() stores them + void rotate_(int &x, int &y) const { + switch (this->rotation_) { + case display::DISPLAY_ROTATION_0_DEGREES: + break; + case display::DISPLAY_ROTATION_90_DEGREES: + std::swap(x, y); + x = this->width_ - x - 1; + break; + case display::DISPLAY_ROTATION_180_DEGREES: + x = this->width_ - x - 1; + y = this->height_ - y - 1; + break; + case display::DISPLAY_ROTATION_270_DEGREES: + std::swap(x, y); + y = this->height_ - y - 1; + break; + } + } + void mark_dirty_(int x_start, int y_start, int w, int h); void dump_pins_(uint8_t start, uint8_t end, const char *name, uint8_t offset); void setup_enables_(); void common_setup_(); @@ -91,6 +121,8 @@ class MipiRgb : public display::Display { const char *model_{"Unknown"}; bool invert_colors_{}; display::ColorOrder color_mode_{display::COLOR_ORDER_BGR}; + bool big_endian_{true}; + bool updating_{false}; size_t width_; size_t height_; uint16_t *buffer_{nullptr}; diff --git a/esphome/components/mipi_spi/mipi_spi.h b/esphome/components/mipi_spi/mipi_spi.h index f55ae723780..cbf025e8c31 100644 --- a/esphome/components/mipi_spi/mipi_spi.h +++ b/esphome/components/mipi_spi/mipi_spi.h @@ -1,5 +1,7 @@ #pragma once +#include +#include #include #include "esphome/components/spi/spi.h" @@ -205,9 +207,8 @@ class MipiSpi : public display::Display, return; if (w <= 0 || h <= 0) return; - if (get_pixel_mode(bitness) != BUFFERPIXEL || big_endian != IS_BIG_ENDIAN) { - // note that the usual logging macros are banned in header files, so use their replacement - esph_log_e(TAG, "Unsupported color depth or bit order"); + if (!this->is_native_block_(order, bitness, big_endian)) { + this->draw_pixels_slow(x_start, y_start, w, h, ptr, order, bitness, big_endian, x_offset, y_offset, x_pad); return; } this->write_to_display_(x_start, y_start, w, h, reinterpret_cast(ptr), x_offset, y_offset, @@ -419,6 +420,18 @@ class MipiSpi : public display::Display, } } + // True when the block is already in the buffer pixel format and can be copied without per pixel work + bool is_native_block_(display::ColorOrder order, display::ColorBitness bitness, bool big_endian) const { + return get_pixel_mode(bitness) == BUFFERPIXEL && big_endian == IS_BIG_ENDIAN && order == display::COLOR_ORDER_RGB && + (HAS_HARDWARE_ROTATION || this->rotation_ == display::DISPLAY_ROTATION_0_DEGREES) && !this->is_clipping(); + } + + // Without a buffer there is nothing to convert a foreign block into, so it is dropped + virtual void draw_pixels_slow(int x_start, int y_start, int w, int h, const uint8_t *ptr, display::ColorOrder order, + display::ColorBitness bitness, bool big_endian, int x_offset, int y_offset, int x_pad) { + esph_log_e(TAG, "Unsupported color depth, order, bit order or rotation"); + } + /** * Writes a buffer to the display. * @@ -570,6 +583,7 @@ class MipiSpiBuffer if (this->auto_clear_enabled_) { this->clear(); } + this->updating_ = true; if (this->show_test_card_) { this->test_card(); } else if (this->page_ != nullptr) { @@ -577,6 +591,7 @@ class MipiSpiBuffer } else if (this->writer_.has_value()) { (*this->writer_)(*this); } + this->updating_ = false; #if ESPHOME_LOG_LEVEL == ESPHOME_LOG_LEVEL_VERBOSE esph_log_v(TAG, "Drawing from line %d took %dms", this->start_line_, millis() - lap); lap = millis(); @@ -610,6 +625,42 @@ class MipiSpiBuffer #endif } + // While update() runs the writer, a block goes into the buffer slice, or the slice flush would paint over it. + // Outside update() (e.g. LVGL) it is written straight to the display. + void draw_pixels_at(int x_start, int y_start, int w, int h, const uint8_t *ptr, display::ColorOrder order, + display::ColorBitness bitness, bool big_endian, int x_offset, int y_offset, int x_pad) override { + if (!this->updating_) { + MipiSpiBuffer::MipiSpi::draw_pixels_at(x_start, y_start, w, h, ptr, order, bitness, big_endian, x_offset, + y_offset, x_pad); + return; + } + if (this->is_failed() || w <= 0 || h <= 0) + return; + if (!this->is_native_block_(order, bitness, big_endian)) { + display::Display::draw_pixels_at(x_start, y_start, w, h, ptr, order, bitness, big_endian, x_offset, y_offset, + x_pad); + return; + } + // Only the rows inside the current slice are kept + const int first_col = std::max(0, -x_start); + const int end_col = std::min(w, this->get_width_internal() - x_start); + const int first_row = std::max(0, this->start_line_ - y_start); + const int end_row = std::min(h, this->end_line_ - y_start); + if (first_col >= end_col || first_row >= end_row) + return; + const size_t stride = x_offset + w + x_pad; + const size_t buffer_stride = round_buffer(this->get_width_internal()); + for (int y = first_row; y != end_row; y++) { + memcpy(this->buffer_ + (y_start + y - this->start_line_) * buffer_stride + x_start + first_col, + ptr + ((y_offset + y) * stride + x_offset + first_col) * sizeof(BUFFERTYPE), + (end_col - first_col) * sizeof(BUFFERTYPE)); + } + this->x_low_ = std::min(this->x_low_, x_start + first_col); + this->y_low_ = std::min(this->y_low_, y_start + first_row); + this->x_high_ = std::max(this->x_high_, x_start + end_col - 1); + this->y_high_ = std::max(this->y_high_, y_start + end_row - 1); + } + // With hardware rotation the buffer is written in drawn coordinates. bool pixel_axes_swapped() const override { return !HAS_HARDWARE_ROTATION && display::Display::pixel_axes_swapped(); } @@ -665,8 +716,12 @@ class MipiSpiBuffer } protected: - // Rotate the coordinates to match the display orientation. - + void draw_pixels_slow(int x_start, int y_start, int w, int h, const uint8_t *ptr, display::ColorOrder order, + display::ColorBitness bitness, bool big_endian, int x_offset, int y_offset, + int x_pad) override { + display::Display::draw_pixels_at(x_start, y_start, w, h, ptr, order, bitness, big_endian, x_offset, y_offset, + x_pad); + } // Convert a color to the buffer pixel format. static BUFFERTYPE convert_color(const Color &color) { if constexpr (BUFFERPIXEL == PIXEL_MODE_8) { @@ -688,6 +743,7 @@ class MipiSpiBuffer uint16_t y_high_{0}; uint16_t start_line_{0}; uint16_t end_line_{1}; + bool updating_{false}; }; } // namespace esphome::mipi_spi diff --git a/esphome/components/mlx90393/sensor_mlx90393.cpp b/esphome/components/mlx90393/sensor_mlx90393.cpp index e47b7d8c30b..7aa01a976d9 100644 --- a/esphome/components/mlx90393/sensor_mlx90393.cpp +++ b/esphome/components/mlx90393/sensor_mlx90393.cpp @@ -112,7 +112,7 @@ void MLX90393Cls::setup() { this->mlx_.begin_with_hal(this, 0, 0); if (!this->apply_all_settings_()) { - this->mark_failed(); + this->mark_failed(LOG_STR(ESP_LOG_MSG_COMM_FAIL)); } // start verify settings process @@ -123,10 +123,8 @@ void MLX90393Cls::dump_config() { ESP_LOGCONFIG(TAG, "MLX90393:"); LOG_I2C_DEVICE(this); - if (this->is_failed()) { - ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL); + if (this->is_failed()) return; - } LOG_UPDATE_INTERVAL(this); LOG_SENSOR(" ", "X Axis", this->x_sensor_); @@ -250,16 +248,12 @@ void MLX90393Cls::verify_settings_timeout_(MLX90393Setting stage) { if (!is_setting_ok) { if (this->mlx_.checkStatus(this->mlx_.reset()) != MLX90393::STATUS_OK) { - ESP_LOGE(TAG, "failed to reset device"); - this->status_set_error(); - this->mark_failed(); + this->mark_failed(LOG_STR("Failed to reset device")); return; } if (!this->apply_all_settings_()) { - ESP_LOGE(TAG, "failed to re-apply settings"); - this->status_set_error(); - this->mark_failed(); + this->mark_failed(LOG_STR("Failed to re-apply settings")); } else { ESP_LOGI(TAG, "reset and re-apply settings completed"); } diff --git a/esphome/components/mmc5603/mmc5603.cpp b/esphome/components/mmc5603/mmc5603.cpp index b7d16b30edd..fa5ba5985d7 100644 --- a/esphome/components/mmc5603/mmc5603.cpp +++ b/esphome/components/mmc5603/mmc5603.cpp @@ -34,35 +34,27 @@ static const uint8_t MMC5603_ODR_REG = 0x1A; void MMC5603Component::setup() { uint8_t id = 0; if (!this->read_byte(MMC56X3_PRODUCT_ID, &id)) { - this->error_code_ = COMMUNICATION_FAILED; - this->mark_failed(); + this->mark_failed(LOG_STR(ESP_LOG_MSG_COMM_FAIL)); return; } if (id != 0 && id != MMC56X3_CHIP_ID) { // ID is not reported correctly by all chips, 0 on some chips - ESP_LOGCONFIG(TAG, "Chip Wrong"); - this->error_code_ = ID_REGISTERS; - this->mark_failed(); + this->mark_failed(LOG_STR("The ID registers don't match - Is this really an MMC5603?")); return; } if (!this->write_byte(MMC56X3_CTRL1_REG, 0x80)) { // turn on set bit - ESP_LOGCONFIG(TAG, "Control 1 Failed for set bit"); - this->error_code_ = COMMUNICATION_FAILED; - this->mark_failed(); + this->mark_failed(LOG_STR("Control 1 failed for set bit")); return; } if (!this->write_byte(MMC56X3_CTRL0_REG, 0x08)) { // turn on set bit - ESP_LOGCONFIG(TAG, "Control 0 Failed for set bit"); - this->error_code_ = COMMUNICATION_FAILED; - this->mark_failed(); + this->mark_failed(LOG_STR("Control 0 failed for set bit")); return; } if (!this->write_byte(MMC56X3_CTRL0_REG, 0x10)) { - this->error_code_ = COMMUNICATION_FAILED; - this->mark_failed(); + this->mark_failed(LOG_STR(ESP_LOG_MSG_COMM_FAIL)); return; } @@ -70,19 +62,13 @@ void MMC5603Component::setup() { ctrl_2 &= ~0x10; // turn off cmm_en bit if (!this->write_byte(MMC56X3_CTRL2_REG, ctrl_2)) { - this->error_code_ = COMMUNICATION_FAILED; - this->mark_failed(); + this->mark_failed(LOG_STR(ESP_LOG_MSG_COMM_FAIL)); return; } } void MMC5603Component::dump_config() { ESP_LOGCONFIG(TAG, "MMC5603:"); LOG_I2C_DEVICE(this); - if (this->error_code_ == COMMUNICATION_FAILED) { - ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL); - } else if (this->error_code_ == ID_REGISTERS) { - ESP_LOGE(TAG, "The ID registers don't match - Is this really an MMC5603?"); - } LOG_UPDATE_INTERVAL(this); ESP_LOGCONFIG(TAG, " Auto set/reset: %s", ONOFF(this->auto_set_reset_)); diff --git a/esphome/components/mmc5603/mmc5603.h b/esphome/components/mmc5603/mmc5603.h index d291e6d2728..9187e019fb3 100644 --- a/esphome/components/mmc5603/mmc5603.h +++ b/esphome/components/mmc5603/mmc5603.h @@ -32,11 +32,6 @@ class MMC5603Component final : public PollingComponent, public i2c::I2CDevice { sensor::Sensor *z_sensor_{nullptr}; sensor::Sensor *heading_sensor_{nullptr}; bool auto_set_reset_{true}; - enum ErrorCode { - NONE = 0, - COMMUNICATION_FAILED, - ID_REGISTERS, - } error_code_{NONE}; }; } // namespace esphome::mmc5603 diff --git a/esphome/components/mpl3115a2/mpl3115a2.cpp b/esphome/components/mpl3115a2/mpl3115a2.cpp index db892716258..4a206b30dfe 100644 --- a/esphome/components/mpl3115a2/mpl3115a2.cpp +++ b/esphome/components/mpl3115a2/mpl3115a2.cpp @@ -10,13 +10,11 @@ ESPHOME_LOG_TAG(TAG, "mpl3115a2"); void MPL3115A2Component::setup() { uint8_t whoami = 0xFF; if (!this->read_byte(MPL3115A2_WHOAMI, &whoami)) { - this->error_code_ = COMMUNICATION_FAILED; - this->mark_failed(); + this->mark_failed(LOG_STR(ESP_LOG_MSG_COMM_FAIL)); return; } if (whoami != 0xC4) { - this->error_code_ = WRONG_ID; - this->mark_failed(); + this->mark_failed(LOG_STR("MPL3115A2 has invalid id")); return; } @@ -32,19 +30,6 @@ void MPL3115A2Component::setup() { void MPL3115A2Component::dump_config() { ESP_LOGCONFIG(TAG, "MPL3115A2:"); LOG_I2C_DEVICE(this); - if (this->is_failed()) { - switch (this->error_code_) { - case COMMUNICATION_FAILED: - ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL); - break; - case WRONG_ID: - ESP_LOGE(TAG, "MPL3115A2 has invalid id"); - break; - default: - ESP_LOGE(TAG, "Setting up MPL3115A2 registers failed!"); - break; - } - } LOG_UPDATE_INTERVAL(this); LOG_SENSOR(" ", "Temperature", this->temperature_); LOG_SENSOR(" ", "Pressure", this->pressure_); diff --git a/esphome/components/mpl3115a2/mpl3115a2.h b/esphome/components/mpl3115a2/mpl3115a2.h index a6163673cbf..a8af1ff0fea 100644 --- a/esphome/components/mpl3115a2/mpl3115a2.h +++ b/esphome/components/mpl3115a2/mpl3115a2.h @@ -94,11 +94,6 @@ class MPL3115A2Component final : public PollingComponent, public i2c::I2CDevice sensor::Sensor *temperature_{nullptr}; sensor::Sensor *altitude_{nullptr}; sensor::Sensor *pressure_{nullptr}; - enum ErrorCode { - NONE = 0, - COMMUNICATION_FAILED, - WRONG_ID, - } error_code_{NONE}; }; } // namespace esphome::mpl3115a2 diff --git a/esphome/components/mpr121/mpr121.cpp b/esphome/components/mpr121/mpr121.cpp index 26cdcae6138..3d9cded59ff 100644 --- a/esphome/components/mpr121/mpr121.cpp +++ b/esphome/components/mpr121/mpr121.cpp @@ -15,8 +15,7 @@ void MPR121Component::setup() { this->write_byte(MPR121_SOFTRESET, 0x63); this->set_timeout(100, [this]() { if (!this->write_byte(MPR121_ECR, 0x0)) { - this->error_code_ = COMMUNICATION_FAILED; - this->mark_failed(); + this->mark_failed(LOG_STR(ESP_LOG_MSG_COMM_FAIL)); return; } // set touch sensitivity for all 12 channels @@ -70,14 +69,6 @@ void MPR121Component::set_release_debounce(uint8_t debounce) { void MPR121Component::dump_config() { ESP_LOGCONFIG(TAG, "MPR121:"); LOG_I2C_DEVICE(this); - switch (this->error_code_) { - case COMMUNICATION_FAILED: - ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL); - break; - case NONE: - default: - break; - } } void MPR121Component::loop() { uint16_t val = 0; diff --git a/esphome/components/mpr121/mpr121.h b/esphome/components/mpr121/mpr121.h index 64c4b291b3d..4d945ded872 100644 --- a/esphome/components/mpr121/mpr121.h +++ b/esphome/components/mpr121/mpr121.h @@ -84,10 +84,6 @@ class MPR121Component final : public Component, public i2c::I2CDevice { uint8_t touch_threshold_{}; uint8_t release_threshold_{}; uint8_t max_touch_channel_{3}; - enum ErrorCode { - NONE = 0, - COMMUNICATION_FAILED, - } error_code_{NONE}; bool flush_gpio_(); diff --git a/esphome/components/mpu6050/mpu6050.cpp b/esphome/components/mpu6050/mpu6050.cpp index ed29852b14c..19b3c2a5d17 100644 --- a/esphome/components/mpu6050/mpu6050.cpp +++ b/esphome/components/mpu6050/mpu6050.cpp @@ -20,11 +20,15 @@ const uint8_t MPU6050_BIT_TEMPERATURE_DISABLED = 3; const float GRAVITY_EARTH = 9.80665f; void MPU6050Component::setup() { + if (!this->setup_registers_()) + this->mark_failed(LOG_STR(ESP_LOG_MSG_COMM_FAIL)); +} + +bool MPU6050Component::setup_registers_() { uint8_t who_am_i; if (!this->read_byte(MPU6050_REGISTER_WHO_AM_I, &who_am_i) || (who_am_i != 0x68 && who_am_i != 0x70 && who_am_i != 0x98)) { - this->mark_failed(); - return; + return false; } ESP_LOGV(TAG, " Setting up Power Management"); @@ -32,30 +36,22 @@ void MPU6050Component::setup() { if (!this->update_register(MPU6050_REGISTER_POWER_MANAGEMENT_1, 0b111 | (1 << MPU6050_BIT_SLEEP_ENABLED) | (1 << MPU6050_BIT_TEMPERATURE_DISABLED), MPU6050_CLOCK_SOURCE_X_GYRO)) { - this->mark_failed(); - return; + return false; } ESP_LOGV(TAG, " Setting up Gyro Config"); // Set scale - 2000DPS if (!this->update_register(MPU6050_REGISTER_GYRO_CONFIG, 0b00011000, MPU6050_SCALE_2000_DPS << 3)) { - this->mark_failed(); - return; + return false; } ESP_LOGV(TAG, " Setting up Accel Config"); // Set range - 2G - if (!this->update_register(MPU6050_REGISTER_ACCEL_CONFIG, 0b00011000, MPU6050_RANGE_2G << 3)) { - this->mark_failed(); - return; - } + return this->update_register(MPU6050_REGISTER_ACCEL_CONFIG, 0b00011000, MPU6050_RANGE_2G << 3); } void MPU6050Component::dump_config() { ESP_LOGCONFIG(TAG, "MPU6050:"); LOG_I2C_DEVICE(this); - if (this->is_failed()) { - ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL); - } LOG_UPDATE_INTERVAL(this); LOG_SENSOR(" ", "Acceleration X", this->accel_x_sensor_); LOG_SENSOR(" ", "Acceleration Y", this->accel_y_sensor_); diff --git a/esphome/components/mpu6050/mpu6050.h b/esphome/components/mpu6050/mpu6050.h index 4410bf01645..2a346c1f4d9 100644 --- a/esphome/components/mpu6050/mpu6050.h +++ b/esphome/components/mpu6050/mpu6050.h @@ -22,6 +22,8 @@ class MPU6050Component final : public PollingComponent, public i2c::I2CDevice { void set_gyro_z_sensor(sensor::Sensor *gyro_z_sensor) { gyro_z_sensor_ = gyro_z_sensor; } protected: + /// Configure the sensor; false on an I2C failure. + bool setup_registers_(); sensor::Sensor *accel_x_sensor_{nullptr}; sensor::Sensor *accel_y_sensor_{nullptr}; sensor::Sensor *accel_z_sensor_{nullptr}; diff --git a/esphome/components/mpu6886/mpu6886.cpp b/esphome/components/mpu6886/mpu6886.cpp index 92542915d65..44d9f3a098b 100644 --- a/esphome/components/mpu6886/mpu6886.cpp +++ b/esphome/components/mpu6886/mpu6886.cpp @@ -25,10 +25,14 @@ const float TEMPERATURE_SENSITIVITY = 326.8; const float TEMPERATURE_OFFSET = 25.0; void MPU6886Component::setup() { + if (!this->setup_registers_()) + this->mark_failed(LOG_STR(ESP_LOG_MSG_COMM_FAIL)); +} + +bool MPU6886Component::setup_registers_() { uint8_t who_am_i; if (!this->read_byte(MPU6886_REGISTER_WHO_AM_I, &who_am_i) || who_am_i != MPU6886_WHO_AM_I_IDENTIFIER) { - this->mark_failed(); - return; + return false; } ESP_LOGV(TAG, " Setting up Power Management"); @@ -36,31 +40,23 @@ void MPU6886Component::setup() { if (!this->update_register(MPU6886_REGISTER_POWER_MANAGEMENT_1, 0b111 | (1 << MPU6886_BIT_SLEEP_ENABLED) | (1 << MPU6886_BIT_TEMPERATURE_DISABLED), MPU6886_CLOCK_SOURCE_X_GYRO)) { - this->mark_failed(); - return; + return false; } ESP_LOGV(TAG, " Setting up Gyroscope Config"); // Set scale - 2000DPS if (!this->update_register(MPU6886_REGISTER_GYRO_CONFIG, 0b00011000, MPU6886_SCALE_2000_DPS << 3)) { - this->mark_failed(); - return; + return false; } ESP_LOGV(TAG, " Setting up Accelerometer Config"); // Set range - 2G - if (!this->update_register(MPU6886_REGISTER_ACCEL_CONFIG, 0b00011000, MPU6886_RANGE_2G << 3)) { - this->mark_failed(); - return; - } + return this->update_register(MPU6886_REGISTER_ACCEL_CONFIG, 0b00011000, MPU6886_RANGE_2G << 3); } void MPU6886Component::dump_config() { ESP_LOGCONFIG(TAG, "MPU6886:"); LOG_I2C_DEVICE(this); - if (this->is_failed()) { - ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL); - } LOG_UPDATE_INTERVAL(this); LOG_SENSOR(" ", "Acceleration X", this->accel_x_sensor_); LOG_SENSOR(" ", "Acceleration Y", this->accel_y_sensor_); diff --git a/esphome/components/mpu6886/mpu6886.h b/esphome/components/mpu6886/mpu6886.h index b795d5f690f..a7c81e17593 100644 --- a/esphome/components/mpu6886/mpu6886.h +++ b/esphome/components/mpu6886/mpu6886.h @@ -22,6 +22,8 @@ class MPU6886Component final : public PollingComponent, public i2c::I2CDevice { void set_gyro_z_sensor(sensor::Sensor *gyro_z_sensor) { gyro_z_sensor_ = gyro_z_sensor; } protected: + /// Configure the sensor; false on an I2C failure. + bool setup_registers_(); sensor::Sensor *accel_x_sensor_{nullptr}; sensor::Sensor *accel_y_sensor_{nullptr}; sensor::Sensor *accel_z_sensor_{nullptr}; diff --git a/esphome/components/ms5611/ms5611.cpp b/esphome/components/ms5611/ms5611.cpp index 8f66d704fe6..e3e5194c415 100644 --- a/esphome/components/ms5611/ms5611.cpp +++ b/esphome/components/ms5611/ms5611.cpp @@ -18,13 +18,13 @@ static const uint8_t MS5611_CMD_READ_PROM = 0xA2; void MS5611Component::setup() { if (!this->write_bytes(MS5611_CMD_RESET, nullptr, 0)) { - this->mark_failed(); + this->mark_failed(LOG_STR(ESP_LOG_MSG_COMM_FAIL)); return; } this->set_timeout(100, [this]() { for (uint8_t offset = 0; offset < 6; offset++) { if (!this->read_byte_16(MS5611_CMD_READ_PROM + (offset * 2), &this->prom_[offset])) { - this->mark_failed(); + this->mark_failed(LOG_STR(ESP_LOG_MSG_COMM_FAIL)); return; } } @@ -33,9 +33,6 @@ void MS5611Component::setup() { void MS5611Component::dump_config() { ESP_LOGCONFIG(TAG, "MS5611:"); LOG_I2C_DEVICE(this); - if (this->is_failed()) { - ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL); - } LOG_UPDATE_INTERVAL(this); LOG_SENSOR(" ", "Temperature", this->temperature_sensor_); LOG_SENSOR(" ", "Pressure", this->pressure_sensor_); diff --git a/esphome/components/msa3xx/msa3xx.cpp b/esphome/components/msa3xx/msa3xx.cpp index 78f2b75c9ce..19a4b067bc7 100644 --- a/esphome/components/msa3xx/msa3xx.cpp +++ b/esphome/components/msa3xx/msa3xx.cpp @@ -118,9 +118,13 @@ const char *orientation_z_to_string(bool orientation) { return orientation ? "Do void MSA3xxComponent::setup() { uint8_t part_id{0xff}; - if (!this->read_byte(static_cast(RegisterMap::PART_ID), &part_id) || (part_id != MSA_3XX_PART_ID)) { - ESP_LOGE(TAG, "Part ID is wrong or missing. Got 0x%02X", part_id); - this->mark_failed(); + if (!this->read_byte(static_cast(RegisterMap::PART_ID), &part_id)) { + this->mark_failed(LOG_STR(ESP_LOG_MSG_COMM_FAIL)); + return; + } + if (part_id != MSA_3XX_PART_ID) { + ESP_LOGE(TAG, "Part ID is wrong. Got 0x%02X", part_id); + this->mark_failed(LOG_STR("Wrong chip ID")); return; } @@ -137,8 +141,7 @@ void MSA3xxComponent::setup() { this->device_params_.accel_data_width = 12; this->device_params_.scale_factor_exp = static_cast(this->range_) - 10; } else { - ESP_LOGE(TAG, "Unknown model"); - this->mark_failed(); + this->mark_failed(LOG_STR("Unknown model")); return; } @@ -155,9 +158,6 @@ void MSA3xxComponent::setup() { void MSA3xxComponent::dump_config() { ESP_LOGCONFIG(TAG, "MSA3xx:"); LOG_I2C_DEVICE(this); - if (this->is_failed()) { - ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL); - } ESP_LOGCONFIG(TAG, " Model: %s\n" " Power Mode: %s\n" diff --git a/esphome/components/nau7802/nau7802.cpp b/esphome/components/nau7802/nau7802.cpp index ad1a569e0fa..cc24353782e 100644 --- a/esphome/components/nau7802/nau7802.cpp +++ b/esphome/components/nau7802/nau7802.cpp @@ -56,8 +56,7 @@ void NAU7802Sensor::setup() { uint8_t rev; if (this->read_register(DEVICE_REV | READ_BIT, &rev, 1)) { - ESP_LOGE(TAG, "Failed I2C read during setup()"); - this->mark_failed(); + this->mark_failed(LOG_STR("Failed I2C read during setup()")); return; } ESP_LOGI(TAG, "Setting up NAU7802 Rev %d", rev); @@ -72,8 +71,7 @@ void NAU7802Sensor::setup() { delay(1); if (!(pu_ctrl.get() & PU_CTRL_POWERUP_READY)) { - ESP_LOGE(TAG, "Failed to reset sensor during setup()"); - this->mark_failed(); + this->mark_failed(LOG_STR("Failed to reset sensor during setup()")); return; } @@ -124,10 +122,8 @@ void NAU7802Sensor::dump_config() { LOG_SENSOR("", "NAU7802", this); LOG_I2C_DEVICE(this); - if (this->is_failed()) { - ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL_FOR, this->get_name().c_str()); + if (this->is_failed()) return; - } // Note these may differ from the values on the device if calbration has been run ESP_LOGCONFIG(TAG, " Offset Calibration: %" PRId32 "\n" diff --git a/esphome/components/opt3001/opt3001.cpp b/esphome/components/opt3001/opt3001.cpp index 0101368cebd..374fd707561 100644 --- a/esphome/components/opt3001/opt3001.cpp +++ b/esphome/components/opt3001/opt3001.cpp @@ -87,9 +87,6 @@ void OPT3001Sensor::read_lx_(const std::function &f) { void OPT3001Sensor::dump_config() { LOG_SENSOR("", "OPT3001", this); LOG_I2C_DEVICE(this); - if (this->is_failed()) { - ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL); - } LOG_UPDATE_INTERVAL(this); } diff --git a/esphome/components/pca6416a/pca6416a.cpp b/esphome/components/pca6416a/pca6416a.cpp index f805a8da545..0825d6eac98 100644 --- a/esphome/components/pca6416a/pca6416a.cpp +++ b/esphome/components/pca6416a/pca6416a.cpp @@ -27,7 +27,7 @@ void PCA6416AComponent::setup() { uint8_t value; if (!this->read_register_(PCA6416A_INPUT0, &value)) { ESP_LOGE(TAG, "PCA6416A not available under 0x%02X", this->address_); - this->mark_failed(); + this->mark_failed(LOG_STR(ESP_LOG_MSG_COMM_FAIL)); return; } @@ -77,9 +77,6 @@ void PCA6416AComponent::dump_config() { } LOG_PIN(" Interrupt Pin: ", this->interrupt_pin_); LOG_I2C_DEVICE(this) - if (this->is_failed()) { - ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL); - } } bool PCA6416AComponent::digital_read_hw(uint8_t pin) { diff --git a/esphome/components/pca9505/__init__.py b/esphome/components/pca9505/__init__.py index 7b0d5ac1732..d50e8205a17 100644 --- a/esphome/components/pca9505/__init__.py +++ b/esphome/components/pca9505/__init__.py @@ -1,6 +1,6 @@ from esphome import pins import esphome.codegen as cg -from esphome.components import i2c +from esphome.components import gpio_expander, i2c import esphome.config_validation as cv from esphome.const import ( CONF_ID, @@ -32,7 +32,7 @@ CONFIG_SCHEMA = ( cv.Schema( { cv.Required(CONF_ID): cv.declare_id(PCA9505Component), - cv.Optional(CONF_INTERRUPT_PIN): pins.internal_gpio_input_pin_schema, + cv.Optional(CONF_INTERRUPT_PIN): gpio_expander.validate_interrupt_pin, } ) .extend(cv.COMPONENT_SCHEMA) diff --git a/esphome/components/pca9505/pca9505.cpp b/esphome/components/pca9505/pca9505.cpp index 20f62c14721..6ac50d3b1bc 100644 --- a/esphome/components/pca9505/pca9505.cpp +++ b/esphome/components/pca9505/pca9505.cpp @@ -18,7 +18,7 @@ void PCA9505Component::setup() { // Test to see if device exists if (!this->read_bank_(0)) { ESP_LOGE(TAG, "PCA9505 not detected at 0x%02X", this->address_); - this->mark_failed(); + this->mark_failed(LOG_STR(ESP_LOG_MSG_COMM_FAIL)); return; } @@ -65,9 +65,6 @@ void PCA9505Component::dump_config() { ESP_LOGCONFIG(TAG, "PCA9505:"); LOG_PIN(" Interrupt Pin: ", this->interrupt_pin_); LOG_I2C_DEVICE(this) - if (this->is_failed()) { - ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL); - } } bool PCA9505Component::digital_read_hw(uint8_t pin) { return this->read_bank_(pin / 8); } diff --git a/esphome/components/pca9554/pca9554.cpp b/esphome/components/pca9554/pca9554.cpp index f367d140678..a12d291f6b2 100644 --- a/esphome/components/pca9554/pca9554.cpp +++ b/esphome/components/pca9554/pca9554.cpp @@ -16,7 +16,7 @@ void PCA9554Component::setup() { // Test to see if device exists if (!this->read_inputs_()) { ESP_LOGE(TAG, "PCA95xx not detected at 0x%02X", this->address_); - this->mark_failed(); + this->mark_failed(LOG_STR(ESP_LOG_MSG_COMM_FAIL)); return; } @@ -64,9 +64,6 @@ void PCA9554Component::dump_config() { this->pin_count_); LOG_PIN(" Interrupt Pin: ", this->interrupt_pin_); LOG_I2C_DEVICE(this) - if (this->is_failed()) { - ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL); - } } bool PCA9554Component::digital_read_hw(uint8_t pin) { diff --git a/esphome/components/pca9685/pca9685_output.cpp b/esphome/components/pca9685/pca9685_output.cpp index fe417ed65de..cd4688a02a4 100644 --- a/esphome/components/pca9685/pca9685_output.cpp +++ b/esphome/components/pca9685/pca9685_output.cpp @@ -21,38 +21,37 @@ static const uint8_t PCA9685_MODE1_AUTOINC = 0b00100000; static const uint8_t PCA9685_MODE1_SLEEP = 0b00010000; void PCA9685Output::setup() { + if (!this->setup_registers_()) + this->mark_failed(LOG_STR(ESP_LOG_MSG_COMM_FAIL)); +} + +bool PCA9685Output::setup_registers_() { ESP_LOGV(TAG, " Resetting devices"); if (!this->write_bytes(PCA9685_REGISTER_SOFTWARE_RESET, nullptr, 0)) { - this->mark_failed(); - return; + return false; } if (!this->write_byte(PCA9685_REGISTER_MODE1, PCA9685_MODE1_RESTART | PCA9685_MODE1_AUTOINC)) { - this->mark_failed(); - return; + return false; } if (!this->write_byte(PCA9685_REGISTER_MODE2, this->mode_)) { - this->mark_failed(); - return; + return false; } uint8_t mode1; if (!this->read_byte(PCA9685_REGISTER_MODE1, &mode1)) { - this->mark_failed(); - return; + return false; } mode1 = (mode1 & ~PCA9685_MODE1_RESTART) | PCA9685_MODE1_SLEEP; if (!this->write_byte(PCA9685_REGISTER_MODE1, mode1)) { - this->mark_failed(); - return; + return false; } int pre_scaler = 3; if (this->extclk_) { mode1 = mode1 | PCA9685_MODE1_EXTCLK; if (!this->write_byte(PCA9685_REGISTER_MODE1, mode1)) { - this->mark_failed(); - return; + return false; } } else { pre_scaler = static_cast((25000000 / (4096 * this->frequency_)) - 1); @@ -61,18 +60,17 @@ void PCA9685Output::setup() { ESP_LOGV(TAG, " -> Prescaler: %d", pre_scaler); } if (!this->write_byte(PCA9685_REGISTER_PRE_SCALE, pre_scaler)) { - this->mark_failed(); - return; + return false; } mode1 = (mode1 & ~PCA9685_MODE1_SLEEP) | PCA9685_MODE1_RESTART; if (!this->write_byte(PCA9685_REGISTER_MODE1, mode1)) { - this->mark_failed(); - return; + return false; } delayMicroseconds(500); this->loop(); + return true; } void PCA9685Output::dump_config() { @@ -88,9 +86,6 @@ void PCA9685Output::dump_config() { " Frequency: %.0f Hz", this->frequency_); } - if (this->is_failed()) { - ESP_LOGE(TAG, "Setting up PCA9685 failed!"); - } } void PCA9685Output::loop() { diff --git a/esphome/components/pca9685/pca9685_output.h b/esphome/components/pca9685/pca9685_output.h index dad722888f1..38a66309bb0 100644 --- a/esphome/components/pca9685/pca9685_output.h +++ b/esphome/components/pca9685/pca9685_output.h @@ -54,6 +54,8 @@ class PCA9685Output final : public Component, public i2c::I2CDevice { void set_phase_balancer(PhaseBalancer balancer) { this->balancer_ = balancer; } protected: + /// Configure the chip; false on an I2C failure. + bool setup_registers_(); friend PCA9685Channel; void set_channel_value_(uint8_t channel, uint16_t value) { diff --git a/esphome/components/pcf85063/pcf85063.cpp b/esphome/components/pcf85063/pcf85063.cpp index bafe19ed2a4..f4123eddffa 100644 --- a/esphome/components/pcf85063/pcf85063.cpp +++ b/esphome/components/pcf85063/pcf85063.cpp @@ -10,7 +10,7 @@ ESPHOME_LOG_TAG(TAG, "pcf85063"); void PCF85063Component::setup() { if (!this->read_rtc_()) { - this->mark_failed(); + this->mark_failed(LOG_STR(ESP_LOG_MSG_COMM_FAIL)); } } @@ -19,9 +19,6 @@ void PCF85063Component::update() { this->read_time(); } void PCF85063Component::dump_config() { ESP_LOGCONFIG(TAG, "PCF85063:"); LOG_I2C_DEVICE(this); - if (this->is_failed()) { - ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL); - } RealTimeClock::dump_config(); } diff --git a/esphome/components/pcf8563/pcf8563.cpp b/esphome/components/pcf8563/pcf8563.cpp index 90ea4b3fd10..f20437366d6 100644 --- a/esphome/components/pcf8563/pcf8563.cpp +++ b/esphome/components/pcf8563/pcf8563.cpp @@ -10,7 +10,7 @@ ESPHOME_LOG_TAG(TAG, "PCF8563"); void PCF8563Component::setup() { if (!this->read_rtc_()) { - this->mark_failed(); + this->mark_failed(LOG_STR(ESP_LOG_MSG_COMM_FAIL)); } } @@ -19,9 +19,6 @@ void PCF8563Component::update() { this->read_time(); } void PCF8563Component::dump_config() { ESP_LOGCONFIG(TAG, "PCF8563:"); LOG_I2C_DEVICE(this); - if (this->is_failed()) { - ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL); - } RealTimeClock::dump_config(); } diff --git a/esphome/components/pcf8574/pcf8574.cpp b/esphome/components/pcf8574/pcf8574.cpp index ea1e894a530..15ed2f8650e 100644 --- a/esphome/components/pcf8574/pcf8574.cpp +++ b/esphome/components/pcf8574/pcf8574.cpp @@ -8,7 +8,7 @@ ESPHOME_LOG_TAG(TAG, "pcf8574"); void PCF8574Component::setup() { if (!this->read_gpio_()) { ESP_LOGE(TAG, "PCF8574 not available under 0x%02X", this->address_); - this->mark_failed(); + this->mark_failed(LOG_STR(ESP_LOG_MSG_COMM_FAIL)); return; } @@ -50,9 +50,6 @@ void PCF8574Component::dump_config() { YESNO(this->pcf8575_), YESNO(this->reset_)); LOG_PIN(" Interrupt Pin: ", this->interrupt_pin_); LOG_I2C_DEVICE(this) - if (this->is_failed()) { - ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL); - } } bool PCF8574Component::digital_read_hw(uint8_t pin) { // Read all pins from hardware into input_mask_ diff --git a/esphome/components/pi4ioe5v6408/pi4ioe5v6408.cpp b/esphome/components/pi4ioe5v6408/pi4ioe5v6408.cpp index 9bc10872d28..0b5af94e696 100644 --- a/esphome/components/pi4ioe5v6408/pi4ioe5v6408.cpp +++ b/esphome/components/pi4ioe5v6408/pi4ioe5v6408.cpp @@ -22,12 +22,12 @@ void PI4IOE5V6408Component::setup() { this->reg(PI4IOE5V6408_REGISTER_OUT_HIGH_IMPEDENCE) = 0b00000000; } else { if (!this->read_gpio_modes_()) { - this->mark_failed(); + this->mark_failed(LOG_STR(ESP_LOG_MSG_COMM_FAIL)); ESP_LOGE(TAG, "Failed to read GPIO modes"); return; } if (!this->read_gpio_outputs_()) { - this->mark_failed(); + this->mark_failed(LOG_STR(ESP_LOG_MSG_COMM_FAIL)); ESP_LOGE(TAG, "Failed to read GPIO outputs"); return; } @@ -51,9 +51,6 @@ void PI4IOE5V6408Component::dump_config() { ESP_LOGCONFIG(TAG, "PI4IOE5V6408:"); LOG_PIN(" Interrupt Pin: ", this->interrupt_pin_); LOG_I2C_DEVICE(this) - if (this->is_failed()) { - ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL); - } } void PI4IOE5V6408Component::pin_mode(uint8_t pin, gpio::Flags flags) { if (flags & gpio::FLAG_OUTPUT) { diff --git a/esphome/components/pm2005/pm2005.cpp b/esphome/components/pm2005/pm2005.cpp index 784a555e024..52dfea5fd5f 100644 --- a/esphome/components/pm2005/pm2005.cpp +++ b/esphome/components/pm2005/pm2005.cpp @@ -53,8 +53,7 @@ void PM2005Component::setup() { } if (this->read(this->data_buffer_, 12) != i2c::ERROR_OK) { - ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL); - this->mark_failed(); + this->mark_failed(LOG_STR(ESP_LOG_MSG_COMM_FAIL)); return; } } @@ -107,9 +106,6 @@ void PM2005Component::dump_config() { this->sensor_type_ == PM2105); LOG_I2C_DEVICE(this); - if (this->is_failed()) { - ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL); - } LOG_SENSOR(" ", "PM1.0", this->pm_1_0_sensor_); LOG_SENSOR(" ", "PM2.5", this->pm_2_5_sensor_); diff --git a/esphome/components/pmwcs3/pmwcs3.cpp b/esphome/components/pmwcs3/pmwcs3.cpp index 7493f5d97fb..60cc4da0e4c 100644 --- a/esphome/components/pmwcs3/pmwcs3.cpp +++ b/esphome/components/pmwcs3/pmwcs3.cpp @@ -55,9 +55,6 @@ void PMWCS3Component::update() { this->read_data_(); } void PMWCS3Component::dump_config() { ESP_LOGCONFIG(TAG, "PMWCS3"); LOG_I2C_DEVICE(this); - if (this->is_failed()) { - ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL); - } LOG_UPDATE_INTERVAL(this); LOG_SENSOR(" ", "e25", this->e25_sensor_); LOG_SENSOR(" ", "ec", this->ec_sensor_); @@ -79,7 +76,7 @@ void PMWCS3Component::read_data_() { float e25, ec, temperature, vwc; if (!this->read_bytes(PMWCS3_REG_GET_DATA, (uint8_t *) &data, 8)) { ESP_LOGVV(TAG, "Reading PMWCS3_REG_GET_DATA failed"); - this->mark_failed(); + this->mark_failed(LOG_STR(ESP_LOG_MSG_COMM_FAIL)); return; } if (this->e25_sensor_ != nullptr) { diff --git a/esphome/components/pn532/pn532.cpp b/esphome/components/pn532/pn532.cpp index 8fd92c9031f..fd38edf136a 100644 --- a/esphome/components/pn532/pn532.cpp +++ b/esphome/components/pn532/pn532.cpp @@ -21,8 +21,7 @@ void PN532::setup() { if (!this->write_command_({PN532_COMMAND_VERSION_DATA})) { ESP_LOGW(TAG, "Error sending version command, trying again"); if (!this->write_command_({PN532_COMMAND_VERSION_DATA})) { - ESP_LOGE(TAG, "Error sending version command"); - this->mark_failed(); + this->mark_failed(LOG_STR("Error sending version command")); return; } } @@ -30,8 +29,7 @@ void PN532::setup() { PN532Frame version_data; // GetFirmwareVersion returns IC, Ver, Rev and Support if (!this->read_response(PN532_COMMAND_VERSION_DATA, version_data) || version_data.size() < 3) { - ESP_LOGE(TAG, "Error getting version"); - this->mark_failed(); + this->mark_failed(LOG_STR("Error getting version")); return; } ESP_LOGD(TAG, "Found chip PN5%02X, Firmware v%d.%d", version_data[0], version_data[1], version_data[2]); @@ -42,15 +40,13 @@ void PN532::setup() { 0x14, // timeout: 20 x 50 ms (only used in virtual card mode) 0x01, // use IRQ })) { - ESP_LOGE(TAG, "No wakeup ack"); - this->mark_failed(); + this->mark_failed(LOG_STR("No wakeup ack")); return; } PN532Frame wakeup_result; if (!this->read_response(PN532_COMMAND_SAMCONFIGURATION, wakeup_result)) { - this->error_code_ = WAKEUP_FAILED; - this->mark_failed(); + this->mark_failed(LOG_STR("Wake Up command failed")); return; } @@ -62,8 +58,7 @@ void PN532::setup() { sam_timeout, // timeout as multiple of 50ms (actually only for virtual card mode, but shouldn't matter) 0x01, // Enable IRQ })) { - this->error_code_ = SAM_COMMAND_FAILED; - this->mark_failed(); + this->mark_failed(LOG_STR("SAM command failed")); return; } @@ -73,8 +68,7 @@ void PN532::setup() { for (uint8_t dat : sam_result) { ESP_LOGV(TAG, " 0x%02X", dat); } - this->error_code_ = SAM_COMMAND_FAILED; - this->mark_failed(); + this->mark_failed(LOG_STR("SAM command failed")); return; } @@ -482,16 +476,6 @@ bool PN532::mifare_read_(uint8_t address, MifareReadData &data) { void PN532::dump_config() { ESP_LOGCONFIG(TAG, "PN532:"); - switch (this->error_code_) { - case NONE: - break; - case WAKEUP_FAILED: - ESP_LOGE(TAG, "Wake Up command failed!"); - break; - case SAM_COMMAND_FAILED: - ESP_LOGE(TAG, "SAM command failed!"); - break; - } LOG_UPDATE_INTERVAL(this); diff --git a/esphome/components/pn532/pn532.h b/esphome/components/pn532/pn532.h index 3a9099f3210..902da35c2fa 100644 --- a/esphome/components/pn532/pn532.h +++ b/esphome/components/pn532/pn532.h @@ -152,11 +152,6 @@ class PN532 : public PollingComponent { FORMAT, WRITE, }; - enum PN532Error : uint8_t { - NONE = 0, - WAKEUP_FAILED, - SAM_COMMAND_FAILED, - }; // members are ordered by alignment, widest first, to minimize padding LazyCallbackManager on_finished_write_callback_; @@ -174,7 +169,6 @@ class PN532 : public PollingComponent { uint32_t rd_start_time_{0}; // valid only while rd_started_ is set PN532ReadReady rd_ready_{WOULDBLOCK}; NfcTask next_task_{READ}; - PN532Error error_code_{NONE}; bool rd_started_{false}; bool updates_enabled_{true}; bool requested_read_{false}; diff --git a/esphome/components/pylontech/pylontech.cpp b/esphome/components/pylontech/pylontech.cpp index 0898b924869..50d8c814fa4 100644 --- a/esphome/components/pylontech/pylontech.cpp +++ b/esphome/components/pylontech/pylontech.cpp @@ -34,9 +34,6 @@ PylontechComponent::PylontechComponent() {} void PylontechComponent::dump_config() { ESP_LOGCONFIG(TAG, "pylontech:"); - if (this->is_failed()) { - ESP_LOGE(TAG, "Connection with pylontech failed!"); - } for (PylontechListener *listener : this->listeners_) { listener->dump_config(); diff --git a/esphome/components/qmc5883l/qmc5883l.cpp b/esphome/components/qmc5883l/qmc5883l.cpp index dcfa46abc5b..2c859822e93 100644 --- a/esphome/components/qmc5883l/qmc5883l.cpp +++ b/esphome/components/qmc5883l/qmc5883l.cpp @@ -29,8 +29,7 @@ void IRAM_ATTR QMC5883LComponent::gpio_intr(QMC5883LComponent *arg) { arg->enabl void QMC5883LComponent::setup() { // Soft Reset if (!this->write_byte(QMC5883L_REGISTER_CONTROL_2, 1 << 7)) { - this->error_code_ = COMMUNICATION_FAILED; - this->mark_failed(); + this->mark_failed(LOG_STR(ESP_LOG_MSG_COMM_FAIL)); return; } delay(10); @@ -51,8 +50,7 @@ void QMC5883LComponent::setup() { control_1 |= this->range_ << 4; control_1 |= this->oversampling_ << 6; if (!this->write_byte(QMC5883L_REGISTER_CONTROL_1, control_1)) { - this->error_code_ = COMMUNICATION_FAILED; - this->mark_failed(); + this->mark_failed(LOG_STR(ESP_LOG_MSG_COMM_FAIL)); return; } @@ -61,15 +59,13 @@ void QMC5883LComponent::setup() { control_2 |= 0b0 << 6; // ROL_PNT (Pointer Roll Over) -> 0b00=disabled, 0b01=enabled control_2 |= 0b0 << 0; // INT_ENB (Interrupt) -> 0b00=disabled, 0b01=enabled if (!this->write_byte(QMC5883L_REGISTER_CONTROL_2, control_2)) { - this->error_code_ = COMMUNICATION_FAILED; - this->mark_failed(); + this->mark_failed(LOG_STR(ESP_LOG_MSG_COMM_FAIL)); return; } uint8_t period = 0x01; // recommended value if (!this->write_byte(QMC5883L_REGISTER_PERIOD, period)) { - this->error_code_ = COMMUNICATION_FAILED; - this->mark_failed(); + this->mark_failed(LOG_STR(ESP_LOG_MSG_COMM_FAIL)); return; } @@ -81,9 +77,6 @@ void QMC5883LComponent::setup() { void QMC5883LComponent::dump_config() { ESP_LOGCONFIG(TAG, "QMC5883L:"); LOG_I2C_DEVICE(this); - if (this->error_code_ == COMMUNICATION_FAILED) { - ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL); - } LOG_UPDATE_INTERVAL(this); LOG_SENSOR(" ", "X Axis", this->x_sensor_); diff --git a/esphome/components/qmc5883l/qmc5883l.h b/esphome/components/qmc5883l/qmc5883l.h index faef423f8cc..d941c5d2e1e 100644 --- a/esphome/components/qmc5883l/qmc5883l.h +++ b/esphome/components/qmc5883l/qmc5883l.h @@ -57,10 +57,6 @@ class QMC5883LComponent final : public PollingComponent, public i2c::I2CDevice { sensor::Sensor *temperature_sensor_{nullptr}; GPIOPin *drdy_pin_{nullptr}; bool drdy_use_isr_{false}; - enum ErrorCode { - NONE = 0, - COMMUNICATION_FAILED, - } error_code_; i2c::ErrorCode read_bytes_16_le_(uint8_t a_register, uint16_t *data, uint8_t len = 1); HighFrequencyLoopRequester high_freq_; }; diff --git a/esphome/components/qmi8658/qmi8658.cpp b/esphome/components/qmi8658/qmi8658.cpp index 5c9402c2018..da1f735fb9e 100644 --- a/esphome/components/qmi8658/qmi8658.cpp +++ b/esphome/components/qmi8658/qmi8658.cpp @@ -32,19 +32,18 @@ void QMI8658Component::setup() { // 1. Verify chip ID uint8_t who_am_i = 0; if (!this->read_byte(QMI8658_REG_WHO_AM_I, &who_am_i)) { - ESP_LOGE(TAG, "Failed to read chip ID - check wiring / address"); - this->mark_failed(); + this->mark_failed(LOG_STR("Failed to read chip ID - check wiring / address")); return; } if (who_am_i != QMI8658_WHO_AM_I_VALUE) { ESP_LOGE(TAG, "Wrong chip ID: 0x%02X (expected 0x%02X)", who_am_i, QMI8658_WHO_AM_I_VALUE); - this->mark_failed(); + this->mark_failed(LOG_STR("Wrong chip ID")); return; } // 2. Soft reset if (!this->write_byte(QMI8658_REG_RESET, QMI8658_RESET_CMD)) { - this->mark_failed(); + this->mark_failed(LOG_STR(ESP_LOG_MSG_COMM_FAIL)); return; } delay(15); // spec: wait for reset to complete @@ -70,7 +69,6 @@ void QMI8658Component::setup() { // 6. Disable the built-in low-pass filters (leave raw data to the motion pipeline) if (!this->write_byte(QMI8658_REG_CTRL5, 0x00)) { this->mark_failed(LOG_STR("Failed to write REG_CTRL5")); - this->mark_failed(); return; } @@ -86,10 +84,8 @@ void QMI8658Component::setup() { void QMI8658Component::dump_config() { ESP_LOGCONFIG(TAG, "QMI8658 IMU:"); LOG_I2C_DEVICE(this); - if (this->is_failed()) { - ESP_LOGE(TAG, " Communication failed!"); + if (this->is_failed()) return; - } ESP_LOGCONFIG(TAG, " Accel range : %s", LOG_STR_ARG(AccelRangeStrings::get_log_str(this->accel_range_ >> 4, AccelRangeStrings::LAST_INDEX))); diff --git a/esphome/components/qspi_dbi/qspi_dbi.cpp b/esphome/components/qspi_dbi/qspi_dbi.cpp index 5b4974b6317..ce0df1beb50 100644 --- a/esphome/components/qspi_dbi/qspi_dbi.cpp +++ b/esphome/components/qspi_dbi/qspi_dbi.cpp @@ -3,6 +3,9 @@ #include "esphome/core/helpers.h" #include "esphome/core/log.h" +#include +#include + namespace esphome::qspi_dbi { // Maximum bytes to log in verbose hex output @@ -32,7 +35,9 @@ void QspiDbi::update() { if (!this->setup_complete_) { return; } + this->updating_ = true; this->do_update_(); + this->updating_ = false; if (this->buffer_ == nullptr || this->x_low_ > this->x_high_ || this->y_low_ > this->y_high_) return; // Some chips require that the drawing window be aligned on certain boundaries @@ -139,9 +144,14 @@ void QspiDbi::draw_pixels_at(int x_start, int y_start, int w, int h, const uint8 if (w <= 0 || h <= 0) return; if (bitness != display::COLOR_BITNESS_565 || order != this->color_mode_ || - big_endian != (this->bit_order_ == spi::BIT_ORDER_MSB_FIRST)) { + big_endian != (this->bit_order_ == spi::BIT_ORDER_MSB_FIRST) || + this->rotation_ != display::DISPLAY_ROTATION_0_DEGREES || this->is_clipping()) { Display::draw_pixels_at(x_start, y_start, w, h, ptr, order, bitness, big_endian, x_offset, y_offset, x_pad); return; + } else if (this->updating_) { + // Inside update() the block goes into the buffer, or the flush at its end would paint over it + this->copy_to_buffer_(x_start, y_start, w, h, ptr, order, bitness, big_endian, x_offset, y_offset, x_pad); + return; } else if (this->draw_from_origin_) { auto stride = x_offset + w + x_pad; for (int y = 0; y != h; y++) { @@ -159,6 +169,31 @@ void QspiDbi::draw_pixels_at(int x_start, int y_start, int w, int h, const uint8 this->write_to_display_(x_start, y_start, w, h, ptr, x_offset, y_offset, x_pad); } +void QspiDbi::copy_to_buffer_(int x_start, int y_start, int w, int h, const uint8_t *ptr, display::ColorOrder order, + display::ColorBitness bitness, bool big_endian, int x_offset, int y_offset, int x_pad) { + // The buffer holds big endian RGB565 + if (!big_endian || order != display::COLOR_ORDER_RGB) { + Display::draw_pixels_at(x_start, y_start, w, h, ptr, order, bitness, big_endian, x_offset, y_offset, x_pad); + return; + } + const int first_col = std::max(0, -x_start), end_col = std::min(w, static_cast(this->width_) - x_start); + const int first_row = std::max(0, -y_start), end_row = std::min(h, static_cast(this->height_) - y_start); + if (first_col >= end_col || first_row >= end_row) + return; + this->check_buffer_(); + if (this->buffer_ == nullptr) + return; + const size_t stride = x_offset + w + x_pad; + for (int y = first_row; y != end_row; y++) { + memcpy(this->buffer_ + ((y_start + y) * this->width_ + x_start + first_col) * 2, + ptr + ((y_offset + y) * stride + x_offset + first_col) * 2, (end_col - first_col) * 2); + } + this->x_low_ = std::min(this->x_low_, x_start + first_col); + this->y_low_ = std::min(this->y_low_, y_start + first_row); + this->x_high_ = std::max(this->x_high_, x_start + end_col - 1); + this->y_high_ = std::max(this->y_high_, y_start + end_row - 1); +} + void QspiDbi::write_to_display_(int x_start, int y_start, int w, int h, const uint8_t *ptr, int x_offset, int y_offset, int x_pad) { this->set_addr_window_(x_start, y_start, x_start + w - 1, y_start + h - 1); diff --git a/esphome/components/qspi_dbi/qspi_dbi.h b/esphome/components/qspi_dbi/qspi_dbi.h index eca062f4711..1c2565625b2 100644 --- a/esphome/components/qspi_dbi/qspi_dbi.h +++ b/esphome/components/qspi_dbi/qspi_dbi.h @@ -116,6 +116,8 @@ class QspiDbi final : public display::DisplayBuffer, display::ColorBitness bitness, bool big_endian, int x_offset, int y_offset, int x_pad) override; void write_to_display_(int x_start, int y_start, int w, int h, const uint8_t *ptr, int x_offset, int y_offset, int x_pad); + void copy_to_buffer_(int x_start, int y_start, int w, int h, const uint8_t *ptr, display::ColorOrder order, + display::ColorBitness bitness, bool big_endian, int x_offset, int y_offset, int x_pad); /** * the RM67162 in quad SPI mode seems to work like this (not in the datasheet, this is deduced from the * sample code.) @@ -160,6 +162,7 @@ class QspiDbi final : public display::DisplayBuffer, bool mirror_x_{}; bool mirror_y_{}; bool draw_from_origin_{false}; + bool updating_{false}; unsigned draw_rounding_{2}; uint8_t brightness_{0xD0}; const char *model_{"Unknown"}; diff --git a/esphome/components/qwiic_pir/qwiic_pir.cpp b/esphome/components/qwiic_pir/qwiic_pir.cpp index fffc3514d03..de9be3c8f0c 100644 --- a/esphome/components/qwiic_pir/qwiic_pir.cpp +++ b/esphome/components/qwiic_pir/qwiic_pir.cpp @@ -9,23 +9,17 @@ void QwiicPIRComponent::setup() { // Verify I2C communcation by reading and verifying the chip ID uint8_t chip_id; if (!this->read_byte(QWIIC_PIR_CHIP_ID, &chip_id)) { - ESP_LOGE(TAG, "Failed to read chip ID"); - this->error_code_ = ERROR_COMMUNICATION_FAILED; - this->mark_failed(); + this->mark_failed(LOG_STR("Failed to read chip ID")); return; } if (chip_id != QWIIC_PIR_DEVICE_ID) { - ESP_LOGE(TAG, "Unknown chip ID"); - this->error_code_ = ERROR_WRONG_CHIP_ID; - this->mark_failed(); + this->mark_failed(LOG_STR("Unknown chip ID")); return; } if (!this->write_byte_16(QWIIC_PIR_DEBOUNCE_TIME, this->debounce_time_)) { - ESP_LOGE(TAG, "Failed to configure debounce time"); - this->error_code_ = ERROR_COMMUNICATION_FAILED; - this->mark_failed(); + this->mark_failed(LOG_STR("Failed to configure debounce time")); return; } @@ -33,9 +27,7 @@ void QwiicPIRComponent::setup() { // Publish the starting raw state of the PIR sensor // If NATIVE mode, the binary_sensor state would be unknown until a motion event if (!this->read_byte(QWIIC_PIR_EVENT_STATUS, &this->event_register_.reg)) { - ESP_LOGE(TAG, "Failed to read initial state"); - this->error_code_ = ERROR_COMMUNICATION_FAILED; - this->mark_failed(); + this->mark_failed(LOG_STR("Failed to read initial state")); return; } @@ -104,20 +96,6 @@ void QwiicPIRComponent::dump_config() { ESP_LOGCONFIG(TAG, " Debounce Time: %ums", this->debounce_time_); } - switch (this->error_code_) { - case NONE: - break; - case ERROR_COMMUNICATION_FAILED: - ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL); - break; - case ERROR_WRONG_CHIP_ID: - ESP_LOGE(TAG, "Unknown chip ID"); - break; - default: - ESP_LOGE(TAG, "Error %d", (int) this->error_code_); - break; - } - LOG_I2C_DEVICE(this); LOG_BINARY_SENSOR(" ", "Binary Sensor", this); } diff --git a/esphome/components/qwiic_pir/qwiic_pir.h b/esphome/components/qwiic_pir/qwiic_pir.h index 8d3b8fb321b..39ed5b32d5a 100644 --- a/esphome/components/qwiic_pir/qwiic_pir.h +++ b/esphome/components/qwiic_pir/qwiic_pir.h @@ -44,12 +44,6 @@ class QwiicPIRComponent final : public Component, public i2c::I2CDevice, public DebounceMode debounce_mode_{}; - enum ErrorCode { - NONE = 0, - ERROR_COMMUNICATION_FAILED, - ERROR_WRONG_CHIP_ID, - } error_code_{NONE}; - union { struct { bool raw_reading : 1; // raw state of PIR sensor diff --git a/esphome/components/rc522/rc522.cpp b/esphome/components/rc522/rc522.cpp index 6549ba9846c..9796974a411 100644 --- a/esphome/components/rc522/rc522.cpp +++ b/esphome/components/rc522/rc522.cpp @@ -76,13 +76,6 @@ void RC522::initialize_() { void RC522::dump_config() { ESP_LOGCONFIG(TAG, "RC522:"); - switch (this->error_code_) { - case NONE: - break; - case RESET_FAILED: - ESP_LOGE(TAG, "Reset command failed"); - break; - } LOG_PIN(" RESET Pin: ", this->reset_pin_); @@ -293,9 +286,7 @@ void RC522::pcd_reset_() { } if (--reset_count_ == 0) { - ESP_LOGE(TAG, "Unable to reset"); - this->error_code_ = RESET_FAILED; - mark_failed(); + this->mark_failed(LOG_STR("Reset command failed")); } } diff --git a/esphome/components/rc522/rc522.h b/esphome/components/rc522/rc522.h index fd3c8196969..00d530c81db 100644 --- a/esphome/components/rc522/rc522.h +++ b/esphome/components/rc522/rc522.h @@ -244,11 +244,6 @@ class RC522 : public PollingComponent { std::vector triggers_ontag_; std::vector triggers_ontagremoved_; std::vector current_uid_; - - enum RC522Error { - NONE = 0, - RESET_FAILED, - } error_code_{NONE}; }; class RC522BinarySensor final : public binary_sensor::BinarySensor { diff --git a/esphome/components/rpi_dpi_rgb/rpi_dpi_rgb.cpp b/esphome/components/rpi_dpi_rgb/rpi_dpi_rgb.cpp index f2f25741f33..9f92a850f55 100644 --- a/esphome/components/rpi_dpi_rgb/rpi_dpi_rgb.cpp +++ b/esphome/components/rpi_dpi_rgb/rpi_dpi_rgb.cpp @@ -51,13 +51,17 @@ void RpiDpiRgb::draw_pixels_at(int x_start, int y_start, int w, int h, const uin display::ColorBitness bitness, bool big_endian, int x_offset, int y_offset, int x_pad) { if (w <= 0 || h <= 0) return; - // if color mapping is required, pass the buck. - // note that endianness is not considered here - it is assumed to match! - if (bitness != display::COLOR_BITNESS_565) { + if (bitness != display::COLOR_BITNESS_565 || order != display::COLOR_ORDER_RGB || !big_endian || + this->rotation_ != display::DISPLAY_ROTATION_0_DEGREES || this->is_clipping()) { display::Display::draw_pixels_at(x_start, y_start, w, h, ptr, order, bitness, big_endian, x_offset, y_offset, x_pad); return; } + this->write_to_display_(x_start, y_start, w, h, ptr, x_offset, y_offset, x_pad); +} + +void RpiDpiRgb::write_to_display_(int x_start, int y_start, int w, int h, const uint8_t *ptr, int x_offset, + int y_offset, int x_pad) { x_start += this->offset_x_; y_start += this->offset_y_; esp_err_t err = ESP_OK; @@ -122,8 +126,7 @@ void RpiDpiRgb::draw_pixel_at(int x, int y, Color color) { } auto pixel = convert_big_endian(display::ColorUtil::color_to_565(color)); - this->draw_pixels_at(x, y, 1, 1, (const uint8_t *) &pixel, display::COLOR_ORDER_RGB, display::COLOR_BITNESS_565, true, - 0, 0, 0); + this->write_to_display_(x, y, 1, 1, (const uint8_t *) &pixel, 0, 0, 0); this->feed_wdt_per_pixel_(); } diff --git a/esphome/components/rpi_dpi_rgb/rpi_dpi_rgb.h b/esphome/components/rpi_dpi_rgb/rpi_dpi_rgb.h index ad2f6e80c26..5f1f3be0191 100644 --- a/esphome/components/rpi_dpi_rgb/rpi_dpi_rgb.h +++ b/esphome/components/rpi_dpi_rgb/rpi_dpi_rgb.h @@ -62,6 +62,8 @@ class RpiDpiRgb final : public display::Display { void dump_config() override; protected: + void write_to_display_(int x_start, int y_start, int w, int h, const uint8_t *ptr, int x_offset, int y_offset, + int x_pad); int get_width_internal() override { return this->width_; } int get_height_internal() override { return this->height_; } void reset_display_() const; diff --git a/esphome/components/runtime_image/image_decoder.cpp b/esphome/components/runtime_image/image_decoder.cpp index 55e25d76b72..d57d5d4e73d 100644 --- a/esphome/components/runtime_image/image_decoder.cpp +++ b/esphome/components/runtime_image/image_decoder.cpp @@ -30,6 +30,6 @@ void ImageDecoder::draw(int x, int y, int w, int h, const Color &color) { } uint8_t *ImageDecoder::image_buffer_() const { return this->image_->buffer_; } -bool ImageDecoder::image_is_big_endian_() const { return this->image_->is_big_endian_; } +bool ImageDecoder::image_is_big_endian_() const { return this->image_->big_endian_; } } // namespace esphome::runtime_image diff --git a/esphome/components/runtime_image/runtime_image.cpp b/esphome/components/runtime_image/runtime_image.cpp index 79d2627a11c..14e0d5665a6 100644 --- a/esphome/components/runtime_image/runtime_image.cpp +++ b/esphome/components/runtime_image/runtime_image.cpp @@ -41,12 +41,11 @@ inline bool is_color_on(const Color &color) { RuntimeImage::RuntimeImage(ImageFormat format, image::ImageType type, image::Transparency transparency, image::Image *placeholder, bool is_big_endian, int fixed_width, int fixed_height) - : Image(nullptr, 0, 0, type, transparency), + : Image(nullptr, 0, 0, type, transparency, is_big_endian), format_(format), fixed_width_(fixed_width), fixed_height_(fixed_height), - placeholder_(placeholder), - is_big_endian_(is_big_endian) {} + placeholder_(placeholder) {} RuntimeImage::~RuntimeImage() { this->release(); } @@ -137,7 +136,7 @@ void RuntimeImage::draw_pixel(int x, int y, const Color &color) { Color mapped_color = color; this->map_chroma_key(mapped_color); uint16_t rgb565 = display::ColorUtil::color_to_565(mapped_color); - if (this->is_big_endian_) { + if (this->big_endian_) { this->buffer_[pos + 0] = static_cast((rgb565 >> 8) & 0xFF); this->buffer_[pos + 1] = static_cast(rgb565 & 0xFF); } else { diff --git a/esphome/components/runtime_image/runtime_image.h b/esphome/components/runtime_image/runtime_image.h index 33bad52acf7..2a3ab40a5c1 100644 --- a/esphome/components/runtime_image/runtime_image.h +++ b/esphome/components/runtime_image/runtime_image.h @@ -227,11 +227,6 @@ class RuntimeImage : public image::Image { // Configuration bool progressive_display_{false}; - /** - * Whether the image is stored in big-endian format. - * This is used to determine how to store 16 bit colors in the buffer. - */ - bool is_big_endian_{false}; /** Whether buffer_ belongs to the caller, so it must not be freed or resized here. */ bool external_buffer_{false}; diff --git a/esphome/components/rx8025t/rx8025t.cpp b/esphome/components/rx8025t/rx8025t.cpp index 502f6f265d6..c94c64f8611 100644 --- a/esphome/components/rx8025t/rx8025t.cpp +++ b/esphome/components/rx8025t/rx8025t.cpp @@ -19,7 +19,7 @@ constexpr uint8_t dec2bcd(uint8_t val) { return ((val / 10) << 4) + (val % 10); void RX8025TComponent::setup() { uint8_t flags; if (!this->read_flags_(&flags)) { - this->mark_failed(); + this->mark_failed(LOG_STR(ESP_LOG_MSG_COMM_FAIL)); return; } @@ -33,9 +33,6 @@ void RX8025TComponent::update() { this->read_time(); } void RX8025TComponent::dump_config() { ESP_LOGCONFIG(TAG, "RX8025T:"); LOG_I2C_DEVICE(this); - if (this->is_failed()) { - ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL); - } time::RealTimeClock::dump_config(); } diff --git a/esphome/components/scd30/scd30.cpp b/esphome/components/scd30/scd30.cpp index 5802503627a..07760ebd191 100644 --- a/esphome/components/scd30/scd30.cpp +++ b/esphome/components/scd30/scd30.cpp @@ -34,8 +34,7 @@ void SCD30Component::setup() { /// Firmware version identification uint16_t raw_firmware_version[3]; if (!this->get_register(SCD30_CMD_GET_FIRMWARE_VERSION, raw_firmware_version, 3)) { - this->error_code_ = FIRMWARE_IDENTIFICATION_FAILED; - this->mark_failed(); + this->mark_failed(LOG_STR("Unable to read sensor firmware version")); return; } ESP_LOGD(TAG, "SCD30 Firmware v%0d.%02d", (uint16_t(raw_firmware_version[0]) >> 8), @@ -49,9 +48,7 @@ void SCD30Component::setup() { } if (!this->write_command(SCD30_CMD_TEMPERATURE_OFFSET, temp_offset)) { - ESP_LOGE(TAG, "Sensor SCD30 error setting temperature offset."); - this->error_code_ = MEASUREMENT_INIT_FAILED; - this->mark_failed(); + this->mark_failed(LOG_STR("Error setting temperature offset")); return; } #ifdef USE_ESP32 @@ -68,9 +65,7 @@ void SCD30Component::setup() { #endif if (!this->write_command(SCD30_CMD_MEASUREMENT_INTERVAL, update_interval_)) { - ESP_LOGE(TAG, "Sensor SCD30 error setting update interval."); - this->error_code_ = MEASUREMENT_INIT_FAILED; - this->mark_failed(); + this->mark_failed(LOG_STR("Error setting update interval")); return; } #ifdef USE_ESP32 @@ -80,9 +75,7 @@ void SCD30Component::setup() { // The start measurement command disables the altitude compensation, if any, so we only set it if it's turned on if (this->altitude_compensation_ != 0xFFFF) { if (!this->write_command(SCD30_CMD_ALTITUDE_COMPENSATION, altitude_compensation_)) { - ESP_LOGE(TAG, "Sensor SCD30 error setting altitude compensation."); - this->error_code_ = MEASUREMENT_INIT_FAILED; - this->mark_failed(); + this->mark_failed(LOG_STR("Error setting altitude compensation")); return; } } @@ -91,9 +84,7 @@ void SCD30Component::setup() { #endif if (!this->write_command(SCD30_CMD_AUTOMATIC_SELF_CALIBRATION, enable_asc_ ? 1 : 0)) { - ESP_LOGE(TAG, "Sensor SCD30 error setting automatic self calibration."); - this->error_code_ = MEASUREMENT_INIT_FAILED; - this->mark_failed(); + this->mark_failed(LOG_STR("Error setting automatic self calibration")); return; } #ifdef USE_ESP32 @@ -102,9 +93,7 @@ void SCD30Component::setup() { /// Sensor initialization if (!this->write_command(SCD30_CMD_START_CONTINUOUS_MEASUREMENTS, this->ambient_pressure_compensation_)) { - ESP_LOGE(TAG, "Sensor SCD30 error starting continuous measurements."); - this->error_code_ = MEASUREMENT_INIT_FAILED; - this->mark_failed(); + this->mark_failed(LOG_STR("Error starting continuous measurements")); return; } @@ -118,22 +107,6 @@ void SCD30Component::setup() { void SCD30Component::dump_config() { ESP_LOGCONFIG(TAG, "scd30:"); LOG_I2C_DEVICE(this); - if (this->is_failed()) { - switch (this->error_code_) { - case COMMUNICATION_FAILED: - ESP_LOGW(TAG, ESP_LOG_MSG_COMM_FAIL); - break; - case MEASUREMENT_INIT_FAILED: - ESP_LOGW(TAG, "Measurement Initialization failed"); - break; - case FIRMWARE_IDENTIFICATION_FAILED: - ESP_LOGW(TAG, "Unable to read sensor firmware version"); - break; - default: - ESP_LOGW(TAG, "Unknown setup error"); - break; - } - } if (this->altitude_compensation_ == 0xFFFF) { ESP_LOGCONFIG(TAG, " Altitude compensation: OFF"); } else { @@ -216,7 +189,6 @@ bool SCD30Component::force_recalibration_with_reference(uint16_t co2_reference) return true; } else { ESP_LOGE(TAG, "Failed to force recalibration with reference."); - this->error_code_ = FORCE_RECALIBRATION_FAILED; this->status_set_warning(); return false; } diff --git a/esphome/components/scd30/scd30.h b/esphome/components/scd30/scd30.h index 0605ab4175a..d3bb980d80c 100644 --- a/esphome/components/scd30/scd30.h +++ b/esphome/components/scd30/scd30.h @@ -29,13 +29,6 @@ class SCD30Component final : public Component, public sensirion_common::Sensirio protected: bool is_data_ready_(); - enum ErrorCode { - COMMUNICATION_FAILED, - FIRMWARE_IDENTIFICATION_FAILED, - MEASUREMENT_INIT_FAILED, - FORCE_RECALIBRATION_FAILED, - UNKNOWN - } error_code_{UNKNOWN}; bool enable_asc_{true}; uint16_t altitude_compensation_{0xFFFF}; uint16_t ambient_pressure_compensation_{0x0000}; diff --git a/esphome/components/scd4x/scd4x.cpp b/esphome/components/scd4x/scd4x.cpp index 8249a6feb1e..bd30f3a42cb 100644 --- a/esphome/components/scd4x/scd4x.cpp +++ b/esphome/components/scd4x/scd4x.cpp @@ -30,8 +30,7 @@ void SCD4XComponent::setup() { this->set_timeout(1000, [this]() { this->status_clear_error(); if (!this->write_command(SCD4X_CMD_STOP_MEASUREMENTS)) { - ESP_LOGE(TAG, "Failed to stop measurements"); - this->mark_failed(); + this->mark_failed(LOG_STR("Failed to stop measurements")); return; } // According to the SCD4x datasheet the sensor will only respond to other commands after waiting 500 ms after @@ -39,9 +38,7 @@ void SCD4XComponent::setup() { this->set_timeout(500, [this]() { uint16_t raw_serial_number[3]; if (!this->get_register(SCD4X_CMD_GET_SERIAL_NUMBER, raw_serial_number, 3, 1)) { - ESP_LOGE(TAG, "Failed to read serial number"); - this->error_code_ = SERIAL_NUMBER_IDENTIFICATION_FAILED; - this->mark_failed(); + this->mark_failed(LOG_STR("Unable to read serial number")); return; } ESP_LOGD(TAG, "Serial number %02d.%02d.%02d", (uint16_t(raw_serial_number[0]) >> 8), @@ -49,33 +46,25 @@ void SCD4XComponent::setup() { if (!this->write_command(SCD4X_CMD_TEMPERATURE_OFFSET, (uint16_t) (temperature_offset_ * SCD4X_TEMPERATURE_OFFSET_MULTIPLIER))) { - ESP_LOGE(TAG, "Error setting temperature offset"); - this->error_code_ = MEASUREMENT_INIT_FAILED; - this->mark_failed(); + this->mark_failed(LOG_STR("Error setting temperature offset")); return; } // If pressure compensation available use it, else use altitude if (this->ambient_pressure_) { if (!this->update_ambient_pressure_compensation_(this->ambient_pressure_)) { - ESP_LOGE(TAG, "Error setting ambient pressure compensation"); - this->error_code_ = MEASUREMENT_INIT_FAILED; - this->mark_failed(); + this->mark_failed(LOG_STR("Error setting ambient pressure compensation")); return; } } else { if (!this->write_command(SCD4X_CMD_ALTITUDE_COMPENSATION, this->altitude_compensation_)) { - ESP_LOGE(TAG, "Error setting altitude compensation"); - this->error_code_ = MEASUREMENT_INIT_FAILED; - this->mark_failed(); + this->mark_failed(LOG_STR("Error setting altitude compensation")); return; } } if (!this->write_command(SCD4X_CMD_AUTOMATIC_SELF_CALIBRATION, this->enable_asc_ ? 1 : 0)) { - ESP_LOGE(TAG, "Error setting automatic self calibration"); - this->error_code_ = MEASUREMENT_INIT_FAILED; - this->mark_failed(); + this->mark_failed(LOG_STR("Error setting automatic self calibration")); return; } @@ -110,22 +99,6 @@ void SCD4XComponent::dump_config() { ESP_LOGCONFIG(TAG, "SCD4X:"); LOG_I2C_DEVICE(this); - if (this->is_failed()) { - switch (this->error_code_) { - case COMMUNICATION_FAILED: - ESP_LOGW(TAG, ESP_LOG_MSG_COMM_FAIL); - break; - case MEASUREMENT_INIT_FAILED: - ESP_LOGW(TAG, "Measurement Initialization failed"); - break; - case SERIAL_NUMBER_IDENTIFICATION_FAILED: - ESP_LOGW(TAG, "Unable to read firmware version"); - break; - default: - ESP_LOGW(TAG, "Unknown setup error"); - break; - } - } ESP_LOGCONFIG(TAG, " Automatic self calibration: %s\n" " Measurement mode: %s\n" @@ -237,7 +210,6 @@ bool SCD4XComponent::perform_forced_calibration(uint16_t current_co2_concentrati return true; } else { ESP_LOGE(TAG, "Force calibration failed"); - this->error_code_ = FRC_FAILED; this->status_set_warning(); return false; } @@ -310,7 +282,6 @@ bool SCD4XComponent::start_measurement_() { while (remaining_retries) { if (!this->write_command(measurement_command)) { ESP_LOGE(TAG, "Error starting measurements"); - this->error_code_ = MEASUREMENT_INIT_FAILED; this->status_set_warning(); if (--remaining_retries == 0) return false; diff --git a/esphome/components/scd4x/scd4x.h b/esphome/components/scd4x/scd4x.h index 4d5dedb5e94..0407af2b5ab 100644 --- a/esphome/components/scd4x/scd4x.h +++ b/esphome/components/scd4x/scd4x.h @@ -7,14 +7,6 @@ namespace esphome::scd4x { -enum ErrorCode : uint8_t { - COMMUNICATION_FAILED, - SERIAL_NUMBER_IDENTIFICATION_FAILED, - MEASUREMENT_INIT_FAILED, - FRC_FAILED, - UNKNOWN, -}; - enum MeasurementMode : uint8_t { PERIODIC, LOW_POWER_PERIODIC, @@ -54,7 +46,6 @@ class SCD4XComponent final : public PollingComponent, public sensirion_common::S uint16_t ambient_pressure_{0}; // Per datasheet, valid values are 700 to 1200 hPa; 0 is a valid sentinel value bool initialized_{false}; bool enable_asc_{false}; - ErrorCode error_code_; MeasurementMode measurement_mode_{PERIODIC}; }; diff --git a/esphome/components/sdp3x/sdp3x.cpp b/esphome/components/sdp3x/sdp3x.cpp index 98b5d9940a3..2f090108a69 100644 --- a/esphome/components/sdp3x/sdp3x.cpp +++ b/esphome/components/sdp3x/sdp3x.cpp @@ -26,20 +26,17 @@ void SDP3XComponent::setup() { this->set_timeout(20, [this] { if (!this->write_command(SDP3X_READ_ID1)) { - ESP_LOGE(TAG, "Read ID1 failed"); - this->mark_failed(); + this->mark_failed(LOG_STR("Read ID1 failed")); return; } if (!this->write_command(SDP3X_READ_ID2)) { - ESP_LOGE(TAG, "Read ID2 failed"); - this->mark_failed(); + this->mark_failed(LOG_STR("Read ID2 failed")); return; } uint16_t data[6]; if (!this->read_data(data, 6)) { - ESP_LOGE(TAG, "Read ID failed"); - this->mark_failed(); + this->mark_failed(LOG_STR("Read ID failed")); return; } @@ -76,8 +73,7 @@ void SDP3XComponent::setup() { } if (!this->write_command(measurement_mode_ == DP_AVG ? SDP3X_START_DP_AVG : SDP3X_START_MASS_FLOW_AVG)) { - ESP_LOGE(TAG, "Start Measurements failed"); - this->mark_failed(); + this->mark_failed(LOG_STR("Start Measurements failed")); return; } ESP_LOGCONFIG(TAG, "started"); @@ -86,9 +82,6 @@ void SDP3XComponent::setup() { void SDP3XComponent::dump_config() { LOG_SENSOR(" ", "SDP3X", this); LOG_I2C_DEVICE(this); - if (this->is_failed()) { - ESP_LOGE(TAG, " Connection with failed"); - } LOG_UPDATE_INTERVAL(this); } diff --git a/esphome/components/sen0321/sen0321.cpp b/esphome/components/sen0321/sen0321.cpp index 564e05e0394..420caa01b4f 100644 --- a/esphome/components/sen0321/sen0321.cpp +++ b/esphome/components/sen0321/sen0321.cpp @@ -9,7 +9,7 @@ ESPHOME_LOG_TAG(TAG, "sen0321_sensor.sensor"); void Sen0321Sensor::setup() { if (!this->write_byte(SENSOR_MODE_REGISTER, SENSOR_MODE_AUTO)) { ESP_LOGW(TAG, "Error setting measurement mode."); - this->mark_failed(); + this->mark_failed(LOG_STR(ESP_LOG_MSG_COMM_FAIL)); }; } @@ -18,9 +18,6 @@ void Sen0321Sensor::update() { this->read_data_(); } void Sen0321Sensor::dump_config() { ESP_LOGCONFIG(TAG, "DF Robot Ozone Sensor sen0321:"); LOG_I2C_DEVICE(this); - if (this->is_failed()) { - ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL); - } LOG_UPDATE_INTERVAL(this); } diff --git a/esphome/components/sen21231/sen21231.cpp b/esphome/components/sen21231/sen21231.cpp index 29175d845cc..d45b247a437 100644 --- a/esphome/components/sen21231/sen21231.cpp +++ b/esphome/components/sen21231/sen21231.cpp @@ -10,10 +10,6 @@ void Sen21231Sensor::update() { this->read_data_(); } void Sen21231Sensor::dump_config() { ESP_LOGCONFIG(TAG, "SEN21231:"); LOG_I2C_DEVICE(this); - if (this->is_failed()) { - ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL); - } - ESP_LOGI(TAG, "SEN21231: %s", this->is_failed() ? LOG_STR_LITERAL("FAILED") : LOG_STR_LITERAL("OK")); LOG_UPDATE_INTERVAL(this); } diff --git a/esphome/components/sen5x/sen5x.cpp b/esphome/components/sen5x/sen5x.cpp index f192a402887..2d7babddf81 100644 --- a/esphome/components/sen5x/sen5x.cpp +++ b/esphome/components/sen5x/sen5x.cpp @@ -60,16 +60,14 @@ void SEN5XComponent::setup() { this->set_timeout(1000, [this]() { // Check if measurement is ready before reading the value if (!this->write_command(SEN5X_CMD_GET_DATA_READY_STATUS)) { - ESP_LOGE(TAG, "Failed to write data ready status command"); - this->mark_failed(); + this->mark_failed(LOG_STR("Failed to write data ready status command")); return; } delay(20); // per datasheet uint16_t raw_read_status; if (!this->read_data(raw_read_status)) { - ESP_LOGE(TAG, "Failed to read data ready status"); - this->mark_failed(); + this->mark_failed(LOG_STR("Failed to read data ready status")); return; } @@ -78,8 +76,7 @@ void SEN5XComponent::setup() { if (raw_read_status) { ESP_LOGD(TAG, "Data is available; stopping periodic measurement"); if (!this->write_command(SEN5X_CMD_STOP_MEASUREMENTS)) { - ESP_LOGE(TAG, "Failed to stop measurements"); - this->mark_failed(); + this->mark_failed(LOG_STR("Failed to stop measurements")); return; } // According to the SEN5x datasheet the sensor will only respond to other commands after waiting 200 ms after @@ -91,9 +88,7 @@ void SEN5XComponent::setup() { // this appears to be all that Sensirion uses for serial numbers, this could change uint16_t raw_serial_number[8]; if (!this->get_register(SEN5X_CMD_GET_SERIAL_NUMBER, raw_serial_number, 8, 20)) { - ESP_LOGE(TAG, "Failed to read serial number"); - this->error_code_ = SERIAL_NUMBER_IDENTIFICATION_FAILED; - this->mark_failed(); + this->mark_failed(LOG_STR("Unable to read serial ID")); return; } const char *serial_number = sensirion_convert_to_string_in_place(raw_serial_number, 8); @@ -121,8 +116,7 @@ void SEN5XComponent::setup() { this->type_ = this->model_override_.value(); } else if (detected_type == Sen5xType::UNKNOWN) { this->type_ = Sen5xType::UNKNOWN; - this->error_code_ = PRODUCT_NAME_FAILED; - this->mark_failed(); + this->mark_failed(LOG_STR("Unable to read product name")); return; } else { this->type_ = detected_type; @@ -147,9 +141,7 @@ void SEN5XComponent::setup() { } if (!this->get_register(SEN5X_CMD_GET_FIRMWARE_VERSION, this->firmware_version_, 20)) { - ESP_LOGE(TAG, "Failed to read firmware version"); - this->error_code_ = FIRMWARE_FAILED; - this->mark_failed(); + this->mark_failed(LOG_STR("Unable to read firmware version")); return; } this->firmware_version_ >>= 8; @@ -189,9 +181,7 @@ void SEN5XComponent::setup() { result = this->write_command(SEN5X_CMD_RHT_ACCELERATION_MODE); } if (!result) { - ESP_LOGE(TAG, "Failed to set rh/t acceleration mode"); - this->error_code_ = COMMUNICATION_FAILED; - this->mark_failed(); + this->mark_failed(LOG_STR("Failed to set rh/t acceleration mode")); return; } delay(20); @@ -225,9 +215,7 @@ void SEN5XComponent::setup() { } if (!this->write_command(cmd)) { - ESP_LOGE(TAG, "Error starting continuous measurements"); - this->error_code_ = MEASUREMENT_INIT_FAILED; - this->mark_failed(); + this->mark_failed(LOG_STR("Error starting continuous measurements")); return; } this->initialized_ = true; @@ -238,28 +226,6 @@ void SEN5XComponent::setup() { void SEN5XComponent::dump_config() { ESP_LOGCONFIG(TAG, "SEN5X:"); LOG_I2C_DEVICE(this); - if (this->is_failed()) { - switch (this->error_code_) { - case COMMUNICATION_FAILED: - ESP_LOGW(TAG, ESP_LOG_MSG_COMM_FAIL); - break; - case MEASUREMENT_INIT_FAILED: - ESP_LOGW(TAG, "Measurement initialization failed"); - break; - case SERIAL_NUMBER_IDENTIFICATION_FAILED: - ESP_LOGW(TAG, "Unable to read serial ID"); - break; - case PRODUCT_NAME_FAILED: - ESP_LOGW(TAG, "Unable to read product name"); - break; - case FIRMWARE_FAILED: - ESP_LOGW(TAG, "Unable to read firmware version"); - break; - default: - ESP_LOGW(TAG, "Unknown setup error"); - break; - } - } ESP_LOGCONFIG(TAG, " Type: %s%s\n" " Firmware version: %d\n" diff --git a/esphome/components/sen5x/sen5x.h b/esphome/components/sen5x/sen5x.h index ed7689af523..07b72a1d69d 100644 --- a/esphome/components/sen5x/sen5x.h +++ b/esphome/components/sen5x/sen5x.h @@ -8,15 +8,6 @@ namespace esphome::sen5x { -enum ERRORCODE : uint8_t { - COMMUNICATION_FAILED, - SERIAL_NUMBER_IDENTIFICATION_FAILED, - MEASUREMENT_INIT_FAILED, - PRODUCT_NAME_FAILED, - FIRMWARE_FAILED, - UNKNOWN -}; - enum RhtAccelerationMode : uint16_t { LOW_ACCELERATION = 0, MEDIUM_ACCELERATION = 1, @@ -107,7 +98,6 @@ class SEN5XComponent final : public PollingComponent, public sensirion_common::S uint32_t voc_baseline_time_{0}; uint16_t firmware_version_{0}; Sen5xType type_{Sen5xType::UNKNOWN}; - ERRORCODE error_code_{ERRORCODE::UNKNOWN}; bool initialized_{false}; bool store_baseline_{false}; diff --git a/esphome/components/sendspin/image/__init__.py b/esphome/components/sendspin/image/__init__.py index a5336d5e052..571f8aa47d8 100644 --- a/esphome/components/sendspin/image/__init__.py +++ b/esphome/components/sendspin/image/__init__.py @@ -173,6 +173,7 @@ async def to_code(config: ConfigType) -> None: settings.height, settings.image_type_enum, settings.transparent, + settings.byte_order_big_endian, ) add_metadata( view_id, diff --git a/esphome/components/sfa30/sfa30.cpp b/esphome/components/sfa30/sfa30.cpp index efa3d8dc61f..b2db91bb587 100644 --- a/esphome/components/sfa30/sfa30.cpp +++ b/esphome/components/sfa30/sfa30.cpp @@ -13,9 +13,7 @@ void SFA30Component::setup() { // Serial Number identification uint16_t raw_device_marking[16]; if (!this->get_register(SFA30_CMD_GET_DEVICE_MARKING, raw_device_marking, 16, 5)) { - ESP_LOGE(TAG, "Failed to read device marking"); - this->error_code_ = DEVICE_MARKING_READ_FAILED; - this->mark_failed(); + this->mark_failed(LOG_STR("Unable to read device marking")); return; } @@ -26,9 +24,7 @@ void SFA30Component::setup() { ESP_LOGD(TAG, "Device Marking: '%s'", this->device_marking_); if (!this->write_command(SFA30_CMD_START_CONTINUOUS_MEASUREMENTS)) { - ESP_LOGE(TAG, "Error starting measurements."); - this->error_code_ = MEASUREMENT_INIT_FAILED; - this->mark_failed(); + this->mark_failed(LOG_STR("Error starting measurements")); return; } } @@ -36,19 +32,6 @@ void SFA30Component::setup() { void SFA30Component::dump_config() { ESP_LOGCONFIG(TAG, "sfa30:"); LOG_I2C_DEVICE(this); - if (this->is_failed()) { - switch (this->error_code_) { - case DEVICE_MARKING_READ_FAILED: - ESP_LOGW(TAG, "Unable to read device marking!"); - break; - case MEASUREMENT_INIT_FAILED: - ESP_LOGW(TAG, "Measurement initialization failed!"); - break; - default: - ESP_LOGW(TAG, "Unknown setup error!"); - break; - } - } LOG_UPDATE_INTERVAL(this); ESP_LOGCONFIG(TAG, " Device Marking: '%s'", this->device_marking_); LOG_SENSOR(" ", "Formaldehyde", this->formaldehyde_sensor_); diff --git a/esphome/components/sfa30/sfa30.h b/esphome/components/sfa30/sfa30.h index 13985b1a294..a9d85f60115 100644 --- a/esphome/components/sfa30/sfa30.h +++ b/esphome/components/sfa30/sfa30.h @@ -7,8 +7,6 @@ namespace esphome::sfa30 { class SFA30Component final : public PollingComponent, public sensirion_common::SensirionI2CDevice { - enum ErrorCode { DEVICE_MARKING_READ_FAILED, MEASUREMENT_INIT_FAILED, UNKNOWN }; - public: void setup() override; void dump_config() override; @@ -21,8 +19,6 @@ class SFA30Component final : public PollingComponent, public sensirion_common::S protected: char device_marking_[32] = {0}; - ErrorCode error_code_{UNKNOWN}; - sensor::Sensor *formaldehyde_sensor_{nullptr}; sensor::Sensor *humidity_sensor_{nullptr}; sensor::Sensor *temperature_sensor_{nullptr}; diff --git a/esphome/components/sfa40/sfa40.cpp b/esphome/components/sfa40/sfa40.cpp index 31a25a4f384..d0d3496afd0 100644 --- a/esphome/components/sfa40/sfa40.cpp +++ b/esphome/components/sfa40/sfa40.cpp @@ -41,15 +41,11 @@ void SFA40Component::setup() { this->write_command(SFA40_CMD_STOP_MEASUREMENT); this->set_timeout(25, [this]() { if (!this->detect_protocol_()) { - ESP_LOGE(TAG, "Failed to detect SFA40 protocol"); - this->error_code_ = PROTOCOL_DETECTION_FAILED; - this->mark_failed(); + this->mark_failed(LOG_STR("Protocol detection failed")); return; } if (!this->write_command(SFA40_CMD_START_MEASUREMENT)) { - ESP_LOGE(TAG, "Failed to start measurements"); - this->error_code_ = MEASUREMENT_INIT_FAILED; - this->mark_failed(); + this->mark_failed(LOG_STR("Failed to start measurements")); return; } this->initialized_ = true; @@ -77,19 +73,6 @@ bool SFA40Component::detect_protocol_() { void SFA40Component::dump_config() { ESP_LOGCONFIG(TAG, "sfa40:"); LOG_I2C_DEVICE(this); - if (this->is_failed()) { - switch (this->error_code_) { - case PROTOCOL_DETECTION_FAILED: - ESP_LOGW(TAG, "Protocol detection failed!"); - break; - case MEASUREMENT_INIT_FAILED: - ESP_LOGW(TAG, "Measurement initialization failed!"); - break; - default: - ESP_LOGW(TAG, "Unknown setup error!"); - break; - } - } LOG_UPDATE_INTERVAL(this); switch (this->protocol_version_) { case ProtocolVersion::PRODUCTION: diff --git a/esphome/components/sfa40/sfa40.h b/esphome/components/sfa40/sfa40.h index 6f67607753c..67995fae2ee 100644 --- a/esphome/components/sfa40/sfa40.h +++ b/esphome/components/sfa40/sfa40.h @@ -25,14 +25,8 @@ class SFA40Component final : public PollingComponent, public sensirion_common::S PRODUCTION = 1, PROTOTYPE = 2, }; - enum ErrorCode : uint8_t { - UNKNOWN_ERROR = 0, - PROTOCOL_DETECTION_FAILED, - MEASUREMENT_INIT_FAILED, - }; bool detect_protocol_(); ProtocolVersion protocol_version_{UNKNOWN}; - ErrorCode error_code_{UNKNOWN_ERROR}; char device_marking_[11]{}; bool initialized_{false}; bool wait_for_ready_{true}; diff --git a/esphome/components/sgp30/sgp30.cpp b/esphome/components/sgp30/sgp30.cpp index 72827ad51b5..387dbf3c485 100644 --- a/esphome/components/sgp30/sgp30.cpp +++ b/esphome/components/sgp30/sgp30.cpp @@ -37,7 +37,7 @@ void SGP30Component::setup() { // Serial Number identification uint16_t raw_serial_number[3]; if (!this->get_register(SGP30_CMD_GET_SERIAL_ID, raw_serial_number, 3)) { - this->mark_failed(); + this->mark_failed(LOG_STR(ESP_LOG_MSG_COMM_FAIL)); return; } this->serial_number_ = (static_cast(raw_serial_number[0]) << 32) | @@ -48,28 +48,24 @@ void SGP30Component::setup() { // Featureset identification for future use uint16_t raw_featureset; if (!this->get_register(SGP30_CMD_GET_FEATURESET, raw_featureset)) { - this->mark_failed(); + this->mark_failed(LOG_STR(ESP_LOG_MSG_COMM_FAIL)); return; } this->featureset_ = raw_featureset; if (uint16_t(this->featureset_ >> 12) != 0x0) { if (uint16_t(this->featureset_ >> 12) == 0x1) { // ID matching a different sensor: SGPC3 - this->error_code_ = UNSUPPORTED_ID; + this->mark_failed(LOG_STR("Unsupported ID")); } else { - // Unknown ID - this->error_code_ = INVALID_ID; + this->mark_failed(LOG_STR("Invalid ID")); } - this->mark_failed(); return; } ESP_LOGV(TAG, "Product version: 0x%0X", uint16_t(this->featureset_ & 0x1FF)); // Sensor initialization if (!this->write_command(SGP30_CMD_IAQ_INIT)) { - ESP_LOGE(TAG, "sgp30_iaq_init failed"); - this->error_code_ = MEASUREMENT_INIT_FAILED; - this->mark_failed(); + this->mark_failed(LOG_STR("Measurement initialization failed")); return; } @@ -227,25 +223,7 @@ void SGP30Component::write_iaq_baseline_(uint16_t eco2_baseline, uint16_t tvoc_b void SGP30Component::dump_config() { ESP_LOGCONFIG(TAG, "SGP30:"); LOG_I2C_DEVICE(this); - if (this->is_failed()) { - switch (this->error_code_) { - case COMMUNICATION_FAILED: - ESP_LOGW(TAG, ESP_LOG_MSG_COMM_FAIL); - break; - case MEASUREMENT_INIT_FAILED: - ESP_LOGW(TAG, "Measurement Initialization failed"); - break; - case INVALID_ID: - ESP_LOGW(TAG, "Invalid ID"); - break; - case UNSUPPORTED_ID: - ESP_LOGW(TAG, "Unsupported ID"); - break; - default: - ESP_LOGW(TAG, "Unknown setup error"); - break; - } - } else { + if (!this->is_failed()) { ESP_LOGCONFIG(TAG, " Serial number: %" PRIu64, this->serial_number_); if (this->eco2_baseline_ != 0x0000 && this->tvoc_baseline_ != 0x0000) { ESP_LOGCONFIG(TAG, diff --git a/esphome/components/sgp30/sgp30.h b/esphome/components/sgp30/sgp30.h index fac3c01b58f..3e95072dcfe 100644 --- a/esphome/components/sgp30/sgp30.h +++ b/esphome/components/sgp30/sgp30.h @@ -46,14 +46,6 @@ class SGP30Component final : public PollingComponent, public sensirion_common::S uint16_t tvoc_baseline_{0x0000}; bool store_baseline_; - enum ErrorCode : uint8_t { - COMMUNICATION_FAILED, - MEASUREMENT_INIT_FAILED, - INVALID_ID, - UNSUPPORTED_ID, - UNKNOWN - } error_code_{UNKNOWN}; - ESPPreferenceObject pref_; SGP30Baselines baselines_storage_; diff --git a/esphome/components/sgp4x/sgp4x.cpp b/esphome/components/sgp4x/sgp4x.cpp index 854dddba059..2772aa4be82 100644 --- a/esphome/components/sgp4x/sgp4x.cpp +++ b/esphome/components/sgp4x/sgp4x.cpp @@ -13,9 +13,7 @@ void SGP4xComponent::setup() { // Serial Number identification uint16_t raw_serial_number[3]; if (!this->get_register(SGP4X_CMD_GET_SERIAL_ID, raw_serial_number, 3, 1)) { - ESP_LOGE(TAG, "Get serial number failed"); - this->error_code_ = SERIAL_NUMBER_IDENTIFICATION_FAILED; - this->mark_failed(); + this->mark_failed(LOG_STR("Get serial number failed")); return; } this->serial_number_ = (uint64_t(raw_serial_number[0]) << 32) | (uint64_t(raw_serial_number[1]) << 16) | @@ -26,7 +24,7 @@ void SGP4xComponent::setup() { uint16_t featureset; if (!this->get_register(SGP4X_CMD_GET_FEATURESET, featureset, 1)) { ESP_LOGD(TAG, "Get feature set failed"); - this->mark_failed(); + this->mark_failed(LOG_STR(ESP_LOG_MSG_COMM_FAIL)); return; } featureset &= 0x1FF; @@ -44,7 +42,7 @@ void SGP4xComponent::setup() { this->measure_time_ = SGP41_MEASURE_TIME; } else { ESP_LOGD(TAG, "Unknown feature set 0x%0X", featureset); - this->mark_failed(); + this->mark_failed(LOG_STR("Unknown feature set")); return; } @@ -111,9 +109,7 @@ void SGP4xComponent::setup() { void SGP4xComponent::self_test_() { ESP_LOGD(TAG, "Starting self-test"); if (!this->write_command(SGP4X_CMD_SELF_TEST)) { - this->error_code_ = COMMUNICATION_FAILED; - ESP_LOGD(TAG, ESP_LOG_MSG_COMM_FAIL); - this->mark_failed(); + this->mark_failed(LOG_STR(ESP_LOG_MSG_COMM_FAIL)); return; } @@ -123,9 +119,8 @@ void SGP4xComponent::self_test_() { // failures bool passed = this->read_data(reply) && (this->sgp_type_ == SGP41 ? (reply & 0x0003) == 0 : (reply >> 8) == 0xD4); if (!passed) { - this->error_code_ = SELF_TEST_FAILED; ESP_LOGW(TAG, "Self-test failed (0x%X)", reply); - this->mark_failed(); + this->mark_failed(LOG_STR("Self-test failed")); return; } @@ -264,22 +259,7 @@ void SGP4xComponent::dump_config() { LOG_I2C_DEVICE(this); ESP_LOGCONFIG(TAG, " Store baseline: %s", YESNO(this->store_baseline_)); - if (this->is_failed()) { - switch (this->error_code_) { - case COMMUNICATION_FAILED: - ESP_LOGW(TAG, ESP_LOG_MSG_COMM_FAIL); - break; - case SERIAL_NUMBER_IDENTIFICATION_FAILED: - ESP_LOGW(TAG, "Get serial number failed"); - break; - case SELF_TEST_FAILED: - ESP_LOGW(TAG, "Self-test failed"); - break; - default: - ESP_LOGW(TAG, "Unknown error"); - break; - } - } else { + if (!this->is_failed()) { ESP_LOGCONFIG(TAG, " Type: %s\n" " Serial number: %" PRIu64 "\n" diff --git a/esphome/components/sgp4x/sgp4x.h b/esphome/components/sgp4x/sgp4x.h index 2aaf06601b1..804f0cdcef9 100644 --- a/esphome/components/sgp4x/sgp4x.h +++ b/esphome/components/sgp4x/sgp4x.h @@ -59,16 +59,6 @@ class SGP4xComponent; class SGP4xComponent final : public PollingComponent, public sensor::Sensor, public sensirion_common::SensirionI2CDevice { - enum ErrorCode { - COMMUNICATION_FAILED, - MEASUREMENT_INIT_FAILED, - INVALID_ID, - UNSUPPORTED_ID, - SERIAL_NUMBER_IDENTIFICATION_FAILED, - SELF_TEST_FAILED, - UNKNOWN - } error_code_{UNKNOWN}; - public: // SGP4xComponent() {}; void set_humidity_sensor(sensor::Sensor *humidity) { humidity_sensor_ = humidity; } diff --git a/esphome/components/sht4x/sht4x.cpp b/esphome/components/sht4x/sht4x.cpp index 45de59fe0dc..d46020b2d9a 100644 --- a/esphome/components/sht4x/sht4x.cpp +++ b/esphome/components/sht4x/sht4x.cpp @@ -30,7 +30,7 @@ void SHT4XComponent::read_serial_number_() { void SHT4XComponent::setup() { auto err = this->write(nullptr, 0); if (err != i2c::ERROR_OK) { - this->mark_failed(); + this->mark_failed(LOG_STR(ESP_LOG_MSG_COMM_FAIL)); return; } @@ -70,9 +70,6 @@ void SHT4XComponent::dump_config() { this->serial_number_); LOG_I2C_DEVICE(this); - if (this->is_failed()) { - ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL); - } if (this->serial_number_ == 0) { ESP_LOGW(TAG, "Get serial number failed"); } diff --git a/esphome/components/st7701s/st7701s.cpp b/esphome/components/st7701s/st7701s.cpp index 47b200c2de6..756473dae71 100644 --- a/esphome/components/st7701s/st7701s.cpp +++ b/esphome/components/st7701s/st7701s.cpp @@ -54,13 +54,17 @@ void ST7701S::draw_pixels_at(int x_start, int y_start, int w, int h, const uint8 display::ColorBitness bitness, bool big_endian, int x_offset, int y_offset, int x_pad) { if (w <= 0 || h <= 0) return; - // if color mapping is required, pass the buck. - // note that endianness is not considered here - it is assumed to match! - if (bitness != display::COLOR_BITNESS_565) { + if (bitness != display::COLOR_BITNESS_565 || order != display::COLOR_ORDER_RGB || !big_endian || + this->rotation_ != display::DISPLAY_ROTATION_0_DEGREES || this->is_clipping()) { display::Display::draw_pixels_at(x_start, y_start, w, h, ptr, order, bitness, big_endian, x_offset, y_offset, x_pad); return; } + this->write_to_display_(x_start, y_start, w, h, ptr, x_offset, y_offset, x_pad); +} + +void ST7701S::write_to_display_(int x_start, int y_start, int w, int h, const uint8_t *ptr, int x_offset, int y_offset, + int x_pad) { x_start += this->offset_x_; y_start += this->offset_y_; esp_err_t err; @@ -105,8 +109,7 @@ void ST7701S::draw_pixel_at(int x, int y, Color color) { } auto pixel = convert_big_endian(display::ColorUtil::color_to_565(color)); - this->draw_pixels_at(x, y, 1, 1, (const uint8_t *) &pixel, display::COLOR_ORDER_RGB, display::COLOR_BITNESS_565, true, - 0, 0, 0); + this->write_to_display_(x, y, 1, 1, (const uint8_t *) &pixel, 0, 0, 0); this->feed_wdt_per_pixel_(); } diff --git a/esphome/components/st7701s/st7701s.h b/esphome/components/st7701s/st7701s.h index fe29d794141..e34b980e73b 100644 --- a/esphome/components/st7701s/st7701s.h +++ b/esphome/components/st7701s/st7701s.h @@ -78,6 +78,8 @@ class ST7701S final : public display::Display, // this will be horribly slow. protected: + void write_to_display_(int x_start, int y_start, int w, int h, const uint8_t *ptr, int x_offset, int y_offset, + int x_pad); void write_command_(uint8_t value); void write_data_(uint8_t value); void write_sequence_(uint8_t cmd, size_t len, const uint8_t *bytes); diff --git a/esphome/core/defines.h b/esphome/core/defines.h index c901ff3e844..0655c4a399c 100644 --- a/esphome/core/defines.h +++ b/esphome/core/defines.h @@ -95,6 +95,7 @@ #define USE_HOMEASSISTANT_TIME #define USE_HOMEASSISTANT_TIMEZONE #define USE_HTTP_REQUEST_OTA_WATCHDOG_TIMEOUT 8000 // NOLINT +#define USE_I2S_AUDIO_FULL_DUPLEX #define USE_I2S_AUDIO_SPDIF_MODE #define USE_IMAGE #define USE_IMPROV_BLE_STATE_CALLBACK diff --git a/tests/component_tests/gpio_expander/test_init.py b/tests/component_tests/gpio_expander/test_init.py index 806b1775d29..34e77d84a2f 100644 --- a/tests/component_tests/gpio_expander/test_init.py +++ b/tests/component_tests/gpio_expander/test_init.py @@ -49,6 +49,7 @@ def test_allow_other_uses_rejected(stage_esp32: None) -> None: "pi4ioe5v6408", "mcp23016", "mcp23017", + "pca9505", ], ) def test_component_schemas_route_through_validator( diff --git a/tests/component_tests/i2s_audio/test_full_duplex_multiple_speakers.yaml b/tests/component_tests/i2s_audio/test_full_duplex_multiple_speakers.yaml new file mode 100644 index 00000000000..5cc4fb38125 --- /dev/null +++ b/tests/component_tests/i2s_audio/test_full_duplex_multiple_speakers.yaml @@ -0,0 +1,42 @@ +esphome: + name: test + +esp32: + board: esp32dev + framework: + type: esp-idf + +i2s_audio: + - id: duplex_bus + i2s_bclk_pin: GPIO5 + i2s_lrclk_pin: GPIO4 + full_duplex: true + +microphone: + - platform: i2s_audio + id: duplex_mic + i2s_audio_id: duplex_bus + i2s_din_pin: GPIO16 + adc_type: external + sample_rate: 48000 + bits_per_sample: 32bit + +speaker: + - platform: i2s_audio + id: duplex_speaker_a + i2s_audio_id: duplex_bus + i2s_dout_pin: + number: GPIO17 + allow_other_uses: true + dac_type: external + sample_rate: 48000 + bits_per_sample: 32bit + - platform: i2s_audio + id: duplex_speaker_b + i2s_audio_id: duplex_bus + i2s_dout_pin: + number: GPIO17 + allow_other_uses: true + dac_type: external + sample_rate: 48000 + bits_per_sample: 32bit diff --git a/tests/component_tests/i2s_audio/test_i2s_audio.py b/tests/component_tests/i2s_audio/test_i2s_audio.py new file mode 100644 index 00000000000..f92d0ca9414 --- /dev/null +++ b/tests/component_tests/i2s_audio/test_i2s_audio.py @@ -0,0 +1,141 @@ +"""Tests for the i2s_audio full duplex bus.""" + +from __future__ import annotations + +from collections.abc import Callable +from pathlib import Path +from typing import Any + +import pytest + +from esphome import config_validation as cv +from esphome.components.i2s_audio import _validate_full_duplex +from esphome.core import CORE, ID + +BUS_ID = "duplex_bus" + + +def _device(**overrides: Any) -> dict[str, Any]: + return { + "platform": "i2s_audio", + "id": ID("device", is_declaration=True), + "i2s_audio_id": ID(BUS_ID, is_declaration=False), + "i2s_dout_pin": 17, + "channel": "stereo", + "i2s_comm_fmt": "stand_i2s", + "sample_rate": 48000, + "bits_per_sample": 32, + "i2s_mode": "primary", + "use_apll": False, + "mclk_multiple": 256, + } | overrides + + +def _full_config( + microphones: list[dict[str, Any]], speakers: list[dict[str, Any]] +) -> dict[str, Any]: + return {"microphone": microphones, "speaker": speakers} + + +def test_full_duplex_generates_code( + generate_main: Callable[[str | Path], str], +) -> None: + """The microphone and every speaker are registered with the bus and the full duplex code is enabled.""" + main_cpp = generate_main( + "tests/component_tests/i2s_audio/test_full_duplex_multiple_speakers.yaml" + ) + + assert "duplex_bus->set_audio_in(duplex_mic);" in main_cpp + assert "duplex_bus->set_audio_out(duplex_speaker_a);" in main_cpp + assert "duplex_bus->set_audio_out(duplex_speaker_b);" in main_cpp + assert "USE_I2S_AUDIO_FULL_DUPLEX" in {define.name for define in CORE.defines} + + +_OTHER_BUS = _device(i2s_audio_id=ID("other_bus", is_declaration=False)) + + +@pytest.mark.parametrize( + ("microphones", "speakers"), + [ + pytest.param([_device(pdm=False)], [_device(spdif_mode=False)], id="matching"), + pytest.param( + [_device(), _OTHER_BUS], [_device(), _OTHER_BUS], id="other-bus-ignored" + ), + pytest.param( + [_device()], [_device(), _device(), _device()], id="several-speakers" + ), + ], +) +def test_full_duplex_accepts( + microphones: list[dict[str, Any]], speakers: list[dict[str, Any]] +) -> None: + """Devices with the same clock and TX settings share the bus; other buses do not count.""" + _validate_full_duplex(_full_config(microphones, speakers), BUS_ID) + + +@pytest.mark.parametrize( + ("microphones", "speakers"), + [ + ([], [_device()]), + ([_device()], []), + ([_device(), _device()], [_device()]), + ], +) +def test_full_duplex_requires_one_microphone_and_a_speaker( + microphones: list[dict[str, Any]], speakers: list[dict[str, Any]] +) -> None: + """Exactly one microphone and at least one speaker must use the bus.""" + with pytest.raises(cv.Invalid, match="exactly one i2s_audio microphone"): + _validate_full_duplex(_full_config(microphones, speakers), BUS_ID) + + +@pytest.mark.parametrize( + ("microphone", "speaker", "error_match"), + [ + pytest.param(_device(pdm=True), _device(), "PDM microphone", id="pdm"), + pytest.param(_device(), _device(spdif_mode=True), "SPDIF speaker", id="spdif"), + ], +) +def test_full_duplex_rejects_unsupported_devices( + microphone: dict[str, Any], speaker: dict[str, Any], error_match: str +) -> None: + """PDM receive and SPDIF output cannot share the standard mode clocks.""" + with pytest.raises(cv.Invalid, match=error_match): + _validate_full_duplex(_full_config([microphone], [speaker]), BUS_ID) + + +@pytest.mark.parametrize( + ("key", "value"), + [ + ("sample_rate", 16000), + ("bits_per_sample", 16), + ("i2s_mode", "secondary"), + ("use_apll", True), + ("mclk_multiple", 384), + ], +) +def test_full_duplex_rejects_mismatched_clock_settings(key: str, value: Any) -> None: + """Settings that shape the shared clocks must match.""" + with pytest.raises(cv.Invalid, match=f"must use the same '{key}'"): + _validate_full_duplex( + _full_config([_device()], [_device(**{key: value})]), BUS_ID + ) + + +@pytest.mark.parametrize( + ("key", "value"), + [ + ("i2s_dout_pin", 18), + ("channel", "mono"), + ("i2s_comm_fmt", "stand_msb"), + ], +) +def test_full_duplex_rejects_mismatched_speakers(key: str, value: Any) -> None: + """Settings that shape the TX channel must match across speakers.""" + with pytest.raises( + cv.Invalid, + match=f"speakers on 'full_duplex' bus '{BUS_ID}' must use the same '{key}'", + ): + _validate_full_duplex( + _full_config([_device()], [_device(), _device(**{key: value})]), BUS_ID + ) diff --git a/tests/component_tests/image/test_init.py b/tests/component_tests/image/test_init.py index 4d62bb52a19..602b923a1dd 100644 --- a/tests/component_tests/image/test_init.py +++ b/tests/component_tests/image/test_init.py @@ -745,7 +745,7 @@ def test_image_generation( in main_cpp ) assert ( - "new(cat_img) image::Image(uint8_t_id, 32, 24, image::IMAGE_TYPE_RGB565, image::TRANSPARENCY_OPAQUE);" + "new(cat_img) image::Image(uint8_t_id, 32, 24, image::IMAGE_TYPE_RGB565, image::TRANSPARENCY_OPAQUE, false);" in main_cpp ) diff --git a/tests/component_tests/sendspin/config/image_byte_order_big_endian.yaml b/tests/component_tests/sendspin/config/image_byte_order_big_endian.yaml new file mode 100644 index 00000000000..e58c546d2ee --- /dev/null +++ b/tests/component_tests/sendspin/config/image_byte_order_big_endian.yaml @@ -0,0 +1,35 @@ +esphome: + name: test + +esp32: + board: esp32dev + framework: + type: esp-idf + +wifi: + ap: + +spi: + clk_pin: 18 + mosi_pin: 23 + +display: + - platform: ili9xxx + model: ili9342 + cs_pin: 22 + dc_pin: 21 + invert_colors: false + +sendspin: + +image: + - platform: sendspin + id: album_slot + format: JPEG + type: RGB565 + byte_order: BIG_ENDIAN + resize: 240x240 + current_image: + id: album_art + transition_image: + id: album_art_transition diff --git a/tests/component_tests/sendspin/config/image_byte_order_little_endian.yaml b/tests/component_tests/sendspin/config/image_byte_order_little_endian.yaml new file mode 100644 index 00000000000..b9c4fd91d21 --- /dev/null +++ b/tests/component_tests/sendspin/config/image_byte_order_little_endian.yaml @@ -0,0 +1,35 @@ +esphome: + name: test + +esp32: + board: esp32dev + framework: + type: esp-idf + +wifi: + ap: + +spi: + clk_pin: 18 + mosi_pin: 23 + +display: + - platform: ili9xxx + model: ili9342 + cs_pin: 22 + dc_pin: 21 + invert_colors: false + +sendspin: + +image: + - platform: sendspin + id: album_slot + format: JPEG + type: RGB565 + byte_order: LITTLE_ENDIAN + resize: 240x240 + current_image: + id: album_art + transition_image: + id: album_art_transition diff --git a/tests/component_tests/sendspin/test_image.py b/tests/component_tests/sendspin/test_image.py index dc9e4978c6b..7de2b9b4dba 100644 --- a/tests/component_tests/sendspin/test_image.py +++ b/tests/component_tests/sendspin/test_image.py @@ -4,6 +4,8 @@ These cover the rejection branches, which a compile test cannot reach: a `test*.yaml` can only assert that a configuration is accepted. """ +from collections.abc import Callable +from pathlib import Path from typing import Any import pytest @@ -120,3 +122,24 @@ def test_display_offset_accepted( config = CONFIG_SCHEMA(_slot_config(display_offset=display_offset)) assert config["display_offset"].total_milliseconds == expected_ms + + +@pytest.mark.parametrize( + ("config_file", "expected"), + [ + ("image_byte_order_big_endian.yaml", "true"), + ("image_byte_order_little_endian.yaml", "false"), + ], +) +def test_views_get_the_byte_order( + generate_main: Callable[[str | Path], str], + component_config_path: Callable[[str], Path], + config_file: str, + expected: str, +) -> None: + """Both views draw the frames the slot decodes, so they must read them in its byte order.""" + main_cpp = generate_main(component_config_path(config_file)) + + views = [line for line in main_cpp.splitlines() if "ArtworkImageView(" in line] + assert len(views) == 2 + assert all(line.endswith(f", {expected});") for line in views), views diff --git a/tests/components/i2s_audio/common-full_duplex.yaml b/tests/components/i2s_audio/common-full_duplex.yaml new file mode 100644 index 00000000000..06ec6a87c96 --- /dev/null +++ b/tests/components/i2s_audio/common-full_duplex.yaml @@ -0,0 +1,26 @@ +i2s_audio: + - id: i2s_duplex_bus + i2s_bclk_pin: ${i2s_bclk_pin} + i2s_lrclk_pin: ${i2s_lrclk_pin} + i2s_mclk_pin: ${i2s_mclk_pin} + full_duplex: true + +microphone: + - platform: i2s_audio + id: duplex_mic + i2s_audio_id: i2s_duplex_bus + i2s_din_pin: ${i2s_din_pin} + adc_type: external + sample_rate: 48000 + bits_per_sample: 32bit + channel: stereo + +speaker: + - platform: i2s_audio + id: duplex_speaker + i2s_audio_id: i2s_duplex_bus + i2s_dout_pin: ${i2s_dout_pin} + dac_type: external + sample_rate: 48000 + bits_per_sample: 32bit + channel: stereo diff --git a/tests/components/i2s_audio/test-full_duplex.esp32-idf.yaml b/tests/components/i2s_audio/test-full_duplex.esp32-idf.yaml new file mode 100644 index 00000000000..4e8c9b4e4c8 --- /dev/null +++ b/tests/components/i2s_audio/test-full_duplex.esp32-idf.yaml @@ -0,0 +1,9 @@ +substitutions: + i2s_bclk_pin: GPIO5 + i2s_lrclk_pin: GPIO4 + i2s_mclk_pin: GPIO15 + i2s_din_pin: GPIO16 + i2s_dout_pin: GPIO17 + +packages: + i2s_audio: !include common-full_duplex.yaml diff --git a/tests/components/i2s_audio/test-full_duplex.esp32-s3-idf.yaml b/tests/components/i2s_audio/test-full_duplex.esp32-s3-idf.yaml new file mode 100644 index 00000000000..4e8c9b4e4c8 --- /dev/null +++ b/tests/components/i2s_audio/test-full_duplex.esp32-s3-idf.yaml @@ -0,0 +1,9 @@ +substitutions: + i2s_bclk_pin: GPIO5 + i2s_lrclk_pin: GPIO4 + i2s_mclk_pin: GPIO15 + i2s_din_pin: GPIO16 + i2s_dout_pin: GPIO17 + +packages: + i2s_audio: !include common-full_duplex.yaml diff --git a/tests/components/image/test_image_draw.cpp b/tests/components/image/test_image_draw.cpp new file mode 100644 index 00000000000..80db8a4ad2a --- /dev/null +++ b/tests/components/image/test_image_draw.cpp @@ -0,0 +1,213 @@ +#include + +#include + +#include "esphome/components/display/display.h" +#include "esphome/components/image/image.h" + +namespace esphome::image::testing { + +class TestDisplay : public display::Display { + public: + void update() override {} + display::DisplayType get_display_type() override { return display::DisplayType::DISPLAY_TYPE_COLOR; } + int get_width_internal() override { return 64; } + int get_height_internal() override { return 48; } +}; + +struct Blit { + int x, y, w, h; + const uint8_t *ptr; + display::ColorBitness bitness; + bool big_endian; + int x_offset, y_offset, x_pad; +}; + +// Records the bulk calls and counts the per pixel calls Image::draw() makes. +class RecordingDisplay : public TestDisplay { + public: + void draw_pixels_at(int x_start, int y_start, int w, int h, const uint8_t *ptr, display::ColorOrder order, + display::ColorBitness bitness, bool big_endian, int x_offset, int y_offset, int x_pad) override { + this->blits.push_back({x_start, y_start, w, h, ptr, bitness, big_endian, x_offset, y_offset, x_pad}); + } + void draw_pixel_at(int x, int y, Color color) override { this->pixels++; } + + std::vector blits; + int pixels{0}; +}; + +// Lets the bulk call fall through to the base class and keeps the pixels it produces. +class FallbackDisplay : public TestDisplay { + public: + void draw_pixel_at(int x, int y, Color color) override { this->pixels.push_back({x, y, color.raw_32}); } + + struct Pixel { + int x, y; + uint32_t raw; + }; + std::vector pixels; +}; + +static constexpr int W = 8, H = 6; + +static std::vector make_data(int bytes_per_pixel) { + std::vector data(W * H * bytes_per_pixel); + uint8_t value = 1; + for (auto &byte : data) + byte = value++; + return data; +} + +// Draws an opaque RGB565 image at (x, y) and returns the single bulk call it made. +static Blit draw_one(int x, int y, const std::vector &data, RecordingDisplay &display, + bool big_endian = false) { + Image image(data.data(), W, H, IMAGE_TYPE_RGB565, TRANSPARENCY_OPAQUE, big_endian); + image.draw(x, y, &display, Color::WHITE, Color::BLACK); + EXPECT_EQ(display.blits.size(), 1u); + EXPECT_EQ(display.pixels, 0); + return display.blits.at(0); +} + +static void expect_window(const Blit &blit, int x, int y, int w, int h, int x_offset, int y_offset, int x_pad) { + EXPECT_EQ(blit.x, x); + EXPECT_EQ(blit.y, y); + EXPECT_EQ(blit.w, w); + EXPECT_EQ(blit.h, h); + EXPECT_EQ(blit.x_offset, x_offset); + EXPECT_EQ(blit.y_offset, y_offset); + EXPECT_EQ(blit.x_pad, x_pad); +} + +TEST(ImageDraw, OpaqueRgb565IsOneBulkCall) { + const auto data = make_data(2); + RecordingDisplay display; + const Blit blit = draw_one(10, 20, data, display); + expect_window(blit, 10, 20, W, H, 0, 0, 0); + EXPECT_EQ(blit.ptr, data.data()); + EXPECT_EQ(blit.bitness, display::COLOR_BITNESS_565); + EXPECT_FALSE(blit.big_endian); +} + +TEST(ImageDraw, BigEndianRgb565IsPassedThrough) { + const auto data = make_data(2); + RecordingDisplay display; + EXPECT_TRUE(draw_one(0, 0, data, display, true).big_endian); +} + +TEST(ImageDraw, OpaqueRgbIsOneBulkCall) { + const auto data = make_data(3); + Image image(data.data(), W, H, IMAGE_TYPE_RGB, TRANSPARENCY_OPAQUE); + RecordingDisplay display; + image.draw(0, 0, &display, Color::WHITE, Color::BLACK); + ASSERT_EQ(display.blits.size(), 1u); + EXPECT_EQ(display.blits[0].bitness, display::COLOR_BITNESS_888); +} + +TEST(ImageDraw, ClippingShrinksTheWindow) { + const auto data = make_data(2); + RecordingDisplay display; + display.start_clipping(12, 22, 15, 24); // right and bottom are exclusive + expect_window(draw_one(10, 20, data, display), 12, 22, 3, 2, 2, 2, W - 5); +} + +TEST(ImageDraw, PartlyOffTheTopLeftIsClamped) { + const auto data = make_data(2); + RecordingDisplay display; + expect_window(draw_one(-3, -2, data, display), 0, 0, W - 3, H - 2, 3, 2, 0); +} + +TEST(ImageDraw, PartlyOffTheBottomRightIsClamped) { + const auto data = make_data(2); + RecordingDisplay display; + expect_window(draw_one(60, 44, data, display), 60, 44, 4, 4, 0, 0, W - 4); +} + +TEST(ImageDraw, FullyOffScreenDrawsNothing) { + const auto data = make_data(2); + Image image(data.data(), W, H, IMAGE_TYPE_RGB565, TRANSPARENCY_OPAQUE); + RecordingDisplay display; + image.draw(64, 0, &display, Color::WHITE, Color::BLACK); + image.draw(0, -H, &display, Color::WHITE, Color::BLACK); + EXPECT_TRUE(display.blits.empty()); + EXPECT_EQ(display.pixels, 0); +} + +TEST(ImageDraw, TransparentAndOtherFormatsStayPerPixel) { + const auto rgb565 = make_data(2); + const auto gray = make_data(1); + Image keyed(rgb565.data(), W, H, IMAGE_TYPE_RGB565, TRANSPARENCY_CHROMA_KEY); + Image grayscale(gray.data(), W, H, IMAGE_TYPE_GRAYSCALE, TRANSPARENCY_OPAQUE); + RecordingDisplay display; + keyed.draw(0, 0, &display, Color::WHITE, Color::BLACK); + grayscale.draw(0, 0, &display, Color::WHITE, Color::BLACK); + EXPECT_TRUE(display.blits.empty()); + EXPECT_GT(display.pixels, 0); +} + +// The base class fallback reads pixels in the image's byte order. +static void expect_fallback_decodes(bool big_endian) { + const auto data = make_data(2); + Image image(data.data(), W, H, IMAGE_TYPE_RGB565, TRANSPARENCY_OPAQUE, big_endian); + FallbackDisplay display; + image.draw(0, 0, &display, Color::WHITE, Color::BLACK); + ASSERT_EQ(display.pixels.size(), static_cast(W * H)); + for (const auto &pixel : display.pixels) { + const uint32_t rgb565 = + display::ColorUtil::read_packed(data.data(), pixel.x + pixel.y * W, big_endian); + const Color expected = display::ColorUtil::to_color(rgb565, display::COLOR_ORDER_RGB, display::COLOR_BITNESS_565); + EXPECT_EQ(pixel.raw, expected.raw_32) << "at " << pixel.x << "," << pixel.y; + } +} + +TEST(ImageDraw, FallbackDecodesLittleEndian) { expect_fallback_decodes(false); } +TEST(ImageDraw, FallbackDecodesBigEndian) { expect_fallback_decodes(true); } + +// The per pixel reader behind get_pixel() reads RGB565 in the image's byte order. +static void expect_reader_decodes(bool big_endian) { + const auto data = make_data(2); + Image image(data.data(), W, H, IMAGE_TYPE_RGB565, TRANSPARENCY_OPAQUE, big_endian); + for (int y = 0; y < H; y++) { + for (int x = 0; x < W; x++) { + const uint32_t rgb565 = + display::ColorUtil::read_packed(data.data(), x + y * W, big_endian); + const Color expected(((rgb565 >> 11) << 3) | ((rgb565 >> 11) >> 2), + (((rgb565 >> 5) & 0x3F) << 2) | (((rgb565 >> 5) & 0x3F) >> 4), + ((rgb565 & 0x1F) << 3) | ((rgb565 & 0x1F) >> 2), 0xFF); + EXPECT_EQ(image.get_pixel(x, y).raw_32, expected.raw_32) << "at " << x << "," << y; + } + } +} + +TEST(ImageDraw, ReaderDecodesLittleEndian) { expect_reader_decodes(false); } +TEST(ImageDraw, ReaderDecodesBigEndian) { expect_reader_decodes(true); } + +// A chroma keyed RGB565 draw stays per pixel and must match get_pixel(). +static void expect_keyed_draw_matches_reader(bool big_endian) { + const auto data = make_data(2); + Image image(data.data(), W, H, IMAGE_TYPE_RGB565, TRANSPARENCY_CHROMA_KEY, big_endian); + FallbackDisplay display; + image.draw(0, 0, &display, Color::WHITE, Color::BLACK); + ASSERT_GT(display.pixels.size(), 0u); + for (const auto &pixel : display.pixels) { + EXPECT_EQ(pixel.raw, image.get_pixel(pixel.x, pixel.y).raw_32) << "at " << pixel.x << "," << pixel.y; + } +} + +TEST(ImageDraw, KeyedDrawMatchesReaderLittleEndian) { expect_keyed_draw_matches_reader(false); } +TEST(ImageDraw, KeyedDrawMatchesReaderBigEndian) { expect_keyed_draw_matches_reader(true); } + +// The fallback keeps red and blue in the order the image stores them. +TEST(ImageDraw, FallbackKeepsRgbChannelOrder) { + const auto data = make_data(3); + Image image(data.data(), W, H, IMAGE_TYPE_RGB, TRANSPARENCY_OPAQUE); + FallbackDisplay display; + image.draw(0, 0, &display, Color::WHITE, Color::BLACK); + ASSERT_EQ(display.pixels.size(), static_cast(W * H)); + for (const auto &pixel : display.pixels) { + // get_pixel() reports an opaque pixel with alpha set; the fallback decodes colour only + const Color expected = image.get_pixel(pixel.x, pixel.y); + EXPECT_EQ(pixel.raw, Color(expected.r, expected.g, expected.b).raw_32) << "at " << pixel.x << "," << pixel.y; + } +} + +} // namespace esphome::image::testing diff --git a/tests/components/lp5562/common.yaml b/tests/components/lp5562/common.yaml index c1608f1a3cf..5bf3406938b 100644 --- a/tests/components/lp5562/common.yaml +++ b/tests/components/lp5562/common.yaml @@ -1,6 +1,7 @@ lp5562: - address: 0x30 id: lp5562_1 + i2c_id: i2c_bus output: - platform: lp5562