1
0
mirror of https://github.com/RIOT-OS/RIOT.git synced 2024-12-29 04:50:03 +01:00

drivers: add SHT3X humidity/temperature sensor

This commit is contained in:
Schorcht 2018-11-21 08:48:20 +01:00
parent 99e75948ef
commit 9d59e27d41
8 changed files with 1018 additions and 0 deletions

View File

@ -378,6 +378,11 @@ ifneq (,$(filter sht1%,$(USEMODULE)))
USEMODULE += xtimer
endif
ifneq (,$(filter sht3x,$(USEMODULE)))
USEMODULE += xtimer
FEATURES_REQUIRED += periph_i2c
endif
ifneq (,$(filter si114%,$(USEMODULE)))
USEMODULE += xtimer
FEATURES_REQUIRED += periph_i2c

View File

@ -210,6 +210,10 @@ ifneq (,$(filter sdcard_spi,$(USEMODULE)))
USEMODULE_INCLUDES += $(RIOTBASE)/drivers/sdcard_spi/include
endif
ifneq (,$(filter sht3x,$(USEMODULE)))
USEMODULE_INCLUDES += $(RIOTBASE)/drivers/sht3x/include
endif
ifneq (,$(filter si114x,$(USEMODULE)))
USEMODULE_INCLUDES += $(RIOTBASE)/drivers/si114x/include
endif

170
drivers/include/sht3x.h Normal file
View File

@ -0,0 +1,170 @@
/*
* Copyright (C) 2018 Gunar Schorcht
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/
/**
* @ingroup drivers_sht3x
* @brief Device Driver for Sensirion SHT30/SHT31/SHT35 Humidity and Temperature Sensors
* @author Gunar Schorcht <gunar@schorcht.net>
* @file
* @{
*/
#ifndef SHT3X_H
#define SHT3X_H
#include <stdint.h>
#include <stdbool.h>
#include "periph/i2c.h"
#ifdef __cplusplus
extern "C" {
#endif
/** possible I2C slave addresses */
#define SHT3X_I2C_ADDR_1 (0x44) /**< ADDR pin connected to GND/VSS */
#define SHT3X_I2C_ADDR_2 (0x45) /**< ADDR pin connected to VDD */
/**
* @brief Driver error codes (returned as negative values)
*/
typedef enum {
SHT3X_OK, /**< no error */
SHT3X_ERROR_I2C, /**< I2C communication failure */
SHT3X_ERROR_CRC, /**< CRC check failed*/
SHT3X_ERROR_STATUS, /**< sensor has wrong status */
SHT3X_ERROR_MEASURE_CMD_INV, /**< measurement command not executed */
} sht3x_error_codes;
/**
* @brief SHT3x measurement modes
*/
typedef enum {
SHT3X_SINGLE_SHOT = 0, /**< single shot measurement */
SHT3X_PERIODIC_0_5_MPS, /**< periodic with 0.5 measurements per second (mps) */
SHT3X_PERIODIC_1_MPS, /**< periodic with 1 measurements per second (mps) */
SHT3X_PERIODIC_2_MPS, /**< periodic with 2 measurements per second (mps) */
SHT3X_PERIODIC_4_MPS, /**< periodic with 4 measurements per second (mps) */
SHT3X_PERIODIC_10_MPS /**< periodic with 10 measurements per second (mps) */
} sht3x_mode_t;
/**
* @brief SHT3x repeatability levels
*
* The stated repeatability is 3 times the standard deviation of multiple
* consecutive measurements at the stated repeatability and at constant
* ambient conditions. It is a measure for the noise on the physical
* sensor output. Different measurement modes allow for high/medium/low
* repeatability. [Datasheet SHT3x-DIS]
*
* The configured repeatability level determines the time required for
* a measurement:
*
* Reapeatability | T typ. | RH typ. | Maximum Measurement Duration
* :---------------|:-------:|:-------:|:---------------------------:
* high | 0.04 | 0.08 | 15.5 ms
* medium | 0.08 | 0.15 | 6.5 ms
* low | 0.15 | 0.21 | 4.5 ms
*/
typedef enum {
SHT3X_HIGH = 0, /**< high repeatability */
SHT3X_MEDIUM, /**< medium repeatability */
SHT3X_LOW /**< low repeatability */
} sht3x_repeat_t;
/**
* @brief SHT3x device initialization parameters
*/
typedef struct {
i2c_t i2c_dev; /**< I2C device */
uint8_t i2c_addr; /**< I2C address */
sht3x_mode_t mode; /**< measurement mode used */
sht3x_repeat_t repeat; /**< repeatability level used */
} sht3x_params_t;
/**
* @brief SHT3x sensor device data structure
*/
typedef struct {
i2c_t i2c_dev; /**< I2C device */
uint8_t i2c_addr; /**< I2C address */
sht3x_mode_t mode; /**< measurement mode used */
sht3x_repeat_t repeat; /**< repeatability level used */
bool meas_started; /**< indicates whether measurement is started */
uint32_t meas_start_time; /**< start time of current measurement in us */
uint32_t meas_duration; /**< time in us until the results of the
current measurement become available */
} sht3x_dev_t;
/**
* @brief Initialize the SHT3x sensor device
*
* This function initializes the sensor device.
*
* If the sensor device is configured in any periodic measurement mode, it
* immediately starts the periodic measurement with configured measurement
* period and configured repeatability.
*
* In single-shot mode, the sensor device goes into the sleep mode to save
* power. The single measurement is started when ::sht3x_read is called.
*
* @param[in] dev Device descriptor of SHT3x device to be initialized
* @param[in] params SHT3x initialization parameters
*
* @return 0 on success or negative error code, see #sht3x_error_codes
*/
int sht3x_init (sht3x_dev_t *dev, const sht3x_params_t *params);
/**
* @brief Read SHT3x measurment results (temperature and humidity)
*
* The function returns the results of one measurement once they are available.
*
* If the single-shot measurement mode is configured, the function
*
* - starts the measurement according to the configured repeatability
* - waits for the sensor to complete the measurement depending on the
* configured repeatability, and
* - returns the measurement results.
*
* @note Since in single-shot measurement mode the measurement is only started
* when this function is called, it delays the calling task up to 16 ms,
* depending on the configured repeatability, in order to wait for the results
* of the measurement.
*
* In the periodic measurement modes, the function returns the results of the
* last periodic measurement, if already available.
*
* @note The ::sht3x_read function should be called less frequently than the
* periodic measurements are performed. Otherwise, the function implicitly
* delays the calling task until the results of the next measurement cycle
* become available to avoid data underrun and sensor blocking.
*
* For either \p temp or \p hum also ```NULL``` can be passed, if only one
* value is of interest.
*
* @param[in] dev Device descriptor of SHT3x device to read from
* @param[out] temp Temperature in hundredths of a degree Celsius
* @param[out] hum Relative Humidity in hundredths of a percent
*
* @return 0 on success or negative error code, see #sht3x_error_codes
*/
int sht3x_read (sht3x_dev_t* dev, int16_t* temp, int16_t* hum);
#ifdef __cplusplus
}
#endif
#endif /* SHT3X_H */
/** @} */

