Skip to content
Merged
Show file tree
Hide file tree
Changes from 14 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
6 changes: 5 additions & 1 deletion src/components/i2c/WipperSnapper_I2C.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -1354,9 +1354,13 @@ void WipperSnapper_Component_I2C::update() {
while (sensorsReturningFalse && retries > 0) {
sensorsReturningFalse = false;
retries--;
curTime = millis();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why would curTime be modified here?


std::vector<WipperSnapper_I2C_Driver *>::iterator iter, end;
for (iter = drivers.begin(), end = drivers.end(); iter != end; ++iter) {
// Per-driver fast tick (non-blocking)
(*iter)->fastTick();

// Number of events which occurred for this driver
msgi2cResponse.payload.resp_i2c_device_event.sensor_event_count = 0;

Expand Down
19 changes: 17 additions & 2 deletions src/components/i2c/drivers/WipperSnapper_I2C_Driver.h
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ class WipperSnapper_I2C_Driver {
public:
/*******************************************************************************/
/*!
@brief Instanciates an I2C sensor.
@brief Instantiates an I2C sensor.
@param i2c
The I2C hardware interface, default is Wire.
@param sensorAddress
Expand All @@ -53,6 +53,21 @@ class WipperSnapper_I2C_Driver {
/*******************************************************************************/
virtual ~WipperSnapper_I2C_Driver() {}

/*******************************************************************************/
/*!
@brief Lightweight, per-update background hook for drivers that need
more frequent internal polling than the publish interval.
Default is a no-op; concrete drivers (e.g., SGP30/40) may
override this to perform a single non-blocking read and cache
results for later retrieval by getEvent*().
@note Call site: WipperSnapper_Component_I2C::update() invokes this
once per loop for each driver. Implementations must be
non-blocking (do not delay); use millis()-based timing if
cadence is required.
*/
/*******************************************************************************/
virtual void fastTick() {}

/*******************************************************************************/
/*!
@brief Initializes the I2C sensor and begins I2C.
Expand Down Expand Up @@ -1312,7 +1327,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) {
Expand Down
107 changes: 91 additions & 16 deletions src/components/i2c/drivers/WipperSnapper_I2C_Driver_SGP30.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
#include "WipperSnapper_I2C_Driver.h"
#include <Adafruit_SGP30.h>

#define SGP30_FASTTICK_INTERVAL_MS 1000 ///< Enforce ~1 Hz cadence

/**************************************************************************/
/*!
@brief Class that provides a driver interface for a SGP30 sensor.
Expand All @@ -24,16 +26,19 @@ 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() {
// Called when a SGP30 component is deleted.
delete _sgp30;
~WipperSnapper_I2C_Driver_SGP30() override {
if (_sgp30) {
delete _sgp30;
_sgp30 = nullptr;
}
}

/*******************************************************************************/
Expand All @@ -44,27 +49,97 @@ 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
_did_measure = false;
_lastFastMs = millis() - SGP30_FASTTICK_INTERVAL_MS;
return true;
}

bool getEventECO2(sensors_event_t *senseEvent) override {
if (!_sgp30)
return false;
if (!_did_measure) {
_did_measure = _sgp30->IAQmeasure();
}
if (!_did_measure)
return false;

senseEvent->eCO2 = (uint16_t)_sgp30->eCO2;
_did_measure = false; // consume cached reading
return true;
}

bool getEventECO2(sensors_event_t *senseEvent) {
bool result = _sgp30->IAQmeasure();
if (result) {
senseEvent->eCO2 = _sgp30->eCO2;
bool getEventTVOC(sensors_event_t *senseEvent) override {
if (!_sgp30)
return false;
if (!_did_measure) {
_did_measure = _sgp30->IAQmeasure();
}
return result;
if (!_did_measure)
return false;

senseEvent->tvoc = (uint16_t)_sgp30->TVOC;
_did_measure = false; // consume cached reading
return true;
}

bool getEventTVOC(sensors_event_t *senseEvent) {
bool result = _sgp30->IAQmeasure();
if (result) {
senseEvent->tvoc = _sgp30->TVOC;
/*******************************************************************************/
/*!
@brief Performs background sampling for the SGP30.

This method enforces a ~1 Hz cadence recommended by the sensor
datasheet. On each call, it checks the elapsed time since the
last poll using `millis()`. If at least
SGP30_FASTTICK_INTERVAL_MS have passed, it performs a single
IAQ measurement and caches the result in `_did_measure`.

Cached values (eCO2, TVOC) are later returned by
`getEventECO2()` and `getEventTVOC()` without re-triggering I2C
traffic.

@note Called automatically from
`WipperSnapper_Component_I2C::update()` once per loop iteration.
Must be non-blocking (no delays). The millis-based guard ensures
the sensor is not over-polled.
*/
/*******************************************************************************/
void fastTick() override {
if (!_sgp30)
return;
if (!iaqEnabled())
return;

uint32_t now = millis();
if (now - _lastFastMs >= SGP30_FASTTICK_INTERVAL_MS) {
_did_measure = _sgp30->IAQmeasure();
_lastFastMs = now;
}
return result;
}

protected:
Adafruit_SGP30 *_sgp30; ///< Pointer to SGP30 temperature sensor object
Adafruit_SGP30 *_sgp30; ///< Pointer to SGP30 sensor object

/** Whether we have a fresh IAQ measurement ready. */
bool _did_measure = false;

/** Timestamp of last poll to enforce 1 Hz cadence. */
uint32_t _lastFastMs = 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_H
98 changes: 87 additions & 11 deletions src/components/i2c/drivers/WipperSnapper_I2C_Driver_SGP40.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@
#include <Adafruit_SGP40.h>
#include <Wire.h>

#define SGP40_FASTTICK_INTERVAL_MS 1000 ///< Enforce ~1 Hz cadence

/**************************************************************************/
/*!
@brief Class that provides a driver interface for the SGP40 sensor.
Expand All @@ -40,6 +42,21 @@ class WipperSnapper_I2C_Driver_SGP40 : public WipperSnapper_I2C_Driver {
: WipperSnapper_I2C_Driver(i2c, sensorAddress) {
_i2c = i2c;
_sensorAddress = sensorAddress;
_sgp40 = nullptr;
}

/*******************************************************************************/
/*!
@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;
}
}

/*******************************************************************************/
Expand All @@ -50,12 +67,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

// Initialize cached values
_rawValue = 0;
_vocIdx = 0;
_lastFastMs = millis() - SGP40_FASTTICK_INTERVAL_MS;
return true;
}

Expand All @@ -64,12 +84,14 @@ 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) {
rawEvent->data[0] = (float)_sgp40->measureRaw();
bool getEventRaw(sensors_event_t *rawEvent) override {
if (!_sgp40)
return false;
rawEvent->data[0] = (float)_rawValue;
return true;
}

Expand All @@ -82,13 +104,67 @@ 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;
vocIndexEvent->voc_index = (uint16_t)_vocIdx;
return true;
}

/*******************************************************************************/
/*!
@brief Performs background sampling for the SGP40.

This method enforces a ~1 Hz cadence recommended by the sensor
datasheet. On each call, it checks the elapsed time since the last
poll using `millis()`. If at least SGP40_FASTTICK_INTERVAL_MS ms
have passed, it reads a new raw value and VOC index from the
sensor and caches them in `_rawValue` and `_vocIdx`.

Cached results are later returned by `getEventRaw()` and
`getEventVOCIndex()` without re-triggering I2C traffic.

@note Called automatically from
`WipperSnapper_Component_I2C::update()` once per loop iteration.
Must be non-blocking (no delays). The millis-based guard ensures
the sensor is not over-polled.
*/
/*******************************************************************************/
void fastTick() override {
if (!_sgp40)
return;
if (!vocEnabled())
return;

uint32_t now = millis();
if (now - _lastFastMs >= SGP40_FASTTICK_INTERVAL_MS) {
_rawValue = _sgp40->measureRaw();
_vocIdx = (int32_t)_sgp40->measureVocIndex();
_lastFastMs = now;
}
}

protected:
Adafruit_SGP40 *_sgp40; ///< SEN5X driver object
Adafruit_SGP40 *_sgp40; ///< Pointer to SGP40 sensor object

/**
* Cached latest measurements from the sensor.
* - _rawValue: raw sensor output (ticks)
* - _vocIdx: VOC Index (signed, per datasheet)
*/
uint16_t _rawValue = 0; ///< Raw sensor output (ticks)
int32_t _vocIdx = 0; ///< VOC Index (signed, per datasheet)
uint32_t _lastFastMs = 0; ///< Last poll timestamp to enforce 1 Hz cadence

/*******************************************************************************/
/*!
@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
Loading