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

driver/scd30: Add driver for Sensirion SCD30

Created tests for Sensirion scd30 driver

Moved Makefile.dep and Makefile.include as per new spec
This commit is contained in:
nagrawal 2020-07-06 16:04:44 +02:00
parent 42eb044ec6
commit 900e4b61dc
13 changed files with 1002 additions and 0 deletions

174
drivers/include/scd30.h Normal file
View File

@ -0,0 +1,174 @@
/*
* Copyright (C) 2020 Puhang Ding
* 2020 Jan Schlichter
* 2020 Nishchay Agrawal
*
* 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_scd30 SCD30 CO2, temperature and humidity sensor
* @ingroup drivers_sensors
* @{
* @file
* @brief Device driver interface for the SCD30 sensor.
*
* @author Nishchay Agrawal <f2016088@pilani.bits-pilani.ac.in>
* @author Puhang Ding <czarsir@gmail.com>
* @author Jan Schlichter <schlichter@ibr.cs.tu-bs.de>
* @}
*/
#ifndef SCD30_H
#define SCD30_H
#include "periph/i2c.h"
#include "saul.h"
#include "xtimer.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief SCD30 Configuration parameter commands
*/
#define SCD30_VERSION 0xD100 /**< Get scd30 version */
#define SCD30_STATUS 0x0202 /**< Get data ready status from scd30
device */
#define SCD30_DATA 0x0300 /**< Get data from scd30 device */
#define SCD30_START 0x0010 /**< Start measuring data from scd30 device */
#define SCD30_STOP 0x0104 /**< Stop measuring data from scd30 device */
#define SCD30_SOFT_RESET 0xD304 /**< Soft reset scd30 device */
#define SCD30_INTERVAL 0x4600 /**< Set measurement interval
(2-1800 seconds) */
#define SCD30_ASC 0x5306 /**< De-Activate Automatic Self-Calibration */
#define SCD30_FRC 0x5204 /**< Forced Recalibration. 400-2000ppm */
#define SCD30_T_OFFSET 0x5403 /**< Set temperature Offset in 0.01 Celsius */
#define SCD30_A_OFFSET 0x5102 /**< Altitude Compensation in meters above
sea level */
/**
* @brief Status and error codes for return values
*/
enum {
SCD30_OK = 0,
SCD30_COM_FAILED = -1, /**< Communication with device failed. */
SCD30_INVALID_VALUE = -2, /**< Device doesn't exist. */
SCD30_CRC_ERROR = -3, /**< Invalid value or length. */
SCD30_NO_NEW_DATA = -4, /**< No new data. */
};
/**
* @brief Measurement from SCD30 sensor
*/
typedef struct {
float co2_concentration; /**< CO2 concentration in ppm */
float temperature; /**< Temperature measured in °C */
float relative_humidity; /**< Relative humidity measured in % */
} scd30_measurement_t;
/**
* @brief Device initialization parameters
*/
typedef struct {
i2c_t i2c_dev; /**< I2C device which is used */
uint8_t i2c_addr; /**< I2C address */
} scd30_params_t;
/**
* @brief Device descriptor for the SCD30 sensor
*/
typedef struct {
scd30_params_t params; /**< Device initialization parameters */
} scd30_t;
/**
* @brief Initialize SCD30
*
* @param dev scd30 device
* @param params scd30 device params
*
* @return SCD30_OK if device started
*/
int8_t scd30_init(scd30_t *dev, const scd30_params_t *params);
/**
* @brief Set a configuration parameter of device
*
* @param dev scd30 device
* @param param param to be set/operation to be performed
* param codes mentioned in scd30_internal
* @param val value to be set for that parameter
*
* @return SCD30_OK on success
*/
int8_t scd30_set_param(const scd30_t *dev, uint16_t param, uint16_t val);
/**
* @brief Get value set for a configuration parameter on the device
*
* @param dev scd30 device
* @param param param to be set/operation to be performed
* param codes mentioned in scd30_internal
* @param val Pointer to the value to be set for that parameter
*
* @return SCD30_OK on success
*/
int8_t scd30_get_param(scd30_t *dev, uint16_t param, uint16_t *val);
/**
* @brief read CO2 concentration, temperature and relative humidity once
*
* @param dev scd30 device
* @param result Values are stored in this struct
*
* @return SCD30_OK on success
*/
int8_t scd30_read_triggered(scd30_t *dev, scd30_measurement_t *result);
/**
* @brief read co2 concentration, temperature and relative humidity
* when continuous measurements are being taken
*
* @param dev scd30 device
* @param result struct to store result
*
* @return SCD30_OK on success
*/
uint8_t scd30_read_periodic(scd30_t *dev, scd30_measurement_t *result);
/**
* @brief Initializes Continuous Measurements
*
* @param dev scd30 device
* @param interval Interval at which new measurements have to be taken
* in seconds (between 2 and 1800 seconds, both inclusive)
* @param apc Average Pressure Compensation
* 0 to disable pressure compensation
* 700-1400 mBar for valid pressure compensation
*
* @return SCD30_OK if device started
*/
int scd30_start_periodic_measurement(scd30_t *dev, uint16_t *interval,
uint16_t *apc);
/**
* @brief Stop Continuous measurements
*
* @param dev scd30 dev device
*
* @return SCD30_OK if measurement stopped
*/
int8_t scd30_stop_measurements(const scd30_t *dev);
#ifdef __cplusplus
}
#endif
#endif /* SCD30_H */

