1 /*
2  * Copyright (c) 2023 Ian Morris
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #define DT_DRV_COMPAT renesas_hs300x
8 
9 #include <zephyr/device.h>
10 #include <zephyr/drivers/i2c.h>
11 #include <zephyr/kernel.h>
12 #include <zephyr/drivers/sensor.h>
13 #include <zephyr/sys/__assert.h>
14 #include <zephyr/logging/log.h>
15 #include <zephyr/sys/byteorder.h>
16 
17 #define HS300X_STATUS_MASK (BIT(0) | BIT(1))
18 
19 LOG_MODULE_REGISTER(HS300X, CONFIG_SENSOR_LOG_LEVEL);
20 
21 struct hs300x_config {
22 	struct i2c_dt_spec bus;
23 };
24 
25 struct hs300x_data {
26 	int16_t t_sample;
27 	uint16_t rh_sample;
28 };
29 
hs300x_read_sample(const struct device * dev,uint16_t * t_sample,uint16_t * rh_sample)30 static int hs300x_read_sample(const struct device *dev, uint16_t *t_sample, uint16_t *rh_sample)
31 {
32 	const struct hs300x_config *cfg = dev->config;
33 	uint8_t rx_buf[4];
34 	int rc;
35 
36 	rc = i2c_read_dt(&cfg->bus, rx_buf, sizeof(rx_buf));
37 	if (rc < 0) {
38 		LOG_ERR("Failed to read data from device.");
39 		return rc;
40 	}
41 
42 	if ((rx_buf[3] & HS300X_STATUS_MASK) != 0) {
43 		LOG_ERR("Stale data");
44 		return -EIO;
45 	}
46 
47 	*rh_sample = sys_get_be16(rx_buf);
48 	*t_sample = sys_get_be16(&rx_buf[2]);
49 
50 	/* Remove status bits (only present in temperature value)*/
51 	*t_sample >>= 2;
52 
53 	return 0;
54 }
55 
hs300x_sample_fetch(const struct device * dev,enum sensor_channel chan)56 static int hs300x_sample_fetch(const struct device *dev, enum sensor_channel chan)
57 {
58 	struct hs300x_data *data = dev->data;
59 	const struct hs300x_config *cfg = dev->config;
60 	int rc;
61 	uint8_t df_dummy = 0x0;
62 
63 	if (chan != SENSOR_CHAN_ALL && chan != SENSOR_CHAN_AMBIENT_TEMP &&
64 	    chan != SENSOR_CHAN_HUMIDITY) {
65 		return -ENOTSUP;
66 	}
67 
68 	/*
69 	 * By default, the sensor should be factory-programmed to operate in Sleep Mode.
70 	 * A Measurement Request (MR) command is required to exit the sensor
71 	 * from its sleep state. An MR command should consist of the 7-bit address followed
72 	 * by an eighth bit set to 0 (write). However, many I2C controllers cannot generate
73 	 * merely the address byte with no data. To overcome this limitation the MR command
74 	 * should be followed by a dummy byte (zero value).
75 	 */
76 	rc = i2c_write_dt(&cfg->bus, (const uint8_t *)&df_dummy, 1);
77 	if (rc < 0) {
78 		LOG_ERR("Failed to start measurement.");
79 		return rc;
80 	}
81 
82 	/*
83 	 * According to datasheet maximum time to make temperature and humidity
84 	 * measurements is 33ms, add a little safety margin...
85 	 */
86 	k_msleep(50);
87 
88 	rc = hs300x_read_sample(dev, &data->t_sample, &data->rh_sample);
89 	if (rc < 0) {
90 		LOG_ERR("Failed to fetch data.");
91 		return rc;
92 	}
93 
94 	return 0;
95 }
96 
hs300x_temp_convert(struct sensor_value * val,int16_t raw)97 static void hs300x_temp_convert(struct sensor_value *val, int16_t raw)
98 {
99 	int32_t micro_c;
100 
101 	/*
102 	 * Convert to micro Celsius. See datasheet "Calculating Humidity and
103 	 * Temperature Output" section for more details on processing sample data.
104 	 */
105 	micro_c = (((int64_t)raw * 165000000) / 16383) - 40000000;
106 
107 	val->val1 = micro_c / 1000000;
108 	val->val2 = micro_c % 1000000;
109 }
110 
hs300x_rh_convert(struct sensor_value * val,uint16_t raw)111 static void hs300x_rh_convert(struct sensor_value *val, uint16_t raw)
112 {
113 	int32_t micro_rh;
114 
115 	/*
116 	 * Convert to micro %RH. See datasheet "Calculating Humidity and
117 	 * Temperature Output" section for more details on processing sample data.
118 	 */
119 	micro_rh = ((uint64_t)raw * 100000000) / 16383;
120 
121 	val->val1 = micro_rh / 1000000;
122 	val->val2 = micro_rh % 1000000;
123 }
124 
hs300x_channel_get(const struct device * dev,enum sensor_channel chan,struct sensor_value * val)125 static int hs300x_channel_get(const struct device *dev, enum sensor_channel chan,
126 			      struct sensor_value *val)
127 {
128 	const struct hs300x_data *data = dev->data;
129 
130 	if (chan == SENSOR_CHAN_AMBIENT_TEMP) {
131 		hs300x_temp_convert(val, data->t_sample);
132 	} else if (chan == SENSOR_CHAN_HUMIDITY) {
133 		hs300x_rh_convert(val, data->rh_sample);
134 	} else {
135 		return -ENOTSUP;
136 	}
137 
138 	return 0;
139 }
140 
hs300x_init(const struct device * dev)141 static int hs300x_init(const struct device *dev)
142 {
143 	const struct hs300x_config *cfg = dev->config;
144 
145 	if (!i2c_is_ready_dt(&cfg->bus)) {
146 		LOG_ERR("I2C dev %s not ready", cfg->bus.bus->name);
147 		return -ENODEV;
148 	}
149 
150 	return 0;
151 }
152 
153 static DEVICE_API(sensor, hs300x_driver_api) = {
154 	.sample_fetch = hs300x_sample_fetch,
155 	.channel_get = hs300x_channel_get,
156 };
157 
158 #define DEFINE_HS300X(n)                                                                           \
159 	static struct hs300x_data hs300x_data_##n;                                                 \
160                                                                                                    \
161 	static const struct hs300x_config hs300x_config_##n = {.bus = I2C_DT_SPEC_INST_GET(n)};    \
162                                                                                                    \
163 	SENSOR_DEVICE_DT_INST_DEFINE(n, hs300x_init, NULL, &hs300x_data_##n, &hs300x_config_##n,   \
164 				     POST_KERNEL, CONFIG_SENSOR_INIT_PRIORITY,                     \
165 				     &hs300x_driver_api);
166 
167 DT_INST_FOREACH_STATUS_OKAY(DEFINE_HS300X)
168