From b134d42e3b91512cdcdfe6b8f13f0e62ba0b2c9b Mon Sep 17 00:00:00 2001 From: Niall Douglas Date: Sat, 18 Oct 2025 02:47:18 +0200 Subject: [PATCH] [xgzp68xx] Add oversampling config and tidy up implementation. (#10306) 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> --- esphome/components/xgzp68xx/sensor.py | 19 ++++++ esphome/components/xgzp68xx/xgzp68xx.cpp | 80 ++++++++++++++++-------- esphome/components/xgzp68xx/xgzp68xx.h | 20 ++++++ tests/components/xgzp68xx/common.yaml | 1 + 4 files changed, 94 insertions(+), 26 deletions(-) diff --git a/esphome/components/xgzp68xx/sensor.py b/esphome/components/xgzp68xx/sensor.py index 74cef3bf7b..2b38392a02 100644 --- a/esphome/components/xgzp68xx/sensor.py +++ b/esphome/components/xgzp68xx/sensor.py @@ -3,6 +3,7 @@ from esphome.components import i2c, sensor import esphome.config_validation as cv from esphome.const import ( CONF_ID, + CONF_OVERSAMPLING, CONF_PRESSURE, CONF_TEMPERATURE, DEVICE_CLASS_PRESSURE, @@ -18,6 +19,17 @@ CODEOWNERS = ["@gcormier"] CONF_K_VALUE = "k_value" xgzp68xx_ns = cg.esphome_ns.namespace("xgzp68xx") +XGZP68XXOversampling = xgzp68xx_ns.enum("XGZP68XXOversampling") +OVERSAMPLING_OPTIONS = { + "256X": XGZP68XXOversampling.XGZP68XX_OVERSAMPLING_256X, + "512X": XGZP68XXOversampling.XGZP68XX_OVERSAMPLING_512X, + "1024X": XGZP68XXOversampling.XGZP68XX_OVERSAMPLING_1024X, + "2048X": XGZP68XXOversampling.XGZP68XX_OVERSAMPLING_2048X, + "4096X": XGZP68XXOversampling.XGZP68XX_OVERSAMPLING_4096X, + "8192X": XGZP68XXOversampling.XGZP68XX_OVERSAMPLING_8192X, + "16384X": XGZP68XXOversampling.XGZP68XX_OVERSAMPLING_16384X, + "32768X": XGZP68XXOversampling.XGZP68XX_OVERSAMPLING_32768X, +} XGZP68XXComponent = xgzp68xx_ns.class_( "XGZP68XXComponent", cg.PollingComponent, i2c.I2CDevice ) @@ -31,6 +43,12 @@ CONFIG_SCHEMA = ( accuracy_decimals=1, device_class=DEVICE_CLASS_PRESSURE, state_class=STATE_CLASS_MEASUREMENT, + ).extend( + { + cv.Optional(CONF_OVERSAMPLING, default="4096X"): cv.enum( + OVERSAMPLING_OPTIONS, upper=True + ), + } ), cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema( unit_of_measurement=UNIT_CELSIUS, @@ -58,5 +76,6 @@ async def to_code(config): 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(pressure_config[CONF_OVERSAMPLING])) cg.add(var.set_k_value(config[CONF_K_VALUE])) diff --git a/esphome/components/xgzp68xx/xgzp68xx.cpp b/esphome/components/xgzp68xx/xgzp68xx.cpp index 20a97cd04b..2b0824de0a 100644 --- a/esphome/components/xgzp68xx/xgzp68xx.cpp +++ b/esphome/components/xgzp68xx/xgzp68xx.cpp @@ -16,16 +16,49 @@ static const uint8_t SYSCONFIG_ADDRESS = 0xA5; static const uint8_t PCONFIG_ADDRESS = 0xA6; static const uint8_t READ_COMMAND = 0x0A; +[[maybe_unused]] static const char *oversampling_to_str(XGZP68XXOversampling oversampling) { + switch (oversampling) { + case XGZP68XX_OVERSAMPLING_256X: + return "256x"; + case XGZP68XX_OVERSAMPLING_512X: + return "512x"; + case XGZP68XX_OVERSAMPLING_1024X: + return "1024x"; + case XGZP68XX_OVERSAMPLING_2048X: + return "2048x"; + case XGZP68XX_OVERSAMPLING_4096X: + return "4096x"; + case XGZP68XX_OVERSAMPLING_8192X: + return "8192x"; + case XGZP68XX_OVERSAMPLING_16384X: + return "16384x"; + case XGZP68XX_OVERSAMPLING_32768X: + return "32768x"; + default: + return "UNKNOWN"; + } +} + void XGZP68XXComponent::update() { + // Do we need to change oversampling? + if (this->last_pressure_oversampling_ != this->pressure_oversampling_) { + uint8_t oldconfig = 0; + this->read_register(PCONFIG_ADDRESS, &oldconfig, 1); + uint8_t newconfig = (oldconfig & 0xf8) | (this->pressure_oversampling_ & 0x7); + this->write_register(PCONFIG_ADDRESS, &newconfig, 1); + ESP_LOGD(TAG, "oversampling to %s: oldconfig = 0x%x newconfig = 0x%x", + oversampling_to_str(this->pressure_oversampling_), oldconfig, newconfig); + this->last_pressure_oversampling_ = this->pressure_oversampling_; + } + // Request temp + pressure acquisition this->write_register(0x30, &READ_COMMAND, 1); // Wait 20mS per datasheet this->set_timeout("measurement", 20, [this]() { - uint8_t data[5]; - uint32_t pressure_raw; - uint16_t temperature_raw; - float pressure_in_pa, temperature; + uint8_t data[5] = {}; + uint32_t pressure_raw = 0; + uint16_t temperature_raw = 0; int success; // Read the sensor data @@ -42,23 +75,11 @@ void XGZP68XXComponent::update() { ESP_LOGV(TAG, "Got raw pressure=%" PRIu32 ", raw temperature=%u", pressure_raw, temperature_raw); ESP_LOGV(TAG, "K value is %u", this->k_value_); - // The most significant bit of both pressure and temperature will be 1 to indicate a negative value. - // This is directly from the datasheet, and the calculations below will handle this. - if (pressure_raw > pow(2, 23)) { - // Negative pressure - pressure_in_pa = (pressure_raw - pow(2, 24)) / (float) (this->k_value_); - } else { - // Positive pressure - pressure_in_pa = pressure_raw / (float) (this->k_value_); - } + // Sign extend the pressure + float pressure_in_pa = (float) (((int32_t) pressure_raw << 8) >> 8); + pressure_in_pa /= (float) (this->k_value_); - if (temperature_raw > pow(2, 15)) { - // Negative temperature - temperature = (float) (temperature_raw - pow(2, 16)) / 256.0f; - } else { - // Positive temperature - temperature = (float) temperature_raw / 256.0f; - } + float temperature = ((float) (int16_t) temperature_raw) / 256.0f; if (this->pressure_sensor_ != nullptr) this->pressure_sensor_->publish_state(pressure_in_pa); @@ -69,20 +90,27 @@ void XGZP68XXComponent::update() { } void XGZP68XXComponent::setup() { - uint8_t config; + uint8_t config1 = 0, config2 = 0; // Display some sample bits to confirm we are talking to the sensor - this->read_register(SYSCONFIG_ADDRESS, &config, 1); - ESP_LOGCONFIG(TAG, - "Gain value is %d\n" - "XGZP68xx started!", - (config >> 3) & 0b111); + if (i2c::ErrorCode::ERROR_OK != this->read_register(SYSCONFIG_ADDRESS, &config1, 1)) { + this->mark_failed(); + return; + } + if (i2c::ErrorCode::ERROR_OK != this->read_register(PCONFIG_ADDRESS, &config2, 1)) { + this->mark_failed(); + return; + } + ESP_LOGD(TAG, "sys_config 0x%x, p_config 0x%x", config1, config2); } void XGZP68XXComponent::dump_config() { ESP_LOGCONFIG(TAG, "XGZP68xx:"); LOG_SENSOR(" ", "Temperature: ", this->temperature_sensor_); LOG_SENSOR(" ", "Pressure: ", this->pressure_sensor_); + if (this->pressure_sensor_ != nullptr) { + ESP_LOGCONFIG(TAG, " Oversampling: %s", oversampling_to_str(this->pressure_oversampling_)); + } LOG_I2C_DEVICE(this); if (this->is_failed()) { ESP_LOGE(TAG, " Connection failed"); diff --git a/esphome/components/xgzp68xx/xgzp68xx.h b/esphome/components/xgzp68xx/xgzp68xx.h index 1bb7304b15..ce9cfd6b78 100644 --- a/esphome/components/xgzp68xx/xgzp68xx.h +++ b/esphome/components/xgzp68xx/xgzp68xx.h @@ -7,11 +7,29 @@ namespace esphome { namespace xgzp68xx { +/// Enum listing all oversampling options for the XGZP68XX. +enum XGZP68XXOversampling : uint8_t { + XGZP68XX_OVERSAMPLING_256X = 0b100, + XGZP68XX_OVERSAMPLING_512X = 0b101, + XGZP68XX_OVERSAMPLING_1024X = 0b000, + XGZP68XX_OVERSAMPLING_2048X = 0b001, + XGZP68XX_OVERSAMPLING_4096X = 0b010, + XGZP68XX_OVERSAMPLING_8192X = 0b011, + XGZP68XX_OVERSAMPLING_16384X = 0b110, + XGZP68XX_OVERSAMPLING_32768X = 0b111, + + XGZP68XX_OVERSAMPLING_UNKNOWN = (uint8_t) -1, +}; + class XGZP68XXComponent : public PollingComponent, public sensor::Sensor, public i2c::I2CDevice { public: SUB_SENSOR(temperature) SUB_SENSOR(pressure) void set_k_value(uint16_t k_value) { this->k_value_ = k_value; } + /// Set the pressure oversampling value. Defaults to 4096X. + void set_pressure_oversampling(XGZP68XXOversampling pressure_oversampling) { + this->pressure_oversampling_ = pressure_oversampling; + } void update() override; void setup() override; @@ -21,6 +39,8 @@ class XGZP68XXComponent : public PollingComponent, public sensor::Sensor, public /// Internal method to read the pressure from the component after it has been scheduled. void read_pressure_(); uint16_t k_value_; + XGZP68XXOversampling pressure_oversampling_{XGZP68XX_OVERSAMPLING_4096X}; + XGZP68XXOversampling last_pressure_oversampling_{XGZP68XX_OVERSAMPLING_UNKNOWN}; }; } // namespace xgzp68xx diff --git a/tests/components/xgzp68xx/common.yaml b/tests/components/xgzp68xx/common.yaml index f76b1de508..00e51e764c 100644 --- a/tests/components/xgzp68xx/common.yaml +++ b/tests/components/xgzp68xx/common.yaml @@ -6,3 +6,4 @@ sensor: name: Pressure Temperature pressure: name: Differential pressure + oversampling: 1024x