View File

@ -0,0 +1,85 @@
/*
* Copyright (C) 2020 Puhang Ding
* 2020 Nishchay Agrawal
*
* 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 sys_auto_init_saul
* @{
*
* @file
* @brief Auto initialization of SCD30 driver.
*
* @author Puhang Ding <czarsir@gmail.com>
* @author Nishchay Agrawal <f2016088@pilani.bits-pilani.ac.in>
*
* @}
*/
#include "saul_reg.h"
#include "log.h"
#include "scd30.h"
#include "scd30_params.h"
#include "scd30_internal.h"
/**
* @brief Allocation of memory for device descriptors
*/
static scd30_t scd30_devs[SCD30_NUMOF];
/**
* @brief Reference the driver structs.
* @{
*/
extern const saul_driver_t scd30_co2_saul_driver;
extern const saul_driver_t scd30_temp_saul_driver;
extern const saul_driver_t scd30_hum_saul_driver;
/** @} */
/**
* @brief Memory for the SAUL registry entries
*/
#define SENSORS_NUMOF 3
static saul_reg_t saul_entries[SCD30_NUMOF * SENSORS_NUMOF];
void auto_init_scd30(void)
{
size_t sensor_no = 0;
for (size_t i = 0; i < SCD30_NUMOF; i++) {
int res = scd30_init(&scd30_devs[i], &scd30_params[i]);
if (res < 0) {
LOG_ERROR("[auto_init_saul] error initializing SCD30 #%u\n", i);
continue;
}
/*** CO2 concentration ***/
saul_entries[sensor_no].dev = &scd30_devs[i];
saul_entries[sensor_no].name = scd30_saul_info[i].name;
saul_entries[sensor_no].driver = &scd30_co2_saul_driver;
saul_reg_add(&saul_entries[sensor_no]);
sensor_no++;
/*** Temperature ***/
saul_entries[sensor_no].dev = &scd30_devs[i];
saul_entries[sensor_no].name = scd30_saul_info[i].name;
saul_entries[sensor_no].driver = &scd30_temp_saul_driver;
saul_reg_add(&saul_entries[sensor_no]);
sensor_no++;
/*** Relative Humidity ***/
saul_entries[sensor_no].dev = &scd30_devs[i];
saul_entries[sensor_no].name = scd30_saul_info[i].name;
saul_entries[sensor_no].driver = &scd30_hum_saul_driver;
saul_reg_add(&saul_entries[sensor_no]);
sensor_no++;
scd30_set_param(&scd30_devs[i], SCD30_START, SCD30_DEF_PRESSURE);
}
}

View File

