Skip to content

Commit

Permalink
Develop (#3)
Browse files Browse the repository at this point in the history
* 0.1.2 add experimental PPM support for H2 and Ethanol
  • Loading branch information
RobTillaart authored Jun 26, 2021
1 parent 59f1f9c commit 5ea6d73
Show file tree
Hide file tree
Showing 10 changed files with 172 additions and 51 deletions.
67 changes: 51 additions & 16 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,26 +7,45 @@

Arduino library for SGP30 environment sensor

Warning: experimental, 0.1.0 is functional not complete.
Warning: experimental, library is not functional complete yet.

## Description

The SGP30 from Sensirion is an environment sensor that measures H2 and Ethanol in the air. From these numbers an intern algorithm derives an CO2 equivalent and a TVOC measurement.
## Description

The CO2 units are ppm, the TVOC units are ppb. Units for H2 and Ethanol are unknown. For larger concentrations the resolution drops, see datasheet.
The SGP30 from Sensirion is an environment sensor that measures H2 and Ethanol in the air. From these numbers an intern algorithm in the sensor derives an CO2 equivalent and a TVOC measurement. The library has an experimental conversion for H2 and Ethanol.

The CO2 and TVOC values can be read up to once per second (1 Hz). Ethanol and H2, the raw data can be sampled up to 40 Hz.
The CO2 units are ppm, the TVOC units are ppb. Units for H2 and Ethanol are ppm. Note that for larger concentrations the resolution of the measurements drops, see datasheet.

The library supports 2 types of interfaces, a synchronous and an asynchronous interface. The sync interface is blocking for up to 40 milliseconds which was enough to trigger the implementation of an async interface. Note: the sync interface is implemented with the async interface.

The SGP30 works with I2C bus at 100 KHz and 400 KHz. In a short test it worked well up to 600 KHz. A faster I2C clock does not give the sync interface much (relative) gain, however for the async the relative gain is much more.

#### Sample frequency

The CO2 and TVOC values can be read up to once per second (1 Hz). Ethanol and H2, the raw data can be sampled up to 40 Hz.

It may take up to 10 seconds (maybe more) before the sensor produces real data.


#### multi sensors.
#### I2C performance

The SGP30 works with I2C bus at 100 KHz and 400 KHz. In a short test it worked well up to 500 KHz. A faster I2C clock does not give the sync interface much (relative) gain, however for the async the relative gain is much more.

(indicative test run with UNO - IDE 1.8.13, CO2 and TVOC only, times in micros)

| I2C speed | measure() | request() | read() |
|:----------:|:---------:|:---------:|:------:|
| 100 kHz | 12360 | 336 | 732 |
| 200 kHz | 12212 | 196 | 408 |
| 300 kHz | 12168 | 144 | 300 |
| 400 kHz | 12140 | 132 | 264 |
| 500 kHz | 12128 | 124 | 236 |

Note the blocking of measure().

The sensor has a fixed I2C address so only one sensor per I2C bus can be used. If one needs more, one should use an I2C multiplexer or an MCU with multiple I2C buses or switch the VCC as a sort of ChipSelect signal.

#### Multiple sensors.

The SGP30 sensor has a fixed I2C address so only one sensor per I2C bus can be used. If one needs more, one should use an I2C multiplexer or an MCU with multiple I2C buses or switch the VCC as a sort of ChipSelect signal.


## Interface
Expand Down Expand Up @@ -68,15 +87,15 @@ The library caches the last read values, and these are the functions to access t

- **uint16_t getTVOC()** gets the TVOC concentration (ppb)
- **uint16_t getCO2()** gets the CO2 **equivalent** concentration (ppm)
- **uint16_t getH2()** gets the H2 concentration. Units unknown.
- **uint16_t getEthanol()** gets the Ethanol concentration. Units unknown.
- **uint16_t getH2_raw()** gets the raw H2. Units unknown.
- **uint16_t getEthanol_raw()** gets the raw Ethanol. Units unknown.


### Calibration

Check the datasheet for operating range, figure 7.

- **float setRelHumidity(float T, float RH)** sets the compensation for temperature (5-55°C) and **relative** humidity (10-95%). These values can be obtained e.g. from an SHT30, DHT22 or similar sensor.
- **float setRelHumidity(float T, float RH)** sets the compensation for temperature (5-55°C) and **relative** humidity (10-95%). These values can be obtained e.g. from an SHT30, DHT22 or similar sensor. The function returns the absolute humidity.
- **void setAbsHumidity(float AbsoluteHumidity)** sets the compensation for **absolute** humidity. Concentration is in gram per cubic meter (g/m3)


Expand All @@ -86,24 +105,40 @@ The baseline functions give the sensor a reference value. After running in a kno

Note: if the sensor has no reads done, these values tend to go to zero. This is because the baselines are based upon recent reads.

- **bool getBaseline(uint16_t *CO2, uint16_t *TVOC)**
- **void setBaseline(uint16_t CO2, uint16_t TVOC)**
- **bool getBaseline(uint16_t \*CO2, uint16_t \*TVOC)** retrieves the baseline values from the sensor.
- **void setBaseline(uint16_t CO2, uint16_t TVOC)** sets the baseline values.


### Misc

- **int lastError()** returns last error. (needs rework)


### Experimental

use at own risk.

Since 0.1.2 the library has experimental support for H2 and Ethanol concentration in ppm.

One should use these functions more as a relative indication than as an absolute measurement as it is definitely not calibrated.

- **float getH2()** gets the H2 concentration. Units ppm.
- **float getEthanol()** gets the Ethanol concentration. Units ppm.

The used references are based upon (1) averaging raw data in outside air at 22°C @ 1 meter and (2) the assumption that this is 0.4 resp 0.5 ppm. (Note only 1 significant digit) as mentioned is datasheet P2.

- **void setSrefH2(uint16_t s = 13119)** // 13119 is my measurement.
- **uint16_t getSrefH2()** returns value set.
- **void setSrefEthanol(uint16_t s = 18472)** // 18472 is my measurement
- **uint16_t getSrefEthanol()** returns value set


## Todo

- redo **getID()**
- redo **lastError()**
- should **getFeatureSet()** become **bool checkFeatureSet()**?
- implement the TVOC starter baseline.
- test test test ....
- get units H2 right if possible
- get units Ethanol right if possible
- CRC handling
- error handling

Expand Down
24 changes: 21 additions & 3 deletions SGP30.cpp
Original file line number Diff line number Diff line change
@@ -1,15 +1,17 @@
//
// FILE: SGP30.cpp
// AUTHOR: Rob Tillaart
// VERSION: 0.1.1
// VERSION: 0.1.2
// DATE: 2021-06-24
// PURPOSE: SGP30 library for Arduino
// URL: https://github.com/RobTillaart/SGP30
// https://www.adafruit.com/product/3709
//
// HISTORY:
// 0.1.0 2021-06-24 initial version
// 0.1.1 2021-06-26 add get/setBaseline ++
// 0.1.0 2021-06-24 initial version
// 0.1.1 2021-06-26 add get/setBaseline ++
// 0.1.2 2021-06-26 experimental add units H2 + Ethanol
//


#include "SGP30.h"
Expand Down Expand Up @@ -210,6 +212,22 @@ bool SGP30::readRaw()
}


