Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion src/integration_tests/ruuvi_driver_sensor_test.c
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ return status; \
#define MAX_SENSORS (sizeof(rd_sensor_data_fields_t)* MAX_BITS_PER_BYTE)
#define MAX_RETRIES (50U) //!< Number of times to run test on statistics-dependent tests, such as sampling noise.
#define MAX_FIFO_DEPTH (32U) //!< How many samples to fetch from FIFO at max
#define MAX_SENSOR_PROVIDED_FIELDS (4U) //!< Largest number of different fields tested sensor can have.
#define MAX_SENSOR_PROVIDED_FIELDS (8U) //!< Largest number of different fields tested sensor can have.

#define LOG_PRINT_DELAY_MS (10U)

Expand Down Expand Up @@ -1069,6 +1069,7 @@ void rd_sensor_data_print (const rd_sensor_data_t * const p_data,
"presence",
"motion",
"ir_object",
"temperature_shock"
};

/* Count enabled sensors */
Expand Down
147 changes: 133 additions & 14 deletions src/interfaces/environmental/ruuvi_interface_sths34pf80.c
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,12 @@ typedef struct
int16_t tobject_raw; //!< Last IR object signal raw value.
uint8_t presence_flag; //!< Last presence flag from FUNC_STATUS.
uint8_t motion_flag; //!< Last motion flag from FUNC_STATUS.
uint8_t tamb_shock_flag; //!< Last ambient shock flag from FUNC_STATUS.
bool algo_valid; //!< Algorithm outputs valid (false after temp shock until recalibrated).
#if SHTS_DEBUG_DATA_IN_ACCELERATION
int16_t tpresence_raw; //!< Last presence algorithm value (debug).
int16_t tmotion_raw; //!< Last motion algorithm value (debug).
#endif
} ri_sths34pf80_ctx_t;

static ri_sths34pf80_ctx_t m_ctx = {0}; //!< Sensor context singleton.
Expand Down Expand Up @@ -122,9 +128,32 @@ static rd_status_t read_sample (void)
err_code |= st_to_ruuvi_error (st_err);
st_err = sths34pf80_tobject_raw_get (&m_ctx.ctx, &m_ctx.tobject_raw);
err_code |= st_to_ruuvi_error (st_err);
#if SHTS_DEBUG_DATA_IN_ACCELERATION
// Read algorithm state values for debugging
st_err = sths34pf80_tpresence_raw_get (&m_ctx.ctx, &m_ctx.tpresence_raw);
err_code |= st_to_ruuvi_error (st_err);
st_err = sths34pf80_tmotion_raw_get (&m_ctx.ctx, &m_ctx.tmotion_raw);
err_code |= st_to_ruuvi_error (st_err);
#endif
// Store flags
m_ctx.presence_flag = func_status.pres_flag;
m_ctx.motion_flag = func_status.mot_flag;
m_ctx.tamb_shock_flag = func_status.tamb_shock_flag;

// Reset algorithm state on temperature shock detection.
// This allows the sensor to recalibrate after rapid ambient temperature changes.
// Mark algorithm outputs as invalid until next sample without shock.
if (func_status.tamb_shock_flag)
{
st_err = sths34pf80_algo_reset (&m_ctx.ctx);
err_code |= st_to_ruuvi_error (st_err);
m_ctx.algo_valid = false;
}
else
{
m_ctx.algo_valid = true;
}

