1 /*
2  * Copyright (c) 2021 Leonard Pollak
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #define DT_DRV_COMPAT sensirion_sht4x
8 
9 #include <device.h>
10 #include <drivers/i2c.h>
11 #include <kernel.h>
12 #include <drivers/sensor.h>
13 #include <sys/__assert.h>
14 #include <logging/log.h>
15 #include <sys/byteorder.h>
16 #include <sys/crc.h>
17 
18 #include <drivers/sensor/sht4x.h>
19 #include "sht4x.h"
20 
21 LOG_MODULE_REGISTER(SHT4X, CONFIG_SENSOR_LOG_LEVEL);
22 
sht4x_compute_crc(uint16_t value)23 static uint8_t sht4x_compute_crc(uint16_t value)
24 {
25 	uint8_t buf[2];
26 
27 	sys_put_be16(value, buf);
28 
29 	return crc8(buf, 2, SHT4X_CRC_POLY, SHT4X_CRC_INIT, false);
30 }
31 
sht4x_write_command(const struct device * dev,uint8_t cmd)32 static int sht4x_write_command(const struct device *dev, uint8_t cmd)
33 {
34 	const struct sht4x_config *cfg = dev->config;
35 	uint8_t tx_buf[1] = { cmd };
36 
37 	return i2c_write_dt(&cfg->bus, tx_buf, sizeof(tx_buf));
38 }
39 
sht4x_read_sample(const struct device * dev,uint16_t * t_sample,uint16_t * rh_sample)40 static int sht4x_read_sample(const struct device *dev,
41 		uint16_t *t_sample,
42 		uint16_t *rh_sample)
43 {
44 	const struct sht4x_config *cfg = dev->config;
45 	uint8_t rx_buf[6];
46 	int rc;
47 
48 	rc = i2c_read_dt(&cfg->bus, rx_buf, sizeof(rx_buf));
49 	if (rc < 0) {
50 		LOG_ERR("Failed to read data from device.");
51 		return rc;
52 	}
53 
54 	*t_sample = sys_get_be16(rx_buf);
55 	if (sht4x_compute_crc(*t_sample) != rx_buf[2]) {
56 		LOG_ERR("Invalid CRC for T.");
57 		return -EIO;
58 	}
59 
60 	*rh_sample = sys_get_be16(&rx_buf[3]);
61 	if (sht4x_compute_crc(*rh_sample) != rx_buf[5]) {
62 		LOG_ERR("Invalid CRC for RH.");
63 		return -EIO;
64 	}
65 
66 	return 0;
67 }
68 
69 /* public API for handling the heater */
sht4x_fetch_with_heater(const struct device * dev)70 int sht4x_fetch_with_heater(const struct device *dev)
71 {
72 	struct sht4x_data *data = dev->data;
73 	int rc;
74 
75 	rc = sht4x_write_command(dev,
76 			heater_cmd[data->heater_power][data->heater_duration]);
77 	if (rc < 0) {
78 		LOG_ERR("Failed to start measurement.");
79 		return rc;
80 	}
81 
82 	k_sleep(K_MSEC(heater_wait_ms[data->heater_duration]));
83 
84 	rc = sht4x_read_sample(dev, &data->t_sample, &data->rh_sample);
85 	if (rc < 0) {
86 		LOG_ERR("Failed to fetch data.");
87 		return rc;
88 	}
89 
90 	return 0;
91 }
92 
sht4x_sample_fetch(const struct device * dev,enum sensor_channel chan)93 static int sht4x_sample_fetch(const struct device *dev,
94 			       enum sensor_channel chan)
95 {
96 	const struct sht4x_config *cfg = dev->config;
97 	struct sht4x_data *data = dev->data;
98 	int rc;
99 
100 	if (chan != SENSOR_CHAN_ALL &&
101 		chan != SENSOR_CHAN_AMBIENT_TEMP &&
102 		chan != SENSOR_CHAN_HUMIDITY) {
103 		return -ENOTSUP;
104 	}
105 
106 	rc = sht4x_write_command(dev, measure_cmd[cfg->repeatability]);
107 	if (rc < 0) {
108 		LOG_ERR("Failed to start measurement.");
109 		return rc;
110 	}
111 
112 	k_sleep(K_USEC(measure_wait_us[cfg->repeatability]));
113 
114 	rc = sht4x_read_sample(dev, &data->t_sample, &data->rh_sample);
115 	if (rc < 0) {
116 		LOG_ERR("Failed to fetch data.");
117 		return rc;
118 	}
119 
120 	return 0;
121 }
122 
sht4x_channel_get(const struct device * dev,enum sensor_channel chan,struct sensor_value * val)123 static int sht4x_channel_get(const struct device *dev,
124 			      enum sensor_channel chan,
125 			      struct sensor_value *val)
126 {
127 	const struct sht4x_data *data = dev->data;
128 
129 	/*
130 	 * See datasheet "Conversion of Signal Output" section
131 	 * for more details on processing sample data.
132 	 */
133 	if (chan == SENSOR_CHAN_AMBIENT_TEMP) {
134 		int64_t tmp;
135 
136 		tmp = data->t_sample * 175;
137 		val->val1 = (int32_t)(tmp / 0xFFFF) - 45;
138 		val->val2 = ((tmp % 0xFFFF) * 1000000) / 0xFFFF;
139 	} else if (chan == SENSOR_CHAN_HUMIDITY) {
140 		uint64_t tmp;
141 
142 		tmp = data->rh_sample * 125U;
143 		val->val1 = (uint32_t)(tmp / 0xFFFF) - 6U;
144 		val->val2 = (tmp % 0xFFFF) * 15625U / 1024U;
145 	} else {
146 		return -ENOTSUP;
147 	}
148 
149 	return 0;
150 }
151 
sht4x_attr_set(const struct device * dev,enum sensor_channel chan,enum sensor_attribute attr,const struct sensor_value * val)152 static int sht4x_attr_set(const struct device *dev,
153 				enum sensor_channel chan,
154 				enum sensor_attribute attr,
155 				const struct sensor_value *val)
156 {
157 	struct sht4x_data *data = dev->data;
158 
159 	if (val->val1 < 0) {
160 		return -EINVAL;
161 	}
162 
163 	switch ((enum sensor_attribute_sht4x)attr) {
164 	case SENSOR_ATTR_SHT4X_HEATER_POWER:
165 		if (val->val1 > SHT4X_HEATER_POWER_IDX_MAX) {
166 			return -EINVAL;
167 		}
168 		data->heater_power = val->val1;
169 		break;
170 	case SENSOR_ATTR_SHT4X_HEATER_DURATION:
171 		if (val->val1 > SHT4X_HEATER_DURATION_IDX_MAX) {
172 			return -EINVAL;
173 		}
174 		data->heater_duration = val->val1;
175 		break;
176 	default:
177 		return -ENOTSUP;
178 	}
179 
180 	return 0;
181 }
182 
sht4x_init(const struct device * dev)183 static int sht4x_init(const struct device *dev)
184 {
185 	const struct sht4x_config *cfg = dev->config;
186 	int rc = 0;
187 
188 	if (!device_is_ready(cfg->bus.bus)) {
189 		LOG_ERR("Device not ready.");
190 		return -ENODEV;
191 	}
192 
193 	rc = sht4x_write_command(dev, SHT4X_CMD_RESET);
194 	if (rc < 0) {
195 		LOG_ERR("Failed to reset the device.");
196 		return rc;
197 	}
198 
199 	k_sleep(K_MSEC(SHT4X_RESET_WAIT_MS));
200 
201 	return 0;
202 }
203 
204 
205 static const struct sensor_driver_api sht4x_api = {
206 	.sample_fetch = sht4x_sample_fetch,
207 	.channel_get = sht4x_channel_get,
208 	.attr_set = sht4x_attr_set,
209 };
210 
211 #define SHT4X_INIT(n)						\
212 	static struct sht4x_data sht4x_data_##n;		\
213 								\
214 	static const struct sht4x_config sht4x_config_##n = {	\
215 		.bus = I2C_DT_SPEC_INST_GET(n),			\
216 		.repeatability = DT_INST_PROP(n, repeatability)	\
217 	};							\
218 								\
219 	DEVICE_DT_INST_DEFINE(n,				\
220 			      sht4x_init,			\
221 			      NULL,				\
222 			      &sht4x_data_##n,			\
223 			      &sht4x_config_##n,		\
224 			      POST_KERNEL,			\
225 			      CONFIG_SENSOR_INIT_PRIORITY,	\
226 			      &sht4x_api);
227 
228 DT_INST_FOREACH_STATUS_OKAY(SHT4X_INIT)
229