1
0
mirror of https://github.com/RIOT-OS/RIOT.git synced 2025-12-16 10:03:50 +01:00

drivers/sen5x: added dedicated driver for the SEN5x

This commit is contained in:
Daniel Prigoshij 2023-05-11 15:51:50 +02:00
parent 8a76cee6c5
commit fadeafdf85
21 changed files with 3655 additions and 0 deletions

View File

@ -46,6 +46,7 @@ rsource "matrix_keypad/Kconfig"
rsource "mma8x5x/Kconfig"
rsource "opt3001/Kconfig"
rsource "seesaw_soil/Kconfig"
rsource "sen5x/Kconfig"
rsource "sht2x/Kconfig"
rsource "sm_pwm_01c/Kconfig"
rsource "sps30/Kconfig"

View File

@ -188,6 +188,10 @@ ifneq (,$(filter sdp3x_%,$(USEMODULE)))
USEMODULE += sdp3x
endif
ifneq (,$(filter sen5%,$(USEMODULE)))
USEMODULE += sen5x
endif
ifneq (,$(filter servo_%,$(USEMODULE)))
USEMODULE += servo
endif

300
drivers/include/sen5x.h Normal file
View File

@ -0,0 +1,300 @@
/*
* Copyright (C) 2023 TUÚ Braunschweig Institut für Betriebssysteme und Rechnerverbund
*
* 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_sen5x Sensirion Embedded I2C SEN5x Driver
* @ingroup drivers_sensors
* @ingroup drivers_saul
* @brief Driver for I2C communication to SEN5x devices.
*
* @{
*
* @file
*
* @author Daniel Prigoshij <d.prigoshij@tu-braunschweig.de>
*/
#ifndef SEN5X_H
#define SEN5X_H
/* Add header includes here */
#include "periph/i2c.h"
#include <stdint.h>
#include "saul.h"
#ifdef __cplusplus
extern "C" {
#endif
/* Declare the API of the driver */
/**
* @brief Wrapper for measured values
*/
typedef struct {
uint16_t mass_concentration_pm1p0; /**< raw value is scaled with factor 10: PM1.0 [µg/m³] = value / 10 */
uint16_t mass_concentration_pm2p5; /**< raw value is scaled with factor 10: PM2.5 [µg/m³] = value / 10 */
uint16_t mass_concentration_pm4p0; /**< raw value is scaled with factor 10: PM4.0 [µg/m³] = value / 10 */
uint16_t mass_concentration_pm10p0; /**< raw value is scaled with factor 10: PM10.0 [µg/m³] = value / 10 */
uint16_t number_concentration_pm0p5; /**< raw value is scaled with factor 10: PM0.5 [#/cm³] = value / 10 */
uint16_t number_concentration_pm1p0; /**< raw value is scaled with factor 10: PM1.0 [#/cm³] = value / 10 */
uint16_t number_concentration_pm2p5; /**< raw value is scaled with factor 10: PM2.5 [#/cm³] = value / 10 */
uint16_t number_concentration_pm4p0; /**< raw value is scaled with factor 10: PM4.0 [#/cm³] = value / 10 */
uint16_t number_concentration_pm10p0; /**< raw value is scaled with factor 10: PM10.0 [#/cm³] = value / 10 */
uint16_t typical_particle_size; /**< raw value is scaled with factor 1000: Size [µm] = value / 1000*/
int16_t ambient_humidity; /**< raw value is scaled with factor 100: RH [%] = value / 100 */
int16_t ambient_temperature; /**< raw value is scaled with factor 200: T [°C] = value / 200 */
int16_t voc_index; /**< raw value is scaled with factor 10: VOC Index = value / 10 */
int16_t nox_index; /**< raw value is scaled with factor 10: NOx Index = value / 10 */
} sen5x_measurement_t;
/**
* @brief Device initialization parameters
*/
typedef struct {
i2c_t i2c_dev; /**< I2C device which is used */
uint8_t i2c_addr; /**< I2C address */
} sen5x_params_t;
/**
* @brief Device descriptor for the driver
*/
typedef struct {
sen5x_params_t params; /**< Device initialization parameters */
} sen5x_t;
/**
* @brief Initialize the given device
*
* @param[inout] dev Device descriptor of the driver
* @param[in] params Initialization parameters
*/
void sen5x_init(sen5x_t *dev, const sen5x_params_t *params);
/**
* @brief Execute a reset on the given device
*
* @param[inout] dev Device descriptor of the driver
*/
void sen5x_reset(const sen5x_t *dev);
/**
* @brief Starts a continuous measurement
*
* @param[inout] dev Device descriptor of the driver
*/
void sen5x_wake(const sen5x_t *dev);
/**
* @brief Starts a continuous measurement without PM. Only humidity, temperature, VOC and NOx are measured.
*
* @param[inout] dev Device descriptor of the driver
*/
void sen5x_wake_no_pm(const sen5x_t *dev);
/**
* @brief Stops the measurement and returns to idle mode
*
* @param[inout] dev Device descriptor of the driver
*/
void sen5x_sleep(const sen5x_t *dev);
/**
* @brief Sets the fan to maximum speed, to clean it within 10 seconds
*
* @param[inout] dev Device descriptor of the driver
*/
void sen5x_clean_fan(const sen5x_t *dev);
/**
* @brief Sets the fan to maximum speed, to clean it within 10 seconds
*
* @param[inout] dev Device descriptor of the driver
*
* @return 0 if no new measurements are available
* @return 1 if new measuremtns are ready to be read
*/
bool sen5x_data_ready_flag(const sen5x_t *dev);
/**
* @brief Read measured mass concentration, humidity and temperature values
*
* @param[inout] dev Device descriptor of the driver
* @param[out] values Pointer to wrapper containing all measured values
*/
void sen5x_read_values(const sen5x_t *dev, sen5x_measurement_t *values);
/**
* @brief Read measured particle matter values
*
* @param[inout] dev Device descriptor of the driver
* @param[out] values Pointer to wrapper containing all measured values
*/
void sen5x_read_pm_values(const sen5x_t *dev, sen5x_measurement_t *values);
/**
* @brief Set a custom temperature offset to the ambient temperature
*
* @param[inout] dev Device descriptor of the driver
* @param[in] temp_offset Temperature offset in °C
* @param[in] slope Normalized temperature offset slope
* @param[in] time_constant Time constant in seconds
*/
void sen5x_set_temperature_offset(const sen5x_t *dev, int16_t temp_offset, int16_t slope, uint16_t time_constant);
/**
* @brief Set a custom temperature offset to the ambient temperature
*
* @param[inout] dev Device descriptor of the driver
* @param[out] temp_offset Temperature offset in °C
* @param[out] slope Normalized temperature offset slope
* @param[out] time_constant Time constant in seconds
*/
void sen5x_get_temperature_offset(const sen5x_t *dev, int16_t *temp_offset, int16_t *slope, uint16_t *time_constant);
/**
* @brief Set the parameter for a warm start on the device, to improve initial accuracy of the ambient temperature output
*
* @param[inout] dev Device descriptor of the driver
* @param[in] warm_start Warm start behavior as a value in the range from
* 0 (cold start, default) to 65535 (warm start).
*/
void sen5x_set_warm_start(const sen5x_t *dev, uint16_t warm_start);
/**
* @brief Get the warm start paramater
*
* @param[inout] dev Device descriptor of the driver
* @param[out] warm_start Warm start behavior as a value in the range from
* 0 (cold start, default) to 65535 (warm start).
*/
void sen5x_get_warm_start(const sen5x_t *dev, uint16_t *warm_start);
/**
* @brief Set the parameters for the VOC Algorithm tuning
*
* @param[inout] dev Device descriptor of the driver
* @param[in] index_offset VOC index representing typical (average) conditions
* @param[in] learning_time_offset_hours Time constant to estimate the VOC algorithm offset from the
* history in hours
* @param[in] learning_time_gain_hours Time constant to estimate the VOC algorithm gain from the history
* in hours
* @param[in] gating_max_duration_minutes Maximum duration of gating in minutes
* @param[in] std_initial Initial estimate for standard deviation
* @param[in] gain_factor Gain factor to amplify or to attenuate the VOC index output
*/
void sen5x_set_voc_algorithm_tuning(
const sen5x_t *dev, int16_t index_offset, int16_t learning_time_offset_hours,
int16_t learning_time_gain_hours, int16_t gating_max_duration_minutes,
int16_t std_initial, int16_t gain_factor);
/**
* @brief Get the VOC Algortihm tuning parameters
*
* @param[inout] dev Device descriptor of the driver
* @param[out] index_offset VOC index representing typical (average) conditions
* @param[out] learning_time_offset_hours Time constant to estimate the VOC algorithm offset from the
* history in hours
* @param[out] learning_time_gain_hours Time constant to estimate the VOC algorithm gain from the history
* in hours
* @param[out] gating_max_duration_minutes Maximum duration of gating in minutes
* @param[out] std_initial Initial estimate for standard deviation
* @param[out] gain_factor Gain factor to amplify or to attenuate the VOC index output
*/
void sen5x_get_voc_algorithm_tuning(
const sen5x_t *dev, int16_t *index_offset, int16_t *learning_time_offset_hours,
int16_t *learning_time_gain_hours, int16_t *gating_max_duration_minutes,
int16_t *std_initial, int16_t *gain_factor);
/**
* @brief Set the parameters for the NOx Algorithm tuning
*
* @param[inout] dev Device descriptor of the driver
* @param[in] index_offset NOx index representing typical (average) conditions
* @param[in] learning_time_offset_hours Time constant to estimate the NOx algorithm offset from the
* history in hours
* @param[in] learning_time_gain_hours The time constant to estimate the NOx algorithm gain from the
* history has no impact for NOx. This parameter is still in place for
* consistency reasons with the VOC tuning parameters command.
* This parameter must always be set to 12 hours
* @param[in] gating_max_duration_minutes Maximum duration of gating in minutes
* @param[in] std_initial Initial estimate for standard deviation
* @param[in] gain_factor Gain factor to amplify or to attenuate the NOx index output
*/
void sen5x_set_nox_algorithm_tuning(
const sen5x_t *dev, int16_t index_offset, int16_t learning_time_offset_hours,
int16_t learning_time_gain_hours, int16_t gating_max_duration_minutes,
int16_t std_initial, int16_t gain_factor);
/**
* @brief Get the NOx Algortihm tuning parameters
*
* @param[inout] dev Device descriptor of the driver
* @param[out] index_offset NOx index representing typical (average) conditions
* @param[out] learning_time_offset_hours Time constant to estimate the NOx algorithm offset from the
* history in hours
* @param[out] learning_time_gain_hours The time constant to estimate the NOx algorithm gain from the
* history has no impact for NOx. This parameter is still in place for
* consistency reasons with the VOC tuning parameters command.
* This parameter must always be set to 12 hours
* @param[out] gating_max_duration_minutes Maximum duration of gating in minutes
* @param[out] std_initial Initial estimate for standard deviation
* @param[out] gain_factor Gain factor to amplify or to attenuate the NOx index output
*/
void sen5x_get_nox_algorithm_tuning(
const sen5x_t *dev, int16_t *index_offset, int16_t *learning_time_offset_hours,
int16_t *learning_time_gain_hours, int16_t *gating_max_duration_minutes,
int16_t *std_initial, int16_t *gain_factor);
/**
* @brief Set the mode for the RH/T acceleration algorithm
*
* @param[inout] dev Device descriptor of the driver
* @param[in] mode RH/T accelaration mode:
* = 0: Low Acceleration
* = 1: High Acceleration
* = 2: Medium Acceleration
*/
void sen5x_set_rht_acceleration(const sen5x_t *dev, uint16_t mode);
/**
* @brief Get the mode for the RH/T acceleration algorithm
*
* @param[inout] dev Device descriptor of the driver
* @param[out] mode RH/T accelaration mode:
* = 0: Low Acceleration
* = 1: High Acceleration
* = 2: Medium Acceleration
*/
void sen5x_get_rht_acceleration(const sen5x_t *dev, uint16_t *mode);
/**
* @brief Get the VOC Algorithm state
*
* @param[inout] dev Device descriptor of the driver
* @param[in] state VOC Algorithm state
* @param[in] state_size Size of the VOC Algorithm state
*/
void sen5x_set_voc_state(const sen5x_t *dev, const uint8_t *state, uint8_t state_size);
/**
* @brief Set the VOC Algorithm state
*
* @param[inout] dev Device descriptor of the driver
* @param[out] state VOC Algorithm state
* @param[in] state_size Size of the VOC Algorithm state
*/
void sen5x_get_voc_state(const sen5x_t *dev, uint8_t *state, uint8_t state_size);
#ifdef __cplusplus
}
#endif
#endif /* SEN5X_H */
/** @} */

View File

