mirror of
				https://github.com/esphome/esphome.git
				synced 2025-10-30 14:43:51 +00:00 
			
		
		
		
	[i2c] Perform register reads as single transactions (#10389)
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
This commit is contained in:
		| @@ -41,7 +41,7 @@ void AXS15231Touchscreen::update_touches() { | ||||
|   i2c::ErrorCode err; | ||||
|   uint8_t data[8]{}; | ||||
|  | ||||
|   err = this->write(AXS_READ_TOUCHPAD, sizeof(AXS_READ_TOUCHPAD), false); | ||||
|   err = this->write(AXS_READ_TOUCHPAD, sizeof(AXS_READ_TOUCHPAD)); | ||||
|   ERROR_CHECK(err); | ||||
|   err = this->read(data, sizeof(data)); | ||||
|   ERROR_CHECK(err); | ||||
|   | ||||
| @@ -203,7 +203,7 @@ void BMI160Component::dump_config() { | ||||
| i2c::ErrorCode BMI160Component::read_le_int16_(uint8_t reg, int16_t *value, uint8_t len) { | ||||
|   uint8_t raw_data[len * 2]; | ||||
|   // read using read_register because we have little-endian data, and read_bytes_16 will swap it | ||||
|   i2c::ErrorCode err = this->read_register(reg, raw_data, len * 2, true); | ||||
|   i2c::ErrorCode err = this->read_register(reg, raw_data, len * 2); | ||||
|   if (err != i2c::ERROR_OK) { | ||||
|     return err; | ||||
|   } | ||||
|   | ||||
| @@ -63,12 +63,12 @@ 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->read_byte(0xD0, &chip_id)) { | ||||
|   if (!this->bmp_read_byte(0xD0, &chip_id)) { | ||||
|     this->error_code_ = COMMUNICATION_FAILED; | ||||
|     this->mark_failed(ESP_LOG_MSG_COMM_FAIL); | ||||
|     return; | ||||
|   } | ||||
|   if (!this->read_byte(0xD0, &chip_id)) { | ||||
|   if (!this->bmp_read_byte(0xD0, &chip_id)) { | ||||
|     this->error_code_ = COMMUNICATION_FAILED; | ||||
|     this->mark_failed(ESP_LOG_MSG_COMM_FAIL); | ||||
|     return; | ||||
| @@ -80,7 +80,7 @@ void BMP280Component::setup() { | ||||
|   } | ||||
|  | ||||
|   // Send a soft reset. | ||||
|   if (!this->write_byte(BMP280_REGISTER_RESET, BMP280_SOFT_RESET)) { | ||||
|   if (!this->bmp_write_byte(BMP280_REGISTER_RESET, BMP280_SOFT_RESET)) { | ||||
|     this->mark_failed("Reset failed"); | ||||
|     return; | ||||
|   } | ||||
| @@ -89,7 +89,7 @@ void BMP280Component::setup() { | ||||
|   uint8_t retry = 5; | ||||
|   do { | ||||
|     delay(2); | ||||
|     if (!this->read_byte(BMP280_REGISTER_STATUS, &status)) { | ||||
|     if (!this->bmp_read_byte(BMP280_REGISTER_STATUS, &status)) { | ||||
|       this->mark_failed("Error reading status register"); | ||||
|       return; | ||||
|     } | ||||
| @@ -115,14 +115,14 @@ void BMP280Component::setup() { | ||||
|   this->calibration_.p9 = this->read_s16_le_(0x9E); | ||||
|  | ||||
|   uint8_t config_register = 0; | ||||
|   if (!this->read_byte(BMP280_REGISTER_CONFIG, &config_register)) { | ||||
|   if (!this->bmp_read_byte(BMP280_REGISTER_CONFIG, &config_register)) { | ||||
|     this->mark_failed("Read config"); | ||||
|     return; | ||||
|   } | ||||
|   config_register &= ~0b11111100; | ||||
|   config_register |= 0b000 << 5;  // 0.5 ms standby time | ||||
|   config_register |= (this->iir_filter_ & 0b111) << 2; | ||||
|   if (!this->write_byte(BMP280_REGISTER_CONFIG, config_register)) { | ||||
|   if (!this->bmp_write_byte(BMP280_REGISTER_CONFIG, config_register)) { | ||||
|     this->mark_failed("Write config"); | ||||
|     return; | ||||
|   } | ||||
| @@ -159,7 +159,7 @@ void BMP280Component::update() { | ||||
|   meas_value |= (this->temperature_oversampling_ & 0b111) << 5; | ||||
|   meas_value |= (this->pressure_oversampling_ & 0b111) << 2; | ||||
|   meas_value |= 0b01;  // Forced mode | ||||
|   if (!this->write_byte(BMP280_REGISTER_CONTROL, meas_value)) { | ||||
|   if (!this->bmp_write_byte(BMP280_REGISTER_CONTROL, meas_value)) { | ||||
|     this->status_set_warning(); | ||||
|     return; | ||||
|   } | ||||
| @@ -188,9 +188,10 @@ void BMP280Component::update() { | ||||
| } | ||||
|  | ||||
| float BMP280Component::read_temperature_(int32_t *t_fine) { | ||||
|   uint8_t data[3]; | ||||
|   if (!this->read_bytes(BMP280_REGISTER_TEMPDATA, data, 3)) | ||||
|   uint8_t data[3]{}; | ||||
|   if (!this->bmp_read_bytes(BMP280_REGISTER_TEMPDATA, data, 3)) | ||||
|     return NAN; | ||||
|   ESP_LOGV(TAG, "Read temperature data, raw: %02X %02X %02X", data[0], data[1], data[2]); | ||||
|   int32_t adc = ((data[0] & 0xFF) << 16) | ((data[1] & 0xFF) << 8) | (data[2] & 0xFF); | ||||
|   adc >>= 4; | ||||
|   if (adc == 0x80000) { | ||||
| @@ -212,7 +213,7 @@ float BMP280Component::read_temperature_(int32_t *t_fine) { | ||||
|  | ||||
| float BMP280Component::read_pressure_(int32_t t_fine) { | ||||
|   uint8_t data[3]; | ||||
|   if (!this->read_bytes(BMP280_REGISTER_PRESSUREDATA, data, 3)) | ||||
|   if (!this->bmp_read_bytes(BMP280_REGISTER_PRESSUREDATA, data, 3)) | ||||
|     return NAN; | ||||
|   int32_t adc = ((data[0] & 0xFF) << 16) | ((data[1] & 0xFF) << 8) | (data[2] & 0xFF); | ||||
|   adc >>= 4; | ||||
| @@ -258,12 +259,12 @@ void BMP280Component::set_pressure_oversampling(BMP280Oversampling pressure_over | ||||
| void BMP280Component::set_iir_filter(BMP280IIRFilter iir_filter) { this->iir_filter_ = iir_filter; } | ||||
| uint8_t BMP280Component::read_u8_(uint8_t a_register) { | ||||
|   uint8_t data = 0; | ||||
|   this->read_byte(a_register, &data); | ||||
|   this->bmp_read_byte(a_register, &data); | ||||
|   return data; | ||||
| } | ||||
| uint16_t BMP280Component::read_u16_le_(uint8_t a_register) { | ||||
|   uint16_t data = 0; | ||||
|   this->read_byte_16(a_register, &data); | ||||
|   this->bmp_read_byte_16(a_register, &data); | ||||
|   return (data >> 8) | (data << 8); | ||||
| } | ||||
| int16_t BMP280Component::read_s16_le_(uint8_t a_register) { return this->read_u16_le_(a_register); } | ||||
|   | ||||
| @@ -67,12 +67,12 @@ class BMP280Component : public PollingComponent { | ||||
|   float get_setup_priority() const override; | ||||
|   void update() override; | ||||
|  | ||||
|   virtual bool read_byte(uint8_t a_register, uint8_t *data) = 0; | ||||
|   virtual bool write_byte(uint8_t a_register, uint8_t data) = 0; | ||||
|   virtual bool read_bytes(uint8_t a_register, uint8_t *data, size_t len) = 0; | ||||
|   virtual bool read_byte_16(uint8_t a_register, uint16_t *data) = 0; | ||||
|  | ||||
|  protected: | ||||
|   virtual bool bmp_read_byte(uint8_t a_register, uint8_t *data) = 0; | ||||
|   virtual bool bmp_write_byte(uint8_t a_register, uint8_t data) = 0; | ||||
|   virtual bool bmp_read_bytes(uint8_t a_register, uint8_t *data, size_t len) = 0; | ||||
|   virtual bool bmp_read_byte_16(uint8_t a_register, uint16_t *data) = 0; | ||||
|  | ||||
|   /// Read the temperature value and store the calculated ambient temperature in t_fine. | ||||
|   float read_temperature_(int32_t *t_fine); | ||||
|   /// Read the pressure value in hPa using the provided t_fine value. | ||||
|   | ||||
| @@ -5,19 +5,6 @@ | ||||
| namespace esphome { | ||||
| namespace bmp280_i2c { | ||||
|  | ||||
| bool BMP280I2CComponent::read_byte(uint8_t a_register, uint8_t *data) { | ||||
|   return I2CDevice::read_byte(a_register, data); | ||||
| }; | ||||
| bool BMP280I2CComponent::write_byte(uint8_t a_register, uint8_t data) { | ||||
|   return I2CDevice::write_byte(a_register, data); | ||||
| }; | ||||
| bool BMP280I2CComponent::read_bytes(uint8_t a_register, uint8_t *data, size_t len) { | ||||
|   return I2CDevice::read_bytes(a_register, data, len); | ||||
| }; | ||||
| bool BMP280I2CComponent::read_byte_16(uint8_t a_register, uint16_t *data) { | ||||
|   return I2CDevice::read_byte_16(a_register, data); | ||||
| }; | ||||
|  | ||||
| void BMP280I2CComponent::dump_config() { | ||||
|   LOG_I2C_DEVICE(this); | ||||
|   BMP280Component::dump_config(); | ||||
|   | ||||
| @@ -11,10 +11,12 @@ static const char *const TAG = "bmp280_i2c.sensor"; | ||||
| /// This class implements support for the BMP280 Temperature+Pressure i2c sensor. | ||||
| class BMP280I2CComponent : public esphome::bmp280_base::BMP280Component, public i2c::I2CDevice { | ||||
|  public: | ||||
|   bool read_byte(uint8_t a_register, uint8_t *data) override; | ||||
|   bool write_byte(uint8_t a_register, uint8_t data) override; | ||||
|   bool read_bytes(uint8_t a_register, uint8_t *data, size_t len) override; | ||||
|   bool read_byte_16(uint8_t a_register, uint16_t *data) override; | ||||
|   bool bmp_read_byte(uint8_t a_register, uint8_t *data) override { return read_byte(a_register, data); } | ||||
|   bool bmp_write_byte(uint8_t a_register, uint8_t data) override { return write_byte(a_register, data); } | ||||
|   bool bmp_read_bytes(uint8_t a_register, uint8_t *data, size_t len) override { | ||||
|     return read_bytes(a_register, data, len); | ||||
|   } | ||||
|   bool bmp_read_byte_16(uint8_t a_register, uint16_t *data) override { return read_byte_16(a_register, data); } | ||||
|   void dump_config() override; | ||||
| }; | ||||
|  | ||||
|   | ||||
| @@ -28,7 +28,7 @@ void BMP280SPIComponent::setup() { | ||||
| // 0x77 is transferred, for read access, the byte 0xF7 is transferred. | ||||
| // https://www.bosch-sensortec.com/media/boschsensortec/downloads/datasheets/bst-bmp280-ds001.pdf | ||||
|  | ||||
| bool BMP280SPIComponent::read_byte(uint8_t a_register, uint8_t *data) { | ||||
| bool BMP280SPIComponent::bmp_read_byte(uint8_t a_register, uint8_t *data) { | ||||
|   this->enable(); | ||||
|   this->transfer_byte(set_bit(a_register, 7)); | ||||
|   *data = this->transfer_byte(0); | ||||
| @@ -36,7 +36,7 @@ bool BMP280SPIComponent::read_byte(uint8_t a_register, uint8_t *data) { | ||||
|   return true; | ||||
| } | ||||
|  | ||||
| bool BMP280SPIComponent::write_byte(uint8_t a_register, uint8_t data) { | ||||
| bool BMP280SPIComponent::bmp_write_byte(uint8_t a_register, uint8_t data) { | ||||
|   this->enable(); | ||||
|   this->transfer_byte(clear_bit(a_register, 7)); | ||||
|   this->transfer_byte(data); | ||||
| @@ -44,7 +44,7 @@ bool BMP280SPIComponent::write_byte(uint8_t a_register, uint8_t data) { | ||||
|   return true; | ||||
| } | ||||
|  | ||||
| bool BMP280SPIComponent::read_bytes(uint8_t a_register, uint8_t *data, size_t len) { | ||||
| bool BMP280SPIComponent::bmp_read_bytes(uint8_t a_register, uint8_t *data, size_t len) { | ||||
|   this->enable(); | ||||
|   this->transfer_byte(set_bit(a_register, 7)); | ||||
|   this->read_array(data, len); | ||||
| @@ -52,7 +52,7 @@ bool BMP280SPIComponent::read_bytes(uint8_t a_register, uint8_t *data, size_t le | ||||
|   return true; | ||||
| } | ||||
|  | ||||
| bool BMP280SPIComponent::read_byte_16(uint8_t a_register, uint16_t *data) { | ||||
| bool BMP280SPIComponent::bmp_read_byte_16(uint8_t a_register, uint16_t *data) { | ||||
|   this->enable(); | ||||
|   this->transfer_byte(set_bit(a_register, 7)); | ||||
|   ((uint8_t *) data)[1] = this->transfer_byte(0); | ||||
|   | ||||
| @@ -10,10 +10,10 @@ class BMP280SPIComponent : public esphome::bmp280_base::BMP280Component, | ||||
|                            public spi::SPIDevice<spi::BIT_ORDER_MSB_FIRST, spi::CLOCK_POLARITY_LOW, | ||||
|                                                  spi::CLOCK_PHASE_LEADING, spi::DATA_RATE_200KHZ> { | ||||
|   void setup() override; | ||||
|   bool read_byte(uint8_t a_register, uint8_t *data) override; | ||||
|   bool write_byte(uint8_t a_register, uint8_t data) override; | ||||
|   bool read_bytes(uint8_t a_register, uint8_t *data, size_t len) override; | ||||
|   bool read_byte_16(uint8_t a_register, uint16_t *data) override; | ||||
|   bool bmp_read_byte(uint8_t a_register, uint8_t *data) override; | ||||
|   bool bmp_write_byte(uint8_t a_register, uint8_t data) override; | ||||
|   bool bmp_read_bytes(uint8_t a_register, uint8_t *data, size_t len) override; | ||||
|   bool bmp_read_byte_16(uint8_t a_register, uint16_t *data) override; | ||||
| }; | ||||
|  | ||||
| }  // namespace bmp280_spi | ||||
|   | ||||
| @@ -91,7 +91,7 @@ bool CH422GComponent::read_inputs_() { | ||||
|  | ||||
| // Write a register. Can't use the standard write_byte() method because there is no single pre-configured i2c address. | ||||
| bool CH422GComponent::write_reg_(uint8_t reg, uint8_t value) { | ||||
|   auto err = this->bus_->write(reg, &value, 1); | ||||
|   auto err = this->bus_->write_readv(reg, &value, 1, nullptr, 0); | ||||
|   if (err != i2c::ERROR_OK) { | ||||
|     this->status_set_warning(str_sprintf("write failed for register 0x%X, error %d", reg, err).c_str()); | ||||
|     return false; | ||||
| @@ -102,7 +102,7 @@ bool CH422GComponent::write_reg_(uint8_t reg, uint8_t value) { | ||||
|  | ||||
| uint8_t CH422GComponent::read_reg_(uint8_t reg) { | ||||
|   uint8_t value; | ||||
|   auto err = this->bus_->read(reg, &value, 1); | ||||
|   auto err = this->bus_->write_readv(reg, nullptr, 0, &value, 1); | ||||
|   if (err != i2c::ERROR_OK) { | ||||
|     this->status_set_warning(str_sprintf("read failed for register 0x%X, error %d", reg, err).c_str()); | ||||
|     return 0; | ||||
|   | ||||
| @@ -83,7 +83,7 @@ void EE895Component::write_command_(uint16_t addr, uint16_t reg_cnt) { | ||||
|   crc16 = calc_crc16_(address, 6); | ||||
|   address[5] = crc16 & 0xFF; | ||||
|   address[6] = (crc16 >> 8) & 0xFF; | ||||
|   this->write(address, 7, true); | ||||
|   this->write(address, 7); | ||||
| } | ||||
|  | ||||
| float EE895Component::read_float_() { | ||||
|   | ||||
| @@ -9,9 +9,8 @@ static const char *const TAG = "hte501"; | ||||
|  | ||||
| void HTE501Component::setup() { | ||||
|   uint8_t address[] = {0x70, 0x29}; | ||||
|   this->write(address, 2, false); | ||||
|   uint8_t identification[9]; | ||||
|   this->read(identification, 9); | ||||
|   this->write_read(address, sizeof address, identification, sizeof identification); | ||||
|   if (identification[8] != crc8(identification, 8, 0xFF, 0x31, true)) { | ||||
|     this->error_code_ = CRC_CHECK_FAILED; | ||||
|     this->mark_failed(); | ||||
| @@ -42,7 +41,7 @@ void HTE501Component::dump_config() { | ||||
| float HTE501Component::get_setup_priority() const { return setup_priority::DATA; } | ||||
| void HTE501Component::update() { | ||||
|   uint8_t address_1[] = {0x2C, 0x1B}; | ||||
|   this->write(address_1, 2, true); | ||||
|   this->write(address_1, 2); | ||||
|   this->set_timeout(50, [this]() { | ||||
|     uint8_t i2c_response[6]; | ||||
|     this->read(i2c_response, 6); | ||||
|   | ||||
| @@ -2,7 +2,6 @@ import logging | ||||
|  | ||||
| from esphome import pins | ||||
| import esphome.codegen as cg | ||||
| from esphome.components import esp32 | ||||
| from esphome.config_helpers import filter_source_files_from_platform | ||||
| import esphome.config_validation as cv | ||||
| from esphome.const import ( | ||||
| @@ -14,8 +13,6 @@ from esphome.const import ( | ||||
|     CONF_SCL, | ||||
|     CONF_SDA, | ||||
|     CONF_TIMEOUT, | ||||
|     KEY_CORE, | ||||
|     KEY_FRAMEWORK_VERSION, | ||||
|     PLATFORM_ESP32, | ||||
|     PLATFORM_ESP8266, | ||||
|     PLATFORM_RP2040, | ||||
| @@ -48,28 +45,8 @@ def _bus_declare_type(value): | ||||
|  | ||||
|  | ||||
| def validate_config(config): | ||||
|     if ( | ||||
|         config[CONF_SCAN] | ||||
|         and CORE.is_esp32 | ||||
|         and CORE.using_esp_idf | ||||
|         and esp32.get_esp32_variant() | ||||
|         in [ | ||||
|             esp32.const.VARIANT_ESP32C5, | ||||
|             esp32.const.VARIANT_ESP32C6, | ||||
|             esp32.const.VARIANT_ESP32P4, | ||||
|         ] | ||||
|     ): | ||||
|         version: cv.Version = CORE.data[KEY_CORE][KEY_FRAMEWORK_VERSION] | ||||
|         if version.major == 5 and ( | ||||
|             (version.minor == 3 and version.patch <= 3) | ||||
|             or (version.minor == 4 and version.patch <= 1) | ||||
|         ): | ||||
|             LOGGER.warning( | ||||
|                 "There is a bug in esp-idf version %s that breaks I2C scan, I2C scan " | ||||
|                 "has been disabled, see https://github.com/esphome/issues/issues/7128", | ||||
|                 str(version), | ||||
|             ) | ||||
|             config[CONF_SCAN] = False | ||||
|     if CORE.using_esp_idf: | ||||
|         return cv.require_framework_version(esp_idf=cv.Version(5, 4, 2))(config) | ||||
|     return config | ||||
|  | ||||
|  | ||||
|   | ||||
| @@ -1,4 +1,6 @@ | ||||
| #include "i2c.h" | ||||
|  | ||||
| #include "esphome/core/defines.h" | ||||
| #include "esphome/core/log.h" | ||||
| #include <memory> | ||||
|  | ||||
| @@ -7,38 +9,48 @@ namespace i2c { | ||||
|  | ||||
| static const char *const TAG = "i2c"; | ||||
|  | ||||
| ErrorCode I2CDevice::read_register(uint8_t a_register, uint8_t *data, size_t len, bool stop) { | ||||
|   ErrorCode err = this->write(&a_register, 1, stop); | ||||
|   if (err != ERROR_OK) | ||||
|     return err; | ||||
|   return bus_->read(address_, data, len); | ||||
| void I2CBus::i2c_scan_() { | ||||
|   // suppress logs from the IDF I2C library during the scan | ||||
| #if defined(USE_ESP32) && defined(USE_LOGGER) | ||||
|   auto previous = esp_log_level_get("*"); | ||||
|   esp_log_level_set("*", ESP_LOG_NONE); | ||||
| #endif | ||||
|  | ||||
|   for (uint8_t address = 8; address != 120; address++) { | ||||
|     auto err = write_readv(address, nullptr, 0, nullptr, 0); | ||||
|     if (err == ERROR_OK) { | ||||
|       scan_results_.emplace_back(address, true); | ||||
|     } else if (err == ERROR_UNKNOWN) { | ||||
|       scan_results_.emplace_back(address, false); | ||||
|     } | ||||
|   } | ||||
| #if defined(USE_ESP32) && defined(USE_LOGGER) | ||||
|   esp_log_level_set("*", previous); | ||||
| #endif | ||||
| } | ||||
|  | ||||
| ErrorCode I2CDevice::read_register16(uint16_t a_register, uint8_t *data, size_t len, bool stop) { | ||||
| ErrorCode I2CDevice::read_register(uint8_t a_register, uint8_t *data, size_t len) { | ||||
|   return bus_->write_readv(this->address_, &a_register, 1, data, len); | ||||
| } | ||||
|  | ||||
| ErrorCode I2CDevice::read_register16(uint16_t a_register, uint8_t *data, size_t len) { | ||||
|   a_register = convert_big_endian(a_register); | ||||
|   ErrorCode const err = this->write(reinterpret_cast<const uint8_t *>(&a_register), 2, stop); | ||||
|   if (err != ERROR_OK) | ||||
|     return err; | ||||
|   return bus_->read(address_, data, len); | ||||
|   return bus_->write_readv(this->address_, reinterpret_cast<const uint8_t *>(&a_register), 2, data, len); | ||||
| } | ||||
|  | ||||
| ErrorCode I2CDevice::write_register(uint8_t a_register, const uint8_t *data, size_t len, bool stop) { | ||||
|   WriteBuffer buffers[2]; | ||||
|   buffers[0].data = &a_register; | ||||
|   buffers[0].len = 1; | ||||
|   buffers[1].data = data; | ||||
|   buffers[1].len = len; | ||||
|   return bus_->writev(address_, buffers, 2, stop); | ||||
| ErrorCode I2CDevice::write_register(uint8_t a_register, const uint8_t *data, size_t len) const { | ||||
|   std::vector<uint8_t> v{}; | ||||
|   v.push_back(a_register); | ||||
|   v.insert(v.end(), data, data + len); | ||||
|   return bus_->write_readv(this->address_, v.data(), v.size(), nullptr, 0); | ||||
| } | ||||
|  | ||||
| ErrorCode I2CDevice::write_register16(uint16_t a_register, const uint8_t *data, size_t len, bool stop) { | ||||
|   a_register = convert_big_endian(a_register); | ||||
|   WriteBuffer buffers[2]; | ||||
|   buffers[0].data = reinterpret_cast<const uint8_t *>(&a_register); | ||||
|   buffers[0].len = 2; | ||||
|   buffers[1].data = data; | ||||
|   buffers[1].len = len; | ||||
|   return bus_->writev(address_, buffers, 2, stop); | ||||
| ErrorCode I2CDevice::write_register16(uint16_t a_register, const uint8_t *data, size_t len) const { | ||||
|   std::vector<uint8_t> v(len + 2); | ||||
|   v.push_back(a_register >> 8); | ||||
|   v.push_back(a_register); | ||||
|   v.insert(v.end(), data, data + len); | ||||
|   return bus_->write_readv(this->address_, v.data(), v.size(), nullptr, 0); | ||||
| } | ||||
|  | ||||
| bool I2CDevice::read_bytes_16(uint8_t a_register, uint16_t *data, uint8_t len) { | ||||
| @@ -49,7 +61,7 @@ bool I2CDevice::read_bytes_16(uint8_t a_register, uint16_t *data, uint8_t len) { | ||||
|   return true; | ||||
| } | ||||
|  | ||||
| bool I2CDevice::write_bytes_16(uint8_t a_register, const uint16_t *data, uint8_t len) { | ||||
| bool I2CDevice::write_bytes_16(uint8_t a_register, const uint16_t *data, uint8_t len) const { | ||||
|   // we have to copy in order to be able to change byte order | ||||
|   std::unique_ptr<uint16_t[]> temp{new uint16_t[len]}; | ||||
|   for (size_t i = 0; i < len; i++) | ||||
|   | ||||
| @@ -1,10 +1,10 @@ | ||||
| #pragma once | ||||
|  | ||||
| #include "i2c_bus.h" | ||||
| #include "esphome/core/helpers.h" | ||||
| #include "esphome/core/optional.h" | ||||
| #include <array> | ||||
| #include <vector> | ||||
| #include "esphome/core/helpers.h" | ||||
| #include "esphome/core/optional.h" | ||||
| #include "i2c_bus.h" | ||||
|  | ||||
| namespace esphome { | ||||
| namespace i2c { | ||||
| @@ -161,51 +161,53 @@ class I2CDevice { | ||||
|   /// @param data pointer to an array to store the bytes | ||||
|   /// @param len length of the buffer = number of bytes to read | ||||
|   /// @return an i2c::ErrorCode | ||||
|   ErrorCode read(uint8_t *data, size_t len) { return bus_->read(address_, data, len); } | ||||
|   ErrorCode read(uint8_t *data, size_t len) const { return bus_->write_readv(this->address_, nullptr, 0, data, len); } | ||||
|  | ||||
|   /// @brief reads an array of bytes from a specific register in the I²C device | ||||
|   /// @param a_register an 8 bits internal address of the I²C register to read from | ||||
|   /// @param data pointer to an array to store the bytes | ||||
|   /// @param len length of the buffer = number of bytes to read | ||||
|   /// @param stop (true/false): True will send a stop message, releasing the bus after | ||||
|   /// transmission. False will send a restart, keeping the connection active. | ||||
|   /// @return an i2c::ErrorCode | ||||
|   ErrorCode read_register(uint8_t a_register, uint8_t *data, size_t len, bool stop = true); | ||||
|   ErrorCode read_register(uint8_t a_register, uint8_t *data, size_t len); | ||||
|  | ||||
|   /// @brief reads an array of bytes from a specific register in the I²C device | ||||
|   /// @param a_register the 16 bits internal address of the I²C register to read from | ||||
|   /// @param data pointer to an array of bytes to store the information | ||||
|   /// @param len length of the buffer = number of bytes to read | ||||
|   /// @param stop (true/false): True will send a stop message, releasing the bus after | ||||
|   /// transmission. False will send a restart, keeping the connection active. | ||||
|   /// @return an i2c::ErrorCode | ||||
|   ErrorCode read_register16(uint16_t a_register, uint8_t *data, size_t len, bool stop = true); | ||||
|   ErrorCode read_register16(uint16_t a_register, uint8_t *data, size_t len); | ||||
|  | ||||
|   /// @brief writes an array of bytes to a device using an I2CBus | ||||
|   /// @param data pointer to an array that contains the bytes to send | ||||
|   /// @param len length of the buffer = number of bytes to write | ||||
|   /// @param stop (true/false): True will send a stop message, releasing the bus after | ||||
|   /// transmission. False will send a restart, keeping the connection active. | ||||
|   /// @return an i2c::ErrorCode | ||||
|   ErrorCode write(const uint8_t *data, size_t len, bool stop = true) { return bus_->write(address_, data, len, stop); } | ||||
|   ErrorCode write(const uint8_t *data, size_t len) const { | ||||
|     return bus_->write_readv(this->address_, data, len, nullptr, 0); | ||||
|   } | ||||
|  | ||||
|   /// @brief writes an array of bytes to a device, then reads an array, as a single transaction | ||||
|   /// @param write_data pointer to an array that contains the bytes to send | ||||
|   /// @param write_len length of the buffer = number of bytes to write | ||||
|   /// @param read_data pointer to an array to store the bytes read | ||||
|   /// @param read_len length of the buffer = number of bytes to read | ||||
|   /// @return an i2c::ErrorCode | ||||
|   ErrorCode write_read(const uint8_t *write_data, size_t write_len, uint8_t *read_data, size_t read_len) const { | ||||
|     return bus_->write_readv(this->address_, write_data, write_len, read_data, read_len); | ||||
|   } | ||||
|  | ||||
|   /// @brief writes an array of bytes to a specific register in the I²C device | ||||
|   /// @param a_register the internal address of the register to read from | ||||
|   /// @param data pointer to an array to store the bytes | ||||
|   /// @param len length of the buffer = number of bytes to read | ||||
|   /// @param stop (true/false): True will send a stop message, releasing the bus after | ||||
|   /// transmission. False will send a restart, keeping the connection active. | ||||
|   /// @return an i2c::ErrorCode | ||||
|   ErrorCode write_register(uint8_t a_register, const uint8_t *data, size_t len, bool stop = true); | ||||
|   ErrorCode write_register(uint8_t a_register, const uint8_t *data, size_t len) const; | ||||
|  | ||||
|   /// @brief write an array of bytes to a specific register in the I²C device | ||||
|   /// @param a_register the 16 bits internal address of the register to read from | ||||
|   /// @param data pointer to an array to store the bytes | ||||
|   /// @param len length of the buffer = number of bytes to read | ||||
|   /// @param stop (true/false): True will send a stop message, releasing the bus after | ||||
|   /// transmission. False will send a restart, keeping the connection active. | ||||
|   /// @return an i2c::ErrorCode | ||||
|   ErrorCode write_register16(uint16_t a_register, const uint8_t *data, size_t len, bool stop = true); | ||||
|   ErrorCode write_register16(uint16_t a_register, const uint8_t *data, size_t len) const; | ||||
|  | ||||
|   /// | ||||
|   /// Compat APIs | ||||
| @@ -217,7 +219,7 @@ class I2CDevice { | ||||
|     return read_register(a_register, data, len) == ERROR_OK; | ||||
|   } | ||||
|  | ||||
|   bool read_bytes_raw(uint8_t *data, uint8_t len) { return read(data, len) == ERROR_OK; } | ||||
|   bool read_bytes_raw(uint8_t *data, uint8_t len) const { return read(data, len) == ERROR_OK; } | ||||
|  | ||||
|   template<size_t N> optional<std::array<uint8_t, N>> read_bytes(uint8_t a_register) { | ||||
|     std::array<uint8_t, N> res; | ||||
| @@ -236,9 +238,7 @@ class I2CDevice { | ||||
|  | ||||
|   bool read_bytes_16(uint8_t a_register, uint16_t *data, uint8_t len); | ||||
|  | ||||
|   bool read_byte(uint8_t a_register, uint8_t *data, bool stop = true) { | ||||
|     return read_register(a_register, data, 1, stop) == ERROR_OK; | ||||
|   } | ||||
|   bool read_byte(uint8_t a_register, uint8_t *data) { return read_register(a_register, data, 1) == ERROR_OK; } | ||||
|  | ||||
|   optional<uint8_t> read_byte(uint8_t a_register) { | ||||
|     uint8_t data; | ||||
| @@ -249,11 +249,11 @@ class I2CDevice { | ||||
|  | ||||
|   bool read_byte_16(uint8_t a_register, uint16_t *data) { return read_bytes_16(a_register, data, 1); } | ||||
|  | ||||
|   bool write_bytes(uint8_t a_register, const uint8_t *data, uint8_t len, bool stop = true) { | ||||
|     return write_register(a_register, data, len, stop) == ERROR_OK; | ||||
|   bool write_bytes(uint8_t a_register, const uint8_t *data, uint8_t len) const { | ||||
|     return write_register(a_register, data, len) == ERROR_OK; | ||||
|   } | ||||
|  | ||||
|   bool write_bytes(uint8_t a_register, const std::vector<uint8_t> &data) { | ||||
|   bool write_bytes(uint8_t a_register, const std::vector<uint8_t> &data) const { | ||||
|     return write_bytes(a_register, data.data(), data.size()); | ||||
|   } | ||||
|  | ||||
| @@ -261,13 +261,42 @@ class I2CDevice { | ||||
|     return write_bytes(a_register, data.data(), data.size()); | ||||
|   } | ||||
|  | ||||
|   bool write_bytes_16(uint8_t a_register, const uint16_t *data, uint8_t len); | ||||
|   bool write_bytes_16(uint8_t a_register, const uint16_t *data, uint8_t len) const; | ||||
|  | ||||
|   bool write_byte(uint8_t a_register, uint8_t data, bool stop = true) { | ||||
|     return write_bytes(a_register, &data, 1, stop); | ||||
|   bool write_byte(uint8_t a_register, uint8_t data) const { return write_bytes(a_register, &data, 1); } | ||||
|  | ||||
|   bool write_byte_16(uint8_t a_register, uint16_t data) const { return write_bytes_16(a_register, &data, 1); } | ||||
|  | ||||
|   // Deprecated functions | ||||
|  | ||||
|   ESPDEPRECATED("The stop argument is no longer used. This will be removed from ESPHome 2026.3.0", "2025.9.0") | ||||
|   ErrorCode read_register(uint8_t a_register, uint8_t *data, size_t len, bool stop) { | ||||
|     return this->read_register(a_register, data, len); | ||||
|   } | ||||
|  | ||||
|   bool write_byte_16(uint8_t a_register, uint16_t data) { return write_bytes_16(a_register, &data, 1); } | ||||
|   ESPDEPRECATED("The stop argument is no longer used. This will be removed from ESPHome 2026.3.0", "2025.9.0") | ||||
|   ErrorCode read_register16(uint16_t a_register, uint8_t *data, size_t len, bool stop) { | ||||
|     return this->read_register16(a_register, data, len); | ||||
|   } | ||||
|  | ||||
|   ESPDEPRECATED("The stop argument is no longer used; use write_read() for consecutive write and read. This will be " | ||||
|                 "removed from ESPHome 2026.3.0", | ||||
|                 "2025.9.0") | ||||
|   ErrorCode write(const uint8_t *data, size_t len, bool stop) const { return this->write(data, len); } | ||||
|  | ||||
|   ESPDEPRECATED("The stop argument is no longer used; use write_read() for consecutive write and read. This will be " | ||||
|                 "removed from ESPHome 2026.3.0", | ||||
|                 "2025.9.0") | ||||
|   ErrorCode write_register(uint8_t a_register, const uint8_t *data, size_t len, bool stop) const { | ||||
|     return this->write_register(a_register, data, len); | ||||
|   } | ||||
|  | ||||
|   ESPDEPRECATED("The stop argument is no longer used; use write_read() for consecutive write and read. This will be " | ||||
|                 "removed from ESPHome 2026.3.0", | ||||
|                 "2025.9.0") | ||||
|   ErrorCode write_register16(uint16_t a_register, const uint8_t *data, size_t len, bool stop) const { | ||||
|     return this->write_register16(a_register, data, len); | ||||
|   } | ||||
|  | ||||
|  protected: | ||||
|   uint8_t address_{0x00};  ///< store the address of the device on the bus | ||||
|   | ||||
| @@ -1,9 +1,12 @@ | ||||
| #pragma once | ||||
| #include <cstddef> | ||||
| #include <cstdint> | ||||
| #include <cstring> | ||||
| #include <utility> | ||||
| #include <vector> | ||||
|  | ||||
| #include "esphome/core/helpers.h" | ||||
|  | ||||
| namespace esphome { | ||||
| namespace i2c { | ||||
|  | ||||
| @@ -39,71 +42,66 @@ struct WriteBuffer { | ||||
| /// note https://www.nxp.com/docs/en/application-note/AN10216.pdf | ||||
| class I2CBus { | ||||
|  public: | ||||
|   /// @brief Creates a ReadBuffer and calls the virtual readv() method to read bytes into this buffer | ||||
|   /// @param address address of the I²C component on the i2c bus | ||||
|   /// @param buffer pointer to an array of bytes that will be used to store the data received | ||||
|   /// @param len length of the buffer = number of bytes to read | ||||
|   /// @return an i2c::ErrorCode | ||||
|   virtual ErrorCode read(uint8_t address, uint8_t *buffer, size_t len) { | ||||
|     ReadBuffer buf; | ||||
|     buf.data = buffer; | ||||
|     buf.len = len; | ||||
|     return readv(address, &buf, 1); | ||||
|   } | ||||
|   virtual ~I2CBus() = default; | ||||
|  | ||||
|   /// @brief This virtual method reads bytes from an I2CBus into an array of ReadBuffer. | ||||
|   /// @param address address of the I²C component on the i2c bus | ||||
|   /// @param buffers pointer to an array of ReadBuffer | ||||
|   /// @param count number of ReadBuffer to read | ||||
|   /// @return an i2c::ErrorCode | ||||
|   /// @details This is a pure virtual method that must be implemented in a subclass. | ||||
|   virtual ErrorCode readv(uint8_t address, ReadBuffer *buffers, size_t count) = 0; | ||||
|  | ||||
|   virtual ErrorCode write(uint8_t address, const uint8_t *buffer, size_t len) { | ||||
|     return write(address, buffer, len, true); | ||||
|   } | ||||
|  | ||||
|   /// @brief Creates a WriteBuffer and calls the writev() method to send the bytes from this buffer | ||||
|   /// @param address address of the I²C component on the i2c bus | ||||
|   /// @param buffer pointer to an array of bytes that contains the data to be sent | ||||
|   /// @param len length of the buffer = number of bytes to write | ||||
|   /// @param stop true or false: True will send a stop message, releasing the bus after | ||||
|   /// transmission. False will send a restart, keeping the connection active. | ||||
|   /// @return an i2c::ErrorCode | ||||
|   virtual ErrorCode write(uint8_t address, const uint8_t *buffer, size_t len, bool stop) { | ||||
|     WriteBuffer buf; | ||||
|     buf.data = buffer; | ||||
|     buf.len = len; | ||||
|     return writev(address, &buf, 1, stop); | ||||
|   } | ||||
|  | ||||
|   virtual ErrorCode writev(uint8_t address, WriteBuffer *buffers, size_t cnt) { | ||||
|     return writev(address, buffers, cnt, true); | ||||
|   } | ||||
|  | ||||
|   /// @brief This virtual method writes bytes to an I2CBus from an array of WriteBuffer. | ||||
|   /// @param address address of the I²C component on the i2c bus | ||||
|   /// @param buffers pointer to an array of WriteBuffer | ||||
|   /// @param count number of WriteBuffer to write | ||||
|   /// @param stop true or false: True will send a stop message, releasing the bus after | ||||
|   /// @brief This virtual method writes bytes to an I2CBus from an array, | ||||
|   /// then reads bytes into an array of ReadBuffer. | ||||
|   /// @param address address of the I²C device on the i2c bus | ||||
|   /// @param write_buffer pointer to data | ||||
|   /// @param write_count number of bytes to write | ||||
|   /// @param read_buffer pointer to an array to receive data | ||||
|   /// @param read_count number of bytes to read | ||||
|   /// transmission. False will send a restart, keeping the connection active. | ||||
|   /// @return an i2c::ErrorCode | ||||
|   /// @details This is a pure virtual method that must be implemented in the subclass. | ||||
|   virtual ErrorCode writev(uint8_t address, WriteBuffer *buffers, size_t count, bool stop) = 0; | ||||
|   virtual ErrorCode write_readv(uint8_t address, const uint8_t *write_buffer, size_t write_count, uint8_t *read_buffer, | ||||
|                                 size_t read_count) = 0; | ||||
|  | ||||
|   // Legacy functions for compatibility | ||||
|  | ||||
|   ErrorCode read(uint8_t address, uint8_t *buffer, size_t len) { | ||||
|     return this->write_readv(address, nullptr, 0, buffer, len); | ||||
|   } | ||||
|  | ||||
|   ErrorCode write(uint8_t address, const uint8_t *buffer, size_t len, bool stop = true) { | ||||
|     return this->write_readv(address, buffer, len, nullptr, 0); | ||||
|   } | ||||
|  | ||||
|   ESPDEPRECATED("This method is deprecated and will be removed in ESPHome 2026.3.0. Use write_readv() instead.", | ||||
|                 "2025.9.0") | ||||
|   ErrorCode readv(uint8_t address, ReadBuffer *read_buffers, size_t count) { | ||||
|     size_t total_len = 0; | ||||
|     for (size_t i = 0; i != count; i++) { | ||||
|       total_len += read_buffers[i].len; | ||||
|     } | ||||
|     std::vector<uint8_t> buffer(total_len); | ||||
|     auto err = this->write_readv(address, nullptr, 0, buffer.data(), total_len); | ||||
|     if (err != ERROR_OK) | ||||
|       return err; | ||||
|     size_t pos = 0; | ||||
|     for (size_t i = 0; i != count; i++) { | ||||
|       if (read_buffers[i].len != 0) { | ||||
|         std::memcpy(read_buffers[i].data, buffer.data() + pos, read_buffers[i].len); | ||||
|         pos += read_buffers[i].len; | ||||
|       } | ||||
|     } | ||||
|     return ERROR_OK; | ||||
|   } | ||||
|  | ||||
|   ESPDEPRECATED("This method is deprecated and will be removed in ESPHome 2026.3.0. Use write_readv() instead.", | ||||
|                 "2025.9.0") | ||||
|   ErrorCode writev(uint8_t address, const WriteBuffer *write_buffers, size_t count, bool stop = true) { | ||||
|     std::vector<uint8_t> buffer{}; | ||||
|     for (size_t i = 0; i != count; i++) { | ||||
|       buffer.insert(buffer.end(), write_buffers[i].data, write_buffers[i].data + write_buffers[i].len); | ||||
|     } | ||||
|     return this->write_readv(address, buffer.data(), buffer.size(), nullptr, 0); | ||||
|   } | ||||
|  | ||||
|  protected: | ||||
|   /// @brief Scans the I2C bus for devices. Devices presence is kept in an array of std::pair | ||||
|   /// that contains the address and the corresponding bool presence flag. | ||||
|   virtual void i2c_scan() { | ||||
|     for (uint8_t address = 8; address < 120; address++) { | ||||
|       auto err = writev(address, nullptr, 0); | ||||
|       if (err == ERROR_OK) { | ||||
|         scan_results_.emplace_back(address, true); | ||||
|       } else if (err == ERROR_UNKNOWN) { | ||||
|         scan_results_.emplace_back(address, false); | ||||
|       } | ||||
|     } | ||||
|   } | ||||
|   void i2c_scan_(); | ||||
|   std::vector<std::pair<uint8_t, bool>> scan_results_;  ///< array containing scan results | ||||
|   bool scan_{false};                                    ///< Should we scan ? Can be set in the yaml | ||||
| }; | ||||
|   | ||||
| @@ -41,7 +41,7 @@ void ArduinoI2CBus::setup() { | ||||
|   this->initialized_ = true; | ||||
|   if (this->scan_) { | ||||
|     ESP_LOGV(TAG, "Scanning bus for active devices"); | ||||
|     this->i2c_scan(); | ||||
|     this->i2c_scan_(); | ||||
|   } | ||||
| } | ||||
|  | ||||
| @@ -111,88 +111,37 @@ void ArduinoI2CBus::dump_config() { | ||||
|   } | ||||
| } | ||||
|  | ||||
| ErrorCode ArduinoI2CBus::readv(uint8_t address, ReadBuffer *buffers, size_t cnt) { | ||||
| ErrorCode ArduinoI2CBus::write_readv(uint8_t address, const uint8_t *write_buffer, size_t write_count, | ||||
|                                      uint8_t *read_buffer, size_t read_count) { | ||||
| #if defined(USE_ESP8266) | ||||
|   this->set_pins_and_clock_();  // reconfigure Wire global state in case there are multiple instances | ||||
| #endif | ||||
|  | ||||
|   // logging is only enabled with vv level, if warnings are shown the caller | ||||
|   // should log them | ||||
|   if (!initialized_) { | ||||
|     ESP_LOGVV(TAG, "i2c bus not initialized!"); | ||||
|     return ERROR_NOT_INITIALIZED; | ||||
|   } | ||||
|   size_t to_request = 0; | ||||
|   for (size_t i = 0; i < cnt; i++) | ||||
|     to_request += buffers[i].len; | ||||
|   size_t ret = wire_->requestFrom(address, to_request, true); | ||||
|   if (ret != to_request) { | ||||
|     ESP_LOGVV(TAG, "RX %u from %02X failed with error %u", to_request, address, ret); | ||||
|     return ERROR_TIMEOUT; | ||||
|   } | ||||
|  | ||||
|   for (size_t i = 0; i < cnt; i++) { | ||||
|     const auto &buf = buffers[i]; | ||||
|     for (size_t j = 0; j < buf.len; j++) | ||||
|       buf.data[j] = wire_->read(); | ||||
|   } | ||||
|  | ||||
| #ifdef ESPHOME_LOG_HAS_VERY_VERBOSE | ||||
|   char debug_buf[4]; | ||||
|   std::string debug_hex; | ||||
|  | ||||
|   for (size_t i = 0; i < cnt; i++) { | ||||
|     const auto &buf = buffers[i]; | ||||
|     for (size_t j = 0; j < buf.len; j++) { | ||||
|       snprintf(debug_buf, sizeof(debug_buf), "%02X", buf.data[j]); | ||||
|       debug_hex += debug_buf; | ||||
|     } | ||||
|   } | ||||
|   ESP_LOGVV(TAG, "0x%02X RX %s", address, debug_hex.c_str()); | ||||
| #endif | ||||
|  | ||||
|   return ERROR_OK; | ||||
| } | ||||
| ErrorCode ArduinoI2CBus::writev(uint8_t address, WriteBuffer *buffers, size_t cnt, bool stop) { | ||||
| #if defined(USE_ESP8266) | ||||
|   this->set_pins_and_clock_();  // reconfigure Wire global state in case there are multiple instances | ||||
| #endif | ||||
|  | ||||
|   // logging is only enabled with vv level, if warnings are shown the caller | ||||
|   // should log them | ||||
|   if (!initialized_) { | ||||
|     ESP_LOGVV(TAG, "i2c bus not initialized!"); | ||||
|     ESP_LOGD(TAG, "i2c bus not initialized!"); | ||||
|     return ERROR_NOT_INITIALIZED; | ||||
|   } | ||||
|  | ||||
| #ifdef ESPHOME_LOG_HAS_VERY_VERBOSE | ||||
|   char debug_buf[4]; | ||||
|   std::string debug_hex; | ||||
|   ESP_LOGV(TAG, "0x%02X TX %s", address, format_hex_pretty(write_buffer, write_count).c_str()); | ||||
|  | ||||
|   for (size_t i = 0; i < cnt; i++) { | ||||
|     const auto &buf = buffers[i]; | ||||
|     for (size_t j = 0; j < buf.len; j++) { | ||||
|       snprintf(debug_buf, sizeof(debug_buf), "%02X", buf.data[j]); | ||||
|       debug_hex += debug_buf; | ||||
|     } | ||||
|   } | ||||
|   ESP_LOGVV(TAG, "0x%02X TX %s", address, debug_hex.c_str()); | ||||
| #endif | ||||
|  | ||||
|   wire_->beginTransmission(address); | ||||
|   size_t written = 0; | ||||
|   for (size_t i = 0; i < cnt; i++) { | ||||
|     const auto &buf = buffers[i]; | ||||
|     if (buf.len == 0) | ||||
|       continue; | ||||
|     size_t ret = wire_->write(buf.data, buf.len); | ||||
|     written += ret; | ||||
|     if (ret != buf.len) { | ||||
|       ESP_LOGVV(TAG, "TX failed at %u", written); | ||||
|   uint8_t status = 0; | ||||
|   if (write_count != 0 || read_count == 0) { | ||||
|     wire_->beginTransmission(address); | ||||
|     size_t ret = wire_->write(write_buffer, write_count); | ||||
|     if (ret != write_count) { | ||||
|       ESP_LOGV(TAG, "TX failed"); | ||||
|       return ERROR_UNKNOWN; | ||||
|     } | ||||
|     status = wire_->endTransmission(read_count == 0); | ||||
|   } | ||||
|   if (status == 0 && read_count != 0) { | ||||
|     size_t ret2 = wire_->requestFrom(address, read_count, true); | ||||
|     if (ret2 != read_count) { | ||||
|       ESP_LOGVV(TAG, "RX %u from %02X failed with error %u", read_count, address, ret2); | ||||
|       return ERROR_TIMEOUT; | ||||
|     } | ||||
|     for (size_t j = 0; j != read_count; j++) | ||||
|       read_buffer[j] = wire_->read(); | ||||
|   } | ||||
|   uint8_t status = wire_->endTransmission(stop); | ||||
|   switch (status) { | ||||
|     case 0: | ||||
|       return ERROR_OK; | ||||
|   | ||||
| @@ -19,8 +19,8 @@ class ArduinoI2CBus : public InternalI2CBus, public Component { | ||||
|  public: | ||||
|   void setup() override; | ||||
|   void dump_config() override; | ||||
|   ErrorCode readv(uint8_t address, ReadBuffer *buffers, size_t cnt) override; | ||||
|   ErrorCode writev(uint8_t address, WriteBuffer *buffers, size_t cnt, bool stop) override; | ||||
|   ErrorCode write_readv(uint8_t address, const uint8_t *write_buffer, size_t write_count, uint8_t *read_buffer, | ||||
|                         size_t read_count) override; | ||||
|   float get_setup_priority() const override { return setup_priority::BUS; } | ||||
|  | ||||
|   void set_scan(bool scan) { scan_ = scan; } | ||||
|   | ||||
| @@ -1,6 +1,7 @@ | ||||
| #ifdef USE_ESP_IDF | ||||
|  | ||||
| #include "i2c_bus_esp_idf.h" | ||||
|  | ||||
| #include <driver/gpio.h> | ||||
| #include <cinttypes> | ||||
| #include <cstring> | ||||
| @@ -9,10 +10,6 @@ | ||||
| #include "esphome/core/helpers.h" | ||||
| #include "esphome/core/log.h" | ||||
|  | ||||
| #if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 3, 0) | ||||
| #define SOC_HP_I2C_NUM SOC_I2C_NUM | ||||
| #endif | ||||
|  | ||||
| namespace esphome { | ||||
| namespace i2c { | ||||
|  | ||||
| @@ -34,7 +31,6 @@ void IDFI2CBus::setup() { | ||||
|  | ||||
|   this->recover_(); | ||||
|  | ||||
| #if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 4, 2) | ||||
|   next_port = (i2c_port_t) (next_port + 1); | ||||
|  | ||||
|   i2c_master_bus_config_t bus_conf{}; | ||||
| @@ -77,56 +73,8 @@ void IDFI2CBus::setup() { | ||||
|  | ||||
|   if (this->scan_) { | ||||
|     ESP_LOGV(TAG, "Scanning for devices"); | ||||
|     this->i2c_scan(); | ||||
|     this->i2c_scan_(); | ||||
|   } | ||||
| #else | ||||
| #if SOC_HP_I2C_NUM > 1 | ||||
|   next_port = (next_port == I2C_NUM_0) ? I2C_NUM_1 : I2C_NUM_MAX; | ||||
| #else | ||||
|   next_port = I2C_NUM_MAX; | ||||
| #endif | ||||
|  | ||||
|   i2c_config_t conf{}; | ||||
|   memset(&conf, 0, sizeof(conf)); | ||||
|   conf.mode = I2C_MODE_MASTER; | ||||
|   conf.sda_io_num = sda_pin_; | ||||
|   conf.sda_pullup_en = sda_pullup_enabled_; | ||||
|   conf.scl_io_num = scl_pin_; | ||||
|   conf.scl_pullup_en = scl_pullup_enabled_; | ||||
|   conf.master.clk_speed = frequency_; | ||||
| #ifdef USE_ESP32_VARIANT_ESP32S2 | ||||
|   // workaround for https://github.com/esphome/issues/issues/6718 | ||||
|   conf.clk_flags = I2C_SCLK_SRC_FLAG_AWARE_DFS; | ||||
| #endif | ||||
|   esp_err_t err = i2c_param_config(port_, &conf); | ||||
|   if (err != ESP_OK) { | ||||
|     ESP_LOGW(TAG, "i2c_param_config failed: %s", esp_err_to_name(err)); | ||||
|     this->mark_failed(); | ||||
|     return; | ||||
|   } | ||||
|   if (timeout_ > 0) { | ||||
|     err = i2c_set_timeout(port_, timeout_ * 80);  // unit: APB 80MHz clock cycle | ||||
|     if (err != ESP_OK) { | ||||
|       ESP_LOGW(TAG, "i2c_set_timeout failed: %s", esp_err_to_name(err)); | ||||
|       this->mark_failed(); | ||||
|       return; | ||||
|     } else { | ||||
|       ESP_LOGV(TAG, "i2c_timeout set to %" PRIu32 " ticks (%" PRIu32 " us)", timeout_ * 80, timeout_); | ||||
|     } | ||||
|   } | ||||
|   err = i2c_driver_install(port_, I2C_MODE_MASTER, 0, 0, 0); | ||||
|   if (err != ESP_OK) { | ||||
|     ESP_LOGW(TAG, "i2c_driver_install failed: %s", esp_err_to_name(err)); | ||||
|     this->mark_failed(); | ||||
|     return; | ||||
|   } | ||||
|  | ||||
|   initialized_ = true; | ||||
|   if (this->scan_) { | ||||
|     ESP_LOGV(TAG, "Scanning bus for active devices"); | ||||
|     this->i2c_scan(); | ||||
|   } | ||||
| #endif | ||||
| } | ||||
|  | ||||
| void IDFI2CBus::dump_config() { | ||||
| @@ -166,267 +114,73 @@ void IDFI2CBus::dump_config() { | ||||
|   } | ||||
| } | ||||
|  | ||||
| #if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 4, 2) | ||||
| void IDFI2CBus::i2c_scan() { | ||||
|   for (uint8_t address = 8; address < 120; address++) { | ||||
|     auto err = i2c_master_probe(this->bus_, address, 20); | ||||
|     if (err == ESP_OK) { | ||||
|       this->scan_results_.emplace_back(address, true); | ||||
|     } | ||||
|   } | ||||
| } | ||||
| #endif | ||||
|  | ||||
| ErrorCode IDFI2CBus::readv(uint8_t address, ReadBuffer *buffers, size_t cnt) { | ||||
|   // logging is only enabled with vv level, if warnings are shown the caller | ||||
| ErrorCode IDFI2CBus::write_readv(uint8_t address, const uint8_t *write_buffer, size_t write_count, uint8_t *read_buffer, | ||||
|                                  size_t read_count) { | ||||
|   // logging is only enabled with v level, if warnings are shown the caller | ||||
|   // should log them | ||||
|   if (!initialized_) { | ||||
|     ESP_LOGVV(TAG, "i2c bus not initialized!"); | ||||
|     ESP_LOGW(TAG, "i2c bus not initialized!"); | ||||
|     return ERROR_NOT_INITIALIZED; | ||||
|   } | ||||
|  | ||||
| #if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 4, 2) | ||||
|   i2c_operation_job_t jobs[cnt + 4]; | ||||
|   uint8_t read = (address << 1) | I2C_MASTER_READ; | ||||
|   size_t last = 0, num = 0; | ||||
|  | ||||
|   jobs[num].command = I2C_MASTER_CMD_START; | ||||
|   num++; | ||||
|  | ||||
|   jobs[num].command = I2C_MASTER_CMD_WRITE; | ||||
|   jobs[num].write.ack_check = true; | ||||
|   jobs[num].write.data = &read; | ||||
|   jobs[num].write.total_bytes = 1; | ||||
|   num++; | ||||
|  | ||||
|   // find the last valid index | ||||
|   for (size_t i = 0; i < cnt; i++) { | ||||
|     const auto &buf = buffers[i]; | ||||
|     if (buf.len == 0) { | ||||
|       continue; | ||||
|   i2c_operation_job_t jobs[8]{}; | ||||
|   size_t num_jobs = 0; | ||||
|   uint8_t write_addr = (address << 1) | I2C_MASTER_WRITE; | ||||
|   uint8_t read_addr = (address << 1) | I2C_MASTER_READ; | ||||
|   ESP_LOGV(TAG, "Writing %zu bytes, reading %zu bytes", write_count, read_count); | ||||
|   if (read_count == 0 && write_count == 0) { | ||||
|     // basically just a bus probe. Send a start, address and stop | ||||
|     ESP_LOGV(TAG, "0x%02X BUS PROBE", address); | ||||
|     jobs[num_jobs++].command = I2C_MASTER_CMD_START; | ||||
|     jobs[num_jobs].command = I2C_MASTER_CMD_WRITE; | ||||
|     jobs[num_jobs].write.ack_check = true; | ||||
|     jobs[num_jobs].write.data = &write_addr; | ||||
|     jobs[num_jobs++].write.total_bytes = 1; | ||||
|   } else { | ||||
|     if (write_count != 0) { | ||||
|       ESP_LOGV(TAG, "0x%02X TX %s", address, format_hex_pretty(write_buffer, write_count).c_str()); | ||||
|       jobs[num_jobs++].command = I2C_MASTER_CMD_START; | ||||
|       jobs[num_jobs].command = I2C_MASTER_CMD_WRITE; | ||||
|       jobs[num_jobs].write.ack_check = true; | ||||
|       jobs[num_jobs].write.data = &write_addr; | ||||
|       jobs[num_jobs++].write.total_bytes = 1; | ||||
|       jobs[num_jobs].command = I2C_MASTER_CMD_WRITE; | ||||
|       jobs[num_jobs].write.ack_check = true; | ||||
|       jobs[num_jobs].write.data = (uint8_t *) write_buffer; | ||||
|       jobs[num_jobs++].write.total_bytes = write_count; | ||||
|     } | ||||
|     last = i; | ||||
|   } | ||||
|  | ||||
|   for (size_t i = 0; i < cnt; i++) { | ||||
|     const auto &buf = buffers[i]; | ||||
|     if (buf.len == 0) { | ||||
|       continue; | ||||
|     } | ||||
|     if (i == last) { | ||||
|       // the last byte read before stop should always be a nack, | ||||
|       // split the last read if len is larger than 1 | ||||
|       if (buf.len > 1) { | ||||
|         jobs[num].command = I2C_MASTER_CMD_READ; | ||||
|         jobs[num].read.ack_value = I2C_ACK_VAL; | ||||
|         jobs[num].read.data = (uint8_t *) buf.data; | ||||
|         jobs[num].read.total_bytes = buf.len - 1; | ||||
|         num++; | ||||
|     if (read_count != 0) { | ||||
|       ESP_LOGV(TAG, "0x%02X RX bytes %zu", address, read_count); | ||||
|       jobs[num_jobs++].command = I2C_MASTER_CMD_START; | ||||
|       jobs[num_jobs].command = I2C_MASTER_CMD_WRITE; | ||||
|       jobs[num_jobs].write.ack_check = true; | ||||
|       jobs[num_jobs].write.data = &read_addr; | ||||
|       jobs[num_jobs++].write.total_bytes = 1; | ||||
|       if (read_count > 1) { | ||||
|         jobs[num_jobs].command = I2C_MASTER_CMD_READ; | ||||
|         jobs[num_jobs].read.ack_value = I2C_ACK_VAL; | ||||
|         jobs[num_jobs].read.data = read_buffer; | ||||
|         jobs[num_jobs++].read.total_bytes = read_count - 1; | ||||
|       } | ||||
|       jobs[num].command = I2C_MASTER_CMD_READ; | ||||
|       jobs[num].read.ack_value = I2C_NACK_VAL; | ||||
|       jobs[num].read.data = (uint8_t *) buf.data + buf.len - 1; | ||||
|       jobs[num].read.total_bytes = 1; | ||||
|       num++; | ||||
|     } else { | ||||
|       jobs[num].command = I2C_MASTER_CMD_READ; | ||||
|       jobs[num].read.ack_value = I2C_ACK_VAL; | ||||
|       jobs[num].read.data = (uint8_t *) buf.data; | ||||
|       jobs[num].read.total_bytes = buf.len; | ||||
|       num++; | ||||
|       jobs[num_jobs].command = I2C_MASTER_CMD_READ; | ||||
|       jobs[num_jobs].read.ack_value = I2C_NACK_VAL; | ||||
|       jobs[num_jobs].read.data = read_buffer + read_count - 1; | ||||
|       jobs[num_jobs++].read.total_bytes = 1; | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   jobs[num].command = I2C_MASTER_CMD_STOP; | ||||
|   num++; | ||||
|  | ||||
|   esp_err_t err = i2c_master_execute_defined_operations(this->dev_, jobs, num, 20); | ||||
|   jobs[num_jobs++].command = I2C_MASTER_CMD_STOP; | ||||
|   ESP_LOGV(TAG, "Sending %zu jobs", num_jobs); | ||||
|   esp_err_t err = i2c_master_execute_defined_operations(this->dev_, jobs, num_jobs, 20); | ||||
|   if (err == ESP_ERR_INVALID_STATE) { | ||||
|     ESP_LOGVV(TAG, "RX from %02X failed: not acked", address); | ||||
|     ESP_LOGV(TAG, "TX to %02X failed: not acked", address); | ||||
|     return ERROR_NOT_ACKNOWLEDGED; | ||||
|   } else if (err == ESP_ERR_TIMEOUT) { | ||||
|     ESP_LOGVV(TAG, "RX from %02X failed: timeout", address); | ||||
|     ESP_LOGV(TAG, "TX to %02X failed: timeout", address); | ||||
|     return ERROR_TIMEOUT; | ||||
|   } else if (err != ESP_OK) { | ||||
|     ESP_LOGVV(TAG, "RX from %02X failed: %s", address, esp_err_to_name(err)); | ||||
|     ESP_LOGV(TAG, "TX to %02X failed: %s", address, esp_err_to_name(err)); | ||||
|     return ERROR_UNKNOWN; | ||||
|   } | ||||
| #else | ||||
|   i2c_cmd_handle_t cmd = i2c_cmd_link_create(); | ||||
|   esp_err_t err = i2c_master_start(cmd); | ||||
|   if (err != ESP_OK) { | ||||
|     ESP_LOGVV(TAG, "RX from %02X master start failed: %s", address, esp_err_to_name(err)); | ||||
|     i2c_cmd_link_delete(cmd); | ||||
|     return ERROR_UNKNOWN; | ||||
|   } | ||||
|   err = i2c_master_write_byte(cmd, (address << 1) | I2C_MASTER_READ, true); | ||||
|   if (err != ESP_OK) { | ||||
|     ESP_LOGVV(TAG, "RX from %02X address write failed: %s", address, esp_err_to_name(err)); | ||||
|     i2c_cmd_link_delete(cmd); | ||||
|     return ERROR_UNKNOWN; | ||||
|   } | ||||
|   for (size_t i = 0; i < cnt; i++) { | ||||
|     const auto &buf = buffers[i]; | ||||
|     if (buf.len == 0) | ||||
|       continue; | ||||
|     err = i2c_master_read(cmd, buf.data, buf.len, i == cnt - 1 ? I2C_MASTER_LAST_NACK : I2C_MASTER_ACK); | ||||
|     if (err != ESP_OK) { | ||||
|       ESP_LOGVV(TAG, "RX from %02X data read failed: %s", address, esp_err_to_name(err)); | ||||
|       i2c_cmd_link_delete(cmd); | ||||
|       return ERROR_UNKNOWN; | ||||
|     } | ||||
|   } | ||||
|   err = i2c_master_stop(cmd); | ||||
|   if (err != ESP_OK) { | ||||
|     ESP_LOGVV(TAG, "RX from %02X stop failed: %s", address, esp_err_to_name(err)); | ||||
|     i2c_cmd_link_delete(cmd); | ||||
|     return ERROR_UNKNOWN; | ||||
|   } | ||||
|   err = i2c_master_cmd_begin(port_, cmd, 20 / portTICK_PERIOD_MS); | ||||
|   // i2c_master_cmd_begin() will block for a whole second if no ack: | ||||
|   // https://github.com/espressif/esp-idf/issues/4999 | ||||
|   i2c_cmd_link_delete(cmd); | ||||
|   if (err == ESP_FAIL) { | ||||
|     // transfer not acked | ||||
|     ESP_LOGVV(TAG, "RX from %02X failed: not acked", address); | ||||
|     return ERROR_NOT_ACKNOWLEDGED; | ||||
|   } else if (err == ESP_ERR_TIMEOUT) { | ||||
|     ESP_LOGVV(TAG, "RX from %02X failed: timeout", address); | ||||
|     return ERROR_TIMEOUT; | ||||
|   } else if (err != ESP_OK) { | ||||
|     ESP_LOGVV(TAG, "RX from %02X failed: %s", address, esp_err_to_name(err)); | ||||
|     return ERROR_UNKNOWN; | ||||
|   } | ||||
| #endif | ||||
|  | ||||
| #ifdef ESPHOME_LOG_HAS_VERY_VERBOSE | ||||
|   char debug_buf[4]; | ||||
|   std::string debug_hex; | ||||
|  | ||||
|   for (size_t i = 0; i < cnt; i++) { | ||||
|     const auto &buf = buffers[i]; | ||||
|     for (size_t j = 0; j < buf.len; j++) { | ||||
|       snprintf(debug_buf, sizeof(debug_buf), "%02X", buf.data[j]); | ||||
|       debug_hex += debug_buf; | ||||
|     } | ||||
|   } | ||||
|   ESP_LOGVV(TAG, "0x%02X RX %s", address, debug_hex.c_str()); | ||||
| #endif | ||||
|  | ||||
|   return ERROR_OK; | ||||
| } | ||||
|  | ||||
| ErrorCode IDFI2CBus::writev(uint8_t address, WriteBuffer *buffers, size_t cnt, bool stop) { | ||||
|   // logging is only enabled with vv level, if warnings are shown the caller | ||||
|   // should log them | ||||
|   if (!initialized_) { | ||||
|     ESP_LOGVV(TAG, "i2c bus not initialized!"); | ||||
|     return ERROR_NOT_INITIALIZED; | ||||
|   } | ||||
|  | ||||
| #ifdef ESPHOME_LOG_HAS_VERY_VERBOSE | ||||
|   char debug_buf[4]; | ||||
|   std::string debug_hex; | ||||
|  | ||||
|   for (size_t i = 0; i < cnt; i++) { | ||||
|     const auto &buf = buffers[i]; | ||||
|     for (size_t j = 0; j < buf.len; j++) { | ||||
|       snprintf(debug_buf, sizeof(debug_buf), "%02X", buf.data[j]); | ||||
|       debug_hex += debug_buf; | ||||
|     } | ||||
|   } | ||||
|   ESP_LOGVV(TAG, "0x%02X TX %s", address, debug_hex.c_str()); | ||||
| #endif | ||||
|  | ||||
| #if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 4, 2) | ||||
|   i2c_operation_job_t jobs[cnt + 3]; | ||||
|   uint8_t write = (address << 1) | I2C_MASTER_WRITE; | ||||
|   size_t num = 0; | ||||
|  | ||||
|   jobs[num].command = I2C_MASTER_CMD_START; | ||||
|   num++; | ||||
|  | ||||
|   jobs[num].command = I2C_MASTER_CMD_WRITE; | ||||
|   jobs[num].write.ack_check = true; | ||||
|   jobs[num].write.data = &write; | ||||
|   jobs[num].write.total_bytes = 1; | ||||
|   num++; | ||||
|  | ||||
|   for (size_t i = 0; i < cnt; i++) { | ||||
|     const auto &buf = buffers[i]; | ||||
|     if (buf.len == 0) { | ||||
|       continue; | ||||
|     } | ||||
|     jobs[num].command = I2C_MASTER_CMD_WRITE; | ||||
|     jobs[num].write.ack_check = true; | ||||
|     jobs[num].write.data = (uint8_t *) buf.data; | ||||
|     jobs[num].write.total_bytes = buf.len; | ||||
|     num++; | ||||
|   } | ||||
|  | ||||
|   if (stop) { | ||||
|     jobs[num].command = I2C_MASTER_CMD_STOP; | ||||
|     num++; | ||||
|   } | ||||
|  | ||||
|   esp_err_t err = i2c_master_execute_defined_operations(this->dev_, jobs, num, 20); | ||||
|   if (err == ESP_ERR_INVALID_STATE) { | ||||
|     ESP_LOGVV(TAG, "TX to %02X failed: not acked", address); | ||||
|     return ERROR_NOT_ACKNOWLEDGED; | ||||
|   } else if (err == ESP_ERR_TIMEOUT) { | ||||
|     ESP_LOGVV(TAG, "TX to %02X failed: timeout", address); | ||||
|     return ERROR_TIMEOUT; | ||||
|   } else if (err != ESP_OK) { | ||||
|     ESP_LOGVV(TAG, "TX to %02X failed: %s", address, esp_err_to_name(err)); | ||||
|     return ERROR_UNKNOWN; | ||||
|   } | ||||
| #else | ||||
|   i2c_cmd_handle_t cmd = i2c_cmd_link_create(); | ||||
|   esp_err_t err = i2c_master_start(cmd); | ||||
|   if (err != ESP_OK) { | ||||
|     ESP_LOGVV(TAG, "TX to %02X master start failed: %s", address, esp_err_to_name(err)); | ||||
|     i2c_cmd_link_delete(cmd); | ||||
|     return ERROR_UNKNOWN; | ||||
|   } | ||||
|   err = i2c_master_write_byte(cmd, (address << 1) | I2C_MASTER_WRITE, true); | ||||
|   if (err != ESP_OK) { | ||||
|     ESP_LOGVV(TAG, "TX to %02X address write failed: %s", address, esp_err_to_name(err)); | ||||
|     i2c_cmd_link_delete(cmd); | ||||
|     return ERROR_UNKNOWN; | ||||
|   } | ||||
|   for (size_t i = 0; i < cnt; i++) { | ||||
|     const auto &buf = buffers[i]; | ||||
|     if (buf.len == 0) | ||||
|       continue; | ||||
|     err = i2c_master_write(cmd, buf.data, buf.len, true); | ||||
|     if (err != ESP_OK) { | ||||
|       ESP_LOGVV(TAG, "TX to %02X data write failed: %s", address, esp_err_to_name(err)); | ||||
|       i2c_cmd_link_delete(cmd); | ||||
|       return ERROR_UNKNOWN; | ||||
|     } | ||||
|   } | ||||
|   if (stop) { | ||||
|     err = i2c_master_stop(cmd); | ||||
|     if (err != ESP_OK) { | ||||
|       ESP_LOGVV(TAG, "TX to %02X master stop failed: %s", address, esp_err_to_name(err)); | ||||
|       i2c_cmd_link_delete(cmd); | ||||
|       return ERROR_UNKNOWN; | ||||
|     } | ||||
|   } | ||||
|   err = i2c_master_cmd_begin(port_, cmd, 20 / portTICK_PERIOD_MS); | ||||
|   i2c_cmd_link_delete(cmd); | ||||
|   if (err == ESP_FAIL) { | ||||
|     // transfer not acked | ||||
|     ESP_LOGVV(TAG, "TX to %02X failed: not acked", address); | ||||
|     return ERROR_NOT_ACKNOWLEDGED; | ||||
|   } else if (err == ESP_ERR_TIMEOUT) { | ||||
|     ESP_LOGVV(TAG, "TX to %02X failed: timeout", address); | ||||
|     return ERROR_TIMEOUT; | ||||
|   } else if (err != ESP_OK) { | ||||
|     ESP_LOGVV(TAG, "TX to %02X failed: %s", address, esp_err_to_name(err)); | ||||
|     return ERROR_UNKNOWN; | ||||
|   } | ||||
| #endif | ||||
|   return ERROR_OK; | ||||
| } | ||||
|  | ||||
| @@ -436,8 +190,8 @@ ErrorCode IDFI2CBus::writev(uint8_t address, WriteBuffer *buffers, size_t cnt, b | ||||
| void IDFI2CBus::recover_() { | ||||
|   ESP_LOGI(TAG, "Performing bus recovery"); | ||||
|  | ||||
|   const gpio_num_t scl_pin = static_cast<gpio_num_t>(scl_pin_); | ||||
|   const gpio_num_t sda_pin = static_cast<gpio_num_t>(sda_pin_); | ||||
|   const auto scl_pin = static_cast<gpio_num_t>(scl_pin_); | ||||
|   const auto sda_pin = static_cast<gpio_num_t>(sda_pin_); | ||||
|  | ||||
|   // For the upcoming operations, target for a 60kHz toggle frequency. | ||||
|   // 1000kHz is the maximum frequency for I2C running in standard-mode, | ||||
| @@ -545,5 +299,4 @@ void IDFI2CBus::recover_() { | ||||
|  | ||||
| }  // namespace i2c | ||||
| }  // namespace esphome | ||||
|  | ||||
| #endif  // USE_ESP_IDF | ||||
|   | ||||
| @@ -2,14 +2,9 @@ | ||||
|  | ||||
| #ifdef USE_ESP_IDF | ||||
|  | ||||
| #include "esp_idf_version.h" | ||||
| #include "esphome/core/component.h" | ||||
| #include "i2c_bus.h" | ||||
| #if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 4, 2) | ||||
| #include <driver/i2c_master.h> | ||||
| #else | ||||
| #include <driver/i2c.h> | ||||
| #endif | ||||
|  | ||||
| namespace esphome { | ||||
| namespace i2c { | ||||
| @@ -24,36 +19,33 @@ class IDFI2CBus : public InternalI2CBus, public Component { | ||||
|  public: | ||||
|   void setup() override; | ||||
|   void dump_config() override; | ||||
|   ErrorCode readv(uint8_t address, ReadBuffer *buffers, size_t cnt) override; | ||||
|   ErrorCode writev(uint8_t address, WriteBuffer *buffers, size_t cnt, bool stop) override; | ||||
|   ErrorCode write_readv(uint8_t address, const uint8_t *write_buffer, size_t write_count, uint8_t *read_buffer, | ||||
|                         size_t read_count) override; | ||||
|   float get_setup_priority() const override { return setup_priority::BUS; } | ||||
|  | ||||
|   void set_scan(bool scan) { scan_ = scan; } | ||||
|   void set_sda_pin(uint8_t sda_pin) { sda_pin_ = sda_pin; } | ||||
|   void set_sda_pullup_enabled(bool sda_pullup_enabled) { sda_pullup_enabled_ = sda_pullup_enabled; } | ||||
|   void set_scl_pin(uint8_t scl_pin) { scl_pin_ = scl_pin; } | ||||
|   void set_scl_pullup_enabled(bool scl_pullup_enabled) { scl_pullup_enabled_ = scl_pullup_enabled; } | ||||
|   void set_frequency(uint32_t frequency) { frequency_ = frequency; } | ||||
|   void set_timeout(uint32_t timeout) { timeout_ = timeout; } | ||||
|   void set_scan(bool scan) { this->scan_ = scan; } | ||||
|   void set_sda_pin(uint8_t sda_pin) { this->sda_pin_ = sda_pin; } | ||||
|   void set_sda_pullup_enabled(bool sda_pullup_enabled) { this->sda_pullup_enabled_ = sda_pullup_enabled; } | ||||
|   void set_scl_pin(uint8_t scl_pin) { this->scl_pin_ = scl_pin; } | ||||
|   void set_scl_pullup_enabled(bool scl_pullup_enabled) { this->scl_pullup_enabled_ = scl_pullup_enabled; } | ||||
|   void set_frequency(uint32_t frequency) { this->frequency_ = frequency; } | ||||
|   void set_timeout(uint32_t timeout) { this->timeout_ = timeout; } | ||||
|  | ||||
|   int get_port() const override { return static_cast<int>(this->port_); } | ||||
|   int get_port() const override { return this->port_; } | ||||
|  | ||||
|  private: | ||||
|   void recover_(); | ||||
|   RecoveryCode recovery_result_; | ||||
|   RecoveryCode recovery_result_{}; | ||||
|  | ||||
|  protected: | ||||
| #if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 4, 2) | ||||
|   i2c_master_dev_handle_t dev_; | ||||
|   i2c_master_bus_handle_t bus_; | ||||
|   void i2c_scan() override; | ||||
| #endif | ||||
|   i2c_port_t port_; | ||||
|   uint8_t sda_pin_; | ||||
|   bool sda_pullup_enabled_; | ||||
|   uint8_t scl_pin_; | ||||
|   bool scl_pullup_enabled_; | ||||
|   uint32_t frequency_; | ||||
|   i2c_master_dev_handle_t dev_{}; | ||||
|   i2c_master_bus_handle_t bus_{}; | ||||
|   i2c_port_t port_{}; | ||||
|   uint8_t sda_pin_{}; | ||||
|   bool sda_pullup_enabled_{}; | ||||
|   uint8_t scl_pin_{}; | ||||
|   bool scl_pullup_enabled_{}; | ||||
|   uint32_t frequency_{}; | ||||
|   uint32_t timeout_ = 0; | ||||
|   bool initialized_ = false; | ||||
| }; | ||||
|   | ||||
| @@ -35,7 +35,7 @@ void IAQCore::setup() { | ||||
| void IAQCore::update() { | ||||
|   uint8_t buffer[sizeof(SensorData)]; | ||||
|  | ||||
|   if (this->read_register(0xB5, buffer, sizeof(buffer), false) != i2c::ERROR_OK) { | ||||
|   if (this->read_register(0xB5, buffer, sizeof(buffer)) != i2c::ERROR_OK) { | ||||
|     ESP_LOGD(TAG, "Read failed"); | ||||
|     this->status_set_warning(); | ||||
|     this->publish_nans_(); | ||||
|   | ||||
| @@ -21,7 +21,7 @@ void INA2XXI2C::dump_config() { | ||||
| } | ||||
|  | ||||
| bool INA2XXI2C::read_ina_register(uint8_t reg, uint8_t *data, size_t len) { | ||||
|   auto ret = this->read_register(reg, data, len, false); | ||||
|   auto ret = this->read_register(reg, data, len); | ||||
|   if (ret != i2c::ERROR_OK) { | ||||
|     ESP_LOGE(TAG, "read_ina_register_ failed. Reg=0x%02X Err=%d", reg, ret); | ||||
|   } | ||||
|   | ||||
| @@ -22,7 +22,7 @@ void KMeterISOComponent::setup() { | ||||
|     this->reset_to_construction_state(); | ||||
|   } | ||||
|  | ||||
|   auto err = this->bus_->writev(this->address_, nullptr, 0); | ||||
|   auto err = this->bus_->write_readv(this->address_, nullptr, 0, nullptr, 0); | ||||
|   if (err == esphome::i2c::ERROR_OK) { | ||||
|     ESP_LOGCONFIG(TAG, "Could write to the address %d.", this->address_); | ||||
|   } else { | ||||
| @@ -33,7 +33,7 @@ void KMeterISOComponent::setup() { | ||||
|   } | ||||
|  | ||||
|   uint8_t read_buf[4] = {1}; | ||||
|   if (!this->read_bytes(KMETER_ERROR_STATUS_REG, read_buf, 1)) { | ||||
|   if (!this->read_register(KMETER_ERROR_STATUS_REG, read_buf, 1)) { | ||||
|     ESP_LOGCONFIG(TAG, "Could not read from the device."); | ||||
|     this->error_code_ = COMMUNICATION_FAILED; | ||||
|     this->mark_failed(); | ||||
|   | ||||
| @@ -185,7 +185,7 @@ uint8_t Lc709203f::get_register_(uint8_t register_to_read, uint16_t *register_va | ||||
|     //  function will send a stop between the read and the write portion of the I2C | ||||
|     //  transaction. This is bad in this case and will result in reading nothing but 0xFFFF | ||||
|     //  from the registers. | ||||
|     return_code = this->read_register(register_to_read, &read_buffer[3], 3, false); | ||||
|     return_code = this->read_register(register_to_read, &read_buffer[3], 3); | ||||
|     if (return_code != i2c::NO_ERROR) { | ||||
|       // Error on the i2c bus | ||||
|       this->status_set_warning( | ||||
| @@ -226,7 +226,7 @@ uint8_t Lc709203f::set_register_(uint8_t register_to_set, uint16_t value_to_set) | ||||
|   for (uint8_t i = 0; i <= LC709203F_I2C_RETRY_COUNT; i++) { | ||||
|     // Note: we don't write the first byte of the write buffer to the device. | ||||
|     //  This is done automatically by the write() function. | ||||
|     return_code = this->write(&write_buffer[1], 4, true); | ||||
|     return_code = this->write(&write_buffer[1], 4); | ||||
|     if (return_code == i2c::NO_ERROR) { | ||||
|       return return_code; | ||||
|     } else { | ||||
|   | ||||
| @@ -328,7 +328,7 @@ bool Mcp4461Component::increase_wiper_(Mcp4461WiperIdx wiper) { | ||||
|   ESP_LOGV(TAG, "Increasing wiper %u", wiper_idx); | ||||
|   uint8_t addr = this->get_wiper_address_(wiper_idx); | ||||
|   uint8_t reg = addr | static_cast<uint8_t>(Mcp4461Commands::INCREMENT); | ||||
|   auto err = this->write(&this->address_, reg, sizeof(reg)); | ||||
|   auto err = this->write(&this->address_, reg); | ||||
|   if (err != i2c::ERROR_OK) { | ||||
|     this->error_code_ = MCP4461_STATUS_I2C_ERROR; | ||||
|     this->status_set_warning(); | ||||
| @@ -359,7 +359,7 @@ bool Mcp4461Component::decrease_wiper_(Mcp4461WiperIdx wiper) { | ||||
|   ESP_LOGV(TAG, "Decreasing wiper %u", wiper_idx); | ||||
|   uint8_t addr = this->get_wiper_address_(wiper_idx); | ||||
|   uint8_t reg = addr | static_cast<uint8_t>(Mcp4461Commands::DECREMENT); | ||||
|   auto err = this->write(&this->address_, reg, sizeof(reg)); | ||||
|   auto err = this->write(&this->address_, reg); | ||||
|   if (err != i2c::ERROR_OK) { | ||||
|     this->error_code_ = MCP4461_STATUS_I2C_ERROR; | ||||
|     this->status_set_warning(); | ||||
|   | ||||
| @@ -75,18 +75,18 @@ float MLX90614Component::get_setup_priority() const { return setup_priority::DAT | ||||
|  | ||||
| void MLX90614Component::update() { | ||||
|   uint8_t emissivity[3]; | ||||
|   if (this->read_register(MLX90614_EMISSIVITY, emissivity, 3, false) != i2c::ERROR_OK) { | ||||
|   if (this->read_register(MLX90614_EMISSIVITY, emissivity, 3) != i2c::ERROR_OK) { | ||||
|     this->status_set_warning(); | ||||
|     return; | ||||
|   } | ||||
|   uint8_t raw_object[3]; | ||||
|   if (this->read_register(MLX90614_TEMPERATURE_OBJECT_1, raw_object, 3, false) != i2c::ERROR_OK) { | ||||
|   if (this->read_register(MLX90614_TEMPERATURE_OBJECT_1, raw_object, 3) != i2c::ERROR_OK) { | ||||
|     this->status_set_warning(); | ||||
|     return; | ||||
|   } | ||||
|  | ||||
|   uint8_t raw_ambient[3]; | ||||
|   if (this->read_register(MLX90614_TEMPERATURE_AMBIENT, raw_ambient, 3, false) != i2c::ERROR_OK) { | ||||
|   if (this->read_register(MLX90614_TEMPERATURE_AMBIENT, raw_ambient, 3) != i2c::ERROR_OK) { | ||||
|     this->status_set_warning(); | ||||
|     return; | ||||
|   } | ||||
|   | ||||
| @@ -10,7 +10,7 @@ static const char *const TAG = "mpl3115a2"; | ||||
|  | ||||
| void MPL3115A2Component::setup() { | ||||
|   uint8_t whoami = 0xFF; | ||||
|   if (!this->read_byte(MPL3115A2_WHOAMI, &whoami, false)) { | ||||
|   if (!this->read_byte(MPL3115A2_WHOAMI, &whoami)) { | ||||
|     this->error_code_ = COMMUNICATION_FAILED; | ||||
|     this->mark_failed(); | ||||
|     return; | ||||
| @@ -54,24 +54,24 @@ void MPL3115A2Component::dump_config() { | ||||
|  | ||||
| void MPL3115A2Component::update() { | ||||
|   uint8_t mode = MPL3115A2_CTRL_REG1_OS128; | ||||
|   this->write_byte(MPL3115A2_CTRL_REG1, mode, true); | ||||
|   this->write_byte(MPL3115A2_CTRL_REG1, mode); | ||||
|   // Trigger a new reading | ||||
|   mode |= MPL3115A2_CTRL_REG1_OST; | ||||
|   if (this->altitude_ != nullptr) | ||||
|     mode |= MPL3115A2_CTRL_REG1_ALT; | ||||
|   this->write_byte(MPL3115A2_CTRL_REG1, mode, true); | ||||
|   this->write_byte(MPL3115A2_CTRL_REG1, mode); | ||||
|  | ||||
|   // Wait until status shows reading available | ||||
|   uint8_t status = 0; | ||||
|   if (!this->read_byte(MPL3115A2_REGISTER_STATUS, &status, false) || (status & MPL3115A2_REGISTER_STATUS_PDR) == 0) { | ||||
|   if (!this->read_byte(MPL3115A2_REGISTER_STATUS, &status) || (status & MPL3115A2_REGISTER_STATUS_PDR) == 0) { | ||||
|     delay(10); | ||||
|     if (!this->read_byte(MPL3115A2_REGISTER_STATUS, &status, false) || (status & MPL3115A2_REGISTER_STATUS_PDR) == 0) { | ||||
|     if (!this->read_byte(MPL3115A2_REGISTER_STATUS, &status) || (status & MPL3115A2_REGISTER_STATUS_PDR) == 0) { | ||||
|       return; | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   uint8_t buffer[5] = {0, 0, 0, 0, 0}; | ||||
|   this->read_register(MPL3115A2_REGISTER_PRESSURE_MSB, buffer, 5, false); | ||||
|   this->read_register(MPL3115A2_REGISTER_PRESSURE_MSB, buffer, 5); | ||||
|  | ||||
|   float altitude = 0, pressure = 0; | ||||
|   if (this->altitude_ != nullptr) { | ||||
|   | ||||
| @@ -33,7 +33,7 @@ float NPI19Component::get_setup_priority() const { return setup_priority::DATA; | ||||
|  | ||||
| i2c::ErrorCode NPI19Component::read_(uint16_t &raw_temperature, uint16_t &raw_pressure) { | ||||
|   // initiate data read from device | ||||
|   i2c::ErrorCode w_err = write(&READ_COMMAND, sizeof(READ_COMMAND), true); | ||||
|   i2c::ErrorCode w_err = write(&READ_COMMAND, sizeof(READ_COMMAND)); | ||||
|   if (w_err != i2c::ERROR_OK) { | ||||
|     return w_err; | ||||
|   } | ||||
|   | ||||
| @@ -72,7 +72,7 @@ void OPT3001Sensor::read_lx_(const std::function<void(float)> &f) { | ||||
|   } | ||||
|  | ||||
|   this->set_timeout("read", OPT3001_CONVERSION_TIME_800, [this, f]() { | ||||
|     if (this->write(&OPT3001_REG_CONFIGURATION, 1, true) != i2c::ERROR_OK) { | ||||
|     if (this->write(&OPT3001_REG_CONFIGURATION, 1) != i2c::ERROR_OK) { | ||||
|       ESP_LOGW(TAG, "Starting configuration register read failed"); | ||||
|       f(NAN); | ||||
|       return; | ||||
|   | ||||
| @@ -33,7 +33,7 @@ void PCA6416AComponent::setup() { | ||||
|   } | ||||
|  | ||||
|   // Test to see if the device supports pull-up resistors | ||||
|   if (this->read_register(PCAL6416A_PULL_EN0, &value, 1, true) == i2c::ERROR_OK) { | ||||
|   if (this->read_register(PCAL6416A_PULL_EN0, &value, 1) == i2c::ERROR_OK) { | ||||
|     this->has_pullup_ = true; | ||||
|   } | ||||
|  | ||||
| @@ -105,7 +105,7 @@ bool PCA6416AComponent::read_register_(uint8_t reg, uint8_t *value) { | ||||
|     return false; | ||||
|   } | ||||
|  | ||||
|   this->last_error_ = this->read_register(reg, value, 1, true); | ||||
|   this->last_error_ = this->read_register(reg, value, 1); | ||||
|   if (this->last_error_ != i2c::ERROR_OK) { | ||||
|     this->status_set_warning(); | ||||
|     ESP_LOGE(TAG, "read_register_(): I2C I/O error: %d", (int) this->last_error_); | ||||
| @@ -122,7 +122,7 @@ bool PCA6416AComponent::write_register_(uint8_t reg, uint8_t value) { | ||||
|     return false; | ||||
|   } | ||||
|  | ||||
|   this->last_error_ = this->write_register(reg, &value, 1, true); | ||||
|   this->last_error_ = this->write_register(reg, &value, 1); | ||||
|   if (this->last_error_ != i2c::ERROR_OK) { | ||||
|     this->status_set_warning(); | ||||
|     ESP_LOGE(TAG, "write_register_(): I2C I/O error: %d", (int) this->last_error_); | ||||
|   | ||||
| @@ -96,7 +96,7 @@ bool PCA9554Component::read_inputs_() { | ||||
|     return false; | ||||
|   } | ||||
|  | ||||
|   this->last_error_ = this->read_register(INPUT_REG * this->reg_width_, inputs, this->reg_width_, true); | ||||
|   this->last_error_ = this->read_register(INPUT_REG * this->reg_width_, inputs, this->reg_width_); | ||||
|   if (this->last_error_ != i2c::ERROR_OK) { | ||||
|     this->status_set_warning(); | ||||
|     ESP_LOGE(TAG, "read_register_(): I2C I/O error: %d", (int) this->last_error_); | ||||
| @@ -114,7 +114,7 @@ bool PCA9554Component::write_register_(uint8_t reg, uint16_t value) { | ||||
|   uint8_t outputs[2]; | ||||
|   outputs[0] = (uint8_t) value; | ||||
|   outputs[1] = (uint8_t) (value >> 8); | ||||
|   this->last_error_ = this->write_register(reg * this->reg_width_, outputs, this->reg_width_, true); | ||||
|   this->last_error_ = this->write_register(reg * this->reg_width_, outputs, this->reg_width_); | ||||
|   if (this->last_error_ != i2c::ERROR_OK) { | ||||
|     this->status_set_warning(); | ||||
|     ESP_LOGE(TAG, "write_register_(): I2C I/O error: %d", (int) this->last_error_); | ||||
|   | ||||
| @@ -51,8 +51,7 @@ void HOT I2CST7567::write_display_data() { | ||||
|     static const size_t BLOCK_SIZE = 64; | ||||
|     for (uint8_t x = 0; x < (uint8_t) this->get_width_internal(); x += BLOCK_SIZE) { | ||||
|       this->write_register(esphome::st7567_base::ST7567_SET_START_LINE, &buffer_[y * this->get_width_internal() + x], | ||||
|                            this->get_width_internal() - x > BLOCK_SIZE ? BLOCK_SIZE : this->get_width_internal() - x, | ||||
|                            true); | ||||
|                            this->get_width_internal() - x > BLOCK_SIZE ? BLOCK_SIZE : this->get_width_internal() - x); | ||||
|     } | ||||
|   } | ||||
| } | ||||
|   | ||||
| @@ -6,23 +6,15 @@ namespace tca9548a { | ||||
|  | ||||
| static const char *const TAG = "tca9548a"; | ||||
|  | ||||
| i2c::ErrorCode TCA9548AChannel::readv(uint8_t address, i2c::ReadBuffer *buffers, size_t cnt) { | ||||
| i2c::ErrorCode TCA9548AChannel::write_readv(uint8_t address, const uint8_t *write_buffer, size_t write_count, | ||||
|                                             uint8_t *read_buffer, size_t read_count) { | ||||
|   auto err = this->parent_->switch_to_channel(channel_); | ||||
|   if (err != i2c::ERROR_OK) | ||||
|     return err; | ||||
|   err = this->parent_->bus_->readv(address, buffers, cnt); | ||||
|   err = this->parent_->bus_->write_readv(address, write_buffer, write_count, read_buffer, read_count); | ||||
|   this->parent_->disable_all_channels(); | ||||
|   return err; | ||||
| } | ||||
| i2c::ErrorCode TCA9548AChannel::writev(uint8_t address, i2c::WriteBuffer *buffers, size_t cnt, bool stop) { | ||||
|   auto err = this->parent_->switch_to_channel(channel_); | ||||
|   if (err != i2c::ERROR_OK) | ||||
|     return err; | ||||
|   err = this->parent_->bus_->writev(address, buffers, cnt, stop); | ||||
|   this->parent_->disable_all_channels(); | ||||
|   return err; | ||||
| } | ||||
|  | ||||
| void TCA9548AComponent::setup() { | ||||
|   uint8_t status = 0; | ||||
|   if (this->read(&status, 1) != i2c::ERROR_OK) { | ||||
|   | ||||
| @@ -14,8 +14,8 @@ class TCA9548AChannel : public i2c::I2CBus { | ||||
|   void set_channel(uint8_t channel) { channel_ = channel; } | ||||
|   void set_parent(TCA9548AComponent *parent) { parent_ = parent; } | ||||
|  | ||||
|   i2c::ErrorCode readv(uint8_t address, i2c::ReadBuffer *buffers, size_t cnt) override; | ||||
|   i2c::ErrorCode writev(uint8_t address, i2c::WriteBuffer *buffers, size_t cnt, bool stop) override; | ||||
|   i2c::ErrorCode write_readv(uint8_t address, const uint8_t *write_buffer, size_t write_count, uint8_t *read_buffer, | ||||
|                              size_t read_count) override; | ||||
|  | ||||
|  protected: | ||||
|   uint8_t channel_; | ||||
|   | ||||
| @@ -9,9 +9,9 @@ static const char *const TAG = "tee501"; | ||||
|  | ||||
| void TEE501Component::setup() { | ||||
|   uint8_t address[] = {0x70, 0x29}; | ||||
|   this->write(address, 2, false); | ||||
|   uint8_t identification[9]; | ||||
|   this->read(identification, 9); | ||||
|   this->write_read(address, sizeof address, identification, sizeof identification); | ||||
|   if (identification[8] != crc8(identification, 8, 0xFF, 0x31, true)) { | ||||
|     this->error_code_ = CRC_CHECK_FAILED; | ||||
|     this->mark_failed(); | ||||
| @@ -41,7 +41,7 @@ void TEE501Component::dump_config() { | ||||
| float TEE501Component::get_setup_priority() const { return setup_priority::DATA; } | ||||
| void TEE501Component::update() { | ||||
|   uint8_t address_1[] = {0x2C, 0x1B}; | ||||
|   this->write(address_1, 2, true); | ||||
|   this->write(address_1, 2); | ||||
|   this->set_timeout(50, [this]() { | ||||
|     uint8_t i2c_response[3]; | ||||
|     this->read(i2c_response, 3); | ||||
|   | ||||
| @@ -74,7 +74,8 @@ void TLC59208FOutput::setup() { | ||||
|   ESP_LOGV(TAG, "  Resetting all devices on the bus"); | ||||
|  | ||||
|   // Reset all devices on the bus | ||||
|   if (this->bus_->write(TLC59208F_SWRST_ADDR >> 1, TLC59208F_SWRST_SEQ, 2) != i2c::ERROR_OK) { | ||||
|   if (this->bus_->write_readv(TLC59208F_SWRST_ADDR >> 1, TLC59208F_SWRST_SEQ, sizeof TLC59208F_SWRST_SEQ, nullptr, 0) != | ||||
|       i2c::ERROR_OK) { | ||||
|     ESP_LOGE(TAG, "RESET failed"); | ||||
|     this->mark_failed(); | ||||
|     return; | ||||
|   | ||||
| @@ -14,14 +14,12 @@ void VEML3235Sensor::setup() { | ||||
|     this->mark_failed(); | ||||
|     return; | ||||
|   } | ||||
|   if ((this->write(&ID_REG, 1, false) != i2c::ERROR_OK) || !this->read_bytes_raw(device_id, 2)) { | ||||
|   if ((this->read_register(ID_REG, device_id, sizeof device_id) != i2c::ERROR_OK)) { | ||||
|     ESP_LOGE(TAG, "Unable to read ID"); | ||||
|     this->mark_failed(); | ||||
|     return; | ||||
|   } else if (device_id[0] != DEVICE_ID) { | ||||
|     ESP_LOGE(TAG, "Incorrect device ID - expected 0x%.2x, read 0x%.2x", DEVICE_ID, device_id[0]); | ||||
|     this->mark_failed(); | ||||
|     return; | ||||
|   } | ||||
| } | ||||
|  | ||||
| @@ -49,7 +47,7 @@ float VEML3235Sensor::read_lx_() { | ||||
|   } | ||||
|  | ||||
|   uint8_t als_regs[] = {0, 0}; | ||||
|   if ((this->write(&ALS_REG, 1, false) != i2c::ERROR_OK) || !this->read_bytes_raw(als_regs, 2)) { | ||||
|   if ((this->read_register(ALS_REG, als_regs, sizeof als_regs) != i2c::ERROR_OK)) { | ||||
|     this->status_set_warning(); | ||||
|     return NAN; | ||||
|   } | ||||
|   | ||||
| @@ -279,20 +279,18 @@ ErrorCode VEML7700Component::reconfigure_time_and_gain_(IntegrationTime time, Ga | ||||
| } | ||||
|  | ||||
| ErrorCode VEML7700Component::read_sensor_output_(Readings &data) { | ||||
|   auto als_err = | ||||
|       this->read_register((uint8_t) CommandRegisters::ALS, (uint8_t *) &data.als_counts, VEML_REG_SIZE, false); | ||||
|   auto als_err = this->read_register((uint8_t) CommandRegisters::ALS, (uint8_t *) &data.als_counts, VEML_REG_SIZE); | ||||
|   if (als_err != i2c::ERROR_OK) { | ||||
|     ESP_LOGW(TAG, "Error reading ALS register, err = %d", als_err); | ||||
|   } | ||||
|   auto white_err = | ||||
|       this->read_register((uint8_t) CommandRegisters::WHITE, (uint8_t *) &data.white_counts, VEML_REG_SIZE, false); | ||||
|       this->read_register((uint8_t) CommandRegisters::WHITE, (uint8_t *) &data.white_counts, VEML_REG_SIZE); | ||||
|   if (white_err != i2c::ERROR_OK) { | ||||
|     ESP_LOGW(TAG, "Error reading WHITE register, err = %d", white_err); | ||||
|   } | ||||
|  | ||||
|   ConfigurationRegister conf{0}; | ||||
|   auto err = | ||||
|       this->read_register((uint8_t) CommandRegisters::ALS_CONF_0, (uint8_t *) conf.raw_bytes, VEML_REG_SIZE, false); | ||||
|   auto err = this->read_register((uint8_t) CommandRegisters::ALS_CONF_0, (uint8_t *) conf.raw_bytes, VEML_REG_SIZE); | ||||
|   if (err != i2c::ERROR_OK) { | ||||
|     ESP_LOGW(TAG, "Error reading ALS_CONF_0 register, err = %d", white_err); | ||||
|   } | ||||
|   | ||||
| @@ -3,7 +3,6 @@ | ||||
| #include "esphome/components/i2c/i2c.h" | ||||
| #include "esphome/components/sensor/sensor.h" | ||||
| #include "esphome/core/component.h" | ||||
| #include "esphome/core/optional.h" | ||||
|  | ||||
| namespace esphome { | ||||
| namespace veml7700 { | ||||
|   | ||||
		Reference in New Issue
	
	Block a user