4
4
#include " WipperSnapper_I2C_Driver.h"
5
5
#include < Adafruit_SGP30.h>
6
6
7
+ #define SGP30_FASTTICK_INTERVAL_MS 1000 // /< Enforce ~1 Hz cadence
8
+
7
9
/* *************************************************************************/
8
10
/* !
9
11
@brief Class that provides a driver interface for a SGP30 sensor.
@@ -24,6 +26,7 @@ class WipperSnapper_I2C_Driver_SGP30 : public WipperSnapper_I2C_Driver {
24
26
: WipperSnapper_I2C_Driver(i2c, sensorAddress) {
25
27
_i2c = i2c;
26
28
_sensorAddress = sensorAddress;
29
+ _sgp30 = nullptr ;
27
30
}
28
31
29
32
/* ******************************************************************************/
@@ -32,8 +35,10 @@ class WipperSnapper_I2C_Driver_SGP30 : public WipperSnapper_I2C_Driver {
32
35
*/
33
36
/* ******************************************************************************/
34
37
~WipperSnapper_I2C_Driver_SGP30 () {
35
- // Called when a SGP30 component is deleted.
36
- delete _sgp30;
38
+ if (_sgp30) {
39
+ delete _sgp30;
40
+ _sgp30 = nullptr ;
41
+ }
37
42
}
38
43
39
44
/* ******************************************************************************/
@@ -44,27 +49,119 @@ class WipperSnapper_I2C_Driver_SGP30 : public WipperSnapper_I2C_Driver {
44
49
/* ******************************************************************************/
45
50
bool begin () {
46
51
_sgp30 = new Adafruit_SGP30 ();
47
- return _sgp30->begin (_i2c);
52
+ if (!_sgp30->begin (_i2c)) {
53
+ delete _sgp30; // avoid leak on init failure
54
+ _sgp30 = nullptr ;
55
+ return false ;
56
+ }
57
+ _sgp30->IAQinit (); // start IAQ algorithm
58
+
59
+ // Initialize cached values and cadence
60
+ _eco2 = 0 ;
61
+ _tvoc = 0 ;
62
+ _lastFastMs = millis () - SGP30_FASTTICK_INTERVAL_MS;
63
+ return true ;
48
64
}
49
65
50
- bool getEventECO2 (sensors_event_t *senseEvent) {
51
- bool result = _sgp30->IAQmeasure ();
52
- if (result) {
53
- senseEvent->eCO2 = _sgp30->eCO2 ;
54
- }
55
- return result;
66
+ /* ******************************************************************************/
67
+ /* !
68
+ @brief Gets the most recently cached eCO2 reading.
69
+
70
+ This value is updated in `fastTick()` at a ~1 Hz cadence
71
+ and returned directly here without re-triggering an I2C
72
+ transaction.
73
+
74
+ @param senseEvent
75
+ Pointer to an Adafruit_Sensor event that will be populated
76
+ with the cached eCO2 value (in ppm).
77
+
78
+ @returns True if a cached value is available, False otherwise.
79
+ */
80
+ /* ******************************************************************************/
81
+ bool getEventECO2 (sensors_event_t *senseEvent) override {
82
+ if (!_sgp30)
83
+ return false ;
84
+ senseEvent->eCO2 = _eco2;
85
+ return true ;
56
86
}
57
87
58
- bool getEventTVOC (sensors_event_t *senseEvent) {
59
- bool result = _sgp30->IAQmeasure ();
60
- if (result) {
61
- senseEvent->tvoc = _sgp30->TVOC ;
88
+ /* ******************************************************************************/
89
+ /* !
90
+ @brief Gets the most recently cached TVOC reading.
91
+
92
+ This value is updated in `fastTick()` at a ~1 Hz cadence
93
+ and returned directly here without re-triggering an I2C
94
+ transaction.
95
+
96
+ @param senseEvent
97
+ Pointer to an Adafruit_Sensor event that will be populated
98
+ with the cached TVOC value (in ppb).
99
+
100
+ @returns True if a cached value is available, False otherwise.
101
+ */
102
+ /* ******************************************************************************/
103
+ bool getEventTVOC (sensors_event_t *senseEvent) override {
104
+ if (!_sgp30)
105
+ return false ;
106
+ senseEvent->tvoc = _tvoc;
107
+ return true ;
108
+ }
109
+
110
+ /* ******************************************************************************/
111
+ /* !
112
+ @brief Performs background sampling for the SGP30.
113
+
114
+ This method enforces a ~1 Hz cadence recommended by the sensor
115
+ datasheet. On each call, it checks the elapsed time since the
116
+ last poll using `millis()`. If at least
117
+ SGP30_FASTTICK_INTERVAL_MS have passed, it performs a single
118
+ IAQ measurement and caches the results in `_eco2` and `_tvoc`.
119
+
120
+ Cached values are then returned by `getEventECO2()` and
121
+ `getEventTVOC()` without re-triggering I2C traffic.
122
+
123
+ @note Called automatically from
124
+ `WipperSnapper_Component_I2C::update()` once per loop iteration.
125
+ Must be non-blocking (no delays). The millis-based guard ensures
126
+ the sensor is not over-polled.
127
+ */
128
+ /* ******************************************************************************/
129
+ void fastTick () override {
130
+ if (!_sgp30)
131
+ return ;
132
+ if (!iaqEnabled ())
133
+ return ;
134
+
135
+ uint32_t now = millis ();
136
+ if (now - _lastFastMs >= SGP30_FASTTICK_INTERVAL_MS) {
137
+ if (_sgp30->IAQmeasure ()) {
138
+ _eco2 = (uint16_t )_sgp30->eCO2 ;
139
+ _tvoc = (uint16_t )_sgp30->TVOC ;
140
+ }
141
+ _lastFastMs = now;
62
142
}
63
- return result;
64
143
}
65
144
66
145
protected:
67
- Adafruit_SGP30 *_sgp30; // /< Pointer to SGP30 temperature sensor object
146
+ Adafruit_SGP30 *_sgp30; // /< Pointer to SGP30 sensor object
147
+
148
+ /* * Cached latest measurements (no averaging). */
149
+ uint16_t _eco2 = 0 ; // /< eCO2, in ppm
150
+ uint16_t _tvoc = 0 ; // /< TVOC, in ppb
151
+
152
+ /* * Timestamp of last poll to enforce 1 Hz cadence. */
153
+ uint32_t _lastFastMs = 0 ;
154
+
155
+ /* ******************************************************************************/
156
+ /* !
157
+ @brief Returns whether IAQ background sampling should be active.
158
+ @return True if either eCO2 or TVOC metrics are configured to publish.
159
+ */
160
+ /* ******************************************************************************/
161
+ inline bool iaqEnabled () {
162
+ // Enable IAQ background reads if either metric is requested
163
+ return (getSensorECO2Period () > 0 ) || (getSensorTVOCPeriod () > 0 );
164
+ }
68
165
};
69
166
70
- #endif // WipperSnapper_I2C_Driver_SGP30
167
+ #endif // WipperSnapper_I2C_Driver_SGP30_H
0 commit comments