@ -0,0 +1,139 @@
/*
* Copyright (C) 2023 TU Braunschweig Institut für Betriebssysteme und Rechnerverbund
*
* 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 sen5x device driver.
*
* @author Daniel Prigoshij <prigoshi@ibr.cs.tu-bs.de>
*
* @}
*/
#include "assert.h"
#include "log.h"
#include "saul_reg.h"
#include "sen5x.h"
#include "sen5x_params.h"
/**
* @brief Allocation of memory for device descriptors
*/
static sen5x_t sen5x_devs[SEN5X_NUM];
/**
* @brief Memory for the SAUL registry entries
*/
static saul_reg_t saul_entries[SEN5X_NUM * 12];
/**
* @brief Define the number of saul info
*/
#define SEN5X_INFO_NUM ARRAY_SIZE(sen5x_saul_info)
/**
* @name Reference the driver structs.
* @{
*/
extern const saul_driver_t sen5x_mass_concentration_pm1p0_driver;
extern const saul_driver_t sen5x_mass_concentration_pm2p5_driver;
extern const saul_driver_t sen5x_mass_concentration_pm4p0_driver;
extern const saul_driver_t sen5x_mass_concentration_pm10p0_driver;
extern const saul_driver_t sen5x_number_concentration_pm0p5_driver;
extern const saul_driver_t sen5x_number_concentration_pm1p0_driver;
extern const saul_driver_t sen5x_number_concentration_pm2p5_driver;
extern const saul_driver_t sen5x_number_concentration_pm4p0_driver;
extern const saul_driver_t sen5x_number_concentration_pm10p0_driver;
extern const saul_driver_t sen5x_typical_particle_size_driver;
extern const saul_driver_t sen5x_ambient_humidity_driver;
extern const saul_driver_t sen5x_ambient_temperature_driver;
/** @} */
void auto_init_sen5x(void)
{
assert(SEN5X_INFO_NUM == SEN5X_NUM);
for (unsigned i = 0; i < SEN5X_NUM; i++) {
LOG_DEBUG("[auto_init_saul] initializing SEN5X #%u\n", i);
if (sen5x_init(&sen5x_devs[i], &sen5x_params[i]) != 0) {
LOG_ERROR("[auto_init_saul] error initializing SEN5X #%u\n", i);
continue;
}
sen5x_wake(&sen5x_devs[i]);
/* Mass Concentration pm1p0 */
saul_entries[(i * 12)].dev = &(sen5x_devs[i]);
saul_entries[(i * 12)].name = sen5x_saul_info[i].name;
saul_entries[(i * 12)].driver = &sen5x_mass_concentration_pm1p0_driver;
/* Mass Concentration pm2p5 */
saul_entries[(i * 12) + 1].dev = &(sen5x_devs[i]);
saul_entries[(i * 12) + 1].name = sen5x_saul_info[i].name;
saul_entries[(i * 12) + 1].driver = &sen5x_mass_concentration_pm2p5_driver;
/* Mass Concentration pm4p0 */
saul_entries[(i * 12) + 2].dev = &(sen5x_devs[i]);
saul_entries[(i * 12) + 2].name = sen5x_saul_info[i].name;
saul_entries[(i * 12) + 2].driver = &sen5x_mass_concentration_pm4p0_driver;
/* Mass Concentration pm10p0 */
saul_entries[(i * 12) + 3].dev = &(sen5x_devs[i]);
saul_entries[(i * 12) + 3].name = sen5x_saul_info[i].name;
saul_entries[(i * 12) + 3].driver = &sen5x_mass_concentration_pm10p0_driver;
/* Number Concentration pm0p5 */
saul_entries[(i * 12) + 4].dev = &(sen5x_devs[i]);
saul_entries[(i * 12) + 4].name = sen5x_saul_info[i].name;
saul_entries[(i * 12) + 4].driver = &sen5x_number_concentration_pm0p5_driver;
/* Number Concentration pm1p0 */
saul_entries[(i * 12) + 5].dev = &(sen5x_devs[i]);
saul_entries[(i * 12) + 5].name = sen5x_saul_info[i].name;
saul_entries[(i * 12) + 5].driver = &sen5x_number_concentration_pm1p0_driver;
/* Number Concentration pm2p5 */
saul_entries[(i * 12) + 6].dev = &(sen5x_devs[i]);
saul_entries[(i * 12) + 6].name = sen5x_saul_info[i].name;
saul_entries[(i * 12) + 6].driver = &sen5x_number_concentration_pm2p5_driver;
/* Number Concentration pm4p0 */
saul_entries[(i * 12) + 7].dev = &(sen5x_devs[i]);
saul_entries[(i * 12) + 7].name = sen5x_saul_info[i].name;
saul_entries[(i * 12) + 7].driver = &sen5x_number_concentration_pm4p0_driver;
/* Number Concentration pm10p0 */
saul_entries[(i * 12) + 8].dev = &(sen5x_devs[i]);
saul_entries[(i * 12) + 8].name = sen5x_saul_info[i].name;
saul_entries[(i * 12) + 8].driver = &sen5x_number_concentration_pm10p0_driver;
/* Typical particle size */
saul_entries[(i * 12) + 9].dev = &(sen5x_devs[i]);
saul_entries[(i * 12) + 9].name = sen5x_saul_info[i].name;
saul_entries[(i * 12) + 9].driver = &sen5x_typical_particle_size_driver;
/* Ambient humidity */
saul_entries[(i * 12) + 10].dev = &(sen5x_devs[i]);
saul_entries[(i * 12) + 10].name = sen5x_saul_info[i].name;
saul_entries[(i * 12) + 10].driver = &sen5x_ambient_humidity_driver;
/* Ambient temperature */
saul_entries[(i * 12) + 11].dev = &(sen5x_devs[i]);
saul_entries[(i * 12) + 11].name = sen5x_saul_info[i].name;
saul_entries[(i * 12) + 11].driver = &sen5x_ambient_temperature_driver;
/* Add register entries to saul */
for (unsigned int j = 0; j < 12; j++) {
saul_reg_add(&(saul_entries[(i * 4) + j]));
}
}
}

11
drivers/sen5x/Kconfig Normal file
View File

@ -0,0 +1,11 @@
# Copyright (c) 2023 TU Braunschweig Institut für Betriebssysteme und Rechnerverbund
#
# 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.
config MODULE_SEN5X
bool "Sensirion Embedded I2C SEN5x Driver"
depends on TEST_KCONFIG
select MODULE_PERIPH_I2C
select MODULE_ZTIMER

1
drivers/sen5x/Makefile Normal file
View File

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

View File

@ -0,0 +1,2 @@
FEATURES_REQUIRED += periph_i2c
USEMODULE += xtimer

View File

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

View File

@ -0,0 +1,37 @@
/*
* Copyright (C) 2023 TU Braunschweig Institut für Betriebssysteme und Rechnerverbund
*
* 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_sen5x
* @{
*
* @file
* @brief Internal addresses, registers and constants
*
* @author Daniel Prigoshij <d.prigoshij@tu-braunschweig.de>
*/
#ifndef SEN5X_CONSTANTS_H
#define SEN5X_CONSTANTS_H
#ifdef __cplusplus
extern "C" {
#endif
/* define here the addresses, register and constants of the driver */
#ifndef SEN5X_I2C_ADDRESS
#define SEN5X_I2C_ADDRESS (0x69)
#endif
#ifdef __cplusplus
}
#endif
#endif /* SEN5X_CONSTANTS_H */
/** @} */

View File