@ -215,6 +215,10 @@ void saul_init_devs(void)
extern void auto_init_qmc5883l(void);
auto_init_qmc5883l();
}
if (IS_USED(MODULE_SCD30)) {
extern void auto_init_scd30(void);
auto_init_scd30();
}
if (IS_USED(MODULE_SHT2X)) {
extern void auto_init_sht2x(void);
auto_init_sht2x();

1
drivers/scd30/Makefile Normal file
View File

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

View File

@ -0,0 +1,5 @@
ifneq (,$(filter scd30,$(USEMODULE)))
FEATURES_REQUIRED += periph_i2c
USEMODULE += checksum
USEMODULE += xtimer
endif

View File

@ -0,0 +1,2 @@
USEMODULE_INCLUDES_scd30 := $(LAST_MAKEFILEDIR)/include
USEMODULE_INCLUDES += $(USEMODULE_INCLUDES_scd30)

View File

@ -0,0 +1,61 @@
/*
* Copyright (C) 2020 Puhang Ding
* 2020 Jan Schlichter
* 2020 Nishchay Agrawal
*
* 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_scd30
* @{
* @file
* @brief Internal constants, configuration commands for SCD30 sensor
*
* @author Puhang Ding <czarsir@gmail.com>
* @author Jan Schlichter <schlichter@ibr.cs.tu-bs.de>
* @author Nishchay Agrawal <f2016088@pilani.bits-pilani.ac.in>
*/
#ifndef SCD30_INTERNAL_H
#define SCD30_INTERNAL_H
#ifdef __cplusplus
extern "C" {
#endif
/**
* @name SCD30 CRC Function and start value for CRC
* @{
*/
#define SCD30_CRC_FUNC 0x31 /**< CRC Function for CRC-8
calculation and verification */
#define SCD30_CRC_START_VAL 0xFF /**< Start value for CRC-8
calculation */
/** @} */
/**
* @name SCD30 Internal Constants
* @{
*/
#define SCD30_DEF_PRESSURE 0x03f5 /**< Ambient Pressure:
1013.25 mBar */
#define SCD30_READ_WRITE_SLEEP_US (4 * US_PER_MS)
#define SCD30_DATA_RDY_TIMEOUT (1 * US_PER_SEC)
#define SCD30_MIN_INTERVAL 2
#define SCD30_MAX_INTERVAL 1800
#define SCD30_MIN_PRESSURE_COMP 700
#define SCD30_MAX_PRESSURE_COMP 1400
/** @} */
#ifdef __cplusplus
}
#endif
#endif /* SCD30_INTERNAL_H */
/** @} */

View File

@ -0,0 +1,86 @@
/*
* Copyright (C) 2020 Puhang Ding
* 2020 Nishchay Agrawal
*
* 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_sensors
* @{
* @file
* @brief Device driver params interface for the SCD30 sensor.
*
* @author Nishchay Agrawal <f2016088@pilani.bits-pilani.ac.in>
* @author Puhang Ding <czarsir@gmail.com>
* @}
*/
#ifndef SCD30_PARAMS_H
#define SCD30_PARAMS_H
#include "periph/i2c.h"
#include "scd30.h"
#include "saul_reg.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @name SCD30 I2C address
* @{
*/
#define SCD30_I2C_ADDR 0x61
/** @} */
/**
* @name Set default configuration parameters for the SCD30
* @{
*/
#ifndef SCD30_PARAM_I2C_DEV
#define SCD30_PARAM_I2C_DEV I2C_DEV(0)
#endif
#ifndef SCD30_PARAM_I2C_ADDR
#define SCD30_PARAM_I2C_ADDR SCD30_I2C_ADDR
#endif
#ifndef SCD30_PARAMS
#define SCD30_PARAMS { .i2c_dev = SCD30_PARAM_I2C_DEV, \
.i2c_addr = SCD30_PARAM_I2C_ADDR }
#endif
#ifndef SCD30_SAUL_INFO
#define SCD30_SAUL_INFO { .name = "scd30" }
#endif
/**@}*/
/**
* @brief Configure SCD30
*/
static const scd30_params_t scd30_params[] =
{
SCD30_PARAMS
};
/**
* @brief Get the number of configured SCD30 devices
*/
#define SCD30_NUMOF ARRAY_SIZE(scd30_params)
/**
* @brief Configure SAUL registry entries
*/
static const saul_reg_info_t scd30_saul_info[] =
{
SCD30_SAUL_INFO
};
#ifdef __cplusplus
}
#endif
#endif /* SCD30_PARAMS_H */

363
drivers/scd30/scd30.c Normal file
View File

