ESPHome  2024.12.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  end_time = micros();
139  if (end_time - start_time > 90) {
140  if (i < 0) {
141  error_code = 3;
142  } else {
143  error_code = 4;
144  }
145  break;
146  }
147  }
148  if (error_code != 0)
149  break;
150 
151  if (i < 0)
152  continue;
153 
154  if (end_time - start_time >= 40) {
155  data[byte] |= 1 << bit;
156  }
157  if (bit == 0) {
158  bit = 7;
159  byte++;
160  } else
161  bit--;
162  }
163  }
164  if (!report_errors && error_code != 0)
165  return false;
166 
167  switch (error_code) {
168  case 1:
169  ESP_LOGW(TAG, "Waiting for DHT communication to clear failed!");
170  return false;
171  case 2:
172  ESP_LOGW(TAG, "Rising edge for bit %d failed!", i);
173  return false;
174  case 3:
175  ESP_LOGW(TAG, "Requesting data from DHT failed!");
176  return false;
177  case 4:
178  ESP_LOGW(TAG, "Falling edge for bit %d failed!", i);
179  return false;
180  case 0:
181  default:
182  break;
183  }
184 
185  ESP_LOGVV(TAG,
186  "Data: Hum=0b" BYTE_TO_BINARY_PATTERN BYTE_TO_BINARY_PATTERN
187  ", Temp=0b" BYTE_TO_BINARY_PATTERN BYTE_TO_BINARY_PATTERN ", Checksum=0b" BYTE_TO_BINARY_PATTERN,
188  BYTE_TO_BINARY(data[0]), BYTE_TO_BINARY(data[1]), BYTE_TO_BINARY(data[2]), BYTE_TO_BINARY(data[3]),
189  BYTE_TO_BINARY(data[4]));
190 
191  uint8_t checksum_a = data[0] + data[1] + data[2] + data[3];
192  // On the DHT11, two algorithms for the checksum seem to be used, either the one from the DHT22,
193  // or just using bytes 0 and 2
194  uint8_t checksum_b = this->model_ == DHT_MODEL_DHT11 ? (data[0] + data[2]) : checksum_a;
195 
196  if (checksum_a != data[4] && checksum_b != data[4]) {
197  if (report_errors) {
198  ESP_LOGW(TAG, "Checksum invalid: %u!=%u", checksum_a, data[4]);
199  }
200  return false;
201  }
202 
203  if (this->model_ == DHT_MODEL_DHT11) {
204  if (checksum_a == data[4]) {
205  // Data format: 8bit integral RH data + 8bit decimal RH data + 8bit integral T data + 8bit decimal T data + 8bit
206  // check sum - some models always have 0 in the decimal part
207  const uint16_t raw_temperature = uint16_t(data[2]) * 10 + (data[3] & 0x7F);
208  *temperature = raw_temperature / 10.0f;
209  if ((data[3] & 0x80) != 0) {
210  // negative
211  *temperature *= -1;
212  }
213 
214  const uint16_t raw_humidity = uint16_t(data[0]) * 10 + data[1];
215  *humidity = raw_humidity / 10.0f;
216  } else {
217  // For compatibility with DHT11 models which might only use 2 bytes checksums, only use the data from these two
218  // bytes
219  *temperature = data[2];
220  *humidity = data[0];
221  }
222  } else {
223  uint16_t raw_humidity = (uint16_t(data[0] & 0xFF) << 8) | (data[1] & 0xFF);
224  uint16_t raw_temperature = (uint16_t(data[2] & 0xFF) << 8) | (data[3] & 0xFF);
225 
226  if (raw_temperature & 0x8000) {
227  if (!(raw_temperature & 0x4000))
228  raw_temperature = ~(raw_temperature & 0x7FFF);
229  } else if (raw_temperature & 0x800) {
230  raw_temperature |= 0xf000;
231  }
232 
233  if (raw_temperature == 1 && raw_humidity == 10) {
234  if (report_errors) {
235  ESP_LOGW(TAG, "Invalid temperature+humidity! Sensor reported 1°C and 1%% Hum");
236  }
237  return false;
238  }
239 
240  *humidity = raw_humidity * 0.1f;
241  if (*humidity > 100)
242  *humidity = NAN;
243  *temperature = int16_t(raw_temperature) * 0.1f;
244  }
245 
246  if (*temperature == 0.0f && (*humidity == 1.0f || *humidity == 2.0f)) {
247  if (report_errors) {
248  ESP_LOGW(TAG, "DHT reports invalid data. Is the update interval too high or the sensor damaged?");
249  }
250  return false;
251  }
252 
253  return true;
254 }
255 
256 } // namespace dht
257 } // 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:606
Implementation of SPI Controller mode.
Definition: a01nyub.cpp:7
void IRAM_ATTR HOT delayMicroseconds(uint32_t us)
Definition: core.cpp:28