Compare commits

..

2 Commits

Author SHA1 Message Date
J. Nick Koston
298724933f temp comment 2026-01-22 12:58:23 -10:00
J. Nick Koston
359d5810db Update ESPAsyncWebServer to 3.9.x (fixes ESP8266 logging crash) 2026-01-22 12:50:44 -10:00
47 changed files with 370 additions and 490 deletions

View File

@@ -11,7 +11,7 @@ ci:
repos:
- repo: https://github.com/astral-sh/ruff-pre-commit
# Ruff version.
rev: v0.14.14
rev: v0.14.13
hooks:
# Run the linter.
- id: ruff

View File

@@ -88,8 +88,7 @@ esphome/components/bmp3xx/* @latonita
esphome/components/bmp3xx_base/* @latonita @martgras
esphome/components/bmp3xx_i2c/* @latonita
esphome/components/bmp3xx_spi/* @latonita
esphome/components/bmp581_base/* @danielkent-net @kahrendt
esphome/components/bmp581_i2c/* @danielkent-net @kahrendt
esphome/components/bmp581/* @kahrendt
esphome/components/bp1658cj/* @Cossid
esphome/components/bp5758d/* @Cossid
esphome/components/bthome_mithermometer/* @nagyrobi

View File

@@ -108,14 +108,10 @@ void ATM90E32Component::update() {
#endif
}
void ATM90E32Component::get_cs_summary_(std::span<char, GPIO_SUMMARY_MAX_LEN> buffer) {
this->cs_->dump_summary(buffer.data(), buffer.size());
}
void ATM90E32Component::setup() {
this->spi_setup();
char cs[GPIO_SUMMARY_MAX_LEN];
this->get_cs_summary_(cs);
this->cs_summary_ = this->cs_->dump_summary();
const char *cs = this->cs_summary_.c_str();
uint16_t mmode0 = 0x87; // 3P4W 50Hz
uint16_t high_thresh = 0;
@@ -163,13 +159,13 @@ void ATM90E32Component::setup() {
if (this->enable_offset_calibration_) {
// Initialize flash storage for offset calibrations
uint32_t o_hash = fnv1_hash("_offset_calibration_");
o_hash = fnv1_hash_extend(o_hash, cs);
o_hash = fnv1_hash_extend(o_hash, this->cs_summary_);
this->offset_pref_ = global_preferences->make_preference<OffsetCalibration[3]>(o_hash, true);
this->restore_offset_calibrations_();
// Initialize flash storage for power offset calibrations
uint32_t po_hash = fnv1_hash("_power_offset_calibration_");
po_hash = fnv1_hash_extend(po_hash, cs);
po_hash = fnv1_hash_extend(po_hash, this->cs_summary_);
this->power_offset_pref_ = global_preferences->make_preference<PowerOffsetCalibration[3]>(po_hash, true);
this->restore_power_offset_calibrations_();
} else {
@@ -190,7 +186,7 @@ void ATM90E32Component::setup() {
if (this->enable_gain_calibration_) {
// Initialize flash storage for gain calibration
uint32_t g_hash = fnv1_hash("_gain_calibration_");
g_hash = fnv1_hash_extend(g_hash, cs);
g_hash = fnv1_hash_extend(g_hash, this->cs_summary_);
this->gain_calibration_pref_ = global_preferences->make_preference<GainCalibration[3]>(g_hash, true);
this->restore_gain_calibrations_();
@@ -221,8 +217,7 @@ void ATM90E32Component::setup() {
}
void ATM90E32Component::log_calibration_status_() {
char cs[GPIO_SUMMARY_MAX_LEN];
this->get_cs_summary_(cs);
const char *cs = this->cs_summary_.c_str();
bool offset_mismatch = false;
bool power_mismatch = false;
@@ -573,8 +568,7 @@ float ATM90E32Component::get_chip_temperature_() {
}
void ATM90E32Component::run_gain_calibrations() {
char cs[GPIO_SUMMARY_MAX_LEN];
this->get_cs_summary_(cs);
const char *cs = this->cs_summary_.c_str();
if (!this->enable_gain_calibration_) {
ESP_LOGW(TAG, "[CALIBRATION][%s] Gain calibration is disabled! Enable it first with enable_gain_calibration: true",
cs);
@@ -674,8 +668,7 @@ void ATM90E32Component::run_gain_calibrations() {
}
void ATM90E32Component::save_gain_calibration_to_memory_() {
char cs[GPIO_SUMMARY_MAX_LEN];
this->get_cs_summary_(cs);
const char *cs = this->cs_summary_.c_str();
bool success = this->gain_calibration_pref_.save(&this->gain_phase_);
global_preferences->sync();
if (success) {
@@ -688,8 +681,7 @@ void ATM90E32Component::save_gain_calibration_to_memory_() {
}
void ATM90E32Component::save_offset_calibration_to_memory_() {
char cs[GPIO_SUMMARY_MAX_LEN];
this->get_cs_summary_(cs);
const char *cs = this->cs_summary_.c_str();
bool success = this->offset_pref_.save(&this->offset_phase_);
global_preferences->sync();
if (success) {
@@ -705,8 +697,7 @@ void ATM90E32Component::save_offset_calibration_to_memory_() {
}
void ATM90E32Component::save_power_offset_calibration_to_memory_() {
char cs[GPIO_SUMMARY_MAX_LEN];
this->get_cs_summary_(cs);
const char *cs = this->cs_summary_.c_str();
bool success = this->power_offset_pref_.save(&this->power_offset_phase_);
global_preferences->sync();
if (success) {
@@ -722,8 +713,7 @@ void ATM90E32Component::save_power_offset_calibration_to_memory_() {
}
void ATM90E32Component::run_offset_calibrations() {
char cs[GPIO_SUMMARY_MAX_LEN];
this->get_cs_summary_(cs);
const char *cs = this->cs_summary_.c_str();
if (!this->enable_offset_calibration_) {
ESP_LOGW(TAG,
"[CALIBRATION][%s] Offset calibration is disabled! Enable it first with enable_offset_calibration: true",
@@ -753,8 +743,7 @@ void ATM90E32Component::run_offset_calibrations() {
}
void ATM90E32Component::run_power_offset_calibrations() {
char cs[GPIO_SUMMARY_MAX_LEN];
this->get_cs_summary_(cs);
const char *cs = this->cs_summary_.c_str();
if (!this->enable_offset_calibration_) {
ESP_LOGW(
TAG,
@@ -827,8 +816,7 @@ void ATM90E32Component::write_power_offsets_to_registers_(uint8_t phase, int16_t
}
void ATM90E32Component::restore_gain_calibrations_() {
char cs[GPIO_SUMMARY_MAX_LEN];
this->get_cs_summary_(cs);
const char *cs = this->cs_summary_.c_str();
for (uint8_t i = 0; i < 3; ++i) {
this->config_gain_phase_[i].voltage_gain = this->phase_[i].voltage_gain_;
this->config_gain_phase_[i].current_gain = this->phase_[i].ct_gain_;
@@ -882,8 +870,7 @@ void ATM90E32Component::restore_gain_calibrations_() {
}
void ATM90E32Component::restore_offset_calibrations_() {
char cs[GPIO_SUMMARY_MAX_LEN];
this->get_cs_summary_(cs);
const char *cs = this->cs_summary_.c_str();
for (uint8_t i = 0; i < 3; ++i)
this->config_offset_phase_[i] = this->offset_phase_[i];
@@ -925,8 +912,7 @@ void ATM90E32Component::restore_offset_calibrations_() {
}
void ATM90E32Component::restore_power_offset_calibrations_() {
char cs[GPIO_SUMMARY_MAX_LEN];
this->get_cs_summary_(cs);
const char *cs = this->cs_summary_.c_str();
for (uint8_t i = 0; i < 3; ++i)
this->config_power_offset_phase_[i] = this->power_offset_phase_[i];
@@ -968,8 +954,7 @@ void ATM90E32Component::restore_power_offset_calibrations_() {
}
void ATM90E32Component::clear_gain_calibrations() {
char cs[GPIO_SUMMARY_MAX_LEN];
this->get_cs_summary_(cs);
const char *cs = this->cs_summary_.c_str();
if (!this->using_saved_calibrations_) {
ESP_LOGI(TAG, "[CALIBRATION][%s] No stored gain calibrations to clear. Current values:", cs);
ESP_LOGI(TAG, "[CALIBRATION][%s] ----------------------------------------------------------", cs);
@@ -1018,8 +1003,7 @@ void ATM90E32Component::clear_gain_calibrations() {
}
void ATM90E32Component::clear_offset_calibrations() {
char cs[GPIO_SUMMARY_MAX_LEN];
this->get_cs_summary_(cs);
const char *cs = this->cs_summary_.c_str();
if (!this->restored_offset_calibration_) {
ESP_LOGI(TAG, "[CALIBRATION][%s] No stored offset calibrations to clear. Current values:", cs);
ESP_LOGI(TAG, "[CALIBRATION][%s] --------------------------------------------------------------", cs);
@@ -1061,8 +1045,7 @@ void ATM90E32Component::clear_offset_calibrations() {
}
void ATM90E32Component::clear_power_offset_calibrations() {
char cs[GPIO_SUMMARY_MAX_LEN];
this->get_cs_summary_(cs);
const char *cs = this->cs_summary_.c_str();
if (!this->restored_power_offset_calibration_) {
ESP_LOGI(TAG, "[CALIBRATION][%s] No stored power offsets to clear. Current values:", cs);
ESP_LOGI(TAG, "[CALIBRATION][%s] ---------------------------------------------------------------------", cs);
@@ -1137,8 +1120,7 @@ int16_t ATM90E32Component::calibrate_power_offset(uint8_t phase, bool reactive)
}
bool ATM90E32Component::verify_gain_writes_() {
char cs[GPIO_SUMMARY_MAX_LEN];
this->get_cs_summary_(cs);
const char *cs = this->cs_summary_.c_str();
bool success = true;
for (uint8_t phase = 0; phase < 3; phase++) {
uint16_t read_voltage = this->read16_(voltage_gain_registers[phase]);

View File

@@ -1,13 +1,11 @@
#pragma once
#include <span>
#include <unordered_map>
#include "atm90e32_reg.h"
#include "esphome/components/sensor/sensor.h"
#include "esphome/components/spi/spi.h"
#include "esphome/core/application.h"
#include "esphome/core/component.h"
#include "esphome/core/gpio.h"
#include "esphome/core/helpers.h"
#include "esphome/core/preferences.h"
@@ -184,7 +182,6 @@ class ATM90E32Component : public PollingComponent,
bool verify_gain_writes_();
bool validate_spi_read_(uint16_t expected, const char *context = nullptr);
void log_calibration_status_();
void get_cs_summary_(std::span<char, GPIO_SUMMARY_MAX_LEN> buffer);
struct ATM90E32Phase {
uint16_t voltage_gain_{0};
@@ -250,6 +247,7 @@ class ATM90E32Component : public PollingComponent,
ESPPreferenceObject offset_pref_;
ESPPreferenceObject power_offset_pref_;
ESPPreferenceObject gain_calibration_pref_;
std::string cs_summary_;
sensor::Sensor *freq_sensor_{nullptr};
#ifdef USE_TEXT_SENSOR

View File

@@ -10,11 +10,12 @@
* - All datasheet page references refer to Bosch Document Number BST-BMP581-DS004-04 (revision number 1.4)
*/
#include "bmp581_base.h"
#include "bmp581.h"
#include "esphome/core/log.h"
#include "esphome/core/hal.h"
namespace esphome::bmp581_base {
namespace esphome {
namespace bmp581 {
static const char *const TAG = "bmp581";
@@ -90,6 +91,7 @@ void BMP581Component::dump_config() {
break;
}
LOG_I2C_DEVICE(this);
LOG_UPDATE_INTERVAL(this);
ESP_LOGCONFIG(TAG, " Measurement conversion time: %ums", this->conversion_time_);
@@ -147,7 +149,7 @@ void BMP581Component::setup() {
uint8_t chip_id;
// read chip id from sensor
if (!this->bmp_read_byte(BMP581_CHIP_ID, &chip_id)) {
if (!this->read_byte(BMP581_CHIP_ID, &chip_id)) {
ESP_LOGE(TAG, "Read chip ID failed");
this->error_code_ = ERROR_COMMUNICATION_FAILED;
@@ -170,7 +172,7 @@ void BMP581Component::setup() {
// 3) Verify sensor status (check if NVM is okay) //
////////////////////////////////////////////////////
if (!this->bmp_read_byte(BMP581_STATUS, &this->status_.reg)) {
if (!this->read_byte(BMP581_STATUS, &this->status_.reg)) {
ESP_LOGE(TAG, "Failed to read status register");
this->error_code_ = ERROR_COMMUNICATION_FAILED;
@@ -357,7 +359,7 @@ bool BMP581Component::check_data_readiness_() {
uint8_t status;
if (!this->bmp_read_byte(BMP581_INT_STATUS, &status)) {
if (!this->read_byte(BMP581_INT_STATUS, &status)) {
ESP_LOGE(TAG, "Failed to read interrupt status register");
return false;
}
@@ -398,7 +400,7 @@ bool BMP581Component::prime_iir_filter_() {
// flush the IIR filter with forced measurements (we will only flush once)
this->dsp_config_.bit.iir_flush_forced_en = true;
if (!this->bmp_write_byte(BMP581_DSP, this->dsp_config_.reg)) {
if (!this->write_byte(BMP581_DSP, this->dsp_config_.reg)) {
ESP_LOGE(TAG, "Failed to write IIR source register");
return false;
@@ -428,7 +430,7 @@ bool BMP581Component::prime_iir_filter_() {
// disable IIR filter flushings on future forced measurements
this->dsp_config_.bit.iir_flush_forced_en = false;
if (!this->bmp_write_byte(BMP581_DSP, this->dsp_config_.reg)) {
if (!this->write_byte(BMP581_DSP, this->dsp_config_.reg)) {
ESP_LOGE(TAG, "Failed to write IIR source register");
return false;
@@ -452,7 +454,7 @@ bool BMP581Component::read_temperature_(float &temperature) {
}
uint8_t data[3];
if (!this->bmp_read_bytes(BMP581_MEASUREMENT_DATA, &data[0], 3)) {
if (!this->read_bytes(BMP581_MEASUREMENT_DATA, &data[0], 3)) {
ESP_LOGW(TAG, "Failed to read measurement");
this->status_set_warning();
@@ -481,7 +483,7 @@ bool BMP581Component::read_temperature_and_pressure_(float &temperature, float &
}
uint8_t data[6];
if (!this->bmp_read_bytes(BMP581_MEASUREMENT_DATA, &data[0], 6)) {
if (!this->read_bytes(BMP581_MEASUREMENT_DATA, &data[0], 6)) {
ESP_LOGW(TAG, "Failed to read measurement");
this->status_set_warning();
@@ -505,7 +507,7 @@ bool BMP581Component::reset_() {
// - returns the Power-On-Reboot interrupt status, which is asserted if successful
// writes reset command to BMP's command register
if (!this->bmp_write_byte(BMP581_COMMAND, RESET_COMMAND)) {
if (!this->write_byte(BMP581_COMMAND, RESET_COMMAND)) {
ESP_LOGE(TAG, "Failed to write reset command");
return false;
@@ -516,7 +518,7 @@ bool BMP581Component::reset_() {
delay(3);
// read interrupt status register
if (!this->bmp_read_byte(BMP581_INT_STATUS, &this->int_status_.reg)) {
if (!this->read_byte(BMP581_INT_STATUS, &this->int_status_.reg)) {
ESP_LOGE(TAG, "Failed to read interrupt status register");
return false;
@@ -560,7 +562,7 @@ bool BMP581Component::write_iir_settings_(IIRFilter temperature_iir, IIRFilter p
// BMP581_DSP register and BMP581_DSP_IIR registers are successive
// - allows us to write the IIR configuration with one command to both registers
uint8_t register_data[2] = {this->dsp_config_.reg, this->iir_config_.reg};
return this->bmp_write_bytes(BMP581_DSP, register_data, sizeof(register_data));
return this->write_bytes(BMP581_DSP, register_data, sizeof(register_data));
}
bool BMP581Component::write_interrupt_source_settings_(bool data_ready_enable) {
@@ -570,7 +572,7 @@ bool BMP581Component::write_interrupt_source_settings_(bool data_ready_enable) {
this->int_source_.bit.drdy_data_reg_en = data_ready_enable;
// write interrupt source register
return this->bmp_write_byte(BMP581_INT_SOURCE, this->int_source_.reg);
return this->write_byte(BMP581_INT_SOURCE, this->int_source_.reg);
}
bool BMP581Component::write_oversampling_settings_(Oversampling temperature_oversampling,
@@ -581,7 +583,7 @@ bool BMP581Component::write_oversampling_settings_(Oversampling temperature_over
this->osr_config_.bit.osr_t = temperature_oversampling;
this->osr_config_.bit.osr_p = pressure_oversampling;
return this->bmp_write_byte(BMP581_OSR, this->osr_config_.reg);
return this->write_byte(BMP581_OSR, this->osr_config_.reg);
}
bool BMP581Component::write_power_mode_(OperationMode mode) {
@@ -591,7 +593,8 @@ bool BMP581Component::write_power_mode_(OperationMode mode) {
this->odr_config_.bit.pwr_mode = mode;
// write odr register
return this->bmp_write_byte(BMP581_ODR, this->odr_config_.reg);
return this->write_byte(BMP581_ODR, this->odr_config_.reg);
}
} // namespace esphome::bmp581_base
} // namespace bmp581
} // namespace esphome

View File

@@ -3,9 +3,11 @@
#pragma once
#include "esphome/core/component.h"
#include "esphome/components/i2c/i2c.h"
#include "esphome/components/sensor/sensor.h"
namespace esphome::bmp581_base {
namespace esphome {
namespace bmp581 {
static const uint8_t BMP581_ASIC_ID = 0x50; // BMP581's ASIC chip ID (page 51 of datasheet)
static const uint8_t RESET_COMMAND = 0xB6; // Soft reset command
@@ -57,7 +59,7 @@ enum IIRFilter {
IIR_FILTER_128 = 0x7
};
class BMP581Component : public PollingComponent {
class BMP581Component : public PollingComponent, public i2c::I2CDevice {
public:
void dump_config() override;
@@ -82,11 +84,6 @@ class BMP581Component : public PollingComponent {
void set_conversion_time(uint8_t conversion_time) { this->conversion_time_ = conversion_time; }
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_write_bytes(uint8_t a_register, uint8_t *data, size_t len) = 0;
sensor::Sensor *temperature_sensor_{nullptr};
sensor::Sensor *pressure_sensor_{nullptr};
@@ -219,4 +216,5 @@ class BMP581Component : public PollingComponent {
} odr_config_ = {.reg = 0};
};
} // namespace esphome::bmp581_base
} // namespace bmp581
} // namespace esphome

View File

@@ -1,5 +1,164 @@
import esphome.config_validation as cv
import math
CONFIG_SCHEMA = cv.invalid(
"The bmp581 sensor component has been renamed to bmp581_i2c."
import esphome.codegen as cg
from esphome.components import i2c, sensor
import esphome.config_validation as cv
from esphome.const import (
CONF_ID,
CONF_IIR_FILTER,
CONF_OVERSAMPLING,
CONF_PRESSURE,
CONF_TEMPERATURE,
DEVICE_CLASS_ATMOSPHERIC_PRESSURE,
DEVICE_CLASS_TEMPERATURE,
STATE_CLASS_MEASUREMENT,
UNIT_CELSIUS,
UNIT_PASCAL,
)
CODEOWNERS = ["@kahrendt"]
DEPENDENCIES = ["i2c"]
bmp581_ns = cg.esphome_ns.namespace("bmp581")
Oversampling = bmp581_ns.enum("Oversampling")
OVERSAMPLING_OPTIONS = {
"NONE": Oversampling.OVERSAMPLING_NONE,
"2X": Oversampling.OVERSAMPLING_X2,
"4X": Oversampling.OVERSAMPLING_X4,
"8X": Oversampling.OVERSAMPLING_X8,
"16X": Oversampling.OVERSAMPLING_X16,
"32X": Oversampling.OVERSAMPLING_X32,
"64X": Oversampling.OVERSAMPLING_X64,
"128X": Oversampling.OVERSAMPLING_X128,
}
IIRFilter = bmp581_ns.enum("IIRFilter")
IIR_FILTER_OPTIONS = {
"OFF": IIRFilter.IIR_FILTER_OFF,
"2X": IIRFilter.IIR_FILTER_2,
"4X": IIRFilter.IIR_FILTER_4,
"8X": IIRFilter.IIR_FILTER_8,
"16X": IIRFilter.IIR_FILTER_16,
"32X": IIRFilter.IIR_FILTER_32,
"64X": IIRFilter.IIR_FILTER_64,
"128X": IIRFilter.IIR_FILTER_128,
}
BMP581Component = bmp581_ns.class_(
"BMP581Component", cg.PollingComponent, i2c.I2CDevice
)
def compute_measurement_conversion_time(config):
# - adds up sensor conversion time based on temperature and pressure oversampling rates given in datasheet
# - returns a rounded up time in ms
# Page 12 of datasheet
PRESSURE_OVERSAMPLING_CONVERSION_TIMES = {
"NONE": 1.0,
"2X": 1.7,
"4X": 2.9,
"8X": 5.4,
"16X": 10.4,
"32X": 20.4,
"64X": 40.4,
"128X": 80.4,
}
# Page 12 of datasheet
TEMPERATURE_OVERSAMPLING_CONVERSION_TIMES = {
"NONE": 1.0,
"2X": 1.1,
"4X": 1.5,
"8X": 2.1,
"16X": 3.3,
"32X": 5.8,
"64X": 10.8,
"128X": 20.8,
}
pressure_conversion_time = (
0.0 # No conversion time necessary without a pressure sensor
)
if pressure_config := config.get(CONF_PRESSURE):
pressure_conversion_time = PRESSURE_OVERSAMPLING_CONVERSION_TIMES[
pressure_config.get(CONF_OVERSAMPLING)
]
temperature_conversion_time = (
1.0 # BMP581 always samples the temperature even if only reading pressure
)
if temperature_config := config.get(CONF_TEMPERATURE):
temperature_conversion_time = TEMPERATURE_OVERSAMPLING_CONVERSION_TIMES[
temperature_config.get(CONF_OVERSAMPLING)
]
# Datasheet indicates a 5% possible error in each conversion time listed
return math.ceil(1.05 * (pressure_conversion_time + temperature_conversion_time))
CONFIG_SCHEMA = (
cv.Schema(
{
cv.GenerateID(): cv.declare_id(BMP581Component),
cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema(
unit_of_measurement=UNIT_CELSIUS,
accuracy_decimals=1,
device_class=DEVICE_CLASS_TEMPERATURE,
state_class=STATE_CLASS_MEASUREMENT,
).extend(
{
cv.Optional(CONF_OVERSAMPLING, default="NONE"): cv.enum(
OVERSAMPLING_OPTIONS, upper=True
),
cv.Optional(CONF_IIR_FILTER, default="OFF"): cv.enum(
IIR_FILTER_OPTIONS, upper=True
),
}
),
cv.Optional(CONF_PRESSURE): sensor.sensor_schema(
unit_of_measurement=UNIT_PASCAL,
accuracy_decimals=0,
device_class=DEVICE_CLASS_ATMOSPHERIC_PRESSURE,
state_class=STATE_CLASS_MEASUREMENT,
).extend(
{
cv.Optional(CONF_OVERSAMPLING, default="16X"): cv.enum(
OVERSAMPLING_OPTIONS, upper=True
),
cv.Optional(CONF_IIR_FILTER, default="OFF"): cv.enum(
IIR_FILTER_OPTIONS, upper=True
),
}
),
}
)
.extend(cv.polling_component_schema("60s"))
.extend(i2c.i2c_device_schema(0x46))
)
async def to_code(config):
var = cg.new_Pvariable(config[CONF_ID])
await cg.register_component(var, config)
await i2c.register_i2c_device(var, config)
if temperature_config := config.get(CONF_TEMPERATURE):
sens = await sensor.new_sensor(temperature_config)
cg.add(var.set_temperature_sensor(sens))
cg.add(
var.set_temperature_oversampling_config(
temperature_config[CONF_OVERSAMPLING]
)
)
cg.add(
var.set_temperature_iir_filter_config(temperature_config[CONF_IIR_FILTER])
)
if pressure_config := config.get(CONF_PRESSURE):
sens = await sensor.new_sensor(pressure_config)
cg.add(var.set_pressure_sensor(sens))
cg.add(var.set_pressure_oversampling_config(pressure_config[CONF_OVERSAMPLING]))
cg.add(var.set_pressure_iir_filter_config(pressure_config[CONF_IIR_FILTER]))
cg.add(var.set_conversion_time(compute_measurement_conversion_time(config)))

View File

@@ -1,157 +0,0 @@
import math
import esphome.codegen as cg
from esphome.components import sensor
import esphome.config_validation as cv
from esphome.const import (
CONF_ID,
CONF_IIR_FILTER,
CONF_OVERSAMPLING,
CONF_PRESSURE,
CONF_TEMPERATURE,
DEVICE_CLASS_ATMOSPHERIC_PRESSURE,
DEVICE_CLASS_TEMPERATURE,
STATE_CLASS_MEASUREMENT,
UNIT_CELSIUS,
UNIT_PASCAL,
)
CODEOWNERS = ["@kahrendt", "@danielkent-net"]
bmp581_ns = cg.esphome_ns.namespace("bmp581_base")
Oversampling = bmp581_ns.enum("Oversampling")
OVERSAMPLING_OPTIONS = {
"NONE": Oversampling.OVERSAMPLING_NONE,
"2X": Oversampling.OVERSAMPLING_X2,
"4X": Oversampling.OVERSAMPLING_X4,
"8X": Oversampling.OVERSAMPLING_X8,
"16X": Oversampling.OVERSAMPLING_X16,
"32X": Oversampling.OVERSAMPLING_X32,
"64X": Oversampling.OVERSAMPLING_X64,
"128X": Oversampling.OVERSAMPLING_X128,
}
IIRFilter = bmp581_ns.enum("IIRFilter")
IIR_FILTER_OPTIONS = {
"OFF": IIRFilter.IIR_FILTER_OFF,
"2X": IIRFilter.IIR_FILTER_2,
"4X": IIRFilter.IIR_FILTER_4,
"8X": IIRFilter.IIR_FILTER_8,
"16X": IIRFilter.IIR_FILTER_16,
"32X": IIRFilter.IIR_FILTER_32,
"64X": IIRFilter.IIR_FILTER_64,
"128X": IIRFilter.IIR_FILTER_128,
}
BMP581Component = bmp581_ns.class_("BMP581Component", cg.PollingComponent)
def compute_measurement_conversion_time(config):
# - adds up sensor conversion time based on temperature and pressure oversampling rates given in datasheet
# - returns a rounded up time in ms
# Page 12 of datasheet
PRESSURE_OVERSAMPLING_CONVERSION_TIMES = {
"NONE": 1.0,
"2X": 1.7,
"4X": 2.9,
"8X": 5.4,
"16X": 10.4,
"32X": 20.4,
"64X": 40.4,
"128X": 80.4,
}
# Page 12 of datasheet
TEMPERATURE_OVERSAMPLING_CONVERSION_TIMES = {
"NONE": 1.0,
"2X": 1.1,
"4X": 1.5,
"8X": 2.1,
"16X": 3.3,
"32X": 5.8,
"64X": 10.8,
"128X": 20.8,
}
pressure_conversion_time = (
0.0 # No conversion time necessary without a pressure sensor
)
if pressure_config := config.get(CONF_PRESSURE):
pressure_conversion_time = PRESSURE_OVERSAMPLING_CONVERSION_TIMES[
pressure_config.get(CONF_OVERSAMPLING)
]
temperature_conversion_time = (
1.0 # BMP581 always samples the temperature even if only reading pressure
)
if temperature_config := config.get(CONF_TEMPERATURE):
temperature_conversion_time = TEMPERATURE_OVERSAMPLING_CONVERSION_TIMES[
temperature_config.get(CONF_OVERSAMPLING)
]
# Datasheet indicates a 5% possible error in each conversion time listed
return math.ceil(1.05 * (pressure_conversion_time + temperature_conversion_time))
CONFIG_SCHEMA_BASE = cv.Schema(
{
cv.GenerateID(): cv.declare_id(BMP581Component),
cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema(
unit_of_measurement=UNIT_CELSIUS,
accuracy_decimals=1,
device_class=DEVICE_CLASS_TEMPERATURE,
state_class=STATE_CLASS_MEASUREMENT,
).extend(
{
cv.Optional(CONF_OVERSAMPLING, default="NONE"): cv.enum(
OVERSAMPLING_OPTIONS, upper=True
),
cv.Optional(CONF_IIR_FILTER, default="OFF"): cv.enum(
IIR_FILTER_OPTIONS, upper=True
),
}
),
cv.Optional(CONF_PRESSURE): sensor.sensor_schema(
unit_of_measurement=UNIT_PASCAL,
accuracy_decimals=0,
device_class=DEVICE_CLASS_ATMOSPHERIC_PRESSURE,
state_class=STATE_CLASS_MEASUREMENT,
).extend(
{
cv.Optional(CONF_OVERSAMPLING, default="16X"): cv.enum(
OVERSAMPLING_OPTIONS, upper=True
),
cv.Optional(CONF_IIR_FILTER, default="OFF"): cv.enum(
IIR_FILTER_OPTIONS, upper=True
),
}
),
}
).extend(cv.polling_component_schema("60s"))
async def to_code_base(config):
var = cg.new_Pvariable(config[CONF_ID])
await cg.register_component(var, config)
if temperature_config := config.get(CONF_TEMPERATURE):
sens = await sensor.new_sensor(temperature_config)
cg.add(var.set_temperature_sensor(sens))
cg.add(
var.set_temperature_oversampling_config(
temperature_config[CONF_OVERSAMPLING]
)
)
cg.add(
var.set_temperature_iir_filter_config(temperature_config[CONF_IIR_FILTER])
)
if pressure_config := config.get(CONF_PRESSURE):
sens = await sensor.new_sensor(pressure_config)
cg.add(var.set_pressure_sensor(sens))
cg.add(var.set_pressure_oversampling_config(pressure_config[CONF_OVERSAMPLING]))
cg.add(var.set_pressure_iir_filter_config(pressure_config[CONF_IIR_FILTER]))
cg.add(var.set_conversion_time(compute_measurement_conversion_time(config)))
return var

View File

@@ -1,12 +0,0 @@
#include "bmp581_i2c.h"
#include "esphome/core/hal.h"
#include "esphome/core/log.h"
namespace esphome::bmp581_i2c {
void BMP581I2CComponent::dump_config() {
LOG_I2C_DEVICE(this);
BMP581Component::dump_config();
}
} // namespace esphome::bmp581_i2c

View File

@@ -1,24 +0,0 @@
#pragma once
#include "esphome/components/bmp581_base/bmp581_base.h"
#include "esphome/components/i2c/i2c.h"
namespace esphome::bmp581_i2c {
static const char *const TAG = "bmp581_i2c.sensor";
/// This class implements support for the BMP581 Temperature+Pressure i2c sensor.
class BMP581I2CComponent : public esphome::bmp581_base::BMP581Component, public i2c::I2CDevice {
public:
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_write_bytes(uint8_t a_register, uint8_t *data, size_t len) override {
return write_bytes(a_register, data, len);
}
void dump_config() override;
};
} // namespace esphome::bmp581_i2c

View File

@@ -1,23 +0,0 @@
import esphome.codegen as cg
from esphome.components import i2c
import esphome.config_validation as cv
from ..bmp581_base import CONFIG_SCHEMA_BASE, to_code_base
AUTO_LOAD = ["bmp581_base"]
CODEOWNERS = ["@kahrendt", "@danielkent-net"]
DEPENDENCIES = ["i2c"]
bmp581_ns = cg.esphome_ns.namespace("bmp581_i2c")
BMP581I2CComponent = bmp581_ns.class_(
"BMP581I2CComponent", cg.PollingComponent, i2c.I2CDevice
)
CONFIG_SCHEMA = CONFIG_SCHEMA_BASE.extend(
i2c.i2c_device_schema(default_address=0x46)
).extend({cv.GenerateID(): cv.declare_id(BMP581I2CComponent)})
async def to_code(config):
var = await to_code_base(config)
await i2c.register_i2c_device(var, config)

View File

@@ -5,6 +5,8 @@
// Once the API is considered stable, this warning will be removed.
#include "esphome/components/infrared/infrared.h"
#include "esphome/components/remote_transmitter/remote_transmitter.h"
#include "esphome/components/remote_receiver/remote_receiver.h"
namespace esphome::ir_rf_proxy {

View File

@@ -391,10 +391,7 @@ void LightCall::transform_parameters_() {
min_mireds > 0.0f && max_mireds > 0.0f) {
ESP_LOGD(TAG, "'%s': setting cold/warm white channels using white/color temperature values",
this->parent_->get_name().c_str());
// Only compute cold_white/warm_white from color_temperature if they're not already explicitly set.
// This is important for state restoration, where both color_temperature and cold_white/warm_white
// are restored from flash - we want to preserve the saved cold_white/warm_white values.
if (this->has_color_temperature() && !this->has_cold_white() && !this->has_warm_white()) {
if (this->has_color_temperature()) {
const float color_temp = clamp(this->color_temperature_, min_mireds, max_mireds);
const float range = max_mireds - min_mireds;
const float ww_fraction = (color_temp - min_mireds) / range;

View File

@@ -32,7 +32,7 @@ class LabelType(WidgetType):
async def to_code(self, w: Widget, config):
"""For a text object, create and set text"""
if (value := config.get(CONF_TEXT)) is not None:
if value := config.get(CONF_TEXT):
await w.set_property(CONF_TEXT, await lv_text.process(value))
await w.set_property(CONF_LONG_MODE, config)
await w.set_property(CONF_RECOLOR, config)

View File

@@ -1,11 +1,9 @@
#ifdef USE_ESP32_VARIANT_ESP32S3
#include "mipi_rgb.h"
#include "esphome/core/gpio.h"
#include "esphome/core/hal.h"
#include "esphome/core/helpers.h"
#include "esphome/core/log.h"
#include "esphome/core/hal.h"
#include "esp_lcd_panel_rgb.h"
#include <span>
namespace esphome {
namespace mipi_rgb {
@@ -345,27 +343,19 @@ int MipiRgb::get_height() {
}
}
static const char *get_pin_name(GPIOPin *pin, std::span<char, GPIO_SUMMARY_MAX_LEN> buffer) {
static std::string get_pin_name(GPIOPin *pin) {
if (pin == nullptr)
return "None";
pin->dump_summary(buffer.data(), buffer.size());
return buffer.data();
return pin->dump_summary();
}
void MipiRgb::dump_pins_(uint8_t start, uint8_t end, const char *name, uint8_t offset) {
char pin_summary[GPIO_SUMMARY_MAX_LEN];
for (uint8_t i = start; i != end; i++) {
this->data_pins_[i]->dump_summary(pin_summary, sizeof(pin_summary));
ESP_LOGCONFIG(TAG, " %s pin %d: %s", name, offset++, pin_summary);
ESP_LOGCONFIG(TAG, " %s pin %d: %s", name, offset++, this->data_pins_[i]->dump_summary().c_str());
}
}
void MipiRgb::dump_config() {
char reset_buf[GPIO_SUMMARY_MAX_LEN];
char de_buf[GPIO_SUMMARY_MAX_LEN];
char pclk_buf[GPIO_SUMMARY_MAX_LEN];
char hsync_buf[GPIO_SUMMARY_MAX_LEN];
char vsync_buf[GPIO_SUMMARY_MAX_LEN];
ESP_LOGCONFIG(TAG,
"MIPI_RGB LCD"
"\n Model: %s"
@@ -389,9 +379,9 @@ void MipiRgb::dump_config() {
this->model_, this->width_, this->height_, this->rotation_, YESNO(this->pclk_inverted_),
this->hsync_pulse_width_, this->hsync_back_porch_, this->hsync_front_porch_, this->vsync_pulse_width_,
this->vsync_back_porch_, this->vsync_front_porch_, YESNO(this->invert_colors_),
(unsigned) (this->pclk_frequency_ / 1000000), get_pin_name(this->reset_pin_, reset_buf),
get_pin_name(this->de_pin_, de_buf), get_pin_name(this->pclk_pin_, pclk_buf),
get_pin_name(this->hsync_pin_, hsync_buf), get_pin_name(this->vsync_pin_, vsync_buf));
(unsigned) (this->pclk_frequency_ / 1000000), get_pin_name(this->reset_pin_).c_str(),
get_pin_name(this->de_pin_).c_str(), get_pin_name(this->pclk_pin_).c_str(),
get_pin_name(this->hsync_pin_).c_str(), get_pin_name(this->vsync_pin_).c_str());
this->dump_pins_(8, 13, "Blue", 0);
this->dump_pins_(13, 16, "Green", 0);

View File

@@ -55,7 +55,6 @@ st7701s = ST7701S(
pclk_frequency="16MHz",
pclk_inverted=True,
initsequence=(
(0x01,), # Software Reset
(0xFF, 0x77, 0x01, 0x00, 0x00, 0x10), # Page 0
(0xC0, 0x3B, 0x00), (0xC1, 0x0D, 0x02), (0xC2, 0x31, 0x05),
(0xB0, 0x00, 0x11, 0x18, 0x0E, 0x11, 0x06, 0x07, 0x08, 0x07, 0x22, 0x04, 0x12, 0x0F, 0xAA, 0x31, 0x18,),

View File

@@ -1,6 +1,5 @@
#ifdef USE_ESP32_VARIANT_ESP32S3
#include "rpi_dpi_rgb.h"
#include "esphome/core/gpio.h"
#include "esphome/core/log.h"
namespace esphome {
@@ -135,11 +134,8 @@ void RpiDpiRgb::dump_config() {
LOG_PIN(" Enable Pin: ", this->enable_pin_);
LOG_PIN(" Reset Pin: ", this->reset_pin_);
size_t data_pin_count = sizeof(this->data_pins_) / sizeof(this->data_pins_[0]);
char pin_summary[GPIO_SUMMARY_MAX_LEN];
for (size_t i = 0; i != data_pin_count; i++) {
this->data_pins_[i]->dump_summary(pin_summary, sizeof(pin_summary));
ESP_LOGCONFIG(TAG, " Data pin %d: %s", i, pin_summary);
}
for (size_t i = 0; i != data_pin_count; i++)
ESP_LOGCONFIG(TAG, " Data pin %d: %s", i, (this->data_pins_[i])->dump_summary().c_str());
}
void RpiDpiRgb::reset_display_() const {

View File

@@ -124,8 +124,8 @@ void SEN5XComponent::setup() {
sen5x_type = SEN55;
}
}
ESP_LOGD(TAG, "Product name: %s", this->product_name_.c_str());
}
ESP_LOGD(TAG, "Product name: %s", this->product_name_.c_str());
if (this->humidity_sensor_ && sen5x_type == SEN50) {
ESP_LOGE(TAG, "Relative humidity requires a SEN54 or SEN55");
this->humidity_sensor_ = nullptr; // mark as not used
@@ -159,14 +159,28 @@ void SEN5XComponent::setup() {
// This ensures the baseline storage is cleared after OTA
// Serial numbers are unique to each sensor, so multiple sensors can be used without conflict
uint32_t hash = fnv1a_hash_extend(App.get_config_version_hash(), combined_serial);
this->pref_ = global_preferences->make_preference<uint16_t[4]>(hash, true);
this->voc_baseline_time_ = App.get_loop_component_start_time();
if (this->pref_.load(&this->voc_baseline_state_)) {
if (!this->write_command(SEN5X_CMD_VOC_ALGORITHM_STATE, this->voc_baseline_state_, 4)) {
ESP_LOGE(TAG, "VOC Baseline State write to sensor failed");
} else {
ESP_LOGV(TAG, "VOC Baseline State loaded");
delay(20);
this->pref_ = global_preferences->make_preference<Sen5xBaselines>(hash, true);
if (this->pref_.load(&this->voc_baselines_storage_)) {
ESP_LOGI(TAG, "Loaded VOC baseline state0: 0x%04" PRIX32 ", state1: 0x%04" PRIX32,
this->voc_baselines_storage_.state0, this->voc_baselines_storage_.state1);
}
// Initialize storage timestamp
this->seconds_since_last_store_ = 0;
if (this->voc_baselines_storage_.state0 > 0 && this->voc_baselines_storage_.state1 > 0) {
ESP_LOGI(TAG, "Setting VOC baseline from save state0: 0x%04" PRIX32 ", state1: 0x%04" PRIX32,
this->voc_baselines_storage_.state0, this->voc_baselines_storage_.state1);
uint16_t states[4];
states[0] = this->voc_baselines_storage_.state0 >> 16;
states[1] = this->voc_baselines_storage_.state0 & 0xFFFF;
states[2] = this->voc_baselines_storage_.state1 >> 16;
states[3] = this->voc_baselines_storage_.state1 & 0xFFFF;
if (!this->write_command(SEN5X_CMD_VOC_ALGORITHM_STATE, states, 4)) {
ESP_LOGE(TAG, "Failed to set VOC baseline from saved state");
}
}
}
@@ -274,14 +288,6 @@ void SEN5XComponent::dump_config() {
ESP_LOGCONFIG(TAG, " RH/T acceleration mode: %s",
LOG_STR_ARG(rht_accel_mode_to_string(this->acceleration_mode_.value())));
}
if (this->voc_sensor_) {
char hex_buf[5 * 4];
format_hex_pretty_to(hex_buf, this->voc_baseline_state_, 4, 0);
ESP_LOGCONFIG(TAG,
" Store Baseline: %s\n"
" State: %s\n",
TRUEFALSE(this->store_baseline_), hex_buf);
}
LOG_UPDATE_INTERVAL(this);
LOG_SENSOR(" ", "PM 1.0", this->pm_1_0_sensor_);
LOG_SENSOR(" ", "PM 2.5", this->pm_2_5_sensor_);
@@ -298,6 +304,36 @@ void SEN5XComponent::update() {
return;
}
// Store baselines after defined interval or if the difference between current and stored baseline becomes too
// much
if (this->store_baseline_ && this->seconds_since_last_store_ > SHORTEST_BASELINE_STORE_INTERVAL) {
if (this->write_command(SEN5X_CMD_VOC_ALGORITHM_STATE)) {
// run it a bit later to avoid adding a delay here
this->set_timeout(550, [this]() {
uint16_t states[4];
if (this->read_data(states, 4)) {
uint32_t state0 = states[0] << 16 | states[1];
uint32_t state1 = states[2] << 16 | states[3];
if ((uint32_t) std::abs(static_cast<int32_t>(this->voc_baselines_storage_.state0 - state0)) >
MAXIMUM_STORAGE_DIFF ||
(uint32_t) std::abs(static_cast<int32_t>(this->voc_baselines_storage_.state1 - state1)) >
MAXIMUM_STORAGE_DIFF) {
this->seconds_since_last_store_ = 0;
this->voc_baselines_storage_.state0 = state0;
this->voc_baselines_storage_.state1 = state1;
if (this->pref_.save(&this->voc_baselines_storage_)) {
ESP_LOGI(TAG, "Stored VOC baseline state0: 0x%04" PRIX32 ", state1: 0x%04" PRIX32,
this->voc_baselines_storage_.state0, this->voc_baselines_storage_.state1);
} else {
ESP_LOGW(TAG, "Could not store VOC baselines");
}
}
}
});
}
}
if (!this->write_command(SEN5X_CMD_READ_MEASUREMENT)) {
this->status_set_warning();
ESP_LOGD(TAG, "Write error: read measurement (%d)", this->last_error_);
@@ -366,29 +402,7 @@ void SEN5XComponent::update() {
if (this->nox_sensor_ != nullptr) {
this->nox_sensor_->publish_state(nox);
}
if (!this->voc_sensor_ || !this->store_baseline_ ||
(App.get_loop_component_start_time() - this->voc_baseline_time_) < SHORTEST_BASELINE_STORE_INTERVAL) {
this->status_clear_warning();
} else {
this->voc_baseline_time_ = App.get_loop_component_start_time();
if (!this->write_command(SEN5X_CMD_VOC_ALGORITHM_STATE)) {
this->status_set_warning();
ESP_LOGW(TAG, ESP_LOG_MSG_COMM_FAIL);
} else {
this->set_timeout(20, [this]() {
if (!this->read_data(this->voc_baseline_state_, 4)) {
this->status_set_warning();
ESP_LOGW(TAG, ESP_LOG_MSG_COMM_FAIL);
} else {
if (this->pref_.save(&this->voc_baseline_state_)) {
ESP_LOGD(TAG, "VOC Baseline State saved");
}
this->status_clear_warning();
}
});
}
}
this->status_clear_warning();
});
}

View File

@@ -24,6 +24,11 @@ enum RhtAccelerationMode : uint16_t {
HIGH_ACCELERATION = 2,
};
struct Sen5xBaselines {
int32_t state0;
int32_t state1;
} PACKED; // NOLINT
struct GasTuning {
uint16_t index_offset;
uint16_t learning_time_offset_hours;
@@ -39,9 +44,11 @@ struct TemperatureCompensation {
uint16_t time_constant;
};
// Shortest time interval of 2H (in milliseconds) for storing baseline values.
// Shortest time interval of 3H for storing baseline values.
// Prevents wear of the flash because of too many write operations
static const uint32_t SHORTEST_BASELINE_STORE_INTERVAL = 2 * 60 * 60 * 1000;
static const uint32_t SHORTEST_BASELINE_STORE_INTERVAL = 10800;
// Store anyway if the baseline difference exceeds the max storage diff value
static const uint32_t MAXIMUM_STORAGE_DIFF = 50;
class SEN5XComponent : public PollingComponent, public sensirion_common::SensirionI2CDevice {
public:
@@ -100,8 +107,7 @@ class SEN5XComponent : public PollingComponent, public sensirion_common::Sensiri
bool write_tuning_parameters_(uint16_t i2c_command, const GasTuning &tuning);
bool write_temperature_compensation_(const TemperatureCompensation &compensation);
uint16_t voc_baseline_state_[4]{0};
uint32_t voc_baseline_time_;
uint32_t seconds_since_last_store_;
uint16_t firmware_version_;
ERRORCODE error_code_;
uint8_t serial_number_[4];
@@ -126,6 +132,7 @@ class SEN5XComponent : public PollingComponent, public sensirion_common::Sensiri
optional<TemperatureCompensation> temperature_compensation_;
ESPPreferenceObject pref_;
std::string product_name_;
Sen5xBaselines voc_baselines_storage_;
};
} // namespace sen5x

View File

@@ -210,7 +210,6 @@ SENSOR_MAP = {
SETTING_MAP = {
CONF_AUTO_CLEANING_INTERVAL: "set_auto_cleaning_interval",
CONF_ACCELERATION_MODE: "set_acceleration_mode",
CONF_STORE_BASELINE: "set_store_baseline",
}

View File

@@ -39,23 +39,42 @@ bool SensirionI2CDevice::read_data(uint16_t *data, const uint8_t len) {
*/
bool SensirionI2CDevice::write_command_(uint16_t command, CommandLen command_len, const uint16_t *data,
const uint8_t data_len) {
uint8_t temp_stack[BUFFER_STACK_SIZE];
std::unique_ptr<uint8_t[]> temp_heap;
uint8_t *temp;
size_t required_buffer_len = data_len * 3 + 2;
SmallBufferWithHeapFallback<BUFFER_STACK_SIZE> buffer(required_buffer_len);
uint8_t *temp = buffer.get();
// Is a dynamic allocation required ?
if (required_buffer_len >= BUFFER_STACK_SIZE) {
temp_heap = std::unique_ptr<uint8_t[]>(new uint8_t[required_buffer_len]);
temp = temp_heap.get();
} else {
temp = temp_stack;
}
// First byte or word is the command
uint8_t raw_idx = 0;
if (command_len == 1) {
temp[raw_idx++] = command & 0xFF;
} else {
// command is 2 bytes
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
temp[raw_idx++] = command >> 8;
temp[raw_idx++] = command & 0xFF;
#else
temp[raw_idx++] = command & 0xFF;
temp[raw_idx++] = command >> 8;
#endif
}
// add parameters followed by crc
// skipped if len == 0
for (size_t i = 0; i < data_len; i++) {
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
temp[raw_idx++] = data[i] >> 8;
temp[raw_idx++] = data[i] & 0xFF;
#else
temp[raw_idx++] = data[i] & 0xFF;
temp[raw_idx++] = data[i] >> 8;
#endif
// Use MSB first since Sensirion devices use CRC-8 with MSB first
uint8_t crc = crc8(&temp[raw_idx - 2], 2, 0xFF, CRC_POLYNOMIAL, true);
temp[raw_idx++] = crc;

View File

@@ -445,18 +445,22 @@ optional<float> CalibratePolynomialFilter::new_value(float value) {
ClampFilter::ClampFilter(float min, float max, bool ignore_out_of_range)
: min_(min), max_(max), ignore_out_of_range_(ignore_out_of_range) {}
optional<float> ClampFilter::new_value(float value) {
if (std::isfinite(this->min_) && !(value >= this->min_)) {
if (this->ignore_out_of_range_) {
return {};
if (std::isfinite(value)) {
if (std::isfinite(this->min_) && value < this->min_) {
if (this->ignore_out_of_range_) {
return {};
} else {
return this->min_;
}
}
return this->min_;
}
if (std::isfinite(this->max_) && !(value <= this->max_)) {
if (this->ignore_out_of_range_) {
return {};
if (std::isfinite(this->max_) && value > this->max_) {
if (this->ignore_out_of_range_) {
return {};
} else {
return this->max_;
}
}
return this->max_;
}
return value;
}

View File

@@ -1,7 +1,6 @@
#include "slow_pwm_output.h"
#include "esphome/core/application.h"
#include "esphome/core/gpio.h"
#include "esphome/core/log.h"
#include "esphome/core/application.h"
namespace esphome {
namespace slow_pwm {
@@ -21,9 +20,7 @@ void SlowPWMOutput::set_output_state_(bool new_state) {
}
if (new_state != current_state_) {
if (this->pin_) {
char pin_summary[GPIO_SUMMARY_MAX_LEN];
this->pin_->dump_summary(pin_summary, sizeof(pin_summary));
ESP_LOGV(TAG, "Switching output pin %s to %s", pin_summary, ONOFF(new_state));
ESP_LOGV(TAG, "Switching output pin %s to %s", this->pin_->dump_summary().c_str(), ONOFF(new_state));
} else {
ESP_LOGV(TAG, "Switching to %s", ONOFF(new_state));
}

View File

@@ -1,6 +1,5 @@
#ifdef USE_ESP32_VARIANT_ESP32S3
#include "st7701s.h"
#include "esphome/core/gpio.h"
#include "esphome/core/log.h"
namespace esphome {
@@ -184,11 +183,8 @@ void ST7701S::dump_config() {
LOG_PIN(" DE Pin: ", this->de_pin_);
LOG_PIN(" Reset Pin: ", this->reset_pin_);
size_t data_pin_count = sizeof(this->data_pins_) / sizeof(this->data_pins_[0]);
char pin_summary[GPIO_SUMMARY_MAX_LEN];
for (size_t i = 0; i != data_pin_count; i++) {
this->data_pins_[i]->dump_summary(pin_summary, sizeof(pin_summary));
ESP_LOGCONFIG(TAG, " Data pin %d: %s", i, pin_summary);
}
for (size_t i = 0; i != data_pin_count; i++)
ESP_LOGCONFIG(TAG, " Data pin %d: %s", i, (this->data_pins_[i])->dump_summary().c_str());
ESP_LOGCONFIG(TAG, " SPI Data rate: %dMHz", (unsigned) (this->data_rate_ / 1000000));
}

View File

@@ -1,3 +1,4 @@
// Trigger CI memory impact (uses updated ESPAsyncWebServer from web_server_base)
#include "web_server.h"
#ifdef USE_WEBSERVER
#include "esphome/components/json/json_util.h"

View File

@@ -48,4 +48,5 @@ async def to_code(config):
if CORE.is_libretiny:
CORE.add_platformio_option("lib_ignore", ["ESPAsyncTCP", "RPAsyncTCP"])
# https://github.com/ESP32Async/ESPAsyncWebServer/blob/main/library.json
cg.add_library("ESP32Async/ESPAsyncWebServer", "3.7.10")
# Testing PR #370 for ESP8266 SSE crash fix
cg.add_library("https://github.com/bdraco/ESPAsyncWebServer.git#pr-370", None)

View File

@@ -1,6 +1,6 @@
pylint==4.0.4
flake8==7.3.0 # also change in .pre-commit-config.yaml when updating
ruff==0.14.14 # also change in .pre-commit-config.yaml when updating
ruff==0.14.13 # also change in .pre-commit-config.yaml when updating
pyupgrade==3.21.2 # also change in .pre-commit-config.yaml when updating
pre-commit

View File

@@ -1,5 +1,5 @@
sensor:
- platform: bmp581_i2c
- platform: bmp581
i2c_id: i2c_bus
temperature:
name: BMP581 Temperature

View File

@@ -1,18 +0,0 @@
remote_receiver:
id: ir_receiver
pin: ${rx_pin}
# Test various hardware types with transmitter/receiver using infrared platform
infrared:
# Infrared receiver
- platform: ir_rf_proxy
id: ir_rx
name: "IR Receiver"
remote_receiver_id: ir_receiver
# RF 900MHz receiver
- platform: ir_rf_proxy
id: rf_900_rx
name: "RF 900 Receiver"
frequency: 900 MHz
remote_receiver_id: ir_receiver

View File

@@ -1,19 +0,0 @@
remote_transmitter:
id: ir_transmitter
pin: ${tx_pin}
carrier_duty_percent: 50%
# Test various hardware types with transmitter/receiver using infrared platform
infrared:
# Infrared transmitter
- platform: ir_rf_proxy
id: ir_tx
name: "IR Transmitter"
remote_transmitter_id: ir_transmitter
# RF 433MHz transmitter
- platform: ir_rf_proxy
id: rf_433_tx
name: "RF 433 Transmitter"
frequency: 433 MHz
remote_transmitter_id: ir_transmitter

View File

@@ -1,7 +1,42 @@
network:
wifi:
ssid: MySSID
password: password1
api:
remote_transmitter:
id: ir_transmitter
pin: ${tx_pin}
carrier_duty_percent: 50%
remote_receiver:
id: ir_receiver
pin: ${rx_pin}
# Test various hardware types with transmitter/receiver using infrared platform
infrared:
# Infrared transmitter
- platform: ir_rf_proxy
id: ir_tx
name: "IR Transmitter"
remote_transmitter_id: ir_transmitter
# Infrared receiver
- platform: ir_rf_proxy
id: ir_rx
name: "IR Receiver"
remote_receiver_id: ir_receiver
# RF 433MHz transmitter
- platform: ir_rf_proxy
id: rf_433_tx
name: "RF 433 Transmitter"
frequency: 433 MHz
remote_transmitter_id: ir_transmitter
# RF 900MHz receiver
- platform: ir_rf_proxy
id: rf_900_rx
name: "RF 900 Receiver"
frequency: 900 MHz
remote_receiver_id: ir_receiver

View File

@@ -1,7 +0,0 @@
substitutions:
tx_pin: GPIO4
rx_pin: GPIO5
packages:
common: !include common.yaml
rx: !include common-rx.yaml

View File

@@ -1,7 +0,0 @@
substitutions:
tx_pin: GPIO4
rx_pin: GPIO5
packages:
common: !include common.yaml
rx: !include common-rx.yaml

View File

@@ -1,7 +0,0 @@
substitutions:
tx_pin: GPIO4
rx_pin: GPIO5
packages:
common: !include common.yaml
rx: !include common-rx.yaml

View File

@@ -1,7 +0,0 @@
substitutions:
tx_pin: GPIO4
rx_pin: GPIO5
packages:
common: !include common.yaml
tx: !include common-tx.yaml

View File

@@ -1,7 +0,0 @@
substitutions:
tx_pin: GPIO4
rx_pin: GPIO5
packages:
common: !include common.yaml
tx: !include common-tx.yaml

View File

@@ -1,7 +0,0 @@
substitutions:
tx_pin: GPIO4
rx_pin: GPIO5
packages:
common: !include common.yaml
tx: !include common-tx.yaml

View File

@@ -1,8 +0,0 @@
substitutions:
tx_pin: GPIO4
rx_pin: GPIO5
packages:
common: !include common.yaml
rx: !include common-rx.yaml
tx: !include common-tx.yaml

View File

@@ -2,7 +2,4 @@ substitutions:
tx_pin: GPIO4
rx_pin: GPIO5
packages:
common: !include common.yaml
rx: !include common-rx.yaml
tx: !include common-tx.yaml
<<: !include common.yaml

View File

@@ -2,7 +2,4 @@ substitutions:
tx_pin: GPIO4
rx_pin: GPIO5
packages:
common: !include common.yaml
rx: !include common-rx.yaml
tx: !include common-tx.yaml
<<: !include common.yaml

View File

@@ -2,7 +2,4 @@ substitutions:
tx_pin: GPIO4
rx_pin: GPIO5
packages:
common: !include common.yaml
rx: !include common-rx.yaml
tx: !include common-tx.yaml
<<: !include common.yaml

View File

@@ -197,9 +197,6 @@ lvgl:
- lvgl.label.update:
id: msgbox_label
text: Unloaded
- lvgl.label.update:
id: msgbox_label
text: "" # Empty text
on_all_events:
logger.log:
format: "Event %s"

View File

@@ -117,7 +117,6 @@ sensor:
- 10.0 -> 12.1
- 13.0 -> 14.0
- clamp:
# Infinity and NaN will be clamped (NaN -> min_value, +Infinity -> max_value, -Infinity -> min_value)
max_value: 10.0
min_value: -10.0
- debounce: 0.1s