initial commit

This commit is contained in:
leo 2022-03-14 13:12:12 +01:00
commit d0df1ec1e0
Signed by: leo
GPG Key ID: 0DD993BFB2B307DB
11 changed files with 2522 additions and 0 deletions

2
CMakeLists.txt Normal file
View File

@ -0,0 +1,2 @@
idf_component_register(SRCS "sensirion_common.c" "sensirion_i2c.c" "sensirion_i2c_hal.c" "scd4x_i2c.c"
INCLUDE_DIRS "include")

452
include/scd4x_i2c.c Normal file
View File

@ -0,0 +1,452 @@
/*
* THIS FILE IS AUTOMATICALLY GENERATED AND MUST NOT BE EDITED MANUALLY!
*
* I2C-Generator: 0.2.0
* Yaml Version: 0.1.0
* Template Version: 0.2.1
*/
/*
* 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 "scd4x_i2c.h"
#include "sensirion_common.h"
#include "sensirion_i2c.h"
#include "sensirion_i2c_hal.h"
#define SCD4X_I2C_ADDRESS 98
int16_t scd4x_start_periodic_measurement() {
int16_t error;
uint8_t buffer[2];
uint16_t offset = 0;
offset = sensirion_i2c_add_command_to_buffer(&buffer[0], offset, 0x21B1);
error = sensirion_i2c_write_data(SCD4X_I2C_ADDRESS, &buffer[0], offset);
if (error) {
return error;
}
sensirion_i2c_hal_sleep_usec(1000);
return NO_ERROR;
}
int16_t scd4x_read_measurement_ticks(uint16_t* co2, uint16_t* temperature,
uint16_t* humidity) {
int16_t error;
uint8_t buffer[9];
uint16_t offset = 0;
offset = sensirion_i2c_add_command_to_buffer(&buffer[0], offset, 0xEC05);
error = sensirion_i2c_write_data(SCD4X_I2C_ADDRESS, &buffer[0], offset);
if (error) {
return error;
}
sensirion_i2c_hal_sleep_usec(1000);
error = sensirion_i2c_read_data_inplace(SCD4X_I2C_ADDRESS, &buffer[0], 6);
if (error) {
return error;
}
*co2 = sensirion_common_bytes_to_uint16_t(&buffer[0]);
*temperature = sensirion_common_bytes_to_uint16_t(&buffer[2]);
*humidity = sensirion_common_bytes_to_uint16_t(&buffer[4]);
return NO_ERROR;
}
int16_t scd4x_read_measurement(uint16_t* co2, int32_t* temperature_m_deg_c,
int32_t* humidity_m_percent_rh) {
int16_t error;
uint16_t temperature;
uint16_t humidity;
error = scd4x_read_measurement_ticks(co2, &temperature, &humidity);
if (error) {
return error;
}
*temperature_m_deg_c = ((21875 * (int32_t)temperature) >> 13) - 45000;
*humidity_m_percent_rh = ((12500 * (int32_t)humidity) >> 13);
return NO_ERROR;
}
int16_t scd4x_stop_periodic_measurement() {
int16_t error;
uint8_t buffer[2];
uint16_t offset = 0;
offset = sensirion_i2c_add_command_to_buffer(&buffer[0], offset, 0x3F86);
error = sensirion_i2c_write_data(SCD4X_I2C_ADDRESS, &buffer[0], offset);
if (error) {
return error;
}
sensirion_i2c_hal_sleep_usec(500000);
return NO_ERROR;
}
int16_t scd4x_get_temperature_offset_ticks(uint16_t* t_offset) {
int16_t error;
uint8_t buffer[3];
uint16_t offset = 0;
offset = sensirion_i2c_add_command_to_buffer(&buffer[0], offset, 0x2318);
error = sensirion_i2c_write_data(SCD4X_I2C_ADDRESS, &buffer[0], offset);
if (error) {
return error;
}
sensirion_i2c_hal_sleep_usec(1000);
error = sensirion_i2c_read_data_inplace(SCD4X_I2C_ADDRESS, &buffer[0], 2);
if (error) {
return error;
}
*t_offset = sensirion_common_bytes_to_uint16_t(&buffer[0]);
return NO_ERROR;
}
int16_t scd4x_get_temperature_offset(int32_t* t_offset_m_deg_c) {
int16_t error;
uint16_t t_offset;
error = scd4x_get_temperature_offset_ticks(&t_offset);
if (error) {
return error;
}
*t_offset_m_deg_c = ((21875 * (int32_t)t_offset) >> 13);
return NO_ERROR;
}
int16_t scd4x_set_temperature_offset_ticks(uint16_t t_offset) {
int16_t error;
uint8_t buffer[5];
uint16_t offset = 0;
offset = sensirion_i2c_add_command_to_buffer(&buffer[0], offset, 0x241D);
offset = sensirion_i2c_add_uint16_t_to_buffer(&buffer[0], offset, t_offset);
error = sensirion_i2c_write_data(SCD4X_I2C_ADDRESS, &buffer[0], offset);
if (error) {
return error;
}
sensirion_i2c_hal_sleep_usec(1000);
return NO_ERROR;
}
int16_t scd4x_set_temperature_offset(int32_t t_offset_m_deg_c) {
uint16_t t_offset = (uint16_t)((t_offset_m_deg_c * 12271) >> 15);
return scd4x_set_temperature_offset_ticks(t_offset);
}
int16_t scd4x_get_sensor_altitude(uint16_t* sensor_altitude) {
int16_t error;
uint8_t buffer[3];
uint16_t offset = 0;
offset = sensirion_i2c_add_command_to_buffer(&buffer[0], offset, 0x2322);
error = sensirion_i2c_write_data(SCD4X_I2C_ADDRESS, &buffer[0], offset);
if (error) {
return error;
}
sensirion_i2c_hal_sleep_usec(1000);
error = sensirion_i2c_read_data_inplace(SCD4X_I2C_ADDRESS, &buffer[0], 2);
if (error) {
return error;
}
*sensor_altitude = sensirion_common_bytes_to_uint16_t(&buffer[0]);
return NO_ERROR;
}
int16_t scd4x_set_sensor_altitude(uint16_t sensor_altitude) {
int16_t error;
uint8_t buffer[5];
uint16_t offset = 0;
offset = sensirion_i2c_add_command_to_buffer(&buffer[0], offset, 0x2427);
offset = sensirion_i2c_add_uint16_t_to_buffer(&buffer[0], offset,
sensor_altitude);
error = sensirion_i2c_write_data(SCD4X_I2C_ADDRESS, &buffer[0], offset);
if (error) {
return error;
}
sensirion_i2c_hal_sleep_usec(1000);
return NO_ERROR;
}
int16_t scd4x_set_ambient_pressure(uint16_t ambient_pressure) {
int16_t error;
uint8_t buffer[5];
uint16_t offset = 0;
offset = sensirion_i2c_add_command_to_buffer(&buffer[0], offset, 0xE000);
offset = sensirion_i2c_add_uint16_t_to_buffer(&buffer[0], offset,
ambient_pressure);
error = sensirion_i2c_write_data(SCD4X_I2C_ADDRESS, &buffer[0], offset);
if (error) {
return error;
}
sensirion_i2c_hal_sleep_usec(1000);
return NO_ERROR;
}
int16_t scd4x_perform_forced_recalibration(uint16_t target_co2_concentration,
uint16_t* frc_correction) {
int16_t error;
uint8_t buffer[5];
uint16_t offset = 0;
offset = sensirion_i2c_add_command_to_buffer(&buffer[0], offset, 0x362F);
offset = sensirion_i2c_add_uint16_t_to_buffer(&buffer[0], offset,
target_co2_concentration);
error = sensirion_i2c_write_data(SCD4X_I2C_ADDRESS, &buffer[0], offset);
if (error) {
return error;
}
sensirion_i2c_hal_sleep_usec(400000);
error = sensirion_i2c_read_data_inplace(SCD4X_I2C_ADDRESS, &buffer[0], 2);
if (error) {
return error;
}
*frc_correction = sensirion_common_bytes_to_uint16_t(&buffer[0]);
return NO_ERROR;
}
int16_t scd4x_get_automatic_self_calibration(uint16_t* asc_enabled) {
int16_t error;
uint8_t buffer[3];
uint16_t offset = 0;
offset = sensirion_i2c_add_command_to_buffer(&buffer[0], offset, 0x2313);
error = sensirion_i2c_write_data(SCD4X_I2C_ADDRESS, &buffer[0], offset);
if (error) {
return error;
}
sensirion_i2c_hal_sleep_usec(1000);
error = sensirion_i2c_read_data_inplace(SCD4X_I2C_ADDRESS, &buffer[0], 2);
if (error) {
return error;
}
*asc_enabled = sensirion_common_bytes_to_uint16_t(&buffer[0]);
return NO_ERROR;
}
int16_t scd4x_set_automatic_self_calibration(uint16_t asc_enabled) {
int16_t error;
uint8_t buffer[5];
uint16_t offset = 0;
offset = sensirion_i2c_add_command_to_buffer(&buffer[0], offset, 0x2416);
offset =
sensirion_i2c_add_uint16_t_to_buffer(&buffer[0], offset, asc_enabled);
error = sensirion_i2c_write_data(SCD4X_I2C_ADDRESS, &buffer[0], offset);
if (error) {
return error;
}
sensirion_i2c_hal_sleep_usec(1000);
return NO_ERROR;
}
int16_t scd4x_start_low_power_periodic_measurement() {
uint8_t buffer[2];
uint16_t offset = 0;
offset = sensirion_i2c_add_command_to_buffer(&buffer[0], offset, 0x21AC);
return sensirion_i2c_write_data(SCD4X_I2C_ADDRESS, &buffer[0], offset);
}
int16_t scd4x_get_data_ready_status(uint16_t* data_ready) {
int16_t error;
uint8_t buffer[3];
uint16_t offset = 0;
offset = sensirion_i2c_add_command_to_buffer(&buffer[0], offset, 0xE4B8);
error = sensirion_i2c_write_data(SCD4X_I2C_ADDRESS, &buffer[0], offset);
if (error) {
return error;
}
sensirion_i2c_hal_sleep_usec(1000);
error = sensirion_i2c_read_data_inplace(SCD4X_I2C_ADDRESS, &buffer[0], 2);
if (error) {
return error;
}
*data_ready = sensirion_common_bytes_to_uint16_t(&buffer[0]);
return NO_ERROR;
}
int16_t scd4x_persist_settings() {
int16_t error;
uint8_t buffer[2];
uint16_t offset = 0;
offset = sensirion_i2c_add_command_to_buffer(&buffer[0], offset, 0x3615);
error = sensirion_i2c_write_data(SCD4X_I2C_ADDRESS, &buffer[0], offset);
if (error) {
return error;
}
sensirion_i2c_hal_sleep_usec(800000);
return NO_ERROR;
}
int16_t scd4x_get_serial_number(uint16_t* serial_0, uint16_t* serial_1,
uint16_t* serial_2) {
int16_t error;
uint8_t buffer[9];
uint16_t offset = 0;
offset = sensirion_i2c_add_command_to_buffer(&buffer[0], offset, 0x3682);
error = sensirion_i2c_write_data(SCD4X_I2C_ADDRESS, &buffer[0], offset);
if (error) {
return error;
}
sensirion_i2c_hal_sleep_usec(1000);
error = sensirion_i2c_read_data_inplace(SCD4X_I2C_ADDRESS, &buffer[0], 6);
if (error) {
return error;
}
*serial_0 = sensirion_common_bytes_to_uint16_t(&buffer[0]);
*serial_1 = sensirion_common_bytes_to_uint16_t(&buffer[2]);
*serial_2 = sensirion_common_bytes_to_uint16_t(&buffer[4]);
return NO_ERROR;
}
int16_t scd4x_perform_self_test(uint16_t* sensor_status) {
int16_t error;
uint8_t buffer[3];
uint16_t offset = 0;
offset = sensirion_i2c_add_command_to_buffer(&buffer[0], offset, 0x3639);
error = sensirion_i2c_write_data(SCD4X_I2C_ADDRESS, &buffer[0], offset);
if (error) {
return error;
}
sensirion_i2c_hal_sleep_usec(10000000);
error = sensirion_i2c_read_data_inplace(SCD4X_I2C_ADDRESS, &buffer[0], 2);
if (error) {
return error;
}
*sensor_status = sensirion_common_bytes_to_uint16_t(&buffer[0]);
return NO_ERROR;
}
int16_t scd4x_perform_factory_reset() {
int16_t error;
uint8_t buffer[2];
uint16_t offset = 0;
offset = sensirion_i2c_add_command_to_buffer(&buffer[0], offset, 0x3632);
error = sensirion_i2c_write_data(SCD4X_I2C_ADDRESS, &buffer[0], offset);
if (error) {
return error;
}
sensirion_i2c_hal_sleep_usec(800000);
return NO_ERROR;
}
int16_t scd4x_reinit() {
int16_t error;
uint8_t buffer[2];
uint16_t offset = 0;
offset = sensirion_i2c_add_command_to_buffer(&buffer[0], offset, 0x3646);
error = sensirion_i2c_write_data(SCD4X_I2C_ADDRESS, &buffer[0], offset);
if (error) {
return error;
}
sensirion_i2c_hal_sleep_usec(20000);
return NO_ERROR;
}
int16_t scd4x_measure_single_shot() {
int16_t error;
uint8_t buffer[2];
uint16_t offset = 0;
offset = sensirion_i2c_add_command_to_buffer(&buffer[0], offset, 0x219D);
error = sensirion_i2c_write_data(SCD4X_I2C_ADDRESS, &buffer[0], offset);
if (error) {
return error;
}
sensirion_i2c_hal_sleep_usec(5000000);
return NO_ERROR;
}
int16_t scd4x_measure_single_shot_rht_only() {
int16_t error;
uint8_t buffer[2];
uint16_t offset = 0;
offset = sensirion_i2c_add_command_to_buffer(&buffer[0], offset, 0x2196);
error = sensirion_i2c_write_data(SCD4X_I2C_ADDRESS, &buffer[0], offset);
if (error) {
return error;
}
sensirion_i2c_hal_sleep_usec(50000);
return NO_ERROR;
}
int16_t scd4x_power_down() {
int16_t error;
uint8_t buffer[2];
uint16_t offset = 0;
offset = sensirion_i2c_add_command_to_buffer(&buffer[0], offset, 0x36E0);
error = sensirion_i2c_write_data(SCD4X_I2C_ADDRESS, &buffer[0], offset);
if (error) {
return error;
}
sensirion_i2c_hal_sleep_usec(1000);
return NO_ERROR;
}
int16_t scd4x_wake_up() {
uint8_t buffer[2];
uint16_t offset = 0;
offset = sensirion_i2c_add_command_to_buffer(&buffer[0], offset, 0x36F6);
// Sensor does not acknowledge the wake-up call, error is ignored
(void)sensirion_i2c_write_data(SCD4X_I2C_ADDRESS, &buffer[0], offset);
sensirion_i2c_hal_sleep_usec(20000);
return NO_ERROR;
}

378
include/scd4x_i2c.h Normal file
View File

@ -0,0 +1,378 @@
/*
* THIS FILE IS AUTOMATICALLY GENERATED AND MUST NOT BE EDITED MANUALLY!
*
* I2C-Generator: 0.2.0
* Yaml Version: 0.1.0
* Template Version: 0.2.1
*/
/*
* 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 SCD4X_I2C_H
#define SCD4X_I2C_H
#ifdef __cplusplus
extern "C" {
#endif
#include "sensirion_config.h"
/**
* scd4x_start_periodic_measurement() - start periodic measurement, signal
* update interval is 5 seconds.
*
* @note This command is only available in idle mode.
*
* @return 0 on success, an error code otherwise
*/
int16_t scd4x_start_periodic_measurement(void);
/**
* scd4x_read_measurement_ticks() - read sensor output. The measurement data can
* only be read out once per signal update interval as the buffer is emptied
* upon read-out. If no data is available in the buffer, the sensor returns a
* NACK. To avoid a NACK response the get_data_ready_status can be issued to
* check data status. The I2C master can abort the read transfer with a NACK
* followed by a STOP condition after any data byte if the user is not
* interested in subsequent data.
*
* @note This command is only available in measurement mode. The firmware
* updates the measurement values depending on the measurement mode.
*
* @param co2 CO concentration in ppm
*
* @param temperature Convert value to °C by: -45 °C + 175 °C * value/2^16
*
* @param humidity Convert value to %RH by: 100%RH * value/2^16
*
* @return 0 on success, an error code otherwise
*/
int16_t scd4x_read_measurement_ticks(uint16_t* co2, uint16_t* temperature,
uint16_t* humidity);
/**
* scd4x_read_measurement() - read sensor output and convert.
* See @ref scd4x_read_measurement_ticks() for more details.
*
* @note This command is only available in measurement mode. The firmware
* updates the measurement values depending on the measurement mode.
*
* @param co2 CO concentration in ppm
*
* @param temperature_m_deg_c Temperature in milli degrees celsius (°C * 1000)
*
* @param humidity_m_percent_rh Relative humidity in milli percent RH
* (%RH * 1000)
*
* @return 0 on success, an error code otherwise
*/
int16_t scd4x_read_measurement(uint16_t* co2, int32_t* temperature_m_deg_c,
int32_t* humidity_m_percent_rh);
/**
* scd4x_stop_periodic_measurement() - Stop periodic measurement and return to
* idle mode for sensor configuration or to safe energy.
*
* @note This command is only available in measurement mode.
*
* @return 0 on success, an error code otherwise
*/
int16_t scd4x_stop_periodic_measurement(void);
/**
* scd4x_get_temperature_offset_ticks() - The temperature offset represents the
* difference between the measured temperature by the SCD4x and the actual
* ambient temperature. Per default, the temperature offset is set to 4°C.
*
* @note Only available in idle mode.
*
* @param t_offset Temperature offset. Convert value to °C by: 175 * value /
* 2^16
*
* @return 0 on success, an error code otherwise
*/
int16_t scd4x_get_temperature_offset_ticks(uint16_t* t_offset);
/**
* scd4x_get_temperature_offset() - The temperature offset represents the
* difference between the measured temperature by the SCD4x and the actual
* ambient temperature. Per default, the temperature offset is set to 4°C.
*
* @note Only available in idle mode.
*
* @param t_offset_m_deg_c Temperature offset in milli degrees Celsius.
*
* @return 0 on success, an error code otherwise
*/
int16_t scd4x_get_temperature_offset(int32_t* t_offset_m_deg_c);
/**
* scd4x_set_temperature_offset_ticks() - Setting the temperature offset of the
* SCD4x inside the customer device correctly allows the user to leverage the RH
* and T output signal. Note that the temperature offset can depend on various
* factors such as the SCD4x measurement mode, self-heating of close components,
* the ambient temperature and air flow. Thus, the SCD4x temperature offset
* should be determined inside the customer device under its typical operation
* and in thermal equilibrium.
*
* @note Only available in idle mode.
*
* @param t_offset Temperature offset. Convert °C to value by: T * 2^16 / 175.
*
* @return 0 on success, an error code otherwise
*/
int16_t scd4x_set_temperature_offset_ticks(uint16_t t_offset);
/**
* scd4x_set_temperature_offset() - Setting the temperature offset of the SCD4x
* inside the customer device correctly allows the user to leverage the RH and T
* output signal. Note that the temperature offset can depend on various factors
* such as the SCD4x measurement mode, self-heating of close components, the
* ambient temperature and air flow. Thus, the SCD4x temperature offset should
* be determined inside the customer device under its typical operation and in
* thermal equilibrium.
*
* @note Only available in idle mode.
*
* @param t_offset_m_deg_c Temperature offset in milli degrees Celsius.
*
* @return 0 on success, an error code otherwise
*/
int16_t scd4x_set_temperature_offset(int32_t t_offset_m_deg_c);
/**
* scd4x_get_sensor_altitude() - Get configured sensor altitude in meters above
* sea level. Per default, the sensor altitude is set to 0 meter above
* sea-level.
*
* @note Only available in idle mode.
*
* @param sensor_altitude Sensor altitude in meters.
*
* @return 0 on success, an error code otherwise
*/
int16_t scd4x_get_sensor_altitude(uint16_t* sensor_altitude);
/**
* scd4x_set_sensor_altitude() - Set sensor altitude in meters above sea level.
* Note that setting a sensor altitude to the sensor overrides any pressure
* compensation based on a previously set ambient pressure.
*
* @note Only available in idle mode.
*
* @param sensor_altitude Sensor altitude in meters.
*
* @return 0 on success, an error code otherwise
*/
int16_t scd4x_set_sensor_altitude(uint16_t sensor_altitude);
/**
* scd4x_set_ambient_pressure() - The set_ambient_pressure command can be sent
* during periodic measurements to enable continuous pressure compensation. Note
* that setting an ambient pressure to the sensor overrides any pressure
* compensation based on a previously set sensor altitude.
*
* @note Available during measurements.
*
* @param ambient_pressure Ambient pressure in hPa. Convert value to Pa by:
* value * 100.
*
* @return 0 on success, an error code otherwise
*/
int16_t scd4x_set_ambient_pressure(uint16_t ambient_pressure);
/**
* scd4x_perform_forced_recalibration() - To successfully conduct an accurate
forced recalibration, the following steps need to be carried out:
1. Operate the SCD4x in a periodic measurement mode for > 3 minutes in an
environment with homogenous and constant CO concentration.
2. Stop periodic measurement. Wait 500 ms.
3. Subsequently issue the perform_forced_recalibration command and optionally
read out the baseline correction. A return value of 0xffff indicates that the
forced recalibration failed.
*
* @param target_co2_concentration Target CO concentration in ppm.
*
* @param frc_correction FRC correction value in CO ppm or 0xFFFF if the
command failed. Convert value to CO ppm with: value - 0x8000
*
* @return 0 on success, an error code otherwise
*/
int16_t scd4x_perform_forced_recalibration(uint16_t target_co2_concentration,
uint16_t* frc_correction);
/**
* scd4x_get_automatic_self_calibration() - By default, the ASC is enabled.
*
* @param asc_enabled 1 if ASC is enabled, 0 if ASC is disabled
*
* @return 0 on success, an error code otherwise
*/
int16_t scd4x_get_automatic_self_calibration(uint16_t* asc_enabled);
/**
* scd4x_set_automatic_self_calibration() - By default, the ASC is enabled.
*
* @param asc_enabled 1 to enable ASC, 0 to disable ASC
*
* @return 0 on success, an error code otherwise
*/
int16_t scd4x_set_automatic_self_calibration(uint16_t asc_enabled);
/**
* scd4x_start_low_power_periodic_measurement() - Start low power periodic
* measurement, signal update interval is 30 seconds.
*
* @note This command is only available in idle mode.
*
* @return 0 on success, an error code otherwise
*/
int16_t scd4x_start_low_power_periodic_measurement(void);
/**
* scd4x_get_data_ready_status() - Check whether new measurement data is
* available for read-out.
*
* @param data_ready If last 11 bits are 0 data not ready, else data ready
*
* @return 0 on success, an error code otherwise
*/
int16_t scd4x_get_data_ready_status(uint16_t* data_ready);
/**
* scd4x_persist_settings() - Configuration settings such as the temperature
* offset, sensor altitude and the ASC enabled/disabled parameter are by default
* stored in the volatile memory (RAM) only and will be lost after a
* power-cycle. The persist_settings command stores the current configuration in
* the EEPROM of the SCD4x, making them resistant to power-cycling. To avoid
* unnecessary wear of the EEPROM, the persist_settings command should only be
* sent when persistence is required and if actual changes to the configuration
* have been made. Note that field calibration history (i.e. FRC and ASC) is
* stored in the EEPROM automatically.
*
* @note
*
* @return 0 on success, an error code otherwise
*/
int16_t scd4x_persist_settings(void);
/**
* scd4x_get_serial_number() - Reading out the serial number can be used to
* identify the chip and to verify the presence of the sensor. The get serial
* number command returns 3 words. Together, the 3 words constitute a unique
* serial number with a length of 48 bits (big endian format).
*
* @param serial_0 First word of the 48 bit serial number
*
* @param serial_1 Second word of the 48 bit serial number
*
* @param serial_2 Third word of the 48 bit serial number
*
* @return 0 on success, an error code otherwise
*/
int16_t scd4x_get_serial_number(uint16_t* serial_0, uint16_t* serial_1,
uint16_t* serial_2);
/**
* scd4x_perform_self_test() - The perform_self_test feature can be used as an
* end-of-line test to confirm sensor functionality.
*
* @param sensor_status 0 means no malfunction detected
*
* @return 0 on success, an error code otherwise
*/
int16_t scd4x_perform_self_test(uint16_t* sensor_status);
/**
* scd4x_perform_factory_reset() - Initiates the reset of all configurations
* stored in the EEPROM and erases the FRC and ASC algorithm history.
*
* @return 0 on success, an error code otherwise
*/
int16_t scd4x_perform_factory_reset(void);
/**
* scd4x_reinit() - The reinit command reinitializes the sensor by reloading
* user settings from EEPROM. Before sending the reinit command, the stop
* measurement command must be issued. If reinit command does not trigger the
* desired re-initialization, a power-cycle should be applied to the SCD4x.
*
* @note Only available in idle mode.
*
* @return 0 on success, an error code otherwise
*/
int16_t scd4x_reinit(void);
/**
* scd4x_measure_single_shot() - On-demand measurement of CO concentration,
* relative humidity and temperature. The sensor output is read with the
* read_measurement command.
*
* @note Only available in idle mode.
*
* @return 0 on success, an error code otherwise
*/
int16_t scd4x_measure_single_shot(void);
/**
* scd4x_measure_single_shot_rht_only() - On-demand measurement of relative
* humidity and temperature only.
*
* @note Only available in idle mode.
*
* @return 0 on success, an error code otherwise
*/
int16_t scd4x_measure_single_shot_rht_only(void);
/**
* scd4x_power_down() - Put the sensor from idle to sleep mode to reduce current
* consumption.
*
* @note Only available in idle mode.
*
* @return 0 on success, an error code otherwise
*/
int16_t scd4x_power_down(void);
/**
* scd4x_wake_up() - Wake up sensor from sleep mode to idle mode.
*
* @note Only available in sleep mode.
*
* @return 0 on success, an error code otherwise
*/
int16_t scd4x_wake_up(void);
#ifdef __cplusplus
}
#endif
#endif /* SCD4X_I2C_H */