@ -0,0 +1,715 @@
/*
* THIS FILE IS AUTOMATICALLY GENERATED
*
* I2C-Generator: 0.3.0
* Yaml Version: 2.1.3
* Template Version: 0.7.0-109-gb259776
*/
/*
* Copyright (c) 2021, Sensirion AG
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of Sensirion AG nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef SEN5X_I2C_H
#define SEN5X_I2C_H
#ifdef __cplusplus
extern "C" {
#endif
#include "sensirion_config.h"
/**
* sen5x_start_measurement() - Starts a continuous measurement.
*
* After starting the measurement, it takes some time (~1s) until the first
* measurement results are available. You could poll with the command
* 0x0202 \"Read Data Ready\" to check when the results are ready to read.
*
* This command is only available in idle mode. If the device is already
* in any measure mode, this command has no effect.
*
* @return 0 on success, an error code otherwise
*/
int16_t sen5x_start_measurement(void);
/**
* sen5x_start_measurement_without_pm() - Starts a continuous measurement
* without PM. Only humidity, temperature, VOC and NOx are available in this
* mode. Laser and fan are switched off to keep power consumption low.
*
* After starting the measurement, it takes some time (~1s) until the first
* measurement results are available. You could poll with the command
* 0x0202 \"Read Data Ready\" to check when the results are ready to read.
*
* This command is only available in idle mode. If the device is already
* in any measure mode, this command has no effect.
*
* Supported sensors: SEN54, SEN55
*
* @return 0 on success, an error code otherwise
*/
int16_t sen5x_start_measurement_without_pm(void);
/**
* sen5x_stop_measurement() - Stops the measurement and returns to idle mode.
*
* If the device is already in idle mode, this command has no effect.
*
* @return 0 on success, an error code otherwise
*/
int16_t sen5x_stop_measurement(void);
/**
* sen5x_read_data_ready() - This command can be used to check if new
* measurement results are ready to read. The data ready flag is automatically
* reset after reading the measurement values with the 0x03.. \"Read Measured
* Values\" commands.
*
* @note During fan (auto-)cleaning, no measurement data is available for
* several seconds and thus this flag will not be set until cleaning has
* finished. So please expect gaps of several seconds at any time if fan
* auto-cleaning is enabled.
*
* @param padding Padding byte, always 0x00.
*
* @param data_ready True (0x01) if data is ready, False (0x00) if not. When no
* measurement is running, False will be returned.
*
* @return 0 on success, an error code otherwise
*/
int16_t sen5x_read_data_ready(bool* data_ready);
/**
* sen5x_read_measured_values() - Returns the measured values.
*
* The command 0x0202 \"Read Data Ready\" can be used to check if new
* data is available since the last read operation. If no new data is
* available, the previous values will be returned again. If no data is
* available at all (e.g. measurement not running for at least one
* second), all values will be at their upper limit (0xFFFF for `uint16`,
* 0x7FFF for `int16`).
*
* @param mass_concentration_pm1p0 Value is scaled with factor 10:
* PM1.0 [µg/m³] = value / 10
* @note If this value is unknown, 0xFFFF is returned.
*
* @param mass_concentration_pm2p5 Value is scaled with factor 10:
* PM2.5 [µg/m³] = value / 10
* @note If this value is unknown, 0xFFFF is returned.
*
* @param mass_concentration_pm4p0 Value is scaled with factor 10:
* PM4.0 [µg/m³] = value / 10
* @note If this value is unknown, 0xFFFF is returned.
*
* @param mass_concentration_pm10p0 Value is scaled with factor 10:
* PM10.0 [µg/m³] = value / 10
* @note If this value is unknown, 0xFFFF is returned.
*
* @param ambient_humidity Value is scaled with factor 100: RH [%] = value / 100
* @note If this value is unknown, 0x7FFF is returned.*
*
* @param ambient_temperature Value is scaled with factor 200:
* T [°C] = value / 200
* @note If this value is unknown, 0x7FFF is returned.*
*
* @param voc_index Value is scaled with factor 10: VOC Index = value / 10
* @note If this value is unknown, 0x7FFF is returned.*
*
* @param nox_index Value is scaled with factor 10: NOx Index = value / 10
* @note If this value is unknown, 0x7FFF is returned. During
* the first 10..11 seconds after power-on or device reset, this
* value will be 0x7FFF as well.*
*
* @return 0 on success, an error code otherwise
*/
int16_t sen5x_read_measured_values(uint16_t* mass_concentration_pm1p0,
uint16_t* mass_concentration_pm2p5,
uint16_t* mass_concentration_pm4p0,
uint16_t* mass_concentration_pm10p0,
int16_t* ambient_humidity,
int16_t* ambient_temperature,
int16_t* voc_index, int16_t* nox_index);
/**
* sen5x_read_measured_raw_values() - Returns the measured raw values.
The command 0x0202 \"Read Data Ready\" can be used to check if new
data is available since the last read operation. If no new data is
available, the previous values will be returned again. If no data
is available at all (e.g. measurement not running for at least one
second), all values will be at their upper limit (0xFFFF for `uint16`,
0x7FFF for `int16`).
*
* @param raw_humidity Value is scaled with factor 100: RH [%] = value / 100
* @note If this value is unknown, 0x7FFF is returned.
*
* @param raw_temperature Value is scaled with factor 200: T [°C] = value / 200
* @note If this value is unknown, 0x7FFF is returned.
*
* @param raw_voc Raw measured VOC ticks without scale factor.
* @note If this value is unknown, 0xFFFF is returned.
*
* @param raw_nox Raw measured NOx ticks without scale factor.
* @note If this value is unknown, 0xFFFF is returned. During
* the first 10..11 seconds after power-on or device reset, this
* value will be 0xFFFF as well.*
*
* @return 0 on success, an error code otherwise
*/
int16_t sen5x_read_measured_raw_values(int16_t* raw_humidity,
int16_t* raw_temperature,
uint16_t* raw_voc, uint16_t* raw_nox);
/**
* sen5x_read_measured_values_sen50() - Returns the measured values for SEN50.
*
* The command 0x0202 \"Read Data Ready\" can be used to check if new
* data is available since the last read operation. If no new data is
* available, the previous values will be returned again. If no data is
* available at all (e.g. measurement not running for at least one
* second), all values will be at their upper limit (0xFFFF).
*
* @param mass_concentration_pm1p0 Value is scaled with factor 10:
* PM1.0 [µg/m³] = value / 10
* @note If this value is unknown, 0xFFFF is returned.
*
* @param mass_concentration_pm2p5 Value is scaled with factor 10:
* PM2.5 [µg/m³] = value / 10
* @note If this value is unknown, 0xFFFF is returned.
*
* @param mass_concentration_pm4p0 Value is scaled with factor 10:
* PM4.0 [µg/m³] = value / 10
* @note If this value is unknown, 0xFFFF is returned.
*
* @param mass_concentration_pm10p0 Value is scaled with factor 10:
* PM10.0 [µg/m³] = value / 10
* @note If this value is unknown, 0xFFFF is returned.
*
* @return 0 on success, an error code otherwise
*/
int16_t sen5x_read_measured_values_sen50(uint16_t* mass_concentration_pm1p0,
uint16_t* mass_concentration_pm2p5,
uint16_t* mass_concentration_pm4p0,
uint16_t* mass_concentration_pm10p0);
/**
* sen5x_read_measured_pm_values() - Returns the measured particulate matter
* values.
*
* The command 0x0202 \"Read Data Ready\" can be used to check if new
* data is available since the last read operation. If no new data is
* available, the previous values will be returned again. If no data
* is available at all (e.g. measurement not running for at least one
* second), all values will be 0xFFFF.
*
* @param mass_concentration_pm1p0 Value is scaled with factor 10:
* PM1.0 [µg/m³] = value / 10
* @note If this value is unknown, 0xFFFF is returned.
*
* @param mass_concentration_pm2p5 Value is scaled with factor 10:
* PM2.5 [µg/m³] = value / 10
* @note If this value is unknown, 0xFFFF is returned.
*
* @param mass_concentration_pm4p0 Value is scaled with factor 10:
* PM4.0 [µg/m³] = value / 10
* @note If this value is unknown, 0xFFFF is returned.
*
* @param mass_concentration_pm10p0 Value is scaled with factor 10:
* PM10.0 [µg/m³] = value / 10
* @note If this value is unknown, 0xFFFF is returned.
*
* @param number_concentration_pm0p5 Value is scaled with factor 10:
* PM0.5 [#/cm³] = value / 10
* @note If this value is unknown, 0xFFFF is returned.
*
* @param number_concentration_pm1p0 Value is scaled with factor 10:
* PM1.0 [#/cm³] = value / 10
* @note If this value is unknown, 0xFFFF is returned.
*
* @param number_concentration_pm2p5 Value is scaled with factor 10:
* PM2.5 [#/cm³] = value / 10
* @note If this value is unknown, 0xFFFF is returned.
*
* @param number_concentration_pm4p0 Value is scaled with factor 10:
* PM4.0 [#/cm³] = value / 10
* @note If this value is unknown, 0xFFFF is returned.
*
* @param number_concentration_pm10p0 Value is scaled with factor 10:
* PM10.0 [#/cm³] = value / 10
* @note If this value is unknown, 0xFFFF is returned.
*
* @param typical_particle_size Value is scaled with factor 1000:
* Size [µm] = value / 1000
* @note If this value is unknown, 0xFFFF is returned.
*
* @return 0 on success, an error code otherwise
*/
int16_t sen5x_read_measured_pm_values(
uint16_t* mass_concentration_pm1p0, uint16_t* mass_concentration_pm2p5,
uint16_t* mass_concentration_pm4p0, uint16_t* mass_concentration_pm10p0,
uint16_t* number_concentration_pm0p5, uint16_t* number_concentration_pm1p0,
uint16_t* number_concentration_pm2p5, uint16_t* number_concentration_pm4p0,
uint16_t* number_concentration_pm10p0, uint16_t* typical_particle_size);
/**
* sen5x_start_fan_cleaning() - Starts the fan cleaning manually. The \"data
* ready\"-flag will be cleared immediately and during the next few seconds, no
* new measurement results will be available (old values will be returned). Once
* the cleaning is finished, the \"data ready\"-flag will be set and new
* measurement results will be available.
*
* When executing this command while cleaning is already active, the
* command does nothing.
*
* If you stop the measurement while fan cleaning is active, the cleaning
* will be aborted immediately.
*
* @note This command is only available in measure mode with PM measurement
* enabled, i.e. only if the fan is already running. In any other state, this
* command does nothing.
*
* @return 0 on success, an error code otherwise
*/
int16_t sen5x_start_fan_cleaning(void);
/**
* sen5x_set_temperature_offset_parameters() - Sets the temperature offset
* parameters for the device.
*
* Supported sensors: SEN54, SEN55
*
* @param temp_offset Constant temperature offset scaled with factor 200 (T [°C]
* = value / 200). The default value is 0.
*
* @param slope Normalized temperature offset slope scaled with factor 10000
* (applied factor = value / 10000). The default value is 0.
*
* @param time_constant Time constant [s] how fast the new slope and offset will
* be applied. After the specified value in seconds, 63% of the new slope and
* offset are applied. A time constant of zero means the new values will be
* applied immediately (within the next measure interval of 1 second).
*
* @return 0 on success, an error code otherwise
*/
int16_t sen5x_set_temperature_offset_parameters(int16_t temp_offset,
int16_t slope,
uint16_t time_constant);
/**
* sen5x_get_temperature_offset_parameters() - Gets the temperature offset
* parameters from the device.
*
* Supported sensors: SEN54, SEN55
*
* @param temp_offset Constant temperature offset scaled with factor 200 (T [°C]
* = value / 200).
*
* @param slope Normalized temperature offset slope scaled with factor 10000
* (applied factor = value / 10000).
*
* @param time_constant Time constant [s] how fast the slope and offset are
* applied. After the specified value in seconds, 63% of the new slope and
* offset are applied.
*
* @return 0 on success, an error code otherwise
*/
int16_t sen5x_get_temperature_offset_parameters(int16_t* temp_offset,
int16_t* slope,
uint16_t* time_constant);
/**
* sen5x_set_warm_start_parameter() - Sets the warm start parameter for the
* device.
*
* Supported sensors: SEN54, SEN55
*
* @note This parameter can be changed in any state of the device (and the
* getter immediately returns the new value), but it is applied only the next
* time starting a measurement, i.e. when sending a \"Start Measurement\"
* command! So the parameter needs to be set *before* a warm-start measurement
* is started.
*
* @param warm_start Warm start behavior as a value in the range from 0 (cold
* start) to 65535 (warm start). The default value is 0.
*
* @return 0 on success, an error code otherwise
*/
int16_t sen5x_set_warm_start_parameter(uint16_t warm_start);
/**
* sen5x_get_warm_start_parameter() - Gets the warm start parameter from the
* device.
*
* Supported sensors: SEN54, SEN55
*
* @param warm_start Warm start behavior as a value in the range from 0 (cold
* start) to 65535 (warm start).
*
* @return 0 on success, an error code otherwise
*/
int16_t sen5x_get_warm_start_parameter(uint16_t* warm_start);
/**
* sen5x_set_voc_algorithm_tuning_parameters() - Sets the tuning parameters of
* the VOC algorithm.
*
* Supported sensors: SEN54, SEN55
*
* @note This command is available only in idle mode. In measure mode, this
* command has no effect. In addition, it has no effect if at least one
* parameter is outside the specified range.
*
* @param index_offset VOC index representing typical (average) conditions.
* Allowed values are in range 1..250. The default value is 100.
*
* @param learning_time_offset_hours Time constant to estimate the VOC algorithm
* offset from the history in hours. Past events will be forgotten after about
* twice the learning time. Allowed values are in range 1..1000. The default
* value is 12 hours.
*
* @param learning_time_gain_hours Time constant to estimate the VOC algorithm
* gain from the history in hours. Past events will be forgotten after about
* twice the learning time. Allowed values are in range 1..1000. The default
* value is 12 hours.
*
* @param gating_max_duration_minutes Maximum duration of gating in minutes
* (freeze of estimator during high VOC index signal). Set to zero to disable
* the gating. Allowed values are in range 0..3000. The default value is 180
* minutes.
*
* @param std_initial Initial estimate for standard deviation. Lower value
* boosts events during initial learning period, but may result in larger
* device-to-device variations. Allowed values are in range 10..5000. The
* default value is 50.
*
* @param gain_factor Gain factor to amplify or to attenuate the VOC index
* output. Allowed values are in range 1..1000. The default value is 230.
*
* @return 0 on success, an error code otherwise
*/
int16_t sen5x_set_voc_algorithm_tuning_parameters(
int16_t index_offset, int16_t learning_time_offset_hours,
int16_t learning_time_gain_hours, int16_t gating_max_duration_minutes,
int16_t std_initial, int16_t gain_factor);
/**
* sen5x_get_voc_algorithm_tuning_parameters() - Gets the currently set tuning
* parameters of the VOC algorithm.
*
* Supported sensors: SEN54, SEN55
*
* @param index_offset VOC index representing typical (average) conditions.
*
* @param learning_time_offset_hours Time constant to estimate the VOC algorithm
* offset from the history in hours. Past events will be forgotten after about
* twice the learning time.
*
* @param learning_time_gain_hours Time constant to estimate the VOC algorithm
* gain from the history in hours. Past events will be forgotten after about
* twice the learning time.
*
* @param gating_max_duration_minutes Maximum duration of gating in minutes
* (freeze of estimator during high VOC index signal). Zero disables the gating.
*
* @param std_initial Initial estimate for standard deviation. Lower value
* boosts events during initial learning period, but may result in larger
* device-to-device variations.
*
* @param gain_factor Gain factor to amplify or to attenuate the VOC index
* output.
*
* @return 0 on success, an error code otherwise
*/
int16_t sen5x_get_voc_algorithm_tuning_parameters(
int16_t* index_offset, int16_t* learning_time_offset_hours,
int16_t* learning_time_gain_hours, int16_t* gating_max_duration_minutes,
int16_t* std_initial, int16_t* gain_factor);
/**
* sen5x_set_nox_algorithm_tuning_parameters() - Sets the tuning parameters of
* the NOx algorithm.
*
* Supported sensors: SEN55
*
* @note This command is available only in idle mode. In measure mode, this
* command has no effect. In addition, it has no effect if at least one
* parameter is outside the specified range.
*
* @param index_offset NOx index representing typical (average) conditions.
* Allowed values are in range 1..250. The default value is 1.
*
* @param learning_time_offset_hours Time constant to estimate the NOx algorithm
* offset from the history in hours. Past events will be forgotten after about
* twice the learning time. Allowed values are in range 1..1000. The default
* value is 12 hours.
*
* @param learning_time_gain_hours The time constant to estimate the NOx
* algorithm gain from the history has no impact for NOx. This parameter is
* still in place for consistency reasons with the VOC tuning parameters
* command. This parameter must always be set to 12 hours.
*
* @param gating_max_duration_minutes Maximum duration of gating in minutes
* (freeze of estimator during high NOx index signal). Set to zero to disable
* the gating. Allowed values are in range 0..3000. The default value is 720
* minutes.
*
* @param std_initial The initial estimate for standard deviation parameter has
* no impact for NOx. This parameter is still in place for consistency reasons
* with the VOC tuning parameters command. This parameter must always be set
* to 50.
*
* @param gain_factor Gain factor to amplify or to attenuate the NOx index
* output. Allowed values are in range 1..1000. The default value is 230.
*
* @return 0 on success, an error code otherwise
*/
int16_t sen5x_set_nox_algorithm_tuning_parameters(
int16_t index_offset, int16_t learning_time_offset_hours,
int16_t learning_time_gain_hours, int16_t gating_max_duration_minutes,
int16_t std_initial, int16_t gain_factor);
/**
* sen5x_get_nox_algorithm_tuning_parameters() - Gets the currently set tuning
* parameters of the NOx algorithm.
*
* Supported sensors: SEN55
*
* @param index_offset NOx index representing typical (average) conditions.
*
* @param learning_time_offset_hours Time constant to estimate the NOx algorithm
* offset from the history in hours. Past events will be forgotten after about
* twice the learning time.
*
* @param learning_time_gain_hours The time constant to estimate the NOx
* algorithm gain from the history has no impact for NOx. This parameter is
* still in place for consistency reasons with the VOC tuning parameters
* command.
*
* @param gating_max_duration_minutes Maximum duration of gating in minutes
* (freeze of estimator during high NOx index signal). Zero disables the gating.
*
* @param std_initial The initial estimate for standard deviation has no impact
* for NOx. This parameter is still in place for consistency reasons with the
* VOC tuning parameters command.
*
* @param gain_factor Gain factor to amplify or to attenuate the NOx index
* output.
*
* @return 0 on success, an error code otherwise
*/
int16_t sen5x_get_nox_algorithm_tuning_parameters(
int16_t* index_offset, int16_t* learning_time_offset_hours,
int16_t* learning_time_gain_hours, int16_t* gating_max_duration_minutes,
int16_t* std_initial, int16_t* gain_factor);
/**
* sen5x_set_rht_acceleration_mode() - Sets the RH/T acceleration mode.
*
* Supported sensors: SEN54, SEN55
*
* @note This parameter can be changed in any state of the device (and the
* getter immediately returns the new value), but it is applied only the next
* time starting a measurement, i.e. when sending a \"Start Measurement\"
* command. So the parameter needs to be set *before* a new measurement is
* started.
*
* @param mode The new RH/T acceleration mode.
*
* @return 0 on success, an error code otherwise
*/
int16_t sen5x_set_rht_acceleration_mode(uint16_t mode);
/**
* sen5x_get_rht_acceleration_mode() - Gets the RH/T acceleration mode.
*
* Supported sensors: SEN54, SEN55
*
* @param mode The current RH/T acceleration mode.
*
* @return 0 on success, an error code otherwise
*/
int16_t sen5x_get_rht_acceleration_mode(uint16_t* mode);
/**
* sen5x_set_voc_algorithm_state() - Sets the VOC algorithm state previously
* received with the \"Get VOC Algorithm State\" command.
*
* Supported sensors: SEN54, SEN55
*
* @note This command is only available in idle mode and the state will be
* applied only once when starting the next measurement. Any further
* measurements (i.e. when stopping and restarting the measure mode) will reset
* the state to initial values. In measure mode, this command has no effect.
*
* @param state VOC algorithm state to restore.
*
* @return 0 on success, an error code otherwise
*/
int16_t sen5x_set_voc_algorithm_state(const uint8_t* state, uint8_t state_size);
/**
* sen5x_get_voc_algorithm_state() - Gets the current VOC algorithm state. This
* data can be used to restore the state with the \"Set VOC Algorithm State\"
* command after a short power cycle or device reset.
*
* This command can be used either in measure mode or in idle mode
* (which will then return the state at the time when the measurement
* was stopped). In measure mode, the state can be read each measure
* interval to always have the latest state available, even in case of
* a sudden power loss.
*
* Supported sensors: SEN54, SEN55
*
* @param state Current VOC algorithm state.
*
* @return 0 on success, an error code otherwise
*/
int16_t sen5x_get_voc_algorithm_state(uint8_t* state, uint8_t state_size);
/**
* sen5x_set_fan_auto_cleaning_interval() - Sets the fan auto cleaning interval
* for the device.
*
* @param interval Fan auto cleaning interval [s]. Set to zero to disable auto
* cleaning.
*
* @return 0 on success, an error code otherwise
*/
int16_t sen5x_set_fan_auto_cleaning_interval(uint32_t interval);
/**
* sen5x_get_fan_auto_cleaning_interval() - Gets the fan auto cleaning interval
* from the device.
*
* @param interval Fan auto cleaning interval [s]. Zero means auto cleaning is
* disabled.
*
* @return 0 on success, an error code otherwise
*/
int16_t sen5x_get_fan_auto_cleaning_interval(uint32_t* interval);
/**
* sen5x_get_product_name() - Gets the product name from the device.
*
* @param product_name Null-terminated ASCII string containing the product name.
* Up to 32 characters can be read from the device.
*
* @return 0 on success, an error code otherwise
*/
int16_t sen5x_get_product_name(unsigned char* product_name,
uint8_t product_name_size);
/**
* sen5x_get_serial_number() - Gets the serial number from the device.
*
* @param serial_number Null-terminated ASCII string containing the serial
* number. Up to 32 characters can be read from the device.
*
* @return 0 on success, an error code otherwise
*/
int16_t sen5x_get_serial_number(unsigned char* serial_number,
uint8_t serial_number_size);
/**
* sen5x_get_version() - Gets the version information for the hardware, firmware
* and communication protocol.
*
* @param firmware_major Firmware major version number.
*
* @param firmware_minor Firmware minor version number.
*
* @param firmware_debug Firmware debug state. If the debug state is set, the
* firmware is in development.
*
* @param hardware_major Hardware major version number.
*
* @param hardware_minor Hardware minor version number.
*
* @param protocol_major Protocol major version number.
*
* @param protocol_minor Protocol minor version number.
*
* @param padding Padding byte, ignore this.
*
* @return 0 on success, an error code otherwise
*/
int16_t sen5x_get_version(uint8_t* firmware_major, uint8_t* firmware_minor,
bool* firmware_debug, uint8_t* hardware_major,
uint8_t* hardware_minor, uint8_t* protocol_major,
uint8_t* protocol_minor);
/**
* sen5x_read_device_status() - Reads the current device status.
* Use this command to get detailed information about the device status.
* The device status is encoded in flags. Each device status flag
* represents a single bit in a 32-bit integer value. If more than one
* error is present, the device status register value is the sum of the
* corresponding flag values. For details about the available flags,
* refer to the device status flags documentation.
*
* @note The status flags of type \"Error\" are sticky, i.e. they are not
* cleared automatically even if the error condition no longer exists. So they
* can only be cleared manually with the command 0xD210 \"Read And Clear Device
* Status\" or with a device reset. All other flags are not sticky, i.e. they
* are cleared automatically if the trigger condition disappears.
*
* @param device_status Device status (32 flags as an integer value). For
* details, please refer to the device status flags documentation.
*
* @return 0 on success, an error code otherwise
*/
int16_t sen5x_read_device_status(uint32_t* device_status);
/**
* sen5x_read_and_clear_device_status() - Reads the current device status (like
* command 0xD206 \"Read Device Status\") and afterwards clears all flags.
*
* @param device_status Device status (32 flags as an integer value) **before**
* clearing it. For details, please refer to the device status flags
* documentation.
*
* @return 0 on success, an error code otherwise
*/
int16_t sen5x_read_and_clear_device_status(uint32_t* device_status);
/**
* sen5x_device_reset() - Executes a reset on the device. This has the same
* effect as a power cycle.
*
* @return 0 on success, an error code otherwise
*/
int16_t sen5x_device_reset(void);
#ifdef __cplusplus
}
#endif
#endif /* SEN5X_I2C_H */

View File