// experimental - datasheet P2
// 1.953125e-3 = 1/512
float SGP30::getH2()
{
float cref = 0.5; // ppm
return cref * exp((_srefH2 - _h2) * 1.953125e-3);
}


float SGP30::getEthanol()
{
float cref = 0.4; // ppm
return cref * exp((_srefEth - _ethanol) * 1.953125e-3);
}


/////////////////////////////////////////////////////
//
// CALIBRATION
Expand Down
29 changes: 22 additions & 7 deletions SGP30.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
//
// FILE: SGP30.h
// AUTHOR: Rob Tillaart
// VERSION: 0.1.1
// VERSION: 0.1.2
// DATE: 2021-06-24
// PURPOSE: SGP30 library for Arduino
// URL: https://github.com/RobTillaart/SGP30
Expand All @@ -12,7 +12,7 @@
#include "Arduino.h"
#include "Wire.h"

#define SGP30_LIB_VERSION (F("0.1.1"))
#define SGP30_LIB_VERSION (F("0.1.2"))

#define SGP30_OK 0x00

Expand Down Expand Up @@ -51,11 +51,13 @@ class SGP30
void requestRaw();
bool readRaw();

// get the data
uint16_t getTVOC() { return _tvoc; };
uint16_t getCO2() { return _co2; };
uint16_t getH2() { return _h2; }
uint16_t getEthanol() { return _ethanol; };
// get the data // UNITS
uint16_t getTVOC() { return _tvoc; }; // PPB
uint16_t getCO2() { return _co2; }; // PPM
uint16_t getH2_raw() { return _h2; }; // UNKNOWN
uint16_t getEthanol_raw() { return _ethanol; }; // UNKNOWN
float getH2(); // experimental // PPM
float getEthanol(); // experimental // PPM


// CALIBRATION - read datasheet
Expand All @@ -74,6 +76,14 @@ class SGP30
void getTVOCStarter() {}; // 0x20B3
*/

// experimental
// 13119 = average raw measured outside 22°C (example)
void setSrefH2(uint16_t s = 13119) { _srefH2 = s; };
uint16_t getSrefH2() { return _srefH2; };
// 18472 = average raw measured outside 22°C (example)
void setSrefEthanol(uint16_t s = 18472) { _srefEth = s; };
uint16_t getSrefEthanol() { return _srefEth; };


// MISC
int lastError(); // TODO extend in code
Expand All @@ -98,6 +108,11 @@ class SGP30
uint16_t _co2;
uint16_t _h2;
uint16_t _ethanol;

// experimental
// average raw values measured outside 22°C
uint16_t _srefH2 = 13119;
uint16_t _srefEth = 18472;

TwoWire* _wire;
};
Expand Down
11 changes: 4 additions & 7 deletions examples/SGP30_I2C_performance/SGP30_I2C_performance.ino
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ void setup()

