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