Skip to content

Commit

Permalink
Updated wrt test
Browse files Browse the repository at this point in the history
  • Loading branch information
edward62740 committed May 22, 2023
1 parent 4becdf2 commit 22358ee
Show file tree
Hide file tree
Showing 3 changed files with 105 additions and 82 deletions.
14 changes: 11 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
The SCD4x sensor has a built-in Automatic Self-Calibration (ASC) that is not available for ultra-low power applications that require power-down of the sensor between measurements.

This C++ algorithm attempts to emulate the behaviors of the ASC in single-shot mode, based on the ASC Application Note (p. 2-5,11-13). The key assumption is that the lowest measured co2 is no less than SCD4X_LPC_BASELINE_CO2_PPM.<br>
Do **not** use this library (or the ASC) if this assumption does not hold for the application requirements.<br>
Do **not** use this (or the ASC) if the assumption does not hold for the application requirements.<br>

![Graph](https://github.com/edward62740/SCD4x-LPC/blob/master/graph.png)
<br>*ASC Application Note (p. 5)*
Expand All @@ -23,7 +23,16 @@ In general, this algorithm has a FSM that goes through 3 states:
- Standard: Standard period of 2016 measurements (recurring)

<br> Wherein the FRC is allowed to run once per period, when the reading is below baseline OR at the end of the period, whichever is earlier.
The maximum offset per period is also varied.
The calibration is defined by a signed offset value for each period, where a measurement point $t \in [0, \text{NUM MEAS}] \cap \mathbb{Z}$, with $f(t)$ representing the co2 reading at point $t$:

```math
\text{{offset}} = \begin{cases}
\text{{BASELINE}} - f(t), & \text{{if }} f(t) < \text{{BASELINE}} \
\text{{BASELINE}} - \min(f(t)), & \text{{otherwise}}
\end{cases}
```
Note that in the case where $f(t) < \text{{BASELINE}}$, accuracy may only be restored over a few periods, as the calibration point may represent only a local minimum.

All the thresholds/configs are user-definable in the header file.

Expand All @@ -32,7 +41,6 @@ Initialization requires the necessary SCD4x driver function pointers to be passe
LPC::SCD4X sensor(scd4x_wake_up, scd4x_power_down, scd4x_measure_single_shot,
scd4x_get_data_ready_flag, scd4x_read_measurement, scd4x_perform_forced_recalibration,
scd4x_persist_settings, sl_sleeptimer_get_tick_count);
}
```

It is recommended to disable ASC and set persistent settings prior to using this algorithm.
4 changes: 1 addition & 3 deletions scd4x_lpc.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
#include "scd4x_lpc.h"
#include "scd4x_i2c.h"


namespace LPC {

SCD4X::SCD4X(int16_t (*wu)(void), int16_t (*pd)(void), int16_t (*meas)(void),
Expand Down Expand Up @@ -39,6 +38,7 @@ lpc_ret SCD4X::sig_ext_reset_fsm(void) {
fsm.ttl = _sig_get_fsm_cnt();
fsm.limit = false;
fsm._cons_fail = 0;
_mutex.state = false;
return ERR_NONE;
}

Expand Down Expand Up @@ -140,7 +140,6 @@ lpc_ret SCD4X::measure()
uint16_t ret = 0xFFFF;
scd_fp.frc(co2 - (fsm.limit ? _sig_get_fsm_offset() : offset),
&ret);

_mutex_request_lock(SCD4X_LPC_CMD_FRC_LOCK_DUR_MS);
frc_ctr++;
prev_frc_offset = ret - 0x8000U;
Expand Down Expand Up @@ -204,7 +203,6 @@ lpc_ret SCD4X::_sig_scd_fail(bool trig)
return ERR_NONE;
}
else fsm._cons_fail++;

return ERR_SCD;
}

Expand Down
169 changes: 93 additions & 76 deletions scd4x_lpc.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,6 @@
#include "stdint.h"
#include <functional>
#include <iostream>
#include <queue>
#include <string_view>
#include <vector>

const uint16_t SCD4X_LPC_BASELINE_CO2_PPM = 400;
const uint32_t SCD4X_LPC_FIRST_CAL_TRIG_CNT = 1;
Expand All @@ -37,71 +34,91 @@ const uint32_t SCD4X_LPC_CMD_PERSIST_LOCK_DUR_MS = 800;

namespace LPC {

typedef enum {
ERR_NONE, ERR_SCD, ERR_LPC, ERR_WAIT, ERR_PANIC, WARN_PRETRIG
} lpc_ret;

class SCD4X
{
public:
SCD4X(int16_t (*wu)(void), int16_t (*pd)(void), int16_t (*meas)(void),
int16_t (*drdy)(bool *ready), int16_t (*read)(uint16_t* co2, int32_t* temp, int32_t* hum),
int16_t (*frc)(uint16_t target, uint16_t* corr), int16_t (*persist)(void),
uint32_t (*getMsTick)(void));

lpc_ret powerOn(void);
lpc_ret powerOff(void);


/**
* @brief This function is called after startup to perform a sensor read.
* The reading is invalid and nothing is returned.
* No calls to this API for 5000ms after this should be made.
* @retval lpc_ret error code
*/
lpc_ret discardMeasurement(void);



lpc_ret measure(void);

/**
* @brief This function is called at least 5000ms after measure() and returns
* sensor measurement data and performs FRC if applicable.
*
*
* @param co2,temp,hum pointers to store sensor data
* @param yield pointer to app function
* @retval lpc_ret error code
*/
lpc_ret read(uint16_t *co2, int32_t *temp, int32_t *hum);

void get_last_frc(int32_t *offset, uint32_t *age, uint32_t *num);

lpc_ret sig_ext_reset_fsm(void);
private:

/* Struct of function ptrs to sensor driver */
struct
{
int16_t (*wu)( void );
int16_t (*pd)( void );
int16_t (*meas)( void );
int16_t (*drdy)(bool *ready );
int16_t (*read)(uint16_t* co2, int32_t* temp, int32_t* hum);
int16_t (*frc)(uint16_t target, uint16_t* corr);
int16_t (*persist)( void );
uint32_t (*getMsTick)(void);
} scd_fp;

typedef enum
{
STATE_FIRST,
STATE_INIT,
STATE_STD,
typedef enum {
ERR_NONE, ERR_SCD, ERR_LPC, ERR_WAIT, ERR_PANIC, WARN_PRETRIG
} lpc_ret;

class SCD4X {
public:
SCD4X(int16_t (*wu)(void), int16_t (*pd)(void), int16_t (*meas)(void),
int16_t (*drdy)(bool *ready),
int16_t (*read)(uint16_t *co2, int32_t *temp, int32_t *hum),
int16_t (*frc)(uint16_t target, uint16_t *corr),
int16_t (*persist)(void), uint32_t (*getMsTick)(void));

/**
* @brief This function powers up the sensor.
* No calls to this API for 20ms after this should be made.
* @retval lpc_ret error code
*/
lpc_ret powerOn(void);

/**
* @brief This function powers down the sensor
* @retval lpc_ret error code
*/
lpc_ret powerOff(void);

/**
* @brief This function is called after startup to perform a sensor read.
* The reading is invalid and nothing is returned.
* No calls to this API for 5000ms after this should be made.
* @retval lpc_ret error code
*/
lpc_ret discardMeasurement(void);

/**
* @brief This function is called after discardMeasurement() to perform a sensor read.
* No calls to this API for 5000ms after this should be made.
* @retval lpc_ret error code, WARN_PRETRIG if next call to read() will do FRC.
*/
lpc_ret measure(void);

/**
* @brief This function is called at least 5000ms after measure() and returns
* sensor measurement data and performs FRC if applicable.
* The user application must be able to handle random FRC should this
* function be called. The maximum execution time is <410ms.
* No calls to this API for 410ms after this should be made.
* @param co2,temp,hum pointers to store sensor data
* @retval lpc_ret error code
*/
lpc_ret read(uint16_t *co2, int32_t *temp, int32_t *hum);

/**
* @brief This function retrieves the last calibration info and total count.
* @param offset,age,num pointers to store calibration info
* @retval none
*/
void get_last_frc(int32_t *offset, uint32_t *age, uint32_t *num);

/**
* @brief This function resets the internal finite state machine.
* Should be used in the case of recoverable faults, signaled by ERR_PANIC.
* Hence, the calibration history will persist.
* @param co2,temp,hum pointers to store sensor data
* @retval lpc_ret error code
*/
lpc_ret sig_ext_reset_fsm(void);
private:

/* Struct of function ptrs to sensor driver */
struct {
int16_t (*wu)(void);
int16_t (*pd)(void);
int16_t (*meas)(void);
int16_t (*drdy)(bool *ready);
int16_t (*read)(uint16_t *co2, int32_t *temp, int32_t *hum);
int16_t (*frc)(uint16_t target, uint16_t *corr);
int16_t (*persist)(void);
uint32_t (*getMsTick)(void);
} scd_fp;

typedef enum {
STATE_FIRST, STATE_INIT, STATE_STD,
} e_state_t;

/* Core FSM struct */
/* Core FSM struct */
struct {
e_state_t state; // current state
int32_t ttl; // num measurements left in cycle
Expand All @@ -111,24 +128,24 @@ namespace LPC {
uint32_t _cons_fail;
} fsm;

struct {
bool state = false;
uint32_t lastTickMs = 0;
uint32_t reqLockDurMs = 0;
struct {
bool state = false;
uint32_t lastTickMs = 0;
uint32_t reqLockDurMs = 0;
} _mutex;
bool _mutex_request_lock(uint32_t dur);
bool _mutex_request_unlock(void);
bool _mutex_request_lock(uint32_t dur);
bool _mutex_request_unlock(void);

lpc_ret _proc_fsm(uint16_t co2);
void _sig_fsm_next(void);
uint32_t _sig_get_fsm_offset(void);
uint32_t _sig_get_fsm_cnt(void);
lpc_ret _sig_scd_fail(bool trig);
uint32_t frc_ctr = 0; // number of frcs done
int32_t prev_frc_offset = 0; // last offset (signed)
uint32_t prev_frc_age = 0;
uint32_t frc_ctr = 0; // number of frcs done
int32_t prev_frc_offset = 0; // last offset (signed)
uint32_t prev_frc_age = 0;

};
};
}

#endif /* SENSORS_SCD4X_SCD4X_LPC_H_ */

0 comments on commit 22358ee

Please sign in to comment.