@ -0,0 +1,76 @@
/*
* Copyright (C) 2023 TU Braunschweig Institut für Betriebssysteme und Rechnerverbund
*
* 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_sen5x
*
* @{
* @file
* @brief Default configuration
*
* @author Daniel Prigoshij <d.prigoshij@tu-braunschweig.de>
*/
#ifndef SEN5X_PARAMS_H
#define SEN5X_PARAMS_H
#include "board.h"
#include "sen5x.h"
#include "sen5x_constants.h"
#include "periph/i2c.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @name Set default configuration parameters for SEN5x sensors
* @{
*/
#ifndef SEN5X_PARAM_I2C_DEV
#define SEN5X_PARAM_I2C_DEV I2C_DEV(0)
#endif
#ifndef SEN5X_PARAM_ADDR
#define SEN5X_PARAM_ADDR SEN5X_I2C_ADDRESS
#endif
#ifndef SEN5X_PARAMS
#define SEN5X_PARAMS { .i2c_dev = SEN5X_PARAM_I2C_DEV, \
.i2c_addr = SEN5X_PARAM_ADDR }
#endif
/**@}*/
/**
* @brief Configure SEN55/54
*/
static const sen5x_params_t sen5x_params[] =
{
SEN5X_PARAMS
};
/**
* @brief Configure SAUL registry entries
*/
static const saul_reg_info_t sen5x_saul_info[] =
{
SEN5X_SAUL_INFO
};
/**
* @brief Get the number of configured SCD30 devices
*/
#define SEN5X_NUM ARRAY_SIZE(sen5x_params)
#ifdef __cplusplus
}
#endif
#endif /* SEN5X_PARAMS_H */
/** @} */

View File

@ -0,0 +1,181 @@
/*
* Copyright (c) 2018, Sensirion AG
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of Sensirion AG nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef SENSIRION_COMMON_H
#define SENSIRION_COMMON_H
#include "sensirion_config.h"
#ifdef __cplusplus
extern "C" {
#endif
#define NO_ERROR 0
#define NOT_IMPLEMENTED_ERROR 31
#ifndef ARRAY_SIZE
#define ARRAY_SIZE(x) (sizeof(x) / sizeof(*(x)))
#endif
#define SENSIRION_COMMAND_SIZE 2
#define SENSIRION_WORD_SIZE 2
#define SENSIRION_NUM_WORDS(x) (sizeof(x) / SENSIRION_WORD_SIZE)
#define SENSIRION_MAX_BUFFER_WORDS 32
/**
* sensirion_common_bytes_to_int16_t() - Convert an array of bytes to an int16_t
*
* Convert an array of bytes received from the sensor in big-endian/MSB-first
* format to an int16_t value in the correct system-endianness.
*
* @param bytes An array of at least two bytes (MSB first)
* @return The byte array represented as int16_t
*/
int16_t sensirion_common_bytes_to_int16_t(const uint8_t* bytes);
/**
* sensirion_common_bytes_to_int32_t() - Convert an array of bytes to an int32_t
*
* Convert an array of bytes received from the sensor in big-endian/MSB-first
* format to an int32_t value in the correct system-endianness.
*
* @param bytes An array of at least four bytes (MSB first)
* @return The byte array represented as int32_t
*/
int32_t sensirion_common_bytes_to_int32_t(const uint8_t* bytes);
/**
* sensirion_common_bytes_to_uint16_t() - Convert an array of bytes to an
* uint16_t
*
* Convert an array of bytes received from the sensor in big-endian/MSB-first
* format to an uint16_t value in the correct system-endianness.
*
* @param bytes An array of at least two bytes (MSB first)
* @return The byte array represented as uint16_t
*/
uint16_t sensirion_common_bytes_to_uint16_t(const uint8_t* bytes);
/**
* sensirion_common_bytes_to_uint32_t() - Convert an array of bytes to an
* uint32_t
*
* Convert an array of bytes received from the sensor in big-endian/MSB-first
* format to an uint32_t value in the correct system-endianness.
*
* @param bytes An array of at least four bytes (MSB first)
* @return The byte array represented as uint32_t
*/
uint32_t sensirion_common_bytes_to_uint32_t(const uint8_t* bytes);
/**
* sensirion_common_bytes_to_float() - Convert an array of bytes to a float
*
* Convert an array of bytes received from the sensor in big-endian/MSB-first
* format to an float value in the correct system-endianness.
*
* @param bytes An array of at least four bytes (MSB first)
* @return The byte array represented as float
*/
float sensirion_common_bytes_to_float(const uint8_t* bytes);
/**
* sensirion_common_uint32_t_to_bytes() - Convert an uint32_t to an array of
* bytes
*
* Convert an uint32_t value in system-endianness to big-endian/MBS-first
* format to send to the sensor.
*
* @param value Value to convert
* @param bytes An array of at least four bytes
*/
void sensirion_common_uint32_t_to_bytes(const uint32_t value, uint8_t* bytes);
/**
* sensirion_common_uint16_t_to_bytes() - Convert an uint16_t to an array of
* bytes
*
* Convert an uint16_t value in system-endianness to big-endian/MBS-first
* format to send to the sensor.
*
* @param value Value to convert
* @param bytes An array of at least two bytes
*/
void sensirion_common_uint16_t_to_bytes(const uint16_t value, uint8_t* bytes);
/**
* sensirion_common_int32_t_to_bytes() - Convert an int32_t to an array of bytes
*
* Convert an int32_t value in system-endianness to big-endian/MBS-first
* format to send to the sensor.
*
* @param value Value to convert
* @param bytes An array of at least four bytes
*/
void sensirion_common_int32_t_to_bytes(const int32_t value, uint8_t* bytes);
/**
* sensirion_common_int16_t_to_bytes() - Convert an int16_t to an array of bytes
*
* Convert an int16_t value in system-endianness to big-endian/MBS-first
* format to send to the sensor.
*
* @param value Value to convert
* @param bytes An array of at least two bytes
*/
void sensirion_common_int16_t_to_bytes(const int16_t value, uint8_t* bytes);
/**
* sensirion_common_float_to_bytes() - Convert an float to an array of bytes
*
* Convert an float value in system-endianness to big-endian/MBS-first
* format to send to the sensor.
*
* @param value Value to convert
* @param bytes An array of at least four bytes
*/
void sensirion_common_float_to_bytes(const float value, uint8_t* bytes);
/**
* sensirion_common_copy_bytes() - Copy bytes from one array to the other.
*
* @param source Array of bytes to be copied.
* @param destination Array of bytes to be copied to.
* @param data_length Number of bytes to copy.
*/
void sensirion_common_copy_bytes(const uint8_t* source, uint8_t* destination,
uint16_t data_length);
#ifdef __cplusplus
}
#endif
#endif /* SENSIRION_COMMON_H */

View File

@ -0,0 +1,88 @@
/*
* Copyright (c) 2019, Sensirion AG
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of Sensirion AG nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef SENSIRION_CONFIG_H
#define SENSIRION_CONFIG_H
/**
* If your platform does not provide the library stdlib.h you have to remove the
* include and define NULL yourself (see below).
*/
#include <stdlib.h>
/**
* #ifndef NULL
* #define NULL ((void *)0)
* #endif
*/
/**
* If your platform does not provide the library stdint.h you have to
* define the integral types yourself (see below).
*/
#include <stdint.h>
/**
* Typedef section for types commonly defined in <stdint.h>
* If your system does not provide stdint headers, please define them
* accordingly. Please make sure to define int64_t and uint64_t.
*/
/* typedef unsigned long long int uint64_t;
* typedef long long int int64_t;
* typedef long int32_t;
* typedef unsigned long uint32_t;
* typedef short int16_t;
* typedef unsigned short uint16_t;
* typedef char int8_t;
* typedef unsigned char uint8_t;
*/
#ifndef __cplusplus
/**
* If your platform doesn't define the bool type we define it as int. Depending
* on your system update the definition below.
*/
#if __STDC_VERSION__ >= 199901L
#include <stdbool.h>
#else
#ifndef bool
#define bool int
#define true 1
#define false 0
#endif /* bool */
#endif /* __STDC_VERSION__ */
#endif /* __cplusplus */
#endif /* SENSIRION_CONFIG_H */

View File

@ -0,0 +1,315 @@
/*
* Copyright (c) 2018, Sensirion AG
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of Sensirion AG nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef SENSIRION_I2C_H
#define SENSIRION_I2C_H
#include "sensirion_config.h"
#ifdef __cplusplus
extern "C" {
#endif
#define CRC_ERROR 1
#define I2C_BUS_ERROR 2
#define I2C_NACK_ERROR 3
#define BYTE_NUM_ERROR 4
#define CRC8_POLYNOMIAL 0x31
#define CRC8_INIT 0xFF
#define CRC8_LEN 1
#define SENSIRION_COMMAND_SIZE 2
#define SENSIRION_WORD_SIZE 2
#define SENSIRION_NUM_WORDS(x) (sizeof(x) / SENSIRION_WORD_SIZE)
#define SENSIRION_MAX_BUFFER_WORDS 32
uint8_t sensirion_i2c_generate_crc(const uint8_t* data, uint16_t count);
int8_t sensirion_i2c_check_crc(const uint8_t* data, uint16_t count,
uint8_t checksum);
/**
* sensirion_i2c_general_call_reset() - Send a general call reset.
*
* @warning This will reset all attached I2C devices on the bus which support
* general call reset.
*
* @return NO_ERROR on success, an error code otherwise
*/
int16_t sensirion_i2c_general_call_reset(void);
/**
* sensirion_i2c_fill_cmd_send_buf() - create the i2c send buffer for a command
* and a set of argument words. The output buffer interleaves argument words
* with their checksums.
* @buf: The generated buffer to send over i2c. Then buffer length must
* be at least SENSIRION_COMMAND_LEN + num_args *
* (SENSIRION_WORD_SIZE + CRC8_LEN).
* @cmd: The i2c command to send. It already includes a checksum.
* @args: The arguments to the command. Can be NULL if none.
* @num_args: The number of word arguments in args.
*
* @return The number of bytes written to buf
*/
uint16_t sensirion_i2c_fill_cmd_send_buf(uint8_t* buf, uint16_t cmd,
const uint16_t* args,
uint8_t num_args);
/**
* sensirion_i2c_read_words() - read data words from sensor
*
* @address: Sensor i2c address
* @data_words: Allocated buffer to store the read words.
* The buffer may also have been modified in case of an error.
* @num_words: Number of data words to read (without CRC bytes)
*
* @return NO_ERROR on success, an error code otherwise
*/
int16_t sensirion_i2c_read_words(uint8_t address, uint16_t* data_words,
uint16_t num_words);
/**
* sensirion_i2c_read_words_as_bytes() - read data words as byte-stream from
* sensor
*
* Read bytes without adjusting values to the uP's word-order.
*
* @address: Sensor i2c address
* @data: Allocated buffer to store the read bytes.
* The buffer may also have been modified in case of an error.
* @num_words: Number of data words(!) to read (without CRC bytes)
* Since only word-chunks can be read from the sensor the size
* is still specified in sensor-words (num_words = num_bytes *
* SENSIRION_WORD_SIZE)
*
* @return NO_ERROR on success, an error code otherwise
*/
int16_t sensirion_i2c_read_words_as_bytes(uint8_t address, uint8_t* data,
uint16_t num_words);
/**
* sensirion_i2c_write_cmd() - writes a command to the sensor
* @address: Sensor i2c address
* @command: Sensor command
*
* @return NO_ERROR on success, an error code otherwise
*/
int16_t sensirion_i2c_write_cmd(uint8_t address, uint16_t command);
/**
* sensirion_i2c_write_cmd_with_args() - writes a command with arguments to the
* sensor
* @address: Sensor i2c address
* @command: Sensor command
* @data: Argument buffer with words to send
* @num_words: Number of data words to send (without CRC bytes)
*
* @return NO_ERROR on success, an error code otherwise
*/
int16_t sensirion_i2c_write_cmd_with_args(uint8_t address, uint16_t command,
const uint16_t* data_words,
uint16_t num_words);
/**
* sensirion_i2c_delayed_read_cmd() - send a command, wait for the sensor to
* process and read data back
* @address: Sensor i2c address
* @cmd: Command
* @delay: Time in microseconds to delay sending the read request
* @data_words: Allocated buffer to store the read data
* @num_words: Data words to read (without CRC bytes)
*
* @return NO_ERROR on success, an error code otherwise
*/
int16_t sensirion_i2c_delayed_read_cmd(uint8_t address, uint16_t cmd,
uint32_t delay_us, uint16_t* data_words,
uint16_t num_words);
/**
* sensirion_i2c_read_cmd() - reads data words from the sensor after a command
* is issued
* @address: Sensor i2c address
* @cmd: Command
* @data_words: Allocated buffer to store the read data
* @num_words: Data words to read (without CRC bytes)
*
* @return NO_ERROR on success, an error code otherwise
*/
int16_t sensirion_i2c_read_cmd(uint8_t address, uint16_t cmd,
uint16_t* data_words, uint16_t num_words);
/**
* sensirion_i2c_add_command_to_buffer() - Add a command to the buffer at
* offset. Adds 2 bytes to the buffer.
*
* @param buffer Pointer to buffer in which the write frame will be prepared.
* Caller needs to make sure that there is enough space after
* offset left to write the data into the buffer.
* @param offset Offset of the next free byte in the buffer.
* @param command Command to be written into the buffer.
*
* @return Offset of next free byte in the buffer after writing the data.
*/
uint16_t sensirion_i2c_add_command_to_buffer(uint8_t* buffer, uint16_t offset,
uint16_t command);
/**
* sensirion_i2c_add_uint32_t_to_buffer() - Add a uint32_t to the buffer at
* offset. Adds 6 bytes to the buffer.
*
* @param buffer Pointer to buffer in which the write frame will be prepared.
* Caller needs to make sure that there is enough space after
* offset left to write the data into the buffer.
* @param offset Offset of the next free byte in the buffer.
* @param data uint32_t to be written into the buffer.
*
* @return Offset of next free byte in the buffer after writing the data.
*/
uint16_t sensirion_i2c_add_uint32_t_to_buffer(uint8_t* buffer, uint16_t offset,
uint32_t data);
/**
* sensirion_i2c_add_int32_t_to_buffer() - Add a int32_t to the buffer at
* offset. Adds 6 bytes to the buffer.
*
* @param buffer Pointer to buffer in which the write frame will be prepared.
* Caller needs to make sure that there is enough space after
* offset left to write the data into the buffer.
* @param offset Offset of the next free byte in the buffer.
* @param data int32_t to be written into the buffer.
*
* @return Offset of next free byte in the buffer after writing the data.
*/
uint16_t sensirion_i2c_add_int32_t_to_buffer(uint8_t* buffer, uint16_t offset,
int32_t data);
/**
* sensirion_i2c_add_uint16_t_to_buffer() - Add a uint16_t to the buffer at
* offset. Adds 3 bytes to the buffer.
*
* @param buffer Pointer to buffer in which the write frame will be prepared.
* Caller needs to make sure that there is enough space after
* offset left to write the data into the buffer.
* @param offset Offset of the next free byte in the buffer.
* @param data uint16_t to be written into the buffer.
*
* @return Offset of next free byte in the buffer after writing the data.
*/
uint16_t sensirion_i2c_add_uint16_t_to_buffer(uint8_t* buffer, uint16_t offset,
uint16_t data);
/**
* sensirion_i2c_add_int16_t_to_buffer() - Add a int16_t to the buffer at
* offset. Adds 3 bytes to the buffer.
*
* @param buffer Pointer to buffer in which the write frame will be prepared.
* Caller needs to make sure that there is enough space after
* offset left to write the data into the buffer.
* @param offset Offset of the next free byte in the buffer.
* @param data int16_t to be written into the buffer.
*
* @return Offset of next free byte in the buffer after writing the data.
*/
uint16_t sensirion_i2c_add_int16_t_to_buffer(uint8_t* buffer, uint16_t offset,
int16_t data);
/**
* sensirion_i2c_add_float_to_buffer() - Add a float to the buffer at offset.
* Adds 6 bytes to the buffer.
*
* @param buffer Pointer to buffer in which the write frame will be prepared.
* Caller needs to make sure that there is enough space after
* offset left to write the data into the buffer.
* @param offset Offset of the next free byte in the buffer.
* @param data float to be written into the buffer.
*
* @return Offset of next free byte in the buffer after writing the data.
*/
uint16_t sensirion_i2c_add_float_to_buffer(uint8_t* buffer, uint16_t offset,
float data);
/**
* sensirion_i2c_add_bytes_to_buffer() - Add a byte array to the buffer at
* offset.
*
* @param buffer Pointer to buffer in which the write frame will be
* prepared. Caller needs to make sure that there is
* enough space after offset left to write the data
* into the buffer.
* @param offset Offset of the next free byte in the buffer.
* @param data Pointer to data to be written into the buffer.
* @param data_length Number of bytes to be written into the buffer. Needs to
* be a multiple of SENSIRION_WORD_SIZE otherwise the
* function returns BYTE_NUM_ERROR.
*
* @return Offset of next free byte in the buffer after writing the
* data.
*/
uint16_t sensirion_i2c_add_bytes_to_buffer(uint8_t* buffer, uint16_t offset,
const uint8_t* data,
uint16_t data_length);
/**
* sensirion_i2c_write_data() - Writes data to the Sensor.
*
* @note This is just a wrapper for sensirion_i2c_hal_write() to
* not need to include the HAL in the drivers.
*
* @param address I2C address to write to.
* @param data Pointer to the buffer containing the data to write.
* @param data_length Number of bytes to send to the Sensor.
*
* @return NO_ERROR on success, error code otherwise
*/
int16_t sensirion_i2c_write_data(uint8_t address, const uint8_t* data,
uint16_t data_length);
/**
* sensirion_i2c_read_data_inplace() - Reads data from the Sensor.
*
* @param address Sensor I2C address
* @param buffer Allocated buffer to store data as bytes. Needs
* to be big enough to store the data including
* CRC. Twice the size of data should always
* suffice.
* @param expected_data_length Number of bytes to read (without CRC). Needs
* to be a multiple of SENSIRION_WORD_SIZE,
* otherwise the function returns BYTE_NUM_ERROR.
*
* @return NO_ERROR on success, an error code otherwise
*/
int16_t sensirion_i2c_read_data_inplace(uint8_t address, uint8_t* buffer,
uint16_t expected_data_length);
#ifdef __cplusplus
}
#endif
#endif /* SENSIRION_I2C_H */

