Data Logger BLE Cellular Board
Loading...
Searching...
No Matches
bme68x.c
Go to the documentation of this file.
1/** \file bme68x.c
2 *
3 * \brief Driver implementation for the BME68X sensor.
4 * This file contains the core logic for initializing, configuring, and
5 * reading data from the Bosch BME68X gas, temperature, humidity, and pressure sensor.
6 *
7 * \date 2023-02-07
8 * \version v4.4.8
9 *
10 * \copyright (c) 2023 Bosch Sensortec GmbH. All rights reserved.
11 *
12 * \note SPDX-License-Identifier: BSD-3-Clause \n
13 * and use in source and binary forms, with or without
14 * modification, are permitted provided that the following conditions are met:
15 * 1. Redistributions of source code must retain the above copyright
16 * notice, this list of conditions and the following disclaimer.
17 *
18 * 2. Redistributions in binary form must reproduce the above copyright
19 * notice, this list of conditions and the following disclaimer in the
20 * documentation and/or other materials provided with the distribution.
21 *
22 * 3. Neither the name of the copyright holder nor the names of its
23 * contributors may be used to endorse or promote products derived from
24 * this software without specific prior written permission.
25 *
26 * \note THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
27 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
28 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
29 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
30 * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
31 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
32 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
33 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
34 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
35 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
36 * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
37 * POSSIBILITY OF SUCH DAMAGE.
38 *
39 *
40 */
41
42#include "bme68x.h"
43#include "common.h"
44#include "jfet_common.h"
45#include <stdio.h>
46
47/* Read the sensor's calibration coefficients from the device and store them in the device structure. */
48static int8_t get_calib_data(struct bme68x_dev *dev);
49
50/* This internal API is used to read variant ID information register status */
51static int8_t read_variant_id(struct bme68x_dev *dev);
52
53/* This internal API is used to calculate the gas wait */
54static uint8_t calc_gas_wait(uint16_t dur);
55
56#ifndef BME68X_USE_FPU
57
58/* This internal API is used to calculate the temperature in integer */
59static int16_t calc_temperature(uint32_t temp_adc, struct bme68x_dev *dev);
60
61/* This internal API is used to calculate the pressure in integer */
62static uint32_t calc_pressure(uint32_t pres_adc, const struct bme68x_dev *dev);
63
64/* This internal API is used to calculate the humidity in integer */
65static uint32_t calc_humidity(uint16_t hum_adc, const struct bme68x_dev *dev);
66
67/** \fn static float calc_gas_resistance_high(uint16_t gas_res_adc, uint8_t gas_range)
68 *
69 * \brief This internal API is used to calculate the gas resistance value in float
70 *
71 * \param gas_res_adc Raw ADC value for gas resistance
72 * \param gas_range Gas range index used for compensation
73 *
74 * \return Calculated gas resistance in ohms
75 */
76static uint32_t calc_gas_resistance_high(uint16_t gas_res_adc, uint8_t gas_range);
77
78/** \fn static float calc_gas_resistance_low(uint16_t gas_res_adc, uint8_t gas_range, const struct bme68x_dev *dev)
79 *
80 * \brief This internal API is used to calculate the gas resistance low value in float
81 *
82 * \param gas_res_adc Raw ADC value for gas resistance
83 * \param gas_range Gas range index used for compensation
84 * \param dev Pointer to device structure containing calibration data
85 *
86 * \return Calculated gas resistance in ohms
87 */
88static uint32_t calc_gas_resistance_low(uint16_t gas_res_adc, uint8_t gas_range, const struct bme68x_dev *dev);
89
90/* This internal API is used to calculate the heater resistance using integer */
91static uint8_t calc_res_heat(uint16_t temp, const struct bme68x_dev *dev);
92
93#else
94
95/** \fn static float calc_temperature(uint32_t temp_adc, struct bme68x_dev *dev)
96 *
97 * \brief This internal API is used to calculate the temperature value
98 *
99 * \param temp_adc: raw ADC temperature reading
100 * \param dev: pointer to device context containing calibration data
101 *
102 * \return: compensated temperature in degrees Celsius
103 */
104static float calc_temperature(uint32_t temp_adc, struct bme68x_dev *dev);
105
106/** \fn static float calc_pressure(uint32_t pres_adc, const struct bme68x_dev *dev)
107 *
108 * \brief This internal API is used to calculate the pressure value
109 *
110 * \param pres_adc: raw ADC pressure reading
111 * \param dev: pointer to device context containing calibration data
112 *
113 * \return: compensated pressure value in Pascals
114 */
115static float calc_pressure(uint32_t pres_adc, const struct bme68x_dev *dev);
116
117/** \fn static float calc_humidity(uint16_t hum_adc, const struct bme68x_dev *dev)
118 *
119 * \brief Calculate relative humidity (in percent, float) from raw ADC value
120 *
121 * \param hum_adc: raw humidity ADC value
122 * \param dev: pointer to device structure containing calibration data
123 *
124 * \return relative humidity in percent (0.0 - 100.0)
125 */
126static float calc_humidity(uint16_t hum_adc, const struct bme68x_dev *dev);
127
128/** \fn static float calc_gas_resistance_high(uint16_t gas_res_adc, uint8_t gas_range)
129 *
130 * \brief This internal API is used to calculate the gas resistance value in float
131 *
132 * \param gas_res_adc Raw ADC value for gas resistance
133 * \param gas_range Gas range index used for compensation
134 *
135 * \return Calculated gas resistance in ohms
136 */
137static float calc_gas_resistance_high(uint16_t gas_res_adc, uint8_t gas_range);
138
139/** \fn static float calc_gas_resistance_low(uint16_t gas_res_adc, uint8_t gas_range, const struct bme68x_dev *dev)
140 *
141 * \brief This internal API is used to calculate the gas resistance low value in float
142 *
143 * \param gas_res_adc Raw ADC value for gas resistance
144 * \param gas_range Gas range index used for compensation
145 * \param dev Pointer to device structure containing calibration data
146 *
147 * \return Calculated gas resistance in ohms
148 */
149static float calc_gas_resistance_low(uint16_t gas_res_adc, uint8_t gas_range, const struct bme68x_dev *dev);
150
151/* This internal API is used to calculate the heater resistance value using float */
152static uint8_t calc_res_heat(uint16_t temp, const struct bme68x_dev *dev);
153
154#endif
155
156/* This internal API is used to read a single data of the sensor */
157static int8_t read_field_data(uint8_t index, struct bme68x_data *data, struct bme68x_dev *dev);
158
159/* This internal API is used to read all data fields of the sensor */
160static int8_t read_all_field_data(struct bme68x_data *const data[], struct bme68x_dev *dev);
161
162/* This internal API is used to switch between SPI memory pages */
163static int8_t set_mem_page(uint8_t reg_addr, struct bme68x_dev *dev);
164
165/* This internal API is used to get the current SPI memory page */
166static int8_t get_mem_page(struct bme68x_dev *dev);
167
168/* This internal API is used to check the bme68x_dev for null pointers */
169static int8_t null_ptr_check(const struct bme68x_dev *dev);
170
171/* This internal API is used to set heater configurations */
172static int8_t set_conf(const struct bme68x_heatr_conf *conf, uint8_t op_mode, uint8_t *nb_conv, struct bme68x_dev *dev);
173
174/* This internal API is used to limit the max value of a parameter */
175static int8_t boundary_check(uint8_t *value, uint8_t max, struct bme68x_dev *dev);
176
177/* This internal API is used to calculate the register value for
178 * shared heater duration */
179static uint8_t calc_heatr_dur_shared(uint16_t dur);
180
181/* This internal API is used to swap two fields */
182static void swap_fields(uint8_t index1, uint8_t index2, struct bme68x_data *field[]);
183
184/* This internal API is used sort the sensor data */
185static void sort_sensor_data(uint8_t low_index, uint8_t high_index, struct bme68x_data *field[]);
186
187/**
188 * @fn static int8_t analyze_sensor_data(const struct bme68x_data *data, uint8_t n_meas)
189 * @brief Function to analyze the sensor data
190 *
191 * @param[in] data Array of measurement data
192 * @param[in] n_meas Number of measurements
193 *
194 * @return Result of API execution status
195 * @retval 0 -> Success
196 * @retval < 0 -> Fail
197 */
198static int8_t analyze_sensor_data(const struct bme68x_data *data, uint8_t n_meas);
199
200/******************************************************************************************/
201/* Global API definitions */
202/******************************************************************************************/
203
204/**
205 * \fn int8_t bme68x_init(struct bme68x_dev *dev)
206 *
207 * \brief Initializes the BME68X sensor.
208 *
209 * This function performs a soft reset, verifies the chip ID, reads the
210 * variant ID, and retrieves calibration data from the sensor.
211 * \param dev Pointer to the device structure.
212 *
213 * \return 0 on success, or a negative error code.
214 */
215int8_t bme68x_init(struct bme68x_dev *dev) {
216 int8_t rslt;
217
218 /* Perform a software reset to bring the device to a clean state */
219 (void)bme68x_soft_reset(dev);
220
221 /* Read the chip ID to verify the sensor is actually a BME68x */
222 rslt = bme68x_get_regs(BME68X_REG_CHIP_ID, &dev->chip_id, 1, dev);
223
224 if (rslt == BME68X_OK) {
225 if (dev->chip_id == BME68X_CHIP_ID) {
226 /* Identify if the sensor is a high or low gas variant */
227 /* Read Variant ID */
228 rslt = read_variant_id(dev);
229
230 if (rslt == BME68X_OK) {
231 /* Get the Calibration data */
232 rslt = get_calib_data(dev);
233 }
234 } else {
235 rslt = BME68X_E_DEV_NOT_FOUND;
236 }
237 }
238 return rslt;
239}
240
241/**
242 * @fn int8_t bme68x_set_regs(const uint8_t *reg_addr, const uint8_t *reg_data, uint32_t len, struct bme68x_dev *dev)
243 *
244 * @brief This API writes the given data to the register address of the sensor
245 *
246 * \param reg_addr Array of register addresses to be written.
247 * \param reg_data Array of data bytes to be written.
248 * \param len Number of registers to write.
249 * \param dev Pointer to the device structure.
250 *
251 * \return 0 on success, or a negative error code.
252 */
253int8_t bme68x_set_regs(const uint8_t *reg_addr, const uint8_t *reg_data, uint32_t len, struct bme68x_dev *dev) {
254#if 0
255 if (dev->intf == BME68X_SPI_INTF)
256 {
257 /* Set the memory page */
258 rslt = set_mem_page(reg_addr, dev);
259 if (rslt == BME68X_OK)
260 {
261 reg_addr = reg_addr & BME68X_SPI_WR_MSK;
262 }
263 }
264 dev->intf_rslt = dev->write(reg_addr, reg_data, len, dev->intf_ptr);
265
266#else
267 int8_t rslt;
268 /* Length of the temporary buffer is 2*(length of register) to accommodate addr + data pairs */
269 uint8_t tmp_buff[BME68X_LEN_INTERLEAVE_BUFF] = {0};
270 uint16_t index;
271
272 /* Check for null pointer in the device structure */
273 rslt = null_ptr_check(dev);
274 if ((rslt == BME68X_OK) && reg_addr && reg_data) {
275 if ((len > 0) && (len <= (BME68X_LEN_INTERLEAVE_BUFF / 2))) {
276 /* Interleave the 2 arrays */
277 for (index = 0; index < len; index++) {
278 if (dev->intf == BME68X_SPI_INTF) {
279 /* Set the memory page */
280 rslt = set_mem_page(reg_addr[index], dev);
281 tmp_buff[(2 * index)] = reg_addr[index] & BME68X_SPI_WR_MSK;
282 } else {
283 tmp_buff[(2 * index)] = reg_addr[index];
284 }
285
286 tmp_buff[(2 * index) + 1] = reg_data[index];
287 }
288
289 /* Write the interleaved array */
290 if (rslt == BME68X_OK) {
291 dev->intf_rslt = dev->write(tmp_buff[0], &tmp_buff[1], (2 * len) - 1, dev->intf_ptr);
292 if (dev->intf_rslt != 0) {
293 rslt = BME68X_E_COM_FAIL;
294 }
295 }
296 } else {
297 rslt = BME68X_E_INVALID_LENGTH;
298 }
299 } else {
300 rslt = BME68X_E_NULL_PTR;
301 }
302#endif
303 return rslt;
304}
305
306/**
307 * @fn int8_t bme68x_get_regs(uint8_t reg_addr, uint8_t *reg_data, uint32_t len, struct bme68x_dev *dev)
308 *
309 * @brief This API reads the data from the given register address of sensor.
310 *
311 * \param reg_addr Starting register address to read from.
312 * \param reg_data Pointer to the buffer to store the read data.
313 * \param len Number of bytes to read.
314 * \param dev Pointer to the device structure.
315 *
316 * \return 0 on success, or a negative error code.
317 */
318int8_t bme68x_get_regs(uint8_t reg_addr, uint8_t *reg_data, uint32_t len, struct bme68x_dev *dev) {
319 int8_t rslt;
320
321 /* Check for null pointer in the device structure and data buffer */
322 rslt = null_ptr_check(dev);
323 if ((rslt == BME68X_OK) && reg_data) {
324 if (dev->intf == BME68X_SPI_INTF) {
325 /* Set the memory page */
326 rslt = set_mem_page(reg_addr, dev);
327 if (rslt == BME68X_OK) {
328 reg_addr = reg_addr | BME68X_SPI_RD_MSK;
329 }
330 }
331
332 /* Execute the interface-specific read function */
333 dev->intf_rslt = dev->read(reg_addr, reg_data, len, dev->intf_ptr);
334 if (dev->intf_rslt != 0) {
335 rslt = BME68X_E_COM_FAIL;
336 }
337 } else {
338 rslt = BME68X_E_NULL_PTR;
339 }
340
341 return rslt;
342}
343
344/**
345 * \fn int8_t bme68x_soft_reset(struct bme68x_dev *dev)
346 *
347 * \brief Triggers a software reset on the BME68X sensor.
348 *
349 * \param dev Pointer to the device structure.
350 *
351 * \return 0 on success, or a negative error code.
352 */
353int8_t bme68x_soft_reset(struct bme68x_dev *dev) {
354 int8_t rslt;
355 /* Register address for soft reset */
356 uint8_t reg_addr = BME68X_REG_SOFT_RESET;
357
358 /* 0xb6 is the soft reset command */
359 uint8_t soft_rst_cmd = BME68X_SOFT_RESET_CMD;
360
361 /* Check for null pointer in the device structure*/
362 rslt = null_ptr_check(dev);
363 if (rslt == BME68X_OK) {
364 /* For SPI, we need to ensure we are on the correct memory page */
365 if (dev->intf == BME68X_SPI_INTF) {
366 rslt = get_mem_page(dev);
367 }
368
369 /* Write the reset command to the sensor */
370 if (rslt == BME68X_OK) {
371 rslt = bme68x_set_regs(&reg_addr, &soft_rst_cmd, 1, dev);
372
373 if (rslt == BME68X_OK) {
374 /* Wait for 5ms */
375 dev->delay_us(BME68X_PERIOD_RESET, dev->intf_ptr);
376
377 /* After reset get the memory page */
378 if (dev->intf == BME68X_SPI_INTF) {
379 rslt = get_mem_page(dev);
380 }
381 }
382 }
383 }
384
385 return rslt;
386}
387
388/**
389 * \fn int8_t bme68x_set_conf(struct bme68x_conf *conf, struct bme68x_dev *dev)
390 *
391 * \brief Sets the oversampling, filter and ODR configurations of the sensor.
392 *
393 * \param conf Pointer to the configuration structure.
394 * \param dev Pointer to the device structure.
395 *
396 * \return 0 on success, or a negative error code.
397 */
398int8_t bme68x_set_conf(struct bme68x_conf *conf, struct bme68x_dev *dev) {
399 int8_t rslt;
400 uint8_t odr20 = 0, odr3 = 1;
401 uint8_t current_op_mode;
402
403 /* Register data starting from BME68X_REG_CTRL_GAS_1(0x71) up to BME68X_REG_CONFIG(0x75) */
404 uint8_t reg_array[BME68X_LEN_CONFIG] = {0x71, 0x72, 0x73, 0x74, 0x75};
405 uint8_t data_array[BME68X_LEN_CONFIG] = {0};
406
407 rslt = bme68x_get_op_mode(&current_op_mode, dev);
408 if (rslt == BME68X_OK) {
409 /* Configure only in the sleep mode */
410 rslt = bme68x_set_op_mode(BME68X_SLEEP_MODE, dev);
411 }
412
413 if (conf == NULL) {
414 rslt = BME68X_E_NULL_PTR;
415 } else if (rslt == BME68X_OK) {
416 /* Read the whole configuration and write it back once later */
417 rslt = bme68x_get_regs(reg_array[0], data_array, BME68X_LEN_CONFIG, dev);
418 dev->info_msg = BME68X_OK;
419 if (rslt == BME68X_OK) {
420 rslt = boundary_check(&conf->filter, BME68X_FILTER_SIZE_127, dev);
421 }
422
423 if (rslt == BME68X_OK) {
424 rslt = boundary_check(&conf->os_temp, BME68X_OS_16X, dev);
425 }
426
427 if (rslt == BME68X_OK) {
428 rslt = boundary_check(&conf->os_pres, BME68X_OS_16X, dev);
429 }
430
431 if (rslt == BME68X_OK) {
432 rslt = boundary_check(&conf->os_hum, BME68X_OS_16X, dev);
433 }
434
435 if (rslt == BME68X_OK) {
436 rslt = boundary_check(&conf->odr, BME68X_ODR_NONE, dev);
437 }
438
439 if (rslt == BME68X_OK) {
440 data_array[4] = BME68X_SET_BITS(data_array[4], BME68X_FILTER, conf->filter);
441 data_array[3] = BME68X_SET_BITS(data_array[3], BME68X_OST, conf->os_temp);
442 data_array[3] = BME68X_SET_BITS(data_array[3], BME68X_OSP, conf->os_pres);
443 data_array[1] = BME68X_SET_BITS_POS_0(data_array[1], BME68X_OSH, conf->os_hum);
444 if (conf->odr != BME68X_ODR_NONE) {
445 odr20 = conf->odr;
446 odr3 = 0;
447 }
448
449 data_array[4] = BME68X_SET_BITS(data_array[4], BME68X_ODR20, odr20);
450 data_array[0] = BME68X_SET_BITS(data_array[0], BME68X_ODR3, odr3);
451 }
452 }
453
454 if (rslt == BME68X_OK) {
455 rslt = bme68x_set_regs(reg_array, data_array, BME68X_LEN_CONFIG, dev);
456 }
457
458 if ((current_op_mode != BME68X_SLEEP_MODE) && (rslt == BME68X_OK)) {
459 rslt = bme68x_set_op_mode(current_op_mode, dev);
460 }
461
462 return rslt;
463}
464
465/*
466 * @brief This API is used to get the oversampling, filter and odr
467 */
468int8_t bme68x_get_conf(struct bme68x_conf *conf, struct bme68x_dev *dev) {
469 int8_t rslt;
470
471 /* starting address of the register array for burst read*/
472 uint8_t reg_addr = BME68X_REG_CTRL_GAS_1;
473 uint8_t data_array[BME68X_LEN_CONFIG];
474
475 rslt = bme68x_get_regs(reg_addr, data_array, 5, dev);
476 if (!conf) {
477 rslt = BME68X_E_NULL_PTR;
478 } else if (rslt == BME68X_OK) {
479 conf->os_hum = BME68X_GET_BITS_POS_0(data_array[1], BME68X_OSH);
480 conf->filter = BME68X_GET_BITS(data_array[4], BME68X_FILTER);
481 conf->os_temp = BME68X_GET_BITS(data_array[3], BME68X_OST);
482 conf->os_pres = BME68X_GET_BITS(data_array[3], BME68X_OSP);
483 if (BME68X_GET_BITS(data_array[0], BME68X_ODR3)) {
484 conf->odr = BME68X_ODR_NONE;
485 } else {
486 conf->odr = BME68X_GET_BITS(data_array[4], BME68X_ODR20);
487 }
488 }
489
490 return rslt;
491}
492
493/*
494 * @brief This API is used to set the operation mode of the sensor
495 */
496int8_t bme68x_set_op_mode(const uint8_t op_mode, struct bme68x_dev *dev) {
497 int8_t rslt;
498 uint8_t tmp_pow_mode;
499 uint8_t pow_mode = 0;
500 uint8_t reg_addr = BME68X_REG_CTRL_MEAS;
501
502 /* Call until in sleep */
503 do {
504 rslt = bme68x_get_regs(BME68X_REG_CTRL_MEAS, &tmp_pow_mode, 1, dev);
505 if (rslt == BME68X_OK) {
506 /* Put to sleep before changing mode */
507 pow_mode = (tmp_pow_mode & BME68X_MODE_MSK);
508 if (pow_mode != BME68X_SLEEP_MODE) {
509 tmp_pow_mode &= ~BME68X_MODE_MSK; /* Set to sleep */
510 rslt = bme68x_set_regs(&reg_addr, &tmp_pow_mode, 1, dev);
511 dev->delay_us(BME68X_PERIOD_POLL, dev->intf_ptr);
512 }
513 }
514 } while ((pow_mode != BME68X_SLEEP_MODE) && (rslt == BME68X_OK));
515
516 /* Already in sleep */
517 if ((op_mode != BME68X_SLEEP_MODE) && (rslt == BME68X_OK)) {
518 tmp_pow_mode = (tmp_pow_mode & ~BME68X_MODE_MSK) | (op_mode & BME68X_MODE_MSK);
519 rslt = bme68x_set_regs(&reg_addr, &tmp_pow_mode, 1, dev);
520 }
521
522 return rslt;
523}
524
525/*
526 * @brief This API is used to get the operation mode of the sensor.
527 */
528int8_t bme68x_get_op_mode(uint8_t *op_mode, struct bme68x_dev *dev) {
529 int8_t rslt;
530 uint8_t mode;
531
532 if (op_mode) {
533 rslt = bme68x_get_regs(BME68X_REG_CTRL_MEAS, &mode, 1, dev);
534
535 /* Masking the other register bit info*/
536 *op_mode = mode & BME68X_MODE_MSK;
537 } else {
538 rslt = BME68X_E_NULL_PTR;
539 }
540
541 return rslt;
542}
543
544/*
545 * @brief This API is used to get the remaining duration that can be used for heating.
546 */
547uint32_t bme68x_get_meas_dur(const uint8_t op_mode, struct bme68x_conf *conf, struct bme68x_dev *dev) {
548 int8_t rslt;
549 uint32_t meas_dur = 0; /* Calculate in us */
550 uint32_t meas_cycles;
551 uint8_t os_to_meas_cycles[6] = {0, 1, 2, 4, 8, 16};
552
553 if (conf != NULL) {
554 /* Boundary check for temperature oversampling */
555 rslt = boundary_check(&conf->os_temp, BME68X_OS_16X, dev);
556
557 if (rslt == BME68X_OK) {
558 /* Boundary check for pressure oversampling */
559 rslt = boundary_check(&conf->os_pres, BME68X_OS_16X, dev);
560 }
561
562 if (rslt == BME68X_OK) {
563 /* Boundary check for humidity oversampling */
564 rslt = boundary_check(&conf->os_hum, BME68X_OS_16X, dev);
565 }
566
567 if (rslt == BME68X_OK) {
568 meas_cycles = os_to_meas_cycles[conf->os_temp];
569 meas_cycles += os_to_meas_cycles[conf->os_pres];
570 meas_cycles += os_to_meas_cycles[conf->os_hum];
571
572 /* TPH measurement duration */
573 meas_dur = meas_cycles * UINT32_C(1963);
574 meas_dur += UINT32_C(477 * 4); /* TPH switching duration */
575 meas_dur += UINT32_C(477 * 5); /* Gas measurement duration */
576
577 if (op_mode != BME68X_PARALLEL_MODE) {
578 meas_dur += UINT32_C(1000); /* Wake up duration of 1ms */
579 }
580 }
581 }
582
583 return meas_dur;
584}
585
586/*
587 * @brief This API reads the pressure, temperature and humidity and gas data
588 * from the sensor, compensates the data and store it in the bme68x_data
589 * structure instance passed by the user.
590 */
591int8_t bme68x_get_data(uint8_t op_mode, struct bme68x_data *data, uint8_t *n_data, struct bme68x_dev *dev) {
592 int8_t rslt;
593 uint8_t i = 0, j = 0, new_fields = 0;
594 struct bme68x_data *field_ptr[3] = {0};
595 struct bme68x_data field_data[3] = {{0}};
596
597 field_ptr[0] = &field_data[0];
598 field_ptr[1] = &field_data[1];
599 field_ptr[2] = &field_data[2];
600
601 rslt = null_ptr_check(dev);
602 if ((rslt == BME68X_OK) && (data != NULL)) {
603 /* Reading the sensor data in forced mode only */
604 if (op_mode == BME68X_FORCED_MODE) {
605 rslt = read_field_data(0, data, dev);
606 if (rslt == BME68X_OK) {
607 if (data->status & BME68X_NEW_DATA_MSK) {
608 new_fields = 1;
609 } else {
610 new_fields = 0;
611 rslt = BME68X_W_NO_NEW_DATA;
612 }
613 }
614 } else if ((op_mode == BME68X_PARALLEL_MODE) || (op_mode == BME68X_SEQUENTIAL_MODE)) {
615 /* Read the 3 fields and count the number of new data fields */
616 rslt = read_all_field_data(field_ptr, dev);
617
618 new_fields = 0;
619 for (i = 0; (i < 3) && (rslt == BME68X_OK); i++) {
620 if (field_ptr[i]->status & BME68X_NEW_DATA_MSK) {
621 new_fields++;
622 }
623 }
624
625 /* Sort the sensor data in parallel & sequential modes*/
626 for (i = 0; (i < 2) && (rslt == BME68X_OK); i++) {
627 for (j = i + 1; j < 3; j++) {
628 sort_sensor_data(i, j, field_ptr);
629 }
630 }
631
632 /* Copy the sorted data */
633 for (i = 0; ((i < 3) && (rslt == BME68X_OK)); i++) {
634 data[i] = *field_ptr[i];
635 }
636
637 if (new_fields == 0) {
638 rslt = BME68X_W_NO_NEW_DATA;
639 }
640 } else {
641 rslt = BME68X_W_DEFINE_OP_MODE;
642 }
643
644 if (n_data == NULL) {
645 rslt = BME68X_E_NULL_PTR;
646 } else {
647 *n_data = new_fields;
648 }
649 } else {
650 rslt = BME68X_E_NULL_PTR;
651 }
652
653 return rslt;
654}
655
656/*
657 * @brief This API is used to set the gas configuration of the sensor.
658 */
659int8_t bme68x_set_heatr_conf(uint8_t op_mode, const struct bme68x_heatr_conf *conf, struct bme68x_dev *dev) {
660 int8_t rslt;
661 uint8_t nb_conv = 0;
662 uint8_t hctrl, run_gas = 0;
663 uint8_t ctrl_gas_data[2];
664 uint8_t ctrl_gas_addr[2] = {BME68X_REG_CTRL_GAS_0, BME68X_REG_CTRL_GAS_1};
665
666 if (conf != NULL) {
667 rslt = bme68x_set_op_mode(BME68X_SLEEP_MODE, dev);
668 if (rslt == BME68X_OK) {
669 rslt = set_conf(conf, op_mode, &nb_conv, dev);
670 }
671
672 if (rslt == BME68X_OK) {
673 rslt = bme68x_get_regs(BME68X_REG_CTRL_GAS_0, ctrl_gas_data, 2, dev);
674 if (rslt == BME68X_OK) {
675 if (conf->enable == BME68X_ENABLE) {
676 hctrl = BME68X_ENABLE_HEATER;
677 if (dev->variant_id == BME68X_VARIANT_GAS_HIGH) {
678 run_gas = BME68X_ENABLE_GAS_MEAS_H;
679 } else {
680 run_gas = BME68X_ENABLE_GAS_MEAS_L;
681 }
682 } else {
683 hctrl = BME68X_DISABLE_HEATER;
684 run_gas = BME68X_DISABLE_GAS_MEAS;
685 }
686
687 ctrl_gas_data[0] = BME68X_SET_BITS(ctrl_gas_data[0], BME68X_HCTRL, hctrl);
688 ctrl_gas_data[1] = BME68X_SET_BITS_POS_0(ctrl_gas_data[1], BME68X_NBCONV, nb_conv);
689 ctrl_gas_data[1] = BME68X_SET_BITS(ctrl_gas_data[1], BME68X_RUN_GAS, run_gas);
690 rslt = bme68x_set_regs(ctrl_gas_addr, ctrl_gas_data, 2, dev);
691 }
692 }
693 } else {
694 rslt = BME68X_E_NULL_PTR;
695 }
696
697 return rslt;
698}
699
700/*!
701 * @brief This API is used to get the gas configuration of the sensor.
702 */
703int8_t bme68x_get_heatr_conf(const struct bme68x_heatr_conf *conf, struct bme68x_dev *dev) {
704 int8_t rslt = BME68X_OK;
705 uint8_t data_array[10] = {0};
706 uint8_t i;
707
708 if ((conf != NULL) && (conf->heatr_dur_prof != NULL) && (conf->heatr_temp_prof != NULL)) {
709 /* FIXME: Add conversion to deg C and ms and add the other parameters */
710 rslt = bme68x_get_regs(BME68X_REG_RES_HEAT0, data_array, 10, dev);
711
712 if (rslt == BME68X_OK) {
713 for (i = 0; i < conf->profile_len; i++) {
714 conf->heatr_temp_prof[i] = data_array[i];
715 }
716
717 rslt = bme68x_get_regs(BME68X_REG_GAS_WAIT0, data_array, 10, dev);
718
719 if (rslt == BME68X_OK) {
720 for (i = 0; i < conf->profile_len; i++) {
721 conf->heatr_dur_prof[i] = data_array[i];
722 }
723 }
724 }
725 } else {
726 rslt = BME68X_E_NULL_PTR;
727 }
728
729 return rslt;
730}
731
732/*
733 * @brief This API performs Self-test of low and high gas variants of BME68X
734 */
735int8_t bme68x_selftest_check(const struct bme68x_dev *dev) {
736
737 int8_t rslt;
738 uint8_t n_fields;
739 uint8_t i = 0;
740 struct bme68x_data data[BME68X_N_MEAS] = {{0}};
741 struct bme68x_dev t_dev;
742 struct bme68x_conf conf;
743 struct bme68x_heatr_conf heatr_conf;
744
745 rslt = null_ptr_check(dev);
746
747 if (rslt == BME68X_OK) {
748 /* Copy required parameters from reference bme68x_dev struct */
749 t_dev.amb_temp = 25;
750 t_dev.read = dev->read;
751 t_dev.write = dev->write;
752 t_dev.intf = dev->intf;
753 t_dev.delay_us = dev->delay_us;
754 t_dev.intf_ptr = dev->intf_ptr;
755
756 rslt = bme68x_init(&t_dev);
757 }
758#if 1
759 if (rslt == BME68X_OK) {
760 /* Set the temperature, pressure and humidity & filter settings */
761 conf.os_hum = BME68X_OS_1X;
762 conf.os_pres = BME68X_OS_16X;
763 conf.os_temp = BME68X_OS_2X;
764
765 /* Set the remaining gas sensor settings and link the heating profile */
766 heatr_conf.enable = BME68X_ENABLE;
767 heatr_conf.heatr_dur = BME68X_HEATR_DUR1;
768 heatr_conf.heatr_temp = BME68X_HIGH_TEMP;
769 rslt = bme68x_set_heatr_conf(BME68X_FORCED_MODE, &heatr_conf, &t_dev);
770 if (rslt == BME68X_OK) {
771 rslt = bme68x_set_conf(&conf, &t_dev);
772 if (rslt == BME68X_OK) {
773 rslt = bme68x_set_op_mode(BME68X_FORCED_MODE, &t_dev); /* Trigger a measurement */
774 if (rslt == BME68X_OK) {
775 /* Wait for the measurement to complete */
776 t_dev.delay_us(BME68X_HEATR_DUR1_DELAY, t_dev.intf_ptr);
777 rslt = bme68x_get_data(BME68X_FORCED_MODE, &data[0], &n_fields, &t_dev);
778 if (rslt == BME68X_OK) {
779 if ((data[0].idac != 0x00) && (data[0].idac != 0xFF) && (data[0].status & BME68X_GASM_VALID_MSK)) {
780 rslt = BME68X_OK;
781 } else {
782 rslt = BME68X_E_SELF_TEST;
783 }
784 }
785 }
786 }
787 }
788
789 heatr_conf.heatr_dur = BME68X_HEATR_DUR2;
790 while ((rslt == BME68X_OK) && (i < BME68X_N_MEAS)) {
791 if (i % 2 == 0) {
792 heatr_conf.heatr_temp = BME68X_HIGH_TEMP; /* Higher temperature */
793 } else {
794 heatr_conf.heatr_temp = BME68X_LOW_TEMP; /* Lower temperature */
795 }
796
797 rslt = bme68x_set_heatr_conf(BME68X_FORCED_MODE, &heatr_conf, &t_dev);
798 if (rslt == BME68X_OK) {
799 rslt = bme68x_set_conf(&conf, &t_dev);
800 if (rslt == BME68X_OK) {
801 rslt = bme68x_set_op_mode(BME68X_FORCED_MODE, &t_dev); /* Trigger a measurement */
802 if (rslt == BME68X_OK) {
803 /* Wait for the measurement to complete */
804 t_dev.delay_us(BME68X_HEATR_DUR2_DELAY, t_dev.intf_ptr);
805 rslt = bme68x_get_data(BME68X_FORCED_MODE, &data[i], &n_fields, &t_dev);
806 }
807 }
808 }
809
810 i++;
811 }
812
813 if (rslt == BME68X_OK) {
814 rslt = analyze_sensor_data(data, BME68X_N_MEAS);
815 }
816 }
817#endif
818 return rslt;
819}
820
821/*****************************INTERNAL APIs***********************************************/
822#ifndef BME68X_USE_FPU
823
824/** \fn static float calc_temperature(uint32_t temp_adc, struct bme68x_dev *dev)
825 *
826 * \brief This internal API is used to calculate the temperature value
827 *
828 * \param temp_adc: raw ADC temperature reading
829 * \param dev: pointer to device context containing calibration data
830 *
831 * \return: compensated temperature in degrees Celsius
832 */
833static int16_t calc_temperature(uint32_t temp_adc, struct bme68x_dev *dev) {
834 int64_t var1;
835 int64_t var2;
836 int64_t var3;
837 int16_t calc_temp;
838
839 /*lint -save -e701 -e702 -e704 */
840 var1 = ((int32_t)temp_adc >> 3) - ((int32_t)dev->calib.par_t1 << 1);
841 var2 = (var1 * (int32_t)dev->calib.par_t2) >> 11;
842 var3 = ((var1 >> 1) * (var1 >> 1)) >> 12;
843 var3 = ((var3) * ((int32_t)dev->calib.par_t3 << 4)) >> 14;
844 dev->calib.t_fine = (int32_t)(var2 + var3);
845 calc_temp = (int16_t)(((dev->calib.t_fine * 5) + 128) >> 8);
846
847 /*lint -restore */
848 return calc_temp;
849}
850
851/** \fn static float calc_pressure(uint32_t pres_adc, const struct bme68x_dev *dev)
852 *
853 * \brief This internal API is used to calculate the pressure value
854 *
855 * \param pres_adc: raw ADC pressure reading
856 * \param dev: pointer to device context containing calibration data
857 *
858 * \return: compensated pressure value in Pascals
859 */
860static uint32_t calc_pressure(uint32_t pres_adc, const struct bme68x_dev *dev) {
861 int32_t var1;
862 int32_t var2;
863 int32_t var3;
864 int32_t pressure_comp;
865
866 /* This value is used to check precedence to multiplication or division
867 * in the pressure compensation equation to achieve least loss of precision and
868 * avoiding overflows.
869 * i.e Comparing value, pres_ovf_check = (1 << 31) >> 1
870 */
871 const int32_t pres_ovf_check = INT32_C(0x40000000);
872
873 /*lint -save -e701 -e702 -e713 */
874 var1 = (((int32_t)dev->calib.t_fine) >> 1) - 64000;
875 var2 = ((((var1 >> 2) * (var1 >> 2)) >> 11) * (int32_t)dev->calib.par_p6) >> 2;
876 var2 = var2 + ((var1 * (int32_t)dev->calib.par_p5) << 1);
877 var2 = (var2 >> 2) + ((int32_t)dev->calib.par_p4 << 16);
878 var1 = (((((var1 >> 2) * (var1 >> 2)) >> 13) * ((int32_t)dev->calib.par_p3 << 5)) >> 3) + (((int32_t)dev->calib.par_p2 * var1) >> 1);
879 var1 = var1 >> 18;
880 var1 = ((32768 + var1) * (int32_t)dev->calib.par_p1) >> 15;
881 pressure_comp = 1048576 - pres_adc;
882 pressure_comp = (int32_t)((pressure_comp - (var2 >> 12)) * ((uint32_t)3125));
883 if (pressure_comp >= pres_ovf_check) {
884 pressure_comp = ((pressure_comp / var1) << 1);
885 } else {
886 pressure_comp = ((pressure_comp << 1) / var1);
887 }
888
889 var1 = ((int32_t)dev->calib.par_p9 * (int32_t)(((pressure_comp >> 3) * (pressure_comp >> 3)) >> 13)) >> 12;
890 var2 = ((int32_t)(pressure_comp >> 2) * (int32_t)dev->calib.par_p8) >> 13;
891 var3 =
892 ((int32_t)(pressure_comp >> 8) * (int32_t)(pressure_comp >> 8) * (int32_t)(pressure_comp >> 8) * (int32_t)dev->calib.par_p10) >> 17;
893 pressure_comp = (int32_t)(pressure_comp) + ((var1 + var2 + var3 + ((int32_t)dev->calib.par_p7 << 7)) >> 4);
894
895 /*lint -restore */
896 return (uint32_t)pressure_comp;
897}
898
899/** \fn static float calc_humidity(uint16_t hum_adc, const struct bme68x_dev *dev)
900 *
901 * \brief Calculate relative humidity (in percent, float) from raw ADC value
902 *
903 * \param hum_adc: raw humidity ADC value
904 * \param dev: pointer to device structure containing calibration data
905 *
906 * \return relative humidity in percent (0.0 - 100.0)
907 */
908static uint32_t calc_humidity(uint16_t hum_adc, const struct bme68x_dev *dev) {
909 int32_t var1;
910 int32_t var2;
911 int32_t var3;
912 int32_t var4;
913 int32_t var5;
914 int32_t var6;
915 int32_t temp_scaled;
916 int32_t calc_hum;
917
918 /*lint -save -e702 -e704 */
919 temp_scaled = (((int32_t)dev->calib.t_fine * 5) + 128) >> 8;
920 var1 = (int32_t)(hum_adc - ((int32_t)((int32_t)dev->calib.par_h1 * 16))) -
921 (((temp_scaled * (int32_t)dev->calib.par_h3) / ((int32_t)100)) >> 1);
922 var2 = ((int32_t)dev->calib.par_h2 *
923 (((temp_scaled * (int32_t)dev->calib.par_h4) / ((int32_t)100)) +
924 (((temp_scaled * ((temp_scaled * (int32_t)dev->calib.par_h5) / ((int32_t)100))) >> 6) / ((int32_t)100)) + (int32_t)(1 << 14))) >>
925 10;
926 var3 = var1 * var2;
927 var4 = (int32_t)dev->calib.par_h6 << 7;
928 var4 = ((var4) + ((temp_scaled * (int32_t)dev->calib.par_h7) / ((int32_t)100))) >> 4;
929 var5 = ((var3 >> 14) * (var3 >> 14)) >> 10;
930 var6 = (var4 * var5) >> 1;
931 calc_hum = (((var3 + var6) >> 10) * ((int32_t)1000)) >> 12;
932 if (calc_hum > 100000) /* Cap at 100%rH */
933 {
934 calc_hum = 100000;
935 } else if (calc_hum < 0) {
936 calc_hum = 0;
937 }
938
939 /*lint -restore */
940 return (uint32_t)calc_hum;
941}
942
943/** \fn static float calc_gas_resistance_low(uint16_t gas_res_adc, uint8_t gas_range, const struct bme68x_dev *dev)
944 *
945 * \brief This internal API is used to calculate the gas resistance low value in float
946 *
947 * \param gas_res_adc Raw ADC value for gas resistance
948 * \param gas_range Gas range index used for compensation
949 * \param dev Pointer to device structure containing calibration data
950 *
951 * \return Calculated gas resistance in ohms
952 */
953static uint32_t calc_gas_resistance_low(uint16_t gas_res_adc, uint8_t gas_range, const struct bme68x_dev *dev) {
954 int64_t var1;
955 uint64_t var2;
956 int64_t var3;
957 uint32_t calc_gas_res;
958 uint32_t lookup_table1[16] = {UINT32_C(2147483647), UINT32_C(2147483647), UINT32_C(2147483647), UINT32_C(2147483647),
959 UINT32_C(2147483647), UINT32_C(2126008810), UINT32_C(2147483647), UINT32_C(2130303777),
960 UINT32_C(2147483647), UINT32_C(2147483647), UINT32_C(2143188679), UINT32_C(2136746228),
961 UINT32_C(2147483647), UINT32_C(2126008810), UINT32_C(2147483647), UINT32_C(2147483647)};
962 uint32_t lookup_table2[16] = {UINT32_C(4096000000), UINT32_C(2048000000), UINT32_C(1024000000), UINT32_C(512000000),
963 UINT32_C(255744255), UINT32_C(127110228), UINT32_C(64000000), UINT32_C(32258064),
964 UINT32_C(16016016), UINT32_C(8000000), UINT32_C(4000000), UINT32_C(2000000),
965 UINT32_C(1000000), UINT32_C(500000), UINT32_C(250000), UINT32_C(125000)};
966
967 /*lint -save -e704 */
968 var1 = (int64_t)((1340 + (5 * (int64_t)dev->calib.range_sw_err)) * ((int64_t)lookup_table1[gas_range])) >> 16;
969 var2 = (((int64_t)((int64_t)gas_res_adc << 15) - (int64_t)(16777216)) + var1);
970 var3 = (((int64_t)lookup_table2[gas_range] * (int64_t)var1) >> 9);
971 calc_gas_res = (uint32_t)((var3 + ((int64_t)var2 >> 1)) / (int64_t)var2);
972
973 /*lint -restore */
974 return calc_gas_res;
975}
976
977/** \fn static float calc_gas_resistance_high(uint16_t gas_res_adc, uint8_t gas_range)
978 *
979 * \brief This internal API is used to calculate the gas resistance value in float
980 *
981 * \param gas_res_adc Raw ADC value for gas resistance
982 * \param gas_range Gas range index used for compensation
983 *
984 * \return Calculated gas resistance in ohms
985 */
986static uint32_t calc_gas_resistance_high(uint16_t gas_res_adc, uint8_t gas_range) {
987 uint32_t calc_gas_res;
988 uint32_t var1 = UINT32_C(262144) >> gas_range;
989 int32_t var2 = (int32_t)gas_res_adc - INT32_C(512);
990
991 var2 *= INT32_C(3);
992 var2 = INT32_C(4096) + var2;
993
994 /* multiplying 10000 then dividing then multiplying by 100 instead of multiplying by 1000000 to prevent overflow */
995 calc_gas_res = (UINT32_C(10000) * var1) / (uint32_t)var2;
996 calc_gas_res = calc_gas_res * 100;
997
998 return calc_gas_res;
999}
1000
1001/* This internal API is used to calculate the heater resistance value using integer */
1002static uint8_t calc_res_heat(uint16_t temp, const struct bme68x_dev *dev) {
1003 uint8_t heatr_res;
1004 int32_t var1;
1005 int32_t var2;
1006 int32_t var3;
1007 int32_t var4;
1008 int32_t var5;
1009 int32_t heatr_res_x100;
1010
1011 if (temp > 400) /* Cap temperature */
1012 {
1013 temp = 400;
1014 }
1015
1016 var1 = (((int32_t)dev->amb_temp * dev->calib.par_gh3) / 1000) * 256;
1017 var2 = (dev->calib.par_gh1 + 784) * (((((dev->calib.par_gh2 + 154009) * temp * 5) / 100) + 3276800) / 10);
1018 var3 = var1 + (var2 / 2);
1019 var4 = (var3 / (dev->calib.res_heat_range + 4));
1020 var5 = (131 * dev->calib.res_heat_val) + 65536;
1021 heatr_res_x100 = (int32_t)(((var4 / var5) - 250) * 34);
1022 heatr_res = (uint8_t)((heatr_res_x100 + 50) / 100);
1023
1024 return heatr_res;
1025}
1026
1027#else
1028
1029/** \fn static float calc_temperature(uint32_t temp_adc, struct bme68x_dev *dev)
1030 *
1031 * \brief This internal API is used to calculate the temperature value
1032 *
1033 * \param temp_adc: raw ADC temperature reading
1034 * \param dev: pointer to device context containing calibration data
1035 *
1036 * \return: compensated temperature in degrees Celsius
1037 */
1038static float calc_temperature(uint32_t temp_adc, struct bme68x_dev *dev) {
1039 float var1;
1040 float var2;
1041 float calc_temp;
1042
1043 /* calculate var1 data */
1044 var1 = ((((float)temp_adc / 16384.0f) - ((float)dev->calib.par_t1 / 1024.0f)) * ((float)dev->calib.par_t2));
1045
1046 /* calculate var2 data */
1047 var2 = (((((float)temp_adc / 131072.0f) - ((float)dev->calib.par_t1 / 8192.0f)) *
1048 (((float)temp_adc / 131072.0f) - ((float)dev->calib.par_t1 / 8192.0f))) *
1049 ((float)dev->calib.par_t3 * 16.0f));
1050
1051 /* t_fine value*/
1052 dev->calib.t_fine = (var1 + var2);
1053
1054 /* compensated temperature data*/
1055 calc_temp = ((dev->calib.t_fine) / 5120.0f);
1056
1057 return calc_temp;
1058}
1059
1060/** \fn static float calc_pressure(uint32_t pres_adc, const struct bme68x_dev *dev)
1061 *
1062 * \brief This internal API is used to calculate the pressure value
1063 *
1064 * \param pres_adc: raw ADC pressure reading
1065 * \param dev: pointer to device context containing calibration data
1066 *
1067 * \return: compensated pressure value in Pascals
1068 */
1069static float calc_pressure(uint32_t pres_adc, const struct bme68x_dev *dev) {
1070 float var1;
1071 float var2;
1072 float var3;
1073 float calc_pres;
1074
1075 var1 = (((float)dev->calib.t_fine / 2.0f) - 64000.0f);
1076 var2 = var1 * var1 * (((float)dev->calib.par_p6) / (131072.0f));
1077 var2 = var2 + (var1 * ((float)dev->calib.par_p5) * 2.0f);
1078 var2 = (var2 / 4.0f) + (((float)dev->calib.par_p4) * 65536.0f);
1079 var1 = (((((float)dev->calib.par_p3 * var1 * var1) / 16384.0f) + ((float)dev->calib.par_p2 * var1)) / 524288.0f);
1080 var1 = ((1.0f + (var1 / 32768.0f)) * ((float)dev->calib.par_p1));
1081 calc_pres = (1048576.0f - ((float)pres_adc));
1082
1083 /* Avoid exception caused by division by zero */
1084 if ((int)var1 != 0) {
1085 calc_pres = (((calc_pres - (var2 / 4096.0f)) * 6250.0f) / var1);
1086 var1 = (((float)dev->calib.par_p9) * calc_pres * calc_pres) / 2147483648.0f;
1087 var2 = calc_pres * (((float)dev->calib.par_p8) / 32768.0f);
1088 var3 = ((calc_pres / 256.0f) * (calc_pres / 256.0f) * (calc_pres / 256.0f) * (dev->calib.par_p10 / 131072.0f));
1089 calc_pres = (calc_pres + (var1 + var2 + var3 + ((float)dev->calib.par_p7 * 128.0f)) / 16.0f);
1090 } else {
1091 calc_pres = 0;
1092 }
1093
1094 return calc_pres;
1095}
1096
1097/** \fn static float calc_humidity(uint16_t hum_adc, const struct bme68x_dev *dev)
1098 *
1099 * \brief Calculate relative humidity (in percent, float) from raw ADC value
1100 *
1101 * \param hum_adc: raw humidity ADC value
1102 * \param dev: pointer to device structure containing calibration data
1103 *
1104 * \return relative humidity in percent (0.0 - 100.0)
1105 */
1106static float calc_humidity(uint16_t hum_adc, const struct bme68x_dev *dev) {
1107 float calc_hum;
1108 float var1;
1109 float var2;
1110 float var3;
1111 float var4;
1112 float temp_comp;
1113
1114 /* compensated temperature data*/
1115 temp_comp = ((dev->calib.t_fine) / 5120.0f);
1116 var1 = (float)((float)hum_adc) - (((float)dev->calib.par_h1 * 16.0f) + (((float)dev->calib.par_h3 / 2.0f) * temp_comp));
1117 var2 = var1 * ((float)(((float)dev->calib.par_h2 / 262144.0f) * (1.0f + (((float)dev->calib.par_h4 / 16384.0f) * temp_comp) +
1118 (((float)dev->calib.par_h5 / 1048576.0f) * temp_comp * temp_comp))));
1119 var3 = (float)dev->calib.par_h6 / 16384.0f;
1120 var4 = (float)dev->calib.par_h7 / 2097152.0f;
1121 calc_hum = var2 + ((var3 + (var4 * temp_comp)) * var2 * var2);
1122 if (calc_hum > 100.0f) {
1123 calc_hum = 100.0f;
1124 } else if (calc_hum < 0.0f) {
1125 calc_hum = 0.0f;
1126 }
1127
1128 return calc_hum;
1129}
1130
1131/** \fn static float calc_gas_resistance_low(uint16_t gas_res_adc, uint8_t gas_range, const struct bme68x_dev *dev)
1132 *
1133 * \brief This internal API is used to calculate the gas resistance low value in float
1134 *
1135 * \param gas_res_adc Raw ADC value for gas resistance
1136 * \param gas_range Gas range index used for compensation
1137 * \param dev Pointer to device structure containing calibration data
1138 *
1139 * \return Calculated gas resistance in ohms
1140 */
1141static float calc_gas_resistance_low(uint16_t gas_res_adc, uint8_t gas_range, const struct bme68x_dev *dev) {
1142 float calc_gas_res;
1143 float var1;
1144 float var2;
1145 float var3;
1146 float gas_res_f = gas_res_adc;
1147 float gas_range_f = (1U << gas_range); /*lint !e790 / Suspicious truncation, integral to float */
1148 const float lookup_k1_range[16] = {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, -1.0f, 0.0f, -0.8f, 0.0f, 0.0f, -0.2f, -0.5f, 0.0f, -1.0f, 0.0f, 0.0f};
1149 const float lookup_k2_range[16] = {0.0f, 0.0f, 0.0f, 0.0f, 0.1f, 0.7f, 0.0f, -0.8f, -0.1f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f};
1150
1151 var1 = (1340.0f + (5.0f * dev->calib.range_sw_err));
1152 var2 = (var1) * (1.0f + lookup_k1_range[gas_range] / 100.0f);
1153 var3 = 1.0f + (lookup_k2_range[gas_range] / 100.0f);
1154 calc_gas_res = 1.0f / (float)(var3 * (0.000000125f) * gas_range_f * (((gas_res_f - 512.0f) / var2) + 1.0f));
1155
1156 return calc_gas_res;
1157}
1158
1159/** \fn static float calc_gas_resistance_high(uint16_t gas_res_adc, uint8_t gas_range)
1160 *
1161 * \brief This internal API is used to calculate the gas resistance value in float
1162 *
1163 * \param gas_res_adc Raw ADC value for gas resistance
1164 * \param gas_range Gas range index used for compensation
1165 *
1166 * \return Calculated gas resistance in ohms
1167 */
1168static float calc_gas_resistance_high(uint16_t gas_res_adc, uint8_t gas_range) {
1169 float calc_gas_res;
1170 uint32_t var1 = UINT32_C(262144) >> gas_range;
1171 int32_t var2 = (int32_t)gas_res_adc - INT32_C(512);
1172
1173 var2 *= INT32_C(3);
1174 var2 = INT32_C(4096) + var2;
1175
1176 calc_gas_res = 1000000.0f * (float)var1 / (float)var2;
1177
1178 return calc_gas_res;
1179}
1180
1181/* This internal API is used to calculate the heater resistance value using float */
1182static uint8_t calc_res_heat(uint16_t temp, const struct bme68x_dev *dev) {
1183 float var1;
1184 float var2;
1185 float var3;
1186 float var4;
1187 float var5;
1188 uint8_t res_heat;
1189
1190 if (temp > 400) /* Cap temperature */
1191 {
1192 temp = 400;
1193 }
1194
1195 var1 = (((float)dev->calib.par_gh1 / (16.0f)) + 49.0f);
1196 var2 = ((((float)dev->calib.par_gh2 / (32768.0f)) * (0.0005f)) + 0.00235f);
1197 var3 = ((float)dev->calib.par_gh3 / (1024.0f));
1198 var4 = (var1 * (1.0f + (var2 * (float)temp)));
1199 var5 = (var4 + (var3 * (float)dev->amb_temp));
1200 res_heat =
1201 (uint8_t)(3.4f *
1202 ((var5 * (4 / (4 + (float)dev->calib.res_heat_range)) * (1 / (1 + ((float)dev->calib.res_heat_val * 0.002f)))) - 25));
1203
1204 return res_heat;
1205}
1206
1207#endif
1208
1209/** \fn static uint8_t calc_gas_wait(uint16_t dur)
1210 * \brief convert duration in milliseconds to encoded register value
1211 * \param dur duration in milliseconds
1212 *
1213 * The sensor expects gas wait time encoded in a specific format where the
1214 * lower 6 bits store a base value and the upper 2 bits act as a multiplier
1215 * (factor) representing powers of 4. Durations >= 0xFC0 are saturated to
1216 * 0xFF (maximum encoded value).
1217 *
1218 * \return Encoded gas wait time suitable for the sensor register
1219 */
1220static uint8_t calc_gas_wait(uint16_t dur) {
1221 uint8_t factor = 0;
1222 uint8_t durval;
1223
1224 if (dur >= 0xfc0) {
1225 durval = 0xff; /* Max duration*/
1226 } else {
1227 while (dur > 0x3F) {
1228 dur = dur / 4;
1229 factor += 1;
1230 }
1231
1232 durval = (uint8_t)(dur + (factor * 64));
1233 }
1234
1235 return durval;
1236}
1237
1238/* This internal API is used to read a single data of the sensor */
1239static int8_t read_field_data(uint8_t index, struct bme68x_data *data, struct bme68x_dev *dev) {
1240 int8_t rslt = BME68X_OK;
1241 uint8_t buff[BME68X_LEN_FIELD] = {0};
1242 uint8_t gas_range_l, gas_range_h;
1243 uint32_t adc_temp;
1244 uint32_t adc_pres;
1245 uint16_t adc_hum;
1246 uint16_t adc_gas_res_low, adc_gas_res_high;
1247 uint8_t tries = 5;
1248
1249 while ((tries) && (rslt == BME68X_OK)) {
1250 rslt = bme68x_get_regs(((uint8_t)(BME68X_REG_FIELD0 + (index * BME68X_LEN_FIELD_OFFSET))), buff, (uint16_t)BME68X_LEN_FIELD, dev);
1251 if (!data) {
1252 rslt = BME68X_E_NULL_PTR;
1253 break;
1254 }
1255
1256 data->status = buff[0] & BME68X_NEW_DATA_MSK;
1257 data->gas_index = buff[0] & BME68X_GAS_INDEX_MSK;
1258 data->meas_index = buff[1];
1259
1260 /* read the raw data from the sensor */
1261 adc_pres = (uint32_t)(((uint32_t)buff[2] * 4096) | ((uint32_t)buff[3] * 16) | ((uint32_t)buff[4] / 16));
1262 adc_temp = (uint32_t)(((uint32_t)buff[5] * 4096) | ((uint32_t)buff[6] * 16) | ((uint32_t)buff[7] / 16));
1263 adc_hum = (uint16_t)(((uint32_t)buff[8] * 256) | (uint32_t)buff[9]);
1264 adc_gas_res_low = (uint16_t)((uint32_t)buff[13] * 4 | (((uint32_t)buff[14]) / 64));
1265 adc_gas_res_high = (uint16_t)((uint32_t)buff[15] * 4 | (((uint32_t)buff[16]) / 64));
1266 gas_range_l = buff[14] & BME68X_GAS_RANGE_MSK;
1267 gas_range_h = buff[16] & BME68X_GAS_RANGE_MSK;
1268 if (dev->variant_id == BME68X_VARIANT_GAS_HIGH) {
1269 data->status |= buff[16] & BME68X_GASM_VALID_MSK;
1270 data->status |= buff[16] & BME68X_HEAT_STAB_MSK;
1271 } else {
1272 data->status |= buff[14] & BME68X_GASM_VALID_MSK;
1273 data->status |= buff[14] & BME68X_HEAT_STAB_MSK;
1274 }
1275
1276 if ((data->status & BME68X_NEW_DATA_MSK) && (rslt == BME68X_OK)) {
1277 rslt = bme68x_get_regs(BME68X_REG_RES_HEAT0 + data->gas_index, &data->res_heat, 1, dev);
1278 if (rslt == BME68X_OK) {
1279 rslt = bme68x_get_regs(BME68X_REG_IDAC_HEAT0 + data->gas_index, &data->idac, 1, dev);
1280 }
1281
1282 if (rslt == BME68X_OK) {
1283 rslt = bme68x_get_regs(BME68X_REG_GAS_WAIT0 + data->gas_index, &data->gas_wait, 1, dev);
1284 }
1285
1286 if (rslt == BME68X_OK) {
1287 data->temperature = calc_temperature(adc_temp, dev);
1288 data->pressure = calc_pressure(adc_pres, dev);
1289 data->humidity = calc_humidity(adc_hum, dev);
1290 if (dev->variant_id == BME68X_VARIANT_GAS_HIGH) {
1291 data->gas_resistance = calc_gas_resistance_high(adc_gas_res_high, gas_range_h);
1292 } else {
1293 data->gas_resistance = calc_gas_resistance_low(adc_gas_res_low, gas_range_l, dev);
1294 }
1295
1296 break;
1297 }
1298 }
1299
1300 if (rslt == BME68X_OK) {
1301 dev->delay_us(BME68X_PERIOD_POLL, dev->intf_ptr);
1302 }
1303
1304 tries--;
1305 }
1306
1307 return rslt;
1308}
1309
1310/* This internal API is used to read all data fields of the sensor */
1311static int8_t read_all_field_data(struct bme68x_data *const data[], struct bme68x_dev *dev) {
1312 int8_t rslt = BME68X_OK;
1313 uint8_t buff[BME68X_LEN_FIELD * 3] = {0};
1314 uint8_t gas_range_l, gas_range_h;
1315 uint32_t adc_temp;
1316 uint32_t adc_pres;
1317 uint16_t adc_hum;
1318 uint16_t adc_gas_res_low, adc_gas_res_high;
1319 uint8_t off;
1320 uint8_t set_val[30] = {0}; /* idac, res_heat, gas_wait */
1321 uint8_t i;
1322
1323 if (!data[0] && !data[1] && !data[2]) {
1324 rslt = BME68X_E_NULL_PTR;
1325 }
1326
1327 if (rslt == BME68X_OK) {
1328 rslt = bme68x_get_regs(BME68X_REG_FIELD0, buff, (uint32_t)BME68X_LEN_FIELD * 3, dev);
1329 }
1330
1331 if (rslt == BME68X_OK) {
1332 rslt = bme68x_get_regs(BME68X_REG_IDAC_HEAT0, set_val, 30, dev);
1333 }
1334
1335 for (i = 0; ((i < 3) && (rslt == BME68X_OK)); i++) {
1336 off = (uint8_t)(i * BME68X_LEN_FIELD);
1337 data[i]->status = buff[off] & BME68X_NEW_DATA_MSK;
1338 data[i]->gas_index = buff[off] & BME68X_GAS_INDEX_MSK;
1339 data[i]->meas_index = buff[off + 1];
1340
1341 /* read the raw data from the sensor */
1342 adc_pres = (uint32_t)(((uint32_t)buff[off + 2] * 4096) | ((uint32_t)buff[off + 3] * 16) | ((uint32_t)buff[off + 4] / 16));
1343 adc_temp = (uint32_t)(((uint32_t)buff[off + 5] * 4096) | ((uint32_t)buff[off + 6] * 16) | ((uint32_t)buff[off + 7] / 16));
1344 adc_hum = (uint16_t)(((uint32_t)buff[off + 8] * 256) | (uint32_t)buff[off + 9]);
1345 adc_gas_res_low = (uint16_t)((uint32_t)buff[off + 13] * 4 | (((uint32_t)buff[off + 14]) / 64));
1346 adc_gas_res_high = (uint16_t)((uint32_t)buff[off + 15] * 4 | (((uint32_t)buff[off + 16]) / 64));
1347 gas_range_l = buff[off + 14] & BME68X_GAS_RANGE_MSK;
1348 gas_range_h = buff[off + 16] & BME68X_GAS_RANGE_MSK;
1349 if (dev->variant_id == BME68X_VARIANT_GAS_HIGH) {
1350 data[i]->status |= buff[off + 16] & BME68X_GASM_VALID_MSK;
1351 data[i]->status |= buff[off + 16] & BME68X_HEAT_STAB_MSK;
1352 } else {
1353 data[i]->status |= buff[off + 14] & BME68X_GASM_VALID_MSK;
1354 data[i]->status |= buff[off + 14] & BME68X_HEAT_STAB_MSK;
1355 }
1356
1357 data[i]->idac = set_val[data[i]->gas_index];
1358 data[i]->res_heat = set_val[10 + data[i]->gas_index];
1359 data[i]->gas_wait = set_val[20 + data[i]->gas_index];
1360 data[i]->temperature = calc_temperature(adc_temp, dev);
1361 data[i]->pressure = calc_pressure(adc_pres, dev);
1362 data[i]->humidity = calc_humidity(adc_hum, dev);
1363 if (dev->variant_id == BME68X_VARIANT_GAS_HIGH) {
1364 data[i]->gas_resistance = calc_gas_resistance_high(adc_gas_res_high, gas_range_h);
1365 } else {
1366 data[i]->gas_resistance = calc_gas_resistance_low(adc_gas_res_low, gas_range_l, dev);
1367 }
1368 }
1369
1370 return rslt;
1371}
1372
1373/* This internal API is used to switch between SPI memory pages */
1374static int8_t set_mem_page(uint8_t reg_addr, struct bme68x_dev *dev) {
1375 int8_t rslt;
1376 uint8_t reg;
1377 uint8_t mem_page;
1378
1379 /* Check for null pointers in the device structure*/
1380 rslt = null_ptr_check(dev);
1381 if (rslt == BME68X_OK) {
1382 if (reg_addr > 0x7f) {
1383 mem_page = BME68X_MEM_PAGE1;
1384 } else {
1385 mem_page = BME68X_MEM_PAGE0;
1386 }
1387
1388 if (mem_page != dev->mem_page) {
1389 dev->mem_page = mem_page;
1390 dev->intf_rslt = dev->read(BME68X_REG_MEM_PAGE | BME68X_SPI_RD_MSK, &reg, 1, dev->intf_ptr);
1391 if (dev->intf_rslt != 0) {
1392 rslt = BME68X_E_COM_FAIL;
1393 }
1394
1395 if (rslt == BME68X_OK) {
1396 reg = reg & (~BME68X_MEM_PAGE_MSK);
1397 reg = reg | (dev->mem_page & BME68X_MEM_PAGE_MSK);
1398 dev->intf_rslt = dev->write(BME68X_REG_MEM_PAGE & BME68X_SPI_WR_MSK, &reg, 1, dev->intf_ptr);
1399 if (dev->intf_rslt != 0) {
1400 rslt = BME68X_E_COM_FAIL;
1401 }
1402 }
1403 }
1404 }
1405
1406 return rslt;
1407}
1408
1409/* This internal API is used to get the current SPI memory page */
1410static int8_t get_mem_page(struct bme68x_dev *dev) {
1411 int8_t rslt;
1412 uint8_t reg;
1413
1414 /* Check for null pointer in the device structure*/
1415 rslt = null_ptr_check(dev);
1416 if (rslt == BME68X_OK) {
1417 dev->intf_rslt = dev->read(BME68X_REG_MEM_PAGE | BME68X_SPI_RD_MSK, &reg, 1, dev->intf_ptr);
1418 if (dev->intf_rslt != 0) {
1419 rslt = BME68X_E_COM_FAIL;
1420 } else {
1421 dev->mem_page = reg & BME68X_MEM_PAGE_MSK;
1422 }
1423 }
1424
1425 return rslt;
1426}
1427
1428/* This internal API is used to limit the max value of a parameter */
1429static int8_t boundary_check(uint8_t *value, uint8_t max, struct bme68x_dev *dev) {
1430 int8_t rslt;
1431
1432 rslt = null_ptr_check(dev);
1433 if ((value != NULL) && (rslt == BME68X_OK)) {
1434 /* Check if value is above maximum value */
1435 if (*value > max) {
1436 /* Auto correct the invalid value to maximum value */
1437 *value = max;
1438 dev->info_msg |= BME68X_I_PARAM_CORR;
1439 }
1440 } else {
1441 rslt = BME68X_E_NULL_PTR;
1442 }
1443
1444 return rslt;
1445}
1446
1447/* This internal API is used to check the bme68x_dev for null pointers */
1448static int8_t null_ptr_check(const struct bme68x_dev *dev) {
1449 int8_t rslt = BME68X_OK;
1450
1451 if ((dev == NULL) || (dev->read == NULL) || (dev->write == NULL) || (dev->delay_us == NULL)) {
1452 /* Device structure pointer is not valid */
1453 rslt = BME68X_E_NULL_PTR;
1454 }
1455
1456 return rslt;
1457}
1458
1459/* This internal API is used to set heater configurations */
1460static int8_t set_conf(const struct bme68x_heatr_conf *conf, uint8_t op_mode, uint8_t *nb_conv, struct bme68x_dev *dev) {
1461 int8_t rslt = BME68X_OK;
1462 uint8_t i;
1463 uint8_t shared_dur;
1464 uint8_t write_len = 0;
1465 uint8_t heater_dur_shared_addr = BME68X_REG_SHD_HEATR_DUR;
1466 uint8_t rh_reg_addr[10] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
1467 uint8_t rh_reg_data[10] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
1468 uint8_t gw_reg_addr[10] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
1469 uint8_t gw_reg_data[10] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
1470
1471 switch (op_mode) {
1472 case BME68X_FORCED_MODE:
1473 rh_reg_addr[0] = BME68X_REG_RES_HEAT0;
1474 rh_reg_data[0] = calc_res_heat(conf->heatr_temp, dev);
1475 gw_reg_addr[0] = BME68X_REG_GAS_WAIT0;
1476 gw_reg_data[0] = calc_gas_wait(conf->heatr_dur);
1477 (*nb_conv) = 0;
1478 write_len = 1;
1479 break;
1480 case BME68X_SEQUENTIAL_MODE:
1481 if ((!conf->heatr_dur_prof) || (!conf->heatr_temp_prof)) {
1482 rslt = BME68X_E_NULL_PTR;
1483 break;
1484 }
1485
1486 for (i = 0; i < conf->profile_len; i++) {
1487 rh_reg_addr[i] = BME68X_REG_RES_HEAT0 + i;
1488 rh_reg_data[i] = calc_res_heat(conf->heatr_temp_prof[i], dev);
1489 gw_reg_addr[i] = BME68X_REG_GAS_WAIT0 + i;
1490 gw_reg_data[i] = calc_gas_wait(conf->heatr_dur_prof[i]);
1491 }
1492
1493 (*nb_conv) = conf->profile_len;
1494 write_len = conf->profile_len;
1495 break;
1496 case BME68X_PARALLEL_MODE:
1497 if ((!conf->heatr_dur_prof) || (!conf->heatr_temp_prof)) {
1498 rslt = BME68X_E_NULL_PTR;
1499 break;
1500 }
1501
1502 if (conf->shared_heatr_dur == 0) {
1503 rslt = BME68X_W_DEFINE_SHD_HEATR_DUR;
1504 }
1505
1506 for (i = 0; i < conf->profile_len; i++) {
1507 rh_reg_addr[i] = BME68X_REG_RES_HEAT0 + i;
1508 rh_reg_data[i] = calc_res_heat(conf->heatr_temp_prof[i], dev);
1509 gw_reg_addr[i] = BME68X_REG_GAS_WAIT0 + i;
1510 gw_reg_data[i] = (uint8_t)conf->heatr_dur_prof[i];
1511 }
1512
1513 (*nb_conv) = conf->profile_len;
1514 write_len = conf->profile_len;
1515 shared_dur = calc_heatr_dur_shared(conf->shared_heatr_dur);
1516 if (rslt == BME68X_OK) {
1517 rslt = bme68x_set_regs(&heater_dur_shared_addr, &shared_dur, 1, dev);
1518 }
1519
1520 break;
1521 default:
1522 rslt = BME68X_W_DEFINE_OP_MODE;
1523 }
1524
1525 if (rslt == BME68X_OK) {
1526 rslt = bme68x_set_regs(rh_reg_addr, rh_reg_data, write_len, dev);
1527 }
1528
1529 if (rslt == BME68X_OK) {
1530 rslt = bme68x_set_regs(gw_reg_addr, gw_reg_data, write_len, dev);
1531 }
1532
1533 return rslt;
1534}
1535
1536/* This internal API is used to calculate the register value for
1537 * shared heater duration */
1538static uint8_t calc_heatr_dur_shared(uint16_t dur) {
1539 uint8_t factor = 0;
1540 uint8_t heatdurval;
1541
1542 if (dur >= 0x783) {
1543 heatdurval = 0xff; /* Max duration */
1544 } else {
1545 /* Step size of 0.477ms */
1546 dur = (uint16_t)(((uint32_t)dur * 1000) / 477);
1547 while (dur > 0x3F) {
1548 dur = dur >> 2;
1549 factor += 1;
1550 }
1551
1552 heatdurval = (uint8_t)(dur + (factor * 64));
1553 }
1554
1555 return heatdurval;
1556}
1557
1558/* This internal API is used sort the sensor data */
1559static void sort_sensor_data(uint8_t low_index, uint8_t high_index, struct bme68x_data *field[]) {
1560 int16_t meas_index1;
1561 int16_t meas_index2;
1562
1563 meas_index1 = (int16_t)field[low_index]->meas_index;
1564 meas_index2 = (int16_t)field[high_index]->meas_index;
1565 if ((field[low_index]->status & BME68X_NEW_DATA_MSK) && (field[high_index]->status & BME68X_NEW_DATA_MSK)) {
1566 int16_t diff = meas_index2 - meas_index1;
1567 if (((diff > -3) && (diff < 0)) || (diff > 2)) {
1568 swap_fields(low_index, high_index, field);
1569 }
1570 } else if (field[high_index]->status & BME68X_NEW_DATA_MSK) {
1571 swap_fields(low_index, high_index, field);
1572 }
1573
1574 /* Sorting field data
1575 *
1576 * The 3 fields are filled in a fixed order with data in an incrementing
1577 * 8-bit sub-measurement index which looks like
1578 * Field index | Sub-meas index
1579 * 0 | 0
1580 * 1 | 1
1581 * 2 | 2
1582 * 0 | 3
1583 * 1 | 4
1584 * 2 | 5
1585 * ...
1586 * 0 | 252
1587 * 1 | 253
1588 * 2 | 254
1589 * 0 | 255
1590 * 1 | 0
1591 * 2 | 1
1592 *
1593 * The fields are sorted in a way so as to always deal with only a snapshot
1594 * of comparing 2 fields at a time. The order being
1595 * field0 & field1
1596 * field0 & field2
1597 * field1 & field2
1598 * Here the oldest data should be in field0 while the newest is in field2.
1599 * In the following documentation, field0's position would referred to as
1600 * the lowest and field2 as the highest.
1601 *
1602 * In order to sort we have to consider the following cases,
1603 *
1604 * Case A: No fields have new data
1605 * Then do not sort, as this data has already been read.
1606 *
1607 * Case B: Higher field has new data
1608 * Then the new field get's the lowest position.
1609 *
1610 * Case C: Both fields have new data
1611 * We have to put the oldest sample in the lowest position. Since the
1612 * sub-meas index contains in essence the age of the sample, we calculate
1613 * the difference between the higher field and the lower field.
1614 * Here we have 3 sub-cases,
1615 * Case 1: Regular read without overwrite
1616 * Field index | Sub-meas index
1617 * 0 | 3
1618 * 1 | 4
1619 *
1620 * Field index | Sub-meas index
1621 * 0 | 3
1622 * 2 | 5
1623 *
1624 * The difference is always <= 2. There is no need to swap as the
1625 * oldest sample is already in the lowest position.
1626 *
1627 * Case 2: Regular read with an overflow and without an overwrite
1628 * Field index | Sub-meas index
1629 * 0 | 255
1630 * 1 | 0
1631 *
1632 * Field index | Sub-meas index
1633 * 0 | 254
1634 * 2 | 0
1635 *
1636 * The difference is always <= -3. There is no need to swap as the
1637 * oldest sample is already in the lowest position.
1638 *
1639 * Case 3: Regular read with overwrite
1640 * Field index | Sub-meas index
1641 * 0 | 6
1642 * 1 | 4
1643 *
1644 * Field index | Sub-meas index
1645 * 0 | 6
1646 * 2 | 5
1647 *
1648 * The difference is always > -3. There is a need to swap as the
1649 * oldest sample is not in the lowest position.
1650 *
1651 * Case 4: Regular read with overwrite and overflow
1652 * Field index | Sub-meas index
1653 * 0 | 0
1654 * 1 | 254
1655 *
1656 * Field index | Sub-meas index
1657 * 0 | 0
1658 * 2 | 255
1659 *
1660 * The difference is always > 2. There is a need to swap as the
1661 * oldest sample is not in the lowest position.
1662 *
1663 * To summarize, we have to swap when
1664 * - The higher field has new data and the lower field does not.
1665 * - If both fields have new data, then the difference of sub-meas index
1666 * between the higher field and the lower field creates the
1667 * following condition for swapping.
1668 * - (diff > -3) && (diff < 0), combination of cases 1, 2, and 3.
1669 * - diff > 2, case 4.
1670 *
1671 * Here the limits of -3 and 2 derive from the fact that there are 3 fields.
1672 * These values decrease or increase respectively if the number of fields increases.
1673 */
1674}
1675
1676/** \fn static void swap_fields(uint8_t index1, uint8_t index2, struct bme68x_data *field[])
1677 * \brief Swap two entries in the sensor data pointer array.
1678 *
1679 * This helper is used to reorder bme68x_data pointers when the measured
1680 * fields need to be sorted by timestamp or measurement order.
1681 *
1682 * \param[in] index1 Index of the first field pointer.
1683 * \param[in] index2 Index of the second field pointer.
1684 * \param[in,out] field Array of pointers to bme68x_data objects.
1685 */
1686static void swap_fields(uint8_t index1, uint8_t index2, struct bme68x_data *field[]) {
1687 struct bme68x_data *temp;
1688
1689 temp = field[index1];
1690 field[index1] = field[index2];
1691 field[index2] = temp;
1692}
1693
1694/* This Function is to analyze the sensor data */
1695static int8_t analyze_sensor_data(const struct bme68x_data *data, uint8_t n_meas) {
1696 int8_t rslt = BME68X_OK;
1697 uint8_t self_test_failed = 0, i;
1698 uint32_t cent_res = 0;
1699
1700 if ((data[0].temperature < BME68X_MIN_TEMPERATURE) || (data[0].temperature > BME68X_MAX_TEMPERATURE)) {
1701 self_test_failed++;
1702 }
1703
1704 if ((data[0].pressure < BME68X_MIN_PRESSURE) || (data[0].pressure > BME68X_MAX_PRESSURE)) {
1705 self_test_failed++;
1706 }
1707
1708 if ((data[0].humidity < BME68X_MIN_HUMIDITY) || (data[0].humidity > BME68X_MAX_HUMIDITY)) {
1709 self_test_failed++;
1710 }
1711
1712 for (i = 0; i < n_meas; i++) /* Every gas measurement should be valid */
1713 {
1714 if (!(data[i].status & BME68X_GASM_VALID_MSK)) {
1715 self_test_failed++;
1716 }
1717 }
1718
1719 if (n_meas >= 6) {
1720 cent_res = (uint32_t)((5 * (data[3].gas_resistance + data[5].gas_resistance)) / (2 * data[4].gas_resistance));
1721 }
1722
1723 if (cent_res < 6) {
1724 self_test_failed++;
1725 }
1726
1727 if (self_test_failed) {
1728 rslt = BME68X_E_SELF_TEST;
1729 }
1730
1731 return rslt;
1732}
1733
1734/** \fn static int8_t get_calib_data(struct bme68x_dev *dev)
1735 * \brief Read calibration coefficients from the BME68x sensor registers.
1736 *
1737 * The calibration data is stored across multiple coefficient blocks in the
1738 * device register map. This function reads the blocks sequentially, stores the
1739 * raw bytes into coeff_array, and converts them into the signed/unsigned
1740 * calibration parameters required for temperature, pressure, humidity, and
1741 * gas compensation calculations.
1742 *
1743 * \param dev Pointer to the BME68X device structure containing the interface and calibration storage.
1744 *
1745 * \return BME68X_OK on success, otherwise an error code from the BME68X API.
1746 */
1747static int8_t get_calib_data(struct bme68x_dev *dev) {
1748 int8_t rslt;
1749 uint8_t coeff_array[BME68X_LEN_COEFF_ALL];
1750
1751 /* Read all calibration coefficient blocks from the sensor registers. */
1752 rslt = bme68x_get_regs(BME68X_REG_COEFF1, coeff_array, BME68X_LEN_COEFF1, dev);
1753 if (rslt == BME68X_OK) {
1754 rslt = bme68x_get_regs(BME68X_REG_COEFF2, &coeff_array[BME68X_LEN_COEFF1], BME68X_LEN_COEFF2, dev);
1755 }
1756
1757 if (rslt == BME68X_OK) {
1758 rslt = bme68x_get_regs(BME68X_REG_COEFF3, &coeff_array[BME68X_LEN_COEFF1 + BME68X_LEN_COEFF2], BME68X_LEN_COEFF3, dev);
1759 }
1760
1761 if (rslt == BME68X_OK) {
1762 /* Temperature related coefficients */
1763 dev->calib.par_t1 = (uint16_t)(BME68X_CONCAT_BYTES(coeff_array[BME68X_IDX_T1_MSB], coeff_array[BME68X_IDX_T1_LSB]));
1764 dev->calib.par_t2 = (int16_t)(BME68X_CONCAT_BYTES(coeff_array[BME68X_IDX_T2_MSB], coeff_array[BME68X_IDX_T2_LSB]));
1765 dev->calib.par_t3 = (int8_t)(coeff_array[BME68X_IDX_T3]);
1766
1767 /* Pressure related coefficients */
1768 dev->calib.par_p1 = (uint16_t)(BME68X_CONCAT_BYTES(coeff_array[BME68X_IDX_P1_MSB], coeff_array[BME68X_IDX_P1_LSB]));
1769 dev->calib.par_p2 = (int16_t)(BME68X_CONCAT_BYTES(coeff_array[BME68X_IDX_P2_MSB], coeff_array[BME68X_IDX_P2_LSB]));
1770 dev->calib.par_p3 = (int8_t)coeff_array[BME68X_IDX_P3];
1771 dev->calib.par_p4 = (int16_t)(BME68X_CONCAT_BYTES(coeff_array[BME68X_IDX_P4_MSB], coeff_array[BME68X_IDX_P4_LSB]));
1772 dev->calib.par_p5 = (int16_t)(BME68X_CONCAT_BYTES(coeff_array[BME68X_IDX_P5_MSB], coeff_array[BME68X_IDX_P5_LSB]));
1773 dev->calib.par_p6 = (int8_t)(coeff_array[BME68X_IDX_P6]);
1774 dev->calib.par_p7 = (int8_t)(coeff_array[BME68X_IDX_P7]);
1775 dev->calib.par_p8 = (int16_t)(BME68X_CONCAT_BYTES(coeff_array[BME68X_IDX_P8_MSB], coeff_array[BME68X_IDX_P8_LSB]));
1776 dev->calib.par_p9 = (int16_t)(BME68X_CONCAT_BYTES(coeff_array[BME68X_IDX_P9_MSB], coeff_array[BME68X_IDX_P9_LSB]));
1777 dev->calib.par_p10 = (uint8_t)(coeff_array[BME68X_IDX_P10]);
1778
1779 /* Humidity related coefficients */
1780 dev->calib.par_h1 =
1781 (uint16_t)(((uint16_t)coeff_array[BME68X_IDX_H1_MSB] << 4) | (coeff_array[BME68X_IDX_H1_LSB] & BME68X_BIT_H1_DATA_MSK));
1782 dev->calib.par_h2 = (uint16_t)(((uint16_t)coeff_array[BME68X_IDX_H2_MSB] << 4) | ((coeff_array[BME68X_IDX_H2_LSB]) >> 4));
1783 dev->calib.par_h3 = (int8_t)coeff_array[BME68X_IDX_H3];
1784 dev->calib.par_h4 = (int8_t)coeff_array[BME68X_IDX_H4];
1785 dev->calib.par_h5 = (int8_t)coeff_array[BME68X_IDX_H5];
1786 dev->calib.par_h6 = (uint8_t)coeff_array[BME68X_IDX_H6];
1787 dev->calib.par_h7 = (int8_t)coeff_array[BME68X_IDX_H7];
1788
1789 /* Gas heater related coefficients */
1790 dev->calib.par_gh1 = (int8_t)coeff_array[BME68X_IDX_GH1];
1791 dev->calib.par_gh2 = (int16_t)(BME68X_CONCAT_BYTES(coeff_array[BME68X_IDX_GH2_MSB], coeff_array[BME68X_IDX_GH2_LSB]));
1792 dev->calib.par_gh3 = (int8_t)coeff_array[BME68X_IDX_GH3];
1793
1794 /* Other coefficients */
1795 dev->calib.res_heat_range = ((coeff_array[BME68X_IDX_RES_HEAT_RANGE] & BME68X_RHRANGE_MSK) / 16);
1796 dev->calib.res_heat_val = (int8_t)coeff_array[BME68X_IDX_RES_HEAT_VAL];
1797 dev->calib.range_sw_err = ((int8_t)(coeff_array[BME68X_IDX_RANGE_SW_ERR] & BME68X_RSERROR_MSK)) / 16;
1798 }
1799
1800 return rslt;
1801}
1802
1803/** \fn static int8_t read_variant_id(struct bme68x_dev *dev)
1804 *
1805 * \brief Read the sensor variant ID from the device register and store it in
1806 * the device structure.
1807 *
1808 * \param[in,out] dev : Pointer to the device structure.
1809 *
1810 * \return BME68X_OK on success, otherwise a negative error code.
1811 */
1812static int8_t read_variant_id(struct bme68x_dev *dev) {
1813 int8_t rslt;
1814 uint8_t reg_data = 0;
1815
1816 /* Read variant ID information register */
1817 rslt = bme68x_get_regs(BME68X_REG_VARIANT_ID, &reg_data, 1, dev);
1818
1819 if (rslt == BME68X_OK) {
1820 dev->variant_id = reg_data;
1821 }
1822
1823 return rslt;
1824}
1825
1826/** \fn int initBME688(void)
1827 * \brief Initializes the BME688 sensor over SPI.
1828 * Performs interface initialization, device reset, and basic data verification.
1829 * \return 0 on success, or a negative error code.
1830 */
1831int initBME688(void) {
1832 int8_t rslt = BME68X_OK;
1833
1834#if 0
1835 /* Interface preference is updated as a parameter
1836 * For I2C : BME68X_I2C_INTF
1837 * For SPI : BME68X_SPI_INTF
1838 */
1839 rslt = bme68x_interface_init(&bme688_dev, BME68X_SPI_INTF);
1840 bme68x_check_rslt("bme68x_interface_init", rslt);
1841
1842/*for testing */
1843#if 0
1844 while (1)
1845 {
1846 // uint8_t i = 0;
1847 int rslt;
1848 uint8_t reg[BME68X_LEN_CONFIG];
1849 uint8_t reg_array[BME68X_LEN_CONFIG] = {0x71, 0x72, 0x73, 0x74, 0x75};
1850 uint8_t data_array[BME68X_LEN_CONFIG] = {0};
1851
1852
1853 rslt = bme68x_get_regs(BME68X_REG_CHIP_ID, &bme688_dev.chip_id, 1, &bme688_dev);
1854 rslt = bme68x_get_regs(reg_array[0], reg, BME68X_LEN_CONFIG, &bme688_dev);
1855 rslt = bme68x_set_regs(reg_array, data_array, BME68X_LEN_CONFIG, &bme688_dev);
1856 rslt = bme68x_get_regs(reg_array[0], reg, BME68X_LEN_CONFIG, &bme688_dev);
1857 // printf("dev_id: 0x%02X\r\n", bme688_dev.chip_id);
1858 k_sleep(K_MSEC(2));
1859 }
1860#endif
1861
1862 rslt = bme68x_init(&bme688_dev);
1863 bme68x_check_rslt("bme68x_init", rslt);
1865 bme68x_check_rslt("bme68x_selftest_check", rslt);
1866
1867 if (rslt == BME68X_OK)
1868 {
1869 myPrintkS("Self-test passed\r\n");
1870 }
1871
1872 if (rslt == BME68X_E_SELF_TEST)
1873 {
1874 myPrintkS("Self-test failed\n");
1875 }
1876
1877#else
1878
1879 /* Interface preference is updated as a parameter
1880 * For I2C : BME68X_I2C_INTF
1881 * For SPI : BME68X_SPI_INTF
1882 */
1883 rslt = bme68x_interface_init(&bme688_dev, BME68X_SPI_INTF);
1884 bme68x_check_rslt("bme68x_interface_init", rslt);
1885
1886 rslt = bme68x_init(&bme688_dev);
1887 bme68x_check_rslt("bme68x_init", rslt);
1888
1889 /*get data*/
1890 rslt = getBME688Data(2, FALSE);
1891 bme68x_check_rslt("getBME688Data", rslt);
1892
1893 if (rslt == BME68X_OK) {
1894 /* BME688 found */
1896 myPrintkI("BME688 Air Quality Present\r\n");
1897 } else {
1898 myPrintkI("BME688 Air Quality Not Present\r\n");
1899 }
1900
1901#endif
1902
1903 return rslt;
1904}
1905
1906/**
1907 * \fn int getBME688Data(int count, uint8_t quiet)
1908 *
1909 * \brief Triggers and retrieves measurement data from the BME688.
1910 *
1911 * \param count Number of samples to collect.
1912 * \param quiet If TRUE, suppresses debug print output.
1913 *
1914 * \return 0 on success, or a negative error code.
1915 */
1916int getBME688Data(int count, uint8_t quiet) {
1917 int8_t rslt = BME68X_OK;
1918 struct bme68x_conf conf;
1919 struct bme68x_heatr_conf heatr_conf;
1920 uint32_t del_period;
1921 // uint32_t time_ms = 0;
1922 uint8_t n_fields = 0;
1923 uint16_t sample_count = 0;
1924
1925 // myPrintk("count: %d\r\n", count);
1926 /* Check if rslt == BME68X_OK, report or handle if otherwise */
1927 if (rslt == BME68X_OK) {
1928 conf.filter = BME68X_FILTER_OFF;
1929 conf.odr = BME68X_ODR_NONE;
1930 conf.os_hum = BME68X_OS_16X;
1931 conf.os_pres = BME68X_OS_1X;
1932 conf.os_temp = BME68X_OS_2X;
1933 rslt = bme68x_set_conf(&conf, &bme688_dev);
1934 if (quiet == FALSE) {
1935 bme68x_check_rslt("bme68x_set_conf", rslt);
1936 }
1937 }
1938
1939 /* Check if rslt == BME68X_OK, report or handle if otherwise */
1940 if (rslt == BME68X_OK) {
1941 heatr_conf.enable = BME68X_ENABLE;
1942 heatr_conf.heatr_temp = 300;
1943 heatr_conf.heatr_dur = 100;
1944 rslt = bme68x_set_heatr_conf(BME68X_FORCED_MODE, &heatr_conf, &bme688_dev);
1945 if (quiet == FALSE) {
1946 bme68x_check_rslt("bme68x_set_heatr_conf", rslt);
1947 }
1948 }
1949
1950 if (quiet == FALSE) {
1951 // myPrintkI("Sample\tTimeStamp(ms)\tTemperature(deg C)\tPressure(Pa)\tHumidity(%%)\tGas resistance(ohm)\tStatus\n");
1952 myPrintkI("Sample\tTemperature(deg C)\tPressure(Pa)\tHumidity(%%)\tGas resistance(ohm)\tStatus\n");
1953 }
1954
1955 // while (sample_count <= SAMPLE_COUNT)
1956 while (sample_count < count) {
1957 // myPrintkW("smaple count: %d\r\n", sample_count);
1958 rslt = bme68x_set_op_mode(BME68X_FORCED_MODE, &bme688_dev);
1959 // bme68x_check_rslt("bme68x_set_op_mode", rslt);
1960 /* Calculate delay period in microseconds */
1961 del_period = bme68x_get_meas_dur(BME68X_FORCED_MODE, &conf, &bme688_dev) + (heatr_conf.heatr_dur * 1000);
1962 bme688_dev.delay_us(del_period, bme688_dev.intf_ptr);
1963 // myPrintkW("delay period: %d us\r\n", del_period);
1964
1965 // // time_ms = coines_get_millis();
1966 // time_ms = 10;
1967
1968 /* Check if rslt == BME68X_OK, report or handle if otherwise */
1969 rslt = bme68x_get_data(BME68X_FORCED_MODE, &BME688Data, &n_fields, &bme688_dev);
1970 // myPrintkW("n_fields: %d\r\n", n_fields);
1971
1972 if (n_fields) {
1973#ifdef BME68X_USE_FPU
1974 if (quiet == FALSE) {
1975 // myPrintkI("%u\t%lu,\t\t%.2f", sample_count, (long unsigned int)time_ms, BME688Data.temperature);
1976 myPrintkI("%u\t%.2f", sample_count, (double)BME688Data.temperature);
1977 myPrintkI(" \t\t\t%.2f", (double)BME688Data.pressure);
1978 myPrintkI(" \t%.2f", (double)BME688Data.humidity);
1979 myPrintkI(" \t\t%.2f", (double)BME688Data.gas_resistance);
1980 myPrintkI(" \t\t0x%02X\r\n", BME688Data.status);
1981 }
1982#else
1983 // printf("%u %lu, %d, %lu, %lu, %lu, 0x%x\n",
1984 // sample_count,
1985 // (long unsigned int)time_ms,
1986 // (data.temperature / 100),
1987 // (long unsigned int)data.pressure,
1988 // (long unsigned int)(data.humidity / 1000),
1989 // (long unsigned int)data.gas_resistance,
1990 // data.status);
1991#endif
1992 }
1993 sample_count++;
1994 // k_sleep(K_SECONDS(2));
1995 }
1996
1997 return rslt;
1998}
int8_t bme68x_set_regs(const uint8_t *reg_addr, const uint8_t *reg_data, uint32_t len, struct bme68x_dev *dev)
This API writes the given data to the register address of the sensor.
Definition bme68x.c:253
static int8_t read_variant_id(struct bme68x_dev *dev)
Read the sensor variant ID from the device register and store it in the device structure.
Definition bme68x.c:1812
int8_t bme68x_set_heatr_conf(uint8_t op_mode, const struct bme68x_heatr_conf *conf, struct bme68x_dev *dev)
Definition bme68x.c:659
uint32_t bme68x_get_meas_dur(const uint8_t op_mode, struct bme68x_conf *conf, struct bme68x_dev *dev)
Definition bme68x.c:547
int8_t bme68x_set_op_mode(const uint8_t op_mode, struct bme68x_dev *dev)
Definition bme68x.c:496
static uint32_t calc_gas_resistance_high(uint16_t gas_res_adc, uint8_t gas_range)
This internal API is used to calculate the gas resistance value in float.
Definition bme68x.c:986
int initBME688(void)
Initializes the BME688 sensor over SPI. Performs interface initialization, device reset,...
Definition bme68x.c:1831
int8_t bme68x_get_op_mode(uint8_t *op_mode, struct bme68x_dev *dev)
Definition bme68x.c:528
static int16_t calc_temperature(uint32_t temp_adc, struct bme68x_dev *dev)
This internal API is used to calculate the temperature value.
Definition bme68x.c:833
static int8_t get_calib_data(struct bme68x_dev *dev)
Read calibration coefficients from the BME68x sensor registers.
Definition bme68x.c:1747
static void sort_sensor_data(uint8_t low_index, uint8_t high_index, struct bme68x_data *field[])
Definition bme68x.c:1559
static uint8_t calc_res_heat(uint16_t temp, const struct bme68x_dev *dev)
Definition bme68x.c:1002
int8_t bme68x_get_heatr_conf(const struct bme68x_heatr_conf *conf, struct bme68x_dev *dev)
This API is used to get the gas configuration of the sensor.
Definition bme68x.c:703
static uint8_t calc_gas_wait(uint16_t dur)
convert duration in milliseconds to encoded register value
Definition bme68x.c:1220
static int8_t set_mem_page(uint8_t reg_addr, struct bme68x_dev *dev)
Definition bme68x.c:1374
static int8_t boundary_check(uint8_t *value, uint8_t max, struct bme68x_dev *dev)
Definition bme68x.c:1429
static uint8_t calc_heatr_dur_shared(uint16_t dur)
Definition bme68x.c:1538
int8_t bme68x_init(struct bme68x_dev *dev)
Initializes the BME68X sensor.
Definition bme68x.c:215
static void swap_fields(uint8_t index1, uint8_t index2, struct bme68x_data *field[])
Swap two entries in the sensor data pointer array.
Definition bme68x.c:1686
int8_t bme68x_get_conf(struct bme68x_conf *conf, struct bme68x_dev *dev)
Definition bme68x.c:468
int8_t bme68x_soft_reset(struct bme68x_dev *dev)
Triggers a software reset on the BME68X sensor.
Definition bme68x.c:353
int8_t bme68x_get_data(uint8_t op_mode, struct bme68x_data *data, uint8_t *n_data, struct bme68x_dev *dev)
Definition bme68x.c:591
int getBME688Data(int count, uint8_t quiet)
Triggers and retrieves measurement data from the BME688.
Definition bme68x.c:1916
static uint32_t calc_humidity(uint16_t hum_adc, const struct bme68x_dev *dev)
Calculate relative humidity (in percent, float) from raw ADC value.
Definition bme68x.c:908
static int8_t read_field_data(uint8_t index, struct bme68x_data *data, struct bme68x_dev *dev)
Definition bme68x.c:1239
static uint32_t calc_pressure(uint32_t pres_adc, const struct bme68x_dev *dev)
This internal API is used to calculate the pressure value.
Definition bme68x.c:860
int8_t bme68x_selftest_check(const struct bme68x_dev *dev)
Definition bme68x.c:735
static int8_t set_conf(const struct bme68x_heatr_conf *conf, uint8_t op_mode, uint8_t *nb_conv, struct bme68x_dev *dev)
Definition bme68x.c:1460
static int8_t get_mem_page(struct bme68x_dev *dev)
Definition bme68x.c:1410
static int8_t analyze_sensor_data(const struct bme68x_data *data, uint8_t n_meas)
Function to analyze the sensor data.
Definition bme68x.c:1695
static int8_t read_all_field_data(struct bme68x_data *const data[], struct bme68x_dev *dev)
Definition bme68x.c:1311
static int8_t null_ptr_check(const struct bme68x_dev *dev)
Definition bme68x.c:1448
int8_t bme68x_set_conf(struct bme68x_conf *conf, struct bme68x_dev *dev)
Sets the oversampling, filter and ODR configurations of the sensor.
Definition bme68x.c:398
static uint32_t calc_gas_resistance_low(uint16_t gas_res_adc, uint8_t gas_range, const struct bme68x_dev *dev)
This internal API is used to calculate the gas resistance low value in float.
Definition bme68x.c:953
int8_t bme68x_get_regs(uint8_t reg_addr, uint8_t *reg_data, uint32_t len, struct bme68x_dev *dev)
This API reads the data from the given register address of sensor.
Definition bme68x.c:318
BME68X Sensor API header file.
int8_t bme68x_interface_init(struct bme68x_dev *bme, uint8_t intf)
Function to select the interface between SPI and I2C.
Definition common.c:401
void bme68x_check_rslt(const char api_name[], int8_t rslt)
Log BME68X API result and error details.
Definition common.c:361
uint32_t sensorTypePresentAll
Definition globals.c:30
struct bme68x_dev bme688_dev
BME688 device.
Definition globals.c:40
struct bme68x_data BME688Data
Definition globals.c:44
common struct, enum, externs, prototypes
int myPrintkS(char *restrict fmt,...)
prints a status message to the UART
Definition main.c:2419
int myPrintkI(char *restrict fmt,...)
prints an information message to the UART
Definition main.c:2386
#define FALSE
Definition jfet_common.h:41
@ SENSORTYPEBME688
Definition jfet_common.h:71