Skip to content

Commit

Permalink
Merge pull request #5 from Sensirion/InitializeMetaData
Browse files Browse the repository at this point in the history
Initialize meta data
  • Loading branch information
maximilianpaulsen authored Feb 28, 2024
2 parents 465cd09 + f69c5cb commit f0ecf66
Show file tree
Hide file tree
Showing 20 changed files with 234 additions and 217 deletions.
7 changes: 4 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ Arduino Library for automatic detection of Sensirion sensors on an I2C Bus. It a
- SVM4X

### Sensor Oddities
- SCD30. This sensor has a particular method of retrieveing measurements, which typically enters a waiting loop until the measurement is ready. To avoid this blocking call, the library uses an alternative method, that sometimes yields I2C errors. These errors are not fatal and can be ignored.
- SGP41. The SGP41 arduino I2C driver returns raw VOC and NOX values, as opposed to the SEN5X sensors, which internally feed the raw values through [Sensirion's gas index algorithm](https://github.com/Sensirion/gas-index-algorithm) and returns a gas index in the range of [0, 500].
- STC3X. The STC3X requires a conditioning phase of up to 10 seconds (this library considers 8 sufficient), during which the value of the data points will be UNDEFINED/0.
- SVM40. The SVM40 Evaluation Kit Board is deprecated and **not** supported by sensor autodetection.
Expand Down Expand Up @@ -65,13 +66,13 @@ To use the library, add the following dependencies to your `platformio.ini`'s `l

```control
lib_deps =
Sensirion UPT I2C Auto Detection
Sensirion/Sensirion UPT I2C Auto Detection
```
PlatformIO will automatically fetch the latest version of the dependencies during the build process.

Alternatively, to install this library in your project environment execute the following command in a terminal:
```bash
pio pkg install --library "Sensirion UPT I2C Auto Detection"
pio pkg install --library "Sensirion/Sensirion UPT I2C Auto Detection"
```

To test the default example (`basicUsage`), use the following platformio command from the project's root directory (the one containing the `platformio.ini` file):
Expand Down Expand Up @@ -118,7 +119,7 @@ In any case, the following steps are essential.

Include the library:
```cpp
#include "Sensirion_Sensor_Auto_Detection.h"
#include "Sensirion_upt_i2c_auto_detection.h"
```
Instantiate I2CAutoDetector and SensorManager globally (before void setup()):

Expand Down
29 changes: 12 additions & 17 deletions src/ISensor.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,22 +22,10 @@ class ISensor {
virtual uint16_t start() = 0;

/**
* @brief Query sensor whether an initialization step is required or not.
*
* @param[out] bool true if either a conditioning or initialization command
* must be called before measurements are available
* @brief Perform extended sensor initialization (if applicable) and
* determines sensor metadata.
*/
virtual bool requiresInitializationStep() const {
return false;
};

/**
* @brief Perform extended sensor initialization.
* As most sensors don't require this, overriding this method is optional.
*/
virtual uint16_t initializationStep() {
return 0;
};
virtual uint16_t initializationStep() = 0;

/**
* @brief Get the duration of the conditioning period
Expand Down Expand Up @@ -96,12 +84,19 @@ class ISensor {
}

/**
* @brief Get the specific SensorId of the ISensor realization
* @brief Get the specific SensorType of the ISensor realization
*
* @return SensorId
* @return SensorType
*/
virtual SensorType getSensorType() const = 0;

/**
* @brief Get the MetaData of the ISensor realization
*
* @return MetaData
*/
virtual MetaData getMetaData() const = 0;

/**
* @brief getter method for _NUMBER_OF_ALLOWED_CONSECUTIVE_ERRORS
*/
Expand Down
2 changes: 1 addition & 1 deletion src/Sensirion_upt_i2c_auto_detection.h
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
#ifndef _SENSIRION_UPT_I2C_AUTO_DETECTION_H_
#define _SENSIRION_UPT_I2C_AUTO_DETECTION_H_

#include "Sensirion_UPT_Core.h"
#include "I2CAutoDetector.h"
#include "Sensirion_UPT_Core.h"
#include "SensorManager.h"
#include <Arduino.h>

Expand Down
38 changes: 18 additions & 20 deletions src/SensorStateMachine.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,32 +18,30 @@ SensorStateMachine::SensorStateMachine(ISensor* pSensor)
};

AutoDetectorError SensorStateMachine::_initialize() {
if (_sensor->requiresInitializationStep()) {
uint16_t error = _sensor->initializationStep();
if (error) {
char errorMsg[256];
errorToString(error, errorMsg, 256);
Serial.printf("Failed to perform initialization step: %s\n",
errorMsg);
return I2C_ERROR;
}

_lastMeasurementTimeStampMs = millis();
_sensorState = SensorStatus::INITIALIZING;

_sensorSignals.init(_sensor->getNumberOfDataPoints());
uint16_t error = _sensor->initializationStep();
if (error) {
char errorMsg[256];
errorToString(error, errorMsg, 256);
Serial.printf("Failed to perform initialization step: %s\n", errorMsg);
return I2C_ERROR;
}

for (size_t i = 0; i < _sensor->getNumberOfDataPoints(); ++i) {
Measurement measurement;
_sensorSignals.addMeasurement(measurement);
}
_sensorSignals.init(_sensor->getNumberOfDataPoints());
_measurementIntervalMs = _sensor->getMinimumMeasurementIntervalMs();
_lastMeasurementTimeStampMs = millis();

if (_sensor->getInitializationIntervalMs() > 0) {
// SGP4X, SCD4X
_sensorState = SensorStatus::INITIALIZING;
} else {
_sensorState = SensorStatus::RUNNING;
_sensorSignals.init(_sensor->getNumberOfDataPoints());
}

_measurementIntervalMs = _sensor->getMinimumMeasurementIntervalMs();
for (size_t i = 0; i < _sensor->getNumberOfDataPoints(); ++i) {
Measurement measurement;
measurement.metaData = _sensor->getMetaData();
_sensorSignals.addMeasurement(measurement);
}

return NO_ERROR;
}
Expand Down
32 changes: 17 additions & 15 deletions src/SensorWrappers/Scd30.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,11 @@
#include "SensirionCore.h"
#include "Sensirion_UPT_Core.h"

Scd30::Scd30(TwoWire& wire) : _wire(wire) {
_metaData.deviceType.sensorType = SensorType::SCD30;
_metaData.platform = DevicePlatform::WIRED;
};

uint16_t Scd30::start() {
_driver.begin(_wire, I2C_ADDRESS);
return 0;
Expand All @@ -28,25 +33,20 @@ uint16_t Scd30::measureAndWrite(Measurement measurements[],
return error;
}

MetaData metaData;
metaData.deviceID = _sensorID;
metaData.deviceType.sensorType = _sensorType;
metaData.platform = DevicePlatform::WIRED;

measurements[0].signalType = SignalType::CO2_PARTS_PER_MILLION;
measurements[0].dataPoint.t_offset = timeStamp;
measurements[0].dataPoint.value = co2Concentration;
measurements[0].metaData = metaData;
measurements[0].metaData = _metaData;

measurements[1].signalType = SignalType::TEMPERATURE_DEGREES_CELSIUS;
measurements[1].dataPoint.t_offset = timeStamp;
measurements[1].dataPoint.value = temperature;
measurements[1].metaData = metaData;
measurements[1].metaData = _metaData;

measurements[2].signalType = SignalType::RELATIVE_HUMIDITY_PERCENTAGE;
measurements[2].dataPoint.t_offset = timeStamp;
measurements[2].dataPoint.value = humidity;
measurements[2].metaData = metaData;
measurements[2].metaData = _metaData;

/* Prepare next reading by querying the dataReadyFlag. We don't need the
* value of the flag, but the query seems to finalize setting off the
Expand All @@ -71,10 +71,11 @@ uint16_t Scd30::initializationStep() {
}

// SCD30 does not support serial no. retrieval via driver
_sensorID = 0;
uint64_t sensorID = 0;
for (size_t i = 0; i < 64; i++) {
_sensorID |= (random(2) << i);
sensorID |= (random(2) << i);
}
_metaData.deviceID = sensorID;

/* See explanatory comment for measureAndWrite() */
uint16_t dataReadyFlag;
Expand All @@ -83,7 +84,12 @@ uint16_t Scd30::initializationStep() {
}

SensorType Scd30::getSensorType() const {
return _sensorType;
return _metaData.deviceType.sensorType;
;
}

MetaData Scd30::getMetaData() const {
return _metaData;
}

size_t Scd30::getNumberOfDataPoints() const {
Expand All @@ -94,10 +100,6 @@ unsigned long Scd30::getMinimumMeasurementIntervalMs() const {
return 2000;
}

bool Scd30::requiresInitializationStep() const {
return true;
}

void* Scd30::getDriver() {
return reinterpret_cast<void*>(&_driver);
}
7 changes: 3 additions & 4 deletions src/SensorWrappers/Scd30.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,22 +8,21 @@
class Scd30 : public ISensor {
public:
static const uint16_t I2C_ADDRESS = 0x61;
explicit Scd30(TwoWire& wire) : _wire(wire){};
explicit Scd30(TwoWire& wire);
uint16_t start() override;
uint16_t measureAndWrite(Measurement measurements[],
const unsigned long timeStamp) override;
uint16_t initializationStep() override;
SensorType getSensorType() const override;
MetaData getMetaData() const override;
size_t getNumberOfDataPoints() const override;
unsigned long getMinimumMeasurementIntervalMs() const override;
bool requiresInitializationStep() const override;
void* getDriver() override;

private:
TwoWire& _wire;
SensirionI2cScd30 _driver;
const SensorType _sensorType = SensorType::SCD30;
uint64_t _sensorID = 0;
MetaData _metaData;
};

#endif /* _SCD30_H_ */
34 changes: 21 additions & 13 deletions src/SensorWrappers/Scd4x.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,11 @@
#include "SensirionCore.h"
#include "Sensirion_UPT_Core.h"

Scd4x::Scd4x(TwoWire& wire) : _wire(wire) {
_metaData.deviceType.sensorType = SensorType::SCD4X;
_metaData.platform = DevicePlatform::WIRED;
};

uint16_t Scd4x::start() {
_driver.begin(_wire);
return 0;
Expand All @@ -17,25 +22,20 @@ uint16_t Scd4x::measureAndWrite(Measurement measurements[],
return error;
}

MetaData metaData;
metaData.deviceID = _sensorID;
metaData.deviceType.sensorType = _sensorType;
metaData.platform = DevicePlatform::WIRED;

measurements[0].signalType = SignalType::CO2_PARTS_PER_MILLION;
measurements[0].dataPoint.t_offset = timeStamp;
measurements[0].dataPoint.value = static_cast<float>(co2);
measurements[0].metaData = metaData;
measurements[0].metaData = _metaData;

measurements[1].signalType = SignalType::TEMPERATURE_DEGREES_CELSIUS;
measurements[1].dataPoint.t_offset = timeStamp;
measurements[1].dataPoint.value = temp;
measurements[1].metaData = metaData;
measurements[1].metaData = _metaData;

measurements[2].signalType = SignalType::RELATIVE_HUMIDITY_PERCENTAGE;
measurements[2].dataPoint.t_offset = timeStamp;
measurements[2].dataPoint.value = humi;
measurements[2].metaData = metaData;
measurements[2].metaData = _metaData;

return HighLevelError::NoError;
}
Expand All @@ -52,17 +52,23 @@ uint16_t Scd4x::initializationStep() {
if (error) {
return error;
}
_sensorID = 0;
_sensorID |=
uint64_t sensorID = 0;
sensorID |=
(static_cast<uint64_t>(word0) << 16 * 2) | (word1 << 16) | word2;

_metaData.deviceID = sensorID;

// Start Measurement
error = _driver.startPeriodicMeasurement();
return error;
}

SensorType Scd4x::getSensorType() const {
return _sensorType;
return _metaData.deviceType.sensorType;
}

MetaData Scd4x::getMetaData() const {
return _metaData;
}

size_t Scd4x::getNumberOfDataPoints() const {
Expand All @@ -73,8 +79,10 @@ unsigned long Scd4x::getMinimumMeasurementIntervalMs() const {
return 5000;
}

bool Scd4x::requiresInitializationStep() const {
return true;
unsigned long Scd4x::getInitializationIntervalMs() const {
// Sensor does not produce measurements for ~12s after
// startPeriodicMeasurement() is called
return 12 * 1000;
}

void* Scd4x::getDriver() {
Expand Down
9 changes: 5 additions & 4 deletions src/SensorWrappers/Scd4x.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,22 +8,23 @@
class Scd4x : public ISensor {
public:
static const uint16_t I2C_ADDRESS = 0x62;
explicit Scd4x(TwoWire& wire) : _wire(wire){};
explicit Scd4x(TwoWire& wire);
uint16_t start() override;
uint16_t measureAndWrite(Measurement measurements[],
const unsigned long timeStamp) override;
uint16_t initializationStep() override;
SensorType getSensorType() const override;
MetaData getMetaData() const override;
size_t getNumberOfDataPoints() const override;
unsigned long getMinimumMeasurementIntervalMs() const override;
bool requiresInitializationStep() const override;
// Same as measurement interval
unsigned long getInitializationIntervalMs() const override;
void* getDriver() override;

private:
TwoWire& _wire;
SensirionI2CScd4x _driver;
const SensorType _sensorType = SensorType::SCD4X;
uint64_t _sensorID = 0;
MetaData _metaData;
};

#endif /* _SCD4X_H_ */
Loading

0 comments on commit f0ecf66

Please sign in to comment.