1 /*
2 * Copyright (c) 2023, Vitrolife A/S
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 *
6 * Datasheet:
7 * https://www.gassensing.co.uk/wp-content/uploads/2023/05/ExplorIR-M-Data-Sheet-Rev-4.13_3.pdf
8 *
9 */
10
11 #define DT_DRV_COMPAT gss_explorir_m
12
13 #include <stdio.h>
14
15 #include <zephyr/kernel.h>
16 #include <zephyr/device.h>
17 #include <zephyr/drivers/sensor.h>
18 #include <zephyr/drivers/sensor/explorir_m.h>
19 #include <zephyr/drivers/uart.h>
20 #include <zephyr/logging/log.h>
21
22 LOG_MODULE_REGISTER(explorir_m_sensor, CONFIG_SENSOR_LOG_LEVEL);
23
24 #define EXPLORIR_M_BEGIN_CHAR ' '
25
26 #define EXPLORIR_M_SET_FILTER_CHAR 'A'
27 #define EXPLORIR_M_GET_FILTER_CHAR 'a'
28 #define EXPLORIR_M_MODE_CHAR 'K'
29 #define EXPLORIR_M_ZERO_POINT_KNOWN_CHAR 'X'
30 #define EXPLORIR_M_CO2_FILTERED_CHAR 'Z'
31 #define EXPLORIR_M_SCALING_CHAR '.'
32 #define EXPLORIR_M_NOT_RECOGNISED_CHAR '?'
33
34 #define EXPLORIR_M_SEPARATOR_CHAR ' '
35 #define EXPLORIR_M_PRE_END_CHAR '\r'
36 #define EXPLORIR_M_END_CHAR '\n'
37
38 #define EXPLORIR_M_TYPE_INDEX 1
39 #define EXPLORIR_M_VALUE_INDEX 3
40
41 #define EXPLORIR_M_BUFFER_LENGTH 16
42
43 #define EXPLORIR_M_MAX_RESPONSE_DELAY 300 /* Add margin to the specified 100 in datasheet */
44 #define EXPLORIR_M_CO2_VALID_DELAY 1200
45
46 struct explorir_m_data {
47 struct k_mutex uart_mutex;
48 struct k_sem uart_rx_sem;
49 uint16_t filtered;
50 uint16_t scaling;
51 uint8_t read_index;
52 uint8_t read_buffer[EXPLORIR_M_BUFFER_LENGTH];
53 };
54
55 struct explorir_m_cfg {
56 const struct device *uart_dev;
57 uart_irq_callback_user_data_t cb;
58 };
59
60 enum explorir_m_uart_set_usage {
61 EXPLORIR_M_SET_NONE,
62 EXPLORIR_M_SET_VAL_ONE,
63 EXPLORIR_M_SET_VAL_ONE_TWO,
64 };
65
66 enum EXPLORIR_M_MODE {
67 EXPLORIR_M_MODE_COMMAND,
68 EXPLORIR_M_MODE_STREAM,
69 EXPLORIR_M_MODE_POLL,
70 };
71
explorir_m_uart_flush(const struct device * uart_dev)72 static void explorir_m_uart_flush(const struct device *uart_dev)
73 {
74 uint8_t tmp;
75
76 while (uart_fifo_read(uart_dev, &tmp, 1) > 0) {
77 continue;
78 }
79 }
80
explorir_m_uart_flush_until_end(const struct device * uart_dev)81 static void explorir_m_uart_flush_until_end(const struct device *uart_dev)
82 {
83 uint8_t tmp;
84 uint32_t uptime;
85
86 uptime = k_uptime_get_32();
87 while (k_uptime_get_32() - uptime < EXPLORIR_M_MAX_RESPONSE_DELAY) {
88 if (uart_poll_in(uart_dev, &tmp) == 0 && tmp == EXPLORIR_M_END_CHAR) {
89 break;
90 }
91 }
92 }
93
explorir_m_buffer_reset(struct explorir_m_data * data)94 static void explorir_m_buffer_reset(struct explorir_m_data *data)
95 {
96 memset(data->read_buffer, 0, data->read_index);
97 data->read_index = 0;
98 }
99
explorir_m_buffer_verify(const struct explorir_m_data * data,char type)100 static int explorir_m_buffer_verify(const struct explorir_m_data *data, char type)
101 {
102 char buffer_type = data->read_buffer[EXPLORIR_M_TYPE_INDEX];
103
104 if (data->read_buffer[0] == EXPLORIR_M_NOT_RECOGNISED_CHAR) {
105 LOG_WRN("Sensor did not recognise the command");
106 return -EIO;
107 }
108
109 if (buffer_type != type) {
110 LOG_WRN("Expected type %c but got %c", type, buffer_type);
111 return -EIO;
112 }
113
114 if (data->read_buffer[0] != EXPLORIR_M_BEGIN_CHAR ||
115 data->read_buffer[2] != EXPLORIR_M_SEPARATOR_CHAR ||
116 data->read_buffer[data->read_index - 2] != EXPLORIR_M_PRE_END_CHAR) {
117 LOG_HEXDUMP_WRN(data->read_buffer, data->read_index, "Invalid buffer");
118 return -EIO;
119 }
120
121 return 0;
122 }
123
explorir_m_buffer_process(struct explorir_m_data * data,char type,struct sensor_value * val)124 static int explorir_m_buffer_process(struct explorir_m_data *data, char type,
125 struct sensor_value *val)
126 {
127 if (explorir_m_buffer_verify(data, type) != 0) {
128 return -EIO;
129 }
130
131 switch (type) {
132 case EXPLORIR_M_SET_FILTER_CHAR:
133 case EXPLORIR_M_MODE_CHAR:
134 case EXPLORIR_M_ZERO_POINT_KNOWN_CHAR:
135 break;
136
137 case EXPLORIR_M_CO2_FILTERED_CHAR:
138 data->scaling = strtol(&data->read_buffer[EXPLORIR_M_VALUE_INDEX], NULL, 10);
139 break;
140
141 case EXPLORIR_M_SCALING_CHAR:
142 data->filtered = strtol(&data->read_buffer[EXPLORIR_M_VALUE_INDEX], NULL, 10);
143 break;
144
145 case EXPLORIR_M_GET_FILTER_CHAR:
146 val->val1 = strtol(&data->read_buffer[EXPLORIR_M_VALUE_INDEX], NULL, 10);
147 break;
148
149 default:
150 LOG_ERR("Unknown type %c/0x%02x", type, type);
151 return -EIO;
152 }
153
154 return 0;
155 }
156
explorir_m_uart_isr(const struct device * uart_dev,void * user_data)157 static void explorir_m_uart_isr(const struct device *uart_dev, void *user_data)
158 {
159 const struct device *dev = user_data;
160 struct explorir_m_data *data = dev->data;
161 int rc, read_len;
162
163 if (!device_is_ready(uart_dev)) {
164 LOG_DBG("UART device is not ready");
165 return;
166 }
167
168 if (!uart_irq_update(uart_dev)) {
169 LOG_DBG("Unable to process interrupts");
170 return;
171 }
172
173 if (!uart_irq_rx_ready(uart_dev)) {
174 LOG_DBG("No RX data");
175 return;
176 }
177
178 read_len = EXPLORIR_M_BUFFER_LENGTH - data->read_index;
179 rc = uart_fifo_read(uart_dev, &data->read_buffer[data->read_index], read_len);
180
181 if (rc < 0 || rc == read_len) {
182 LOG_ERR("UART read failed: %d", rc < 0 ? rc : -ERANGE);
183 explorir_m_uart_flush(uart_dev);
184 LOG_HEXDUMP_WRN(data->read_buffer, data->read_index, "Discarding");
185 explorir_m_buffer_reset(data);
186 } else {
187 data->read_index += rc;
188
189 if (data->read_buffer[data->read_index - 1] != EXPLORIR_M_END_CHAR) {
190 return;
191 }
192 }
193
194 k_sem_give(&data->uart_rx_sem);
195 }
196
explorir_m_uart_terminate(const struct device * uart_dev)197 static void explorir_m_uart_terminate(const struct device *uart_dev)
198 {
199 uart_poll_out(uart_dev, EXPLORIR_M_PRE_END_CHAR);
200 uart_poll_out(uart_dev, EXPLORIR_M_END_CHAR);
201 }
202
explorir_m_uart_transceive(const struct device * dev,char type,struct sensor_value * val,enum explorir_m_uart_set_usage set)203 static int explorir_m_uart_transceive(const struct device *dev, char type, struct sensor_value *val,
204 enum explorir_m_uart_set_usage set)
205 {
206 const struct explorir_m_cfg *cfg = dev->config;
207 struct explorir_m_data *data = dev->data;
208 char buf[EXPLORIR_M_BUFFER_LENGTH];
209 int rc, len;
210
211 if (val == NULL && set != EXPLORIR_M_SET_NONE) {
212 LOG_ERR("val is NULL but set is not NONE");
213 return -EINVAL;
214 }
215
216 k_mutex_lock(&data->uart_mutex, K_FOREVER);
217
218 uart_poll_out(cfg->uart_dev, type);
219
220 if (set == EXPLORIR_M_SET_VAL_ONE) {
221 len = snprintf(buf, EXPLORIR_M_BUFFER_LENGTH, "%c%u", EXPLORIR_M_SEPARATOR_CHAR,
222 val->val1);
223 } else if (set == EXPLORIR_M_SET_VAL_ONE_TWO) {
224 len = snprintf(buf, EXPLORIR_M_BUFFER_LENGTH, "%c%u%c%u", EXPLORIR_M_SEPARATOR_CHAR,
225 val->val1, EXPLORIR_M_SEPARATOR_CHAR, val->val2);
226 } else {
227 len = 0;
228 }
229
230 if (len == EXPLORIR_M_BUFFER_LENGTH) {
231 LOG_WRN("Set value truncated");
232 }
233 for (int i = 0; i != len; i++) {
234 uart_poll_out(cfg->uart_dev, buf[i]);
235 }
236
237 explorir_m_buffer_reset(data);
238 k_sem_reset(&data->uart_rx_sem);
239
240 explorir_m_uart_terminate(cfg->uart_dev);
241
242 rc = k_sem_take(&data->uart_rx_sem, K_MSEC(EXPLORIR_M_MAX_RESPONSE_DELAY));
243 if (rc != 0) {
244 LOG_WRN("%c did not receive a response: %d", type, rc);
245 }
246
247 if (rc == 0) {
248 rc = explorir_m_buffer_process(data, type, val);
249 }
250
251 k_mutex_unlock(&data->uart_mutex);
252
253 return rc;
254 }
255
256 /*
257 * This calibrate function uses a known gas concentration [ppm] via val->val1 to calibrate.
258 * Calibration should be done when temperature is stabile and gas is fully diffused into the sensor.
259 */
explorir_m_calibrate(const struct device * dev,struct sensor_value * val)260 static int explorir_m_calibrate(const struct device *dev, struct sensor_value *val)
261 {
262 struct explorir_m_data *data = dev->data;
263 struct sensor_value original;
264 struct sensor_value tmp;
265 int restore_rc;
266 int rc;
267
268 /* Prevent sensor interaction while using calibration filter value */
269 k_mutex_lock(&data->uart_mutex, K_FOREVER);
270
271 rc = explorir_m_uart_transceive(dev, EXPLORIR_M_GET_FILTER_CHAR, &original,
272 EXPLORIR_M_SET_NONE);
273 if (rc != 0) {
274 goto unlock;
275 }
276
277 /*
278 * From datasheet section "Zero point setting":
279 * To improve zeroing accuracy, the recommended digital filter setting is 32.
280 */
281 tmp.val1 = 32;
282 rc = explorir_m_uart_transceive(dev, EXPLORIR_M_SET_FILTER_CHAR, &tmp,
283 EXPLORIR_M_SET_VAL_ONE);
284 if (rc == 0) {
285 tmp.val1 = val->val1 / data->filtered;
286 rc = explorir_m_uart_transceive(dev, EXPLORIR_M_ZERO_POINT_KNOWN_CHAR, &tmp,
287 EXPLORIR_M_SET_VAL_ONE);
288 }
289
290 restore_rc = explorir_m_uart_transceive(dev, EXPLORIR_M_SET_FILTER_CHAR, &original,
291 EXPLORIR_M_SET_VAL_ONE);
292 if (restore_rc != 0) {
293 LOG_ERR("Could not restore filter value");
294 }
295
296 unlock:
297 k_mutex_unlock(&data->uart_mutex);
298
299 return rc != 0 ? rc : restore_rc;
300 }
301
explorir_m_attr_get(const struct device * dev,enum sensor_channel chan,enum sensor_attribute attr,struct sensor_value * val)302 static int explorir_m_attr_get(const struct device *dev, enum sensor_channel chan,
303 enum sensor_attribute attr, struct sensor_value *val)
304 {
305 if (chan != SENSOR_CHAN_CO2) {
306 return -ENOTSUP;
307 }
308
309 switch (attr) {
310 case SENSOR_ATTR_EXPLORIR_M_FILTER:
311 return explorir_m_uart_transceive(dev, EXPLORIR_M_GET_FILTER_CHAR, val,
312 EXPLORIR_M_SET_NONE);
313 default:
314 return -ENOTSUP;
315 }
316 }
317
explorir_m_attr_set(const struct device * dev,enum sensor_channel chan,enum sensor_attribute attr,const struct sensor_value * val)318 static int explorir_m_attr_set(const struct device *dev, enum sensor_channel chan,
319 enum sensor_attribute attr, const struct sensor_value *val)
320 {
321 if (chan != SENSOR_CHAN_CO2) {
322 return -ENOTSUP;
323 }
324
325 switch (attr) {
326 case SENSOR_ATTR_CALIBRATION:
327 return explorir_m_calibrate(dev, (struct sensor_value *)val);
328 case SENSOR_ATTR_EXPLORIR_M_FILTER:
329 if (val->val1 < 0 || val->val1 > 255) {
330 return -ERANGE;
331 }
332 return explorir_m_uart_transceive(dev, EXPLORIR_M_SET_FILTER_CHAR,
333 (struct sensor_value *)val,
334 EXPLORIR_M_SET_VAL_ONE);
335 default:
336 return -ENOTSUP;
337 }
338 }
339
explorir_m_sample_fetch(const struct device * dev,enum sensor_channel chan)340 static int explorir_m_sample_fetch(const struct device *dev, enum sensor_channel chan)
341 {
342 if (chan != SENSOR_CHAN_CO2 && chan != SENSOR_CHAN_ALL) {
343 return -ENOTSUP;
344 }
345
346 return explorir_m_uart_transceive(dev, EXPLORIR_M_CO2_FILTERED_CHAR, NULL,
347 EXPLORIR_M_SET_NONE);
348 }
349
explorir_m_channel_get(const struct device * dev,enum sensor_channel chan,struct sensor_value * val)350 static int explorir_m_channel_get(const struct device *dev, enum sensor_channel chan,
351 struct sensor_value *val)
352 {
353 struct explorir_m_data *data = dev->data;
354
355 if (chan != SENSOR_CHAN_CO2) {
356 return -ENOTSUP;
357 }
358
359 if (k_uptime_get() < EXPLORIR_M_CO2_VALID_DELAY) {
360 return -EAGAIN;
361 }
362
363 val->val1 = data->filtered * data->scaling;
364 val->val2 = 0;
365
366 return 0;
367 }
368
369 static DEVICE_API(sensor, explorir_m_api_funcs) = {
370 .attr_set = explorir_m_attr_set,
371 .attr_get = explorir_m_attr_get,
372 .sample_fetch = explorir_m_sample_fetch,
373 .channel_get = explorir_m_channel_get,
374 };
375
explorir_m_init(const struct device * dev)376 static int explorir_m_init(const struct device *dev)
377 {
378 const struct explorir_m_cfg *cfg = dev->config;
379 struct explorir_m_data *data = dev->data;
380 struct sensor_value val;
381 int rc;
382
383 LOG_DBG("Initializing %s", dev->name);
384
385 if (!device_is_ready(cfg->uart_dev)) {
386 return -ENODEV;
387 }
388
389 k_mutex_init(&data->uart_mutex);
390 k_sem_init(&data->uart_rx_sem, 0, 1);
391
392 uart_irq_rx_disable(cfg->uart_dev);
393 uart_irq_tx_disable(cfg->uart_dev);
394
395 rc = uart_irq_callback_user_data_set(cfg->uart_dev, cfg->cb, (void *)dev);
396 if (rc != 0) {
397 LOG_ERR("UART IRQ setup failed: %d", rc);
398 return rc;
399 }
400
401 /* Terminate garbled tx due to GPIO setup or crash during unfinished send */
402 explorir_m_uart_terminate(cfg->uart_dev);
403 explorir_m_uart_flush_until_end(cfg->uart_dev);
404
405 uart_irq_rx_enable(cfg->uart_dev);
406
407 val.val1 = EXPLORIR_M_MODE_POLL;
408 explorir_m_uart_transceive(dev, EXPLORIR_M_MODE_CHAR, &val, EXPLORIR_M_SET_VAL_ONE);
409 explorir_m_uart_transceive(dev, EXPLORIR_M_SCALING_CHAR, NULL, EXPLORIR_M_SET_NONE);
410
411 return rc;
412 }
413
414 #define EXPLORIR_M_INIT(n) \
415 \
416 static struct explorir_m_data explorir_m_data_##n; \
417 \
418 static const struct explorir_m_cfg explorir_m_cfg_##n = { \
419 .uart_dev = DEVICE_DT_GET(DT_INST_BUS(n)), \
420 .cb = explorir_m_uart_isr, \
421 }; \
422 \
423 SENSOR_DEVICE_DT_INST_DEFINE(n, explorir_m_init, NULL, &explorir_m_data_##n, \
424 &explorir_m_cfg_##n, POST_KERNEL, \
425 CONFIG_SENSOR_INIT_PRIORITY, &explorir_m_api_funcs);
426
427 DT_INST_FOREACH_STATUS_OKAY(EXPLORIR_M_INIT)
428