ESPHome  2024.11.1
htu31d.cpp
Go to the documentation of this file.
1 /*
2  * This file contains source code derived from Adafruit_HTU31D which is under
3  * the BSD license:
4  * Written by Limor Fried/Ladyada for Adafruit Industries.
5  * BSD license, all text above must be included in any redistribution.
6  *
7  * Modifications made by Mark Spicer.
8  */
9 
10 #include "htu31d.h"
11 #include "esphome/core/hal.h"
12 #include "esphome/core/helpers.h"
13 #include "esphome/core/log.h"
14 
15 #include <cinttypes>
16 
17 namespace esphome {
18 namespace htu31d {
19 
21 static const char *const TAG = "htu31d";
22 
24 static const uint8_t HTU31D_DEFAULT_I2CADDR = 0x40;
25 
27 static const uint8_t HTU31D_READTEMPHUM = 0x00;
28 
30 static const uint8_t HTU31D_CONVERSION = 0x40;
31 
33 static const uint8_t HTU31D_READSERIAL = 0x0A;
34 
36 static const uint8_t HTU31D_HEATERON = 0x04;
37 
39 static const uint8_t HTU31D_HEATEROFF = 0x02;
40 
42 static const uint8_t HTU31D_RESET = 0x1E;
43 
45 static const uint8_t HTU31D_DIAGNOSTICS = 0x08;
46 
52 uint8_t compute_crc(uint32_t value) {
53  uint32_t polynom = 0x98800000; // x^8 + x^5 + x^4 + 1
54  uint32_t msb = 0x80000000;
55  uint32_t mask = 0xFF800000;
56  uint32_t threshold = 0x00000080;
57  uint32_t result = value;
58 
59  while (msb != threshold) {
60  // Check if msb of current value is 1 and apply XOR mask
61  if (result & msb)
62  result = ((result ^ polynom) & mask) | (result & ~mask);
63 
64  // Shift by one
65  msb >>= 1;
66  mask >>= 1;
67  polynom >>= 1;
68  }
69 
70  return result;
71 }
72 
78  ESP_LOGCONFIG(TAG, "Setting up esphome/components/htu31d HTU31D...");
79 
80  if (!this->reset_()) {
81  this->mark_failed();
82  return;
83  }
84 
85  if (this->read_serial_num_() == 0) {
86  this->mark_failed();
87  return;
88  }
89 }
90 
96  ESP_LOGD(TAG, "Checking temperature and humidty values");
97 
98  // Trigger a conversion. From the spec sheet: The conversion command triggers
99  // a single temperature and humidity conversion.
100  if (this->write_register(HTU31D_CONVERSION, nullptr, 0) != i2c::ERROR_OK) {
101  this->status_set_warning();
102  ESP_LOGE(TAG, "Received errror writing conversion register");
103  return;
104  }
105 
106  // Wait conversion time.
107  this->set_timeout(20, [this]() {
108  uint8_t thdata[6];
109  if (this->read_register(HTU31D_READTEMPHUM, thdata, 6) != i2c::ERROR_OK) {
110  this->status_set_warning();
111  ESP_LOGE(TAG, "Error reading temperature/humidty register");
112  return;
113  }
114 
115  // Calculate temperature value.
116  uint16_t raw_temp = encode_uint16(thdata[0], thdata[1]);
117 
118  uint8_t crc = compute_crc((uint32_t) raw_temp << 8);
119  if (crc != thdata[2]) {
120  this->status_set_warning();
121  ESP_LOGE(TAG, "Error validating temperature CRC");
122  return;
123  }
124 
125  float temperature = raw_temp;
126  temperature /= 65535.0f;
127  temperature *= 165;
128  temperature -= 40;
129 
130  if (this->temperature_ != nullptr) {
131  this->temperature_->publish_state(temperature);
132  }
133 
134  // Calculate humidty value.
135  uint16_t raw_hum = encode_uint16(thdata[3], thdata[4]);
136 
137  crc = compute_crc((uint32_t) raw_hum << 8);
138  if (crc != thdata[5]) {
139  this->status_set_warning();
140  ESP_LOGE(TAG, "Error validating humidty CRC");
141  return;
142  }
143 
144  float humidity = raw_hum;
145  humidity /= 65535.0f;
146  humidity *= 100;
147 
148  if (this->humidity_ != nullptr) {
149  this->humidity_->publish_state(humidity);
150  }
151 
152  ESP_LOGD(TAG, "Got Temperature=%.1f°C Humidity=%.1f%%", temperature, humidity);
153  this->status_clear_warning();
154  });
155 }
156 
161  ESP_LOGCONFIG(TAG, "HTU31D:");
162  LOG_I2C_DEVICE(this);
163  if (this->is_failed()) {
164  ESP_LOGE(TAG, "Communication with HTU31D failed!");
165  }
166  LOG_UPDATE_INTERVAL(this);
167  LOG_SENSOR(" ", "Temperature", this->temperature_);
168  LOG_SENSOR(" ", "Humidity", this->humidity_);
169 }
170 
177  if (this->write_register(HTU31D_RESET, nullptr, 0) != i2c::ERROR_OK) {
178  return false;
179  }
180 
181  delay(15);
182  return true;
183 }
184 
191  uint8_t reply[4];
192  uint32_t serial = 0;
193  uint8_t padding = 0;
194 
195  // Verify we can read the device serial.
196  if (this->read_register(HTU31D_READSERIAL, reply, 4) != i2c::ERROR_OK) {
197  ESP_LOGE(TAG, "Error reading device serial");
198  return 0;
199  }
200 
201  serial = encode_uint32(reply[0], reply[1], reply[2], padding);
202 
203  uint8_t crc = compute_crc(serial);
204  if (crc != reply[3]) {
205  ESP_LOGE(TAG, "Error validating serial CRC");
206  return 0;
207  }
208 
209  ESP_LOGD(TAG, "Found serial: 0x%" PRIX32, serial);
210 
211  return serial;
212 }
213 
221  uint8_t reply[1];
222  uint8_t heater_enabled_position = 0;
223  uint8_t mask = 1 << heater_enabled_position;
224  uint8_t diagnostics = 0;
225 
226  if (this->read_register(HTU31D_DIAGNOSTICS, reply, 1) != i2c::ERROR_OK) {
227  ESP_LOGE(TAG, "Error reading device serial");
228  return false;
229  }
230 
231  diagnostics = reply[0];
232  return (diagnostics & mask) != 0;
233 }
234 
241  bool current = this->is_heater_enabled();
242 
243  // If the current state matches the desired state, there is nothing to do.
244  if (current == desired) {
245  return;
246  }
247 
248  // Update heater state.
250  if (desired) {
251  err = this->write_register(HTU31D_HEATERON, nullptr, 0);
252  } else {
253  err = this->write_register(HTU31D_HEATEROFF, nullptr, 0);
254  }
255 
256  // Record any error.
257  if (err != i2c::ERROR_OK) {
258  this->status_set_warning();
259  ESP_LOGE(TAG, "Received error updating heater state");
260  return;
261  }
262 }
263 
270 } // namespace htu31d
271 } // namespace esphome
const float DATA
For components that import data from directly connected sensors like DHT.
Definition: component.cpp:19
ErrorCode read_register(uint8_t a_register, uint8_t *data, size_t len, bool stop=true)
reads an array of bytes from a specific register in the I²C device
Definition: i2c.cpp:10
void status_set_warning(const char *message="unspecified")
Definition: component.cpp:151
void setup() override
Resets the sensor and ensures that the devices serial number can be read over I2C.
Definition: htu31d.cpp:77
bool is_failed() const
Definition: component.cpp:143
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
bool reset_()
Sends a &#39;reset&#39; request to the HTU31D, followed by a 15ms delay.
Definition: htu31d.cpp:176
constexpr uint32_t encode_uint32(uint8_t byte1, uint8_t byte2, uint8_t byte3, uint8_t byte4)
Encode a 32-bit value given four bytes in most to least significant byte order.
Definition: helpers.h:187
sensor::Sensor * humidity_
Definition: htu31d.h:30
No error found during execution of method.
Definition: i2c_bus.h:13
float get_setup_priority() const override
Sets the startup priority for this component.
Definition: htu31d.cpp:269
void dump_config() override
Update the sensor values (temperature+humidity).
Definition: htu31d.cpp:160
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
uint32_t read_serial_num_()
Reads the serial number from the device and checks the CRC.
Definition: htu31d.cpp:190
uint8_t compute_crc(uint32_t value)
Computes a CRC result for the provided input.
Definition: htu31d.cpp:52
uint16_t temperature
Definition: sun_gtil2.cpp:26
constexpr uint16_t encode_uint16(uint8_t msb, uint8_t lsb)
Encode a 16-bit value given the most and least significant byte.
Definition: helpers.h:183
u_int8_t raw_temp
bool is_heater_enabled()
Checks the diagnostics register to determine if the heater is currently enabled.
Definition: htu31d.cpp:220
virtual void mark_failed()
Mark this component as failed.
Definition: component.cpp:118
Implementation of SPI Controller mode.
Definition: a01nyub.cpp:7
ErrorCode write_register(uint8_t a_register, const uint8_t *data, size_t len, bool stop=true)
writes an array of bytes to a specific register in the I²C device
Definition: i2c.cpp:25
void set_heater_state(bool desired)
Sets the heater state on or off.
Definition: htu31d.cpp:240
void update() override
Setup (reset) the sensor and check connection.
Definition: htu31d.cpp:95
ErrorCode
Error codes returned by I2CBus and I2CDevice methods.
Definition: i2c_bus.h:11
sensor::Sensor * temperature_
Definition: htu31d.h:29
void IRAM_ATTR HOT delay(uint32_t ms)
Definition: core.cpp:26