1
drivers/sht3x/Makefile Normal file
View File

@ -0,0 +1 @@
include $(RIOTBASE)/Makefile.base

196
drivers/sht3x/doc.txt Normal file
View File

@ -0,0 +1,196 @@
/*
* Copyright (C) 2018 Gunar Schorcht
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/
/**
@defgroup drivers_sht3x SHT3x Humidity and Temperature Sensor Series
@ingroup drivers_sensors
@ingroup drivers_saul
@brief Device Driver for Sensirion SHT30/SHT31/SHT35 Humidity and Temperature Sensors
# Driver for SHT3x Digital Temperature and Humidity Sensors
The driver is for the usage with [RIOT-OS](https://github.com/RIOT-OS/RIOT). It supports the Sensirion SHT30, SHT31, and SHT35 digital temperature and humidity sensors.
## About the sensors
SHT3x are a digital temperature and humidity sensors that use an I2C interface with up to 1 MHz communication speed. They can operate with **three levels of repeatability** (#sht3x_repeat_t)
- low,
- medium, and
- high,
and in two different modes (#sht3x_mode_t)
- the **single shot data acquisition mode** (or short **single shot mode**), and
- the **periodic data acquisition mode** (or short **periodic mode**).
In periodic mode, the SHT3x sensors support an **Alert Mode**, which allows to define temperature and humidity limits and indicate an alert by a dedicated ALERT pin or a status flag if any of the limits is reached reached.
@note The Alert Mode is not yet supported.
## Measurement process
Once a SHT3x sensor is initialized using the ::sht3x_init function, it can be used for measurements.
### Single shot mode
In **single shot mode**, a measurement command triggers the acquisition of **exactly one data pair**. Each data pair consists of temperature and humidity as 16-bit decimal values.
Due to the measurement duration of up to 16 ms, the measurement process is separated into steps to avoid blocking of the system during measurements:
1. Send a measurement command to the sensor to start exactly one single measurement.
2. Wait for the sensor to complete the measurement depending on the configured repeatability.
3. Fetch the results.
For convenience the high level function ::sht3x_read comprises all three steps above in only one function to perform a measurement.
@note Since in single-shot measurement mode the measurement is only started when ::sht3x_read function is called, it delays the calling task up to 16 ms, depending on the configured repeatability, in order to wait for the results of the measurement.
The advantage of this mode is that the sensor can switch between successive measurements into the sleep mode, which is more energy-efficient. This is particularly useful when the measurement rate is less than 1 measurement per second.
### Periodic mode
In this mode, one issued measurement command yields a stream of data pairs. Each data pair consists again of temperature and humidity as 16-bit decimal values. As soon as the measurement command has been sent to the sensor, it automatically performs measurements **periodically at a rate of 0.5, 1, 2, 4 or 10 measurements per second (mps)**. The data pairs can be fetched with the same rate or a lower rate.
As in *single shot mode*, the measurement process is separated into the following steps:
1. Send a measurement command to the sensor to start periodic measurements.
2. Wait for the sensor to complete the measurement depending on the configured repeatability and measurement period.
3. Fetch the results.
While periodic measurement is started (step 1) already in the ::sht3x_init function, the application has to call the ::sht3x_read function on a regular base to fetch the results from last periodic measurement. Possible periodic measurement modes are defined by #sht3x_mode_t.
@note The ::sht3x_read function should be called less frequently than the periodic measurements are performed. Otherwise, the function implicitly delays the calling task until the results of the next measurement cycle become available to avoid data underrun and sensor blocking.
## Measurement results
Once new measurement results are available, the ::sht3x_read function
- fetches raw sensor data in 16-decimal format
- checks the CRC checksums, and
- converts raw data to measured data, and
- returns the measured data.
These measured data are:
- the temperature given in hundredths of degree Celsius
- the relative humidity in hundredths of percents
For either the \p temp or \p hum parameter also ```NULL``` can be passed, if only one value is of interest.
## Error Handling
All driver functions return 0 on success or one of a negative error code defined by #sht3x_error_codes.
## Repeatability
The SHT3x sensor supports **three levels of repeatability** (low, medium and high). The stated repeatability is 3 times the standard deviation of multiple consecutive measurements at the stated repeatability and at constant ambient conditions. It is a measure for the noise on the physical sensor output. Different measurement modes allow for high/medium/low repeatability. [Datasheet SHT3x-DIS](https://www.sensirion.com/fileadmin/user_upload/customers/sensirion/Dokumente/0_Datasheets/Humidity/Sensirion_Humidity_Sensors_SHT3x_Datasheet_digital.pdf)
The repeatability settings influences the measurement duration as well as the power consumption of the sensor. The measurement takes typically 2.5 ms with low repeatability, 4.5 ms with medium repeatability and 12.5 ms with high repeatability. That is, the measurement produces a noticeable delay in execution.
Reapeatability | T typ. | RH typ. | Maximum Measurement Duration
:---------------|:-------:|:-------:|:---------------------------:
high | 0.04 | 0.08 | 15.5 ms
medium | 0.08 | 0.15 | 6.5 ms
low | 0.15 | 0.21 | 4.5 ms
While the sensor measures at the lowest repeatability, the average current consumption is 800 μA. That is, the higher the repeatability level, the longer the measurement takes and the higher the power consumption. The sensor consumes only 0.2 μA in standby mode.
Possible repeatabilities are defined by #sht3x_repeat_t.
## Usage
Before using the SHT3x driver, the configuration has to be defined and the ::sht3x_init function needs to be called to initialize and configure the sensor device of type #sht3x_dev_t.
The structure of a configuration is defined in #sht3x_params_t and consists of the following parameters:
Parameter | Symbol | Default
:----------------|:---------------------|:------------------
I2C device | SHT3X_PARAM_I2C_DEV | I2C_DEV(0)
I2C address | SHT3X_PARAM_I2C_ADDR | SHT3X_I2C_ADDR_2
Measurement mode | SHT3X_PARAM_MODE | SHT3X_PERIODIC_2_MPS
Repeatability | SHT3X_PARAM_REPEAT | SHT3X_HIGH
The default configuration is defined ```sht3x_params.h``` and can be overridden by the application before including ```sht3x_params.h```.
Example:
```
#define SHT3X_PARAM_MODE (SHT3X_SINGLE_SHOT)
#define SHT3X_PARAM_REPEAT (SHT3X_MEDIUM)
...
#include "sht3x.h"
#include "sht3x_params.h"
...
```
The ::sht3x_init function has to be called for each SHT3x sensor to initialize the sensor and to check its availability as well as its error state.
```
sht3x_dev_t dev;
if ((res = sht3x_init(&dev, &sht3x_params[0])) != SHT3X_OK) {
... /* error handling */
}
```
Parameter ```sht3x_params``` is also defined by default in file ```sht3x_params.h``` and can be overridden by the application before including ```sht3x_params.h```.
Example:
```
...
#include "sht3x.h"
#include "sht3x_params.h"
...
#define SHT3X_PARAMS { .i2c_dev = I2C_DEV(0), \
.i2c_addr = SHT3X_I2C_ADDR_1, \
.mode = SHT3X_PERIODIC_2_MPS, \
.repeat = SHT3X_HIGH \
}, \
{ .i2c_dev = I2C_DEV(0), \
.i2c_addr = SHT3X_I2C_ADDR_2, \
.mode = SHT3X_SINGLE_SHOT, \
.repeat = SHT3X_LOW \
}, \
...
sht3x_dev_t dev1;
sht3x_dev_t dev2;
if ((res = sht3x_init(&dev1, &sht3x_params[0])) != SHT3X_OK) {
... /* error handling */
}
if ((res = sht3x_init(&dev2, &sht3x_params[1])) != SHT3X_OK) {
... /* error handling */
}
```
In this example the configuration for two devices is defined.
Once the sensor devices are initialized and configured, the ::sht3x_read function can be used to read measurement results from the sensor.
Example:
```
while (1) {
int16_t temp;
int16_t hum;
if ((res = sht3x_read(&dev, &temp, &hum)) == SHT3X_OK) {
printf("Temperature [°C]: %d.%d\n"
"Relative Humidity [%%]: %d.%d\n"
"+-------------------------------------+\n",
temp / 100, temp % 100,
hum / 100, hum % 100);
}
else {
printf("Could not read data from sensor, error %d\n", res);
}
xtimer_usleep(1000000);
}
```
*/

View File

@ -0,0 +1,79 @@
/*
* Copyright (C) 2018 Gunar Schorcht
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/
/**
* @ingroup drivers_sht3x
* @brief Default configuration for Sensirion SHT30/SHT31/SHT35 devices
* @author Gunar Schorcht <gunar@schorcht.net>
* @file
* @{
*/
#ifndef SHT3X_PARAMS_H
#define SHT3X_PARAMS_H
#include "board.h"
#include "sht3x.h"
#include "saul_reg.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @name SHT3x default configuration parameters
* @{
*/
#ifndef SHT3X_PARAM_I2C_DEV
#define SHT3X_PARAM_I2C_DEV (I2C_DEV(0))
#endif
#ifndef SHT3X_PARAM_I2C_ADDR
#define SHT3X_PARAM_I2C_ADDR (SHT3X_I2C_ADDR_2)
#endif
#ifndef SHT3X_PARAM_MODE
#define SHT3X_PARAM_MODE (SHT3X_PERIODIC_2_MPS)
#endif
#ifndef SHT3X_PARAM_REPEAT
#define SHT3X_PARAM_REPEAT (SHT3X_HIGH)
#endif
#ifndef SHT3X_PARAMS
#define SHT3X_PARAMS { .i2c_dev = SHT3X_PARAM_I2C_DEV, \
.i2c_addr = SHT3X_PARAM_I2C_ADDR, \
.mode = SHT3X_PARAM_MODE, \
.repeat = SHT3X_PARAM_REPEAT \
}
#endif
#ifndef SHT3X_SAUL_INFO
#define SHT3X_SAUL_INFO { .name = "sht3x1" }
#endif
/**@}*/
/**
* @brief SHT3x configuration
*/
static const sht3x_params_t sht3x_params[] =
{
SHT3X_PARAMS
};
/**
* @brief Additional meta information to keep in the SAUL registry
*/
static const saul_reg_info_t sht3x_saul_info[] =
{
SHT3X_SAUL_INFO
};
#ifdef __cplusplus
}
#endif
#endif /* SHT3X_PARAMS_H */
/** @} */

442
drivers/sht3x/sht3x.c Normal file
View File

@ -0,0 +1,442 @@
/*
* Copyright (C) 2018 Gunar Schorcht
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/
/**
* @ingroup drivers_sht3x
* @brief Device Driver for Sensirion SHT30/SHT31/SHT35 Humidity and Temperature Sensors
* @author Gunar Schorcht <gunar@schorcht.net>
* @file
*/
#define ENABLE_DEBUG (0)
#include "debug.h"
#include "log.h"
#include <errno.h>
#include <string.h>
#include "sht3x.h"
#include "xtimer.h"
#define ASSERT_PARAM(cond) \
do { \
if (!(cond)) { \
DEBUG("[sht3x] %s: %s\n", \
__func__, "parameter condition (" #cond ") not fulfilled"); \
assert(cond); \
} \
} while (0)
#define DEBUG_DEV(f, d, ...) \
DEBUG("[sht3x] %s dev=%d addr=%02x: " f "\n", \
__func__, d->i2c_dev, d->i2c_addr, ## __VA_ARGS__)
#define ERROR_DEV(f, d, ...) \
LOG_ERROR("[sht3x] dev=%d addr=%x: " f "\n", \
d->i2c_dev, d->i2c_addr, ## __VA_ARGS__)
/* SHT3x common commands */
#define SHT3X_CMD_CLEAR_STATUS 0x3041 /* clear status command */
#define SHT3X_CMD_HEATER_OFF 0x3066 /* Heater disable command */
#define SHT3X_CMD_BREAK 0x3093 /* Break command */
#define SHT3X_CMD_RESET 0x30A2 /* reset command */
#define SHT3X_CMD_FETCH_DATA 0xE000 /* fetch data command */
#define SHT3X_CMD_STATUS 0xF32D /* get status command */
/* SHT3x status register flags */
#define SHT3X_STATUS_REG_MASK (0xbc13) /* valid status register bit mask */
#define SHT3X_STATUS_REG_CRC (1 << 0) /* write data checksum status */
#define SHT3X_STATUS_REG_CMD (1 << 1) /* last command execution status */
/* Raw date size */
#define SHT3X_RAW_DATA_SIZE 6
/* SHT3x measurement period times in us */
static const uint32_t SHT3X_MEASURE_PERIOD[] = {
0, /* [SINGLE_SHOT ] */
2000000, /* [PERIODIC_0_5] */
1000000, /* [PERIODIC_1 ] */
500000, /* [PERIODIC_2 ] */
250000, /* [PERIODIC_4 ] */
100000 /* [PERIODIC_10 ] */
};
/* SHT3x measurement command sequences */
static const uint16_t SHT3X_CMD_MEASURE[6][3] = {
{0x2400, 0x240b, 0x2416}, /* [SINGLE_SHOT ][H, M, L] without clock stretching */
{0x2032, 0x2024, 0x202f}, /* [PERIODIC_0_5][H, M, L] */
{0x2130, 0x2126, 0x212d}, /* [PERIODIC_1 ][H, M, L] */
{0x2236, 0x2220, 0x222b}, /* [PERIODIC_2 ][H, M, L] */
{0x2234, 0x2322, 0x2329}, /* [PERIODIC_4 ][H, M, L] */
{0x2737, 0x2721, 0x272a} /* [PERIODIC_10 ][H, M, L] */
};
/* maximum measurement durations dependent on repatability in ms */
#define SHT3X_MEAS_DURATION_REP_HIGH 16
#define SHT3X_MEAS_DURATION_REP_MEDIUM 7
#define SHT3X_MEAS_DURATION_REP_LOW 5
/* measurement durations in us */
const uint16_t SHT3X_MEAS_DURATION_US[3] = { SHT3X_MEAS_DURATION_REP_HIGH * 1000,
SHT3X_MEAS_DURATION_REP_MEDIUM * 1000,
SHT3X_MEAS_DURATION_REP_LOW * 1000 };
/* functions for internal use */
static int _get_raw_data(sht3x_dev_t* dev, uint8_t* raw_data);
static int _compute_values (uint8_t* raw_data, int16_t* temp, int16_t* hum);
/* sensor commands */
static int _start_measurement (sht3x_dev_t* dev);
static int _reset (sht3x_dev_t* dev);
static int _status (sht3x_dev_t* dev, uint16_t* status);
/* sensor access functions */
static int _send_command(sht3x_dev_t* dev, uint16_t cmd);
static int _read_data(sht3x_dev_t* dev, uint8_t *data, uint8_t len);
/* helper functions */
static uint8_t _crc8 (uint8_t data[], int len);
/* ------------------------------------------------ */
int sht3x_init (sht3x_dev_t *dev, const sht3x_params_t *params)
{
ASSERT_PARAM(dev != NULL);
ASSERT_PARAM(params != NULL);
int res = SHT3X_OK;
/* inititalize sensor data structure */
dev->i2c_dev = params->i2c_dev;
dev->i2c_addr = params->i2c_addr;
dev->mode = params->mode;
dev->repeat = params->repeat;
dev->meas_start_time = 0;
dev->meas_duration = 0;
dev->meas_started = false;
/* try to reset the sensor */
if ((res = _reset(dev)) != SHT3X_OK) {
return res;
}
DEBUG_DEV("sensor initialized", dev);
/* start periodic measurements */
if ((dev->mode != SHT3X_SINGLE_SHOT) &&
(res = _start_measurement (dev)) != SHT3X_OK){
return res;
}
return res;
}
int sht3x_read (sht3x_dev_t* dev, int16_t* temp, int16_t* hum)
{
ASSERT_PARAM(dev != NULL);
ASSERT_PARAM(temp != NULL || hum != NULL);
int res = SHT3X_OK;
uint8_t raw_data[SHT3X_RAW_DATA_SIZE];
/*
* fetches raw sensor data, implicitly starts the measurement if necessary,
* e.g., in single-shot measurement mode, and waits until sensor data are
* available
*/
if ((res = _get_raw_data (dev, raw_data)) != SHT3X_OK) {
return res;
}
return _compute_values (raw_data, temp, hum);
}
/* Functions for internal use only */
static int _start_measurement (sht3x_dev_t* dev)
{
/* start measurement according to selected mode */
if (_send_command(dev, SHT3X_CMD_MEASURE[dev->mode][dev->repeat]) != SHT3X_OK)
{
DEBUG_DEV("start measurement failed, "
"could not send SHT3X_CMD_MEASURE to sensor", dev);
return -SHT3X_ERROR_I2C;
}
/*
* in periodic modes, we check whether the command were processed, in
* single shot mode, reading results has to follow directly.
*/
if (dev->mode != SHT3X_SINGLE_SHOT) {
/* sensor needs up to 250 us to process the measurement command */
xtimer_usleep (1000);
uint16_t status;
int res;
/* read the status after start measurement command */
if ((res = _status(dev, &status)) != SHT3X_OK) {
return res;
}
if (status & SHT3X_STATUS_REG_CMD) {
DEBUG_DEV("start measurement failed, "
"SHT3X_CMD_MEASURE were not executed", dev);
return -SHT3X_ERROR_MEASURE_CMD_INV;
}
}
dev->meas_start_time = xtimer_now_usec();
dev->meas_duration = SHT3X_MEAS_DURATION_US[dev->repeat];
dev->meas_started = true;
return SHT3X_OK;
}
static int _get_raw_data(sht3x_dev_t* dev, uint8_t* raw_data)
{
int res = SHT3X_OK;
/* start the measurement if it has not started yet */
if (!dev->meas_started &&
(res = _start_measurement (dev)) != SHT3X_OK) {
return res;
}
/* determine the time elapsed since the start of current measurement cycle */
uint32_t elapsed = xtimer_now_usec() - dev->meas_start_time;
if (elapsed < dev->meas_duration) {
/* if necessary, wait until the measurement results become available */
xtimer_usleep(dev->meas_duration - elapsed);
}
/* send fetch command in any periodic mode (mode > 0) before read raw data */
if (dev->mode != SHT3X_SINGLE_SHOT &&
_send_command(dev, SHT3X_CMD_FETCH_DATA) != SHT3X_OK) {
DEBUG_DEV("could not send SHT3X_CMD_FETCH_DATA to sensor", dev);
return -SHT3X_ERROR_I2C;
}
/* read raw data */
if (_read_data(dev, raw_data, SHT3X_RAW_DATA_SIZE) != SHT3X_OK) {
DEBUG_DEV("could not read raw sensor data", dev);
return -SHT3X_ERROR_I2C;
}
/* in single shot mode upate dmeasurement started flag of the driver */
if (dev->mode == SHT3X_SINGLE_SHOT) {
dev->meas_started = false;
}
/* start next measurement cycle in periodic modes */
else {
dev->meas_start_time = xtimer_now_usec();
dev->meas_duration = SHT3X_MEASURE_PERIOD[dev->mode];
}
/* check temperature crc */
if (_crc8(raw_data,2) != raw_data[2]) {
DEBUG_DEV("CRC check for temperature data failed", dev);
return -SHT3X_ERROR_CRC;
}
/* check humidity crc */
if (_crc8(raw_data+3,2) != raw_data[5]) {
DEBUG_DEV("CRC check for humidity data failed", dev);
return -SHT3X_ERROR_CRC;
}
return SHT3X_OK;
}
static int _compute_values (uint8_t* raw_data, int16_t* temp, int16_t* hum)
{
if (temp) {
uint32_t _tmp = ((uint32_t)raw_data[0] << 8) + raw_data[1]; /* S_T */
_tmp *= 17500; /* S_T x 175 scaled to hundredths of degree */
_tmp >>= 16; /* S_T x 175 / 65535 (shift inaccuracy < resolution) */
_tmp -= 4500; /* S_T x 175 / 65535 - 45 */
*temp = _tmp;
}
if (hum) {
uint32_t _tmp = ((uint32_t)raw_data[3] << 8) + raw_data[4]; /* S_RH */
_tmp *= 10000; /* S_RH x 100 scaled to hundredths of degree */
_tmp >>= 16; /* S_RH x 100 / 65535 (shift inaccuracy < resolution) */
*hum = _tmp;
}
return SHT3X_OK;
}
static int _send_command(sht3x_dev_t* dev, uint16_t cmd)
{
ASSERT_PARAM (dev != NULL);
int res = SHT3X_OK;
uint8_t data[2] = { cmd >> 8, cmd & 0xff };
DEBUG_DEV("send command 0x%02x%02x", dev, data[0], data[1]);
if (i2c_acquire(dev->i2c_dev) != 0) {
DEBUG_DEV ("could not aquire I2C bus", dev);
return -SHT3X_ERROR_I2C;
}
res = i2c_write_bytes(dev->i2c_dev, dev->i2c_addr, (const void*)data, 2, 0);
i2c_release(dev->i2c_dev);
if (res != 0) {
DEBUG_DEV("could not send command 0x%02x%02x to sensor, reason=%d",
dev, data[0], data[1], res);
return -SHT3X_ERROR_I2C;
}
return SHT3X_OK;
}
static int _read_data(sht3x_dev_t* dev, uint8_t *data, uint8_t len)
{
int res = SHT3X_OK;
if (i2c_acquire(dev->i2c_dev) != 0) {
DEBUG_DEV ("could not aquire I2C bus", dev);
return -SHT3X_ERROR_I2C;
}
res = i2c_read_bytes(dev->i2c_dev, dev->i2c_addr, (void*)data, len, 0);
i2c_release(dev->i2c_dev);
if (res == 0) {
#if ENABLE_DEBUG
printf("[sht3x] %s bus=%d addr=%02x: read following bytes: ",
__func__, dev->i2c_dev, dev->i2c_addr);
for (int i=0; i < len; i++)
printf("%02x ", data[i]);
printf("\n");
#endif /* ENABLE_DEBUG */
}
else {
DEBUG_DEV("could not read %d bytes from sensor, reason %d",
dev, len, res);
return -SHT3X_ERROR_I2C;
}
return SHT3X_OK;
}
static int _reset (sht3x_dev_t* dev)
{
ASSERT_PARAM (dev != NULL);
DEBUG_DEV("", dev);
/*
* Sensor can only be soft resetted in idle mode. Therefore, we
* send a break and wait 1 ms. After that the sensor should be
* in idle mode. We don't check I2C errors at this moment.
*/
_send_command(dev, SHT3X_CMD_BREAK);
xtimer_usleep (1000);
/* send the soft-reset command */
if (_send_command(dev, SHT3X_CMD_RESET) != SHT3X_OK) {
DEBUG_DEV("reset failed, could not send SHT3X_CMD_RESET", dev);
return -SHT3X_ERROR_I2C;
}
/* wait for 2 ms, the time needed to restart (according to datasheet 0.5 ms) */
xtimer_usleep (2000);
/* send reset command */
if (_send_command(dev, SHT3X_CMD_CLEAR_STATUS) != SHT3X_OK) {
DEBUG_DEV("reset failed, could not send SHT3X_CMD_CLEAR_STATUS", dev);
return -SHT3X_ERROR_I2C;
}
/* sensor needs some time to process the command */
xtimer_usleep (500);
uint16_t status;
int res = SHT3X_OK;
/* read the status after reset */
if ((res = _status(dev, &status)) != SHT3X_OK) {
return res;
}
/* status register should be 0x0000 after a reset */
if (status != 0) {
DEBUG_DEV("reset failed, sensor status is status=%04x", dev, status);
return -SHT3X_ERROR_STATUS;
}
DEBUG_DEV("sensor status=%04x", dev, status);
return res;
}
static int _status (sht3x_dev_t* dev, uint16_t* status)
{
ASSERT_PARAM (dev != NULL);
ASSERT_PARAM (status != NULL);
DEBUG_DEV("", dev);
uint8_t data[3];
/* read sensor status */
if (_send_command(dev, SHT3X_CMD_STATUS) != SHT3X_OK) {
DEBUG_DEV("could not send SHT3X_CMD_STATUS to sensor", dev);
return -SHT3X_ERROR_I2C;
}
if (_read_data(dev, data, 3) != SHT3X_OK) {
DEBUG_DEV("could not read status data from sensor", dev);
return -SHT3X_ERROR_I2C;
}
/* check status crc */
if (_crc8(data,2) != data[2]) {
DEBUG_DEV("CRC check for status failed", dev);
return -SHT3X_ERROR_CRC;
}
*status = (data[0] << 8 | data[1]) & SHT3X_STATUS_REG_MASK;
DEBUG_DEV("status=%02x", dev, *status);
return SHT3X_OK;
}
static const uint8_t g_polynom = 0x31;
static uint8_t _crc8 (uint8_t data[], int len)
{
/* initialization value */
uint8_t crc = 0xff;
/* iterate over all bytes */
for (int i=0; i < len; i++)
{
crc ^= data[i];
for (int i = 0; i < 8; i++)
{
bool xor = crc & 0x80;
crc = crc << 1;
crc = xor ? crc ^ g_polynom : crc;
}
}
return crc;
}

121
drivers/sht3x/sht3x_saul.c Normal file
View File

@ -0,0 +1,121 @@
/*
* Copyright (C) 2018 Gunar Schorcht
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/
/**
* @ingroup drivers_sht3x
* @brief SAUL adaption for Sensirion SHT30/SHT31/SHT35 devices
* @author Gunar Schorcht <gunar@schorcht.net>
* @file
*/
#include <errno.h>
#include <stdio.h>
#include <string.h>
#include "phydat.h"
#include "saul.h"
#include "sht3x.h"
#include "sht3x_params.h"
#define SHT3X_NUM (sizeof(sht3x_params) / sizeof(sht3x_params[0]))
extern sht3x_dev_t sht3x_devs[SHT3X_NUM];
/**
* Humidity and temperature sensor values are fetched by separate saul
* functions. To avoid double waiting for the sensor and readout of the
* sensor values, we read both sensor values once, if necessary, and
* store them in local variables to provide them in the separate saul
* read functions.
*/
static bool _temp_valid[SHT3X_NUM] = { false };
static bool _hum_valid[SHT3X_NUM] = { false };
static int16_t _temp[SHT3X_NUM];
static int16_t _hum[SHT3X_NUM];
static unsigned _dev2index (const sht3x_dev_t *dev)
{
/*
* returns the index of the device in sht3x_devs[] or SHT3_NUM
* if not found
*/
for (unsigned i = 0; i < SHT3X_NUM; i++) {
if (dev == &sht3x_devs[i]) {
return i;
}
}
return SHT3X_NUM;
}
static int read(int dev)
{
/* read both sensor values */
unsigned res = sht3x_read(&sht3x_devs[dev], &_temp[dev], &_hum[dev]);
if (res != SHT3X_OK) {
return res;
}
/* mark both sensor values as valid */
_temp_valid[dev] = true;
_hum_valid[dev] = true;
return SHT3X_OK;
}
static int read_temp(const void *dev, phydat_t *data)
{
/* find the device index */
unsigned dev_index = _dev2index((const sht3x_dev_t *)dev);
if (dev_index == SHT3X_NUM) {
/* return with error if device index could not be found */
return -ECANCELED;
}
/* either local variable is valid or fetching it was successful */
if (_temp_valid[dev_index] || read(dev_index) == SHT3X_OK) {
/* mark local variable as invalid */
_temp_valid[dev_index] = false;
data->val[0] = _temp[dev_index];
data->unit = UNIT_TEMP_C;
data->scale = -2;
return 1;
}
return -ECANCELED;
}
static int read_hum(const void *dev, phydat_t *data)
{
/* find the device index */
unsigned dev_index = _dev2index((const sht3x_dev_t *)dev);
if (dev_index == SHT3X_NUM) {
/* return with error if device index could not be found */
return -ECANCELED;
}
/* either local variable is valid or fetching it was successful */
if (_hum_valid[dev_index] || read(dev_index) == SHT3X_OK) {
/* mark local variable as invalid */
_hum_valid[dev_index] = false;
data->val[0] = _hum[dev_index];
data->unit = UNIT_PERCENT;
data->scale = -2;
return 1;
}
return -ECANCELED;
}
const saul_driver_t sht3x_saul_driver_temperature = {
.read = read_temp,
.write = saul_notsup,
.type = SAUL_SENSE_TEMP
};
const saul_driver_t sht3x_saul_driver_humidity = {
.read = read_hum,
.write = saul_notsup,
.type = SAUL_SENSE_HUM
};