View File

@ -0,0 +1,103 @@
/*
* Copyright (c) 2018, Sensirion AG
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of Sensirion AG nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef SENSIRION_I2C_HAL_H
#define SENSIRION_I2C_HAL_H
#include "sensirion_config.h"
#include "sen5x_params.h"
#include "xtimer.h"
#include "periph/i2c.h"
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
/**
* Initialize all hard- and software components that are needed for the I2C
* communication.
*/
void sensirion_i2c_hal_init(void);
/**
* Release all resources initialized by sensirion_i2c_hal_init().
*/
void sensirion_i2c_hal_free(void);
/**
* Execute one read transaction on the I2C bus, reading a given number of bytes.
* If the device does not acknowledge the read command, an error shall be
* returned.
*
* @param address 7-bit I2C address to read from
* @param data pointer to the buffer where the data is to be stored
* @param count number of bytes to read from I2C and store in the buffer
* @returns 0 on success, error code otherwise
*/
int8_t sensirion_i2c_hal_read(uint8_t address, uint8_t* data, uint16_t count);
/**
* Execute one write transaction on the I2C bus, sending a given number of
* bytes. The bytes in the supplied buffer must be sent to the given address. If
* the slave device does not acknowledge any of the bytes, an error shall be
* returned.
*
* @param address 7-bit I2C address to write to
* @param data pointer to the buffer containing the data to write
* @param count number of bytes to read from the buffer and send over I2C
* @returns 0 on success, error code otherwise
*/
int8_t sensirion_i2c_hal_write(uint8_t address, const uint8_t* data,
uint16_t count);
/**
* Sleep for a given number of microseconds. The function should delay the
* execution approximately, but no less than, the given time.
*
* When using hardware i2c:
* Despite the unit, a <10 millisecond precision is sufficient.
*
* When using software i2c:
* The precision needed depends on the desired i2c frequency, i.e. should be
* exact to about half a clock cycle (defined in
* `SENSIRION_I2C_CLOCK_PERIOD_USEC` in `sensirion_sw_i2c_gpio.h`).
*
* Example with 400kHz requires a precision of 1 / (2 * 400kHz) == 1.25usec.
*
* @param useconds the sleep time in microseconds
*/
void sensirion_i2c_hal_sleep_usec(uint32_t useconds);
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif /* SENSIRION_I2C_HAL_H */

263
drivers/sen5x/sen5x.c Normal file
View File

@ -0,0 +1,263 @@
/*
* Copyright (C) 2023 TU Braunschweig Institut für Betriebssysteme und Rechnerverbund
*
* 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_sen5x
* @{
*
* @file
* @brief Device driver implementation for the Sensirion Embedded I2C SEN5x Driver
*
* @author Daniel Prigoshij <d.prigoshij@tu-braunschweig.de>
*
* @}
*/
#include "sen5x.h"
#include "sen5x_constants.h"
#include "sen5x_params.h"
#include "sensirion_i2c_hal.h"
#include "sen5x_i2c.h"
void sen5x_init(sen5x_t *dev, const sen5x_params_t *params)
{
/* check parameters */
assert(dev && params);
dev->params = *params;
i2c_init(dev->params.i2c_dev);
sen5x_reset(dev);
}
void sen5x_reset(const sen5x_t *dev)
{
assert(dev);
i2c_acquire(dev->params.i2c_dev);
sen5x_device_reset();
i2c_release(dev->params.i2c_dev);
}
void sen5x_wake(const sen5x_t *dev)
{
assert(dev);
i2c_acquire(dev->params.i2c_dev);
sen5x_start_measurement();
i2c_release(dev->params.i2c_dev);
}
void sen5x_sleep(const sen5x_t *dev)
{
assert(dev);
i2c_acquire(dev->params.i2c_dev);
sen5x_stop_measurement();
i2c_release(dev->params.i2c_dev);
}
void sen5x_clean_fan(const sen5x_t *dev)
{
assert(dev);
i2c_acquire(dev->params.i2c_dev);
sen5x_start_fan_cleaning();
i2c_release(dev->params.i2c_dev);
}
bool sen5x_data_ready_flag(const sen5x_t *dev)
{
assert(dev && status);
i2c_acquire(dev->params.i2c_dev);
bool* status;
sen5x_read_data_ready(&status);
i2c_release(dev->params.i2c_dev);
return status;
}
void sen5x_read_values(sen5x_t *dev ,sen5x_measurement_t *values)
{
assert(dev && values);
i2c_acquire(dev->params.i2c_dev);
sen5x_read_measured_values(
&values->mass_concentration_pm1p0, &values->mass_concentration_pm2p5,
&values->mass_concentration_pm4p0, &values->mass_concentration_pm10p0,
&values->ambient_humidity, &values->ambient_temperature, &values->voc_index,
&values->nox_index);
i2c_release(dev->params.i2c_dev);
}
void sen5x_read_pm_values(const sen5x_t *dev, sen5x_measurement_t *values)
{
assert(dev && values);
i2c_acquire(dev->params.i2c_dev);
sen5x_read_measured_pm_values(
&values->mass_concentration_pm1p0, &values->mass_concentration_pm2p5,
&values->mass_concentration_pm4p0, &values->mass_concentration_pm10p0,
&values->number_concentration_pm0p5, &values->number_concentration_pm1p0,
&values->number_concentration_pm2p5, &values->number_concentration_pm4p0,
&values->number_concentration_pm10p0, &values->typical_particle_size);
i2c_release(dev->params.i2c_dev);
}
void sen5x_set_temperature_offset(const sen5x_t *dev, int16_t temp_offset, int16_t slope, uint16_t time_constant)
{
assert(dev);
i2c_acquire(dev->params.i2c_dev);
sen5x_set_temperature_offset_parameters(temp_offset, slope, time_constant);
i2c_release(dev->params.i2c_dev);
}
void sen5x_get_temperature_offset(const sen5x_t *dev, int16_t *temp_offset, int16_t *slope, uint16_t *time_constant)
{
assert(dev && temp_offset && slope && time_constant);
i2c_acquire(dev->params.i2c_dev);
sen5x_get_temperature_offset_parameters(temp_offset, slope, time_constant);
i2c_release(dev->params.i2c_dev);
}
void sen5x_set_warm_start(const sen5x_t *dev, uint16_t warm_start)
{
assert(dev);
i2c_acquire(dev->params.i2c_dev);
sen5x_set_warm_start_parameter(warm_start);
i2c_release(dev->params.i2c_dev);
}
void sen5x_get_warm_start(const sen5x_t *dev, uint16_t *warm_start)
{
assert(dev && warm_start);
i2c_acquire(dev->params.i2c_dev);
sen5x_get_warm_start_parameter(warm_start);
i2c_release(dev->params.i2c_dev);
}
void sen5x_set_voc_algorithm_tuning(
const sen5x_t *dev, int16_t index_offset, int16_t learning_time_offset_hours,
int16_t learning_time_gain_hours, int16_t gating_max_duration_minutes,
int16_t std_initial, int16_t gain_factor)
{
assert(dev);
i2c_acquire(dev->params.i2c_dev);
sen5x_set_voc_algorithm_tuning_parameters(
index_offset, learning_time_offset_hours,
learning_time_gain_hours, gating_max_duration_minutes,
std_initial, gain_factor);
i2c_release(dev->params.i2c_dev);
}
void sen5x_get_voc_algorithm_tuning(
const sen5x_t *dev, int16_t *index_offset, int16_t *learning_time_offset_hours,
int16_t *learning_time_gain_hours, int16_t *gating_max_duration_minutes,
int16_t *std_initial, int16_t *gain_factor)
{
assert(dev && index_offset && learning_time_offset_hours && learning_time_gain_hours
&& gating_max_duration_minutes && std_initial && gain_factor);
i2c_acquire(dev->params.i2c_dev);
sen5x_get_voc_algorithm_tuning_parameters(
index_offset, learning_time_offset_hours,
learning_time_gain_hours, gating_max_duration_minutes,
std_initial, gain_factor);
i2c_release(dev->params.i2c_dev);
}
void sen5x_set_nox_algorithm_tuning(
const sen5x_t *dev, int16_t index_offset, int16_t learning_time_offset_hours,
int16_t learning_time_gain_hours, int16_t gating_max_duration_minutes,
int16_t std_initial, int16_t gain_factor)
{
assert(dev);
i2c_acquire(dev->params.i2c_dev);
sen5x_set_nox_algorithm_tuning_parameters(
index_offset, learning_time_offset_hours,
learning_time_gain_hours, gating_max_duration_minutes,
std_initial, gain_factor);
i2c_release(dev->params.i2c_dev);
}
void sen5x_get_nox_algorithm_tuning(
const sen5x_t *dev, int16_t *index_offset, int16_t *learning_time_offset_hours,
int16_t *learning_time_gain_hours, int16_t *gating_max_duration_minutes,
int16_t *std_initial, int16_t *gain_factor)
{
assert(dev && index_offset && learning_time_offset_hours && learning_time_gain_hours
&& gating_max_duration_minutes && std_initial && gain_factor);
i2c_acquire(dev->params.i2c_dev);
sen5x_get_nox_algorithm_tuning_parameters(
index_offset, learning_time_offset_hours,
learning_time_gain_hours, gating_max_duration_minutes,
std_initial, gain_factor);
i2c_release(dev->params.i2c_dev);
}
void sen5x_set_rht_acceleration(const sen5x_t *dev, uint16_t mode)
{
assert(dev);
i2c_acquire(dev->params.i2c_dev);
sen5x_set_rht_acceleration_mode(mode);
i2c_release(dev->params.i2c_dev);
}
void sen5x_get_rht_acceleration(const sen5x_t *dev, uint16_t *mode)
{
assert(dev && mode);
i2c_acquire(dev->params.i2c_dev);
sen5x_get_rht_acceleration_mode(mode);
i2c_release(dev->params.i2c_dev);
}
void sen5x_set_voc_state(const sen5x_t *dev, const uint8_t *state, uint8_t state_size)
{
assert(dev && state);
i2c_acquire(dev->params.i2c_dev);
sen5x_set_voc_algorithm_state(state, state_size);
i2c_release(dev->params.i2c_dev);
}
void sen5x_get_voc_state(const sen5x_t *dev, uint8_t *state, uint8_t state_size)
{
assert(dev && state);
i2c_acquire(dev->params.i2c_dev);
sen5x_get_voc_algorithm_state(state, state_size);
i2c_release(dev->params.i2c_dev);
}

690
drivers/sen5x/sen5x_i2c.c Normal file
View File

