-
Notifications
You must be signed in to change notification settings - Fork 54
SGP30 / SGP40 background sampling #811
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 8 commits
c9ec675
0b020d4
f98d42b
662f61c
b1154e2
5cbb3d5
281e41d
222e45b
1dc39ca
fe0ca83
f9dbb5e
557a0f4
0a0927d
7e6e75c
7f7f4dd
ed2fe67
a0fdee7
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -1032,7 +1032,7 @@ bool WipperSnapper_Component_I2C::initI2CDevice( | |
_drivers_out.push_back(_ssd1306); | ||
WS_DEBUG_PRINTLN("SSD1306 display initialized Successfully!"); | ||
} else { | ||
WS_DEBUG_PRINTLN("ERROR: I2C device type not found!") | ||
WS_DEBUG_PRINTLN("ERROR: I2C device type not found!"); | ||
_busStatusResponse = | ||
wippersnapper_i2c_v1_BusResponse_BUS_RESPONSE_UNSUPPORTED_SENSOR; | ||
return false; | ||
|
@@ -1347,13 +1347,20 @@ void WipperSnapper_Component_I2C::update() { | |
msgi2cResponse.which_payload = | ||
wippersnapper_signal_v1_I2CResponse_resp_i2c_device_event_tag; | ||
|
||
// one fast pass for all drivers every update() call | ||
for (auto *drv : drivers) { | ||
if (drv) | ||
drv->fastTick(); | ||
} | ||
|
||
long curTime; | ||
bool sensorsReturningFalse = true; | ||
int retries = 3; | ||
|
||
while (sensorsReturningFalse && retries > 0) { | ||
sensorsReturningFalse = false; | ||
retries--; | ||
curTime = millis(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why would |
||
|
||
std::vector<WipperSnapper_I2C_Driver *>::iterator iter, end; | ||
for (iter = drivers.begin(), end = drivers.end(); iter != end; ++iter) { | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -34,7 +34,7 @@ class WipperSnapper_I2C_Driver { | |
public: | ||
/*******************************************************************************/ | ||
/*! | ||
@brief Instanciates an I2C sensor. | ||
@brief Instanctiates an I2C sensor. | ||
|
||
@param i2c | ||
The I2C hardware interface, default is Wire. | ||
@param sensorAddress | ||
|
@@ -53,6 +53,20 @@ class WipperSnapper_I2C_Driver { | |
/*******************************************************************************/ | ||
virtual ~WipperSnapper_I2C_Driver() {} | ||
|
||
/*******************************************************************************/ | ||
/*! | ||
@brief Per-update background hook for drivers that need faster internal | ||
sampling than the user publish interval. | ||
Default is a no-op; override in concrete drivers (e.g., | ||
SGP30/40) to maintain required ~1 Hz reads and accumulate/condition values | ||
for later publish. | ||
@note Call site: WipperSnapper_Component_I2C::update() will invoke | ||
this once per loop for each driver. Implementations must be | ||
non-blocking (do not delay); use millis()-based timing. | ||
*/ | ||
/*******************************************************************************/ | ||
virtual void fastTick() {} | ||
|
||
/*******************************************************************************/ | ||
/*! | ||
@brief Initializes the I2C sensor and begins I2C. | ||
|
@@ -1312,7 +1326,7 @@ class WipperSnapper_I2C_Driver { | |
@brief Updates the properties of a proximity sensor. | ||
@param period | ||
The time interval at which to return new data from the | ||
proimity sensor. | ||
proximity sensor. | ||
*/ | ||
/*******************************************************************************/ | ||
virtual void updateSensorProximity(float period) { | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -24,16 +24,20 @@ class WipperSnapper_I2C_Driver_SGP30 : public WipperSnapper_I2C_Driver { | |
: WipperSnapper_I2C_Driver(i2c, sensorAddress) { | ||
_i2c = i2c; | ||
_sensorAddress = sensorAddress; | ||
_sgp30 = nullptr; | ||
} | ||
|
||
/*******************************************************************************/ | ||
/*! | ||
@brief Destructor for an SGP30 sensor. | ||
*/ | ||
/*******************************************************************************/ | ||
~WipperSnapper_I2C_Driver_SGP30() { | ||
~WipperSnapper_I2C_Driver_SGP30() override { | ||
tyeth marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
// Called when a SGP30 component is deleted. | ||
delete _sgp30; | ||
if (_sgp30) { | ||
delete _sgp30; | ||
_sgp30 = nullptr; | ||
} | ||
} | ||
|
||
/*******************************************************************************/ | ||
|
@@ -44,27 +48,90 @@ class WipperSnapper_I2C_Driver_SGP30 : public WipperSnapper_I2C_Driver { | |
/*******************************************************************************/ | ||
bool begin() { | ||
_sgp30 = new Adafruit_SGP30(); | ||
return _sgp30->begin(_i2c); | ||
if (!_sgp30->begin(_i2c)) { | ||
delete _sgp30; // avoid leak on init failure | ||
_sgp30 = nullptr; | ||
return false; | ||
} | ||
_sgp30->IAQinit(); // start IAQ algorithm | ||
_lastFastMs = millis(); // reset fast sampler | ||
_n = _eco2Sum = _tvocSum = 0; // clear accumulators | ||
return true; | ||
} | ||
|
||
bool getEventECO2(sensors_event_t *senseEvent) { | ||
bool result = _sgp30->IAQmeasure(); | ||
if (result) { | ||
senseEvent->eCO2 = _sgp30->eCO2; | ||
bool getEventECO2(sensors_event_t *senseEvent) override { | ||
brentru marked this conversation as resolved.
Show resolved
Hide resolved
|
||
if (!_sgp30) | ||
return false; | ||
if (_n > 0) { | ||
senseEvent->eCO2 = (uint16_t)(_eco2Sum / _n); | ||
_eco2Sum = 0; | ||
_tvocSum = 0; | ||
_n = 0; | ||
return true; | ||
} | ||
if (_sgp30->IAQmeasure()) { | ||
senseEvent->eCO2 = (uint16_t)_sgp30->eCO2; | ||
return true; | ||
} | ||
return result; | ||
return false; | ||
} | ||
|
||
bool getEventTVOC(sensors_event_t *senseEvent) { | ||
bool result = _sgp30->IAQmeasure(); | ||
if (result) { | ||
senseEvent->tvoc = _sgp30->TVOC; | ||
bool getEventTVOC(sensors_event_t *senseEvent) override { | ||
if (!_sgp30) | ||
return false; | ||
if (_n > 0) { | ||
senseEvent->tvoc = (uint16_t)(_tvocSum / _n); | ||
_eco2Sum = 0; | ||
_tvocSum = 0; | ||
_n = 0; | ||
return true; | ||
} | ||
if (_sgp30->IAQmeasure()) { | ||
senseEvent->tvoc = (uint16_t)_sgp30->TVOC; | ||
return true; | ||
} | ||
return false; | ||
} | ||
|
||
void fastTick() override { | ||
brentru marked this conversation as resolved.
Show resolved
Hide resolved
brentru marked this conversation as resolved.
Show resolved
Hide resolved
|
||
if (!iaqEnabled()) | ||
return; // nothing enabled, save cycles | ||
uint32_t now = millis(); | ||
if (now - _lastFastMs >= 1000) { // ~1 Hz cadence | ||
|
||
if (_sgp30 && _sgp30->IAQmeasure()) { | ||
_eco2Sum += _sgp30->eCO2; // uint16_t in library | ||
_tvocSum += _sgp30->TVOC; // uint16_t in library | ||
_n++; | ||
} | ||
_lastFastMs = now; | ||
} | ||
return result; | ||
} | ||
|
||
protected: | ||
Adafruit_SGP30 *_sgp30; ///< Pointer to SGP30 temperature sensor object | ||
Adafruit_SGP30 *_sgp30; ///< Pointer to SGP30 sensor object | ||
|
||
/** Millis timestamp of last 1 Hz background read. */ | ||
uint32_t _lastFastMs = 0; | ||
|
||
/** Number of samples accumulated since last publish. */ | ||
uint32_t _n = 0; | ||
|
||
|
||
/** Running sum of eCO2 samples for averaging. */ | ||
uint32_t _eco2Sum = 0; | ||
|
||
/** Running sum of TVOC samples for averaging. */ | ||
uint32_t _tvocSum = 0; | ||
|
||
/*******************************************************************************/ | ||
/*! | ||
@brief Returns whether IAQ background sampling should be active. | ||
@return True if either eCO2 or TVOC metrics are configured to publish. | ||
*/ | ||
/*******************************************************************************/ | ||
inline bool iaqEnabled() { | ||
// Enable IAQ background reads if either metric is requested | ||
return (getSensorECO2Period() > 0) || (getSensorTVOCPeriod() > 0); | ||
} | ||
}; | ||
|
||
#endif // WipperSnapper_I2C_Driver_SGP30 | ||
#endif // WipperSnapper_I2C_Driver_SGP30 |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -40,6 +40,21 @@ class WipperSnapper_I2C_Driver_SGP40 : public WipperSnapper_I2C_Driver { | |
: WipperSnapper_I2C_Driver(i2c, sensorAddress) { | ||
_i2c = i2c; | ||
_sensorAddress = sensorAddress; | ||
_sgp40 = nullptr; | ||
brentru marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} | ||
|
||
/*******************************************************************************/ | ||
/*! | ||
@brief Destructor for an SGP40 sensor driver. | ||
Cleans up and deallocates the underlying Adafruit_SGP40 object | ||
when the driver is destroyed. | ||
*/ | ||
/*******************************************************************************/ | ||
~WipperSnapper_I2C_Driver_SGP40() override { | ||
if (_sgp40) { | ||
delete _sgp40; | ||
_sgp40 = nullptr; | ||
} | ||
} | ||
|
||
/*******************************************************************************/ | ||
|
@@ -50,12 +65,15 @@ class WipperSnapper_I2C_Driver_SGP40 : public WipperSnapper_I2C_Driver { | |
/*******************************************************************************/ | ||
bool begin() { | ||
_sgp40 = new Adafruit_SGP40(); | ||
if (!_sgp40->begin(_i2c)) { | ||
if (!_sgp40 || !_sgp40->begin(_i2c)) { | ||
delete _sgp40; | ||
_sgp40 = nullptr; | ||
return false; | ||
} | ||
|
||
// TODO: update to use setCalibration() and pass in temp/humidity | ||
|
||
_lastFastMs = millis(); | ||
_n = 0; | ||
_vocSum = 0.0f; | ||
_rawSum = 0; | ||
|
||
return true; | ||
} | ||
|
||
|
@@ -64,11 +82,20 @@ class WipperSnapper_I2C_Driver_SGP40 : public WipperSnapper_I2C_Driver { | |
@brief Gets the sensor's current raw unprocessed value. | ||
@param rawEvent | ||
Pointer to an Adafruit_Sensor event. | ||
@returns True if the temperature was obtained successfully, False | ||
@returns True if the raw value was obtained successfully, False | ||
otherwise. | ||
*/ | ||
/*******************************************************************************/ | ||
bool getEventRaw(sensors_event_t *rawEvent) { | ||
bool getEventRaw(sensors_event_t *rawEvent) override { | ||
if (!_sgp40) | ||
return false; | ||
if (_n > 0) { | ||
rawEvent->data[0] = (float)_rawSum / (float)_n; | ||
_rawSum = 0; | ||
_vocSum = 0.0f; | ||
_n = 0; | ||
return true; | ||
} | ||
rawEvent->data[0] = (float)_sgp40->measureRaw(); | ||
return true; | ||
} | ||
|
@@ -82,13 +109,66 @@ class WipperSnapper_I2C_Driver_SGP40 : public WipperSnapper_I2C_Driver { | |
otherwise. | ||
*/ | ||
/*******************************************************************************/ | ||
bool getEventVOCIndex(sensors_event_t *vocIndexEvent) { | ||
vocIndexEvent->voc_index = (float)_sgp40->measureVocIndex(); | ||
bool getEventVOCIndex(sensors_event_t *vocIndexEvent) override { | ||
if (!_sgp40) | ||
return false; | ||
if (_n > 0) { | ||
vocIndexEvent->voc_index = (uint16_t)(_vocSum / (float)_n); | ||
_rawSum = 0; | ||
_vocSum = 0.0f; | ||
_n = 0; | ||
return true; | ||
} | ||
vocIndexEvent->voc_index = (uint16_t)_sgp40->measureVocIndex(); | ||
return true; | ||
} | ||
|
||
/*******************************************************************************/ | ||
/*! | ||
@brief Performs background sampling for the SGP40. | ||
Runs once per second to accumulate raw and VOC index values | ||
for later averaging in getEventRaw() and getEventVOCIndex(). | ||
*/ | ||
/*******************************************************************************/ | ||
void fastTick() override { | ||
if (!_sgp40) | ||
return; | ||
if (!vocEnabled()) | ||
return; | ||
|
||
uint32_t now = millis(); | ||
if (now - _lastFastMs >= 1000) { | ||
|
||
_rawSum += _sgp40->measureRaw(); | ||
_vocSum += _sgp40->measureVocIndex(); | ||
_n++; | ||
_lastFastMs = now; | ||
} | ||
} | ||
|
||
protected: | ||
Adafruit_SGP40 *_sgp40; ///< SEN5X driver object | ||
Adafruit_SGP40 *_sgp40; ///< SGP40 | ||
|
||
/** Millis timestamp of last 1 Hz background read. */ | ||
uint32_t _lastFastMs = 0; | ||
|
||
/** Number of samples accumulated since last publish. */ | ||
uint32_t _n = 0; | ||
|
||
|
||
/** Running sum of VOC index samples for averaging. */ | ||
float _vocSum = 0.0f; | ||
|
||
/** Running sum of raw samples for averaging. */ | ||
uint32_t _rawSum = 0; | ||
|
||
/*******************************************************************************/ | ||
/*! | ||
@brief Returns whether VOC background sampling should be active. | ||
@return True if either VOC Index or raw value is configured to publish. | ||
*/ | ||
/*******************************************************************************/ | ||
inline bool vocEnabled() { | ||
return (getSensorVOCIndexPeriod() > 0) || (getSensorRawPeriod() > 0); | ||
} | ||
}; | ||
|
||
#endif // WipperSnapper_I2C_Driver_SEN5X | ||
#endif // WipperSnapper_I2C_Driver_SGP40_H |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do not use
auto
here or create a loop. It is redundant and adds a performance overhead for what should be a tight loop.Instead, try calling
drv->fastTick()
when we are looping through the vectors below. SincefastTick
isvirtual
, and the vect. of drivers exists, you dont need to check fornullptr
either.