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