1 /*
2 * Copyright (c) 2024 Jan Fäh
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #define DT_DRV_COMPAT sensirion_sts4x
8
9 #include <zephyr/device.h>
10 #include <zephyr/kernel.h>
11 #include <zephyr/drivers/sensor.h>
12 #include <zephyr/drivers/i2c.h>
13 #include <zephyr/logging/log.h>
14 #include <zephyr/sys/__assert.h>
15 #include <zephyr/sys/byteorder.h>
16 #include <zephyr/sys/crc.h>
17 #include <zephyr/devicetree.h>
18
19 LOG_MODULE_REGISTER(STS4X, CONFIG_SENSOR_LOG_LEVEL);
20
21 #define STS4X_CMD_RESET 0x94
22
23 #define STS4X_RESET_TIME 1
24
25 #define STS4X_CRC_POLY 0x31
26 #define STS4X_CRC_INIT 0xFF
27
28 #define STS4X_MAX_TEMP 175
29 #define STS4X_MIN_TEMP -45
30
31 struct sts4x_config {
32 struct i2c_dt_spec bus;
33 uint8_t repeatability;
34 };
35
36 struct sts4x_data {
37 uint16_t temp_sample;
38 };
39
40 static const uint8_t measure_cmds[3] = {0xE0, 0xF6, 0xFD};
41 static const uint16_t measure_time_us[3] = {1600, 4500, 8300};
42
sts4x_crc_check(uint16_t value,uint8_t sensor_crc)43 static int sts4x_crc_check(uint16_t value, uint8_t sensor_crc)
44 {
45 uint8_t buf[2];
46
47 sys_put_be16(value, buf);
48
49 uint8_t calculated_crc = crc8(buf, 2, STS4X_CRC_POLY, STS4X_CRC_INIT, false);
50
51 if (calculated_crc == sensor_crc) {
52 return 0;
53 }
54
55 return -EIO;
56 }
57
sts4x_write_command(const struct device * dev,uint8_t cmd)58 static int sts4x_write_command(const struct device *dev, uint8_t cmd)
59 {
60 const struct sts4x_config *cfg = dev->config;
61 uint8_t tx_buf = cmd;
62
63 return i2c_write_dt(&cfg->bus, &tx_buf, 1);
64 }
65
sts4x_read_sample(const struct device * dev,uint16_t * temp_sample)66 static int sts4x_read_sample(const struct device *dev, uint16_t *temp_sample)
67 {
68 const struct sts4x_config *cfg = dev->config;
69 uint8_t rx_buf[3];
70 int ret;
71
72 ret = i2c_read_dt(&cfg->bus, rx_buf, sizeof(rx_buf));
73 if (ret < 0) {
74 LOG_ERR("Failed to read data.");
75 return ret;
76 }
77
78 *temp_sample = sys_get_be16(rx_buf);
79 ret = sts4x_crc_check(*temp_sample, rx_buf[2]);
80 if (ret < 0) {
81 LOG_ERR("Invalid CRC.");
82 return ret;
83 }
84
85 return 0;
86 }
87
sts4x_sample_fetch(const struct device * dev,enum sensor_channel chan)88 static int sts4x_sample_fetch(const struct device *dev, enum sensor_channel chan)
89 {
90 struct sts4x_data *data = dev->data;
91 const struct sts4x_config *cfg = dev->config;
92 int ret;
93
94 if (chan == SENSOR_CHAN_ALL || chan == SENSOR_CHAN_AMBIENT_TEMP) {
95 ret = sts4x_write_command(dev, measure_cmds[cfg->repeatability]);
96 if (ret < 0) {
97 LOG_ERR("Failed to write measure command.");
98 return ret;
99 }
100
101 k_usleep(measure_time_us[cfg->repeatability]);
102
103 ret = sts4x_read_sample(dev, &data->temp_sample);
104 if (ret < 0) {
105 LOG_ERR("Failed to get temperature data.");
106 return ret;
107 }
108
109 return 0;
110 } else {
111 return -ENOTSUP;
112 }
113 }
114
sts4x_channel_get(const struct device * dev,enum sensor_channel chan,struct sensor_value * val)115 static int sts4x_channel_get(const struct device *dev, enum sensor_channel chan,
116 struct sensor_value *val)
117 {
118 const struct sts4x_data *data = dev->data;
119
120 if (chan == SENSOR_CHAN_AMBIENT_TEMP) {
121 int64_t temp;
122
123 temp = data->temp_sample * STS4X_MAX_TEMP;
124 val->val1 = (int32_t)(temp / 0xFFFF) + STS4X_MIN_TEMP;
125 val->val2 = ((temp % 0xFFFF) * 1000000) / 0xFFFF;
126 } else {
127 return -ENOTSUP;
128 }
129 return 0;
130 }
131
sts4x_init(const struct device * dev)132 static int sts4x_init(const struct device *dev)
133 {
134 const struct sts4x_config *cfg = dev->config;
135 int ret;
136
137 if (!i2c_is_ready_dt(&cfg->bus)) {
138 LOG_ERR("Device not ready.");
139 return -ENODEV;
140 }
141
142 ret = sts4x_write_command(dev, STS4X_CMD_RESET);
143 if (ret < 0) {
144 LOG_ERR("Failed to reset the device.");
145 return ret;
146 }
147
148 k_msleep(STS4X_RESET_TIME);
149
150 return 0;
151 }
152
153 static DEVICE_API(sensor, sts4x_api_funcs) = {
154 .sample_fetch = sts4x_sample_fetch,
155 .channel_get = sts4x_channel_get,
156 };
157
158 #define STS4X_INIT(inst) \
159 static struct sts4x_data sts4x_data_##inst; \
160 static const struct sts4x_config sts4x_config_##inst = { \
161 .bus = I2C_DT_SPEC_INST_GET(inst), \
162 .repeatability = DT_INST_PROP(inst, repeatability), \
163 }; \
164 SENSOR_DEVICE_DT_INST_DEFINE(inst, sts4x_init, NULL, &sts4x_data_##inst, \
165 &sts4x_config_##inst, POST_KERNEL, \
166 CONFIG_SENSOR_INIT_PRIORITY, &sts4x_api_funcs);
167
168 DT_INST_FOREACH_STATUS_OKAY(STS4X_INIT)
169