// Record timestamp
m_ctx.tsample = rd_sensor_timestamp_get();
return err_code;
Expand Down Expand Up @@ -207,11 +236,6 @@ rd_status_t ri_sths34pf80_init (rd_sensor_t * p_sensor, rd_bus_t bus, uint8_t ha
st_err = sths34pf80_boot_set (&m_ctx.ctx, 1);
err_code |= st_to_ruuvi_error (st_err);
ri_delay_ms (BOOT_DELAY_MS);
// Configure default settings: ODR off (sleep mode), BDU enabled
st_err = sths34pf80_odr_set (&m_ctx.ctx, STHS34PF80_ODR_OFF);
err_code |= st_to_ruuvi_error (st_err);
st_err = sths34pf80_block_data_update_set (&m_ctx.ctx, 1);
err_code |= st_to_ruuvi_error (st_err);

if (RD_SUCCESS != err_code)
{
Expand All @@ -228,6 +252,21 @@ rd_status_t ri_sths34pf80_init (rd_sensor_t * p_sensor, rd_bus_t bus, uint8_t ha
m_ctx.tobject_raw = 0;
m_ctx.presence_flag = 0;
m_ctx.motion_flag = 0;
m_ctx.tamb_shock_flag = 0;
m_ctx.algo_valid = false;
#if SHTS_DEBUG_DATA_IN_ACCELERATION
m_ctx.tpresence_raw = 0;
m_ctx.tmotion_raw = 0;
#endif
// Apply default algorithm configuration (thresholds, hysteresis, averaging)
err_code |= ri_sths34pf80_configure_defaults();

if (RD_SUCCESS != err_code)
{
rd_sensor_uninitialize (p_sensor);
return err_code;
}

// Setup sensor struct
p_sensor->init = ri_sths34pf80_init;
p_sensor->uninit = ri_sths34pf80_uninit;
Expand All @@ -245,6 +284,12 @@ rd_status_t ri_sths34pf80_init (rd_sensor_t * p_sensor, rd_bus_t bus, uint8_t ha
p_sensor->configuration_set = rd_sensor_configuration_set;
p_sensor->configuration_get = rd_sensor_configuration_get;
// Define provided data fields
#if SHTS_DEBUG_DATA_IN_ACCELERATION
p_sensor->provides.datas.acceleration_x_g = 1; // Debug: tobject
p_sensor->provides.datas.acceleration_y_g = 1; // Debug: tmotion
p_sensor->provides.datas.acceleration_z_g = 1; // Debug: tpresence
p_sensor->provides.datas.debug_tamb = 1; // Debug: tamb_shock
#endif
p_sensor->provides.datas.temperature_c = 1;
p_sensor->provides.datas.presence = 1;
p_sensor->provides.datas.motion = 1;
Expand Down Expand Up @@ -717,30 +762,104 @@ rd_status_t ri_sths34pf80_data_get (rd_sensor_data_t * const data)
// Note: Only fields that are requested AND provided will be populated
rd_sensor_data_t d_environmental = {0};
rd_sensor_data_fields_t env_fields = {0};
// Ambient temperature in Celsius
float values[4] = {0};
values[0] = tambient_to_celsius (m_ctx.tambient_raw);
values[1] = (float) m_ctx.presence_flag;
values[2] = (float) m_ctx.motion_flag;
values[3] = (float) m_ctx.tobject_raw; // Dimensionless IR signal
// Populate data array using indexed constants
float values[SHTS_DATA_FIELD_COUNT] = {0};
values[STHS34PF80_TAMBIENT_C] = tambient_to_celsius (m_ctx.tambient_raw);
values[STHS34PF80_PRESENCE_FLAG] = (float) m_ctx.presence_flag;
values[STHS34PF80_MOTION_FLAG] = (float) m_ctx.motion_flag;
values[STHS34PF80_TOBJECT_RAW] = (float) m_ctx.tobject_raw;
#if SHTS_DEBUG_DATA_IN_ACCELERATION
// Debug: Algorithm state values in acceleration format for easier plotting
values[STHS34PF80_DEBUG_TOBJECT] = (float) m_ctx.tobject_raw / 1000.0f;
values[STHS34PF80_DEBUG_TMOTION] = (float) m_ctx.tmotion_raw / 1000.0f;
values[STHS34PF80_DEBUG_TPRESENCE] = (float) m_ctx.tpresence_raw / 1000.0f;
values[STHS34PF80_DEBUG_TAMB_SHOCK] = (float) m_ctx.tamb_shock_flag;
#endif
// Set fields that this sensor provides
rd_sensor_data_fields_t provided_fields = {0};
#if SHTS_DEBUG_DATA_IN_ACCELERATION
provided_fields.datas.acceleration_x_g = 1; // Debug: tobject
provided_fields.datas.acceleration_y_g = 1; // Debug: tmotion
provided_fields.datas.acceleration_z_g = 1; // Debug: tpresence
provided_fields.datas.debug_tamb = 1; // Debug: tamb_shock
#endif
provided_fields.datas.temperature_c = 1;
provided_fields.datas.presence = 1;
provided_fields.datas.motion = 1;
provided_fields.datas.ir_object = 1;

// Only mark fields valid if a sample has actually been taken
if (RD_UINT64_INVALID != m_ctx.tsample)
{
#if SHTS_DEBUG_DATA_IN_ACCELERATION
env_fields.datas.acceleration_x_g = 1; // Debug: tobject
env_fields.datas.debug_tamb = 1; // Debug: tamb_shock
#endif
env_fields.datas.temperature_c = 1;
env_fields.datas.presence = 1;
env_fields.datas.motion = 1;
env_fields.datas.ir_object = 1;

// Algorithm-derived fields are only valid if no temperature shock occurred
if (m_ctx.algo_valid)
{
#if SHTS_DEBUG_DATA_IN_ACCELERATION
env_fields.datas.acceleration_y_g = 1; // Debug: tmotion
env_fields.datas.acceleration_z_g = 1; // Debug: tpresence
#endif
env_fields.datas.presence = 1;
env_fields.datas.motion = 1;
}
}

d_environmental.data = values;
d_environmental.valid = env_fields;
d_environmental.fields = env_fields;
d_environmental.fields = provided_fields;
d_environmental.timestamp_ms = m_ctx.tsample;
rd_sensor_data_populate (data, &d_environmental, data->fields);
data->timestamp_ms = m_ctx.tsample;
return err_code;
}

rd_status_t ri_sths34pf80_configure_defaults (void)
{
rd_status_t err_code = RD_SUCCESS;
int32_t st_err = 0;
VERIFY_SENSOR_SLEEPS();
// Configure ODR off (sleep mode) and BDU enabled
st_err = sths34pf80_odr_set (&m_ctx.ctx, STHS34PF80_ODR_OFF);
err_code |= st_to_ruuvi_error (st_err);
st_err = sths34pf80_block_data_update_set (&m_ctx.ctx, 1);
err_code |= st_to_ruuvi_error (st_err);
// Set averaging: AVG_TMOS = 32, AVG_TAMB = 8
st_err = sths34pf80_avg_tobject_num_set (&m_ctx.ctx, RI_STHS34PF80_AVG_TMOS_DEFAULT);
err_code |= st_to_ruuvi_error (st_err);
st_err = sths34pf80_avg_tambient_num_set (&m_ctx.ctx, RI_STHS34PF80_AVG_TAMB_DEFAULT);
err_code |= st_to_ruuvi_error (st_err);
// Set tambient shock threshold and hysteresis
st_err = sths34pf80_tambient_shock_threshold_set (&m_ctx.ctx,
RI_STHS34PF80_TAMB_SHOCK_THS_DEFAULT);
err_code |= st_to_ruuvi_error (st_err);
st_err = sths34pf80_tambient_shock_hysteresis_set (&m_ctx.ctx,
RI_STHS34PF80_TAMB_SHOCK_HYS_DEFAULT);
err_code |= st_to_ruuvi_error (st_err);
// Set presence threshold and hysteresis
st_err = sths34pf80_presence_threshold_set (&m_ctx.ctx,
RI_STHS34PF80_PRESENCE_THS_DEFAULT);
err_code |= st_to_ruuvi_error (st_err);
st_err = sths34pf80_presence_hysteresis_set (&m_ctx.ctx,
RI_STHS34PF80_PRESENCE_HYS_DEFAULT);
err_code |= st_to_ruuvi_error (st_err);
// Set motion threshold and hysteresis
st_err = sths34pf80_motion_threshold_set (&m_ctx.ctx,
RI_STHS34PF80_MOTION_THS_DEFAULT);
err_code |= st_to_ruuvi_error (st_err);
st_err = sths34pf80_motion_hysteresis_set (&m_ctx.ctx,
RI_STHS34PF80_MOTION_HYS_DEFAULT);
err_code |= st_to_ruuvi_error (st_err);
// Reset algorithm to apply new settings
st_err = sths34pf80_algo_reset (&m_ctx.ctx);
err_code |= st_to_ruuvi_error (st_err);
return err_code;
}

/** @} */
#endif // RI_STHS34PF80_ENABLED
53 changes: 53 additions & 0 deletions src/interfaces/environmental/ruuvi_interface_sths34pf80.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#if RI_STHS34PF80_ENABLED || DOXYGEN
#include "ruuvi_driver_error.h"
#include "ruuvi_driver_sensor.h"
#include "sths34pf80_reg.h"

/**
* @addtogroup Environmental
Expand Down Expand Up @@ -48,6 +49,29 @@
* @endcode
*/

#define SHTS_DEBUG_DATA_IN_ACCELERATION (1U) //!< Enable to log raw data in acceleration format for easier debugging.
#define SHTS_DATA_FIELD_COUNT (4U + (4 * SHTS_DEBUG_DATA_IN_ACCELERATION)) //!< 4 base fields + 4 debug fields (3 accel + 1 tamb_shock) when debug enabled

// Data field array indices
// NOTE: These indices are relative to the enabled fields in the bitfield.
// The rd_sensor_data_populate function counts set bits to determine array placement.
// When debug is enabled, the first 3 positions are debug acceleration values, base fields start at index 3, and index 7 holds debug_tamb_shock.
#if SHTS_DEBUG_DATA_IN_ACCELERATION
#define STHS34PF80_DEBUG_TOBJECT (0) //!< Debug: IR object raw (as accel_x)
#define STHS34PF80_DEBUG_TMOTION (1) //!< Debug: Motion algo (as accel_y)
#define STHS34PF80_DEBUG_TPRESENCE (2) //!< Debug: Presence algo (as accel_z)
#define STHS34PF80_TAMBIENT_C (3) //!< Ambient temperature in Celsius
#define STHS34PF80_PRESENCE_FLAG (4) //!< Presence detection flag
#define STHS34PF80_MOTION_FLAG (5) //!< Motion detection flag
#define STHS34PF80_TOBJECT_RAW (6) //!< IR object signal (dimensionless)
#define STHS34PF80_DEBUG_TAMB_SHOCK (7) //!< Debug: Ambient shock value
#else
#define STHS34PF80_TAMBIENT_C (0) //!< Ambient temperature in Celsius
#define STHS34PF80_PRESENCE_FLAG (1) //!< Presence detection flag
#define STHS34PF80_MOTION_FLAG (2) //!< Motion detection flag
#define STHS34PF80_TOBJECT_RAW (3) //!< IR object signal (dimensionless)
#endif

#define STHS34PF80_I2C_ADDR_DEFAULT (0x5AU) //!< Default I2C address (7-bit)
// Note: WHO_AM_I value (0xD3) is defined as STHS34PF80_ID in sths34pf80_reg.h

Expand All @@ -60,6 +84,20 @@
#define RI_STHS34PF80_SAMPLERATE_0HZ25 RD_SENSOR_CFG_CUSTOM_1 //!< 0.25 Hz (4 second period)
#define RI_STHS34PF80_SAMPLERATE_0HZ50 RD_SENSOR_CFG_CUSTOM_2 //!< 0.50 Hz (2 second period)

/**
* @brief Default algorithm configuration values from ST reference example.
* @{
*/
#define RI_STHS34PF80_AVG_TMOS_DEFAULT STHS34PF80_AVG_TMOS_32 //!< TMOS averaging: 32 samples
#define RI_STHS34PF80_AVG_TAMB_DEFAULT STHS34PF80_AVG_T_8 //!< Tambient averaging: 8 samples
#define RI_STHS34PF80_TAMB_SHOCK_THS_DEFAULT (35U) //!< Ambient shock threshold [LSB]
#define RI_STHS34PF80_TAMB_SHOCK_HYS_DEFAULT (5U) //!< Ambient shock hysteresis [LSB]
#define RI_STHS34PF80_PRESENCE_THS_DEFAULT (200U) //!< Presence threshold [LSB]
#define RI_STHS34PF80_PRESENCE_HYS_DEFAULT (20U) //!< Presence hysteresis [LSB]
#define RI_STHS34PF80_MOTION_THS_DEFAULT (300U) //!< Motion threshold [LSB]
#define RI_STHS34PF80_MOTION_HYS_DEFAULT (30U) //!< Motion hysteresis [LSB]
/** @} */

/** @brief @ref rd_sensor_init_fp */
rd_status_t ri_sths34pf80_init (rd_sensor_t * p_sensor, rd_bus_t bus, uint8_t handle);
/** @brief @ref rd_sensor_init_fp */
Expand Down Expand Up @@ -87,6 +125,21 @@ rd_status_t ri_sths34pf80_mode_get (uint8_t * mode);
/** @brief @ref rd_sensor_data_fp */
rd_status_t ri_sths34pf80_data_get (rd_sensor_data_t * const data);

/**
* @brief Configure STHS34PF80 algorithm parameters with recommended defaults.
*
* Applies averaging, threshold, and hysteresis settings from ST reference example.
* This configuration is applied automatically during @ref ri_sths34pf80_init().
* Optionally, this function may be called again after init while the sensor is in
* sleep mode, for example to (re)apply or adjust the configuration before starting
* continuous mode.
*
* @return RD_SUCCESS on success.
* @return RD_ERROR_INVALID_STATE if sensor is not in sleep mode.
* @return RD_ERROR_INTERNAL on driver error.
*/
rd_status_t ri_sths34pf80_configure_defaults (void);

/** @} */
#endif // RI_STHS34PF80_ENABLED
#endif // RUUVI_INTERFACE_STHS34PF80_H
5 changes: 4 additions & 1 deletion src/ruuvi_driver_sensor.h
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,8 @@ typedef struct
unsigned int presence : 1; //!< Presence detected, boolean.
unsigned int motion : 1; //!< Motion detected, boolean.
unsigned int ir_object : 1; //!< IR object signal, dimensionless.
unsigned int reserved: 7; //!< Reserved bits, force remainder of bitfield to 0.
unsigned int debug_tamb : 1; //!< Debug: ambient shock value. Will be deleted.
unsigned int reserved: 6; //!< Reserved bits, force remainder of bitfield to 0.
} rd_sensor_data_bitfield_t;

/**
Expand Down Expand Up @@ -199,6 +200,8 @@ typedef struct
#define RD_SENSOR_MOTION_FIELD ((rd_sensor_data_fields_t){.datas.motion=1})
/** @brief Shorthand for calling rd_sensor_data_parse(p_data, FIELD) */
#define RD_SENSOR_IR_OBJ_FIELD ((rd_sensor_data_fields_t){.datas.ir_object=1})
/** @brief Shorthand for calling rd_sensor_data_parse(p_data, FIELD). Will be deleted. */
#define RD_SENSOR_DEBUG_TAMB_FIELD ((rd_sensor_data_fields_t){.datas.debug_tamb=1})



Expand Down
Loading
Loading