ESPHome  2024.4.0
aht10.cpp
Go to the documentation of this file.
1 // Implementation based on:
2 // - AHT10: https://github.com/Thinary/AHT10
3 // - Official Datasheet (cn):
4 // http://www.aosong.com/userfiles/files/media/aht10%E8%A7%84%E6%A0%BC%E4%B9%A6v1_1%EF%BC%8820191015%EF%BC%89.pdf
5 // - Unofficial Translated Datasheet (en):
6 // https://wiki.liutyi.info/download/attachments/30507639/Aosong_AHT10_en_draft_0c.pdf
7 //
8 // When configured for humidity, the log 'Components should block for at most 20-30ms in loop().' will be generated in
9 // verbose mode. This is due to technical specs of the sensor and can not be avoided.
10 //
11 // According to the datasheet, the component is supposed to respond in more than 75ms. In fact, it can answer almost
12 // immediately for temperature. But for humidity, it takes >90ms to get a valid data. From experience, we have best
13 // results making successive requests; the current implementation makes 3 attempts with a delay of 30ms each time.
14 
15 #include "aht10.h"
16 #include "esphome/core/log.h"
17 #include "esphome/core/hal.h"
18 
19 namespace esphome {
20 namespace aht10 {
21 
22 static const char *const TAG = "aht10";
23 static const uint8_t AHT10_INITIALIZE_CMD[] = {0xE1, 0x08, 0x00};
24 static const uint8_t AHT20_INITIALIZE_CMD[] = {0xBE, 0x08, 0x00};
25 static const uint8_t AHT10_MEASURE_CMD[] = {0xAC, 0x33, 0x00};
26 static const uint8_t AHT10_SOFTRESET_CMD[] = {0xBA};
27 
28 static const uint8_t AHT10_DEFAULT_DELAY = 5; // ms, for initialization and temperature measurement
29 static const uint8_t AHT10_READ_DELAY = 80; // ms, time to wait for conversion result
30 static const uint8_t AHT10_SOFTRESET_DELAY = 30; // ms
31 
32 static const uint8_t AHT10_ATTEMPTS = 3; // safety margin, normally 3 attempts are enough: 3*30=90ms
33 static const uint8_t AHT10_INIT_ATTEMPTS = 10;
34 
35 static const uint8_t AHT10_STATUS_BUSY = 0x80;
36 
38  if (this->write(AHT10_SOFTRESET_CMD, sizeof(AHT10_SOFTRESET_CMD)) != i2c::ERROR_OK) {
39  ESP_LOGE(TAG, "Reset AHT10 failed!");
40  }
41  delay(AHT10_SOFTRESET_DELAY);
42 
44  switch (this->variant_) {
46  ESP_LOGCONFIG(TAG, "Setting up AHT20");
47  error_code = this->write(AHT20_INITIALIZE_CMD, sizeof(AHT20_INITIALIZE_CMD));
48  break;
50  ESP_LOGCONFIG(TAG, "Setting up AHT10");
51  error_code = this->write(AHT10_INITIALIZE_CMD, sizeof(AHT10_INITIALIZE_CMD));
52  break;
53  }
54  if (error_code != i2c::ERROR_OK) {
55  ESP_LOGE(TAG, "Communication with AHT10 failed!");
56  this->mark_failed();
57  return;
58  }
59  uint8_t data = AHT10_STATUS_BUSY;
60  int cal_attempts = 0;
61  while (data & AHT10_STATUS_BUSY) {
62  delay(AHT10_DEFAULT_DELAY);
63  if (this->read(&data, 1) != i2c::ERROR_OK) {
64  ESP_LOGE(TAG, "Communication with AHT10 failed!");
65  this->mark_failed();
66  return;
67  }
68  ++cal_attempts;
69  if (cal_attempts > AHT10_INIT_ATTEMPTS) {
70  ESP_LOGE(TAG, "AHT10 initialization timed out!");
71  this->mark_failed();
72  return;
73  }
74  }
75  if ((data & 0x68) != 0x08) { // Bit[6:5] = 0b00, NORMAL mode and Bit[3] = 0b1, CALIBRATED
76  ESP_LOGE(TAG, "AHT10 initialization failed!");
77  this->mark_failed();
78  return;
79  }
80 
81  ESP_LOGV(TAG, "AHT10 initialization");
82 }
83 
85  if (this->read_count_ == AHT10_ATTEMPTS) {
86  this->read_count_ = 0;
87  this->status_set_error("Measurements reading timed-out!");
88  return;
89  }
90  this->read_count_++;
91  this->set_timeout(AHT10_READ_DELAY, [this]() { this->read_data_(); });
92 }
93 
95  uint8_t data[6];
96  if (this->read_count_ > 1)
97  ESP_LOGD(TAG, "Read attempt %d at %ums", this->read_count_, (unsigned) (millis() - this->start_time_));
98  if (this->read(data, 6) != i2c::ERROR_OK) {
99  this->status_set_warning("AHT10 read failed, retrying soon");
100  this->restart_read_();
101  return;
102  }
103 
104  if ((data[0] & 0x80) == 0x80) { // Bit[7] = 0b1, device is busy
105  ESP_LOGD(TAG, "AHT10 is busy, waiting...");
106  this->restart_read_();
107  return;
108  }
109  if (data[1] == 0x0 && data[2] == 0x0 && (data[3] >> 4) == 0x0) {
110  // Unrealistic humidity (0x0)
111  if (this->humidity_sensor_ == nullptr) {
112  ESP_LOGV(TAG, "ATH10 Unrealistic humidity (0x0), but humidity is not required");
113  } else {
114  ESP_LOGD(TAG, "ATH10 Unrealistic humidity (0x0), retrying...");
115  if (this->write(AHT10_MEASURE_CMD, sizeof(AHT10_MEASURE_CMD)) != i2c::ERROR_OK) {
116  this->status_set_warning("Communication with AHT10 failed!");
117  }
118  this->restart_read_();
119  return;
120  }
121  }
122  if (this->read_count_ > 1)
123  ESP_LOGD(TAG, "Success at %ums", (unsigned) (millis() - this->start_time_));
124  uint32_t raw_temperature = ((data[3] & 0x0F) << 16) | (data[4] << 8) | data[5];
125  uint32_t raw_humidity = ((data[1] << 16) | (data[2] << 8) | data[3]) >> 4;
126 
127  if (this->temperature_sensor_ != nullptr) {
128  float temperature = ((200.0f * (float) raw_temperature) / 1048576.0f) - 50.0f;
129  this->temperature_sensor_->publish_state(temperature);
130  }
131  if (this->humidity_sensor_ != nullptr) {
132  float humidity;
133  if (raw_humidity == 0) { // unrealistic value
134  humidity = NAN;
135  } else {
136  humidity = (float) raw_humidity * 100.0f / 1048576.0f;
137  }
138  if (std::isnan(humidity)) {
139  ESP_LOGW(TAG, "Invalid humidity! Sensor reported 0%% Hum");
140  }
141  this->humidity_sensor_->publish_state(humidity);
142  }
143  this->status_clear_warning();
144  this->read_count_ = 0;
145 }
147  if (this->read_count_ != 0)
148  return;
149  this->start_time_ = millis();
150  if (this->write(AHT10_MEASURE_CMD, sizeof(AHT10_MEASURE_CMD)) != i2c::ERROR_OK) {
151  this->status_set_warning("Communication with AHT10 failed!");
152  return;
153  }
154  this->restart_read_();
155 }
156 
158 
160  ESP_LOGCONFIG(TAG, "AHT10:");
161  LOG_I2C_DEVICE(this);
162  if (this->is_failed()) {
163  ESP_LOGE(TAG, "Communication with AHT10 failed!");
164  }
165  LOG_SENSOR(" ", "Temperature", this->temperature_sensor_);
166  LOG_SENSOR(" ", "Humidity", this->humidity_sensor_);
167 }
168 
169 } // namespace aht10
170 } // namespace esphome
const float DATA
For components that import data from directly connected sensors like DHT.
Definition: component.cpp:19
void status_set_warning(const char *message="unspecified")
Definition: component.cpp:151
ErrorCode read(uint8_t *data, size_t len)
reads an array of bytes from the device using an I2CBus
Definition: i2c.h:160
void set_timeout(const std::string &name, uint32_t timeout, std::function< void()> &&f)
Set a timeout function with a unique name.
Definition: component.cpp:69
sensor::Sensor * humidity_sensor_
Definition: aht10.h:27
method called invalid argument(s)
Definition: i2c_bus.h:14
uint32_t IRAM_ATTR HOT millis()
Definition: core.cpp:25
ErrorCode write(const uint8_t *data, size_t len, bool stop=true)
writes an array of bytes to a device using an I2CBus
Definition: i2c.h:186
void status_set_error(const char *message="unspecified")
Definition: component.cpp:159
No error found during execution of method.
Definition: i2c_bus.h:13
void status_clear_warning()
Definition: component.cpp:166
void update() override
Definition: aht10.cpp:146
void publish_state(float state)
Publish a new state to the front-end.
Definition: sensor.cpp:39
uint16_t temperature
Definition: sun_gtil2.cpp:26
void dump_config() override
Definition: aht10.cpp:159
sensor::Sensor * temperature_sensor_
Definition: aht10.h:26
float get_setup_priority() const override
Definition: aht10.cpp:157
virtual void mark_failed()
Mark this component as failed.
Definition: component.cpp:118
This is a workaround until we can figure out a way to get the tflite-micro idf component code availab...
Definition: a01nyub.cpp:7
ErrorCode
Error codes returned by I2CBus and I2CDevice methods.
Definition: i2c_bus.h:11
void setup() override
Definition: aht10.cpp:37
void IRAM_ATTR HOT delay(uint32_t ms)
Definition: core.cpp:26