mirror of
https://github.com/esphome/esphome.git
synced 2026-02-07 03:41:37 +08:00
Merge remote-tracking branch 'upstream/dev' into integration
This commit is contained in:
@@ -532,7 +532,7 @@ esphome/components/uart/packet_transport/* @clydebarrow
|
||||
esphome/components/udp/* @clydebarrow
|
||||
esphome/components/ufire_ec/* @pvizeli
|
||||
esphome/components/ufire_ise/* @pvizeli
|
||||
esphome/components/ultrasonic/* @OttoWinter
|
||||
esphome/components/ultrasonic/* @ssieb @swoboda1337
|
||||
esphome/components/update/* @jesserockz
|
||||
esphome/components/uponor_smatrix/* @kroimon
|
||||
esphome/components/usb_cdc_acm/* @kbx81
|
||||
|
||||
@@ -74,10 +74,9 @@ void ADCSensor::setup() {
|
||||
if (this->calibration_handle_ == nullptr) {
|
||||
adc_cali_handle_t handle = nullptr;
|
||||
|
||||
#if USE_ESP32_VARIANT_ESP32C2 || USE_ESP32_VARIANT_ESP32C3 || USE_ESP32_VARIANT_ESP32C5 || \
|
||||
USE_ESP32_VARIANT_ESP32C6 || USE_ESP32_VARIANT_ESP32C61 || USE_ESP32_VARIANT_ESP32H2 || \
|
||||
USE_ESP32_VARIANT_ESP32P4 || USE_ESP32_VARIANT_ESP32S3
|
||||
// RISC-V variants and S3 use curve fitting calibration
|
||||
#if USE_ESP32_VARIANT_ESP32C3 || USE_ESP32_VARIANT_ESP32C5 || USE_ESP32_VARIANT_ESP32C6 || \
|
||||
USE_ESP32_VARIANT_ESP32C61 || USE_ESP32_VARIANT_ESP32H2 || USE_ESP32_VARIANT_ESP32P4 || USE_ESP32_VARIANT_ESP32S3
|
||||
// RISC-V variants (except C2) and S3 use curve fitting calibration
|
||||
adc_cali_curve_fitting_config_t cali_config = {}; // Zero initialize first
|
||||
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 3, 0)
|
||||
cali_config.chan = this->channel_;
|
||||
@@ -95,14 +94,14 @@ void ADCSensor::setup() {
|
||||
ESP_LOGW(TAG, "Curve fitting calibration failed with error %d, will use uncalibrated readings", err);
|
||||
this->setup_flags_.calibration_complete = false;
|
||||
}
|
||||
#else // Other ESP32 variants use line fitting calibration
|
||||
#else // ESP32, ESP32-S2, and ESP32-C2 use line fitting calibration
|
||||
adc_cali_line_fitting_config_t cali_config = {
|
||||
.unit_id = this->adc_unit_,
|
||||
.atten = this->attenuation_,
|
||||
.bitwidth = ADC_BITWIDTH_DEFAULT,
|
||||
#if !defined(USE_ESP32_VARIANT_ESP32S2)
|
||||
#if !defined(USE_ESP32_VARIANT_ESP32S2) && !defined(USE_ESP32_VARIANT_ESP32C2)
|
||||
.default_vref = 1100, // Default reference voltage in mV
|
||||
#endif // !defined(USE_ESP32_VARIANT_ESP32S2)
|
||||
#endif // !defined(USE_ESP32_VARIANT_ESP32S2) && !defined(USE_ESP32_VARIANT_ESP32C2)
|
||||
};
|
||||
err = adc_cali_create_scheme_line_fitting(&cali_config, &handle);
|
||||
if (err == ESP_OK) {
|
||||
@@ -113,7 +112,7 @@ void ADCSensor::setup() {
|
||||
ESP_LOGW(TAG, "Line fitting calibration failed with error %d, will use uncalibrated readings", err);
|
||||
this->setup_flags_.calibration_complete = false;
|
||||
}
|
||||
#endif // USE_ESP32_VARIANT_ESP32C2 || ESP32C3 || ESP32C5 || ESP32C6 || ESP32C61 || ESP32H2 || ESP32P4 || ESP32S3
|
||||
#endif // ESP32C3 || ESP32C5 || ESP32C6 || ESP32C61 || ESP32H2 || ESP32P4 || ESP32S3
|
||||
}
|
||||
|
||||
this->setup_flags_.init_complete = true;
|
||||
@@ -185,13 +184,12 @@ float ADCSensor::sample_fixed_attenuation_() {
|
||||
} else {
|
||||
ESP_LOGW(TAG, "ADC calibration conversion failed with error %d, disabling calibration", err);
|
||||
if (this->calibration_handle_ != nullptr) {
|
||||
#if USE_ESP32_VARIANT_ESP32C2 || USE_ESP32_VARIANT_ESP32C3 || USE_ESP32_VARIANT_ESP32C5 || \
|
||||
USE_ESP32_VARIANT_ESP32C6 || USE_ESP32_VARIANT_ESP32C61 || USE_ESP32_VARIANT_ESP32H2 || \
|
||||
USE_ESP32_VARIANT_ESP32P4 || USE_ESP32_VARIANT_ESP32S3
|
||||
#if USE_ESP32_VARIANT_ESP32C3 || USE_ESP32_VARIANT_ESP32C5 || USE_ESP32_VARIANT_ESP32C6 || \
|
||||
USE_ESP32_VARIANT_ESP32C61 || USE_ESP32_VARIANT_ESP32H2 || USE_ESP32_VARIANT_ESP32P4 || USE_ESP32_VARIANT_ESP32S3
|
||||
adc_cali_delete_scheme_curve_fitting(this->calibration_handle_);
|
||||
#else // Other ESP32 variants use line fitting calibration
|
||||
adc_cali_delete_scheme_line_fitting(this->calibration_handle_);
|
||||
#endif // USE_ESP32_VARIANT_ESP32C2 || ESP32C3 || ESP32C5 || ESP32C6 || ESP32C61 || ESP32H2 || ESP32P4 || ESP32S3
|
||||
#endif // ESP32C3 || ESP32C5 || ESP32C6 || ESP32C61 || ESP32H2 || ESP32P4 || ESP32S3
|
||||
this->calibration_handle_ = nullptr;
|
||||
}
|
||||
}
|
||||
@@ -219,9 +217,8 @@ float ADCSensor::sample_autorange_() {
|
||||
// Need to recalibrate for the new attenuation
|
||||
if (this->calibration_handle_ != nullptr) {
|
||||
// Delete old calibration handle
|
||||
#if USE_ESP32_VARIANT_ESP32C2 || USE_ESP32_VARIANT_ESP32C3 || USE_ESP32_VARIANT_ESP32C5 || \
|
||||
USE_ESP32_VARIANT_ESP32C6 || USE_ESP32_VARIANT_ESP32C61 || USE_ESP32_VARIANT_ESP32H2 || \
|
||||
USE_ESP32_VARIANT_ESP32P4 || USE_ESP32_VARIANT_ESP32S3
|
||||
#if USE_ESP32_VARIANT_ESP32C3 || USE_ESP32_VARIANT_ESP32C5 || USE_ESP32_VARIANT_ESP32C6 || \
|
||||
USE_ESP32_VARIANT_ESP32C61 || USE_ESP32_VARIANT_ESP32H2 || USE_ESP32_VARIANT_ESP32P4 || USE_ESP32_VARIANT_ESP32S3
|
||||
adc_cali_delete_scheme_curve_fitting(this->calibration_handle_);
|
||||
#else
|
||||
adc_cali_delete_scheme_line_fitting(this->calibration_handle_);
|
||||
@@ -232,9 +229,8 @@ float ADCSensor::sample_autorange_() {
|
||||
// Create new calibration handle for this attenuation
|
||||
adc_cali_handle_t handle = nullptr;
|
||||
|
||||
#if USE_ESP32_VARIANT_ESP32C2 || USE_ESP32_VARIANT_ESP32C3 || USE_ESP32_VARIANT_ESP32C5 || \
|
||||
USE_ESP32_VARIANT_ESP32C6 || USE_ESP32_VARIANT_ESP32C61 || USE_ESP32_VARIANT_ESP32H2 || \
|
||||
USE_ESP32_VARIANT_ESP32P4 || USE_ESP32_VARIANT_ESP32S3
|
||||
#if USE_ESP32_VARIANT_ESP32C3 || USE_ESP32_VARIANT_ESP32C5 || USE_ESP32_VARIANT_ESP32C6 || \
|
||||
USE_ESP32_VARIANT_ESP32C61 || USE_ESP32_VARIANT_ESP32H2 || USE_ESP32_VARIANT_ESP32P4 || USE_ESP32_VARIANT_ESP32S3
|
||||
adc_cali_curve_fitting_config_t cali_config = {};
|
||||
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 3, 0)
|
||||
cali_config.chan = this->channel_;
|
||||
@@ -251,7 +247,7 @@ float ADCSensor::sample_autorange_() {
|
||||
.unit_id = this->adc_unit_,
|
||||
.atten = atten,
|
||||
.bitwidth = ADC_BITWIDTH_DEFAULT,
|
||||
#if !defined(USE_ESP32_VARIANT_ESP32S2)
|
||||
#if !defined(USE_ESP32_VARIANT_ESP32S2) && !defined(USE_ESP32_VARIANT_ESP32C2)
|
||||
.default_vref = 1100,
|
||||
#endif
|
||||
};
|
||||
@@ -268,9 +264,8 @@ float ADCSensor::sample_autorange_() {
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGW(TAG, "ADC read failed in autorange with error %d", err);
|
||||
if (handle != nullptr) {
|
||||
#if USE_ESP32_VARIANT_ESP32C2 || USE_ESP32_VARIANT_ESP32C3 || USE_ESP32_VARIANT_ESP32C5 || \
|
||||
USE_ESP32_VARIANT_ESP32C6 || USE_ESP32_VARIANT_ESP32C61 || USE_ESP32_VARIANT_ESP32H2 || \
|
||||
USE_ESP32_VARIANT_ESP32P4 || USE_ESP32_VARIANT_ESP32S3
|
||||
#if USE_ESP32_VARIANT_ESP32C3 || USE_ESP32_VARIANT_ESP32C5 || USE_ESP32_VARIANT_ESP32C6 || \
|
||||
USE_ESP32_VARIANT_ESP32C61 || USE_ESP32_VARIANT_ESP32H2 || USE_ESP32_VARIANT_ESP32P4 || USE_ESP32_VARIANT_ESP32S3
|
||||
adc_cali_delete_scheme_curve_fitting(handle);
|
||||
#else
|
||||
adc_cali_delete_scheme_line_fitting(handle);
|
||||
@@ -291,9 +286,8 @@ float ADCSensor::sample_autorange_() {
|
||||
ESP_LOGVV(TAG, "Autorange atten=%d: UNCALIBRATED FALLBACK - raw=%d -> %.6fV (3.3V ref)", atten, raw, voltage);
|
||||
}
|
||||
// Clean up calibration handle
|
||||
#if USE_ESP32_VARIANT_ESP32C2 || USE_ESP32_VARIANT_ESP32C3 || USE_ESP32_VARIANT_ESP32C5 || \
|
||||
USE_ESP32_VARIANT_ESP32C6 || USE_ESP32_VARIANT_ESP32C61 || USE_ESP32_VARIANT_ESP32H2 || \
|
||||
USE_ESP32_VARIANT_ESP32P4 || USE_ESP32_VARIANT_ESP32S3
|
||||
#if USE_ESP32_VARIANT_ESP32C3 || USE_ESP32_VARIANT_ESP32C5 || USE_ESP32_VARIANT_ESP32C6 || \
|
||||
USE_ESP32_VARIANT_ESP32C61 || USE_ESP32_VARIANT_ESP32H2 || USE_ESP32_VARIANT_ESP32P4 || USE_ESP32_VARIANT_ESP32S3
|
||||
adc_cali_delete_scheme_curve_fitting(handle);
|
||||
#else
|
||||
adc_cali_delete_scheme_line_fitting(handle);
|
||||
|
||||
@@ -2,6 +2,28 @@
|
||||
#include "esphome/core/helpers.h"
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
// Helper macros
|
||||
#define PARSE_INT(field, field_name) \
|
||||
{ \
|
||||
get_token(token_buf); \
|
||||
auto val = parse_number<int>(token_buf); \
|
||||
if (val.has_value()) { \
|
||||
(field) = val.value(); \
|
||||
} else { \
|
||||
ESP_LOGD(TAG, "invalid " field_name " in line %s", buffer.substr(0, buffer.size() - 2).c_str()); \
|
||||
return; \
|
||||
} \
|
||||
}
|
||||
|
||||
#define PARSE_STR(field, field_name) \
|
||||
{ \
|
||||
get_token(field); \
|
||||
if (strlen(field) < 2) { \
|
||||
ESP_LOGD(TAG, "too short " field_name " in line %s", buffer.substr(0, buffer.size() - 2).c_str()); \
|
||||
return; \
|
||||
} \
|
||||
}
|
||||
|
||||
namespace esphome {
|
||||
namespace pylontech {
|
||||
|
||||
@@ -64,33 +86,106 @@ void PylontechComponent::loop() {
|
||||
void PylontechComponent::process_line_(std::string &buffer) {
|
||||
ESP_LOGV(TAG, "Read from serial: %s", buffer.substr(0, buffer.size() - 2).c_str());
|
||||
// clang-format off
|
||||
// example line to parse:
|
||||
// Power Volt Curr Tempr Tlow Thigh Vlow Vhigh Base.St Volt.St Curr.St Temp.St Coulomb Time B.V.St B.T.St MosTempr M.T.St
|
||||
// 1 50548 8910 25000 24200 25000 3368 3371 Charge Normal Normal Normal 97% 2021-06-30 20:49:45 Normal Normal 22700 Normal
|
||||
// example lines to parse:
|
||||
// Power Volt Curr Tempr Tlow Thigh Vlow Vhigh Base.St Volt.St Curr.St Temp.St Coulomb Time B.V.St B.T.St MosTempr M.T.St
|
||||
// 1 50548 8910 25000 24200 25000 3368 3371 Charge Normal Normal Normal 97% 2021-06-30 20:49:45 Normal Normal 22700 Normal
|
||||
// 1 46012 1255 9100 5300 5500 3047 3091 SysError Low Normal Normal 4% 2025-11-28 17:56:33 Low Normal 7800 Normal
|
||||
// newer firmware example:
|
||||
// Power Volt Curr Tempr Tlow Tlow.Id Thigh Thigh.Id Vlow Vlow.Id Vhigh Vhigh.Id Base.St Volt.St Curr.St Temp.St Coulomb Time B.V.St B.T.St MosTempr M.T.St SysAlarm.St
|
||||
// 1 49405 0 17600 13700 8 14500 0 3293 2 3294 0 Idle Normal Normal Normal 60% 2025-12-05 00:53:41 Normal Normal 16600 Normal Normal
|
||||
// clang-format on
|
||||
|
||||
PylontechListener::LineContents l{};
|
||||
char mostempr_s[6];
|
||||
const int parsed = sscanf( // NOLINT
|
||||
buffer.c_str(), "%d %d %d %d %d %d %d %d %7s %7s %7s %7s %d%% %*d-%*d-%*d %*d:%*d:%*d %*s %*s %5s %*s", // NOLINT
|
||||
&l.bat_num, &l.volt, &l.curr, &l.tempr, &l.tlow, &l.thigh, &l.vlow, &l.vhigh, l.base_st, l.volt_st, // NOLINT
|
||||
l.curr_st, l.temp_st, &l.coulomb, mostempr_s); // NOLINT
|
||||
|
||||
if (l.bat_num <= 0) {
|
||||
ESP_LOGD(TAG, "invalid bat_num in line %s", buffer.substr(0, buffer.size() - 2).c_str());
|
||||
return;
|
||||
const char *cursor = buffer.c_str();
|
||||
char token_buf[TEXT_SENSOR_MAX_LEN] = {0};
|
||||
|
||||
// Helper Lambda to extract tokens
|
||||
auto get_token = [&](char *token_buf) -> void {
|
||||
// Skip leading whitespace
|
||||
while (*cursor == ' ' || *cursor == '\t') {
|
||||
cursor++;
|
||||
}
|
||||
|
||||
if (*cursor == '\0') {
|
||||
token_buf[0] = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
const char *start = cursor;
|
||||
|
||||
// Find end of field
|
||||
while (*cursor != '\0' && *cursor != ' ' && *cursor != '\t' && *cursor != '\r') {
|
||||
cursor++;
|
||||
}
|
||||
|
||||
size_t token_len = std::min(static_cast<size_t>(cursor - start), static_cast<size_t>(TEXT_SENSOR_MAX_LEN - 1));
|
||||
memcpy(token_buf, start, token_len);
|
||||
token_buf[token_len] = 0;
|
||||
};
|
||||
|
||||
{
|
||||
get_token(token_buf);
|
||||
auto val = parse_number<int>(token_buf);
|
||||
if (val.has_value() && val.value() > 0) {
|
||||
l.bat_num = val.value();
|
||||
} else if (strcmp(token_buf, "Power") == 0) {
|
||||
// header line i.e. "Power Volt Curr" and so on
|
||||
this->has_tlow_id_ = buffer.find("Tlow.Id") != std::string::npos;
|
||||
ESP_LOGD(TAG, "header line %s Tlow.Id: %s", this->has_tlow_id_ ? "with" : "without",
|
||||
buffer.substr(0, buffer.size() - 2).c_str());
|
||||
return;
|
||||
} else {
|
||||
ESP_LOGD(TAG, "unknown line %s", buffer.substr(0, buffer.size() - 2).c_str());
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (parsed != 14) {
|
||||
ESP_LOGW(TAG, "invalid line: found only %d items in %s", parsed, buffer.substr(0, buffer.size() - 2).c_str());
|
||||
return;
|
||||
PARSE_INT(l.volt, "Volt");
|
||||
PARSE_INT(l.curr, "Curr");
|
||||
PARSE_INT(l.tempr, "Tempr");
|
||||
PARSE_INT(l.tlow, "Tlow");
|
||||
if (this->has_tlow_id_) {
|
||||
get_token(token_buf); // Skip Tlow.Id
|
||||
}
|
||||
auto mostempr_parsed = parse_number<int>(mostempr_s);
|
||||
if (mostempr_parsed.has_value()) {
|
||||
l.mostempr = mostempr_parsed.value();
|
||||
} else {
|
||||
l.mostempr = -300;
|
||||
ESP_LOGW(TAG, "bat_num %d: received no mostempr", l.bat_num);
|
||||
PARSE_INT(l.thigh, "Thigh");
|
||||
if (this->has_tlow_id_) {
|
||||
get_token(token_buf); // Skip Thigh.Id
|
||||
}
|
||||
PARSE_INT(l.vlow, "Vlow");
|
||||
if (this->has_tlow_id_) {
|
||||
get_token(token_buf); // Skip Vlow.Id
|
||||
}
|
||||
PARSE_INT(l.vhigh, "Vhigh");
|
||||
if (this->has_tlow_id_) {
|
||||
get_token(token_buf); // Skip Vhigh.Id
|
||||
}
|
||||
PARSE_STR(l.base_st, "Base.St");
|
||||
PARSE_STR(l.volt_st, "Volt.St");
|
||||
PARSE_STR(l.curr_st, "Curr.St");
|
||||
PARSE_STR(l.temp_st, "Temp.St");
|
||||
{
|
||||
get_token(token_buf);
|
||||
for (char &i : token_buf) {
|
||||
if (i == '%') {
|
||||
i = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
auto coul_val = parse_number<int>(token_buf);
|
||||
if (coul_val.has_value()) {
|
||||
l.coulomb = coul_val.value();
|
||||
} else {
|
||||
ESP_LOGD(TAG, "invalid Coulomb in line %s", buffer.substr(0, buffer.size() - 2).c_str());
|
||||
return;
|
||||
}
|
||||
}
|
||||
get_token(token_buf); // Skip Date
|
||||
get_token(token_buf); // Skip Time
|
||||
get_token(token_buf); // Skip B.V.St
|
||||
get_token(token_buf); // Skip B.T.St
|
||||
PARSE_INT(l.mostempr, "Mostempr");
|
||||
|
||||
ESP_LOGD(TAG, "successful line %s", buffer.substr(0, buffer.size() - 2).c_str());
|
||||
|
||||
for (PylontechListener *listener : this->listeners_) {
|
||||
listener->on_line_read(&l);
|
||||
@@ -99,3 +194,6 @@ void PylontechComponent::process_line_(std::string &buffer) {
|
||||
|
||||
} // namespace pylontech
|
||||
} // namespace esphome
|
||||
|
||||
#undef PARSE_INT
|
||||
#undef PARSE_STR
|
||||
|
||||
@@ -8,14 +8,14 @@ namespace esphome {
|
||||
namespace pylontech {
|
||||
|
||||
static const uint8_t NUM_BUFFERS = 20;
|
||||
static const uint8_t TEXT_SENSOR_MAX_LEN = 8;
|
||||
static const uint8_t TEXT_SENSOR_MAX_LEN = 14;
|
||||
|
||||
class PylontechListener {
|
||||
public:
|
||||
struct LineContents {
|
||||
int bat_num = 0, volt, curr, tempr, tlow, thigh, vlow, vhigh, coulomb, mostempr;
|
||||
char base_st[TEXT_SENSOR_MAX_LEN], volt_st[TEXT_SENSOR_MAX_LEN], curr_st[TEXT_SENSOR_MAX_LEN],
|
||||
temp_st[TEXT_SENSOR_MAX_LEN];
|
||||
char base_st[TEXT_SENSOR_MAX_LEN] = {0}, volt_st[TEXT_SENSOR_MAX_LEN] = {0}, curr_st[TEXT_SENSOR_MAX_LEN] = {0},
|
||||
temp_st[TEXT_SENSOR_MAX_LEN] = {0};
|
||||
};
|
||||
|
||||
virtual void on_line_read(LineContents *line);
|
||||
@@ -43,6 +43,7 @@ class PylontechComponent : public PollingComponent, public uart::UARTDevice {
|
||||
std::string buffer_[NUM_BUFFERS];
|
||||
int buffer_index_write_ = 0;
|
||||
int buffer_index_read_ = 0;
|
||||
bool has_tlow_id_ = false;
|
||||
|
||||
std::vector<PylontechListener *> listeners_{};
|
||||
};
|
||||
|
||||
@@ -1 +1 @@
|
||||
CODEOWNERS = ["@OttoWinter"]
|
||||
CODEOWNERS = ["@swoboda1337", "@ssieb"]
|
||||
|
||||
@@ -34,7 +34,7 @@ CONFIG_SCHEMA = (
|
||||
{
|
||||
cv.Required(CONF_TRIGGER_PIN): pins.internal_gpio_output_pin_schema,
|
||||
cv.Required(CONF_ECHO_PIN): pins.internal_gpio_input_pin_schema,
|
||||
cv.Optional(CONF_TIMEOUT): cv.distance,
|
||||
cv.Optional(CONF_TIMEOUT, default="2m"): cv.distance,
|
||||
cv.Optional(
|
||||
CONF_PULSE_TIME, default="10us"
|
||||
): cv.positive_time_period_microseconds,
|
||||
@@ -52,12 +52,5 @@ async def to_code(config):
|
||||
cg.add(var.set_trigger_pin(trigger))
|
||||
echo = await cg.gpio_pin_expression(config[CONF_ECHO_PIN])
|
||||
cg.add(var.set_echo_pin(echo))
|
||||
|
||||
# Remove before 2026.8.0
|
||||
if CONF_TIMEOUT in config:
|
||||
_LOGGER.warning(
|
||||
"'timeout' option is deprecated and will be removed in 2026.8.0. "
|
||||
"The option has no effect and can be safely removed."
|
||||
)
|
||||
|
||||
cg.add(var.set_timeout_us(config[CONF_TIMEOUT] / (0.000343 / 2)))
|
||||
cg.add(var.set_pulse_time_us(config[CONF_PULSE_TIME]))
|
||||
|
||||
@@ -6,12 +6,11 @@ namespace esphome::ultrasonic {
|
||||
|
||||
static const char *const TAG = "ultrasonic.sensor";
|
||||
|
||||
static constexpr uint32_t DEBOUNCE_US = 50; // Ignore edges within 50us (noise filtering)
|
||||
static constexpr uint32_t MEASUREMENT_TIMEOUT_US = 80000; // Maximum time to wait for measurement completion
|
||||
static constexpr uint32_t START_TIMEOUT_US = 40000; // Maximum time to wait for echo pulse to start
|
||||
|
||||
void IRAM_ATTR UltrasonicSensorStore::gpio_intr(UltrasonicSensorStore *arg) {
|
||||
uint32_t now = micros();
|
||||
if (!arg->echo_start || (now - arg->echo_start_us) <= DEBOUNCE_US) {
|
||||
if (arg->echo_pin_isr.digital_read()) {
|
||||
arg->echo_start_us = now;
|
||||
arg->echo_start = true;
|
||||
} else {
|
||||
@@ -38,6 +37,7 @@ void UltrasonicSensorComponent::setup() {
|
||||
this->trigger_pin_->digital_write(false);
|
||||
this->trigger_pin_isr_ = this->trigger_pin_->to_isr();
|
||||
this->echo_pin_->setup();
|
||||
this->store_.echo_pin_isr = this->echo_pin_->to_isr();
|
||||
this->echo_pin_->attach_interrupt(UltrasonicSensorStore::gpio_intr, &this->store_, gpio::INTERRUPT_ANY_EDGE);
|
||||
}
|
||||
|
||||
@@ -53,29 +53,55 @@ void UltrasonicSensorComponent::loop() {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this->store_.echo_start) {
|
||||
uint32_t elapsed = micros() - this->measurement_start_us_;
|
||||
if (elapsed >= START_TIMEOUT_US) {
|
||||
ESP_LOGW(TAG, "'%s' - Measurement start timed out", this->name_.c_str());
|
||||
this->publish_state(NAN);
|
||||
this->measurement_pending_ = false;
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
uint32_t elapsed;
|
||||
if (this->store_.echo_end) {
|
||||
elapsed = this->store_.echo_end_us - this->store_.echo_start_us;
|
||||
} else {
|
||||
elapsed = micros() - this->store_.echo_start_us;
|
||||
}
|
||||
if (elapsed >= this->timeout_us_) {
|
||||
ESP_LOGD(TAG, "'%s' - Measurement pulse timed out after %" PRIu32 "us", this->name_.c_str(), elapsed);
|
||||
this->publish_state(NAN);
|
||||
this->measurement_pending_ = false;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (this->store_.echo_end) {
|
||||
uint32_t pulse_duration = this->store_.echo_end_us - this->store_.echo_start_us;
|
||||
ESP_LOGV(TAG, "Echo took %" PRIu32 "us", pulse_duration);
|
||||
float result = UltrasonicSensorComponent::us_to_m(pulse_duration);
|
||||
ESP_LOGD(TAG, "'%s' - Got distance: %.3f m", this->name_.c_str(), result);
|
||||
float result;
|
||||
if (this->store_.echo_start) {
|
||||
uint32_t pulse_duration = this->store_.echo_end_us - this->store_.echo_start_us;
|
||||
ESP_LOGV(TAG, "pulse start took %" PRIu32 "us, echo took %" PRIu32 "us",
|
||||
this->store_.echo_start_us - this->measurement_start_us_, pulse_duration);
|
||||
result = UltrasonicSensorComponent::us_to_m(pulse_duration);
|
||||
ESP_LOGD(TAG, "'%s' - Got distance: %.3f m", this->name_.c_str(), result);
|
||||
} else {
|
||||
ESP_LOGW(TAG, "'%s' - pulse end before pulse start, does the echo pin need to be inverted?", this->name_.c_str());
|
||||
result = NAN;
|
||||
}
|
||||
this->publish_state(result);
|
||||
this->measurement_pending_ = false;
|
||||
return;
|
||||
}
|
||||
|
||||
uint32_t elapsed = micros() - this->measurement_start_us_;
|
||||
if (elapsed >= MEASUREMENT_TIMEOUT_US) {
|
||||
ESP_LOGD(TAG, "'%s' - Measurement timed out after %" PRIu32 "us", this->name_.c_str(), elapsed);
|
||||
this->publish_state(NAN);
|
||||
this->measurement_pending_ = false;
|
||||
}
|
||||
}
|
||||
|
||||
void UltrasonicSensorComponent::dump_config() {
|
||||
LOG_SENSOR("", "Ultrasonic Sensor", this);
|
||||
LOG_PIN(" Echo Pin: ", this->echo_pin_);
|
||||
LOG_PIN(" Trigger Pin: ", this->trigger_pin_);
|
||||
ESP_LOGCONFIG(TAG, " Pulse time: %" PRIu32 " us", this->pulse_time_us_);
|
||||
ESP_LOGCONFIG(TAG,
|
||||
" Pulse time: %" PRIu32 " µs\n"
|
||||
" Timeout: %" PRIu32 " µs",
|
||||
this->pulse_time_us_, this->timeout_us_);
|
||||
LOG_UPDATE_INTERVAL(this);
|
||||
}
|
||||
|
||||
|
||||
@@ -11,6 +11,8 @@ namespace esphome::ultrasonic {
|
||||
struct UltrasonicSensorStore {
|
||||
static void gpio_intr(UltrasonicSensorStore *arg);
|
||||
|
||||
ISRInternalGPIOPin echo_pin_isr;
|
||||
volatile uint32_t wait_start_us{0};
|
||||
volatile uint32_t echo_start_us{0};
|
||||
volatile uint32_t echo_end_us{0};
|
||||
volatile bool echo_start{false};
|
||||
@@ -27,6 +29,8 @@ class UltrasonicSensorComponent : public sensor::Sensor, public PollingComponent
|
||||
void dump_config() override;
|
||||
void update() override;
|
||||
|
||||
/// Set the maximum time in µs to wait for the echo to return
|
||||
void set_timeout_us(uint32_t timeout_us) { this->timeout_us_ = timeout_us; }
|
||||
/// Set the time in µs the trigger pin should be enabled for in µs, defaults to 10µs (for HC-SR04)
|
||||
void set_pulse_time_us(uint32_t pulse_time_us) { this->pulse_time_us_ = pulse_time_us; }
|
||||
|
||||
@@ -39,6 +43,7 @@ class UltrasonicSensorComponent : public sensor::Sensor, public PollingComponent
|
||||
ISRInternalGPIOPin trigger_pin_isr_;
|
||||
InternalGPIOPin *echo_pin_;
|
||||
UltrasonicSensorStore store_;
|
||||
uint32_t timeout_us_{};
|
||||
uint32_t pulse_time_us_{};
|
||||
|
||||
uint32_t measurement_start_us_{0};
|
||||
|
||||
12
tests/components/adc/test.esp32-c2-idf.yaml
Normal file
12
tests/components/adc/test.esp32-c2-idf.yaml
Normal file
@@ -0,0 +1,12 @@
|
||||
sensor:
|
||||
- id: my_sensor
|
||||
platform: adc
|
||||
pin: GPIO1
|
||||
name: ADC Test sensor
|
||||
update_interval: "1:01"
|
||||
attenuation: 2.5db
|
||||
unit_of_measurement: "°C"
|
||||
icon: "mdi:water-percent"
|
||||
accuracy_decimals: 5
|
||||
setup_priority: -100
|
||||
force_update: true
|
||||
@@ -0,0 +1,20 @@
|
||||
esphome:
|
||||
name: componenttestesp32c2idf
|
||||
friendly_name: $component_name
|
||||
|
||||
esp32:
|
||||
board: esp32-c2-devkitm-1
|
||||
framework:
|
||||
type: esp-idf
|
||||
# Use custom partition table with larger app partition (3MB)
|
||||
# Default IDF partitions only allow 1.75MB which is too small for grouped tests
|
||||
partitions: ../partitions_testing.csv
|
||||
|
||||
logger:
|
||||
level: VERY_VERBOSE
|
||||
|
||||
packages:
|
||||
component_under_test: !include
|
||||
file: $component_test_file
|
||||
vars:
|
||||
component_test_file: $component_test_file
|
||||
Reference in New Issue
Block a user