ESPHome  2025.2.0
dht.cpp
Go to the documentation of this file.
1 #include "dht.h"
2 #include "esphome/core/log.h"
3 #include "esphome/core/helpers.h"
4 
5 namespace esphome {
6 namespace dht {
7 
8 static const char *const TAG = "dht";
9 
10 void DHT::setup() {
11  ESP_LOGCONFIG(TAG, "Setting up DHT...");
12  this->pin_->digital_write(true);
13  this->pin_->setup();
14  this->pin_->digital_write(true);
15 }
17  ESP_LOGCONFIG(TAG, "DHT:");
18  LOG_PIN(" Pin: ", this->pin_);
19  if (this->is_auto_detect_) {
20  ESP_LOGCONFIG(TAG, " Auto-detected model: %s", this->model_ == DHT_MODEL_DHT11 ? "DHT11" : "DHT22");
21  } else if (this->model_ == DHT_MODEL_DHT11) {
22  ESP_LOGCONFIG(TAG, " Model: DHT11");
23  } else {
24  ESP_LOGCONFIG(TAG, " Model: DHT22 (or equivalent)");
25  }
26  ESP_LOGCONFIG(TAG, " Internal Pull-up: %s", ONOFF(this->pin_->get_flags() & gpio::FLAG_PULLUP));
27 
28  LOG_UPDATE_INTERVAL(this);
29 
30  LOG_SENSOR(" ", "Temperature", this->temperature_sensor_);
31  LOG_SENSOR(" ", "Humidity", this->humidity_sensor_);
32 }
33 
34 void DHT::update() {
35  float temperature, humidity;
36  bool success;
37  if (this->model_ == DHT_MODEL_AUTO_DETECT) {
38  this->model_ = DHT_MODEL_DHT22;
39  success = this->read_sensor_(&temperature, &humidity, false);
40  if (!success) {
41  this->model_ = DHT_MODEL_DHT11;
42  return;
43  }
44  } else {
45  success = this->read_sensor_(&temperature, &humidity, true);
46  }
47 
48  if (success) {
49  ESP_LOGD(TAG, "Got Temperature=%.1f°C Humidity=%.1f%%", temperature, humidity);
50 
51  if (this->temperature_sensor_ != nullptr)
52  this->temperature_sensor_->publish_state(temperature);
53  if (this->humidity_sensor_ != nullptr)
54  this->humidity_sensor_->publish_state(humidity);
55  this->status_clear_warning();
56  } else {
57  const char *str = "";
58  if (this->is_auto_detect_) {
59  str = " and consider manually specifying the DHT model using the model option";
60  }
61  ESP_LOGW(TAG, "Invalid readings! Please check your wiring (pull-up resistor, pin number)%s.", str);
62  if (this->temperature_sensor_ != nullptr)
64  if (this->humidity_sensor_ != nullptr)
65  this->humidity_sensor_->publish_state(NAN);
66  this->status_set_warning();
67  }
68 }
69 
72  this->model_ = model;
73  this->is_auto_detect_ = model == DHT_MODEL_AUTO_DETECT;
74 }
75 bool HOT IRAM_ATTR DHT::read_sensor_(float *temperature, float *humidity, bool report_errors) {
76  *humidity = NAN;
77  *temperature = NAN;
78 
79  int error_code = 0;
80  int8_t i = 0;
81  uint8_t data[5] = {0, 0, 0, 0, 0};
82 
83  this->pin_->digital_write(false);
85  this->pin_->digital_write(false);
86 
87  if (this->model_ == DHT_MODEL_DHT11) {
88  delayMicroseconds(18000);
89  } else if (this->model_ == DHT_MODEL_SI7021) {
90 #ifdef USE_ESP8266
91  delayMicroseconds(500);
92  this->pin_->digital_write(true);
94 #else
95  delayMicroseconds(400);
96  this->pin_->digital_write(true);
97 #endif
98  } else if (this->model_ == DHT_MODEL_DHT22_TYPE2) {
99  delayMicroseconds(2000);
100  } else if (this->model_ == DHT_MODEL_AM2120 || this->model_ == DHT_MODEL_AM2302) {
101  delayMicroseconds(1000);
102  } else {
103  delayMicroseconds(800);
104  }
105  this->pin_->pin_mode(this->pin_->get_flags());
106 
107  {
108  InterruptLock lock;
109  // Host pull up 20-40us then DHT response 80us
110  // Start waiting for initial rising edge at the center when we
111  // expect the DHT response (30us+40us)
112  delayMicroseconds(70);
113 
114  uint8_t bit = 7;
115  uint8_t byte = 0;
116 
117  for (i = -1; i < 40; i++) {
118  uint32_t start_time = micros();
119 
120  // Wait for rising edge
121  while (!this->pin_->digital_read()) {
122  if (micros() - start_time > 90) {
123  if (i < 0) {
124  error_code = 1;
125  } else {
126  error_code = 2;
127  }
128  break;
129  }
130  }
131  if (error_code != 0)
132  break;
133 
134  start_time = micros();
135  uint32_t end_time = start_time;
136 
137  // Wait for falling edge
138  while (this->pin_->digital_read()) {
139  end_time = micros();
140  if (end_time - start_time > 90) {
141  if (i < 0) {
142  error_code = 3;
143  } else {
144  error_code = 4;
145  }
146  break;
147  }
148  }
149  if (error_code != 0)
150  break;
151 
152  if (i < 0)
153  continue;
154 
155  if (end_time - start_time >= 40) {
156  data[byte] |= 1 << bit;
157  }
158  if (bit == 0) {
159  bit = 7;
160  byte++;
161  } else {
162  bit--;
163  }
164  }
165  }
166  if (!report_errors && error_code != 0)
167  return false;
168 
169  switch (error_code) {
170  case 1:
171  ESP_LOGW(TAG, "Waiting for DHT communication to clear failed!");
172  return false;
173  case 2:
174  ESP_LOGW(TAG, "Rising edge for bit %d failed!", i);
175  return false;
176  case 3:
177  ESP_LOGW(TAG, "Requesting data from DHT failed!");
178  return false;
179  case 4:
180  ESP_LOGW(TAG, "Falling edge for bit %d failed!", i);
181  return false;
182  case 0:
183  default:
184  break;
185  }
186 
187  ESP_LOGVV(TAG,
188  "Data: Hum=0b" BYTE_TO_BINARY_PATTERN BYTE_TO_BINARY_PATTERN
189  ", Temp=0b" BYTE_TO_BINARY_PATTERN BYTE_TO_BINARY_PATTERN ", Checksum=0b" BYTE_TO_BINARY_PATTERN,
190  BYTE_TO_BINARY(data[0]), BYTE_TO_BINARY(data[1]), BYTE_TO_BINARY(data[2]), BYTE_TO_BINARY(data[3]),
191  BYTE_TO_BINARY(data[4]));
192 
193  uint8_t checksum_a = data[0] + data[1] + data[2] + data[3];
194  // On the DHT11, two algorithms for the checksum seem to be used, either the one from the DHT22,
195  // or just using bytes 0 and 2
196  uint8_t checksum_b = this->model_ == DHT_MODEL_DHT11 ? (data[0] + data[2]) : checksum_a;
197 
198  if (checksum_a != data[4] && checksum_b != data[4]) {
199  if (report_errors) {
200  ESP_LOGW(TAG, "Checksum invalid: %u!=%u", checksum_a, data[4]);
201  }
202  return false;
203  }
204 
205  if (this->model_ == DHT_MODEL_DHT11) {
206  if (checksum_a == data[4]) {
207  // Data format: 8bit integral RH data + 8bit decimal RH data + 8bit integral T data + 8bit decimal T data + 8bit
208  // check sum - some models always have 0 in the decimal part
209  const uint16_t raw_temperature = uint16_t(data[2]) * 10 + (data[3] & 0x7F);
210  *temperature = raw_temperature / 10.0f;
211  if ((data[3] & 0x80) != 0) {
212  // negative
213  *temperature *= -1;
214  }
215 
216  const uint16_t raw_humidity = uint16_t(data[0]) * 10 + data[1];
217  *humidity = raw_humidity / 10.0f;
218  } else {
219  // For compatibility with DHT11 models which might only use 2 bytes checksums, only use the data from these two
220  // bytes
221  *temperature = data[2];
222  *humidity = data[0];
223  }
224  } else {
225  uint16_t raw_humidity = (uint16_t(data[0] & 0xFF) << 8) | (data[1] & 0xFF);
226  uint16_t raw_temperature = (uint16_t(data[2] & 0xFF) << 8) | (data[3] & 0xFF);
227 
228  if (raw_temperature & 0x8000) {
229  if (!(raw_temperature & 0x4000))
230  raw_temperature = ~(raw_temperature & 0x7FFF);
231  } else if (raw_temperature & 0x800) {
232  raw_temperature |= 0xf000;
233  }
234 
235  if (raw_temperature == 1 && raw_humidity == 10) {
236  if (report_errors) {
237  ESP_LOGW(TAG, "Invalid temperature+humidity! Sensor reported 1°C and 1%% Hum");
238  }
239  return false;
240  }
241 
242  *humidity = raw_humidity * 0.1f;
243  if (*humidity > 100)
244  *humidity = NAN;
245  *temperature = int16_t(raw_temperature) * 0.1f;
246  }
247 
248  if (*temperature == 0.0f && (*humidity == 1.0f || *humidity == 2.0f)) {
249  if (report_errors) {
250  ESP_LOGW(TAG, "DHT reports invalid data. Is the update interval too high or the sensor damaged?");
251  }
252  return false;
253  }
254 
255  return true;
256 }
257 
258 } // namespace dht
259 } // namespace esphome
virtual void digital_write(bool value)=0
sensor::Sensor * temperature_sensor_
Definition: dht.h:60
const float DATA
For components that import data from directly connected sensors like DHT.
Definition: component.cpp:19
DHTModel model_
Definition: dht.h:58
void set_dht_model(DHTModel model)
Manually select the DHT model.
Definition: dht.cpp:71
InternalGPIOPin * pin_
Definition: dht.h:57
void status_set_warning(const char *message="unspecified")
Definition: component.cpp:151
bool is_auto_detect_
Definition: dht.h:59
sensor::Sensor * humidity_sensor_
Definition: dht.h:61
virtual void pin_mode(gpio::Flags flags)=0
virtual void setup()=0
uint32_t IRAM_ATTR HOT micros()
Definition: core.cpp:27
float get_setup_priority() const override
HARDWARE_LATE setup priority.
Definition: dht.cpp:70
void dump_config() override
Definition: dht.cpp:16
virtual gpio::Flags get_flags() const =0
Retrieve GPIO pin flags.
void status_clear_warning()
Definition: component.cpp:166
void publish_state(float state)
Publish a new state to the front-end.
Definition: sensor.cpp:39
DHTModel
Definition: dht.h:10
uint16_t temperature
Definition: sun_gtil2.cpp:26
bool read_sensor_(float *temperature, float *humidity, bool report_errors)
Definition: dht.cpp:75
virtual bool digital_read()=0
void setup() override
Set up the pins and check connection.
Definition: dht.cpp:10
void update() override
Update sensor values and push them to the frontend.
Definition: dht.cpp:34
Helper class to disable interrupts.
Definition: helpers.h:614
Implementation of SPI Controller mode.
Definition: a01nyub.cpp:7
void IRAM_ATTR HOT delayMicroseconds(uint32_t us)
Definition: core.cpp:28