mirror of
				https://github.com/esphome/esphome.git
				synced 2025-10-30 22:53:59 +00:00 
			
		
		
		
	
							
								
								
									
										2
									
								
								Doxyfile
									
									
									
									
									
								
							
							
						
						
									
										2
									
								
								Doxyfile
									
									
									
									
									
								
							| @@ -48,7 +48,7 @@ PROJECT_NAME           = ESPHome | |||||||
| # could be handy for archiving the generated documentation or if some version | # could be handy for archiving the generated documentation or if some version | ||||||
| # control system is used. | # control system is used. | ||||||
|  |  | ||||||
| PROJECT_NUMBER         = 2025.8.1 | PROJECT_NUMBER         = 2025.8.2 | ||||||
|  |  | ||||||
| # Using the PROJECT_BRIEF tag one can provide an optional one line description | # Using the PROJECT_BRIEF tag one can provide an optional one line description | ||||||
| # for a project that appears at the top of each page and should give viewer a | # for a project that appears at the top of each page and should give viewer a | ||||||
|   | |||||||
| @@ -132,14 +132,17 @@ def choose_upload_log_host( | |||||||
|                 ] |                 ] | ||||||
|                 resolved.append(choose_prompt(options, purpose=purpose)) |                 resolved.append(choose_prompt(options, purpose=purpose)) | ||||||
|             elif device == "OTA": |             elif device == "OTA": | ||||||
|                 if (show_ota and "ota" in CORE.config) or ( |                 if CORE.address and ( | ||||||
|                     show_api and "api" in CORE.config |                     (show_ota and "ota" in CORE.config) | ||||||
|  |                     or (show_api and "api" in CORE.config) | ||||||
|                 ): |                 ): | ||||||
|                     resolved.append(CORE.address) |                     resolved.append(CORE.address) | ||||||
|                 elif show_mqtt and has_mqtt_logging(): |                 elif show_mqtt and has_mqtt_logging(): | ||||||
|                     resolved.append("MQTT") |                     resolved.append("MQTT") | ||||||
|             else: |             else: | ||||||
|                 resolved.append(device) |                 resolved.append(device) | ||||||
|  |         if not resolved: | ||||||
|  |             _LOGGER.error("All specified devices: %s could not be resolved.", defaults) | ||||||
|         return resolved |         return resolved | ||||||
|  |  | ||||||
|     # No devices specified, show interactive chooser |     # No devices specified, show interactive chooser | ||||||
|   | |||||||
| @@ -41,7 +41,7 @@ void AXS15231Touchscreen::update_touches() { | |||||||
|   i2c::ErrorCode err; |   i2c::ErrorCode err; | ||||||
|   uint8_t data[8]{}; |   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); |   ERROR_CHECK(err); | ||||||
|   err = this->read(data, sizeof(data)); |   err = this->read(data, sizeof(data)); | ||||||
|   ERROR_CHECK(err); |   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) { | i2c::ErrorCode BMI160Component::read_le_int16_(uint8_t reg, int16_t *value, uint8_t len) { | ||||||
|   uint8_t raw_data[len * 2]; |   uint8_t raw_data[len * 2]; | ||||||
|   // read using read_register because we have little-endian data, and read_bytes_16 will swap it |   // 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) { |   if (err != i2c::ERROR_OK) { | ||||||
|     return err; |     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. |   // 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 |   // 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->error_code_ = COMMUNICATION_FAILED; | ||||||
|     this->mark_failed(ESP_LOG_MSG_COMM_FAIL); |     this->mark_failed(ESP_LOG_MSG_COMM_FAIL); | ||||||
|     return; |     return; | ||||||
|   } |   } | ||||||
|   if (!this->read_byte(0xD0, &chip_id)) { |   if (!this->bmp_read_byte(0xD0, &chip_id)) { | ||||||
|     this->error_code_ = COMMUNICATION_FAILED; |     this->error_code_ = COMMUNICATION_FAILED; | ||||||
|     this->mark_failed(ESP_LOG_MSG_COMM_FAIL); |     this->mark_failed(ESP_LOG_MSG_COMM_FAIL); | ||||||
|     return; |     return; | ||||||
| @@ -80,7 +80,7 @@ void BMP280Component::setup() { | |||||||
|   } |   } | ||||||
|  |  | ||||||
|   // Send a soft reset. |   // 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"); |     this->mark_failed("Reset failed"); | ||||||
|     return; |     return; | ||||||
|   } |   } | ||||||
| @@ -89,7 +89,7 @@ void BMP280Component::setup() { | |||||||
|   uint8_t retry = 5; |   uint8_t retry = 5; | ||||||
|   do { |   do { | ||||||
|     delay(2); |     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"); |       this->mark_failed("Error reading status register"); | ||||||
|       return; |       return; | ||||||
|     } |     } | ||||||
| @@ -115,14 +115,14 @@ void BMP280Component::setup() { | |||||||
|   this->calibration_.p9 = this->read_s16_le_(0x9E); |   this->calibration_.p9 = this->read_s16_le_(0x9E); | ||||||
|  |  | ||||||
|   uint8_t config_register = 0; |   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"); |     this->mark_failed("Read config"); | ||||||
|     return; |     return; | ||||||
|   } |   } | ||||||
|   config_register &= ~0b11111100; |   config_register &= ~0b11111100; | ||||||
|   config_register |= 0b000 << 5;  // 0.5 ms standby time |   config_register |= 0b000 << 5;  // 0.5 ms standby time | ||||||
|   config_register |= (this->iir_filter_ & 0b111) << 2; |   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"); |     this->mark_failed("Write config"); | ||||||
|     return; |     return; | ||||||
|   } |   } | ||||||
| @@ -159,7 +159,7 @@ void BMP280Component::update() { | |||||||
|   meas_value |= (this->temperature_oversampling_ & 0b111) << 5; |   meas_value |= (this->temperature_oversampling_ & 0b111) << 5; | ||||||
|   meas_value |= (this->pressure_oversampling_ & 0b111) << 2; |   meas_value |= (this->pressure_oversampling_ & 0b111) << 2; | ||||||
|   meas_value |= 0b01;  // Forced mode |   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(); |     this->status_set_warning(); | ||||||
|     return; |     return; | ||||||
|   } |   } | ||||||
| @@ -188,9 +188,10 @@ void BMP280Component::update() { | |||||||
| } | } | ||||||
|  |  | ||||||
| float BMP280Component::read_temperature_(int32_t *t_fine) { | float BMP280Component::read_temperature_(int32_t *t_fine) { | ||||||
|   uint8_t data[3]; |   uint8_t data[3]{}; | ||||||
|   if (!this->read_bytes(BMP280_REGISTER_TEMPDATA, data, 3)) |   if (!this->bmp_read_bytes(BMP280_REGISTER_TEMPDATA, data, 3)) | ||||||
|     return NAN; |     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); |   int32_t adc = ((data[0] & 0xFF) << 16) | ((data[1] & 0xFF) << 8) | (data[2] & 0xFF); | ||||||
|   adc >>= 4; |   adc >>= 4; | ||||||
|   if (adc == 0x80000) { |   if (adc == 0x80000) { | ||||||
| @@ -212,7 +213,7 @@ float BMP280Component::read_temperature_(int32_t *t_fine) { | |||||||
|  |  | ||||||
| float BMP280Component::read_pressure_(int32_t t_fine) { | float BMP280Component::read_pressure_(int32_t t_fine) { | ||||||
|   uint8_t data[3]; |   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; |     return NAN; | ||||||
|   int32_t adc = ((data[0] & 0xFF) << 16) | ((data[1] & 0xFF) << 8) | (data[2] & 0xFF); |   int32_t adc = ((data[0] & 0xFF) << 16) | ((data[1] & 0xFF) << 8) | (data[2] & 0xFF); | ||||||
|   adc >>= 4; |   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; } | void BMP280Component::set_iir_filter(BMP280IIRFilter iir_filter) { this->iir_filter_ = iir_filter; } | ||||||
| uint8_t BMP280Component::read_u8_(uint8_t a_register) { | uint8_t BMP280Component::read_u8_(uint8_t a_register) { | ||||||
|   uint8_t data = 0; |   uint8_t data = 0; | ||||||
|   this->read_byte(a_register, &data); |   this->bmp_read_byte(a_register, &data); | ||||||
|   return data; |   return data; | ||||||
| } | } | ||||||
| uint16_t BMP280Component::read_u16_le_(uint8_t a_register) { | uint16_t BMP280Component::read_u16_le_(uint8_t a_register) { | ||||||
|   uint16_t data = 0; |   uint16_t data = 0; | ||||||
|   this->read_byte_16(a_register, &data); |   this->bmp_read_byte_16(a_register, &data); | ||||||
|   return (data >> 8) | (data << 8); |   return (data >> 8) | (data << 8); | ||||||
| } | } | ||||||
| int16_t BMP280Component::read_s16_le_(uint8_t a_register) { return this->read_u16_le_(a_register); } | 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; |   float get_setup_priority() const override; | ||||||
|   void update() 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: |  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. |   /// Read the temperature value and store the calculated ambient temperature in t_fine. | ||||||
|   float read_temperature_(int32_t *t_fine); |   float read_temperature_(int32_t *t_fine); | ||||||
|   /// Read the pressure value in hPa using the provided t_fine value. |   /// Read the pressure value in hPa using the provided t_fine value. | ||||||
|   | |||||||
| @@ -5,19 +5,6 @@ | |||||||
| namespace esphome { | namespace esphome { | ||||||
| namespace bmp280_i2c { | 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() { | void BMP280I2CComponent::dump_config() { | ||||||
|   LOG_I2C_DEVICE(this); |   LOG_I2C_DEVICE(this); | ||||||
|   BMP280Component::dump_config(); |   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. | /// This class implements support for the BMP280 Temperature+Pressure i2c sensor. | ||||||
| class BMP280I2CComponent : public esphome::bmp280_base::BMP280Component, public i2c::I2CDevice { | class BMP280I2CComponent : public esphome::bmp280_base::BMP280Component, public i2c::I2CDevice { | ||||||
|  public: |  public: | ||||||
|   bool read_byte(uint8_t a_register, uint8_t *data) override; |   bool bmp_read_byte(uint8_t a_register, uint8_t *data) override { return read_byte(a_register, data); } | ||||||
|   bool write_byte(uint8_t a_register, uint8_t data) override; |   bool bmp_write_byte(uint8_t a_register, uint8_t data) override { return write_byte(a_register, data); } | ||||||
|   bool read_bytes(uint8_t a_register, uint8_t *data, size_t len) override; |   bool bmp_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; |     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; |   void dump_config() override; | ||||||
| }; | }; | ||||||
|  |  | ||||||
|   | |||||||
| @@ -28,7 +28,7 @@ void BMP280SPIComponent::setup() { | |||||||
| // 0x77 is transferred, for read access, the byte 0xF7 is transferred. | // 0x77 is transferred, for read access, the byte 0xF7 is transferred. | ||||||
| // https://www.bosch-sensortec.com/media/boschsensortec/downloads/datasheets/bst-bmp280-ds001.pdf | // 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->enable(); | ||||||
|   this->transfer_byte(set_bit(a_register, 7)); |   this->transfer_byte(set_bit(a_register, 7)); | ||||||
|   *data = this->transfer_byte(0); |   *data = this->transfer_byte(0); | ||||||
| @@ -36,7 +36,7 @@ bool BMP280SPIComponent::read_byte(uint8_t a_register, uint8_t *data) { | |||||||
|   return true; |   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->enable(); | ||||||
|   this->transfer_byte(clear_bit(a_register, 7)); |   this->transfer_byte(clear_bit(a_register, 7)); | ||||||
|   this->transfer_byte(data); |   this->transfer_byte(data); | ||||||
| @@ -44,7 +44,7 @@ bool BMP280SPIComponent::write_byte(uint8_t a_register, uint8_t data) { | |||||||
|   return true; |   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->enable(); | ||||||
|   this->transfer_byte(set_bit(a_register, 7)); |   this->transfer_byte(set_bit(a_register, 7)); | ||||||
|   this->read_array(data, len); |   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; |   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->enable(); | ||||||
|   this->transfer_byte(set_bit(a_register, 7)); |   this->transfer_byte(set_bit(a_register, 7)); | ||||||
|   ((uint8_t *) data)[1] = this->transfer_byte(0); |   ((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, |                            public spi::SPIDevice<spi::BIT_ORDER_MSB_FIRST, spi::CLOCK_POLARITY_LOW, | ||||||
|                                                  spi::CLOCK_PHASE_LEADING, spi::DATA_RATE_200KHZ> { |                                                  spi::CLOCK_PHASE_LEADING, spi::DATA_RATE_200KHZ> { | ||||||
|   void setup() override; |   void setup() override; | ||||||
|   bool read_byte(uint8_t a_register, uint8_t *data) override; |   bool bmp_read_byte(uint8_t a_register, uint8_t *data) override; | ||||||
|   bool write_byte(uint8_t a_register, uint8_t data) override; |   bool bmp_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 bmp_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_16(uint8_t a_register, uint16_t *data) override; | ||||||
| }; | }; | ||||||
|  |  | ||||||
| }  // namespace bmp280_spi | }  // 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. | // 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) { | 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) { |   if (err != i2c::ERROR_OK) { | ||||||
|     this->status_set_warning(str_sprintf("write failed for register 0x%X, error %d", reg, err).c_str()); |     this->status_set_warning(str_sprintf("write failed for register 0x%X, error %d", reg, err).c_str()); | ||||||
|     return false; |     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 CH422GComponent::read_reg_(uint8_t reg) { | ||||||
|   uint8_t value; |   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) { |   if (err != i2c::ERROR_OK) { | ||||||
|     this->status_set_warning(str_sprintf("read failed for register 0x%X, error %d", reg, err).c_str()); |     this->status_set_warning(str_sprintf("read failed for register 0x%X, error %d", reg, err).c_str()); | ||||||
|     return 0; |     return 0; | ||||||
|   | |||||||
| @@ -83,7 +83,7 @@ void EE895Component::write_command_(uint16_t addr, uint16_t reg_cnt) { | |||||||
|   crc16 = calc_crc16_(address, 6); |   crc16 = calc_crc16_(address, 6); | ||||||
|   address[5] = crc16 & 0xFF; |   address[5] = crc16 & 0xFF; | ||||||
|   address[6] = (crc16 >> 8) & 0xFF; |   address[6] = (crc16 >> 8) & 0xFF; | ||||||
|   this->write(address, 7, true); |   this->write(address, 7); | ||||||
| } | } | ||||||
|  |  | ||||||
| float EE895Component::read_float_() { | float EE895Component::read_float_() { | ||||||
|   | |||||||
| @@ -100,8 +100,8 @@ void ESPHomeOTAComponent::handle_handshake_() { | |||||||
|   /// Handle the initial OTA handshake. |   /// Handle the initial OTA handshake. | ||||||
|   /// |   /// | ||||||
|   /// This method is non-blocking and will return immediately if no data is available. |   /// This method is non-blocking and will return immediately if no data is available. | ||||||
|   /// It waits for the first magic byte (0x6C) before proceeding to handle_data_(). |   /// It reads all 5 magic bytes (0x6C, 0x26, 0xF7, 0x5C, 0x45) non-blocking | ||||||
|   /// A 10-second timeout is enforced from initial connection. |   /// before proceeding to handle_data_(). A 10-second timeout is enforced from initial connection. | ||||||
|  |  | ||||||
|   if (this->client_ == nullptr) { |   if (this->client_ == nullptr) { | ||||||
|     // We already checked server_->ready() in loop(), so we can accept directly |     // We already checked server_->ready() in loop(), so we can accept directly | ||||||
| @@ -126,6 +126,7 @@ void ESPHomeOTAComponent::handle_handshake_() { | |||||||
|     } |     } | ||||||
|     this->log_start_("handshake"); |     this->log_start_("handshake"); | ||||||
|     this->client_connect_time_ = App.get_loop_component_start_time(); |     this->client_connect_time_ = App.get_loop_component_start_time(); | ||||||
|  |     this->magic_buf_pos_ = 0;  // Reset magic buffer position | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   // Check for handshake timeout |   // Check for handshake timeout | ||||||
| @@ -136,9 +137,11 @@ void ESPHomeOTAComponent::handle_handshake_() { | |||||||
|     return; |     return; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   // Try to read first byte of magic bytes |   // Try to read remaining magic bytes | ||||||
|   uint8_t first_byte; |   if (this->magic_buf_pos_ < 5) { | ||||||
|   ssize_t read = this->client_->read(&first_byte, 1); |     // Read as many bytes as available | ||||||
|  |     uint8_t bytes_to_read = 5 - this->magic_buf_pos_; | ||||||
|  |     ssize_t read = this->client_->read(this->magic_buf_ + this->magic_buf_pos_, bytes_to_read); | ||||||
|  |  | ||||||
|     if (read == -1 && (errno == EAGAIN || errno == EWOULDBLOCK)) { |     if (read == -1 && (errno == EAGAIN || errno == EWOULDBLOCK)) { | ||||||
|       return;  // No data yet, try again next loop |       return;  // No data yet, try again next loop | ||||||
| @@ -147,7 +150,7 @@ void ESPHomeOTAComponent::handle_handshake_() { | |||||||
|     if (read <= 0) { |     if (read <= 0) { | ||||||
|       // Error or connection closed |       // Error or connection closed | ||||||
|       if (read == -1) { |       if (read == -1) { | ||||||
|       this->log_socket_error_("reading first byte"); |         this->log_socket_error_("reading magic bytes"); | ||||||
|       } else { |       } else { | ||||||
|         ESP_LOGW(TAG, "Remote closed during handshake"); |         ESP_LOGW(TAG, "Remote closed during handshake"); | ||||||
|       } |       } | ||||||
| @@ -155,15 +158,26 @@ void ESPHomeOTAComponent::handle_handshake_() { | |||||||
|       return; |       return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|   // Got first byte, check if it's the magic byte |     this->magic_buf_pos_ += read; | ||||||
|   if (first_byte != 0x6C) { |   } | ||||||
|     ESP_LOGW(TAG, "Invalid initial byte: 0x%02X", first_byte); |  | ||||||
|  |   // Check if we have all 5 magic bytes | ||||||
|  |   if (this->magic_buf_pos_ == 5) { | ||||||
|  |     // Validate magic bytes | ||||||
|  |     static const uint8_t MAGIC_BYTES[5] = {0x6C, 0x26, 0xF7, 0x5C, 0x45}; | ||||||
|  |     if (memcmp(this->magic_buf_, MAGIC_BYTES, 5) != 0) { | ||||||
|  |       ESP_LOGW(TAG, "Magic bytes mismatch! 0x%02X-0x%02X-0x%02X-0x%02X-0x%02X", this->magic_buf_[0], | ||||||
|  |                this->magic_buf_[1], this->magic_buf_[2], this->magic_buf_[3], this->magic_buf_[4]); | ||||||
|  |       // Send error response (non-blocking, best effort) | ||||||
|  |       uint8_t error = static_cast<uint8_t>(ota::OTA_RESPONSE_ERROR_MAGIC); | ||||||
|  |       this->client_->write(&error, 1); | ||||||
|       this->cleanup_connection_(); |       this->cleanup_connection_(); | ||||||
|       return; |       return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|   // First byte is valid, continue with data handling |     // All 5 magic bytes are valid, continue with data handling | ||||||
|     this->handle_data_(); |     this->handle_data_(); | ||||||
|  |   } | ||||||
| } | } | ||||||
|  |  | ||||||
| void ESPHomeOTAComponent::handle_data_() { | void ESPHomeOTAComponent::handle_data_() { | ||||||
| @@ -186,18 +200,6 @@ void ESPHomeOTAComponent::handle_data_() { | |||||||
|   size_t size_acknowledged = 0; |   size_t size_acknowledged = 0; | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
|   // Read remaining 4 bytes of magic (we already read the first byte 0x6C in handle_handshake_) |  | ||||||
|   if (!this->readall_(buf, 4)) { |  | ||||||
|     this->log_read_error_("magic bytes"); |  | ||||||
|     goto error;  // NOLINT(cppcoreguidelines-avoid-goto) |  | ||||||
|   } |  | ||||||
|   // Check remaining magic bytes: 0x26, 0xF7, 0x5C, 0x45 |  | ||||||
|   if (buf[0] != 0x26 || buf[1] != 0xF7 || buf[2] != 0x5C || buf[3] != 0x45) { |  | ||||||
|     ESP_LOGW(TAG, "Magic bytes mismatch! 0x6C-0x%02X-0x%02X-0x%02X-0x%02X", buf[0], buf[1], buf[2], buf[3]); |  | ||||||
|     error_code = ota::OTA_RESPONSE_ERROR_MAGIC; |  | ||||||
|     goto error;  // NOLINT(cppcoreguidelines-avoid-goto) |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   // Send OK and version - 2 bytes |   // Send OK and version - 2 bytes | ||||||
|   buf[0] = ota::OTA_RESPONSE_OK; |   buf[0] = ota::OTA_RESPONSE_OK; | ||||||
|   buf[1] = USE_OTA_VERSION; |   buf[1] = USE_OTA_VERSION; | ||||||
| @@ -487,6 +489,7 @@ void ESPHomeOTAComponent::cleanup_connection_() { | |||||||
|   this->client_->close(); |   this->client_->close(); | ||||||
|   this->client_ = nullptr; |   this->client_ = nullptr; | ||||||
|   this->client_connect_time_ = 0; |   this->client_connect_time_ = 0; | ||||||
|  |   this->magic_buf_pos_ = 0; | ||||||
| } | } | ||||||
|  |  | ||||||
| void ESPHomeOTAComponent::yield_and_feed_watchdog_() { | void ESPHomeOTAComponent::yield_and_feed_watchdog_() { | ||||||
|   | |||||||
| @@ -41,11 +41,13 @@ class ESPHomeOTAComponent : public ota::OTAComponent { | |||||||
|   std::string password_; |   std::string password_; | ||||||
| #endif  // USE_OTA_PASSWORD | #endif  // USE_OTA_PASSWORD | ||||||
|  |  | ||||||
|   uint16_t port_; |  | ||||||
|   uint32_t client_connect_time_{0}; |  | ||||||
|  |  | ||||||
|   std::unique_ptr<socket::Socket> server_; |   std::unique_ptr<socket::Socket> server_; | ||||||
|   std::unique_ptr<socket::Socket> client_; |   std::unique_ptr<socket::Socket> client_; | ||||||
|  |  | ||||||
|  |   uint32_t client_connect_time_{0}; | ||||||
|  |   uint16_t port_; | ||||||
|  |   uint8_t magic_buf_[5]; | ||||||
|  |   uint8_t magic_buf_pos_{0}; | ||||||
| }; | }; | ||||||
|  |  | ||||||
| }  // namespace esphome | }  // namespace esphome | ||||||
|   | |||||||
| @@ -9,9 +9,8 @@ static const char *const TAG = "hte501"; | |||||||
|  |  | ||||||
| void HTE501Component::setup() { | void HTE501Component::setup() { | ||||||
|   uint8_t address[] = {0x70, 0x29}; |   uint8_t address[] = {0x70, 0x29}; | ||||||
|   this->write(address, 2, false); |  | ||||||
|   uint8_t identification[9]; |   uint8_t identification[9]; | ||||||
|   this->read(identification, 9); |   this->write_read(address, sizeof address, identification, sizeof identification); | ||||||
|   if (identification[8] != calc_crc8_(identification, 0, 7)) { |   if (identification[8] != calc_crc8_(identification, 0, 7)) { | ||||||
|     this->error_code_ = CRC_CHECK_FAILED; |     this->error_code_ = CRC_CHECK_FAILED; | ||||||
|     this->mark_failed(); |     this->mark_failed(); | ||||||
| @@ -42,7 +41,7 @@ void HTE501Component::dump_config() { | |||||||
| float HTE501Component::get_setup_priority() const { return setup_priority::DATA; } | float HTE501Component::get_setup_priority() const { return setup_priority::DATA; } | ||||||
| void HTE501Component::update() { | void HTE501Component::update() { | ||||||
|   uint8_t address_1[] = {0x2C, 0x1B}; |   uint8_t address_1[] = {0x2C, 0x1B}; | ||||||
|   this->write(address_1, 2, true); |   this->write(address_1, 2); | ||||||
|   this->set_timeout(50, [this]() { |   this->set_timeout(50, [this]() { | ||||||
|     uint8_t i2c_response[6]; |     uint8_t i2c_response[6]; | ||||||
|     this->read(i2c_response, 6); |     this->read(i2c_response, 6); | ||||||
|   | |||||||
| @@ -2,7 +2,6 @@ import logging | |||||||
|  |  | ||||||
| from esphome import pins | from esphome import pins | ||||||
| import esphome.codegen as cg | import esphome.codegen as cg | ||||||
| from esphome.components import esp32 |  | ||||||
| from esphome.config_helpers import filter_source_files_from_platform | from esphome.config_helpers import filter_source_files_from_platform | ||||||
| import esphome.config_validation as cv | import esphome.config_validation as cv | ||||||
| from esphome.const import ( | from esphome.const import ( | ||||||
| @@ -14,8 +13,6 @@ from esphome.const import ( | |||||||
|     CONF_SCL, |     CONF_SCL, | ||||||
|     CONF_SDA, |     CONF_SDA, | ||||||
|     CONF_TIMEOUT, |     CONF_TIMEOUT, | ||||||
|     KEY_CORE, |  | ||||||
|     KEY_FRAMEWORK_VERSION, |  | ||||||
|     PLATFORM_ESP32, |     PLATFORM_ESP32, | ||||||
|     PLATFORM_ESP8266, |     PLATFORM_ESP8266, | ||||||
|     PLATFORM_RP2040, |     PLATFORM_RP2040, | ||||||
| @@ -48,28 +45,8 @@ def _bus_declare_type(value): | |||||||
|  |  | ||||||
|  |  | ||||||
| def validate_config(config): | def validate_config(config): | ||||||
|     if ( |     if CORE.using_esp_idf: | ||||||
|         config[CONF_SCAN] |         return cv.require_framework_version(esp_idf=cv.Version(5, 4, 2))(config) | ||||||
|         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 |  | ||||||
|     return config |     return config | ||||||
|  |  | ||||||
|  |  | ||||||
|   | |||||||
| @@ -1,4 +1,6 @@ | |||||||
| #include "i2c.h" | #include "i2c.h" | ||||||
|  |  | ||||||
|  | #include "esphome/core/defines.h" | ||||||
| #include "esphome/core/log.h" | #include "esphome/core/log.h" | ||||||
| #include <memory> | #include <memory> | ||||||
|  |  | ||||||
| @@ -7,38 +9,48 @@ namespace i2c { | |||||||
|  |  | ||||||
| static const char *const TAG = "i2c"; | static const char *const TAG = "i2c"; | ||||||
|  |  | ||||||
| ErrorCode I2CDevice::read_register(uint8_t a_register, uint8_t *data, size_t len, bool stop) { | void I2CBus::i2c_scan_() { | ||||||
|   ErrorCode err = this->write(&a_register, 1, stop); |   // suppress logs from the IDF I2C library during the scan | ||||||
|   if (err != ERROR_OK) | #if defined(USE_ESP32) && defined(USE_LOGGER) | ||||||
|     return err; |   auto previous = esp_log_level_get("*"); | ||||||
|   return bus_->read(address_, data, len); |   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); |   a_register = convert_big_endian(a_register); | ||||||
|   ErrorCode const err = this->write(reinterpret_cast<const uint8_t *>(&a_register), 2, stop); |   return bus_->write_readv(this->address_, reinterpret_cast<const uint8_t *>(&a_register), 2, data, len); | ||||||
|   if (err != ERROR_OK) |  | ||||||
|     return err; |  | ||||||
|   return bus_->read(address_, data, len); |  | ||||||
| } | } | ||||||
|  |  | ||||||
| ErrorCode I2CDevice::write_register(uint8_t a_register, const uint8_t *data, size_t len, bool stop) { | ErrorCode I2CDevice::write_register(uint8_t a_register, const uint8_t *data, size_t len) const { | ||||||
|   WriteBuffer buffers[2]; |   std::vector<uint8_t> v{}; | ||||||
|   buffers[0].data = &a_register; |   v.push_back(a_register); | ||||||
|   buffers[0].len = 1; |   v.insert(v.end(), data, data + len); | ||||||
|   buffers[1].data = data; |   return bus_->write_readv(this->address_, v.data(), v.size(), nullptr, 0); | ||||||
|   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, bool stop) { | ErrorCode I2CDevice::write_register16(uint16_t a_register, const uint8_t *data, size_t len) const { | ||||||
|   a_register = convert_big_endian(a_register); |   std::vector<uint8_t> v(len + 2); | ||||||
|   WriteBuffer buffers[2]; |   v.push_back(a_register >> 8); | ||||||
|   buffers[0].data = reinterpret_cast<const uint8_t *>(&a_register); |   v.push_back(a_register); | ||||||
|   buffers[0].len = 2; |   v.insert(v.end(), data, data + len); | ||||||
|   buffers[1].data = data; |   return bus_->write_readv(this->address_, v.data(), v.size(), nullptr, 0); | ||||||
|   buffers[1].len = len; |  | ||||||
|   return bus_->writev(address_, buffers, 2, stop); |  | ||||||
| } | } | ||||||
|  |  | ||||||
| bool I2CDevice::read_bytes_16(uint8_t a_register, uint16_t *data, uint8_t len) { | 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; |   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 |   // we have to copy in order to be able to change byte order | ||||||
|   std::unique_ptr<uint16_t[]> temp{new uint16_t[len]}; |   std::unique_ptr<uint16_t[]> temp{new uint16_t[len]}; | ||||||
|   for (size_t i = 0; i < len; i++) |   for (size_t i = 0; i < len; i++) | ||||||
|   | |||||||
| @@ -1,10 +1,10 @@ | |||||||
| #pragma once | #pragma once | ||||||
|  |  | ||||||
| #include "i2c_bus.h" |  | ||||||
| #include "esphome/core/helpers.h" |  | ||||||
| #include "esphome/core/optional.h" |  | ||||||
| #include <array> | #include <array> | ||||||
| #include <vector> | #include <vector> | ||||||
|  | #include "esphome/core/helpers.h" | ||||||
|  | #include "esphome/core/optional.h" | ||||||
|  | #include "i2c_bus.h" | ||||||
|  |  | ||||||
| namespace esphome { | namespace esphome { | ||||||
| namespace i2c { | namespace i2c { | ||||||
| @@ -161,51 +161,53 @@ class I2CDevice { | |||||||
|   /// @param data pointer to an array to store the bytes |   /// @param data pointer to an array to store the bytes | ||||||
|   /// @param len length of the buffer = number of bytes to read |   /// @param len length of the buffer = number of bytes to read | ||||||
|   /// @return an i2c::ErrorCode |   /// @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 |   /// @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 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 data pointer to an array to store the bytes | ||||||
|   /// @param len length of the buffer = number of bytes to read |   /// @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 |   /// @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 |   /// @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 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 data pointer to an array of bytes to store the information | ||||||
|   /// @param len length of the buffer = number of bytes to read |   /// @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 |   /// @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 |   /// @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 data pointer to an array that contains the bytes to send | ||||||
|   /// @param len length of the buffer = number of bytes to write |   /// @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 |   /// @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 |   /// @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 a_register the internal address of the register to read from | ||||||
|   /// @param data pointer to an array to store the bytes |   /// @param data pointer to an array to store the bytes | ||||||
|   /// @param len length of the buffer = number of bytes to read |   /// @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 |   /// @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 |   /// @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 a_register the 16 bits internal address of the register to read from | ||||||
|   /// @param data pointer to an array to store the bytes |   /// @param data pointer to an array to store the bytes | ||||||
|   /// @param len length of the buffer = number of bytes to read |   /// @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 |   /// @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 |   /// Compat APIs | ||||||
| @@ -217,7 +219,7 @@ class I2CDevice { | |||||||
|     return read_register(a_register, data, len) == ERROR_OK; |     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) { |   template<size_t N> optional<std::array<uint8_t, N>> read_bytes(uint8_t a_register) { | ||||||
|     std::array<uint8_t, N> res; |     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_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) { |   bool read_byte(uint8_t a_register, uint8_t *data) { return read_register(a_register, data, 1) == ERROR_OK; } | ||||||
|     return read_register(a_register, data, 1, stop) == ERROR_OK; |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   optional<uint8_t> read_byte(uint8_t a_register) { |   optional<uint8_t> read_byte(uint8_t a_register) { | ||||||
|     uint8_t data; |     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 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) { |   bool write_bytes(uint8_t a_register, const uint8_t *data, uint8_t len) const { | ||||||
|     return write_register(a_register, data, len, stop) == ERROR_OK; |     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()); |     return write_bytes(a_register, data.data(), data.size()); | ||||||
|   } |   } | ||||||
|  |  | ||||||
| @@ -261,13 +261,42 @@ class I2CDevice { | |||||||
|     return write_bytes(a_register, data.data(), data.size()); |     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) { |   bool write_byte(uint8_t a_register, uint8_t data) const { return write_bytes(a_register, &data, 1); } | ||||||
|     return write_bytes(a_register, &data, 1, stop); |  | ||||||
|  |   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: |  protected: | ||||||
|   uint8_t address_{0x00};  ///< store the address of the device on the bus |   uint8_t address_{0x00};  ///< store the address of the device on the bus | ||||||
|   | |||||||
| @@ -1,9 +1,12 @@ | |||||||
| #pragma once | #pragma once | ||||||
| #include <cstddef> | #include <cstddef> | ||||||
| #include <cstdint> | #include <cstdint> | ||||||
|  | #include <cstring> | ||||||
| #include <utility> | #include <utility> | ||||||
| #include <vector> | #include <vector> | ||||||
|  |  | ||||||
|  | #include "esphome/core/helpers.h" | ||||||
|  |  | ||||||
| namespace esphome { | namespace esphome { | ||||||
| namespace i2c { | namespace i2c { | ||||||
|  |  | ||||||
| @@ -39,71 +42,66 @@ struct WriteBuffer { | |||||||
| /// note https://www.nxp.com/docs/en/application-note/AN10216.pdf | /// note https://www.nxp.com/docs/en/application-note/AN10216.pdf | ||||||
| class I2CBus { | class I2CBus { | ||||||
|  public: |  public: | ||||||
|   /// @brief Creates a ReadBuffer and calls the virtual readv() method to read bytes into this buffer |   virtual ~I2CBus() = default; | ||||||
|   /// @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); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   /// @brief This virtual method reads bytes from an I2CBus into an array of ReadBuffer. |   /// @brief This virtual method writes bytes to an I2CBus from an array, | ||||||
|   /// @param address address of the I²C component on the i2c bus |   /// then reads bytes into an array of ReadBuffer. | ||||||
|   /// @param buffers pointer to an array of ReadBuffer |   /// @param address address of the I²C device on the i2c bus | ||||||
|   /// @param count number of ReadBuffer to read |   /// @param write_buffer pointer to data | ||||||
|   /// @return an i2c::ErrorCode |   /// @param write_count number of bytes to write | ||||||
|   /// @details This is a pure virtual method that must be implemented in a subclass. |   /// @param read_buffer pointer to an array to receive data | ||||||
|   virtual ErrorCode readv(uint8_t address, ReadBuffer *buffers, size_t count) = 0; |   /// @param read_count number of bytes to read | ||||||
|  |  | ||||||
|   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 |  | ||||||
|   /// transmission. False will send a restart, keeping the connection active. |   /// transmission. False will send a restart, keeping the connection active. | ||||||
|   /// @return an i2c::ErrorCode |   /// @return an i2c::ErrorCode | ||||||
|   /// @details This is a pure virtual method that must be implemented in the subclass. |   /// @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: |  protected: | ||||||
|   /// @brief Scans the I2C bus for devices. Devices presence is kept in an array of std::pair |   /// @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. |   /// that contains the address and the corresponding bool presence flag. | ||||||
|   virtual void i2c_scan() { |   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); |  | ||||||
|       } |  | ||||||
|     } |  | ||||||
|   } |  | ||||||
|   std::vector<std::pair<uint8_t, bool>> scan_results_;  ///< array containing scan results |   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 |   bool scan_{false};                                    ///< Should we scan ? Can be set in the yaml | ||||||
| }; | }; | ||||||
|   | |||||||
| @@ -41,7 +41,7 @@ void ArduinoI2CBus::setup() { | |||||||
|   this->initialized_ = true; |   this->initialized_ = true; | ||||||
|   if (this->scan_) { |   if (this->scan_) { | ||||||
|     ESP_LOGV(TAG, "Scanning bus for active devices"); |     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) | #if defined(USE_ESP8266) | ||||||
|   this->set_pins_and_clock_();  // reconfigure Wire global state in case there are multiple instances |   this->set_pins_and_clock_();  // reconfigure Wire global state in case there are multiple instances | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
|   // logging is only enabled with vv level, if warnings are shown the caller |  | ||||||
|   // should log them |  | ||||||
|   if (!initialized_) { |   if (!initialized_) { | ||||||
|     ESP_LOGVV(TAG, "i2c bus not initialized!"); |     ESP_LOGD(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!"); |  | ||||||
|     return ERROR_NOT_INITIALIZED; |     return ERROR_NOT_INITIALIZED; | ||||||
|   } |   } | ||||||
|  |  | ||||||
| #ifdef ESPHOME_LOG_HAS_VERY_VERBOSE |   ESP_LOGV(TAG, "0x%02X TX %s", address, format_hex_pretty(write_buffer, write_count).c_str()); | ||||||
|   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 |  | ||||||
|  |  | ||||||
|  |   uint8_t status = 0; | ||||||
|  |   if (write_count != 0 || read_count == 0) { | ||||||
|     wire_->beginTransmission(address); |     wire_->beginTransmission(address); | ||||||
|   size_t written = 0; |     size_t ret = wire_->write(write_buffer, write_count); | ||||||
|   for (size_t i = 0; i < cnt; i++) { |     if (ret != write_count) { | ||||||
|     const auto &buf = buffers[i]; |       ESP_LOGV(TAG, "TX failed"); | ||||||
|     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); |  | ||||||
|       return ERROR_UNKNOWN; |       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) { |   switch (status) { | ||||||
|     case 0: |     case 0: | ||||||
|       return ERROR_OK; |       return ERROR_OK; | ||||||
|   | |||||||
| @@ -19,8 +19,8 @@ class ArduinoI2CBus : public InternalI2CBus, public Component { | |||||||
|  public: |  public: | ||||||
|   void setup() override; |   void setup() override; | ||||||
|   void dump_config() override; |   void dump_config() override; | ||||||
|   ErrorCode readv(uint8_t address, ReadBuffer *buffers, size_t cnt) override; |   ErrorCode write_readv(uint8_t address, const uint8_t *write_buffer, size_t write_count, uint8_t *read_buffer, | ||||||
|   ErrorCode writev(uint8_t address, WriteBuffer *buffers, size_t cnt, bool stop) override; |                         size_t read_count) override; | ||||||
|   float get_setup_priority() const override { return setup_priority::BUS; } |   float get_setup_priority() const override { return setup_priority::BUS; } | ||||||
|  |  | ||||||
|   void set_scan(bool scan) { scan_ = scan; } |   void set_scan(bool scan) { scan_ = scan; } | ||||||
|   | |||||||
| @@ -1,6 +1,7 @@ | |||||||
| #ifdef USE_ESP_IDF | #ifdef USE_ESP_IDF | ||||||
|  |  | ||||||
| #include "i2c_bus_esp_idf.h" | #include "i2c_bus_esp_idf.h" | ||||||
|  |  | ||||||
| #include <driver/gpio.h> | #include <driver/gpio.h> | ||||||
| #include <cinttypes> | #include <cinttypes> | ||||||
| #include <cstring> | #include <cstring> | ||||||
| @@ -9,10 +10,6 @@ | |||||||
| #include "esphome/core/helpers.h" | #include "esphome/core/helpers.h" | ||||||
| #include "esphome/core/log.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 esphome { | ||||||
| namespace i2c { | namespace i2c { | ||||||
|  |  | ||||||
| @@ -34,7 +31,6 @@ void IDFI2CBus::setup() { | |||||||
|  |  | ||||||
|   this->recover_(); |   this->recover_(); | ||||||
|  |  | ||||||
| #if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 4, 2) |  | ||||||
|   next_port = (i2c_port_t) (next_port + 1); |   next_port = (i2c_port_t) (next_port + 1); | ||||||
|  |  | ||||||
|   i2c_master_bus_config_t bus_conf{}; |   i2c_master_bus_config_t bus_conf{}; | ||||||
| @@ -77,56 +73,8 @@ void IDFI2CBus::setup() { | |||||||
|  |  | ||||||
|   if (this->scan_) { |   if (this->scan_) { | ||||||
|     ESP_LOGV(TAG, "Scanning for devices"); |     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() { | void IDFI2CBus::dump_config() { | ||||||
| @@ -166,267 +114,73 @@ void IDFI2CBus::dump_config() { | |||||||
|   } |   } | ||||||
| } | } | ||||||
|  |  | ||||||
| #if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 4, 2) | ErrorCode IDFI2CBus::write_readv(uint8_t address, const uint8_t *write_buffer, size_t write_count, uint8_t *read_buffer, | ||||||
| void IDFI2CBus::i2c_scan() { |                                  size_t read_count) { | ||||||
|   for (uint8_t address = 8; address < 120; address++) { |   // logging is only enabled with v level, if warnings are shown the caller | ||||||
|     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 |  | ||||||
|   // should log them |   // should log them | ||||||
|   if (!initialized_) { |   if (!initialized_) { | ||||||
|     ESP_LOGVV(TAG, "i2c bus not initialized!"); |     ESP_LOGW(TAG, "i2c bus not initialized!"); | ||||||
|     return ERROR_NOT_INITIALIZED; |     return ERROR_NOT_INITIALIZED; | ||||||
|   } |   } | ||||||
|  |  | ||||||
| #if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 4, 2) |   i2c_operation_job_t jobs[8]{}; | ||||||
|   i2c_operation_job_t jobs[cnt + 4]; |   size_t num_jobs = 0; | ||||||
|   uint8_t read = (address << 1) | I2C_MASTER_READ; |   uint8_t write_addr = (address << 1) | I2C_MASTER_WRITE; | ||||||
|   size_t last = 0, num = 0; |   uint8_t read_addr = (address << 1) | I2C_MASTER_READ; | ||||||
|  |   ESP_LOGV(TAG, "Writing %zu bytes, reading %zu bytes", write_count, read_count); | ||||||
|   jobs[num].command = I2C_MASTER_CMD_START; |   if (read_count == 0 && write_count == 0) { | ||||||
|   num++; |     // basically just a bus probe. Send a start, address and stop | ||||||
|  |     ESP_LOGV(TAG, "0x%02X BUS PROBE", address); | ||||||
|   jobs[num].command = I2C_MASTER_CMD_WRITE; |     jobs[num_jobs++].command = I2C_MASTER_CMD_START; | ||||||
|   jobs[num].write.ack_check = true; |     jobs[num_jobs].command = I2C_MASTER_CMD_WRITE; | ||||||
|   jobs[num].write.data = &read; |     jobs[num_jobs].write.ack_check = true; | ||||||
|   jobs[num].write.total_bytes = 1; |     jobs[num_jobs].write.data = &write_addr; | ||||||
|   num++; |     jobs[num_jobs++].write.total_bytes = 1; | ||||||
|  |  | ||||||
|   // find the last valid index |  | ||||||
|   for (size_t i = 0; i < cnt; i++) { |  | ||||||
|     const auto &buf = buffers[i]; |  | ||||||
|     if (buf.len == 0) { |  | ||||||
|       continue; |  | ||||||
|     } |  | ||||||
|     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++; |  | ||||||
|       } |  | ||||||
|       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 { |   } else { | ||||||
|       jobs[num].command = I2C_MASTER_CMD_READ; |     if (write_count != 0) { | ||||||
|       jobs[num].read.ack_value = I2C_ACK_VAL; |       ESP_LOGV(TAG, "0x%02X TX %s", address, format_hex_pretty(write_buffer, write_count).c_str()); | ||||||
|       jobs[num].read.data = (uint8_t *) buf.data; |       jobs[num_jobs++].command = I2C_MASTER_CMD_START; | ||||||
|       jobs[num].read.total_bytes = buf.len; |       jobs[num_jobs].command = I2C_MASTER_CMD_WRITE; | ||||||
|       num++; |       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; | ||||||
|  |     } | ||||||
|  |     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_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_jobs++].command = I2C_MASTER_CMD_STOP; | ||||||
|   jobs[num].command = I2C_MASTER_CMD_STOP; |   ESP_LOGV(TAG, "Sending %zu jobs", num_jobs); | ||||||
|   num++; |   esp_err_t err = i2c_master_execute_defined_operations(this->dev_, jobs, num_jobs, 20); | ||||||
|  |  | ||||||
|   esp_err_t err = i2c_master_execute_defined_operations(this->dev_, jobs, num, 20); |  | ||||||
|   if (err == ESP_ERR_INVALID_STATE) { |   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; |     return ERROR_NOT_ACKNOWLEDGED; | ||||||
|   } else if (err == ESP_ERR_TIMEOUT) { |   } 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; |     return ERROR_TIMEOUT; | ||||||
|   } else if (err != ESP_OK) { |   } 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; |     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; |   return ERROR_OK; | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -436,8 +190,8 @@ ErrorCode IDFI2CBus::writev(uint8_t address, WriteBuffer *buffers, size_t cnt, b | |||||||
| void IDFI2CBus::recover_() { | void IDFI2CBus::recover_() { | ||||||
|   ESP_LOGI(TAG, "Performing bus recovery"); |   ESP_LOGI(TAG, "Performing bus recovery"); | ||||||
|  |  | ||||||
|   const gpio_num_t scl_pin = static_cast<gpio_num_t>(scl_pin_); |   const auto scl_pin = static_cast<gpio_num_t>(scl_pin_); | ||||||
|   const gpio_num_t sda_pin = static_cast<gpio_num_t>(sda_pin_); |   const auto sda_pin = static_cast<gpio_num_t>(sda_pin_); | ||||||
|  |  | ||||||
|   // For the upcoming operations, target for a 60kHz toggle frequency. |   // For the upcoming operations, target for a 60kHz toggle frequency. | ||||||
|   // 1000kHz is the maximum frequency for I2C running in standard-mode, |   // 1000kHz is the maximum frequency for I2C running in standard-mode, | ||||||
| @@ -545,5 +299,4 @@ void IDFI2CBus::recover_() { | |||||||
|  |  | ||||||
| }  // namespace i2c | }  // namespace i2c | ||||||
| }  // namespace esphome | }  // namespace esphome | ||||||
|  |  | ||||||
| #endif  // USE_ESP_IDF | #endif  // USE_ESP_IDF | ||||||
|   | |||||||
| @@ -2,14 +2,9 @@ | |||||||
|  |  | ||||||
| #ifdef USE_ESP_IDF | #ifdef USE_ESP_IDF | ||||||
|  |  | ||||||
| #include "esp_idf_version.h" |  | ||||||
| #include "esphome/core/component.h" | #include "esphome/core/component.h" | ||||||
| #include "i2c_bus.h" | #include "i2c_bus.h" | ||||||
| #if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 4, 2) |  | ||||||
| #include <driver/i2c_master.h> | #include <driver/i2c_master.h> | ||||||
| #else |  | ||||||
| #include <driver/i2c.h> |  | ||||||
| #endif |  | ||||||
|  |  | ||||||
| namespace esphome { | namespace esphome { | ||||||
| namespace i2c { | namespace i2c { | ||||||
| @@ -24,36 +19,33 @@ class IDFI2CBus : public InternalI2CBus, public Component { | |||||||
|  public: |  public: | ||||||
|   void setup() override; |   void setup() override; | ||||||
|   void dump_config() override; |   void dump_config() override; | ||||||
|   ErrorCode readv(uint8_t address, ReadBuffer *buffers, size_t cnt) override; |   ErrorCode write_readv(uint8_t address, const uint8_t *write_buffer, size_t write_count, uint8_t *read_buffer, | ||||||
|   ErrorCode writev(uint8_t address, WriteBuffer *buffers, size_t cnt, bool stop) override; |                         size_t read_count) override; | ||||||
|   float get_setup_priority() const override { return setup_priority::BUS; } |   float get_setup_priority() const override { return setup_priority::BUS; } | ||||||
|  |  | ||||||
|   void set_scan(bool scan) { scan_ = scan; } |   void set_scan(bool scan) { this->scan_ = scan; } | ||||||
|   void set_sda_pin(uint8_t sda_pin) { sda_pin_ = sda_pin; } |   void set_sda_pin(uint8_t sda_pin) { this->sda_pin_ = sda_pin; } | ||||||
|   void set_sda_pullup_enabled(bool sda_pullup_enabled) { sda_pullup_enabled_ = sda_pullup_enabled; } |   void set_sda_pullup_enabled(bool sda_pullup_enabled) { this->sda_pullup_enabled_ = sda_pullup_enabled; } | ||||||
|   void set_scl_pin(uint8_t scl_pin) { scl_pin_ = scl_pin; } |   void set_scl_pin(uint8_t scl_pin) { this->scl_pin_ = scl_pin; } | ||||||
|   void set_scl_pullup_enabled(bool scl_pullup_enabled) { scl_pullup_enabled_ = scl_pullup_enabled; } |   void set_scl_pullup_enabled(bool scl_pullup_enabled) { this->scl_pullup_enabled_ = scl_pullup_enabled; } | ||||||
|   void set_frequency(uint32_t frequency) { frequency_ = frequency; } |   void set_frequency(uint32_t frequency) { this->frequency_ = frequency; } | ||||||
|   void set_timeout(uint32_t timeout) { timeout_ = timeout; } |   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: |  private: | ||||||
|   void recover_(); |   void recover_(); | ||||||
|   RecoveryCode recovery_result_; |   RecoveryCode recovery_result_{}; | ||||||
|  |  | ||||||
|  protected: |  protected: | ||||||
| #if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 4, 2) |   i2c_master_dev_handle_t dev_{}; | ||||||
|   i2c_master_dev_handle_t dev_; |   i2c_master_bus_handle_t bus_{}; | ||||||
|   i2c_master_bus_handle_t bus_; |   i2c_port_t port_{}; | ||||||
|   void i2c_scan() override; |   uint8_t sda_pin_{}; | ||||||
| #endif |   bool sda_pullup_enabled_{}; | ||||||
|   i2c_port_t port_; |   uint8_t scl_pin_{}; | ||||||
|   uint8_t sda_pin_; |   bool scl_pullup_enabled_{}; | ||||||
|   bool sda_pullup_enabled_; |   uint32_t frequency_{}; | ||||||
|   uint8_t scl_pin_; |  | ||||||
|   bool scl_pullup_enabled_; |  | ||||||
|   uint32_t frequency_; |  | ||||||
|   uint32_t timeout_ = 0; |   uint32_t timeout_ = 0; | ||||||
|   bool initialized_ = false; |   bool initialized_ = false; | ||||||
| }; | }; | ||||||
|   | |||||||
| @@ -35,7 +35,7 @@ void IAQCore::setup() { | |||||||
| void IAQCore::update() { | void IAQCore::update() { | ||||||
|   uint8_t buffer[sizeof(SensorData)]; |   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"); |     ESP_LOGD(TAG, "Read failed"); | ||||||
|     this->status_set_warning(); |     this->status_set_warning(); | ||||||
|     this->publish_nans_(); |     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) { | 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) { |   if (ret != i2c::ERROR_OK) { | ||||||
|     ESP_LOGE(TAG, "read_ina_register_ failed. Reg=0x%02X Err=%d", reg, ret); |     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(); |     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) { |   if (err == esphome::i2c::ERROR_OK) { | ||||||
|     ESP_LOGCONFIG(TAG, "Could write to the address %d.", this->address_); |     ESP_LOGCONFIG(TAG, "Could write to the address %d.", this->address_); | ||||||
|   } else { |   } else { | ||||||
| @@ -33,7 +33,7 @@ void KMeterISOComponent::setup() { | |||||||
|   } |   } | ||||||
|  |  | ||||||
|   uint8_t read_buf[4] = {1}; |   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."); |     ESP_LOGCONFIG(TAG, "Could not read from the device."); | ||||||
|     this->error_code_ = COMMUNICATION_FAILED; |     this->error_code_ = COMMUNICATION_FAILED; | ||||||
|     this->mark_failed(); |     this->mark_failed(); | ||||||
|   | |||||||
| @@ -184,7 +184,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 |     //  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 |     //  transaction. This is bad in this case and will result in reading nothing but 0xFFFF | ||||||
|     //  from the registers. |     //  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) { |     if (return_code != i2c::NO_ERROR) { | ||||||
|       // Error on the i2c bus |       // Error on the i2c bus | ||||||
|       this->status_set_warning( |       this->status_set_warning( | ||||||
| @@ -225,7 +225,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++) { |   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. |     // Note: we don't write the first byte of the write buffer to the device. | ||||||
|     //  This is done automatically by the write() function. |     //  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) { |     if (return_code == i2c::NO_ERROR) { | ||||||
|       return return_code; |       return return_code; | ||||||
|     } else { |     } else { | ||||||
|   | |||||||
| @@ -328,7 +328,7 @@ bool Mcp4461Component::increase_wiper_(Mcp4461WiperIdx wiper) { | |||||||
|   ESP_LOGV(TAG, "Increasing wiper %u", wiper_idx); |   ESP_LOGV(TAG, "Increasing wiper %u", wiper_idx); | ||||||
|   uint8_t addr = this->get_wiper_address_(wiper_idx); |   uint8_t addr = this->get_wiper_address_(wiper_idx); | ||||||
|   uint8_t reg = addr | static_cast<uint8_t>(Mcp4461Commands::INCREMENT); |   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) { |   if (err != i2c::ERROR_OK) { | ||||||
|     this->error_code_ = MCP4461_STATUS_I2C_ERROR; |     this->error_code_ = MCP4461_STATUS_I2C_ERROR; | ||||||
|     this->status_set_warning(); |     this->status_set_warning(); | ||||||
| @@ -359,7 +359,7 @@ bool Mcp4461Component::decrease_wiper_(Mcp4461WiperIdx wiper) { | |||||||
|   ESP_LOGV(TAG, "Decreasing wiper %u", wiper_idx); |   ESP_LOGV(TAG, "Decreasing wiper %u", wiper_idx); | ||||||
|   uint8_t addr = this->get_wiper_address_(wiper_idx); |   uint8_t addr = this->get_wiper_address_(wiper_idx); | ||||||
|   uint8_t reg = addr | static_cast<uint8_t>(Mcp4461Commands::DECREMENT); |   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) { |   if (err != i2c::ERROR_OK) { | ||||||
|     this->error_code_ = MCP4461_STATUS_I2C_ERROR; |     this->error_code_ = MCP4461_STATUS_I2C_ERROR; | ||||||
|     this->status_set_warning(); |     this->status_set_warning(); | ||||||
|   | |||||||
| @@ -309,8 +309,12 @@ class DriverChip: | |||||||
|                 CONF_NATIVE_HEIGHT, height + offset_height * 2 |                 CONF_NATIVE_HEIGHT, height + offset_height * 2 | ||||||
|             ) |             ) | ||||||
|             offset_height = native_height - height - offset_height |             offset_height = native_height - height - offset_height | ||||||
|         # Swap default dimensions if swap_xy is set |         # Swap default dimensions if swap_xy is set, or if rotation is 90/270 and we are not using a buffer | ||||||
|         if transform[CONF_SWAP_XY] is True: |         rotated = not requires_buffer(config) and config.get(CONF_ROTATION, 0) in ( | ||||||
|  |             90, | ||||||
|  |             270, | ||||||
|  |         ) | ||||||
|  |         if transform[CONF_SWAP_XY] is True or rotated: | ||||||
|             width, height = height, width |             width, height = height, width | ||||||
|             offset_height, offset_width = offset_width, offset_height |             offset_height, offset_width = offset_width, offset_height | ||||||
|         return width, height, offset_width, offset_height |         return width, height, offset_width, offset_height | ||||||
|   | |||||||
| @@ -90,18 +90,18 @@ float MLX90614Component::get_setup_priority() const { return setup_priority::DAT | |||||||
|  |  | ||||||
| void MLX90614Component::update() { | void MLX90614Component::update() { | ||||||
|   uint8_t emissivity[3]; |   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(); |     this->status_set_warning(); | ||||||
|     return; |     return; | ||||||
|   } |   } | ||||||
|   uint8_t raw_object[3]; |   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(); |     this->status_set_warning(); | ||||||
|     return; |     return; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   uint8_t raw_ambient[3]; |   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(); |     this->status_set_warning(); | ||||||
|     return; |     return; | ||||||
|   } |   } | ||||||
|   | |||||||
| @@ -10,7 +10,7 @@ static const char *const TAG = "mpl3115a2"; | |||||||
|  |  | ||||||
| void MPL3115A2Component::setup() { | void MPL3115A2Component::setup() { | ||||||
|   uint8_t whoami = 0xFF; |   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->error_code_ = COMMUNICATION_FAILED; | ||||||
|     this->mark_failed(); |     this->mark_failed(); | ||||||
|     return; |     return; | ||||||
| @@ -54,24 +54,24 @@ void MPL3115A2Component::dump_config() { | |||||||
|  |  | ||||||
| void MPL3115A2Component::update() { | void MPL3115A2Component::update() { | ||||||
|   uint8_t mode = MPL3115A2_CTRL_REG1_OS128; |   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 |   // Trigger a new reading | ||||||
|   mode |= MPL3115A2_CTRL_REG1_OST; |   mode |= MPL3115A2_CTRL_REG1_OST; | ||||||
|   if (this->altitude_ != nullptr) |   if (this->altitude_ != nullptr) | ||||||
|     mode |= MPL3115A2_CTRL_REG1_ALT; |     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 |   // Wait until status shows reading available | ||||||
|   uint8_t status = 0; |   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); |     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; |       return; | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   uint8_t buffer[5] = {0, 0, 0, 0, 0}; |   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; |   float altitude = 0, pressure = 0; | ||||||
|   if (this->altitude_ != nullptr) { |   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) { | i2c::ErrorCode NPI19Component::read_(uint16_t &raw_temperature, uint16_t &raw_pressure) { | ||||||
|   // initiate data read from device |   // 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) { |   if (w_err != i2c::ERROR_OK) { | ||||||
|     return w_err; |     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]() { |   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"); |       ESP_LOGW(TAG, "Starting configuration register read failed"); | ||||||
|       f(NAN); |       f(NAN); | ||||||
|       return; |       return; | ||||||
|   | |||||||
| @@ -33,7 +33,7 @@ void PCA6416AComponent::setup() { | |||||||
|   } |   } | ||||||
|  |  | ||||||
|   // Test to see if the device supports pull-up resistors |   // 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; |     this->has_pullup_ = true; | ||||||
|   } |   } | ||||||
|  |  | ||||||
| @@ -105,7 +105,7 @@ bool PCA6416AComponent::read_register_(uint8_t reg, uint8_t *value) { | |||||||
|     return false; |     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) { |   if (this->last_error_ != i2c::ERROR_OK) { | ||||||
|     this->status_set_warning(); |     this->status_set_warning(); | ||||||
|     ESP_LOGE(TAG, "read_register_(): I2C I/O error: %d", (int) this->last_error_); |     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; |     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) { |   if (this->last_error_ != i2c::ERROR_OK) { | ||||||
|     this->status_set_warning(); |     this->status_set_warning(); | ||||||
|     ESP_LOGE(TAG, "write_register_(): I2C I/O error: %d", (int) this->last_error_); |     ESP_LOGE(TAG, "write_register_(): I2C I/O error: %d", (int) this->last_error_); | ||||||
|   | |||||||
| @@ -96,7 +96,7 @@ bool PCA9554Component::read_inputs_() { | |||||||
|     return false; |     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) { |   if (this->last_error_ != i2c::ERROR_OK) { | ||||||
|     this->status_set_warning(); |     this->status_set_warning(); | ||||||
|     ESP_LOGE(TAG, "read_register_(): I2C I/O error: %d", (int) this->last_error_); |     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]; |   uint8_t outputs[2]; | ||||||
|   outputs[0] = (uint8_t) value; |   outputs[0] = (uint8_t) value; | ||||||
|   outputs[1] = (uint8_t) (value >> 8); |   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) { |   if (this->last_error_ != i2c::ERROR_OK) { | ||||||
|     this->status_set_warning(); |     this->status_set_warning(); | ||||||
|     ESP_LOGE(TAG, "write_register_(): I2C I/O error: %d", (int) this->last_error_); |     ESP_LOGE(TAG, "write_register_(): I2C I/O error: %d", (int) this->last_error_); | ||||||
|   | |||||||
| @@ -138,11 +138,37 @@ void Rtttl::stop() { | |||||||
|     this->set_state_(STATE_STOPPING); |     this->set_state_(STATE_STOPPING); | ||||||
|   } |   } | ||||||
| #endif | #endif | ||||||
|  |   this->position_ = this->rtttl_.length(); | ||||||
|  |   this->note_duration_ = 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void Rtttl::finish_() { | ||||||
|  |   ESP_LOGV(TAG, "Rtttl::finish_()"); | ||||||
|  | #ifdef USE_OUTPUT | ||||||
|  |   if (this->output_ != nullptr) { | ||||||
|  |     this->output_->set_level(0.0); | ||||||
|  |     this->set_state_(State::STATE_STOPPED); | ||||||
|  |   } | ||||||
|  | #endif | ||||||
|  | #ifdef USE_SPEAKER | ||||||
|  |   if (this->speaker_ != nullptr) { | ||||||
|  |     SpeakerSample sample[2]; | ||||||
|  |     sample[0].left = 0; | ||||||
|  |     sample[0].right = 0; | ||||||
|  |     sample[1].left = 0; | ||||||
|  |     sample[1].right = 0; | ||||||
|  |     this->speaker_->play((uint8_t *) (&sample), 8); | ||||||
|  |     this->speaker_->finish(); | ||||||
|  |     this->set_state_(State::STATE_STOPPING); | ||||||
|  |   } | ||||||
|  | #endif | ||||||
|  |   // Ensure no more notes are played in case finish_() is called for an error. | ||||||
|  |   this->position_ = this->rtttl_.length(); | ||||||
|   this->note_duration_ = 0; |   this->note_duration_ = 0; | ||||||
| } | } | ||||||
|  |  | ||||||
| void Rtttl::loop() { | void Rtttl::loop() { | ||||||
|   if (this->note_duration_ == 0 || this->state_ == State::STATE_STOPPED) { |   if (this->state_ == State::STATE_STOPPED) { | ||||||
|     this->disable_loop(); |     this->disable_loop(); | ||||||
|     return; |     return; | ||||||
|   } |   } | ||||||
| @@ -152,6 +178,8 @@ void Rtttl::loop() { | |||||||
|     if (this->state_ == State::STATE_STOPPING) { |     if (this->state_ == State::STATE_STOPPING) { | ||||||
|       if (this->speaker_->is_stopped()) { |       if (this->speaker_->is_stopped()) { | ||||||
|         this->set_state_(State::STATE_STOPPED); |         this->set_state_(State::STATE_STOPPED); | ||||||
|  |       } else { | ||||||
|  |         return; | ||||||
|       } |       } | ||||||
|     } else if (this->state_ == State::STATE_INIT) { |     } else if (this->state_ == State::STATE_INIT) { | ||||||
|       if (this->speaker_->is_stopped()) { |       if (this->speaker_->is_stopped()) { | ||||||
| @@ -207,7 +235,7 @@ void Rtttl::loop() { | |||||||
|   if (this->output_ != nullptr && millis() - this->last_note_ < this->note_duration_) |   if (this->output_ != nullptr && millis() - this->last_note_ < this->note_duration_) | ||||||
|     return; |     return; | ||||||
| #endif | #endif | ||||||
|   if (!this->rtttl_[this->position_]) { |   if (this->position_ >= this->rtttl_.length()) { | ||||||
|     this->finish_(); |     this->finish_(); | ||||||
|     return; |     return; | ||||||
|   } |   } | ||||||
| @@ -346,31 +374,6 @@ void Rtttl::loop() { | |||||||
|   this->last_note_ = millis(); |   this->last_note_ = millis(); | ||||||
| } | } | ||||||
|  |  | ||||||
| void Rtttl::finish_() { |  | ||||||
| #ifdef USE_OUTPUT |  | ||||||
|   if (this->output_ != nullptr) { |  | ||||||
|     this->output_->set_level(0.0); |  | ||||||
|     this->set_state_(State::STATE_STOPPED); |  | ||||||
|   } |  | ||||||
| #endif |  | ||||||
| #ifdef USE_SPEAKER |  | ||||||
|   if (this->speaker_ != nullptr) { |  | ||||||
|     SpeakerSample sample[2]; |  | ||||||
|     sample[0].left = 0; |  | ||||||
|     sample[0].right = 0; |  | ||||||
|     sample[1].left = 0; |  | ||||||
|     sample[1].right = 0; |  | ||||||
|     this->speaker_->play((uint8_t *) (&sample), 8); |  | ||||||
|  |  | ||||||
|     this->speaker_->finish(); |  | ||||||
|     this->set_state_(State::STATE_STOPPING); |  | ||||||
|   } |  | ||||||
| #endif |  | ||||||
|   this->note_duration_ = 0; |  | ||||||
|   this->on_finished_playback_callback_.call(); |  | ||||||
|   ESP_LOGD(TAG, "Playback finished"); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| #if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_DEBUG | #if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_DEBUG | ||||||
| static const LogString *state_to_string(State state) { | static const LogString *state_to_string(State state) { | ||||||
|   switch (state) { |   switch (state) { | ||||||
| @@ -397,7 +400,11 @@ void Rtttl::set_state_(State state) { | |||||||
|            LOG_STR_ARG(state_to_string(state))); |            LOG_STR_ARG(state_to_string(state))); | ||||||
|  |  | ||||||
|   // Clear loop_done when transitioning from STOPPED to any other state |   // Clear loop_done when transitioning from STOPPED to any other state | ||||||
|   if (old_state == State::STATE_STOPPED && state != State::STATE_STOPPED) { |   if (state == State::STATE_STOPPED) { | ||||||
|  |     this->disable_loop(); | ||||||
|  |     this->on_finished_playback_callback_.call(); | ||||||
|  |     ESP_LOGD(TAG, "Playback finished"); | ||||||
|  |   } else if (old_state == State::STATE_STOPPED) { | ||||||
|     this->enable_loop(); |     this->enable_loop(); | ||||||
|   } |   } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -60,35 +60,60 @@ class Rtttl : public Component { | |||||||
|     } |     } | ||||||
|     return ret; |     return ret; | ||||||
|   } |   } | ||||||
|  |   /** | ||||||
|  |    * @brief Finalizes the playback of the RTTTL string. | ||||||
|  |    * | ||||||
|  |    * This method is called internally when the end of the RTTTL string is reached | ||||||
|  |    * or when a parsing error occurs. It stops the output, sets the component state, | ||||||
|  |    * and triggers the on_finished_playback_callback_. | ||||||
|  |    */ | ||||||
|   void finish_(); |   void finish_(); | ||||||
|   void set_state_(State state); |   void set_state_(State state); | ||||||
|  |  | ||||||
|  |   /// The RTTTL string to play. | ||||||
|   std::string rtttl_{""}; |   std::string rtttl_{""}; | ||||||
|  |   /// The current position in the RTTTL string. | ||||||
|   size_t position_{0}; |   size_t position_{0}; | ||||||
|  |   /// The duration of a whole note in milliseconds. | ||||||
|   uint16_t wholenote_; |   uint16_t wholenote_; | ||||||
|  |   /// The default duration of a note (e.g. 4 for a quarter note). | ||||||
|   uint16_t default_duration_; |   uint16_t default_duration_; | ||||||
|  |   /// The default octave for a note. | ||||||
|   uint16_t default_octave_; |   uint16_t default_octave_; | ||||||
|  |   /// The time the last note was started. | ||||||
|   uint32_t last_note_; |   uint32_t last_note_; | ||||||
|  |   /// The duration of the current note in milliseconds. | ||||||
|   uint16_t note_duration_; |   uint16_t note_duration_; | ||||||
|  |  | ||||||
|  |   /// The frequency of the current note in Hz. | ||||||
|   uint32_t output_freq_; |   uint32_t output_freq_; | ||||||
|  |   /// The gain of the output. | ||||||
|   float gain_{0.6f}; |   float gain_{0.6f}; | ||||||
|  |   /// The current state of the RTTTL player. | ||||||
|   State state_{State::STATE_STOPPED}; |   State state_{State::STATE_STOPPED}; | ||||||
|  |  | ||||||
| #ifdef USE_OUTPUT | #ifdef USE_OUTPUT | ||||||
|  |   /// The output to write the sound to. | ||||||
|   output::FloatOutput *output_; |   output::FloatOutput *output_; | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
| #ifdef USE_SPEAKER | #ifdef USE_SPEAKER | ||||||
|  |   /// The speaker to write the sound to. | ||||||
|   speaker::Speaker *speaker_{nullptr}; |   speaker::Speaker *speaker_{nullptr}; | ||||||
|  |   /// The sample rate of the speaker. | ||||||
|   int sample_rate_{16000}; |   int sample_rate_{16000}; | ||||||
|  |   /// The number of samples for one full cycle of a note's waveform, in Q10 fixed-point format. | ||||||
|   int samples_per_wave_{0}; |   int samples_per_wave_{0}; | ||||||
|  |   /// The number of samples sent. | ||||||
|   int samples_sent_{0}; |   int samples_sent_{0}; | ||||||
|  |   /// The total number of samples to send. | ||||||
|   int samples_count_{0}; |   int samples_count_{0}; | ||||||
|  |   /// The number of samples for the gap between notes. | ||||||
|   int samples_gap_{0}; |   int samples_gap_{0}; | ||||||
|  |  | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
|  |   /// The callback to call when playback is finished. | ||||||
|   CallbackManager<void()> on_finished_playback_callback_; |   CallbackManager<void()> on_finished_playback_callback_; | ||||||
| }; | }; | ||||||
|  |  | ||||||
|   | |||||||
| @@ -51,8 +51,7 @@ void HOT I2CST7567::write_display_data() { | |||||||
|     static const size_t BLOCK_SIZE = 64; |     static const size_t BLOCK_SIZE = 64; | ||||||
|     for (uint8_t x = 0; x < (uint8_t) this->get_width_internal(); x += BLOCK_SIZE) { |     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->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, |                            this->get_width_internal() - x > BLOCK_SIZE ? BLOCK_SIZE : this->get_width_internal() - x); | ||||||
|                            true); |  | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -6,23 +6,15 @@ namespace tca9548a { | |||||||
|  |  | ||||||
| static const char *const TAG = "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_); |   auto err = this->parent_->switch_to_channel(channel_); | ||||||
|   if (err != i2c::ERROR_OK) |   if (err != i2c::ERROR_OK) | ||||||
|     return err; |     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(); |   this->parent_->disable_all_channels(); | ||||||
|   return err; |   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() { | void TCA9548AComponent::setup() { | ||||||
|   uint8_t status = 0; |   uint8_t status = 0; | ||||||
|   if (this->read(&status, 1) != i2c::ERROR_OK) { |   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_channel(uint8_t channel) { channel_ = channel; } | ||||||
|   void set_parent(TCA9548AComponent *parent) { parent_ = parent; } |   void set_parent(TCA9548AComponent *parent) { parent_ = parent; } | ||||||
|  |  | ||||||
|   i2c::ErrorCode readv(uint8_t address, i2c::ReadBuffer *buffers, size_t cnt) override; |   i2c::ErrorCode write_readv(uint8_t address, const uint8_t *write_buffer, size_t write_count, uint8_t *read_buffer, | ||||||
|   i2c::ErrorCode writev(uint8_t address, i2c::WriteBuffer *buffers, size_t cnt, bool stop) override; |                              size_t read_count) override; | ||||||
|  |  | ||||||
|  protected: |  protected: | ||||||
|   uint8_t channel_; |   uint8_t channel_; | ||||||
|   | |||||||
| @@ -9,9 +9,9 @@ static const char *const TAG = "tee501"; | |||||||
|  |  | ||||||
| void TEE501Component::setup() { | void TEE501Component::setup() { | ||||||
|   uint8_t address[] = {0x70, 0x29}; |   uint8_t address[] = {0x70, 0x29}; | ||||||
|   this->write(address, 2, false); |  | ||||||
|   uint8_t identification[9]; |   uint8_t identification[9]; | ||||||
|   this->read(identification, 9); |   this->read(identification, 9); | ||||||
|  |   this->write_read(address, sizeof address, identification, sizeof identification); | ||||||
|   if (identification[8] != calc_crc8_(identification, 0, 7)) { |   if (identification[8] != calc_crc8_(identification, 0, 7)) { | ||||||
|     this->error_code_ = CRC_CHECK_FAILED; |     this->error_code_ = CRC_CHECK_FAILED; | ||||||
|     this->mark_failed(); |     this->mark_failed(); | ||||||
| @@ -41,7 +41,7 @@ void TEE501Component::dump_config() { | |||||||
| float TEE501Component::get_setup_priority() const { return setup_priority::DATA; } | float TEE501Component::get_setup_priority() const { return setup_priority::DATA; } | ||||||
| void TEE501Component::update() { | void TEE501Component::update() { | ||||||
|   uint8_t address_1[] = {0x2C, 0x1B}; |   uint8_t address_1[] = {0x2C, 0x1B}; | ||||||
|   this->write(address_1, 2, true); |   this->write(address_1, 2); | ||||||
|   this->set_timeout(50, [this]() { |   this->set_timeout(50, [this]() { | ||||||
|     uint8_t i2c_response[3]; |     uint8_t i2c_response[3]; | ||||||
|     this->read(i2c_response, 3); |     this->read(i2c_response, 3); | ||||||
|   | |||||||
| @@ -74,7 +74,8 @@ void TLC59208FOutput::setup() { | |||||||
|   ESP_LOGV(TAG, "  Resetting all devices on the bus"); |   ESP_LOGV(TAG, "  Resetting all devices on the bus"); | ||||||
|  |  | ||||||
|   // Reset 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"); |     ESP_LOGE(TAG, "RESET failed"); | ||||||
|     this->mark_failed(); |     this->mark_failed(); | ||||||
|     return; |     return; | ||||||
|   | |||||||
| @@ -14,14 +14,12 @@ void VEML3235Sensor::setup() { | |||||||
|     this->mark_failed(); |     this->mark_failed(); | ||||||
|     return; |     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"); |     ESP_LOGE(TAG, "Unable to read ID"); | ||||||
|     this->mark_failed(); |     this->mark_failed(); | ||||||
|     return; |  | ||||||
|   } else if (device_id[0] != DEVICE_ID) { |   } else if (device_id[0] != DEVICE_ID) { | ||||||
|     ESP_LOGE(TAG, "Incorrect device ID - expected 0x%.2x, read 0x%.2x", DEVICE_ID, device_id[0]); |     ESP_LOGE(TAG, "Incorrect device ID - expected 0x%.2x, read 0x%.2x", DEVICE_ID, device_id[0]); | ||||||
|     this->mark_failed(); |     this->mark_failed(); | ||||||
|     return; |  | ||||||
|   } |   } | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -49,7 +47,7 @@ float VEML3235Sensor::read_lx_() { | |||||||
|   } |   } | ||||||
|  |  | ||||||
|   uint8_t als_regs[] = {0, 0}; |   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(); |     this->status_set_warning(); | ||||||
|     return NAN; |     return NAN; | ||||||
|   } |   } | ||||||
|   | |||||||
| @@ -279,20 +279,18 @@ ErrorCode VEML7700Component::reconfigure_time_and_gain_(IntegrationTime time, Ga | |||||||
| } | } | ||||||
|  |  | ||||||
| ErrorCode VEML7700Component::read_sensor_output_(Readings &data) { | ErrorCode VEML7700Component::read_sensor_output_(Readings &data) { | ||||||
|   auto als_err = |   auto als_err = this->read_register((uint8_t) CommandRegisters::ALS, (uint8_t *) &data.als_counts, VEML_REG_SIZE); | ||||||
|       this->read_register((uint8_t) CommandRegisters::ALS, (uint8_t *) &data.als_counts, VEML_REG_SIZE, false); |  | ||||||
|   if (als_err != i2c::ERROR_OK) { |   if (als_err != i2c::ERROR_OK) { | ||||||
|     ESP_LOGW(TAG, "Error reading ALS register, err = %d", als_err); |     ESP_LOGW(TAG, "Error reading ALS register, err = %d", als_err); | ||||||
|   } |   } | ||||||
|   auto white_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) { |   if (white_err != i2c::ERROR_OK) { | ||||||
|     ESP_LOGW(TAG, "Error reading WHITE register, err = %d", white_err); |     ESP_LOGW(TAG, "Error reading WHITE register, err = %d", white_err); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   ConfigurationRegister conf{0}; |   ConfigurationRegister conf{0}; | ||||||
|   auto err = |   auto err = this->read_register((uint8_t) CommandRegisters::ALS_CONF_0, (uint8_t *) conf.raw_bytes, VEML_REG_SIZE); | ||||||
|       this->read_register((uint8_t) CommandRegisters::ALS_CONF_0, (uint8_t *) conf.raw_bytes, VEML_REG_SIZE, false); |  | ||||||
|   if (err != i2c::ERROR_OK) { |   if (err != i2c::ERROR_OK) { | ||||||
|     ESP_LOGW(TAG, "Error reading ALS_CONF_0 register, err = %d", white_err); |     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/i2c/i2c.h" | ||||||
| #include "esphome/components/sensor/sensor.h" | #include "esphome/components/sensor/sensor.h" | ||||||
| #include "esphome/core/component.h" | #include "esphome/core/component.h" | ||||||
| #include "esphome/core/optional.h" |  | ||||||
|  |  | ||||||
| namespace esphome { | namespace esphome { | ||||||
| namespace veml7700 { | namespace veml7700 { | ||||||
|   | |||||||
| @@ -151,6 +151,8 @@ void WiFiComponent::loop() { | |||||||
|         this->status_set_warning("waiting to reconnect"); |         this->status_set_warning("waiting to reconnect"); | ||||||
|         if (millis() - this->action_started_ > 5000) { |         if (millis() - this->action_started_ > 5000) { | ||||||
|           if (this->fast_connect_ || this->retry_hidden_) { |           if (this->fast_connect_ || this->retry_hidden_) { | ||||||
|  |             if (!this->selected_ap_.get_bssid().has_value()) | ||||||
|  |               this->selected_ap_ = this->sta_[0]; | ||||||
|             this->start_connecting(this->selected_ap_, false); |             this->start_connecting(this->selected_ap_, false); | ||||||
|           } else { |           } else { | ||||||
|             this->start_scanning(); |             this->start_scanning(); | ||||||
| @@ -670,10 +672,12 @@ void WiFiComponent::check_connecting_finished() { | |||||||
|       return; |       return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     ESP_LOGI(TAG, "Connected"); | ||||||
|     // We won't retry hidden networks unless a reconnect fails more than three times again |     // We won't retry hidden networks unless a reconnect fails more than three times again | ||||||
|  |     if (this->retry_hidden_ && !this->selected_ap_.get_hidden()) | ||||||
|  |       ESP_LOGW(TAG, "Network '%s' should be marked as hidden", this->selected_ap_.get_ssid().c_str()); | ||||||
|     this->retry_hidden_ = false; |     this->retry_hidden_ = false; | ||||||
|  |  | ||||||
|     ESP_LOGI(TAG, "Connected"); |  | ||||||
|     this->print_connect_params_(); |     this->print_connect_params_(); | ||||||
|  |  | ||||||
|     if (this->has_ap()) { |     if (this->has_ap()) { | ||||||
|   | |||||||
| @@ -547,8 +547,6 @@ void WiFiComponent::wifi_event_callback_(esphome_wifi_event_id_t event, esphome_ | |||||||
|     } |     } | ||||||
|     case ESPHOME_EVENT_ID_WIFI_STA_STOP: { |     case ESPHOME_EVENT_ID_WIFI_STA_STOP: { | ||||||
|       ESP_LOGV(TAG, "STA stop"); |       ESP_LOGV(TAG, "STA stop"); | ||||||
|       // Clear the STA interface handle to prevent use-after-free |  | ||||||
|       s_sta_netif = nullptr; |  | ||||||
|       break; |       break; | ||||||
|     } |     } | ||||||
|     case ESPHOME_EVENT_ID_WIFI_STA_CONNECTED: { |     case ESPHOME_EVENT_ID_WIFI_STA_CONNECTED: { | ||||||
| @@ -638,10 +636,6 @@ void WiFiComponent::wifi_event_callback_(esphome_wifi_event_id_t event, esphome_ | |||||||
|     } |     } | ||||||
|     case ESPHOME_EVENT_ID_WIFI_AP_STOP: { |     case ESPHOME_EVENT_ID_WIFI_AP_STOP: { | ||||||
|       ESP_LOGV(TAG, "AP stop"); |       ESP_LOGV(TAG, "AP stop"); | ||||||
| #ifdef USE_WIFI_AP |  | ||||||
|       // Clear the AP interface handle to prevent use-after-free |  | ||||||
|       s_ap_netif = nullptr; |  | ||||||
| #endif |  | ||||||
|       break; |       break; | ||||||
|     } |     } | ||||||
|     case ESPHOME_EVENT_ID_WIFI_AP_STACONNECTED: { |     case ESPHOME_EVENT_ID_WIFI_AP_STACONNECTED: { | ||||||
|   | |||||||
| @@ -697,8 +697,6 @@ void WiFiComponent::wifi_process_event_(IDFWiFiEvent *data) { | |||||||
|   } else if (data->event_base == WIFI_EVENT && data->event_id == WIFI_EVENT_STA_STOP) { |   } else if (data->event_base == WIFI_EVENT && data->event_id == WIFI_EVENT_STA_STOP) { | ||||||
|     ESP_LOGV(TAG, "STA stop"); |     ESP_LOGV(TAG, "STA stop"); | ||||||
|     s_sta_started = false; |     s_sta_started = false; | ||||||
|     // Clear the STA interface handle to prevent use-after-free |  | ||||||
|     s_sta_netif = nullptr; |  | ||||||
|  |  | ||||||
|   } else if (data->event_base == WIFI_EVENT && data->event_id == WIFI_EVENT_STA_AUTHMODE_CHANGE) { |   } else if (data->event_base == WIFI_EVENT && data->event_id == WIFI_EVENT_STA_AUTHMODE_CHANGE) { | ||||||
|     const auto &it = data->data.sta_authmode_change; |     const auto &it = data->data.sta_authmode_change; | ||||||
| @@ -797,10 +795,6 @@ void WiFiComponent::wifi_process_event_(IDFWiFiEvent *data) { | |||||||
|   } else if (data->event_base == WIFI_EVENT && data->event_id == WIFI_EVENT_AP_STOP) { |   } else if (data->event_base == WIFI_EVENT && data->event_id == WIFI_EVENT_AP_STOP) { | ||||||
|     ESP_LOGV(TAG, "AP stop"); |     ESP_LOGV(TAG, "AP stop"); | ||||||
|     s_ap_started = false; |     s_ap_started = false; | ||||||
| #ifdef USE_WIFI_AP |  | ||||||
|     // Clear the AP interface handle to prevent use-after-free |  | ||||||
|     s_ap_netif = nullptr; |  | ||||||
| #endif |  | ||||||
|  |  | ||||||
|   } else if (data->event_base == WIFI_EVENT && data->event_id == WIFI_EVENT_AP_PROBEREQRECVED) { |   } else if (data->event_base == WIFI_EVENT && data->event_id == WIFI_EVENT_AP_PROBEREQRECVED) { | ||||||
|     const auto &it = data->data.ap_probe_req_rx; |     const auto &it = data->data.ap_probe_req_rx; | ||||||
|   | |||||||
| @@ -4,7 +4,7 @@ from enum import Enum | |||||||
|  |  | ||||||
| from esphome.enum import StrEnum | from esphome.enum import StrEnum | ||||||
|  |  | ||||||
| __version__ = "2025.8.1" | __version__ = "2025.8.2" | ||||||
|  |  | ||||||
| ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_" | ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_" | ||||||
| VALID_SUBSTITUTIONS_CHARACTERS = ( | VALID_SUBSTITUTIONS_CHARACTERS = ( | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user