HUM: SGP30-2.5K AIR QUALITY SENSOR
You are a beginner with Dasduino. Or electronics? A specific module caught your eye, but you do not know how to use it? Do not worry, HUM is here for you! How to Use Module (HUM) is a blog tutorials series by soldered where you will find all you need in order to begin working with your favorite module. Tutorials include: technical characteristics, work principle, instructions on how to connect module with Dasduino and the basic code. Everything else is left to your imagination.
INTRODUCTION
In this tutorial we will introduce you to the SGP30-2.5K air quality sensor. This sensor can read the amount of CO2 particles in the air, and besides that, the air polution itself, and the measuring unit that is used is PPM (Parts Per Million), i.e. PPB (Parts Per Billion) for air quality. Such sensor is very important if we want to make an automated system that will activate the ventilation system if the air we are in is highly contaminated, and it will purify the air, or even if we only want to deal with accumulating data about the space around us (along with classic temperature, humidity and air pressure measuring). This sensor is often found in air purifiers and humidifiers.
Module characteristics:
Power supply voltage: 2.2 VDC – 6VDC
Communication: I2C
Current: 48.2 mA for normal work mode (reading values), 2-10 uA for sleep mode
Value reading frequency: 1 Hz
Value reading range: 400 – 60000 ppm (Parts Per Million) for eCO2, 0 – 60000 ppb (Parts Per Billion) for TVOC
HOW DOES THE MODULE WORK
The module has 4 pins: GND, VCC, SDA i SCL, which means that it communicates with our Croduino via I2C communication. The sensor itself works at 1.8 VDC, but for simplicity, there is a voltage stabilizer on the module itself that will “lower” 5V from Croduino (or 3.3V) to 1.8V, and also on the module, there is a Level Converter for lowering the communication voltage from 5V (or 3.3V) to 1.8V. The module itself does not detect CO2 directly as an industrial or laboratory CO2 sensor, but detects an equivalent level of CO2 in the air (this is also the reason why it says eCO2, not CO2). The detection of the amount of CO2 particles in the air is based on the detection of the amount of hydrogen molecules in the air. Therefore, when CO2 enters the sensor, a series of chemical processes “convert” that CO2 into hydrogen, which is then detected on the sensor. What may be the first problem is the fact that such sensor will detect hydrogen itself as CO2, which will disrupt the measurements. But most of these sensors have a series of filters that prevent the passage of hydrogen particles, but only CO2 flows through to the point where the conversion of CO2 into hydrogen takes place. But it should also be kept in mind that no such filter is perfect, and that with a higher amount of hydrogen in the air, it will leak a part of the hydrogen in the detection sensor itself, so we will not have accurate measurements, which is also the reason why this sensor can not replace some laboratory and industrial CO2 sensors. Read more about CO2 and CO sensor work mode here. Also, this type of sensor is not intended to be used in external conditions, but is intended to be used within some premises. The reason why it is not intended for external use are the conditions in which it would operate, and those are large differences in temperature, high levels of air contamination measured, large and sudden changes in air humidity (which greatly affects the accuracy of measurements), and very rapid changes of air contamination with various particles. All of the previously mentioned (except the last one) will not only drastically shorten the duration of the sensor, but also give the user the wrong information, especially the latter (very sudden changes in air contamination).
The sensor works in a way that it monitors the amount of CO2 and air quality, and monitors how it changes, which is the “background” level of these values. Based on that, it calculates a baseline that gives it a reference with which it can compare the amount of CO2 in the air as well as the air quality. Therefore, if we want the sensor to give us as accurate results as possible, it is necessary to perform the calibration process. The calibration process is as follows:
The sensor must be placed in a room where the amount of CO2 is very small and air quality is very good (the air is not so contaminated with various particles and molecules). The amount of CO2 and air quality during the calibration of the sensor must not vary, that is, it should be minimal. The sensor must operate at least 12h. After that, the sensor will have a calculated baseline value that we need to read and save. Therewith the calibration process is finished, but for further operation of the sensor, it is necessary to read a new value at least every hour, so that in case of deactivation of the sensor, we have a memorized value that we will immediately enter when starting the sensor, and it should not repeat the process of calibration. It should be noted that these values are valid for 7 days if the sensor is not used, and that after that, the calibration process is required again to find out the new baseline value. It is possible to use the old baseline value, but then the sensor measurements will not be very accurate.
HOW TO CONNECT IT
The connection of the module is very simple, since it uses the I2C communication, and the module itself has everything necessary for the proper operation of the sensor. In the image below, you can see the scheme for sensor connecting. If data is to be displayed on the LCD, it is necessary to connect a 16×2 LCD, but if the data is to be displayed on the Serial Monitor, the LCD and trimmer connection to adjust the contrast on the LCD is not required. Also, if no measurement correction is desired, so that humidity and temperature (in this example via DHT22 sensor) are measured, it is not necessary to connect it. The trimmer that is used is 10kOhm, as well as the resistor for the DHT22 sensor.
ARDUINO CODE
There are three codes; the one that displays the measured values on a 16×2 LCD display, the second print of data is displayed on the Serial Monitor of Arduino IDE, while the third is displayed on the LCD, so that besides the data from the SPG30 sensor, it displays data from the DHT22 sensor for the purpose of correcting the parameters measured by the SGP30 sensor. To make it easier for us to communicate with the sensor, to read values from it, we will use a ready-made library. If you do not know how to install a library, make sure to check our tutorial.
The first code that displays data on the Serial Monitor and by pressing the button it displays the baseline sensor values.
#include <Wire.h> //Adding the library that allows communicating with the sensor itself
#include "Adafruit_SGP30.h" //Adding the library that allows reading values from the sensor
Adafruit_SGP30 sgp;
//SGP30 sensor library constructor
#define TIPKA 12 //Croduino pin to which a push button is connected
void
setup
() {
Serial.begin(9600);
//Start serial communication of 9600 baud speed
Serial.println(F(
"Test SGP30-2.5k senzora"
));
//Display a message to see if we are connected to Croduino
pinMode
(TIPKA,
INPUT_PULLUP
);
//Pin to which a push button is connected, we define it as input with built-in pull up resistors
if
(! sgp.begin()) {
//Check if it is possible to find the sensor on the I2C connection
Serial.println(F(
"Can not find sensor :(\nCheck the connections!"
));
while
(1);
//If it is not possible to find the sensor, stop further program execution
}
Serial.println(F(
"Sensor found! :)"
));
//If we have previous baseline values, we can immediately enter them to the sensor
//If not, leave this commented
//sgp.setIAQBaseline(0x8E68, 0x8F41);
//NOTE: The values listed here are just an example and are not supposed to be entered, because it will lead to wrong readings.
}
uint8_t tipkaStaro = 1, tipkaTrenutno = 1;
//Variable which remembers the previous state on the push button. It is necessary in order to know when the button is pressed.
void
loop
() {
if
(! sgp.IAQmeasure()) {
//Try reading the eCO2 i TVOC values from the sensor and display them on the Serial Monitor
Serial.println(F(
"Unsuccessful reading of the sensor values! :("
));
return
;
}
else
{
Serial.print(F(
"TVOC: "
)); Serial.print(sgp.TVOC); Serial.print(F(
" ppb\t"
));
Serial.print(F(
"eCO2: "
)); Serial.print(sgp.eCO2); Serial.println(F(
" ppm"
));
}
tipkaTrenutno =
digitalRead
(TIPKA);
if
(!tipkaTrenutno && tipkaStaro) {
//If the button is pressed, read and display the baseline value on the Serial Monitor
uint16_t TVOC_base, eCO2_base;
//Variables to which we save the baseline values.
if
(! sgp.getIAQBaseline(&eCO2_base, &TVOC_base)) {
Serial.println(F(
"Unsuccessful reading of the baseline sensor values"
));
return
;
}
else
{
Serial.print(F(
">>>>Baseline vrijednosti [u HEX formatu]: eCO2: 0x"
)); Serial.print(eCO2_base, HEX);
Serial.print(F(
" & TVOC: 0x"
)); Serial.println(TVOC_base, HEX);
}
}
tipkaStaro = tipkaTrenutno;
//Remember the previous state of the push button, in order to know when it is pressed (it is pressed when the old state is 1, and the new is 0)
delay
(1000);
}
The second code works almost the same as the first, except that instead of displaying the measured data on the Serial Monitor of Arduino IDE, it displays them on a 16×2 LCD.
#include <Wire.h> //Adding the library that allows communicating with the sensor itself
#include "Adafruit_SGP30.h" //Adding the library that allows reading values from the sensor
#include <LiquidCrystal.h> //Adding the library that allows communication and control of a 16x2 LCD
Adafruit_SGP30 sgp;
//SGP30 sensor library constructor
//Determine which LCD pin will be connected to the Croduino pin, e.g. RS pin on the LCD will be connected to Croduino's D2
const
int
LCD_RS = 2, LCD_EN = 3, LCD_D4 = 4, LCD_D5 = 5, LCD_D6 = 6, LCD_D7 = 7;
LiquidCrystal lcd(LCD_RS, LCD_EN, LCD_D4, LCD_D5, LCD_D6, LCD_D7);
#define TIPKA 12 //Pin to which a push button is connected
void
setup
() {
lcd.begin(16, 2);
//LCD library initialization, we immediately initialize it to the 16x2 LCD
pinMode
(TIPKA,
INPUT_PULLUP
);
//Pin to which a push button is connected, we define it as input with built-in pull up resistors
if
(! sgp.begin()) {
//Check if it is possible to find the sensor on the I2C connection
lcd.print(F(
"No sensor :(\nCheck the connections!"
));
while
(1);
//If it is not possible to find the sensor, stop further program execution
}
//If we have previous baseline values, we can immediately enter them to the sensor
//If not, leave this commented.
//sgp.setIAQBaseline(0x8E68, 0x8F41);
//NOTE: The values listed here are just an example and are not supposed to be entered, because it will lead to wrong readings.
}
uint8_t tipkaStaro = 1, tipkaTrenutno = 1;
//Variable which remembers the previous state on the push button. It is necessary in order to know when the button is pressed.
void
loop
() {
if
(! sgp.IAQmeasure()) {
//Try reading the eCO2 i TVOC values from the sensor and display them on the LCD
lcd.clear();
//Clear the LCD before each new display
lcd.print(F(
"Unsuccessful"
));
lcd.setCursor(0, 1);
lcd.print(F(
"r :("
reading));
return
;
}
else
{
lcd.clear();
//Clear the LCD before each new display
lcd.print(F(
"TVOC:"
)); lcd.print(sgp.TVOC); lcd.print(F(
"ppb"
));
lcd.setCursor(0,1);
lcd.print(F(
"eCO2:"
)); lcd.print(sgp.eCO2); lcd.print(F(
"ppm"
));
}
tipkaTrenutno =
digitalRead
(TIPKA);
if
(!tipkaTrenutno && tipkaStaro) {
//If the button is pressed, read and display the baseline value on the LCD
uint16_t TVOC_base, eCO2_base;
//Variables to which we save the baseline values.
if
(! sgp.getIAQBaseline(&eCO2_base, &TVOC_base)) {
lcd.clear();
//Clear the LCD before each new display
lcd.println(F(
"Can not read"
));
lcd.setCursor(0, 1);
lcd.print(F(
"baseline"
));
return
;
}
else
{
lcd.clear();
//Clear the LCD before each new display
lcd.print(F(
"Base CO2:0x"
)); lcd.print(eCO2_base, HEX);
lcd.setCursor(0,1);
lcd.print(F(
"Base TVOC:0x"
)); lcd.print(TVOC_base, HEX);
delay
(1000);
}
}
tipkaStaro = tipkaTrenutno;
//Remember the previous state of the push button, in order to know when it is pressed (it is pressed when the old state is 1, and the new is 0)
delay
(1000);
}
Finally, the code which, besides reading the measured data from the SGP30 sensor, measures the air temperature and humidity from the DHT22 sensor by correcting the measured data from the SGP30 sensor to obtain more accurate data from the same sensor (because humidity and temperature influence the accuracy of measured CO2 and TVOC data the most).
#include <Wire.h> //Adding the library that allows communicating with the sensor itself
#include "Adafruit_SGP30.h" //Adding the library that allows reading values from the sensor
#include <LiquidCrystal.h> //Adding the library that allows communication and control of a 16x2 LCD
#include "DHT.h" //Adding the library for reading humidity and air temperature values from the DHT22 sensor
#define TIPKA 12 //Croduino pin which a push button is connected to
#define DHTPIN 8 //Croduino pin which the DHT22 sensor is connected to (in this case, to Croduino pin D8)
#define DHTTYPE DHT22 //The type of DHT sensor we are using - in this case DHT22
Adafruit_SGP30 sgp;
//SGP30 sensor library constructor
//Determine which LCD pin will be connected to the Croduino pin, e.g. RS pin on the LCD will be connected to Croduino's D2
const
int
LCD_RS = 2, LCD_EN = 3, LCD_D4 = 4, LCD_D5 = 5, LCD_D6 = 6, LCD_D7 = 7;
LiquidCrystal lcd(LCD_RS, LCD_EN, LCD_D4, LCD_D5, LCD_D6, LCD_D7);
DHT dht(DHTPIN, DHTTYPE);
//DHT library constructor
//Function that returns the absolute value of air humidity
uint32_t getAbsoluteHumidity(
float
temperature,
float
humidity) {
//Approximation formula that can be found in the SGP30 sensor datasheet
const
float
absoluteHumidity = 216.7f * ((humidity / 100.0f) * 6.112f *
exp
((17.62f * temperature) / (243.12f + temperature)) / (273.15f + temperature));
// [g/m^3]
const
uint32_t absoluteHumidityScaled =
static_cast
<uint32_t>(1000.0f * absoluteHumidity);
// [mg/m^3]
return
absoluteHumidityScaled;
}
void
setup
() {
lcd.begin(16, 2);
//LCD library initialization, we immediately initialize it to the 16x2 LCD
dht.begin();
//DHT air temperature and humidity sensor library initialization
pinMode
(TIPKA,
INPUT_PULLUP
);
//Pin to which a push button is connected, we define it as input with built-in pull up resistors
if
(! sgp.begin()) {
//Check if it is possible to find the sensor on the I2C connection
lcd.print(F(
"No sensor :(\nCheck the connections!"
));
while
(1);
//If it is not possible to find the sensor, stop further program execution
}
//If we have previous baseline values, we can immediately enter them to the sensor.
//If not, leave this commented.
//sgp.setIAQBaseline(0x8E68, 0x8F41);
//NOTE: The values listed here are just an example and are not supposed to be entered, because it will lead to wrong readings.
}
uint8_t tipkaStaro = 1, tipkaTrenutno = 1;
//Variable which remembers the previous state on the push button. It is necessary in order to know when the button is pressed.
void
loop
() {
float
temperature = dht.readTemperature();
//Read air temperature from the DHT22 sensor
float
humidity = dht.readHumidity();
//Read air humidity from the DHT22 sensor
if
(!(isnan(temperature) || isnan(humidity))) sgp.setHumidity(getAbsoluteHumidity(temperature, humidity));
//Correct the measurements from the SGP30 sensor, by counting the air temperature and humidity that most affect the accuracy of the SGP30 sensor measurements
if
(! sgp.IAQmeasure()) {
//Try reading the eCO2 i TVOC values from the sensor and display them on the LCD
lcd.clear();
//Clear the LCD before each new display
lcd.print(F(
"Unsuccessful"
));
lcd.setCursor(0, 1);
lcd.print(F(
"reading :("
));
return
;
}
else
{
lcd.clear();
//Clear the LCD before each new display
lcd.print(F(
"T:"
)); lcd.print(temperature, 1); lcd.print(F(
"C H:"
)); lcd.print(humidity, 1); lcd.print(
'%'
);
lcd.setCursor(0, 1);
lcd.print(sgp.TVOC); lcd.print(F(
"ppb"
)); lcd.print(sgp.eCO2); lcd.print(F(
"ppm"
));
}
tipkaTrenutno =
digitalRead
(TIPKA);
if
(!tipkaTrenutno && tipkaStaro) {
//If the button is pressed, read and display the baseline value on the LCD
uint16_t TVOC_base, eCO2_base;
//Variables to which we save the baseline values.
if
(! sgp.getIAQBaseline(&eCO2_base, &TVOC_base)) {
lcd.clear();
//Clear the LCD before each new display
lcd.println(F(
"Can not read"
));
lcd.setCursor(0, 1);
lcd.print(F(
"baseline"
));
return
;
}
else
{
lcd.clear();
//Clear the LCD before each new display
lcd.print(F(
"Base CO2:0x"
)); lcd.print(eCO2_base, HEX);
lcd.setCursor(0, 1);
lcd.print(F(
"Base TVOC:0x"
)); lcd.print(TVOC_base, HEX);
delay
(1000);
}
}
tipkaStaro = tipkaTrenutno;
//Remember the previous state of the push button, in order to know when it is pressed (it is pressed when the old state is 1, and the new is 0)
delay
(1000);
}