@ -0,0 +1,363 @@
/*
* Copyright (C) 2020 Puhang Ding
* 2020 Jan Schlichter
* 2020 Nishchay Agrawal
*
* 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_scd30
* @{
* @file
* @brief Sensirion SCD30 sensor driver implementation
*
* @author Nishchay Agrawal <f2016088@pilani.bits-pilani.ac.in>
* @author Puhang Ding <czarsir@gmail.com>
* @author Jan Schlichter <schlichter@ibr.cs.tu-bs.de>
* @}
*/
#include <string.h>
#include "periph/i2c.h"
#include "xtimer.h"
#include "byteorder.h"
#include "checksum/crc8.h"
#include "scd30.h"
#include "scd30_internal.h"
#define ENABLE_DEBUG (0)
#include "debug.h"
#define SCD30_I2C (dev->params.i2c_dev)
#define SCD30_I2C_ADDRESS (dev->params.i2c_addr)
static int8_t _scd30_read_data(scd30_t *dev, scd30_measurement_t *result);
static bool _scd30_crc_check(uint8_t *buff, uint8_t len);
static bool _is_valid_interval(uint16_t *interval);
static bool _is_valid_press_comp(uint16_t *apc);
static inline float _reinterpret_float(uint32_t raw_val);
static float _raw_val_to_float(const uint8_t *buffer);
int8_t scd30_init(scd30_t *dev, const scd30_params_t *params)
{
DEBUG("[scd30] init\n");
dev->params = *params;
int ret = 0;
uint16_t version;
ret = scd30_get_param(dev, SCD30_VERSION, &version);
DEBUG("[scd30] --- Version 0x%02x%02x\n", (version >> 8), version);
if (ret < 0) {
DEBUG("[scd30] Failed to get version\n");
return ret;
}
DEBUG("[scd30] init: done.\n");
return SCD30_OK;
}
int8_t scd30_set_param(const scd30_t *dev, uint16_t param, uint16_t val)
{
uint8_t buffer[5];
int ret = 0;
buffer[0] = (uint8_t)(param >> 8);
buffer[1] = (uint8_t)param;
buffer[2] = (uint8_t)(val >> 8);
buffer[3] = (uint8_t)val;
buffer[4] = crc8(buffer + 2, 2, SCD30_CRC_FUNC, SCD30_CRC_START_VAL);
i2c_acquire(SCD30_I2C);
ret = i2c_write_bytes(SCD30_I2C, SCD30_I2C_ADDRESS, buffer, 5, 0);
i2c_release(SCD30_I2C);
if (ret != 0) {
return SCD30_COM_FAILED;
}
else {
xtimer_usleep(SCD30_READ_WRITE_SLEEP_US);
return SCD30_OK;
}
}
int8_t scd30_get_param(scd30_t *dev, uint16_t param, uint16_t *val)
{
uint8_t buffer[3];
int ret = 0;
param = htons(param);
i2c_acquire(SCD30_I2C);
ret = i2c_write_bytes(SCD30_I2C, SCD30_I2C_ADDRESS, &param, 2, 0);
if (ret != 0) {
i2c_release(SCD30_I2C);
return ret;
}
i2c_release(SCD30_I2C);
xtimer_usleep(SCD30_READ_WRITE_SLEEP_US);
i2c_acquire(SCD30_I2C);
ret = i2c_read_bytes(SCD30_I2C, SCD30_I2C_ADDRESS, buffer, 3, 0);
i2c_release(SCD30_I2C);
if (ret != 0) {
DEBUG("[scd30] Problem reading param %u from sensor\n", param);
}
if (!_scd30_crc_check(buffer, 2)) {
return SCD30_CRC_ERROR;
}
*val = byteorder_bebuftohs(buffer);
return SCD30_OK;
}
int8_t scd30_read_triggered(scd30_t *dev, scd30_measurement_t *result)
{
uint16_t state = 0;
uint16_t curr_interval;
uint32_t initial_time;
int ret;
ret = scd30_get_param(dev, SCD30_INTERVAL, &curr_interval);
if (ret != 0) {
return ret;
}
if (curr_interval != SCD30_MIN_INTERVAL) {
ret = scd30_set_param(dev, SCD30_INTERVAL, SCD30_MIN_INTERVAL);
if (ret != 0) {
DEBUG("[scd30] Error setting interval to min\n");
return ret;
}
}
/* Doing this to reduce the traffic on the I2C bus as
* compared when constantly polling the device for status
*/
xtimer_sleep(SCD30_MIN_INTERVAL);
initial_time = xtimer_now_usec();
ret = scd30_get_param(dev, SCD30_STATUS, &state);
if (ret != 0) {
return ret;
}
while (state != 1 &&
(xtimer_now_usec() - initial_time) < SCD30_DATA_RDY_TIMEOUT) {
ret = scd30_get_param(dev, SCD30_STATUS, &state);
if (ret != 0) {
return ret;
}
}
ret = _scd30_read_data(dev, result);
if (ret != SCD30_OK) {
DEBUG("[scd30] Error reading data \n");
return ret;
}
if (curr_interval != SCD30_MIN_INTERVAL) {
ret = scd30_set_param(dev, SCD30_INTERVAL, curr_interval);
if (ret != 0) {
DEBUG("[scd30] Error resetting interval to original value\n");
return ret;
}
}
return SCD30_OK;
}
uint8_t scd30_read_periodic(scd30_t *dev, scd30_measurement_t *result)
{
uint16_t state = 0;
uint32_t initial_time;
int ret = 0;
initial_time = xtimer_now_usec();
ret = scd30_get_param(dev, SCD30_STATUS, &state);
if (ret != 0) {
return ret;
}
while (state != 1 &&
(xtimer_now_usec() - initial_time) < SCD30_DATA_RDY_TIMEOUT) {
scd30_get_param(dev, SCD30_STATUS, &state);
}
if (state != 1) {
return SCD30_NO_NEW_DATA;
}
ret = _scd30_read_data(dev, result);
if (ret != SCD30_OK) {
DEBUG("[scd30] Error reading values");
return ret;
}
return SCD30_OK;
}
int scd30_start_periodic_measurement(scd30_t *dev, uint16_t *interval,
uint16_t *apc)
{
int ret = 0;
/* Check if input is valid */
if (!_is_valid_interval(interval)) {
DEBUG("[scd30] Interval value out of range\n");
return SCD30_INVALID_VALUE;
}
if (!_is_valid_press_comp(apc)) {
DEBUG("[scd30] Ambient pressure compensation value out of range\n");
return SCD30_INVALID_VALUE;
}
ret = scd30_set_param(dev, SCD30_INTERVAL, *interval);
if (ret != 0) {
return ret;
}
ret = scd30_set_param(dev, SCD30_START, *apc);
if (ret != 0) {
return ret;
}
return SCD30_OK;
}
int8_t scd30_stop_measurements(const scd30_t *dev)
{
const uint16_t cmd = htons(SCD30_STOP);
int ret;
i2c_acquire(SCD30_I2C);
ret = i2c_write_bytes(SCD30_I2C, SCD30_I2C_ADDRESS, &cmd, 2, 0);
i2c_release(SCD30_I2C);
if (ret != 0) {
DEBUG("[scd30]: Error stopping measurements.\n");
}
return SCD30_OK;
}
/**
* intern function to read data
*
* @param dev device
* @param result struct in which data read is stored
* @return SCD30_OK on success
*/
static int8_t _scd30_read_data(scd30_t *dev, scd30_measurement_t *result)
{
uint8_t buffer[18];
int ret = 0;
const uint16_t cmd = htons(SCD30_DATA);
i2c_acquire(SCD30_I2C);
ret = i2c_write_bytes(SCD30_I2C, SCD30_I2C_ADDRESS, &cmd, 2, 0);
if (ret != 0) {
i2c_release(SCD30_I2C);
return ret;
}
ret = i2c_read_bytes(SCD30_I2C, SCD30_I2C_ADDRESS, buffer, 18, 0);
if (ret != 0) {
DEBUG("[scd30] read_data: ret %i.\n", ret);
i2c_release(SCD30_I2C);
return ret;
}
i2c_release(SCD30_I2C);
if (!_scd30_crc_check(buffer, 2)) {
return SCD30_CRC_ERROR;
}
if (!_scd30_crc_check(buffer + 3, 2)) {
return SCD30_CRC_ERROR;
}
if (!_scd30_crc_check(buffer + 6, 2)) {
return SCD30_CRC_ERROR;
}
if (!_scd30_crc_check(buffer + 9, 2)) {
return SCD30_CRC_ERROR;
}
if (!_scd30_crc_check(buffer + 12, 2)) {
return SCD30_CRC_ERROR;
}
if (!_scd30_crc_check(buffer + 15, 2)) {
return SCD30_CRC_ERROR;
}
result->co2_concentration = _raw_val_to_float(&buffer[0]);
result->temperature = _raw_val_to_float(&buffer[6]);
result->relative_humidity = _raw_val_to_float(&buffer[12]);
return SCD30_OK;
}
/**
* check if crc is valid (Assuming crc value to compare with is stored at
* end of buffer)
*
* @param buff buffer of which crc value has to be checked
* @param len length of buffer
* @return true on success
*/
static bool _scd30_crc_check(uint8_t *buff, uint8_t len)
{
return crc8(buff, len, SCD30_CRC_FUNC, SCD30_CRC_START_VAL) == buff[len];
}
/**
* check if interval is within bounds
*
* @param interval Pointer to interval to check
* @return true if interval within bounds
*/
static bool _is_valid_interval(uint16_t *interval)
{
if (*interval < SCD30_MIN_INTERVAL || *interval > SCD30_MAX_INTERVAL) {
return false;
}
return true;
}
/**
* check if pressure compensation is within allowed values
*
* @param apc Pointer to Pressure compensation to check
* @return true if value within bounds
*/
static bool _is_valid_press_comp(uint16_t *apc)
{
if ((*apc < SCD30_MIN_PRESSURE_COMP || *apc > SCD30_MAX_PRESSURE_COMP) &&
*apc != 0) {
return false;
}
return true;
}
/**
* convert IEEE754 stored in uint32_t to actual float value
*
* @param raw_val Value to convert
* @return Converted value
*/
static inline float _reinterpret_float(uint32_t raw_val)
{
union {
float float_val;
uint32_t int_val;
} to_float = { .int_val = raw_val };
return to_float.float_val;
}
/**
* convert value from buffer storing IEEE754 represented
* float to actual float value
*
* @param buffer buffer with value
* @return Converted value
*/
static float _raw_val_to_float(const uint8_t *buffer)
{
uint32_t tmp = byteorder_bebuftohl(buffer);
return _reinterpret_float(tmp);
}

