Hi Greg, We have experimentally found out that this change helps with somewhat quirky CO2/VOC-sensor.
Best Regards, Juha Niskanen
From f09acf45e1c94e454e3379a165aebe601e39b22d Mon Sep 17 00:00:00 2001 From: Juha Niskanen <juha.niska...@haltian.com> Date: Thu, 5 Mar 2020 09:55:57 +0200 Subject: [PATCH] drivers/sensors/sgp30.c: reset I2C in case init msg takes too long --- drivers/sensors/sgp30.c | 95 ++++++++++++++++++++++++++++++++++++++++--- include/nuttx/sensors/sgp30.h | 5 +++ 2 files changed, 94 insertions(+), 6 deletions(-) diff --git a/drivers/sensors/sgp30.c b/drivers/sensors/sgp30.c index ad0f515..5a8ce07 100644 --- a/drivers/sensors/sgp30.c +++ b/drivers/sensors/sgp30.c @@ -72,6 +72,8 @@ #endif #define SGP30_I2C_RETRIES 3 +#define SGP30_INIT_RETRIES 5 +#define SGP30_INIT_LIMIT_MS 10 /**************************************************************************** * Private @@ -375,6 +377,29 @@ static uint8_t sgp30_crc_word(uint16_t word) } /**************************************************************************** + * Name: sgp30_soft_reset + ****************************************************************************/ + +static int sgp30_soft_reset(FAR struct sgp30_dev_s *priv) +{ + struct i2c_msg_s msg[1]; + uint8_t buf[1]; + int ret = 0; + + buf[0] = CONFIG_SGP30_RESET_SECOND_BYTE; + + msg[0].frequency = CONFIG_SGP30_I2C_FREQUENCY; + msg[0].addr = CONFIG_SGP30_RESET_ADDR; + msg[0].flags = 0; + msg[0].buffer = buf; + msg[0].length = 1; + + ret = sgp30_do_transfer(priv->i2c, msg, 1); + + return (ret >= 0) ? OK : ret; +} + +/**************************************************************************** * Name: sgp30_set_command_param ****************************************************************************/ @@ -424,16 +449,39 @@ static int sgp30_check_data_crc(FAR const struct sgp30_word_s *words, * ****************************************************************************/ -static bool has_time_passed(struct timespec curr, - struct timespec start, +static bool has_time_passed(FAR struct timespec *curr, + FAR struct timespec *start, unsigned int secs_since_start) { - if ((long)((start.tv_sec + secs_since_start) - curr.tv_sec) == 0) + if ((long)((start->tv_sec + secs_since_start) - curr->tv_sec) == 0) + { + return start->tv_nsec <= curr->tv_nsec; + } + + return (long)((start->tv_sec + secs_since_start) - curr->tv_sec) <= 0; +} + +/**************************************************************************** + * Name: time_has_passed_ms + * + * Description: + * Return true if curr >= start + msecs_since_start + * + ****************************************************************************/ + +static bool time_has_passed_ms(FAR struct timespec *curr, + FAR struct timespec *start, + unsigned int msecs_since_start) +{ + uint32_t start_msec = start->tv_nsec / (1000 * 1000); + uint32_t curr_msec = curr->tv_nsec / (1000 * 1000); + + if (start->tv_sec < curr->tv_sec) { - return start.tv_nsec <= curr.tv_nsec; + curr_msec += 1000 * ((long)(curr->tv_sec - start->tv_sec)); } - return (long)((start.tv_sec + secs_since_start) - curr.tv_sec) <= 0; + return (start_msec + msecs_since_start) <= curr_msec; } /**************************************************************************** @@ -536,6 +584,7 @@ static int sgp30_open(FAR struct file *filep) sgp30_read_cmd(priv, SGP30_CMD_GET_FEATURE_SET_VERSION, buf, 1) >= 0 && sgp30_check_data_crc(buf, 1) >= 0) { + struct timespec start, curr; sgp30_dbg("serial id: %04x-%04x-%04x\n", sgp30_data_word_to_uint16(serial + 0), sgp30_data_word_to_uint16(serial + 1), @@ -546,12 +595,46 @@ static int sgp30_open(FAR struct file *filep) add_sensor_randomness((buf[0].crc << 24) ^ (serial[0].crc << 16) ^ (serial[1].crc << 8) ^ (serial[2].crc << 0)); + clock_gettime(CLOCK_REALTIME, &start); ret = sgp30_write_cmd(priv, SGP30_CMD_INIT_AIR_QUALITY, NULL, 0); if (ret < 0) { sgp30_dbg("sgp30_write_cmd(SGP30_CMD_INIT_AIR_QUALITY) failed, %d\n", ret); } + else + { + uint32_t repeat = SGP30_INIT_RETRIES; + clock_gettime(CLOCK_REALTIME, &curr); + sgp30_dbg("sgp30_write_cmd(SGP30_CMD_INIT_AIR_QUALITY)\n"); + while (repeat-- && time_has_passed_ms(&curr, &start, SGP30_INIT_LIMIT_MS)) + { + /* Infrequently the SGP30_CMD_INIT_AIR_QUALITY message delivery + * takes suspiciously long time (SGP30_INIT_LIMIT_MS or more) and + * in these cases the TVOC values will never reach the correct + * level (not even after 24 hours). + * If this delay is detected, the sensor is given a "General Call" + * soft reset as described in the SGP30 datasheet and initialization + * is tried again after CONFIG_SGP30_RESET_DELAY_US. + */ + + ret = sgp30_soft_reset(priv); + if (ret < 0) + { + sgp30_dbg("sgp30_soft_reset failed, %d\n", ret); + return ret; + } + nxsig_usleep(CONFIG_SGP30_RESET_DELAY_US); + + clock_gettime(CLOCK_REALTIME, &start); + ret = sgp30_write_cmd(priv, SGP30_CMD_INIT_AIR_QUALITY, NULL, 0); + clock_gettime(CLOCK_REALTIME, &curr); + if (ret < 0) + { + sgp30_dbg("sgp30_write_cmd(SGP30_CMD_INIT_AIR_QUALITY) failed, %d\n", ret); + } + } + } } else { @@ -646,7 +729,7 @@ static ssize_t sgp30_read(FAR struct file *filep, FAR char *buffer, clock_gettime(CLOCK_REALTIME, &ts); - while (!has_time_passed(ts, priv->last_update, 1)) + while (!has_time_passed(&ts, &priv->last_update, 1)) { if (filep->f_oflags & O_NONBLOCK) { diff --git a/include/nuttx/sensors/sgp30.h b/include/nuttx/sensors/sgp30.h index 36e7907..538a7bb 100644 --- a/include/nuttx/sensors/sgp30.h +++ b/include/nuttx/sensors/sgp30.h @@ -47,6 +47,11 @@ #define CONFIG_SGP30_ADDR 0x58 +#define CONFIG_SGP30_RESET_ADDR 0x00 +#define CONFIG_SGP30_RESET_SECOND_BYTE 0x06 + +#define CONFIG_SGP30_RESET_DELAY_US 300000 + /**************************************************************************** * Public Types ****************************************************************************/ -- 2.7.4