Skip to content
This repository was archived by the owner on Jan 24, 2026. It is now read-only.

Commit e1cc4d3

Browse files
authored
sht4x: Driver + example for SHT4x temperature and humidity sensor (#165)
1 parent 98c7ae5 commit e1cc4d3

16 files changed

Lines changed: 674 additions & 0 deletions

File tree

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,7 @@ See [GitHub examples](https://github.com/UncleRus/esp-idf-lib/tree/master/exampl
129129
|----------------|-------------------------------------------------------------------------|---------|---------------
130130
| **dht** | Driver for DHT11, AM2301 (DHT21, DHT22, AM2302, AM2321), Itead Si7021 | BSD | No
131131
| **sht3x** | Driver for Sensirion SHT3x digital temperature and humidity sensor | BSD | Yes
132+
| **sht4x** | Driver for Sensirion SHT4x digital temperature and humidity sensor | BSD | Yes
132133
| **si7021** | Driver for Si7013/Si7020/Si7021/HTU21D/SHT2x and compatible | BSD | Yes
133134
| **ds18x20** | Driver for DS18B20/DS18S20 families of one-wire temperature sensor ICs | BSD | No
134135
| **max31725** | Driver for MAX31725/MAX31726 temperature sensors | BSD | Yes

components/sht4x/CMakeLists.txt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
idf_component_register(
2+
SRCS "sht4x.c"
3+
INCLUDE_DIRS .
4+
REQUIRES i2cdev log esp_idf_lib_helpers
5+
)

components/sht4x/LICENSE

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
Copyright (C) 2021 Ruslan V. Uss (https://github.com/UncleRus)
2+
3+
Redistribution and use in source and binary forms, with or without
4+
modification, are permitted provided that the following conditions are met:
5+
6+
1. Redistributions of source code must retain the above copyright notice,
7+
this list of conditions and the following disclaimer.
8+
9+
2. Redistributions in binary form must reproduce the above copyright notice,
10+
this list of conditions and the following disclaimer in the documentation
11+
and/or other materials provided with the distribution.
12+
13+
3. Neither the name of the copyright holder nor the names of itscontributors
14+
may be used to endorse or promote products derived from this software without
15+
specific prior written permission.
16+
17+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
18+
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19+
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20+
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
21+
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22+
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
23+
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
24+
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
25+
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26+
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

components/sht4x/component.mk

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
COMPONENT_ADD_INCLUDEDIRS = .
2+
COMPONENT_DEPENDS = i2cdev log

components/sht4x/sht4x.c

Lines changed: 285 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,285 @@
1+
/**
2+
* @file sht4x.c
3+
*
4+
* ESP-IDF driver for Sensirion SHT40/SHT41 digital temperature and humidity sensor
5+
*
6+
* Copyright (C) 2021 Ruslan V. Uss <https://github.com/UncleRus>
7+
*
8+
* BSD Licensed as described in the file LICENSE
9+
*/
10+
11+
#include <esp_log.h>
12+
#include <freertos/FreeRTOS.h>
13+
#include <freertos/task.h>
14+
#include <esp_idf_lib_helpers.h>
15+
#include <esp_timer.h>
16+
#include "sht4x.h"
17+
18+
#define I2C_FREQ_HZ 1000000 // 1MHz
19+
20+
static const char *TAG = "sht4x";
21+
22+
#define CMD_RESET 0x94
23+
#define CMD_SERIAL 0x89
24+
#define CMD_MEAS_HIGH 0xfd
25+
#define CMD_MEAS_MED 0xf6
26+
#define CMD_MEAS_LOW 0xe0
27+
#define CMD_MEAS_H_HIGH_LONG 0x39
28+
#define CMD_MEAS_H_HIGH_SHORT 0x32
29+
#define CMD_MEAS_H_MED_LONG 0x2f
30+
#define CMD_MEAS_H_MED_SHORT 0x24
31+
#define CMD_MEAS_H_LOW_LONG 0x1e
32+
#define CMD_MEAS_H_LOW_SHORT 0x15
33+
34+
#define CHECK(x) do { esp_err_t __; if ((__ = x) != ESP_OK) return __; } while (0)
35+
#define CHECK_ARG(VAL) do { if (!(VAL)) return ESP_ERR_INVALID_ARG; } while (0)
36+
37+
#define G_POLYNOM 0x31
38+
39+
static uint8_t crc8(uint8_t data[], size_t len)
40+
{
41+
uint8_t crc = 0xff;
42+
43+
for (size_t i = 0; i < len; i++)
44+
{
45+
crc ^= data[i];
46+
for (size_t i = 0; i < 8; i++)
47+
crc = crc & 0x80 ? (crc << 1) ^ G_POLYNOM : crc << 1;
48+
}
49+
return crc;
50+
}
51+
52+
static inline size_t get_duration_ms(sht4x_t *dev)
53+
{
54+
switch (dev->heater)
55+
{
56+
case SHT4X_HEATER_HIGH_LONG:
57+
case SHT4X_HEATER_MEDIUM_LONG:
58+
case SHT4X_HEATER_LOW_LONG:
59+
return 1100;
60+
case SHT4X_HEATER_HIGH_SHORT:
61+
case SHT4X_HEATER_MEDIUM_SHORT:
62+
case SHT4X_HEATER_LOW_SHORT:
63+
return 110;
64+
default:
65+
switch (dev->repeatability)
66+
{
67+
case SHT4X_HIGH:
68+
return 10;
69+
case SHT4X_MEDIUM:
70+
return 5;
71+
default:
72+
return 2;
73+
}
74+
}
75+
}
76+
77+
static inline uint8_t get_meas_cmd(sht4x_t *dev)
78+
{
79+
switch (dev->heater)
80+
{
81+
case SHT4X_HEATER_HIGH_LONG:
82+
return CMD_MEAS_H_HIGH_LONG;
83+
case SHT4X_HEATER_HIGH_SHORT:
84+
return CMD_MEAS_H_HIGH_SHORT;
85+
case SHT4X_HEATER_MEDIUM_LONG:
86+
return CMD_MEAS_H_MED_LONG;
87+
case SHT4X_HEATER_MEDIUM_SHORT:
88+
return CMD_MEAS_H_MED_SHORT;
89+
case SHT4X_HEATER_LOW_LONG:
90+
return CMD_MEAS_H_LOW_LONG;
91+
case SHT4X_HEATER_LOW_SHORT:
92+
return CMD_MEAS_H_LOW_SHORT;
93+
default:
94+
switch (dev->repeatability)
95+
{
96+
case SHT4X_HIGH:
97+
return CMD_MEAS_HIGH;
98+
case SHT4X_MEDIUM:
99+
return CMD_MEAS_MED;
100+
default:
101+
return CMD_MEAS_LOW;
102+
}
103+
}
104+
}
105+
106+
static inline esp_err_t send_cmd_nolock(sht4x_t *dev, uint8_t cmd)
107+
{
108+
ESP_LOGD(TAG, "Sending cmd %02x...", cmd);
109+
return i2c_dev_write(&dev->i2c_dev, NULL, 0, &cmd, 1);
110+
}
111+
112+
static inline esp_err_t read_res_nolock(sht4x_t *dev, sht4x_raw_data_t res)
113+
{
114+
esp_err_t r = i2c_dev_read(&dev->i2c_dev, NULL, 0, res, SHT4X_RAW_DATA_SIZE);
115+
ESP_LOGD(TAG, "Got response %02x %02x %02x %02x %02x %02x",
116+
res[0], res[1], res[2], res[3], res[4], res[5]);
117+
118+
if (res[2] != crc8(res, 2) || res[5] != crc8(res + 3, 2))
119+
{
120+
ESP_LOGE(TAG, "Invalid CRC");
121+
return ESP_ERR_INVALID_CRC;
122+
}
123+
124+
return r;
125+
}
126+
127+
static esp_err_t send_cmd(sht4x_t *dev, uint8_t cmd)
128+
{
129+
I2C_DEV_TAKE_MUTEX(&dev->i2c_dev);
130+
I2C_DEV_CHECK(&dev->i2c_dev, send_cmd_nolock(dev, cmd));
131+
I2C_DEV_GIVE_MUTEX(&dev->i2c_dev);
132+
133+
return ESP_OK;
134+
}
135+
136+
static esp_err_t read_res(sht4x_t *dev, sht4x_raw_data_t res)
137+
{
138+
I2C_DEV_TAKE_MUTEX(&dev->i2c_dev);
139+
I2C_DEV_CHECK(&dev->i2c_dev, read_res_nolock(dev, res));
140+
I2C_DEV_GIVE_MUTEX(&dev->i2c_dev);
141+
142+
return ESP_OK;
143+
}
144+
145+
static esp_err_t exec_cmd(sht4x_t *dev, uint8_t cmd, size_t delay_ticks, sht4x_raw_data_t res)
146+
{
147+
I2C_DEV_TAKE_MUTEX(&dev->i2c_dev);
148+
I2C_DEV_CHECK(&dev->i2c_dev, send_cmd_nolock(dev, cmd));
149+
if (delay_ticks)
150+
vTaskDelay(delay_ticks);
151+
I2C_DEV_CHECK(&dev->i2c_dev, read_res_nolock(dev, res));
152+
I2C_DEV_GIVE_MUTEX(&dev->i2c_dev);
153+
154+
return ESP_OK;
155+
}
156+
157+
static inline bool is_measuring(sht4x_t *dev)
158+
{
159+
// not running if measurement is not started
160+
if (!dev->meas_started)
161+
return false;
162+
163+
// not running if time elapsed is greater than duration
164+
uint64_t elapsed = esp_timer_get_time() - dev->meas_start_time;
165+
return elapsed < get_duration_ms(dev) * 1000;
166+
}
167+
168+
///////////////////////////////////////////////////////////////////////////////
169+
170+
esp_err_t sht4x_init_desc(sht4x_t *dev, i2c_port_t port, gpio_num_t sda_gpio, gpio_num_t scl_gpio)
171+
{
172+
CHECK_ARG(dev);
173+
174+
dev->i2c_dev.port = port;
175+
dev->i2c_dev.addr = SHT4X_I2C_ADDRESS;
176+
dev->i2c_dev.cfg.sda_io_num = sda_gpio;
177+
dev->i2c_dev.cfg.scl_io_num = scl_gpio;
178+
#if HELPER_TARGET_IS_ESP32
179+
dev->i2c_dev.cfg.master.clk_speed = I2C_FREQ_HZ;
180+
#endif
181+
182+
return i2c_dev_create_mutex(&dev->i2c_dev);
183+
}
184+
185+
esp_err_t sht4x_free_desc(sht4x_t *dev)
186+
{
187+
CHECK_ARG(dev);
188+
189+
return i2c_dev_delete_mutex(&dev->i2c_dev);
190+
}
191+
192+
esp_err_t sht4x_init(sht4x_t *dev)
193+
{
194+
CHECK_ARG(dev);
195+
196+
dev->repeatability = SHT4X_HIGH;
197+
dev->heater = SHT4X_HEATER_OFF;
198+
199+
sht4x_raw_data_t s;
200+
CHECK(exec_cmd(dev, CMD_SERIAL, 0, s));
201+
dev->serial = ((uint32_t)s[0] << 24) | ((uint32_t)s[1] << 16) | ((uint32_t)s[3] << 8) | s[4];
202+
203+
return sht4x_reset(dev);
204+
}
205+
206+
esp_err_t sht4x_reset(sht4x_t *dev)
207+
{
208+
dev->meas_start_time = 0;
209+
dev->meas_started = false;
210+
211+
CHECK(send_cmd(dev, CMD_RESET));
212+
vTaskDelay(1);
213+
214+
return ESP_OK;
215+
}
216+
217+
esp_err_t sht4x_measure(sht4x_t *dev, float *temperature, float *humidity)
218+
{
219+
CHECK_ARG(dev && (temperature || humidity));
220+
221+
sht4x_raw_data_t raw;
222+
CHECK(exec_cmd(dev, get_meas_cmd(dev), sht4x_get_measurement_duration(dev), raw));
223+
224+
return sht4x_compute_values(raw, temperature, humidity);
225+
}
226+
227+
esp_err_t sht4x_start_measurement(sht4x_t *dev)
228+
{
229+
CHECK_ARG(dev);
230+
231+
if (is_measuring(dev))
232+
{
233+
ESP_LOGE(TAG, "Measurement is still running");
234+
return ESP_ERR_INVALID_STATE;
235+
}
236+
237+
dev->meas_start_time = esp_timer_get_time();
238+
CHECK(send_cmd(dev, get_meas_cmd(dev)));
239+
dev->meas_started = true;
240+
241+
return ESP_OK;
242+
}
243+
244+
size_t sht4x_get_measurement_duration(sht4x_t *dev)
245+
{
246+
if (!dev) return 0;
247+
248+
size_t res = pdMS_TO_TICKS(get_duration_ms(dev));
249+
return res == 0 ? 1 : res;
250+
}
251+
252+
esp_err_t sht4x_get_raw_data(sht4x_t *dev, sht4x_raw_data_t raw)
253+
{
254+
CHECK_ARG(dev);
255+
256+
if (is_measuring(dev))
257+
{
258+
ESP_LOGE(TAG, "Measurement is still running");
259+
return ESP_ERR_INVALID_STATE;
260+
}
261+
262+
dev->meas_started = false;
263+
return read_res(dev, raw);
264+
}
265+
266+
esp_err_t sht4x_compute_values(sht4x_raw_data_t raw_data, float *temperature, float *humidity)
267+
{
268+
CHECK_ARG(raw_data && (temperature || humidity));
269+
270+
if (temperature)
271+
*temperature = ((uint16_t)raw_data[0] << 8 | raw_data[1]) * 175.0 / 65535.0 - 45.0;
272+
273+
if (humidity)
274+
*humidity = ((uint16_t)raw_data[3] << 8 | raw_data[4]) * 125.0 / 65535.0 - 6.0;
275+
276+
return ESP_OK;
277+
}
278+
279+
esp_err_t sht4x_get_results(sht4x_t *dev, float *temperature, float *humidity)
280+
{
281+
sht4x_raw_data_t raw;
282+
CHECK(sht4x_get_raw_data(dev, raw));
283+
284+
return sht4x_compute_values(raw, temperature, humidity);
285+
}

0 commit comments

Comments
 (0)