@ -0,0 +1,690 @@
/*
* THIS FILE IS AUTOMATICALLY GENERATED
*
* I2C-Generator: 0.3.0
* Yaml Version: 2.1.3
* Template Version: 0.7.0-109-gb259776
*/
/*
* Copyright (c) 2021, Sensirion AG
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of Sensirion AG nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#include "sen5x_i2c.h"
#include "sensirion_common.h"
#include "sensirion_i2c.h"
#include "sensirion_i2c_hal.h"
#define SEN5X_I2C_ADDRESS 0x69
int16_t sen5x_start_measurement(void) {
int16_t error;
uint8_t buffer[2];
uint16_t offset = 0;
offset = sensirion_i2c_add_command_to_buffer(&buffer[0], offset, 0x21);
error = sensirion_i2c_write_data(SEN5X_I2C_ADDRESS, &buffer[0], offset);
if (error) {
return error;
}
sensirion_i2c_hal_sleep_usec(50000);
return NO_ERROR;
}
int16_t sen5x_start_measurement_without_pm(void) {
int16_t error;
uint8_t buffer[2];
uint16_t offset = 0;
offset = sensirion_i2c_add_command_to_buffer(&buffer[0], offset, 0x37);
error = sensirion_i2c_write_data(SEN5X_I2C_ADDRESS, &buffer[0], offset);
if (error) {
return error;
}
sensirion_i2c_hal_sleep_usec(50000);
return NO_ERROR;
}
int16_t sen5x_stop_measurement(void) {
int16_t error;
uint8_t buffer[2];
uint16_t offset = 0;
offset = sensirion_i2c_add_command_to_buffer(&buffer[0], offset, 0x104);
error = sensirion_i2c_write_data(SEN5X_I2C_ADDRESS, &buffer[0], offset);
if (error) {
return error;
}
sensirion_i2c_hal_sleep_usec(200000);
return NO_ERROR;
}
int16_t sen5x_read_data_ready(bool* data_ready) {
int16_t error;
uint8_t buffer[3];
uint16_t offset = 0;
offset = sensirion_i2c_add_command_to_buffer(&buffer[0], offset, 0x202);
error = sensirion_i2c_write_data(SEN5X_I2C_ADDRESS, &buffer[0], offset);
if (error) {
return error;
}
sensirion_i2c_hal_sleep_usec(20000);
error = sensirion_i2c_read_data_inplace(SEN5X_I2C_ADDRESS, &buffer[0], 2);
if (error) {
return error;
}
*data_ready = buffer[1];
return NO_ERROR;
}
int16_t sen5x_read_measured_values(uint16_t* mass_concentration_pm1p0,
uint16_t* mass_concentration_pm2p5,
uint16_t* mass_concentration_pm4p0,
uint16_t* mass_concentration_pm10p0,
int16_t* ambient_humidity,
int16_t* ambient_temperature,
int16_t* voc_index, int16_t* nox_index) {
int16_t error;
uint8_t buffer[24];
uint16_t offset = 0;
offset = sensirion_i2c_add_command_to_buffer(&buffer[0], offset, 0x3C4);
error = sensirion_i2c_write_data(SEN5X_I2C_ADDRESS, &buffer[0], offset);
if (error) {
return error;
}
sensirion_i2c_hal_sleep_usec(20000);
error = sensirion_i2c_read_data_inplace(SEN5X_I2C_ADDRESS, &buffer[0], 16);
if (error) {
return error;
}
*mass_concentration_pm1p0 = sensirion_common_bytes_to_uint16_t(&buffer[0]);
*mass_concentration_pm2p5 = sensirion_common_bytes_to_uint16_t(&buffer[2]);
*mass_concentration_pm4p0 = sensirion_common_bytes_to_uint16_t(&buffer[4]);
*mass_concentration_pm10p0 = sensirion_common_bytes_to_uint16_t(&buffer[6]);
*ambient_humidity = sensirion_common_bytes_to_int16_t(&buffer[8]);
*ambient_temperature = sensirion_common_bytes_to_int16_t(&buffer[10]);
*voc_index = sensirion_common_bytes_to_int16_t(&buffer[12]);
*nox_index = sensirion_common_bytes_to_int16_t(&buffer[14]);
return NO_ERROR;
}
int16_t sen5x_read_measured_raw_values(int16_t* raw_humidity,
int16_t* raw_temperature,
uint16_t* raw_voc, uint16_t* raw_nox) {
int16_t error;
uint8_t buffer[12];
uint16_t offset = 0;
offset = sensirion_i2c_add_command_to_buffer(&buffer[0], offset, 0x3D2);
error = sensirion_i2c_write_data(SEN5X_I2C_ADDRESS, &buffer[0], offset);
if (error) {
return error;
}
sensirion_i2c_hal_sleep_usec(20000);
error = sensirion_i2c_read_data_inplace(SEN5X_I2C_ADDRESS, &buffer[0], 8);
if (error) {
return error;
}
*raw_humidity = sensirion_common_bytes_to_int16_t(&buffer[0]);
*raw_temperature = sensirion_common_bytes_to_int16_t(&buffer[2]);
*raw_voc = sensirion_common_bytes_to_uint16_t(&buffer[4]);
*raw_nox = sensirion_common_bytes_to_uint16_t(&buffer[6]);
return NO_ERROR;
}
int16_t sen5x_read_measured_values_sen50(uint16_t* mass_concentration_pm1p0,
uint16_t* mass_concentration_pm2p5,
uint16_t* mass_concentration_pm4p0,
uint16_t* mass_concentration_pm10p0) {
int16_t error;
int16_t ambient_humidity_dummy;
int16_t ambient_temperature_dummy;
int16_t voc_index_dummy;
int16_t nox_index_dummy;
error = sen5x_read_measured_values(
mass_concentration_pm1p0, mass_concentration_pm2p5,
mass_concentration_pm4p0, mass_concentration_pm10p0,
&ambient_humidity_dummy, &ambient_temperature_dummy, &voc_index_dummy,
&nox_index_dummy);
return error;
}
int16_t sen5x_read_measured_pm_values(
uint16_t* mass_concentration_pm1p0, uint16_t* mass_concentration_pm2p5,
uint16_t* mass_concentration_pm4p0, uint16_t* mass_concentration_pm10p0,
uint16_t* number_concentration_pm0p5, uint16_t* number_concentration_pm1p0,
uint16_t* number_concentration_pm2p5, uint16_t* number_concentration_pm4p0,
uint16_t* number_concentration_pm10p0, uint16_t* typical_particle_size) {
int16_t error;
uint8_t buffer[30];
uint16_t offset = 0;
offset = sensirion_i2c_add_command_to_buffer(&buffer[0], offset, 0x413);
error = sensirion_i2c_write_data(SEN5X_I2C_ADDRESS, &buffer[0], offset);
if (error) {
return error;
}
sensirion_i2c_hal_sleep_usec(20000);
error = sensirion_i2c_read_data_inplace(SEN5X_I2C_ADDRESS, &buffer[0], 20);
if (error) {
return error;
}
*mass_concentration_pm1p0 = sensirion_common_bytes_to_uint16_t(&buffer[0]);
*mass_concentration_pm2p5 = sensirion_common_bytes_to_uint16_t(&buffer[2]);
*mass_concentration_pm4p0 = sensirion_common_bytes_to_uint16_t(&buffer[4]);
*mass_concentration_pm10p0 = sensirion_common_bytes_to_uint16_t(&buffer[6]);
*number_concentration_pm0p5 =
sensirion_common_bytes_to_uint16_t(&buffer[8]);
*number_concentration_pm1p0 =
sensirion_common_bytes_to_uint16_t(&buffer[10]);
*number_concentration_pm2p5 =
sensirion_common_bytes_to_uint16_t(&buffer[12]);
*number_concentration_pm4p0 =
sensirion_common_bytes_to_uint16_t(&buffer[14]);
*number_concentration_pm10p0 =
sensirion_common_bytes_to_uint16_t(&buffer[16]);
*typical_particle_size = sensirion_common_bytes_to_uint16_t(&buffer[18]);
return NO_ERROR;
}
int16_t sen5x_start_fan_cleaning(void) {
int16_t error;
uint8_t buffer[2];
uint16_t offset = 0;
offset = sensirion_i2c_add_command_to_buffer(&buffer[0], offset, 0x5607);
error = sensirion_i2c_write_data(SEN5X_I2C_ADDRESS, &buffer[0], offset);
if (error) {
return error;
}
sensirion_i2c_hal_sleep_usec(20000);
return NO_ERROR;
}
int16_t sen5x_set_temperature_offset_parameters(int16_t temp_offset,
int16_t slope,
uint16_t time_constant) {
int16_t error;
uint8_t buffer[11];
uint16_t offset = 0;
offset = sensirion_i2c_add_command_to_buffer(&buffer[0], offset, 0x60B2);
offset =
sensirion_i2c_add_int16_t_to_buffer(&buffer[0], offset, temp_offset);
offset = sensirion_i2c_add_int16_t_to_buffer(&buffer[0], offset, slope);
offset =
sensirion_i2c_add_uint16_t_to_buffer(&buffer[0], offset, time_constant);
error = sensirion_i2c_write_data(SEN5X_I2C_ADDRESS, &buffer[0], offset);
if (error) {
return error;
}
sensirion_i2c_hal_sleep_usec(20000);
return NO_ERROR;
}
int16_t sen5x_get_temperature_offset_parameters(int16_t* temp_offset,
int16_t* slope,
uint16_t* time_constant) {
int16_t error;
uint8_t buffer[9];
uint16_t offset = 0;
offset = sensirion_i2c_add_command_to_buffer(&buffer[0], offset, 0x60B2);
error = sensirion_i2c_write_data(SEN5X_I2C_ADDRESS, &buffer[0], offset);
if (error) {
return error;
}
sensirion_i2c_hal_sleep_usec(20000);
error = sensirion_i2c_read_data_inplace(SEN5X_I2C_ADDRESS, &buffer[0], 6);
if (error) {
return error;
}
*temp_offset = sensirion_common_bytes_to_int16_t(&buffer[0]);
*slope = sensirion_common_bytes_to_int16_t(&buffer[2]);
*time_constant = sensirion_common_bytes_to_uint16_t(&buffer[4]);
return NO_ERROR;
}
int16_t sen5x_set_warm_start_parameter(uint16_t warm_start) {
int16_t error;
uint8_t buffer[5];
uint16_t offset = 0;
offset = sensirion_i2c_add_command_to_buffer(&buffer[0], offset, 0x60C6);
offset =
sensirion_i2c_add_uint16_t_to_buffer(&buffer[0], offset, warm_start);
error = sensirion_i2c_write_data(SEN5X_I2C_ADDRESS, &buffer[0], offset);
if (error) {
return error;
}
sensirion_i2c_hal_sleep_usec(20000);
return NO_ERROR;
}
int16_t sen5x_get_warm_start_parameter(uint16_t* warm_start) {
int16_t error;
uint8_t buffer[3];
uint16_t offset = 0;
offset = sensirion_i2c_add_command_to_buffer(&buffer[0], offset, 0x60C6);
error = sensirion_i2c_write_data(SEN5X_I2C_ADDRESS, &buffer[0], offset);
if (error) {
return error;
}
sensirion_i2c_hal_sleep_usec(20000);
error = sensirion_i2c_read_data_inplace(SEN5X_I2C_ADDRESS, &buffer[0], 2);
if (error) {
return error;
}
*warm_start = sensirion_common_bytes_to_uint16_t(&buffer[0]);
return NO_ERROR;
}
int16_t sen5x_set_voc_algorithm_tuning_parameters(
int16_t index_offset, int16_t learning_time_offset_hours,
int16_t learning_time_gain_hours, int16_t gating_max_duration_minutes,
int16_t std_initial, int16_t gain_factor) {
int16_t error;
uint8_t buffer[20];
uint16_t offset = 0;
offset = sensirion_i2c_add_command_to_buffer(&buffer[0], offset, 0x60D0);
offset =
sensirion_i2c_add_int16_t_to_buffer(&buffer[0], offset, index_offset);
offset = sensirion_i2c_add_int16_t_to_buffer(&buffer[0], offset,
learning_time_offset_hours);
offset = sensirion_i2c_add_int16_t_to_buffer(&buffer[0], offset,
learning_time_gain_hours);
offset = sensirion_i2c_add_int16_t_to_buffer(&buffer[0], offset,
gating_max_duration_minutes);
offset =
sensirion_i2c_add_int16_t_to_buffer(&buffer[0], offset, std_initial);
offset =
sensirion_i2c_add_int16_t_to_buffer(&buffer[0], offset, gain_factor);
error = sensirion_i2c_write_data(SEN5X_I2C_ADDRESS, &buffer[0], offset);
if (error) {
return error;
}
sensirion_i2c_hal_sleep_usec(20000);
return NO_ERROR;
}
int16_t sen5x_get_voc_algorithm_tuning_parameters(
int16_t* index_offset, int16_t* learning_time_offset_hours,
int16_t* learning_time_gain_hours, int16_t* gating_max_duration_minutes,
int16_t* std_initial, int16_t* gain_factor) {
int16_t error;
uint8_t buffer[18];
uint16_t offset = 0;
offset = sensirion_i2c_add_command_to_buffer(&buffer[0], offset, 0x60D0);
error = sensirion_i2c_write_data(SEN5X_I2C_ADDRESS, &buffer[0], offset);
if (error) {
return error;
}
sensirion_i2c_hal_sleep_usec(20000);
error = sensirion_i2c_read_data_inplace(SEN5X_I2C_ADDRESS, &buffer[0], 12);
if (error) {
return error;
}
*index_offset = sensirion_common_bytes_to_int16_t(&buffer[0]);
*learning_time_offset_hours = sensirion_common_bytes_to_int16_t(&buffer[2]);
*learning_time_gain_hours = sensirion_common_bytes_to_int16_t(&buffer[4]);
*gating_max_duration_minutes =
sensirion_common_bytes_to_int16_t(&buffer[6]);
*std_initial = sensirion_common_bytes_to_int16_t(&buffer[8]);
*gain_factor = sensirion_common_bytes_to_int16_t(&buffer[10]);
return NO_ERROR;
}
int16_t sen5x_set_nox_algorithm_tuning_parameters(
int16_t index_offset, int16_t learning_time_offset_hours,
int16_t learning_time_gain_hours, int16_t gating_max_duration_minutes,
int16_t std_initial, int16_t gain_factor) {
int16_t error;
uint8_t buffer[20];
uint16_t offset = 0;
offset = sensirion_i2c_add_command_to_buffer(&buffer[0], offset, 0x60E1);
offset =
sensirion_i2c_add_int16_t_to_buffer(&buffer[0], offset, index_offset);
offset = sensirion_i2c_add_int16_t_to_buffer(&buffer[0], offset,
learning_time_offset_hours);
offset = sensirion_i2c_add_int16_t_to_buffer(&buffer[0], offset,
learning_time_gain_hours);
offset = sensirion_i2c_add_int16_t_to_buffer(&buffer[0], offset,
gating_max_duration_minutes);
offset =
sensirion_i2c_add_int16_t_to_buffer(&buffer[0], offset, std_initial);
offset =
sensirion_i2c_add_int16_t_to_buffer(&buffer[0], offset, gain_factor);
error = sensirion_i2c_write_data(SEN5X_I2C_ADDRESS, &buffer[0], offset);
if (error) {
return error;
}
sensirion_i2c_hal_sleep_usec(20000);
return NO_ERROR;
}
int16_t sen5x_get_nox_algorithm_tuning_parameters(
int16_t* index_offset, int16_t* learning_time_offset_hours,
int16_t* learning_time_gain_hours, int16_t* gating_max_duration_minutes,
int16_t* std_initial, int16_t* gain_factor) {
int16_t error;
uint8_t buffer[18];
uint16_t offset = 0;
offset = sensirion_i2c_add_command_to_buffer(&buffer[0], offset, 0x60E1);
error = sensirion_i2c_write_data(SEN5X_I2C_ADDRESS, &buffer[0], offset);
if (error) {
return error;
}
sensirion_i2c_hal_sleep_usec(20000);
error = sensirion_i2c_read_data_inplace(SEN5X_I2C_ADDRESS, &buffer[0], 12);
if (error) {
return error;
}
*index_offset = sensirion_common_bytes_to_int16_t(&buffer[0]);
*learning_time_offset_hours = sensirion_common_bytes_to_int16_t(&buffer[2]);
*learning_time_gain_hours = sensirion_common_bytes_to_int16_t(&buffer[4]);
*gating_max_duration_minutes =
sensirion_common_bytes_to_int16_t(&buffer[6]);
*std_initial = sensirion_common_bytes_to_int16_t(&buffer[8]);
*gain_factor = sensirion_common_bytes_to_int16_t(&buffer[10]);
return NO_ERROR;
}
int16_t sen5x_set_rht_acceleration_mode(uint16_t mode) {
int16_t error;
uint8_t buffer[5];
uint16_t offset = 0;
offset = sensirion_i2c_add_command_to_buffer(&buffer[0], offset, 0x60F7);
offset = sensirion_i2c_add_uint16_t_to_buffer(&buffer[0], offset, mode);
error = sensirion_i2c_write_data(SEN5X_I2C_ADDRESS, &buffer[0], offset);
if (error) {
return error;
}
sensirion_i2c_hal_sleep_usec(20000);
return NO_ERROR;
}
int16_t sen5x_get_rht_acceleration_mode(uint16_t* mode) {
int16_t error;
uint8_t buffer[3];
uint16_t offset = 0;
offset = sensirion_i2c_add_command_to_buffer(&buffer[0], offset, 0x60F7);
error = sensirion_i2c_write_data(SEN5X_I2C_ADDRESS, &buffer[0], offset);
if (error) {
return error;
}
sensirion_i2c_hal_sleep_usec(20000);
error = sensirion_i2c_read_data_inplace(SEN5X_I2C_ADDRESS, &buffer[0], 2);
if (error) {
return error;
}
*mode = sensirion_common_bytes_to_uint16_t(&buffer[0]);
return NO_ERROR;
}
int16_t sen5x_set_voc_algorithm_state(const uint8_t* state,
uint8_t state_size) {
int16_t error;
uint8_t buffer[14];
uint16_t offset = 0;
offset = sensirion_i2c_add_command_to_buffer(&buffer[0], offset, 0x6181);
offset = sensirion_i2c_add_bytes_to_buffer(&buffer[0], offset, state,
state_size);
error = sensirion_i2c_write_data(SEN5X_I2C_ADDRESS, &buffer[0], offset);
if (error) {
return error;
}
sensirion_i2c_hal_sleep_usec(20000);
return NO_ERROR;
}
int16_t sen5x_get_voc_algorithm_state(uint8_t* state, uint8_t state_size) {
int16_t error;
uint8_t buffer[12];
uint16_t offset = 0;
offset = sensirion_i2c_add_command_to_buffer(&buffer[0], offset, 0x6181);
error = sensirion_i2c_write_data(SEN5X_I2C_ADDRESS, &buffer[0], offset);
if (error) {
return error;
}
sensirion_i2c_hal_sleep_usec(20000);
error = sensirion_i2c_read_data_inplace(SEN5X_I2C_ADDRESS, &buffer[0], 8);
if (error) {
return error;
}
sensirion_common_copy_bytes(&buffer[0], state, state_size);
return NO_ERROR;
}
int16_t sen5x_set_fan_auto_cleaning_interval(uint32_t interval) {
int16_t error;
uint8_t buffer[8];
uint16_t offset = 0;
offset = sensirion_i2c_add_command_to_buffer(&buffer[0], offset, 0x8004);
offset = sensirion_i2c_add_uint32_t_to_buffer(&buffer[0], offset, interval);
error = sensirion_i2c_write_data(SEN5X_I2C_ADDRESS, &buffer[0], offset);
if (error) {
return error;
}
sensirion_i2c_hal_sleep_usec(20000);
return NO_ERROR;
}
int16_t sen5x_get_fan_auto_cleaning_interval(uint32_t* interval) {
int16_t error;
uint8_t buffer[6];
uint16_t offset = 0;
offset = sensirion_i2c_add_command_to_buffer(&buffer[0], offset, 0x8004);
error = sensirion_i2c_write_data(SEN5X_I2C_ADDRESS, &buffer[0], offset);
if (error) {
return error;
}
sensirion_i2c_hal_sleep_usec(20000);
error = sensirion_i2c_read_data_inplace(SEN5X_I2C_ADDRESS, &buffer[0], 4);
if (error) {
return error;
}
*interval = sensirion_common_bytes_to_uint32_t(&buffer[0]);
return NO_ERROR;
}
int16_t sen5x_get_product_name(unsigned char* product_name,
uint8_t product_name_size) {
int16_t error;
uint8_t buffer[48];
uint16_t offset = 0;
offset = sensirion_i2c_add_command_to_buffer(&buffer[0], offset, 0xD014);
error = sensirion_i2c_write_data(SEN5X_I2C_ADDRESS, &buffer[0], offset);
if (error) {
return error;
}
sensirion_i2c_hal_sleep_usec(50000);
error = sensirion_i2c_read_data_inplace(SEN5X_I2C_ADDRESS, &buffer[0], 32);
if (error) {
return error;
}
sensirion_common_copy_bytes(&buffer[0], product_name, product_name_size);
return NO_ERROR;
}
int16_t sen5x_get_serial_number(unsigned char* serial_number,
uint8_t serial_number_size) {
int16_t error;
uint8_t buffer[48];
uint16_t offset = 0;
offset = sensirion_i2c_add_command_to_buffer(&buffer[0], offset, 0xD033);
error = sensirion_i2c_write_data(SEN5X_I2C_ADDRESS, &buffer[0], offset);
if (error) {
return error;
}
sensirion_i2c_hal_sleep_usec(50000);
error = sensirion_i2c_read_data_inplace(SEN5X_I2C_ADDRESS, &buffer[0], 32);
if (error) {
return error;
}
sensirion_common_copy_bytes(&buffer[0], serial_number, serial_number_size);
return NO_ERROR;
}
int16_t sen5x_get_version(uint8_t* firmware_major, uint8_t* firmware_minor,
bool* firmware_debug, uint8_t* hardware_major,
uint8_t* hardware_minor, uint8_t* protocol_major,
uint8_t* protocol_minor) {
int16_t error;
uint8_t buffer[12];
uint16_t offset = 0;
offset = sensirion_i2c_add_command_to_buffer(&buffer[0], offset, 0xD100);
error = sensirion_i2c_write_data(SEN5X_I2C_ADDRESS, &buffer[0], offset);
if (error) {
return error;
}
sensirion_i2c_hal_sleep_usec(20000);
error = sensirion_i2c_read_data_inplace(SEN5X_I2C_ADDRESS, &buffer[0], 8);
if (error) {
return error;
}
*firmware_major = buffer[0];
*firmware_minor = buffer[1];
*firmware_debug = buffer[2];
*hardware_major = buffer[3];
*hardware_minor = buffer[4];
*protocol_major = buffer[5];
*protocol_minor = buffer[6];
return NO_ERROR;
}
int16_t sen5x_read_device_status(uint32_t* device_status) {
int16_t error;
uint8_t buffer[6];
uint16_t offset = 0;
offset = sensirion_i2c_add_command_to_buffer(&buffer[0], offset, 0xD206);
error = sensirion_i2c_write_data(SEN5X_I2C_ADDRESS, &buffer[0], offset);
if (error) {
return error;
}
sensirion_i2c_hal_sleep_usec(20000);
error = sensirion_i2c_read_data_inplace(SEN5X_I2C_ADDRESS, &buffer[0], 4);
if (error) {
return error;
}
*device_status = sensirion_common_bytes_to_uint32_t(&buffer[0]);
return NO_ERROR;
}
int16_t sen5x_read_and_clear_device_status(uint32_t* device_status) {
int16_t error;
uint8_t buffer[6];
uint16_t offset = 0;
offset = sensirion_i2c_add_command_to_buffer(&buffer[0], offset, 0xD210);
error = sensirion_i2c_write_data(SEN5X_I2C_ADDRESS, &buffer[0], offset);
if (error) {
return error;
}
sensirion_i2c_hal_sleep_usec(20000);
error = sensirion_i2c_read_data_inplace(SEN5X_I2C_ADDRESS, &buffer[0], 4);
if (error) {
return error;
}
*device_status = sensirion_common_bytes_to_uint32_t(&buffer[0]);
return NO_ERROR;
}
int16_t sen5x_device_reset(void) {
int16_t error;
uint8_t buffer[2];
uint16_t offset = 0;
offset = sensirion_i2c_add_command_to_buffer(&buffer[0], offset, 0xD304);
error = sensirion_i2c_write_data(SEN5X_I2C_ADDRESS, &buffer[0], offset);
if (error) {
return error;
}
sensirion_i2c_hal_sleep_usec(200000);
return NO_ERROR;
}

237
drivers/sen5x/sen5x_saul.c Normal file
View File

@ -0,0 +1,237 @@
/*
* Copyright (C) 2023 TU Braunschweig Institut für Betriebssysteme und Rechnerverbund
*
* 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_sen5x
* @{
*
* @file
* @brief SAUL adaptation for SEN50/54/55 devices.
*
* @author Daniel Prigoshij <prigoshi@ibr.cs.tu-bs.de>
*
* @}
*/
#include "saul.h"
#include "sen5x.h"
#include "sen5x_params.h"
static int read_mass_concentration_pm1p0(const void *dev, phydat_t *res) {
sen5x_t *d = (sen5x_t *)dev;
sen5x_read_pm_values(d, &d->values);
res->val[0] = (int16_t)d->values.mass_concentration_pm1p0;
res->unit = UNIT_GPM3;
res->scale = -5;
return 1;
}
static int read_mass_concentration_pm2p5(const void *dev, phydat_t *res) {
sen5x_t *d = (sen5x_t *)dev;
sen5x_read_pm_values(d, &d->values);
res->val[0] = (int16_t)d->values.mass_concentration_pm2p5;
res->unit = UNIT_GPM3;
res->scale = -5;
return 1;
}
static int read_mass_concentration_pm4p0(const void *dev, phydat_t *res) {
sen5x_t *d = (sen5x_t *)dev;
sen5x_read_pm_values(d, &d->values);
res->val[0] = (int16_t)d->values.mass_concentration_pm4p0;
res->unit = UNIT_GPM3;
res->scale = -5;
return 1;
}
static int read_mass_concentration_pm10p0(const void *dev, phydat_t *res) {
sen5x_t *d = (sen5x_t *)dev;
sen5x_read_pm_values(d, &d->values);
res->val[0] = (int16_t)d->values.mass_concentration_pm10p0;
res->unit = UNIT_GPM3;
res->scale = -5;
return 1;
}
static int read_number_concentration_pm0p5(const void *dev, phydat_t *res) {
sen5x_t *d = (sen5x_t *)dev;
sen5x_read_pm_values(d, &d->values);
res->val[0] = (int16_t)d->values.number_concentration_pm0p5;
res->unit = UNIT_CPM3;
res->scale = -5;
return 1;
}
static int read_number_concentration_pm1p0(const void *dev, phydat_t *res) {
sen5x_t *d = (sen5x_t *)dev;
sen5x_read_pm_values(d, &d->values);
res->val[0] = (int16_t)d->values.number_concentration_pm1p0;
res->unit = UNIT_CPM3;
res->scale = -5;
return 1;
}
static int read_number_concentration_pm2p5(const void *dev, phydat_t *res) {
sen5x_t *d = (sen5x_t *)dev;
sen5x_read_pm_values(d, &d->values);
res->val[0] = (int16_t)d->values.number_concentration_pm2p5;
res->unit = UNIT_CPM3;
res->scale = -5;
return 1;
}
static int read_number_concentration_pm4p0(const void *dev, phydat_t *res) {
sen5x_t *d = (sen5x_t *)dev;
sen5x_read_pm_values(d, &d->values);
res->val[0] = (int16_t)d->values.number_concentration_pm4p0;
res->unit = UNIT_CPM3;
res->scale = -5;
return 1;
}
static int read_number_concentration_pm10p0(const void *dev, phydat_t *res) {
sen5x_t *d = (sen5x_t *)dev;
sen5x_read_pm_values(d, &d->values);
res->val[0] = (int16_t)d->values.number_concentration_pm10p0;
res->unit = UNIT_CPM3;
res->scale = -5;
return 1;
}
static int read_typical_particle_size(const void *dev, phydat_t *res) {
sen5x_t *d = (sen5x_t *)dev;
sen5x_read_pm_values(d, &d->values);
res->val[0] = (int16_t)d->values.typical_particle_size;
res->unit = UNIT_M;
res->scale = -3;
return 1;
}
static int read_ambient_humidity(const void *dev, phydat_t *res) {
sen5x_t *d = (sen5x_t *)dev;
sen5x_read_pm_values(d, &d->values);
res->val[0] = (int16_t)d->values.ambient_humidity;
res->unit = UNIT_PERCENT;
res->scale = 2;
return 1;
}
static int read_ambient_temperature(const void *dev, phydat_t *res) {
sen5x_t *d = (sen5x_t *)dev;
sen5x_read_pm_values(d, &d->values);
res->val[0] = ((int16_t)d->values.ambient_temperature) / 2;
res->unit = UNIT_TEMP_C;
res->scale = 2;
return 1;
}
const saul_driver_t sen5x_mass_concentration_pm1p0_driver = {
.read = read_mass_concentration_pm1p0,
.write = saul_write_notsup,
.type = SAUL_SENSE_PM
};
const saul_driver_t sen5x_mass_concentration_pm2p5_driver = {
.read = read_mass_concentration_pm2p5,
.write = saul_write_notsup,
.type = SAUL_SENSE_PM
};
const saul_driver_t sen5x_mass_concentration_pm4p0_driver = {
.read = read_mass_concentration_pm4p0,
.write = saul_write_notsup,
.type = SAUL_SENSE_PM
};
const saul_driver_t sen5x_mass_concentration_pm10p0_driver = {
.read = read_mass_concentration_pm10p0,
.write = saul_write_notsup,
.type = SAUL_SENSE_PM
};
const saul_driver_t sen5x_number_concentration_pm0p5_driver = {
.read = read_number_concentration_pm0p5,
.write = saul_write_notsup,
.type = SAUL_SENSE_COUNT
};
const saul_driver_t sen5x_number_concentration_pm1p0_driver = {
.read = read_number_concentration_pm1p0,
.write = saul_write_notsup,
.type = SAUL_SENSE_COUNT
};
const saul_driver_t sen5x_number_concentration_pm2p5_driver = {
.read = read_number_concentration_pm2p5,
.write = saul_write_notsup,
.type = SAUL_SENSE_COUNT
};
const saul_driver_t sen5x_number_concentration_pm4p0_driver = {
.read = read_number_concentration_pm4p0,
.write = saul_write_notsup,
.type = SAUL_SENSE_COUNT
};
const saul_driver_t sen5x_number_concentration_pm10p0_driver = {
.read = read_number_concentration_pm10p0,
.write = saul_write_notsup,
.type = SAUL_SENSE_COUNT
};
const saul_driver_t sen5x_typical_particle_size_driver = {
.read = read_typical_particle_size,
.write = saul_write_notsup,
.type = SAUL_SENSE_SIZE
};
const saul_driver_t sen5x_ambient_humidity_driver = {
.read = read_ambient_humidity,
.write = saul_write_notsup,
.type = SAUL_SENSE_HUM
};
const saul_driver_t sen5x_ambient_temperature_driver = {
.read = read_ambient_temperature,
.write = saul_write_notsup,
.type = SAUL_SENSE_TEMP
};