// has 12 millisecond delay build in...
Serial.println("\nMEASURE()");
for (uint32_t sp = 100000; sp <= 600000; sp += 50000)
for (uint32_t sp = 100000; sp <= 500000; sp += 50000)
{
Wire.setClock(sp);
start = micros();
Expand All @@ -49,7 +49,7 @@ void setup()

// has 40 millisecond delay build in...
Serial.println("\nMEASURE()");
for (uint32_t sp = 100000; sp <= 600000; sp += 50000)
for (uint32_t sp = 100000; sp <= 500000; sp += 50000)
{
Wire.setClock(sp);
start = micros();
Expand All @@ -63,7 +63,7 @@ void setup()


Serial.println("\nREQUEST()");
for (uint32_t sp = 100000; sp <= 600000; sp += 50000)
for (uint32_t sp = 100000; sp <= 500000; sp += 50000)
{
Wire.setClock(sp);
start = micros();
Expand All @@ -76,7 +76,7 @@ void setup()
}

Serial.println("\nREAD()");
for (uint32_t sp = 100000; sp <= 600000; sp += 50000)
for (uint32_t sp = 100000; sp <= 500000; sp += 50000)
{
Wire.setClock(sp);
start = micros();
Expand All @@ -88,9 +88,6 @@ void setup()
SGP.request();
delay(1000);
}



}


Expand Down
27 changes: 24 additions & 3 deletions examples/SGP30_demo_H2_Ethanol/SGP30_demo_H2_Ethanol.ino
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@
// https://www.adafruit.com/product/3709

// 20 samples per second RAW data
//
// calculates the average, which can be used to determine the Sref
// for the H2 and Ethanol concentration functions.


#include "SGP30.h"
Expand All @@ -17,6 +20,10 @@ uint8_t count = 0;
uint32_t lastTime = 0;


float avgH2;
float avgEth;


void setup()
{
Serial.begin(115200);
Expand All @@ -42,13 +49,27 @@ void loop()
{
if (count == 0)
{
Serial.println("\nH2 \tEthanol");
Serial.println("\nH2_raw \tEth_raw\tavgH2\tAvgEth\tH2_ppm\tEthanol_ppm");
count = 10;
}
Serial.print(SGP.getH2());

avgH2 = avgH2 + 0.1 * (SGP.getH2_raw() - avgH2);
avgEth = avgEth + 0.1 * (SGP.getEthanol_raw() - avgEth);

Serial.print(SGP.getH2_raw());
Serial.print("\t");
Serial.print(SGP.getEthanol_raw());
Serial.print("\t");
Serial.print(avgH2 / 1000, 3);
Serial.print("\t");
Serial.print(avgEth / 1000, 3);
Serial.print("\t");
Serial.print(SGP.getH2(), 3);
Serial.print("\t");
Serial.print(SGP.getEthanol(), 3);
Serial.print("\t");
Serial.print(SGP.getEthanol());
Serial.println();

count--;
}

Expand Down
7 changes: 0 additions & 7 deletions examples/SGP30_demo_async/SGP30_demo_async.ino
Original file line number Diff line number Diff line change
Expand Up @@ -34,13 +34,6 @@ void setup()
Serial.println(SGP.getFeatureSet(), HEX);
SGP.GenericReset();

Serial.print("DEVID:\t");
SGP.getID();
for (int i = 0; i < 6; i++)
{
if (SGP._id[i] < 0x10) Serial.print(0); // ÏD: 00.00.01.9B.57.23
Serial.print(SGP._id[i], HEX);
}
Serial.println();
SGP.request();
}
Expand Down
13 changes: 9 additions & 4 deletions keywords.txt
Original file line number Diff line number Diff line change
Expand Up @@ -26,14 +26,19 @@ getTVOC KEYWORD2
getCO2 KEYWORD2
getH2 KEYWORD2
getEthanol KEYWORD2
getH2_raw KEYWORD2
getEthanol_raw KEYWORD2

setRelHumidity KEYWORD2
setAbsHumidity KEYWORD2

setAcqBase KEYWORD2
getAcqBase KEYWORD2
setTVOCBase KEYWORD2
getTVOCBase KEYWORD2
setBaseline KEYWORD2
getBaseline KEYWORD2

setSrefH2 KEYWORD2
getSrefH2 KEYWORD2
setSrefEthanol KEYWORD2
getSrefEthanol KEYWORD2

lastError KEYWORD2

Expand Down
2 changes: 1 addition & 1 deletion library.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
"type": "git",
"url": "https://github.com/RobTillaart/SGP30.git"
},
"version": "0.1.1",
"version": "0.1.2",
"license": "MIT",
"frameworks": "arduino",
"platforms": "*"
Expand Down
2 changes: 1 addition & 1 deletion library.properties
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
name=SGP30
version=0.1.1
version=0.1.2
author=Rob Tillaart <rob.tillaart@gmail.com>
maintainer=Rob Tillaart <rob.tillaart@gmail.com>
sentence=Arduino library for SGP30 environment sensor.
Expand Down
Loading

0 comments on commit 5ea6d73

Please sign in to comment.