mirror of
https://github.com/esphome/esphome.git
synced 2026-06-24 11:07:33 +00:00
[xdb401] XDB401 Pressure Sensor (#15108)
Co-authored-by: Ricky Tsai <ricky@rtnztech.com> Co-authored-by: pre-commit-ci-lite[bot] <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com> Co-authored-by: Jonathan Swoboda <154711427+swoboda1337@users.noreply.github.com> Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
This commit is contained in:
@@ -596,6 +596,7 @@ esphome/components/wk2212_spi/* @DrCoolZic
|
||||
esphome/components/wl_134/* @hobbypunk90
|
||||
esphome/components/wts01/* @alepee
|
||||
esphome/components/x9c/* @EtienneMD
|
||||
esphome/components/xdb401/* @RT530
|
||||
esphome/components/xgzp68xx/* @gcormier
|
||||
esphome/components/xiaomi_hhccjcy10/* @fariouche
|
||||
esphome/components/xiaomi_lywsd02mmc/* @juanluss31
|
||||
|
||||
1
esphome/components/xdb401/__init__.py
Normal file
1
esphome/components/xdb401/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
CODEOWNERS = ["@RT530"]
|
||||
64
esphome/components/xdb401/sensor.py
Normal file
64
esphome/components/xdb401/sensor.py
Normal file
@@ -0,0 +1,64 @@
|
||||
import esphome.codegen as cg
|
||||
from esphome.components import i2c, sensor
|
||||
import esphome.config_validation as cv
|
||||
from esphome.const import (
|
||||
CONF_ID,
|
||||
CONF_PRESSURE,
|
||||
CONF_TEMPERATURE,
|
||||
DEVICE_CLASS_PRESSURE,
|
||||
DEVICE_CLASS_TEMPERATURE,
|
||||
STATE_CLASS_MEASUREMENT,
|
||||
UNIT_CELSIUS,
|
||||
UNIT_PASCAL,
|
||||
)
|
||||
|
||||
DEPENDENCIES = ["i2c"]
|
||||
|
||||
CONF_PRESSURE_RANGE_BAR = "pressure_range_bar"
|
||||
|
||||
xdb401_ns = cg.esphome_ns.namespace("xdb401")
|
||||
|
||||
XDB401Component = xdb401_ns.class_(
|
||||
"XDB401Component", cg.PollingComponent, i2c.I2CDevice
|
||||
)
|
||||
|
||||
CONFIG_SCHEMA = (
|
||||
cv.Schema(
|
||||
{
|
||||
cv.GenerateID(): cv.declare_id(XDB401Component),
|
||||
cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema(
|
||||
unit_of_measurement=UNIT_CELSIUS,
|
||||
accuracy_decimals=2,
|
||||
device_class=DEVICE_CLASS_TEMPERATURE,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
),
|
||||
cv.Optional(CONF_PRESSURE): sensor.sensor_schema(
|
||||
unit_of_measurement=UNIT_PASCAL,
|
||||
accuracy_decimals=0,
|
||||
device_class=DEVICE_CLASS_PRESSURE,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
),
|
||||
cv.Optional(CONF_PRESSURE_RANGE_BAR, default=10): cv.one_of(
|
||||
1, 2, 5, 10, 20, 50, 100, int=True
|
||||
),
|
||||
}
|
||||
)
|
||||
.extend(cv.polling_component_schema("60s"))
|
||||
.extend(i2c.i2c_device_schema(0x7F))
|
||||
)
|
||||
|
||||
|
||||
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)
|
||||
|
||||
cg.add(var.set_pressure_range_bar(config[CONF_PRESSURE_RANGE_BAR]))
|
||||
|
||||
if temperature_config := config.get(CONF_TEMPERATURE):
|
||||
sens = await sensor.new_sensor(temperature_config)
|
||||
cg.add(var.set_temperature_sensor(sens))
|
||||
|
||||
if pressure_config := config.get(CONF_PRESSURE):
|
||||
sens = await sensor.new_sensor(pressure_config)
|
||||
cg.add(var.set_pressure_sensor(sens))
|
||||
177
esphome/components/xdb401/xdb401.cpp
Normal file
177
esphome/components/xdb401/xdb401.cpp
Normal file
@@ -0,0 +1,177 @@
|
||||
#include "esphome/core/log.h"
|
||||
#include "esphome/core/helpers.h"
|
||||
#include "xdb401.h"
|
||||
|
||||
namespace esphome::xdb401 {
|
||||
|
||||
static const char *const TAG = "xdb401";
|
||||
|
||||
static const uint8_t REG_PRESSURE = 0x06;
|
||||
static const uint8_t REG_TEMPERATURE = 0x09;
|
||||
static const uint8_t REG_MAKE_MEASURE = 0x30;
|
||||
static const uint8_t CMD_MAKE_MEASURE = 0x0A;
|
||||
static const uint8_t MASK_MEASURE_READY = 0x08;
|
||||
static const float CONVERT_PRESSURE = 8388608.0f; // 0x800000
|
||||
|
||||
static const uint32_t CHECK_DELAY = 5;
|
||||
static const uint8_t CHECK_ATTEMPTS = 6;
|
||||
static const uint8_t MARK_FAIL_AFTER = 5;
|
||||
|
||||
void XDB401Component::setup() {
|
||||
ESP_LOGCONFIG(TAG, "Running setup");
|
||||
|
||||
uint8_t meas_resp[1] = {};
|
||||
i2c::ErrorCode err_code = this->read_register(REG_MAKE_MEASURE, meas_resp, sizeof(meas_resp));
|
||||
if (err_code != i2c::ERROR_OK) {
|
||||
this->mark_failed(LOG_STR("I2C communication failed"));
|
||||
return;
|
||||
}
|
||||
|
||||
this->comm_err_counter_ = 0;
|
||||
}
|
||||
|
||||
void XDB401Component::dump_config() {
|
||||
ESP_LOGCONFIG(TAG, "XDB401:");
|
||||
LOG_I2C_DEVICE(this);
|
||||
LOG_UPDATE_INTERVAL(this);
|
||||
ESP_LOGCONFIG(TAG, " Pressure Range: %u bar", this->pressure_range_bar_);
|
||||
LOG_SENSOR(" ", "Pressure", this->pressure_sensor_);
|
||||
LOG_SENSOR(" ", "Temperature", this->temperature_sensor_);
|
||||
}
|
||||
|
||||
void XDB401Component::handle_comm_failure_(const char *message) {
|
||||
this->status_set_warning(message);
|
||||
|
||||
if (this->comm_err_counter_ >= MARK_FAIL_AFTER) {
|
||||
this->mark_failed(LOG_STR("Too many consecutive I2C communication errors"));
|
||||
} else {
|
||||
this->comm_err_counter_++;
|
||||
}
|
||||
|
||||
this->measurement_in_progress_ = false;
|
||||
}
|
||||
|
||||
i2c::ErrorCode XDB401Component::start_measurement_() {
|
||||
i2c::ErrorCode err_code = this->write_register(REG_MAKE_MEASURE, &CMD_MAKE_MEASURE, sizeof(CMD_MAKE_MEASURE));
|
||||
if (err_code != i2c::ERROR_OK) {
|
||||
ESP_LOGE(TAG, "Error starting measurement, code: %u", err_code);
|
||||
return err_code;
|
||||
}
|
||||
|
||||
return i2c::ERROR_OK;
|
||||
}
|
||||
|
||||
void XDB401Component::check_measurement_ready_(uint8_t attempt) {
|
||||
uint8_t meas_resp[1] = {};
|
||||
i2c::ErrorCode err_code = this->read_register(REG_MAKE_MEASURE, meas_resp, sizeof(meas_resp));
|
||||
if (err_code != i2c::ERROR_OK) {
|
||||
ESP_LOGE(TAG, "Error reading measurement status, code: %u", err_code);
|
||||
this->handle_comm_failure_("I2C communication failed");
|
||||
return;
|
||||
}
|
||||
|
||||
ESP_LOGV(TAG, "Config response %02X", meas_resp[0]);
|
||||
|
||||
// Bit 3 shall be 0 when measurement is ready
|
||||
if ((meas_resp[0] & MASK_MEASURE_READY) == 0) {
|
||||
ESP_LOGV(TAG, "Meas mode entered after %u ms", attempt * CHECK_DELAY);
|
||||
this->read_measurement_();
|
||||
return;
|
||||
}
|
||||
|
||||
if (attempt >= CHECK_ATTEMPTS) {
|
||||
ESP_LOGE(TAG, "Device not in measurement mode after timeout of %u ms", CHECK_DELAY * CHECK_ATTEMPTS);
|
||||
this->handle_comm_failure_("Measurement timeout");
|
||||
return;
|
||||
}
|
||||
|
||||
this->set_timeout(CHECK_DELAY, [this, attempt]() { this->check_measurement_ready_(attempt + 1); });
|
||||
}
|
||||
|
||||
void XDB401Component::read_measurement_() {
|
||||
float temperature{};
|
||||
float pressure{};
|
||||
|
||||
i2c::ErrorCode err_code = this->read_pressure_(pressure);
|
||||
if (err_code != i2c::ERROR_OK) {
|
||||
this->handle_comm_failure_("Could not read pressure data");
|
||||
return;
|
||||
}
|
||||
|
||||
err_code = this->read_temperature_(temperature);
|
||||
if (err_code != i2c::ERROR_OK) {
|
||||
this->handle_comm_failure_("Could not read temperature data");
|
||||
return;
|
||||
}
|
||||
|
||||
ESP_LOGD(TAG, "Got pressure=%.1f Pa, temperature=%.2f°C", pressure, temperature);
|
||||
|
||||
if (this->temperature_sensor_ != nullptr)
|
||||
this->temperature_sensor_->publish_state(temperature);
|
||||
if (this->pressure_sensor_ != nullptr)
|
||||
this->pressure_sensor_->publish_state(pressure);
|
||||
|
||||
this->comm_err_counter_ = 0;
|
||||
this->status_clear_warning();
|
||||
this->measurement_in_progress_ = false;
|
||||
}
|
||||
|
||||
i2c::ErrorCode XDB401Component::read_pressure_(float &pressure) {
|
||||
uint8_t p_data[3]{};
|
||||
i2c::ErrorCode err_code = this->read_register(REG_PRESSURE, p_data, 3);
|
||||
if (err_code != i2c::ERROR_OK) {
|
||||
ESP_LOGE(TAG, "Error reading pressure register");
|
||||
return err_code;
|
||||
}
|
||||
char pressure_buf[format_hex_pretty_size(3)];
|
||||
format_hex_pretty_to(pressure_buf, sizeof(pressure_buf), p_data, 3);
|
||||
ESP_LOGV(TAG, "Got pressure data: %s", pressure_buf);
|
||||
|
||||
// Sign-extend 24-bit big-endian pressure value to int32_t.
|
||||
int32_t raw_pressure = static_cast<int32_t>(encode_uint24(p_data[0], p_data[1], p_data[2]) << 8) >> 8;
|
||||
ESP_LOGD(TAG, "Pressure data raw %i", raw_pressure);
|
||||
|
||||
pressure = (static_cast<float>(raw_pressure) / CONVERT_PRESSURE) *
|
||||
XDB401Component::full_scale_pressure_pa(this->pressure_range_bar_);
|
||||
|
||||
return err_code;
|
||||
}
|
||||
|
||||
i2c::ErrorCode XDB401Component::read_temperature_(float &temperature) {
|
||||
uint8_t t_data[2]{};
|
||||
i2c::ErrorCode err_code = this->read_register(REG_TEMPERATURE, t_data, 2);
|
||||
if (err_code != i2c::ERROR_OK) {
|
||||
ESP_LOGE(TAG, "Error reading temperature register");
|
||||
return err_code;
|
||||
}
|
||||
|
||||
char temperature_buf[format_hex_pretty_size(2)];
|
||||
format_hex_pretty_to(temperature_buf, sizeof(temperature_buf), t_data, 2);
|
||||
ESP_LOGV(TAG, "Got temperature data: %s", temperature_buf);
|
||||
|
||||
// Temperature is a signed 16-bit big-endian value in 1/256 °C (Q8.8 fixed point).
|
||||
int16_t raw_temperature = static_cast<int16_t>(encode_uint16(t_data[0], t_data[1]));
|
||||
ESP_LOGD(TAG, "Temperature data raw %i", raw_temperature);
|
||||
|
||||
temperature = static_cast<float>(raw_temperature) / 256.0f;
|
||||
|
||||
return err_code;
|
||||
}
|
||||
|
||||
void XDB401Component::update() {
|
||||
if (this->measurement_in_progress_) {
|
||||
ESP_LOGV(TAG, "Skipping update, measurement already in progress");
|
||||
return;
|
||||
}
|
||||
|
||||
i2c::ErrorCode err_code = this->start_measurement_();
|
||||
if (err_code != i2c::ERROR_OK) {
|
||||
this->handle_comm_failure_("I2C communication failed");
|
||||
return;
|
||||
}
|
||||
|
||||
this->measurement_in_progress_ = true;
|
||||
this->set_timeout(CHECK_DELAY, [this]() { this->check_measurement_ready_(1); });
|
||||
}
|
||||
|
||||
} // namespace esphome::xdb401
|
||||
37
esphome/components/xdb401/xdb401.h
Normal file
37
esphome/components/xdb401/xdb401.h
Normal file
@@ -0,0 +1,37 @@
|
||||
#pragma once
|
||||
|
||||
#include "esphome/components/i2c/i2c.h"
|
||||
#include "esphome/components/sensor/sensor.h"
|
||||
#include "esphome/core/component.h"
|
||||
|
||||
namespace esphome::xdb401 {
|
||||
|
||||
class XDB401Component : public PollingComponent, public i2c::I2CDevice {
|
||||
public:
|
||||
void set_temperature_sensor(sensor::Sensor *temperature_sensor) { this->temperature_sensor_ = temperature_sensor; }
|
||||
void set_pressure_sensor(sensor::Sensor *pressure_sensor) { this->pressure_sensor_ = pressure_sensor; }
|
||||
void set_pressure_range_bar(uint8_t pressure_range_bar) { this->pressure_range_bar_ = pressure_range_bar; }
|
||||
|
||||
void setup() override;
|
||||
void dump_config() override;
|
||||
void update() override;
|
||||
|
||||
protected:
|
||||
void handle_comm_failure_(const char *message);
|
||||
i2c::ErrorCode start_measurement_();
|
||||
void check_measurement_ready_(uint8_t attempt);
|
||||
void read_measurement_();
|
||||
i2c::ErrorCode read_pressure_(float &pressure);
|
||||
i2c::ErrorCode read_temperature_(float &temperature);
|
||||
|
||||
static constexpr float full_scale_pressure_pa(uint8_t pressure_range_bar) { return pressure_range_bar * 100000.0f; }
|
||||
|
||||
uint8_t comm_err_counter_{0};
|
||||
bool measurement_in_progress_{false};
|
||||
uint8_t pressure_range_bar_{10};
|
||||
|
||||
sensor::Sensor *temperature_sensor_{nullptr};
|
||||
sensor::Sensor *pressure_sensor_{nullptr};
|
||||
};
|
||||
|
||||
} // namespace esphome::xdb401
|
||||
10
tests/components/xdb401/common.yaml
Normal file
10
tests/components/xdb401/common.yaml
Normal file
@@ -0,0 +1,10 @@
|
||||
sensor:
|
||||
- platform: xdb401
|
||||
update_interval: 1s
|
||||
i2c_id: i2c_bus
|
||||
|
||||
temperature:
|
||||
name: water temperature
|
||||
|
||||
pressure:
|
||||
name: water pressure
|
||||
4
tests/components/xdb401/test.esp32-idf.yaml
Normal file
4
tests/components/xdb401/test.esp32-idf.yaml
Normal file
@@ -0,0 +1,4 @@
|
||||
packages:
|
||||
i2c: !include ../../test_build_components/common/i2c/esp32-idf.yaml
|
||||
|
||||
<<: !include common.yaml
|
||||
4
tests/components/xdb401/test.esp8266-ard.yaml
Normal file
4
tests/components/xdb401/test.esp8266-ard.yaml
Normal file
@@ -0,0 +1,4 @@
|
||||
packages:
|
||||
i2c: !include ../../test_build_components/common/i2c/esp8266-ard.yaml
|
||||
|
||||
<<: !include common.yaml
|
||||
Reference in New Issue
Block a user