181
include/sensirion_common.h Normal file
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 */

314
include/sensirion_i2c.h Normal file
View File

@ -0,0 +1,314 @@
/*
* 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,
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 */

112
include/sensirion_i2c_hal.h Normal file
View File

@ -0,0 +1,112 @@
/*
* 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"
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
/**
* Select the current i2c bus by index.
* All following i2c operations will be directed at that bus.
*
* THE IMPLEMENTATION IS OPTIONAL ON SINGLE-BUS SETUPS (all sensors on the same
* bus)
*
* @param bus_idx Bus index to select
* @returns 0 on success, an error code otherwise
*/
int16_t sensirion_i2c_hal_select_bus(uint8_t bus_idx);
/**
* Initialize all hard- and software components that are needed for the I2C
* communication.
*/
void sensirion_i2c_hal_init(int SDA, int SCL);
/**
* 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 */

452
scd4x_i2c.c Normal file
View File

@ -0,0 +1,452 @@
/*
* THIS FILE IS AUTOMATICALLY GENERATED AND MUST NOT BE EDITED MANUALLY!
*
* I2C-Generator: 0.2.0
* Yaml Version: 0.1.0
* Template Version: 0.2.1
*/
/*
* 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 "scd4x_i2c.h"
#include "sensirion_common.h"
#include "sensirion_i2c.h"
#include "sensirion_i2c_hal.h"
#define SCD4X_I2C_ADDRESS 98
int16_t scd4x_start_periodic_measurement() {
int16_t error;
uint8_t buffer[2];
uint16_t offset = 0;
offset = sensirion_i2c_add_command_to_buffer(&buffer[0], offset, 0x21B1);
error = sensirion_i2c_write_data(SCD4X_I2C_ADDRESS, &buffer[0], offset);
if (error) {
return error;
}
sensirion_i2c_hal_sleep_usec(1000);
return NO_ERROR;
}
int16_t scd4x_read_measurement_ticks(uint16_t* co2, uint16_t* temperature,
uint16_t* humidity) {
int16_t error;
uint8_t buffer[9];
uint16_t offset = 0;
offset = sensirion_i2c_add_command_to_buffer(&buffer[0], offset, 0xEC05);
error = sensirion_i2c_write_data(SCD4X_I2C_ADDRESS, &buffer[0], offset);
if (error) {
return error;
}
sensirion_i2c_hal_sleep_usec(1000);
error = sensirion_i2c_read_data_inplace(SCD4X_I2C_ADDRESS, &buffer[0], 6);
if (error) {
return error;
}
*co2 = sensirion_common_bytes_to_uint16_t(&buffer[0]);
*temperature = sensirion_common_bytes_to_uint16_t(&buffer[2]);
*humidity = sensirion_common_bytes_to_uint16_t(&buffer[4]);
return NO_ERROR;
}
int16_t scd4x_read_measurement(uint16_t* co2, int32_t* temperature_m_deg_c,
int32_t* humidity_m_percent_rh) {
int16_t error;
uint16_t temperature;
uint16_t humidity;
error = scd4x_read_measurement_ticks(co2, &temperature, &humidity);
if (error) {
return error;
}
*temperature_m_deg_c = ((21875 * (int32_t)temperature) >> 13) - 45000;
*humidity_m_percent_rh = ((12500 * (int32_t)humidity) >> 13);
return NO_ERROR;
}
int16_t scd4x_stop_periodic_measurement() {
int16_t error;
uint8_t buffer[2];
uint16_t offset = 0;
offset = sensirion_i2c_add_command_to_buffer(&buffer[0], offset, 0x3F86);
error = sensirion_i2c_write_data(SCD4X_I2C_ADDRESS, &buffer[0], offset);
if (error) {
return error;
}
sensirion_i2c_hal_sleep_usec(500000);
return NO_ERROR;
}
int16_t scd4x_get_temperature_offset_ticks(uint16_t* t_offset) {
int16_t error;
uint8_t buffer[3];
uint16_t offset = 0;
offset = sensirion_i2c_add_command_to_buffer(&buffer[0], offset, 0x2318);
error = sensirion_i2c_write_data(SCD4X_I2C_ADDRESS, &buffer[0], offset);
if (error) {
return error;
}
sensirion_i2c_hal_sleep_usec(1000);
error = sensirion_i2c_read_data_inplace(SCD4X_I2C_ADDRESS, &buffer[0], 2);
if (error) {
return error;
}
*t_offset = sensirion_common_bytes_to_uint16_t(&buffer[0]);
return NO_ERROR;
}
int16_t scd4x_get_temperature_offset(int32_t* t_offset_m_deg_c) {
int16_t error;
uint16_t t_offset;
error = scd4x_get_temperature_offset_ticks(&t_offset);
if (error) {
return error;
}
*t_offset_m_deg_c = ((21875 * (int32_t)t_offset) >> 13);
return NO_ERROR;
}
int16_t scd4x_set_temperature_offset_ticks(uint16_t t_offset) {
int16_t error;
uint8_t buffer[5];
uint16_t offset = 0;
offset = sensirion_i2c_add_command_to_buffer(&buffer[0], offset, 0x241D);
offset = sensirion_i2c_add_uint16_t_to_buffer(&buffer[0], offset, t_offset);
error = sensirion_i2c_write_data(SCD4X_I2C_ADDRESS, &buffer[0], offset);
if (error) {
return error;
}
sensirion_i2c_hal_sleep_usec(1000);
return NO_ERROR;
}
int16_t scd4x_set_temperature_offset(int32_t t_offset_m_deg_c) {
uint16_t t_offset = (uint16_t)((t_offset_m_deg_c * 12271) >> 15);
return scd4x_set_temperature_offset_ticks(t_offset);
}
int16_t scd4x_get_sensor_altitude(uint16_t* sensor_altitude) {
int16_t error;
uint8_t buffer[3];
uint16_t offset = 0;
offset = sensirion_i2c_add_command_to_buffer(&buffer[0], offset, 0x2322);
error = sensirion_i2c_write_data(SCD4X_I2C_ADDRESS, &buffer[0], offset);
if (error) {
return error;
}
sensirion_i2c_hal_sleep_usec(1000);
error = sensirion_i2c_read_data_inplace(SCD4X_I2C_ADDRESS, &buffer[0], 2);
if (error) {
return error;
}
*sensor_altitude = sensirion_common_bytes_to_uint16_t(&buffer[0]);
return NO_ERROR;
}
int16_t scd4x_set_sensor_altitude(uint16_t sensor_altitude) {
int16_t error;
uint8_t buffer[5];
uint16_t offset = 0;
offset = sensirion_i2c_add_command_to_buffer(&buffer[0], offset, 0x2427);
offset = sensirion_i2c_add_uint16_t_to_buffer(&buffer[0], offset,
sensor_altitude);
error = sensirion_i2c_write_data(SCD4X_I2C_ADDRESS, &buffer[0], offset);
if (error) {
return error;
}
sensirion_i2c_hal_sleep_usec(1000);
return NO_ERROR;
}
int16_t scd4x_set_ambient_pressure(uint16_t ambient_pressure) {
int16_t error;
uint8_t buffer[5];
uint16_t offset = 0;
offset = sensirion_i2c_add_command_to_buffer(&buffer[0], offset, 0xE000);
offset = sensirion_i2c_add_uint16_t_to_buffer(&buffer[0], offset,
ambient_pressure);
error = sensirion_i2c_write_data(SCD4X_I2C_ADDRESS, &buffer[0], offset);
if (error) {
return error;
}
sensirion_i2c_hal_sleep_usec(1000);
return NO_ERROR;
}
int16_t scd4x_perform_forced_recalibration(uint16_t target_co2_concentration,
uint16_t* frc_correction) {
int16_t error;
uint8_t buffer[5];
uint16_t offset = 0;
offset = sensirion_i2c_add_command_to_buffer(&buffer[0], offset, 0x362F);
offset = sensirion_i2c_add_uint16_t_to_buffer(&buffer[0], offset,
target_co2_concentration);
error = sensirion_i2c_write_data(SCD4X_I2C_ADDRESS, &buffer[0], offset);
if (error) {
return error;
}
sensirion_i2c_hal_sleep_usec(400000);
error = sensirion_i2c_read_data_inplace(SCD4X_I2C_ADDRESS, &buffer[0], 2);
if (error) {
return error;
}
*frc_correction = sensirion_common_bytes_to_uint16_t(&buffer[0]);
return NO_ERROR;
}
int16_t scd4x_get_automatic_self_calibration(uint16_t* asc_enabled) {
int16_t error;
uint8_t buffer[3];
uint16_t offset = 0;
offset = sensirion_i2c_add_command_to_buffer(&buffer[0], offset, 0x2313);
error = sensirion_i2c_write_data(SCD4X_I2C_ADDRESS, &buffer[0], offset);
if (error) {
return error;
}
sensirion_i2c_hal_sleep_usec(1000);
error = sensirion_i2c_read_data_inplace(SCD4X_I2C_ADDRESS, &buffer[0], 2);
if (error) {
return error;
}
*asc_enabled = sensirion_common_bytes_to_uint16_t(&buffer[0]);
return NO_ERROR;
}
int16_t scd4x_set_automatic_self_calibration(uint16_t asc_enabled) {
int16_t error;
uint8_t buffer[5];
uint16_t offset = 0;
offset = sensirion_i2c_add_command_to_buffer(&buffer[0], offset, 0x2416);
offset =
sensirion_i2c_add_uint16_t_to_buffer(&buffer[0], offset, asc_enabled);
error = sensirion_i2c_write_data(SCD4X_I2C_ADDRESS, &buffer[0], offset);
if (error) {
return error;
}
sensirion_i2c_hal_sleep_usec(1000);
return NO_ERROR;
}
int16_t scd4x_start_low_power_periodic_measurement() {
uint8_t buffer[2];
uint16_t offset = 0;
offset = sensirion_i2c_add_command_to_buffer(&buffer[0], offset, 0x21AC);
return sensirion_i2c_write_data(SCD4X_I2C_ADDRESS, &buffer[0], offset);
}
int16_t scd4x_get_data_ready_status(uint16_t* data_ready) {
int16_t error;
uint8_t buffer[3];
uint16_t offset = 0;
offset = sensirion_i2c_add_command_to_buffer(&buffer[0], offset, 0xE4B8);
error = sensirion_i2c_write_data(SCD4X_I2C_ADDRESS, &buffer[0], offset);
if (error) {
return error;
}
sensirion_i2c_hal_sleep_usec(1000);
error = sensirion_i2c_read_data_inplace(SCD4X_I2C_ADDRESS, &buffer[0], 2);
if (error) {
return error;
}
*data_ready = sensirion_common_bytes_to_uint16_t(&buffer[0]);
return NO_ERROR;
}
int16_t scd4x_persist_settings() {
int16_t error;
uint8_t buffer[2];
uint16_t offset = 0;
offset = sensirion_i2c_add_command_to_buffer(&buffer[0], offset, 0x3615);
error = sensirion_i2c_write_data(SCD4X_I2C_ADDRESS, &buffer[0], offset);
if (error) {
return error;
}
sensirion_i2c_hal_sleep_usec(800000);
return NO_ERROR;
}
int16_t scd4x_get_serial_number(uint16_t* serial_0, uint16_t* serial_1,
uint16_t* serial_2) {
int16_t error;
uint8_t buffer[9];
uint16_t offset = 0;
offset = sensirion_i2c_add_command_to_buffer(&buffer[0], offset, 0x3682);
error = sensirion_i2c_write_data(SCD4X_I2C_ADDRESS, &buffer[0], offset);
if (error) {
return error;
}
sensirion_i2c_hal_sleep_usec(1000);
error = sensirion_i2c_read_data_inplace(SCD4X_I2C_ADDRESS, &buffer[0], 6);
if (error) {
return error;
}
*serial_0 = sensirion_common_bytes_to_uint16_t(&buffer[0]);
*serial_1 = sensirion_common_bytes_to_uint16_t(&buffer[2]);
*serial_2 = sensirion_common_bytes_to_uint16_t(&buffer[4]);
return NO_ERROR;
}
int16_t scd4x_perform_self_test(uint16_t* sensor_status) {
int16_t error;
uint8_t buffer[3];
uint16_t offset = 0;
offset = sensirion_i2c_add_command_to_buffer(&buffer[0], offset, 0x3639);
error = sensirion_i2c_write_data(SCD4X_I2C_ADDRESS, &buffer[0], offset);
if (error) {
return error;
}
sensirion_i2c_hal_sleep_usec(10000000);
error = sensirion_i2c_read_data_inplace(SCD4X_I2C_ADDRESS, &buffer[0], 2);
if (error) {
return error;
}
*sensor_status = sensirion_common_bytes_to_uint16_t(&buffer[0]);
return NO_ERROR;
}
int16_t scd4x_perform_factory_reset() {
int16_t error;
uint8_t buffer[2];
uint16_t offset = 0;
offset = sensirion_i2c_add_command_to_buffer(&buffer[0], offset, 0x3632);
error = sensirion_i2c_write_data(SCD4X_I2C_ADDRESS, &buffer[0], offset);
if (error) {
return error;
}
sensirion_i2c_hal_sleep_usec(800000);
return NO_ERROR;
}
int16_t scd4x_reinit() {
int16_t error;
uint8_t buffer[2];
uint16_t offset = 0;
offset = sensirion_i2c_add_command_to_buffer(&buffer[0], offset, 0x3646);
error = sensirion_i2c_write_data(SCD4X_I2C_ADDRESS, &buffer[0], offset);
if (error) {
return error;
}
sensirion_i2c_hal_sleep_usec(20000);
return NO_ERROR;
}
int16_t scd4x_measure_single_shot() {
int16_t error;
uint8_t buffer[2];
uint16_t offset = 0;
offset = sensirion_i2c_add_command_to_buffer(&buffer[0], offset, 0x219D);
error = sensirion_i2c_write_data(SCD4X_I2C_ADDRESS, &buffer[0], offset);
if (error) {
return error;
}
sensirion_i2c_hal_sleep_usec(5000000);
return NO_ERROR;
}
int16_t scd4x_measure_single_shot_rht_only() {
int16_t error;
uint8_t buffer[2];
uint16_t offset = 0;
offset = sensirion_i2c_add_command_to_buffer(&buffer[0], offset, 0x2196);
error = sensirion_i2c_write_data(SCD4X_I2C_ADDRESS, &buffer[0], offset);
if (error) {
return error;
}
sensirion_i2c_hal_sleep_usec(50000);
return NO_ERROR;
}
int16_t scd4x_power_down() {
int16_t error;
uint8_t buffer[2];
uint16_t offset = 0;
offset = sensirion_i2c_add_command_to_buffer(&buffer[0], offset, 0x36E0);
error = sensirion_i2c_write_data(SCD4X_I2C_ADDRESS, &buffer[0], offset);
if (error) {
return error;
}
sensirion_i2c_hal_sleep_usec(1000);
return NO_ERROR;
}
int16_t scd4x_wake_up() {
uint8_t buffer[2];
uint16_t offset = 0;
offset = sensirion_i2c_add_command_to_buffer(&buffer[0], offset, 0x36F6);
// Sensor does not acknowledge the wake-up call, error is ignored
(void)sensirion_i2c_write_data(SCD4X_I2C_ADDRESS, &buffer[0], offset);
sensirion_i2c_hal_sleep_usec(20000);
return NO_ERROR;
}

101
sensirion_common.c Normal file
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];
}
}

294
sensirion_i2c.c Normal file
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,
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;
}

148
sensirion_i2c_hal.c Normal file
View File

@ -0,0 +1,148 @@
/*
* 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"
#include "driver/i2c.h"
#include <stdio.h>
/*
* INSTRUCTIONS
* ============
*
* Implement all functions where they are marked as IMPLEMENT.
* Follow the function specification in the comments.
*/
/**
* Select the current i2c bus by index.
* All following i2c operations will be directed at that bus.
*
* THE IMPLEMENTATION IS OPTIONAL ON SINGLE-BUS SETUPS (all sensors on the same
* bus)
*
* @param bus_idx Bus index to select
* @returns 0 on success, an error code otherwise
*/
int16_t sensirion_i2c_hal_select_bus(uint8_t bus_idx) {
/* TODO:IMPLEMENT or leave empty if all sensors are located on one single
* bus
*/
return NOT_IMPLEMENTED_ERROR;
}
/**
* Initialize all hard- and software components that are needed for the I2C
* communication.
*/
void sensirion_i2c_hal_init(int SDA, int SCL) {
int i2c_master_port = I2C_NUM_0;
i2c_config_t conf = {
.mode = I2C_MODE_MASTER,
.sda_io_num = SDA, // select GPIO specific to your project
.sda_pullup_en = GPIO_PULLUP_ENABLE,
.scl_io_num = SCL, // select GPIO specific to your project
.scl_pullup_en = GPIO_PULLUP_ENABLE,
.master.clk_speed = 10000, // select frequency specific to your project
// .clk_flags = 0, /*!< Optional, you can use I2C_SCLK_SRC_FLAG_* flags to choose i2c source clock here. */
};
i2c_param_config(i2c_master_port, &conf);
i2c_driver_install(i2c_master_port, I2C_MODE_MASTER, 0, 0, 0);
}
/**
* Release all resources initialized by sensirion_i2c_hal_init().
*/
void sensirion_i2c_hal_free(void) {
i2c_driver_delete(I2C_NUM_0); /* TODO:non const port */
}
/**
* 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) {
i2c_cmd_handle_t cmdLnk = i2c_cmd_link_create();
esp_err_t err = ESP_OK;
err = i2c_master_start(cmdLnk);
err = i2c_master_write_byte(cmdLnk, address << 1 | I2C_MASTER_READ, true);
err = i2c_master_read(cmdLnk, data, count-1, I2C_MASTER_ACK);
err = i2c_master_read(cmdLnk, data + count - 1, 1, I2C_MASTER_NACK);
err = i2c_master_stop(cmdLnk);
i2c_master_cmd_begin(I2C_NUM_0,cmdLnk,1000 / portTICK_RATE_MS);
i2c_cmd_link_delete(cmdLnk);
return err;
}
/**
* 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) {
i2c_cmd_handle_t cmdLnk = i2c_cmd_link_create();
esp_err_t err = ESP_OK;
err = i2c_master_start(cmdLnk);
err = i2c_master_write_byte(cmdLnk, address << 1 | I2C_MASTER_WRITE, true);
err = i2c_master_write(cmdLnk, data, count, true);
err = i2c_master_stop(cmdLnk);
i2c_master_cmd_begin(I2C_NUM_0,cmdLnk,1000 / portTICK_RATE_MS); /* TODO:non const port */
i2c_cmd_link_delete(cmdLnk);
return err;
}
/**
* 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) {
vTaskDelay(useconds / portTICK_PERIOD_MS / 1000);
}