mirror of
https://github.com/esphome/esphome.git
synced 2024-11-25 16:38:16 +01:00
Adds i2c timeout config (#4614)
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
This commit is contained in:
parent
16d154e2e5
commit
5441213b27
5 changed files with 90 additions and 9 deletions
|
@ -4,6 +4,7 @@ import esphome.final_validate as fv
|
||||||
from esphome import pins
|
from esphome import pins
|
||||||
from esphome.const import (
|
from esphome.const import (
|
||||||
CONF_FREQUENCY,
|
CONF_FREQUENCY,
|
||||||
|
CONF_TIMEOUT,
|
||||||
CONF_ID,
|
CONF_ID,
|
||||||
CONF_INPUT,
|
CONF_INPUT,
|
||||||
CONF_OUTPUT,
|
CONF_OUTPUT,
|
||||||
|
@ -59,6 +60,7 @@ CONFIG_SCHEMA = cv.All(
|
||||||
cv.Optional(CONF_FREQUENCY, default="50kHz"): cv.All(
|
cv.Optional(CONF_FREQUENCY, default="50kHz"): cv.All(
|
||||||
cv.frequency, cv.Range(min=0, min_included=False)
|
cv.frequency, cv.Range(min=0, min_included=False)
|
||||||
),
|
),
|
||||||
|
cv.Optional(CONF_TIMEOUT): cv.positive_time_period,
|
||||||
cv.Optional(CONF_SCAN, default=True): cv.boolean,
|
cv.Optional(CONF_SCAN, default=True): cv.boolean,
|
||||||
}
|
}
|
||||||
).extend(cv.COMPONENT_SCHEMA),
|
).extend(cv.COMPONENT_SCHEMA),
|
||||||
|
@ -81,6 +83,8 @@ async def to_code(config):
|
||||||
|
|
||||||
cg.add(var.set_frequency(int(config[CONF_FREQUENCY])))
|
cg.add(var.set_frequency(int(config[CONF_FREQUENCY])))
|
||||||
cg.add(var.set_scan(config[CONF_SCAN]))
|
cg.add(var.set_scan(config[CONF_SCAN]))
|
||||||
|
if CONF_TIMEOUT in config:
|
||||||
|
cg.add(var.set_timeout(int(config[CONF_TIMEOUT].total_microseconds)))
|
||||||
if CORE.using_arduino:
|
if CORE.using_arduino:
|
||||||
cg.add_library("Wire", None)
|
cg.add_library("Wire", None)
|
||||||
|
|
||||||
|
@ -119,23 +123,56 @@ async def register_i2c_device(var, config):
|
||||||
|
|
||||||
|
|
||||||
def final_validate_device_schema(
|
def final_validate_device_schema(
|
||||||
name: str, *, min_frequency: cv.frequency = None, max_frequency: cv.frequency = None
|
name: str,
|
||||||
|
*,
|
||||||
|
min_frequency: cv.frequency = None,
|
||||||
|
max_frequency: cv.frequency = None,
|
||||||
|
min_timeout: cv.time_period = None,
|
||||||
|
max_timeout: cv.time_period = None,
|
||||||
):
|
):
|
||||||
hub_schema = {}
|
hub_schema = {}
|
||||||
if min_frequency is not None:
|
if (min_frequency is not None) and (max_frequency is not None):
|
||||||
|
hub_schema[cv.Required(CONF_FREQUENCY)] = cv.Range(
|
||||||
|
min=cv.frequency(min_frequency),
|
||||||
|
min_included=True,
|
||||||
|
max=cv.frequency(max_frequency),
|
||||||
|
max_included=True,
|
||||||
|
msg=f"Component {name} requires a frequency between {min_frequency} and {max_frequency} for the I2C bus",
|
||||||
|
)
|
||||||
|
elif min_frequency is not None:
|
||||||
hub_schema[cv.Required(CONF_FREQUENCY)] = cv.Range(
|
hub_schema[cv.Required(CONF_FREQUENCY)] = cv.Range(
|
||||||
min=cv.frequency(min_frequency),
|
min=cv.frequency(min_frequency),
|
||||||
min_included=True,
|
min_included=True,
|
||||||
msg=f"Component {name} requires a minimum frequency of {min_frequency} for the I2C bus",
|
msg=f"Component {name} requires a minimum frequency of {min_frequency} for the I2C bus",
|
||||||
)
|
)
|
||||||
|
elif max_frequency is not None:
|
||||||
if max_frequency is not None:
|
|
||||||
hub_schema[cv.Required(CONF_FREQUENCY)] = cv.Range(
|
hub_schema[cv.Required(CONF_FREQUENCY)] = cv.Range(
|
||||||
max=cv.frequency(max_frequency),
|
max=cv.frequency(max_frequency),
|
||||||
max_included=True,
|
max_included=True,
|
||||||
msg=f"Component {name} cannot be used with a frequency of over {max_frequency} for the I2C bus",
|
msg=f"Component {name} cannot be used with a frequency of over {max_frequency} for the I2C bus",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if (min_timeout is not None) and (max_timeout is not None):
|
||||||
|
hub_schema[cv.Required(CONF_TIMEOUT)] = cv.Range(
|
||||||
|
min=cv.time_period(min_timeout),
|
||||||
|
min_included=True,
|
||||||
|
max=cv.time_period(max_timeout),
|
||||||
|
max_included=True,
|
||||||
|
msg=f"Component {name} requires a timeout between {min_timeout} and {max_timeout} for the I2C bus",
|
||||||
|
)
|
||||||
|
elif min_timeout is not None:
|
||||||
|
hub_schema[cv.Required(CONF_TIMEOUT)] = cv.Range(
|
||||||
|
min=cv.time_period(min_timeout),
|
||||||
|
min_included=True,
|
||||||
|
msg=f"Component {name} requires a minimum timeout of {min_timeout} for the I2C bus",
|
||||||
|
)
|
||||||
|
elif max_timeout is not None:
|
||||||
|
hub_schema[cv.Required(CONF_TIMEOUT)] = cv.Range(
|
||||||
|
max=cv.time_period(max_timeout),
|
||||||
|
max_included=True,
|
||||||
|
msg=f"Component {name} cannot be used with a timeout of over {max_timeout} for the I2C bus",
|
||||||
|
)
|
||||||
|
|
||||||
return cv.Schema(
|
return cv.Schema(
|
||||||
{cv.Required(CONF_I2C_ID): fv.id_declaration_match_schema(hub_schema)},
|
{cv.Required(CONF_I2C_ID): fv.id_declaration_match_schema(hub_schema)},
|
||||||
extra=cv.ALLOW_EXTRA,
|
extra=cv.ALLOW_EXTRA,
|
||||||
|
|
|
@ -52,6 +52,18 @@ void ArduinoI2CBus::set_pins_and_clock_() {
|
||||||
#else
|
#else
|
||||||
wire_->begin(static_cast<int>(sda_pin_), static_cast<int>(scl_pin_));
|
wire_->begin(static_cast<int>(sda_pin_), static_cast<int>(scl_pin_));
|
||||||
#endif
|
#endif
|
||||||
|
if (timeout_ > 0) { // if timeout specified in yaml
|
||||||
|
#if defined(USE_ESP32)
|
||||||
|
// https://github.com/espressif/arduino-esp32/blob/master/libraries/Wire/src/Wire.cpp
|
||||||
|
wire_->setTimeOut(timeout_ / 1000); // unit: ms
|
||||||
|
#elif defined(USE_ESP8266)
|
||||||
|
// https://github.com/esp8266/Arduino/blob/master/libraries/Wire/Wire.h
|
||||||
|
wire_->setClockStretchLimit(timeout_); // unit: us
|
||||||
|
#elif defined(USE_RP2040)
|
||||||
|
// https://github.com/earlephilhower/ArduinoCore-API/blob/e37df85425e0ac020bfad226d927f9b00d2e0fb7/api/Stream.h
|
||||||
|
wire_->setTimeout(timeout_ / 1000); // unit: ms
|
||||||
|
#endif
|
||||||
|
}
|
||||||
wire_->setClock(frequency_);
|
wire_->setClock(frequency_);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -60,6 +72,15 @@ void ArduinoI2CBus::dump_config() {
|
||||||
ESP_LOGCONFIG(TAG, " SDA Pin: GPIO%u", this->sda_pin_);
|
ESP_LOGCONFIG(TAG, " SDA Pin: GPIO%u", this->sda_pin_);
|
||||||
ESP_LOGCONFIG(TAG, " SCL Pin: GPIO%u", this->scl_pin_);
|
ESP_LOGCONFIG(TAG, " SCL Pin: GPIO%u", this->scl_pin_);
|
||||||
ESP_LOGCONFIG(TAG, " Frequency: %u Hz", this->frequency_);
|
ESP_LOGCONFIG(TAG, " Frequency: %u Hz", this->frequency_);
|
||||||
|
if (timeout_ > 0) {
|
||||||
|
#if defined(USE_ESP32)
|
||||||
|
ESP_LOGCONFIG(TAG, " Timeout: %u ms", this->timeout_ / 1000);
|
||||||
|
#elif defined(USE_ESP8266)
|
||||||
|
ESP_LOGCONFIG(TAG, " Timeout: %u us", this->timeout_);
|
||||||
|
#elif defined(USE_RP2040)
|
||||||
|
ESP_LOGCONFIG(TAG, " Timeout: %u ms", this->timeout_ / 1000);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
switch (this->recovery_result_) {
|
switch (this->recovery_result_) {
|
||||||
case RECOVERY_COMPLETED:
|
case RECOVERY_COMPLETED:
|
||||||
ESP_LOGCONFIG(TAG, " Recovery: bus successfully recovered");
|
ESP_LOGCONFIG(TAG, " Recovery: bus successfully recovered");
|
||||||
|
|
|
@ -27,6 +27,7 @@ class ArduinoI2CBus : public I2CBus, public Component {
|
||||||
void set_sda_pin(uint8_t sda_pin) { sda_pin_ = sda_pin; }
|
void set_sda_pin(uint8_t sda_pin) { sda_pin_ = sda_pin; }
|
||||||
void set_scl_pin(uint8_t scl_pin) { scl_pin_ = scl_pin; }
|
void set_scl_pin(uint8_t scl_pin) { scl_pin_ = scl_pin; }
|
||||||
void set_frequency(uint32_t frequency) { frequency_ = frequency; }
|
void set_frequency(uint32_t frequency) { frequency_ = frequency; }
|
||||||
|
void set_timeout(uint32_t timeout) { timeout_ = timeout; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void recover_();
|
void recover_();
|
||||||
|
@ -38,6 +39,7 @@ class ArduinoI2CBus : public I2CBus, public Component {
|
||||||
uint8_t sda_pin_;
|
uint8_t sda_pin_;
|
||||||
uint8_t scl_pin_;
|
uint8_t scl_pin_;
|
||||||
uint32_t frequency_;
|
uint32_t frequency_;
|
||||||
|
uint32_t timeout_ = 0;
|
||||||
bool initialized_ = false;
|
bool initialized_ = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
#ifdef USE_ESP_IDF
|
#ifdef USE_ESP_IDF
|
||||||
|
|
||||||
#include "i2c_bus_esp_idf.h"
|
#include "i2c_bus_esp_idf.h"
|
||||||
#include "esphome/core/hal.h"
|
|
||||||
#include "esphome/core/log.h"
|
|
||||||
#include "esphome/core/helpers.h"
|
|
||||||
#include "esphome/core/application.h"
|
|
||||||
#include <cstring>
|
|
||||||
#include <cinttypes>
|
#include <cinttypes>
|
||||||
|
#include <cstring>
|
||||||
|
#include "esphome/core/application.h"
|
||||||
|
#include "esphome/core/hal.h"
|
||||||
|
#include "esphome/core/helpers.h"
|
||||||
|
#include "esphome/core/log.h"
|
||||||
|
|
||||||
namespace esphome {
|
namespace esphome {
|
||||||
namespace i2c {
|
namespace i2c {
|
||||||
|
@ -45,6 +45,20 @@ void IDFI2CBus::setup() {
|
||||||
this->mark_failed();
|
this->mark_failed();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (timeout_ > 0) { // if timeout specified in yaml:
|
||||||
|
if (timeout_ > 13000) {
|
||||||
|
ESP_LOGW(TAG, "i2c timeout of %" PRIu32 "us greater than max of 13ms on esp-idf, setting to max", timeout_);
|
||||||
|
timeout_ = 13000;
|
||||||
|
}
|
||||||
|
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 %d ticks (%d us)", timeout_ * 80, timeout_);
|
||||||
|
}
|
||||||
|
}
|
||||||
err = i2c_driver_install(port_, I2C_MODE_MASTER, 0, 0, ESP_INTR_FLAG_IRAM);
|
err = i2c_driver_install(port_, I2C_MODE_MASTER, 0, 0, ESP_INTR_FLAG_IRAM);
|
||||||
if (err != ESP_OK) {
|
if (err != ESP_OK) {
|
||||||
ESP_LOGW(TAG, "i2c_driver_install failed: %s", esp_err_to_name(err));
|
ESP_LOGW(TAG, "i2c_driver_install failed: %s", esp_err_to_name(err));
|
||||||
|
@ -62,6 +76,9 @@ void IDFI2CBus::dump_config() {
|
||||||
ESP_LOGCONFIG(TAG, " SDA Pin: GPIO%u", this->sda_pin_);
|
ESP_LOGCONFIG(TAG, " SDA Pin: GPIO%u", this->sda_pin_);
|
||||||
ESP_LOGCONFIG(TAG, " SCL Pin: GPIO%u", this->scl_pin_);
|
ESP_LOGCONFIG(TAG, " SCL Pin: GPIO%u", this->scl_pin_);
|
||||||
ESP_LOGCONFIG(TAG, " Frequency: %" PRIu32 " Hz", this->frequency_);
|
ESP_LOGCONFIG(TAG, " Frequency: %" PRIu32 " Hz", this->frequency_);
|
||||||
|
if (timeout_ > 0) {
|
||||||
|
ESP_LOGCONFIG(TAG, " Timeout: %" PRIu32 "us", this->timeout_);
|
||||||
|
}
|
||||||
switch (this->recovery_result_) {
|
switch (this->recovery_result_) {
|
||||||
case RECOVERY_COMPLETED:
|
case RECOVERY_COMPLETED:
|
||||||
ESP_LOGCONFIG(TAG, " Recovery: bus successfully recovered");
|
ESP_LOGCONFIG(TAG, " Recovery: bus successfully recovered");
|
||||||
|
@ -127,6 +144,8 @@ ErrorCode IDFI2CBus::readv(uint8_t address, ReadBuffer *buffers, size_t cnt) {
|
||||||
return ERROR_UNKNOWN;
|
return ERROR_UNKNOWN;
|
||||||
}
|
}
|
||||||
err = i2c_master_cmd_begin(port_, cmd, 20 / portTICK_PERIOD_MS);
|
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);
|
i2c_cmd_link_delete(cmd);
|
||||||
if (err == ESP_FAIL) {
|
if (err == ESP_FAIL) {
|
||||||
// transfer not acked
|
// transfer not acked
|
||||||
|
|
|
@ -29,6 +29,7 @@ class IDFI2CBus : public I2CBus, public Component {
|
||||||
void set_scl_pin(uint8_t scl_pin) { scl_pin_ = scl_pin; }
|
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_scl_pullup_enabled(bool scl_pullup_enabled) { scl_pullup_enabled_ = scl_pullup_enabled; }
|
||||||
void set_frequency(uint32_t frequency) { frequency_ = frequency; }
|
void set_frequency(uint32_t frequency) { frequency_ = frequency; }
|
||||||
|
void set_timeout(uint32_t timeout) { timeout_ = timeout; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void recover_();
|
void recover_();
|
||||||
|
@ -41,6 +42,7 @@ class IDFI2CBus : public I2CBus, public Component {
|
||||||
uint8_t scl_pin_;
|
uint8_t scl_pin_;
|
||||||
bool scl_pullup_enabled_;
|
bool scl_pullup_enabled_;
|
||||||
uint32_t frequency_;
|
uint32_t frequency_;
|
||||||
|
uint32_t timeout_ = 0;
|
||||||
bool initialized_ = false;
|
bool initialized_ = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue