Skip to content

Commit c9ec675

Browse files
committed
SGP30 / SGP40 background sampling
Add SGP30 / SGP40 background sampling. Requires frequently 1s polling or returns 0 values.
1 parent e960cee commit c9ec675

File tree

4 files changed

+148
-29
lines changed

4 files changed

+148
-29
lines changed

src/components/i2c/WipperSnapper_I2C.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1347,13 +1347,19 @@ void WipperSnapper_Component_I2C::update() {
13471347
msgi2cResponse.which_payload =
13481348
wippersnapper_signal_v1_I2CResponse_resp_i2c_device_event_tag;
13491349

1350+
// one fast pass for all drivers every update() call
1351+
for (auto *drv : drivers) {
1352+
if (drv) drv->fastTick();
1353+
}
1354+
13501355
long curTime;
13511356
bool sensorsReturningFalse = true;
13521357
int retries = 3;
13531358

13541359
while (sensorsReturningFalse && retries > 0) {
13551360
sensorsReturningFalse = false;
13561361
retries--;
1362+
curTime = millis();
13571363

13581364
std::vector<WipperSnapper_I2C_Driver *>::iterator iter, end;
13591365
for (iter = drivers.begin(), end = drivers.end(); iter != end; ++iter) {

src/components/i2c/drivers/WipperSnapper_I2C_Driver.h

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ class WipperSnapper_I2C_Driver {
3434
public:
3535
/*******************************************************************************/
3636
/*!
37-
@brief Instanciates an I2C sensor.
37+
@brief Instanctiates an I2C sensor.
3838
@param i2c
3939
The I2C hardware interface, default is Wire.
4040
@param sensorAddress
@@ -53,6 +53,20 @@ class WipperSnapper_I2C_Driver {
5353
/*******************************************************************************/
5454
virtual ~WipperSnapper_I2C_Driver() {}
5555

56+
/*******************************************************************************/
57+
/*!
58+
@brief Per-update background hook for drivers that need faster internal
59+
sampling than the user publish interval.
60+
Default is a no-op; override in concrete drivers (e.g., SGP30/40)
61+
to maintain required ~1 Hz reads and accumulate/condition
62+
values for later publish.
63+
@note Call site: WipperSnapper_Component_I2C::update() will invoke
64+
this once per loop for each driver. Implementations must be
65+
non-blocking (do not delay); use millis()-based timing.
66+
*/
67+
/*******************************************************************************/
68+
virtual void fastTick() {}
69+
5670
/*******************************************************************************/
5771
/*!
5872
@brief Initializes the I2C sensor and begins I2C.
@@ -1312,7 +1326,7 @@ class WipperSnapper_I2C_Driver {
13121326
@brief Updates the properties of a proximity sensor.
13131327
@param period
13141328
The time interval at which to return new data from the
1315-
proimity sensor.
1329+
proximity sensor.
13161330
*/
13171331
/*******************************************************************************/
13181332
virtual void updateSensorProximity(float period) {

src/components/i2c/drivers/WipperSnapper_I2C_Driver_SGP30.h

Lines changed: 52 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -24,16 +24,20 @@ class WipperSnapper_I2C_Driver_SGP30 : public WipperSnapper_I2C_Driver {
2424
: WipperSnapper_I2C_Driver(i2c, sensorAddress) {
2525
_i2c = i2c;
2626
_sensorAddress = sensorAddress;
27+
_sgp30 = nullptr;
2728
}
2829

2930
/*******************************************************************************/
3031
/*!
3132
@brief Destructor for an SGP30 sensor.
3233
*/
3334
/*******************************************************************************/
34-
~WipperSnapper_I2C_Driver_SGP30() {
35+
~WipperSnapper_I2C_Driver_SGP30() override {
3536
// Called when a SGP30 component is deleted.
36-
delete _sgp30;
37+
if (_sgp30) {
38+
delete _sgp30;
39+
_sgp30 = nullptr;
40+
}
3741
}
3842

3943
/*******************************************************************************/
@@ -42,29 +46,60 @@ class WipperSnapper_I2C_Driver_SGP30 : public WipperSnapper_I2C_Driver {
4246
@returns True if initialized successfully, False otherwise.
4347
*/
4448
/*******************************************************************************/
45-
bool begin() {
49+
bool begin() override {
4650
_sgp30 = new Adafruit_SGP30();
47-
return _sgp30->begin(_i2c);
51+
if (!_sgp30->begin(_i2c)) {
52+
delete _sgp30; // avoid leak on init failure
53+
_sgp30 = nullptr;
54+
return false;
55+
}
56+
_sgp30->IAQinit(); // start IAQ algorithm
57+
_lastFastMs = millis(); // reset fast sampler
58+
_n = _eco2Sum = _tvocSum = 0; // clear accumulators
59+
return true;
4860
}
4961

50-
bool getEventECO2(sensors_event_t *senseEvent) {
51-
bool result = _sgp30->IAQmeasure();
52-
if (result) {
53-
senseEvent->eCO2 = _sgp30->eCO2;
54-
}
55-
return result;
62+
bool getEventECO2(sensors_event_t *senseEvent) override {
63+
if (!_sgp30) return false;
64+
bool ok = _sgp30->IAQmeasure();
65+
if (ok) senseEvent->eCO2 = _sgp30->eCO2;
66+
return ok;
67+
}
68+
69+
bool getEventTVOC(sensors_event_t *senseEvent) override {
70+
if (!_sgp30) return false;
71+
bool ok = _sgp30->IAQmeasure();
72+
if (ok) senseEvent->tvoc = _sgp30->TVOC;
73+
return ok;
5674
}
5775

58-
bool getEventTVOC(sensors_event_t *senseEvent) {
59-
bool result = _sgp30->IAQmeasure();
60-
if (result) {
61-
senseEvent->tvoc = _sgp30->TVOC;
76+
void fastTick() override {
77+
if (!iaqEnabled())
78+
return; // nothing enabled, save cycles
79+
uint32_t now = millis();
80+
if (now - _lastFastMs >= 1000) { // ~1 Hz cadence
81+
if (_sgp30 && _sgp30->IAQmeasure()) {
82+
_eco2Sum += _sgp30->eCO2; // uint16_t in library
83+
_tvocSum += _sgp30->TVOC; // uint16_t in library
84+
_n++;
85+
}
86+
_lastFastMs = now;
6287
}
63-
return result;
6488
}
6589

6690
protected:
67-
Adafruit_SGP30 *_sgp30; ///< Pointer to SGP30 temperature sensor object
91+
Adafruit_SGP30 *_sgp30; ///< Pointer to SGP30 sensor object
92+
93+
// Fast sampling state
94+
uint32_t _lastFastMs = 0;
95+
uint32_t _n = 0;
96+
uint32_t _eco2Sum = 0;
97+
uint32_t _tvocSum = 0;
98+
99+
inline bool iaqEnabled() const {
100+
// Enable IAQ background reads if either metric is requested
101+
return (getSensorECO2Period() > 0) || (getSensorTVOCPeriod() > 0);
102+
}
68103
};
69104

70-
#endif // WipperSnapper_I2C_Driver_SGP30
105+
#endif // WipperSnapper_I2C_Driver_SGP30

src/components/i2c/drivers/WipperSnapper_I2C_Driver_SGP40.h

Lines changed: 74 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,21 @@ class WipperSnapper_I2C_Driver_SGP40 : public WipperSnapper_I2C_Driver {
4040
: WipperSnapper_I2C_Driver(i2c, sensorAddress) {
4141
_i2c = i2c;
4242
_sensorAddress = sensorAddress;
43+
_sgp40 = nullptr;
44+
}
45+
46+
/*******************************************************************************/
47+
/*!
48+
@brief Destructor for an SGP40 sensor driver.
49+
Cleans up and deallocates the underlying Adafruit_SGP40 object
50+
when the driver is destroyed.
51+
*/
52+
/*******************************************************************************/
53+
~WipperSnapper_I2C_Driver_SGP40() override {
54+
if (_sgp40) {
55+
delete _sgp40;
56+
_sgp40 = nullptr;
57+
}
4358
}
4459

4560
/*******************************************************************************/
@@ -48,14 +63,17 @@ class WipperSnapper_I2C_Driver_SGP40 : public WipperSnapper_I2C_Driver {
4863
@returns True if initialized successfully, False otherwise.
4964
*/
5065
/*******************************************************************************/
51-
bool begin() {
66+
bool begin() override {
5267
_sgp40 = new Adafruit_SGP40();
53-
if (!_sgp40->begin(_i2c)) {
68+
if (!_sgp40 || !_sgp40->begin(_i2c)) {
69+
delete _sgp40;
70+
_sgp40 = nullptr;
5471
return false;
5572
}
56-
57-
// TODO: update to use setCalibration() and pass in temp/humidity
58-
73+
_lastFastMs = millis();
74+
_n = 0;
75+
_vocSum = 0.0f;
76+
_rawSum = 0;
5977
return true;
6078
}
6179

@@ -64,11 +82,19 @@ class WipperSnapper_I2C_Driver_SGP40 : public WipperSnapper_I2C_Driver {
6482
@brief Gets the sensor's current raw unprocessed value.
6583
@param rawEvent
6684
Pointer to an Adafruit_Sensor event.
67-
@returns True if the temperature was obtained successfully, False
85+
@returns True if the raw value was obtained successfully, False
6886
otherwise.
6987
*/
7088
/*******************************************************************************/
71-
bool getEventRaw(sensors_event_t *rawEvent) {
89+
bool getEventRaw(sensors_event_t *rawEvent) override {
90+
if (!_sgp40) return false;
91+
if (_n > 0) {
92+
rawEvent->data[0] = (float)_rawSum / (float)_n;
93+
_rawSum = 0;
94+
_vocSum = 0.0f;
95+
_n = 0;
96+
return true;
97+
}
7298
rawEvent->data[0] = (float)_sgp40->measureRaw();
7399
return true;
74100
}
@@ -82,13 +108,51 @@ class WipperSnapper_I2C_Driver_SGP40 : public WipperSnapper_I2C_Driver {
82108
otherwise.
83109
*/
84110
/*******************************************************************************/
85-
bool getEventVOCIndex(sensors_event_t *vocIndexEvent) {
111+
bool getEventVOCIndex(sensors_event_t *vocIndexEvent) override {
112+
if (!_sgp40) return false;
113+
if (_n > 0) {
114+
vocIndexEvent->voc_index = _vocSum / (float)_n;
115+
_rawSum = 0;
116+
_vocSum = 0.0f;
117+
_n = 0;
118+
return true;
119+
}
86120
vocIndexEvent->voc_index = (float)_sgp40->measureVocIndex();
87121
return true;
88122
}
89123

124+
/*******************************************************************************/
125+
/*!
126+
@brief Performs background sampling for the SGP40.
127+
Runs once per second to accumulate raw and VOC index values
128+
for later averaging in getEventRaw() and getEventVOCIndex().
129+
*/
130+
/*******************************************************************************/
131+
void fastTick() override {
132+
if (!_sgp40) return;
133+
if (!vocEnabled()) return;
134+
135+
uint32_t now = millis();
136+
if (now - _lastFastMs >= 1000) {
137+
_rawSum += _sgp40->measureRaw();
138+
_vocSum += _sgp40->measureVocIndex();
139+
_n++;
140+
_lastFastMs = now;
141+
}
142+
}
143+
90144
protected:
91-
Adafruit_SGP40 *_sgp40; ///< SEN5X driver object
145+
Adafruit_SGP40 *_sgp40; ///< SGP40
146+
// background accumulation state
147+
uint32_t _lastFastMs = 0;
148+
uint32_t _n = 0;
149+
float _vocSum = 0.0f;
150+
uint32_t _rawSum = 0;
151+
152+
// enable fast sampling if either output is requested
153+
inline bool vocEnabled() const {
154+
return (getSensorVOCIndexPeriod() > 0) || (getSensorRawPeriod() > 0);
155+
}
92156
};
93157

94-
#endif // WipperSnapper_I2C_Driver_SEN5X
158+
#endif // WipperSnapper_I2C_Driver_SGP40_H

0 commit comments

Comments
 (0)