View File

@ -0,0 +1,101 @@
/*
* Copyright (c) 2018, Sensirion AG
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of Sensirion AG nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#include "sensirion_common.h"
#include "sensirion_config.h"
uint16_t sensirion_common_bytes_to_uint16_t(const uint8_t* bytes) {
return (uint16_t)bytes[0] << 8 | (uint16_t)bytes[1];
}
uint32_t sensirion_common_bytes_to_uint32_t(const uint8_t* bytes) {
return (uint32_t)bytes[0] << 24 | (uint32_t)bytes[1] << 16 |
(uint32_t)bytes[2] << 8 | (uint32_t)bytes[3];
}
int16_t sensirion_common_bytes_to_int16_t(const uint8_t* bytes) {
return (int16_t)sensirion_common_bytes_to_uint16_t(bytes);
}
int32_t sensirion_common_bytes_to_int32_t(const uint8_t* bytes) {
return (int32_t)sensirion_common_bytes_to_uint32_t(bytes);
}
float sensirion_common_bytes_to_float(const uint8_t* bytes) {
union {
uint32_t u32_value;
float float32;
} tmp;
tmp.u32_value = sensirion_common_bytes_to_uint32_t(bytes);
return tmp.float32;
}
void sensirion_common_uint32_t_to_bytes(const uint32_t value, uint8_t* bytes) {
bytes[0] = value >> 24;
bytes[1] = value >> 16;
bytes[2] = value >> 8;
bytes[3] = value;
}
void sensirion_common_uint16_t_to_bytes(const uint16_t value, uint8_t* bytes) {
bytes[0] = value >> 8;
bytes[1] = value;
}
void sensirion_common_int32_t_to_bytes(const int32_t value, uint8_t* bytes) {
bytes[0] = value >> 24;
bytes[1] = value >> 16;
bytes[2] = value >> 8;
bytes[3] = value;
}
void sensirion_common_int16_t_to_bytes(const int16_t value, uint8_t* bytes) {
bytes[0] = value >> 8;
bytes[1] = value;
}
void sensirion_common_float_to_bytes(const float value, uint8_t* bytes) {
union {
uint32_t u32_value;
float float32;
} tmp;
tmp.float32 = value;
sensirion_common_uint32_t_to_bytes(tmp.u32_value, bytes);
}
void sensirion_common_copy_bytes(const uint8_t* source, uint8_t* destination,
uint16_t data_length) {
uint16_t i;
for (i = 0; i < data_length; i++) {
destination[i] = source[i];
}
}

View File

@ -0,0 +1,294 @@
/*
* Copyright (c) 2018, Sensirion AG
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of Sensirion AG nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#include "sensirion_i2c.h"
#include "sensirion_common.h"
#include "sensirion_config.h"
#include "sensirion_i2c_hal.h"
uint8_t sensirion_i2c_generate_crc(const uint8_t* data, uint16_t count) {
uint16_t current_byte;
uint8_t crc = CRC8_INIT;
uint8_t crc_bit;
/* calculates 8-Bit checksum with given polynomial */
for (current_byte = 0; current_byte < count; ++current_byte) {
crc ^= (data[current_byte]);
for (crc_bit = 8; crc_bit > 0; --crc_bit) {
if (crc & 0x80)
crc = (crc << 1) ^ CRC8_POLYNOMIAL;
else
crc = (crc << 1);
}
}
return crc;
}
int8_t sensirion_i2c_check_crc(const uint8_t* data, uint16_t count,
uint8_t checksum) {
if (sensirion_i2c_generate_crc(data, count) != checksum)
return CRC_ERROR;
return NO_ERROR;
}
int16_t sensirion_i2c_general_call_reset(void) {
const uint8_t data = 0x06;
return sensirion_i2c_hal_write(0, &data, (uint16_t)sizeof(data));
}
uint16_t sensirion_i2c_fill_cmd_send_buf(uint8_t* buf, uint16_t cmd,
const uint16_t* args,
uint8_t num_args) {
uint8_t i;
uint16_t idx = 0;
buf[idx++] = (uint8_t)((cmd & 0xFF00) >> 8);
buf[idx++] = (uint8_t)((cmd & 0x00FF) >> 0);
for (i = 0; i < num_args; ++i) {
buf[idx++] = (uint8_t)((args[i] & 0xFF00) >> 8);
buf[idx++] = (uint8_t)((args[i] & 0x00FF) >> 0);
uint8_t crc = sensirion_i2c_generate_crc((uint8_t*)&buf[idx - 2],
SENSIRION_WORD_SIZE);
buf[idx++] = crc;
}
return idx;
}
int16_t sensirion_i2c_read_words_as_bytes(uint8_t address, uint8_t* data,
uint16_t num_words) {
int16_t ret;
uint16_t i, j;
uint16_t size = num_words * (SENSIRION_WORD_SIZE + CRC8_LEN);
uint16_t word_buf[SENSIRION_MAX_BUFFER_WORDS];
uint8_t* const buf8 = (uint8_t*)word_buf;
ret = sensirion_i2c_hal_read(address, buf8, size);
if (ret != NO_ERROR)
return ret;
/* check the CRC for each word */
for (i = 0, j = 0; i < size; i += SENSIRION_WORD_SIZE + CRC8_LEN) {
ret = sensirion_i2c_check_crc(&buf8[i], SENSIRION_WORD_SIZE,
buf8[i + SENSIRION_WORD_SIZE]);
if (ret != NO_ERROR)
return ret;
data[j++] = buf8[i];
data[j++] = buf8[i + 1];
}
return NO_ERROR;
}
int16_t sensirion_i2c_read_words(uint8_t address, uint16_t* data_words,
uint16_t num_words) {
int16_t ret;
uint8_t i;
ret = sensirion_i2c_read_words_as_bytes(address, (uint8_t*)data_words,
num_words);
if (ret != NO_ERROR)
return ret;
for (i = 0; i < num_words; ++i) {
const uint8_t* word_bytes = (uint8_t*)&data_words[i];
data_words[i] = ((uint16_t)word_bytes[0] << 8) | word_bytes[1];
}
return NO_ERROR;
}
int16_t sensirion_i2c_write_cmd(uint8_t address, uint16_t command) {
uint8_t buf[SENSIRION_COMMAND_SIZE];
sensirion_i2c_fill_cmd_send_buf(buf, command, NULL, 0);
return sensirion_i2c_hal_write(address, buf, SENSIRION_COMMAND_SIZE);
}
int16_t sensirion_i2c_write_cmd_with_args(uint8_t address, uint16_t command,
const uint16_t* data_words,
uint16_t num_words) {
uint8_t buf[SENSIRION_MAX_BUFFER_WORDS];
uint16_t buf_size;
buf_size =
sensirion_i2c_fill_cmd_send_buf(buf, command, data_words, num_words);
return sensirion_i2c_hal_write(address, buf, buf_size);
}
int16_t sensirion_i2c_delayed_read_cmd(uint8_t address, uint16_t cmd,
uint32_t delay_us, uint16_t* data_words,
uint16_t num_words) {
int16_t ret;
uint8_t buf[SENSIRION_COMMAND_SIZE];
sensirion_i2c_fill_cmd_send_buf(buf, cmd, NULL, 0);
ret = sensirion_i2c_hal_write(address, buf, SENSIRION_COMMAND_SIZE);
if (ret != NO_ERROR)
return ret;
if (delay_us)
sensirion_i2c_hal_sleep_usec(delay_us);
return sensirion_i2c_read_words(address, data_words, num_words);
}
int16_t sensirion_i2c_read_cmd(uint8_t address, uint16_t cmd,
uint16_t* data_words, uint16_t num_words) {
return sensirion_i2c_delayed_read_cmd(address, cmd, 0, data_words,
num_words);
}
uint16_t sensirion_i2c_add_command_to_buffer(uint8_t* buffer, uint16_t offset,
uint16_t command) {
buffer[offset++] = (uint8_t)((command & 0xFF00) >> 8);
buffer[offset++] = (uint8_t)((command & 0x00FF) >> 0);
return offset;
}
uint16_t sensirion_i2c_add_uint32_t_to_buffer(uint8_t* buffer, uint16_t offset,
uint32_t data) {
buffer[offset++] = (uint8_t)((data & 0xFF000000) >> 24);
buffer[offset++] = (uint8_t)((data & 0x00FF0000) >> 16);
buffer[offset] = sensirion_i2c_generate_crc(
&buffer[offset - SENSIRION_WORD_SIZE], SENSIRION_WORD_SIZE);
offset++;
buffer[offset++] = (uint8_t)((data & 0x0000FF00) >> 8);
buffer[offset++] = (uint8_t)((data & 0x000000FF) >> 0);
buffer[offset] = sensirion_i2c_generate_crc(
&buffer[offset - SENSIRION_WORD_SIZE], SENSIRION_WORD_SIZE);
offset++;
return offset;
}
uint16_t sensirion_i2c_add_int32_t_to_buffer(uint8_t* buffer, uint16_t offset,
int32_t data) {
return sensirion_i2c_add_uint32_t_to_buffer(buffer, offset, (uint32_t)data);
}
uint16_t sensirion_i2c_add_uint16_t_to_buffer(uint8_t* buffer, uint16_t offset,
uint16_t data) {
buffer[offset++] = (uint8_t)((data & 0xFF00) >> 8);
buffer[offset++] = (uint8_t)((data & 0x00FF) >> 0);
buffer[offset] = sensirion_i2c_generate_crc(
&buffer[offset - SENSIRION_WORD_SIZE], SENSIRION_WORD_SIZE);
offset++;
return offset;
}
uint16_t sensirion_i2c_add_int16_t_to_buffer(uint8_t* buffer, uint16_t offset,
int16_t data) {
return sensirion_i2c_add_uint16_t_to_buffer(buffer, offset, (uint16_t)data);
}
uint16_t sensirion_i2c_add_float_to_buffer(uint8_t* buffer, uint16_t offset,
float data) {
union {
uint32_t uint32_data;
float float_data;
} convert;
convert.float_data = data;
buffer[offset++] = (uint8_t)((convert.uint32_data & 0xFF000000) >> 24);
buffer[offset++] = (uint8_t)((convert.uint32_data & 0x00FF0000) >> 16);
buffer[offset] = sensirion_i2c_generate_crc(
&buffer[offset - SENSIRION_WORD_SIZE], SENSIRION_WORD_SIZE);
offset++;
buffer[offset++] = (uint8_t)((convert.uint32_data & 0x0000FF00) >> 8);
buffer[offset++] = (uint8_t)((convert.uint32_data & 0x000000FF) >> 0);
buffer[offset] = sensirion_i2c_generate_crc(
&buffer[offset - SENSIRION_WORD_SIZE], SENSIRION_WORD_SIZE);
offset++;
return offset;
}
uint16_t sensirion_i2c_add_bytes_to_buffer(uint8_t* buffer, uint16_t offset,
const uint8_t* data,
uint16_t data_length) {
uint16_t i;
if (data_length % SENSIRION_WORD_SIZE != 0) {
return BYTE_NUM_ERROR;
}
for (i = 0; i < data_length; i += 2) {
buffer[offset++] = data[i];
buffer[offset++] = data[i + 1];
buffer[offset] = sensirion_i2c_generate_crc(
&buffer[offset - SENSIRION_WORD_SIZE], SENSIRION_WORD_SIZE);
offset++;
}
return offset;
}
int16_t sensirion_i2c_write_data(uint8_t address, const uint8_t* data,
uint16_t data_length) {
return sensirion_i2c_hal_write(address, data, data_length);
}
int16_t sensirion_i2c_read_data_inplace(uint8_t address, uint8_t* buffer,
uint16_t expected_data_length) {
int16_t error;
uint16_t i, j;
uint16_t size = (expected_data_length / SENSIRION_WORD_SIZE) *
(SENSIRION_WORD_SIZE + CRC8_LEN);
if (expected_data_length % SENSIRION_WORD_SIZE != 0) {
return BYTE_NUM_ERROR;
}
error = sensirion_i2c_hal_read(address, buffer, size);
if (error) {
return error;
}
for (i = 0, j = 0; i < size; i += SENSIRION_WORD_SIZE + CRC8_LEN) {
error = sensirion_i2c_check_crc(&buffer[i], SENSIRION_WORD_SIZE,
buffer[i + SENSIRION_WORD_SIZE]);
if (error) {
return error;
}
buffer[j++] = buffer[i];
buffer[j++] = buffer[i + 1];
}
return NO_ERROR;
}

