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:
parent
42eb044ec6
commit
900e4b61dc
174
drivers/include/scd30.h
Normal file
174
drivers/include/scd30.h
Normal 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 */
|
85
drivers/saul/init_devs/auto_init_scd30.c
Normal file
85
drivers/saul/init_devs/auto_init_scd30.c
Normal 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);
|
||||
}
|
||||
}
|
@ -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
1
drivers/scd30/Makefile
Normal file
@ -0,0 +1 @@
|
||||
include $(RIOTBASE)/Makefile.base
|
5
drivers/scd30/Makefile.dep
Normal file
5
drivers/scd30/Makefile.dep
Normal file
@ -0,0 +1,5 @@
|
||||
ifneq (,$(filter scd30,$(USEMODULE)))
|
||||
FEATURES_REQUIRED += periph_i2c
|
||||
USEMODULE += checksum
|
||||
USEMODULE += xtimer
|
||||
endif
|
2
drivers/scd30/Makefile.include
Normal file
2
drivers/scd30/Makefile.include
Normal file
@ -0,0 +1,2 @@
|
||||
USEMODULE_INCLUDES_scd30 := $(LAST_MAKEFILEDIR)/include
|
||||
USEMODULE_INCLUDES += $(USEMODULE_INCLUDES_scd30)
|
61
drivers/scd30/include/scd30_internal.h
Normal file
61
drivers/scd30/include/scd30_internal.h
Normal 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 */
|
||||
/** @} */
|
86
drivers/scd30/include/scd30_params.h
Normal file
86
drivers/scd30/include/scd30_params.h
Normal 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
363
drivers/scd30/scd30.c
Normal 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, ¶m, 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
103
drivers/scd30/scd30_saul.c
Normal 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,
|
||||
};
|
12
tests/driver_scd30/Makefile
Normal file
12
tests/driver_scd30/Makefile
Normal 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
|
18
tests/driver_scd30/README.md
Normal file
18
tests/driver_scd30/README.md
Normal 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
88
tests/driver_scd30/main.c
Normal 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, ¶ms);
|
||||
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;
|
||||
}
|
Loading…
Reference in New Issue
Block a user