Fix AHT10 / AHT20 communication (#5198)

Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
This commit is contained in:
Theo Hussey 2023-12-12 23:28:59 +00:00 committed by GitHub
parent 259a6d52e1
commit 03baaa94a8
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 51 additions and 21 deletions

View file

@ -21,36 +21,49 @@ namespace esphome {
namespace aht10 { namespace aht10 {
static const char *const TAG = "aht10"; static const char *const TAG = "aht10";
static const uint8_t AHT10_CALIBRATE_CMD[] = {0xE1}; static const size_t SIZE_CALIBRATE_CMD = 3;
static const uint8_t AHT10_CALIBRATE_CMD[] = {0xE1, 0x08, 0x00};
static const uint8_t AHT20_CALIBRATE_CMD[] = {0xBE, 0x08, 0x00};
static const uint8_t AHT10_MEASURE_CMD[] = {0xAC, 0x33, 0x00}; static const uint8_t AHT10_MEASURE_CMD[] = {0xAC, 0x33, 0x00};
static const uint8_t AHT10_DEFAULT_DELAY = 5; // ms, for calibration and temperature measurement static const uint8_t AHT10_DEFAULT_DELAY = 5; // ms, for calibration and temperature measurement
static const uint8_t AHT10_HUMIDITY_DELAY = 30; // ms static const uint8_t AHT10_HUMIDITY_DELAY = 30; // ms
static const uint8_t AHT10_ATTEMPTS = 3; // safety margin, normally 3 attempts are enough: 3*30=90ms static const uint8_t AHT10_ATTEMPTS = 3; // safety margin, normally 3 attempts are enough: 3*30=90ms
static const uint8_t AHT10_CAL_ATTEMPTS = 10;
static const uint8_t AHT10_STATUS_BUSY = 0x80;
void AHT10Component::setup() { void AHT10Component::setup() {
ESP_LOGCONFIG(TAG, "Setting up AHT10..."); const uint8_t *calibrate_cmd;
switch (this->variant_) {
case AHT10Variant::AHT20:
calibrate_cmd = AHT20_CALIBRATE_CMD;
ESP_LOGCONFIG(TAG, "Setting up AHT20");
break;
case AHT10Variant::AHT10:
default:
calibrate_cmd = AHT10_CALIBRATE_CMD;
ESP_LOGCONFIG(TAG, "Setting up AHT10");
}
if (!this->write_bytes(0, AHT10_CALIBRATE_CMD, sizeof(AHT10_CALIBRATE_CMD))) { if (this->write(calibrate_cmd, SIZE_CALIBRATE_CMD) != i2c::ERROR_OK) {
ESP_LOGE(TAG, "Communication with AHT10 failed!"); ESP_LOGE(TAG, "Communication with AHT10 failed!");
this->mark_failed(); this->mark_failed();
return; return;
} }
uint8_t data = 0; uint8_t data = AHT10_STATUS_BUSY;
if (this->write(&data, 1) != i2c::ERROR_OK) { int cal_attempts = 0;
ESP_LOGD(TAG, "Communication with AHT10 failed!"); while (data & AHT10_STATUS_BUSY) {
this->mark_failed(); delay(AHT10_DEFAULT_DELAY);
return; if (this->read(&data, 1) != i2c::ERROR_OK) {
} ESP_LOGE(TAG, "Communication with AHT10 failed!");
delay(AHT10_DEFAULT_DELAY); this->mark_failed();
if (this->read(&data, 1) != i2c::ERROR_OK) { return;
ESP_LOGD(TAG, "Communication with AHT10 failed!"); }
this->mark_failed(); ++cal_attempts;
return; if (cal_attempts > AHT10_CAL_ATTEMPTS) {
} ESP_LOGE(TAG, "AHT10 calibration timed out!");
if (this->read(&data, 1) != i2c::ERROR_OK) { this->mark_failed();
ESP_LOGD(TAG, "Communication with AHT10 failed!"); return;
this->mark_failed(); }
return;
} }
if ((data & 0x68) != 0x08) { // Bit[6:5] = 0b00, NORMAL mode and Bit[3] = 0b1, CALIBRATED if ((data & 0x68) != 0x08) { // Bit[6:5] = 0b00, NORMAL mode and Bit[3] = 0b1, CALIBRATED
ESP_LOGE(TAG, "AHT10 calibration failed!"); ESP_LOGE(TAG, "AHT10 calibration failed!");
@ -62,7 +75,7 @@ void AHT10Component::setup() {
} }
void AHT10Component::update() { void AHT10Component::update() {
if (!this->write_bytes(0, AHT10_MEASURE_CMD, sizeof(AHT10_MEASURE_CMD))) { if (this->write(AHT10_MEASURE_CMD, sizeof(AHT10_MEASURE_CMD)) != i2c::ERROR_OK) {
ESP_LOGE(TAG, "Communication with AHT10 failed!"); ESP_LOGE(TAG, "Communication with AHT10 failed!");
this->status_set_warning(); this->status_set_warning();
return; return;
@ -89,7 +102,7 @@ void AHT10Component::update() {
break; break;
} else { } else {
ESP_LOGD(TAG, "ATH10 Unrealistic humidity (0x0), retrying..."); ESP_LOGD(TAG, "ATH10 Unrealistic humidity (0x0), retrying...");
if (!this->write_bytes(0, AHT10_MEASURE_CMD, sizeof(AHT10_MEASURE_CMD))) { if (this->write(AHT10_MEASURE_CMD, sizeof(AHT10_MEASURE_CMD)) != i2c::ERROR_OK) {
ESP_LOGE(TAG, "Communication with AHT10 failed!"); ESP_LOGE(TAG, "Communication with AHT10 failed!");
this->status_set_warning(); this->status_set_warning();
return; return;

View file

@ -1,5 +1,7 @@
#pragma once #pragma once
#include <utility>
#include "esphome/core/component.h" #include "esphome/core/component.h"
#include "esphome/components/sensor/sensor.h" #include "esphome/components/sensor/sensor.h"
#include "esphome/components/i2c/i2c.h" #include "esphome/components/i2c/i2c.h"
@ -7,12 +9,15 @@
namespace esphome { namespace esphome {
namespace aht10 { namespace aht10 {
enum AHT10Variant { AHT10, AHT20 };
class AHT10Component : public PollingComponent, public i2c::I2CDevice { class AHT10Component : public PollingComponent, public i2c::I2CDevice {
public: public:
void setup() override; void setup() override;
void update() override; void update() override;
void dump_config() override; void dump_config() override;
float get_setup_priority() const override; float get_setup_priority() const override;
void set_variant(AHT10Variant variant) { this->variant_ = variant; }
void set_temperature_sensor(sensor::Sensor *temperature_sensor) { temperature_sensor_ = temperature_sensor; } void set_temperature_sensor(sensor::Sensor *temperature_sensor) { temperature_sensor_ = temperature_sensor; }
void set_humidity_sensor(sensor::Sensor *humidity_sensor) { humidity_sensor_ = humidity_sensor; } void set_humidity_sensor(sensor::Sensor *humidity_sensor) { humidity_sensor_ = humidity_sensor; }
@ -20,6 +25,7 @@ class AHT10Component : public PollingComponent, public i2c::I2CDevice {
protected: protected:
sensor::Sensor *temperature_sensor_{nullptr}; sensor::Sensor *temperature_sensor_{nullptr};
sensor::Sensor *humidity_sensor_{nullptr}; sensor::Sensor *humidity_sensor_{nullptr};
AHT10Variant variant_{};
}; };
} // namespace aht10 } // namespace aht10

View file

@ -10,6 +10,7 @@ from esphome.const import (
STATE_CLASS_MEASUREMENT, STATE_CLASS_MEASUREMENT,
UNIT_CELSIUS, UNIT_CELSIUS,
UNIT_PERCENT, UNIT_PERCENT,
CONF_VARIANT,
) )
DEPENDENCIES = ["i2c"] DEPENDENCIES = ["i2c"]
@ -17,6 +18,12 @@ DEPENDENCIES = ["i2c"]
aht10_ns = cg.esphome_ns.namespace("aht10") aht10_ns = cg.esphome_ns.namespace("aht10")
AHT10Component = aht10_ns.class_("AHT10Component", cg.PollingComponent, i2c.I2CDevice) AHT10Component = aht10_ns.class_("AHT10Component", cg.PollingComponent, i2c.I2CDevice)
AHT10Variant = aht10_ns.enum("AHT10Variant")
AHT10_VARIANTS = {
"AHT10": AHT10Variant.AHT10,
"AHT20": AHT10Variant.AHT20,
}
CONFIG_SCHEMA = ( CONFIG_SCHEMA = (
cv.Schema( cv.Schema(
{ {
@ -33,6 +40,9 @@ CONFIG_SCHEMA = (
device_class=DEVICE_CLASS_HUMIDITY, device_class=DEVICE_CLASS_HUMIDITY,
state_class=STATE_CLASS_MEASUREMENT, state_class=STATE_CLASS_MEASUREMENT,
), ),
cv.Optional(CONF_VARIANT, default="AHT10"): cv.enum(
AHT10_VARIANTS, upper=True
),
} }
) )
.extend(cv.polling_component_schema("60s")) .extend(cv.polling_component_schema("60s"))
@ -44,6 +54,7 @@ async def to_code(config):
var = cg.new_Pvariable(config[CONF_ID]) var = cg.new_Pvariable(config[CONF_ID])
await cg.register_component(var, config) await cg.register_component(var, config)
await i2c.register_i2c_device(var, config) await i2c.register_i2c_device(var, config)
cg.add(var.set_variant(config[CONF_VARIANT]))
if temperature := config.get(CONF_TEMPERATURE): if temperature := config.get(CONF_TEMPERATURE):
sens = await sensor.new_sensor(temperature) sens = await sensor.new_sensor(temperature)