SGP30 Gas Sensor
The SGP30 is a metal oxide gas sensor with multiple sensing elements on a single chip. It integrates 4 gas sensing elements and has a fully calibrated air quality output signal. In addition, the SGP is easy to integrate and can integrate metal oxide gas sensors into mobile devices, opening up new possibilities for environmental monitoring in smart home, appliance, and IoT applications. It is mainly used for formaldehyde detection!
Module Source
Purchase link: https://item.taobao.com/item.htm?spm=a21n57.1.0.0.1925523cK81vXF&id=629652345522&ns=1&abbucket=0
Also available for purchase: https://item.taobao.com/item.htm?spm=a21n57.1.0.0.1925523cK81vXF&id=609271054989&ns=1&abbucket=0#detail
Materials download link: https://pan.baidu.com/s/16ITjdV34J8K2Wu24sB_iPg Extraction code: 1vds
Specifications
Operating voltage: 3.3V Operating current: 40mA Output method: IIC Number of pins: 4 Pin
View Materials
Introduction The SGP30 is a metal oxide indoor gas sensor with multiple sensing elements on a single chip. It integrates 4 gas sensing elements and has a fully calibrated air quality output signal, mainly for air quality detection. It can output: TVOC (Total Volatile Organic Compounds), with a range of 0~60000ppb; CO2 concentration, with a range of 400~60000ppm.
The sensing (MEMS) part of the SGP30 is based on a heated membrane of metal oxide (MOx) nanoparticles. The oxygen adsorbed on the gas-sensitive material - metal oxide particles - reacts with the target gas, thereby releasing electrons. This causes the resistance of the metal oxide layer measured by the sensor to change. In short, the appearance of reducing gases causes the oxygen concentration on the surface of the gas-sensitive material to decrease, changing the resistance (or conductance) of the semiconductor. Subsequently, the circuit (ASIC) part detects, processes, and converts the resistance, and finally obtains the gas value. Timing The I2C slave address is 0X58. Since the address only uses 7 bits, the highest bit is unused, and the lowest bit determines whether it is a read or write operation: 0 is read, 1 is write. Therefore:
- For writing to SGP30, the address is (0X58 << 1) = 0XB0
- For reading from SGP30, the address is ((0X58 << 1)) | 0X01 = 0XB1
All SGP30 commands are double-byte, with the high byte sent first. The following commands are available:
There are two commonly used ones: 0x2003 is the command to initialize the SGP30, and 0x2008 is the command to get air quality values.
The data format obtained by SGP30 is: 2 bytes of CO2 data + 1 byte of CO2 CRC check + 2 bytes of TVOC data + 1 byte of TVOC CRC check. The module takes about 15s to initialize after power-on. During the initialization phase, the CO2 concentration read is 400ppm, and the TVOC is 0ppd and remains constant. Therefore, after power-on, keep reading until TVOC is not 0 and CO2 is not 400, then the SGP30 module initialization is complete.
After initialization is complete, the data read out at the beginning will fluctuate greatly, which is a normal phenomenon, and will gradually stabilize after a period of time. Gas sensors are easily affected by the environment, and fluctuations in measurement data are normal. You can add a filter function for filtering.
Porting Process
Pin Selection
Port to Project
Our goal is to port the example to the ESP32-S3 dev board. Complete driver code has been provided for you. Follow the steps below to complete the porting.
For detailed instructions on creating folders and new .c and .h files, refer to section 1.4.2 in the [DHT11 Temperature and Humidity Sensor] chapter; we will not repeat it here.
However, here we change the file names bsp_dht11.c and bsp_dht11.h to bsp_sgp30.c and bsp_sgp30.h, and change the folder name to SGP30.
Write Code
In the bsp_sgp30.c file, write the following code.
/*
* LCSC-Openkits (LCKFB) software and hardware materials and related expansion board software and hardware materials are all open source on the official website.
* Dev board official website: www.lckfb.com
* Technical support resides on the forum; any technical questions are welcome for exchange and learning at any time.
* LCKFB Forum: club.szlcsc.com
* Follow our Bilibili account: [LCSC-Openkits (LCKFB)] to keep up with our latest updates!
* We do not make money by selling boards; we take cultivating engineers as our mission.
* Change Logs:
* Date Author Notes
* 2024-01-05 LCKFB-lp first version
*/
#include "bsp_sgp30.h"
#include "stdio.h"
void delay_ms(unsigned int ms)
{
vTaskDelay(ms / portTICK_PERIOD_MS);
}
void delay_us(unsigned int us)
{
ets_delay_us(us);
}
void delay_1ms(unsigned int ms)
{
vTaskDelay(ms / portTICK_PERIOD_MS);
}
void delay_1us(unsigned int us)
{
ets_delay_us(us);
}
/******************************************************************
* Function Name: SGP30_GPIO_Init
* Function Description: SGP30 pin initialization
* Function Parameters: None
* Function Return: None
* Author: LC
* Notes: Only pin initialization; the actual initialization is: SGP30_Init
******************************************************************/
void SGP30_GPIO_Init(void)
{
gpio_config_t lll_config = {
.pin_bit_mask = (1ULL<<SGP30_SCL_PIN)|(1ULL<<SGP30_SDA_PIN), // Configure pins
.mode =GPIO_MODE_OUTPUT, // Output mode
.pull_up_en = GPIO_PULLUP_ENABLE, // Enable pull-up
.pull_down_en = GPIO_PULLDOWN_DISABLE, // Disable pull-down
.intr_type = GPIO_INTR_DISABLE // Disable pin interrupt
};
gpio_config(&lll_config);
}
/******************************************************************
* Function Name: IIC_Start
* Function Description: IIC start timing
* Function Parameters: None
* Function Return: None
* Author: LC
* Notes: None
******************************************************************/
void IIC_Start(void)
{
SDA_OUT();
SDA(1);
delay_us(5);
SCL(1);
delay_us(5);
SDA(0);
delay_us(5);
SCL(0);
delay_us(5);
}
/******************************************************************
* Function Name: IIC_Stop
* Function Description: IIC stop signal
* Function Parameters: None
* Function Return: None
* Author: LC
* Notes: None
******************************************************************/
void IIC_Stop(void)
{
SDA_OUT();
SCL(0);
SDA(0);
SCL(1);
delay_us(5);
SDA(1);
delay_us(5);
}
/******************************************************************
* Function Name: IIC_Send_Ack
* Function Description: Host sends acknowledge or non-acknowledge signal
* Function Parameters: 0 sends acknowledge 1 sends non-acknowledge
* Function Return: None
* Author: LC
* Notes: None
******************************************************************/
void IIC_Send_Ack(unsigned char ack)
{
SDA_OUT();
SCL(0);
SDA(0);
delay_us(5);
if(!ack) SDA(0);
else SDA(1);
SCL(1);
delay_us(5);
SCL(0);
SDA(1);
}
/******************************************************************
* Function Name: I2C_WaitAck
* Function Description: Wait for slave acknowledge
* Function Parameters: None
* Function Return: 0=acknowledged 1=timeout no acknowledge
* Author: LC
* Notes: None
******************************************************************/
unsigned char I2C_WaitAck(void)
{
char ack = 0;
unsigned char ack_flag = 10;
SCL(0);
SDA(1);
SDA_IN();
delay_us(5);
SCL(1);
delay_us(5);
while( (SDA_GET()==1) && ( ack_flag ) )
{
ack_flag--;
delay_us(5);
}
if( ack_flag <= 0 )
{
IIC_Stop();
return 1;
}
else
{
SCL(0);
SDA_OUT();
}
return ack;
}
/******************************************************************
* Function Name: Send_Byte
* Function Description: Write one byte
* Function Parameters: dat - data to write
* Function Return: None
* Author: LC
* Notes: None
******************************************************************/
void Send_Byte(uint8_t dat)
{
int i = 0;
SDA_OUT();
SCL(0);// Pull clock low to start data transfer
for( i = 0; i < 8; i++ )
{
SDA( (dat & 0x80) >> 7 );
delay_us(1);
SCL(1);
delay_us(5);
SCL(0);
delay_us(5);
dat<<=1;
}
}
/******************************************************************
* Function Name: Read_Byte
* Function Description: IIC read timing
* Function Parameters: None
* Function Return: Data read
* Author: LC
* Notes: None
******************************************************************/
unsigned char Read_Byte(void)
{
unsigned char i,receive=0;
SDA_IN();// Set SDA as input
for(i=0;i<8;i++ )
{
SCL(0);
delay_us(5);
SCL(1);
delay_us(5);
receive<<=1;
if( SDA_GET() )
{
receive|=1;
}
delay_us(5);
}
SCL(0);
return receive;
}
/******************************************************************
* Function Name: SGP30_Write
* Function Description: SGP30 write command
* Function Parameters: regaddr_H - command high 8 bits regaddr_L - command low 8 bits
* Function Return: None
* Author: LC
* Notes: None
******************************************************************/
void SGP30_Write_cmd(uint8_t regaddr_H, uint8_t regaddr_L)
{
IIC_Start();
Send_Byte(0XB0); // Send device address + write command
I2C_WaitAck();
Send_Byte(regaddr_H); // Send control address
I2C_WaitAck();
Send_Byte(regaddr_L); // Send data
I2C_WaitAck();
IIC_Stop();
delay_1ms(100);
}
/******************************************************************
* Function Name:
* Function Description:
* Function Parameters:
* Function Return:
* Author: LC
* Notes: The data format obtained by SGP30 is: 2 bytes of CO2 data + 1 byte of CO2 CRC check + 2 bytes of TVOC data + 1 byte of TVOC CRC check.
The module takes about 15s to initialize after power-on. During the initialization phase, the CO2 concentration read is 400ppm, and the TVOC is 0ppd and remains constant.
Therefore, after power-on, keep reading until TVOC is not 0 and CO2 is not 400, then the SGP30 module initialization is complete.
After initialization is complete, the data read out at the beginning will fluctuate greatly, which is a normal phenomenon, and will gradually stabilize after a period of time.
Gas sensors are easily affected by the environment, and fluctuations in measurement data are normal. You can add a filter function for filtering.
******************************************************************/
uint32_t SGP30_Read(void)
{
uint32_t dat;
uint8_t crc;
IIC_Start();
Send_Byte(0XB1); // Send device address + read command
I2C_WaitAck();
dat = Read_Byte();// CO2 data high 8 bits
IIC_Send_Ack(0);
dat <<= 8;
dat += Read_Byte();// CO2 data low 8 bits
IIC_Send_Ack(0);
crc = Read_Byte(); // CO2 CRC check
IIC_Send_Ack(0);
crc = crc;
dat <<= 8;
dat += Read_Byte();// TVOC data high 8 bits
IIC_Send_Ack(0);
dat <<= 8;
dat += Read_Byte();// TVOC data low 8 bits
IIC_Send_Ack(1);
IIC_Stop();
return(dat);
}
/******************************************************************
* Function Name: SGP30_Init
* Function Description: SGP30 initialization
* Function Parameters: None
* Function Return: None
* Author: LC
* Notes: None
******************************************************************/
void SGP30_Init(void)
{
SGP30_GPIO_Init();
SGP30_Write_cmd(0x20, 0x03);
}2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
In the bsp_sgp30.h file, write the following code.
/*
* LCSC-Openkits (LCKFB) software and hardware materials and related expansion board software and hardware materials are all open source on the official website.
* Dev board official website: www.lckfb.com
* Technical support resides on the forum; any technical questions are welcome for exchange and learning at any time.
* LCKFB Forum: club.szlcsc.com
* Follow our Bilibili account: [LCSC-Openkits (LCKFB)] to keep up with our latest updates!
* We do not make money by selling boards; we take cultivating engineers as our mission.
* Change Logs:
* Date Author Notes
* 2024-01-05 LCKFB-lp first version
*/
#ifndef _BSP_SGP30_H_
#define _BSP_SGP30_H_
#include <stdio.h>
#include <inttypes.h>
#include "sdkconfig.h"
#include "driver/gpio.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_rom_sys.h"
#include "esp_timer.h"
#include "driver/uart.h"
#include "rom/ets_sys.h"
#include "esp_system.h"
#include "driver/gptimer.h"
#include "esp_log.h"
#include "freertos/queue.h"
#include "driver/spi_master.h"
#include "nvs_flash.h"
#include "esp_adc/adc_cali_scheme.h"
#include "esp_adc/adc_cali.h"
#include "driver/adc.h"
#include "esp_adc_cal.h"
// Port porting
#define SGP30_SCL_PIN 1
#define SGP30_SDA_PIN 2
// Set SDA output mode
#define SDA_OUT() gpio_set_direction(SGP30_SDA_PIN,GPIO_MODE_OUTPUT)
// Set SDA input mode
#define SDA_IN() gpio_set_direction(SGP30_SDA_PIN,GPIO_MODE_INPUT)
// Get the level change of SDA pin
#define SDA_GET() gpio_get_level(SGP30_SDA_PIN)
// SDA and SCL output
#define SDA(x) gpio_set_level(SGP30_SDA_PIN, (x?1:0))
#define SCL(x) gpio_set_level(SGP30_SCL_PIN, (x?1:0))
void delay_us(unsigned int us);
void delay_ms(unsigned int ms);
void delay_1us(unsigned int us);
void delay_1ms(unsigned int ms);
void SGP30_Init(void);
uint32_t SGP30_Read(void);
void SGP30_Write_cmd(uint8_t a, uint8_t b);
#endif2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
Porting Verification
In the main function of your own project, write the following.
/*
* LCSC-Openkits (LCKFB) software and hardware materials and related expansion board software and hardware materials are all open source on the official website.
* Dev board official website: www.lckfb.com
* Technical support resides on the forum; any technical questions are welcome for exchange and learning at any time.
* LCKFB Forum: club.szlcsc.com
* Follow our Bilibili account: [LCSC-Openkits (LCKFB)] to keep up with our latest updates!
* We do not make money by selling boards; we take cultivating engineers as our mission.
* Change Logs:
* Date Author Notes
* 2024-01-05 LCKFB-lp first version
*/
#include <stdio.h>
#include "bsp_sgp30.h"
void app_main(void)
{
uint32_t CO2Data, TVOCData; // Define CO2 concentration variable and TVOC concentration variable
uint32_t sgp30_dat; // Define data read from SGP30
SGP30_Init();
delay_ms(1000);
while(1)
{
SGP30_Write_cmd(0x20,0x08);
sgp30_dat = SGP30_Read(); // Read SGP30 value
CO2Data = (sgp30_dat & 0xffff0000) >> 16; // Get CO2 value
TVOCData = sgp30_dat & 0x0000ffff; // Get TVOC value
printf("CO2 : %ld\r\nTVOC : %ld\r\n",CO2Data,TVOCData);
delay_ms(1000);
}
}2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
Power-on effect:
Driver code:
File Download
📌 Materials Download Center (click to jump)
📌 In the Materials Download Center -> Module Porting Materials Download, inside the compressed package of this chapter.