103
drivers/scd30/scd30_saul.c Normal file
View File

@ -0,0 +1,103 @@
/*
* Copyright (C) 2020 Technische Universität Braunschweig
*
* 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_scd30
* @file
* @brief SAUL adaption for Sensirion SCD30 sensor
* @author Puhang Ding <czarsir@gmail.com>
* @author Nishchay Agrawal <f2016088@pilani.bits-pilani.ac.in>
* @author Jan Schlichter <schlichter@ibr.cs.tu-bs.de>
*/
#include "saul.h"
#include "scd30.h"
#include "scd30_internal.h"
static void _float_fit(float src, phydat_t *data, uint32_t mul)
{
int32_t i32;
i32 = src * mul;
phydat_fit(data, &i32, 1);
}
static int _co2_read(const void *dev, phydat_t *res)
{
int ret = 0;
scd30_measurement_t result;
scd30_set_param(dev, SCD30_START, SCD30_DEF_PRESSURE);
ret = scd30_read_triggered((scd30_t *)dev, &result);
if (ret != 0) {
return ret;
}
res->scale = -2;
res->unit = UNIT_PPM;
_float_fit(result.co2_concentration, res, 100);
scd30_stop_measurements(dev);
return 1;
}
static int _temp_read(const void *dev, phydat_t *res)
{
int ret = 0;
scd30_measurement_t result;
scd30_set_param(dev, SCD30_START, SCD30_DEF_PRESSURE);
ret = scd30_read_triggered((scd30_t *)dev, &result);
if (ret != 0) {
return ret;
}
res->unit = UNIT_TEMP_C;
res->scale = -2;
_float_fit(result.temperature, res, 100);
scd30_stop_measurements(dev);
return 1;
}
static int _hum_read(const void *dev, phydat_t *res)
{
int ret = 0;
scd30_measurement_t result;
scd30_set_param(dev, SCD30_START, SCD30_DEF_PRESSURE);
ret = scd30_read_triggered((scd30_t *)dev, &result);
if (ret != 0) {
return ret;
}
res->unit = UNIT_PERCENT;
res->scale = -2;
_float_fit(result.relative_humidity, res, 100);
scd30_stop_measurements(dev);
return 1;
}
const saul_driver_t scd30_co2_saul_driver = {
.read = _co2_read,
.write = saul_notsup,
.type = SAUL_SENSE_CO2,
};
const saul_driver_t scd30_temp_saul_driver = {
.read = _temp_read,
.write = saul_notsup,
.type = SAUL_SENSE_TEMP,
};
const saul_driver_t scd30_hum_saul_driver = {
.read = _hum_read,
.write = saul_notsup,
.type = SAUL_SENSE_HUM,
};