View File

@ -0,0 +1,95 @@
/*
* Copyright (c) 2018, Sensirion AG
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of Sensirion AG nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#include "sensirion_i2c_hal.h"
#include "sensirion_common.h"
#include "sensirion_config.h"
/**
* Initialize all hard- and software components that are needed for the I2C
* communication.
*/
void sensirion_i2c_hal_init(void) {
// initialize the bus
i2c_init(I2C_DEVICE);
// first, acquire the shared bus again
i2c_acquire(I2C_DEVICE);
}
/**
* Release all resources initialized by sensirion_i2c_hal_init().
*/
void sensirion_i2c_hal_free(void) {
i2c_release(I2C_DEVICE);
}
/**
* Execute one read transaction on the I2C bus, reading a given number of bytes.
* If the device does not acknowledge the read command, an error shall be
* returned.
*
* @param address 7-bit I2C address to read from
* @param data pointer to the buffer where the data is to be stored
* @param count number of bytes to read from I2C and store in the buffer
* @returns 0 on success, error code otherwise
*/
int8_t sensirion_i2c_hal_read(uint8_t address, uint8_t* data, uint16_t count) {
return i2c_read_bytes(I2C_DEVICE, address, data, count, 0);
}
/**
* Execute one write transaction on the I2C bus, sending a given number of
* bytes. The bytes in the supplied buffer must be sent to the given address. If
* the slave device does not acknowledge any of the bytes, an error shall be
* returned.
*
* @param address 7-bit I2C address to write to
* @param data pointer to the buffer containing the data to write
* @param count number of bytes to read from the buffer and send over I2C
* @returns 0 on success, error code otherwise
*/
int8_t sensirion_i2c_hal_write(uint8_t address, const uint8_t* data, uint16_t count) {
return i2c_write_bytes(I2C_DEVICE, address, data, count, 0);
}
/**
* Sleep for a given number of microseconds. The function should delay the
* execution for at least the given time, but may also sleep longer.
*
* Despite the unit, a <10 millisecond precision is sufficient.
*
* @param useconds the sleep time in microseconds
*/
void sensirion_i2c_hal_sleep_usec(uint32_t useconds) {
xtimer_usleep(useconds);
}