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