View File

@ -0,0 +1,12 @@
include ../Makefile.tests_common
DRIVER ?= scd30
USEMODULE += $(DRIVER)
USEMODULE += printf_float
TEST_ITERATIONS ?= 10
# export iterations for continuous measurements
CFLAGS += -DTEST_ITERATIONS=$(TEST_ITERATIONS)
include $(RIOTBASE)/Makefile.include

View File

@ -0,0 +1,18 @@
## About
This is a test application for the Sensirion SCD30 CO2 concentration,
temperature and relative humidity sensor
## Usage
This test application will initialize the SCD30 sensor and print TEST_ITERATIONS
number of following values first in continuous mode and then triggered mode every
second:
* CO2 concentration
* Temperature
* Relative humidity
The user can specify the number of iterations by setting the variable
`TEST_ITERATIONS` (default value is 10) from commandline as follows:
```
make BOARD=... TEST_ITERATIONS=... -C tests/driver_scd30
```

88
tests/driver_scd30/main.c Normal file
View File

@ -0,0 +1,88 @@
/*
* Copyright (C) 2020 Puhang Ding
* 2020 Jan Schlichter
* 2020 Nishchay Agrawal
*
* 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_scd30
* @{
* @file
* @brief Sensirion SCD30 sensor driver implementation
*
* @author Puhang Ding <czarsir@gmail.com>
* @author Jan Schlichter <schlichter@ibr.cs.tu-bs.de>
* @author Nishchay Agrawal <f2016088@pilani.bits-pilani.ac.in>
* @}
*/
#include <stdio.h>
#include "periph/gpio.h"
#include "xtimer.h"
#include "scd30.h"
#include "scd30_params.h"
#include "scd30_internal.h"
#define MEASUREMENT_INTERVAL_SECS (2)
scd30_t scd30_dev;
scd30_params_t params = SCD30_PARAMS;
scd30_measurement_t result;
int main(void)
{
printf("SCD30 Test:\n");
int i = 0;
scd30_init(&scd30_dev, &params);
uint16_t pressure_compensation = SCD30_DEF_PRESSURE;
uint16_t value = 0;
uint16_t interval = MEASUREMENT_INTERVAL_SECS;
scd30_set_param(&scd30_dev, SCD30_INTERVAL, MEASUREMENT_INTERVAL_SECS);
scd30_set_param(&scd30_dev, SCD30_START, pressure_compensation);
scd30_get_param(&scd30_dev, SCD30_INTERVAL, &value);
printf("[test][dev-%d] Interval: %u s\n", i, value);
scd30_get_param(&scd30_dev, SCD30_T_OFFSET, &value);
printf("[test][dev-%d] Temperature Offset: %u.%02u C\n", i, value / 100u,
value % 100u);
scd30_get_param(&scd30_dev, SCD30_A_OFFSET, &value);
printf("[test][dev-%d] Altitude Compensation: %u m\n", i, value);
scd30_get_param(&scd30_dev, SCD30_ASC, &value);
printf("[test][dev-%d] ASC: %u\n", i, value);
scd30_get_param(&scd30_dev, SCD30_FRC, &value);
printf("[test][dev-%d] FRC: %u ppm\n", i, value);
while (i < TEST_ITERATIONS) {
xtimer_sleep(1);
scd30_read_triggered(&scd30_dev, &result);
printf(
"[scd30_test-%d] Triggered measurements co2: %.02fppm,"
" temp: %.02f°C, hum: %.02f%%. \n", i, result.co2_concentration,
result.temperature, result.relative_humidity);
i++;
}
i = 0;
scd30_start_periodic_measurement(&scd30_dev, &interval,
&pressure_compensation);
while (i < TEST_ITERATIONS) {
xtimer_sleep(MEASUREMENT_INTERVAL_SECS);
scd30_read_periodic(&scd30_dev, &result);
printf(
"[scd30_test-%d] Continuous measurements co2: %.02fppm,"
" temp: %.02f°C, hum: %.02f%%. \n", i, result.co2_concentration,
result.temperature, result.relative_humidity);
i++;
}
scd30_stop_measurements(&scd30_dev);
return 0;
}