Merge remote-tracking branch 'origin/i2c-update-register' into i2c-update-register-migrate

# Conflicts:
#	esphome/components/bme680/bme680.cpp
#	esphome/components/mpu6050/mpu6050.cpp
#	esphome/components/mpu6886/mpu6886.cpp
This commit is contained in:
J. Nick Koston
2026-10-16 07:11:25 -10:00
207 changed files with 1907 additions and 1685 deletions
+9
View File
@@ -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
`<component> is marked FAILED: <reason>` 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
+1 -1
View File
@@ -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 \
+2 -5
View File
@@ -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) {
+4 -11
View File
@@ -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_);
}
+1 -6
View File
@@ -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() {
+6 -22
View File
@@ -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_);
}
+1 -4
View File
@@ -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_);
}
+2 -2
View File
@@ -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),
+1 -1
View File
@@ -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;
+2 -1
View File
@@ -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]
+4 -21
View File
@@ -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"
-6
View File
@@ -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_;
+3 -19
View File
@@ -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) \
-5
View File
@@ -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};
+3 -5
View File
@@ -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"
+2 -6
View File
@@ -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"
-3
View File
@@ -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) {
+3 -6
View File
@@ -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 * <nominal voltage, e.g. 230> * sqrt(2) * <fraction of nominal, e.g. 0.9> / (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_);
+1 -4
View File
@@ -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_);
+1 -4
View File
@@ -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);
}
@@ -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;
}
@@ -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;
}
{
+1 -4
View File
@@ -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) {
+1 -16
View File
@@ -7,8 +7,6 @@
#include <esphome/components/sensor/sensor.h>
#include <esphome/core/component.h>
#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);
@@ -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
+18 -27
View File
@@ -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);
+2
View File
@@ -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.
+13 -17
View File
@@ -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_);
+2
View File
@@ -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 */
+11 -15
View File
@@ -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)));
+1 -4
View File
@@ -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_);
+1 -17
View File
@@ -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);
@@ -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
+8 -38
View File
@@ -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;
}
@@ -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;
+10 -64
View File
@@ -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;
}
@@ -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 {
+1 -4
View File
@@ -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_);
+1 -11
View File
@@ -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() {
-5
View File
@@ -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
+7 -31
View File
@@ -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
-9
View File
@@ -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};
+1 -4
View File
@@ -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) {
+2 -8
View File
@@ -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) {
+1 -5
View File
@@ -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
+1 -4
View File
@@ -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);
@@ -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");
}
}
+1 -4
View File
@@ -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_);
}
+4
View File
@@ -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);
+15 -21
View File
@@ -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, &reg)) {
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_);
+2
View File
@@ -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_();
+1 -4
View File
@@ -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();
}
-4
View File
@@ -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) {
+1 -4
View File
@@ -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_);
+15 -49
View File
@@ -62,20 +62,17 @@ void ENS160Component::setup() {
// check part_id
uint16_t part_id;
if (!this->read_bytes(ENS160_REG_PART_ID, reinterpret_cast<uint8_t *>(&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<ValidityFlag>((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_);
@@ -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,
+7 -13
View File
@@ -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_);
+1 -2
View File
@@ -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);
+1 -6
View File
@@ -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() {
+2 -9
View File
@@ -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));
+2 -9
View File
@@ -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);
+1 -6
View File
@@ -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) {
+1 -6
View File
@@ -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) {
@@ -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; }
-3
View File
@@ -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);
}
-3
View File
@@ -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);
}
+7 -1
View File
@@ -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]
-3
View File
@@ -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_);
@@ -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
@@ -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);
};
-3
View File
@@ -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_);
-3
View File
@@ -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_);
-3
View File
@@ -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() {
+5 -15
View File
@@ -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_);
-5
View File
@@ -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_;
};
@@ -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);
+1 -4
View File
@@ -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_);
+2 -5
View File
@@ -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_);
-3
View File
@@ -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_);
@@ -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"));
}
});
}
+85 -3
View File
@@ -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"}
)
@@ -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
+62 -2
View File
@@ -23,6 +23,15 @@ class I2SAudioBase : public Parented<I2SAudioComponent> {
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<I2SAudioComponent> {
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_;
@@ -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 */
@@ -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
@@ -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
@@ -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_,
};
@@ -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
@@ -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<uint32_t> timeout_;
// A format start_i2s_driver() refused. Those checks compare against fixed settings, so it can never start.
optional<audio::AudioStreamInfo> rejected_stream_info_;
bool pause_state_{false};
bool keep_alive_{false};
@@ -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<uint8_t>(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
@@ -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};
};
+1 -5
View File
@@ -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_);
}
+3 -6
View File
@@ -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<uint8_t>(this->accel_range_));
ESP_LOGCONFIG(TAG, " Gyro range: ±%d°/s", 250 << static_cast<uint8_t>(this->gyro_range_));
+28 -7
View File
@@ -4,6 +4,9 @@
#include "esphome/core/helpers.h"
#include "esphome/core/log.h"
#include <algorithm>
#include <cstring>
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<int>(this->x_low_, x_start + first_col);
this->y_low_ = std::min<int>(this->y_low_, y_start + first_row);
this->x_high_ = std::max<int>(this->x_high_, x_start + end_col - 1);
this->y_high_ = std::max<int>(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
+133 -78
View File
@@ -1,5 +1,7 @@
#include "image.h"
#include <algorithm>
#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<bool ROWS_OUTER, typename F>
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_<false>(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_<true>(x, y, display, color_on, color_off, x_start, x_end, y_start, y_end);
}
template<bool ROWS_OUTER>
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<ROWS_OUTER>(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<ROWS_OUTER>(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<ROWS_OUTER>(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<ROWS_OUTER>(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<ROWS_OUTER>(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<size_t>(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<size_t>(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;
+9 -2
View File
@@ -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<bool ROWS_OUTER>
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
+4 -6
View File
@@ -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_);
+4 -6
View File
@@ -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,
+3 -21
View File
@@ -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() {
-7
View File
@@ -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

Some files were not shown because too many files have changed in this diff Show More