目录
1 SGP30介绍
SGP30是一种高性能气体传感器,用于检测室内空气中的二氧化碳(CO2)和挥发性有机化合物(TVOS)。它由Sensirion公司设计和生产,采用了最新的传感器技术和算法,能够提供准确可靠的气体测量数据。SGP30采用先进的MOX(金属氧化物)传感器技术,能够快速、准确地测量二氧化碳和挥发性有机化合物的浓度。它可以检测从室内空气中释放出的有害气体,例如甲醛、醇类、酮类和酯类化合物等。SGP30的测量范围广泛,可测量的CO2浓度范围为400ppm至60,000ppm,TVOS浓度范围为0ppb至60,000ppb。
SGP30具有高灵敏度、低功耗和可靠性。它采用数字通信接口,可以轻松集成到各种应用中。此外,SGP30还具有温度和湿度补偿功能,可以提高测量的准确性和稳定性。SGP30广泛应用于室内空气质量监测、室外空气污染监测、智能家居系统、工业过程控制和航空航天等领域。它可以帮助用户实时监测和控制室内空气质量,提供一个舒适、健康的生活和工作环境。
SGP30在智能家居、农业环境检测等相关课程设计或毕业设计中经常出现,作为一款检测精度高,检测速度快的传感器,下面将使用标准库读取SGP30二氧化碳的值,ESP32使用库读取SGP30的二氧化碳值与TVOC值。
2.STM32驱动代码
stm32与sgp30接线
PB0 SCL
PB1 SDA
VCC VCC
GND GND
stm32与oled屏幕接线
PB8 SCL
PB9 SDA
VCC VCC
GND GND
sgp30.c
#include "sgp30.h"
#include "Delay.h"
void SGP30_GPIO_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(SGP30_SCL_GPIO_CLK | SGP30_SDA_GPIO_SDA, ENABLE);
GPIO_InitStructure.GPIO_Pin = SGP30_SCL_GPIO_PIN;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(SGP30_SCL_GPIO_PORT, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = SGP30_SDA_GPIO_PIN;
GPIO_Init(SGP30_SDA_GPIO_PORT, &GPIO_InitStructure);
}
void SDA_OUT(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Pin = SGP30_SDA_GPIO_PIN;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(SGP30_SDA_GPIO_PORT, &GPIO_InitStructure);
}
void SDA_IN(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Pin = SGP30_SDA_GPIO_PIN;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_10MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(SGP30_SDA_GPIO_PORT, &GPIO_InitStructure);
}
//产生IIC起始信号
void SGP30_IIC_Start(void)
{
SDA_OUT();
SGP30_SDA_HIGH;
SGP30_SCL_HIGH;
delay_us(20);
// SGP30_SDA = 0; //START:when CLK is high,DATA change form high to low
SGP30_SDA_LOW;
delay_us(20);
// SGP30_SCL = 0; //钳住I2C总线,准备发送或接收数据
SGP30_SCL_LOW;
}
//产生IIC停止信号
void SGP30_IIC_Stop(void)
{
SDA_OUT();
// SGP30_SCL = 0;
// SGP30_SDA = 0; //STOP:when CLK is high DATA change form low to high
SGP30_SCL_LOW;
SGP30_SDA_LOW;
delay_us(20);
// SGP30_SCL = 1;
// SGP30_SDA = 1; //发送I2C总线结束信号
SGP30_SCL_HIGH;
SGP30_SDA_HIGH;
delay_us(20);
}
//等待应答信号到来
//返回值:1,接收应答失败
// 0,接收应答成功
u8 SGP30_IIC_Wait_Ack(void)
{
u8 ucErrTime = 0;
SDA_IN();
// SGP30_SDA = 1;
SGP30_SDA_HIGH;
delay_us(10);
// SGP30_SCL = 1;
SGP30_SCL_HIGH;
delay_us(10);
while(SGP30_SDA_READ())
{
ucErrTime++;
if(ucErrTime > 250)
{
SGP30_IIC_Stop();
return 1;
}
}
// SGP30_SCL = 0; //时钟输出0
SGP30_SCL_LOW;
return 0;
}
//产生ACK应答
void SGP30_IIC_Ack(void)
{
// SGP30_SCL = 0;
SGP30_SCL_LOW;
SDA_OUT();
// SGP30_SDA = 0;
SGP30_SDA_LOW;
delay_us(20);
// SGP30_SCL = 1;
SGP30_SCL_HIGH;
delay_us(20);
// SGP30_SCL = 0;
SGP30_SCL_LOW;
}
//不产生ACK应答
void SGP30_IIC_NAck(void)
{
// SGP30_SCL = 0;
SGP30_SCL_LOW;
SDA_OUT();
// SGP30_SDA = 1;
SGP30_SDA_HIGH;
delay_us(20);
// SGP30_SCL = 1;
SGP30_SCL_HIGH;
delay_us(20);
// SGP30_SCL = 0;
SGP30_SCL_LOW;
}
//IIC发送一个字节
//返回从机有无应答
//1,有应答
//0,无应答
void SGP30_IIC_Send_Byte(u8 txd)
{
u8 t;
SDA_OUT();
// SGP30_SCL = 0; //拉低时钟开始数据传输
SGP30_SCL_LOW;
for(t = 0; t < 8; t++)
{
if((txd & 0x80) >> 7)
// SGP30_SDA = 1;
SGP30_SDA_HIGH;
else
// SGP30_SDA = 0;
SGP30_SDA_LOW;
txd <<= 1;
delay_us(20);
// SGP30_SCL = 1;
SGP30_SCL_HIGH;
delay_us(20);
// SGP30_SCL = 0;
SGP30_SCL_LOW;
delay_us(20);
}
delay_us(20);
}
//读1个字节,ack=1时,发送ACK,ack=0,发送nACK
u16 SGP30_IIC_Read_Byte(u8 ack)
{
u8 i;
u16 receive = 0;
SDA_IN();
for(i = 0; i < 8; i++ )
{
// SGP30_SCL = 0;
SGP30_SCL_LOW;
delay_us(20);
// SGP30_SCL = 1;
SGP30_SCL_HIGH;
receive <<= 1;
if(SGP30_SDA_READ())
receive++;
delay_us(20);
}
if (!ack)
SGP30_IIC_NAck();//发送nACK
else
SGP30_IIC_Ack(); //发送ACK
return receive;
}
//初始化IIC接口
void SGP30_Init(void)
{
SGP30_GPIO_Init();
SGP30_Write(0x20, 0x03);
// SGP30_ad_write(0x20,0x61);
// SGP30_ad_write(0x01,0x00);
}
void SGP30_Write(u8 a, u8 b)
{
SGP30_IIC_Start();
SGP30_IIC_Send_Byte(SGP30_write); //发送器件地址+写指令
SGP30_IIC_Wait_Ack();
SGP30_IIC_Send_Byte(a); //发送控制字节
SGP30_IIC_Wait_Ack();
SGP30_IIC_Send_Byte(b);
SGP30_IIC_Wait_Ack();
SGP30_IIC_Stop();
delay_ms(100);
}
u32 SGP30_Read(void)
{
u32 dat;
u8 crc;
SGP30_IIC_Start();
SGP30_IIC_Send_Byte(SGP30_read); //发送器件地址+读指令
SGP30_IIC_Wait_Ack();
dat = SGP30_IIC_Read_Byte(1);
dat <<= 8;
dat += SGP30_IIC_Read_Byte(1);
crc = SGP30_IIC_Read_Byte(1); //crc数据,舍去
crc = crc; //为了不让出现编译警告
dat <<= 8;
dat += SGP30_IIC_Read_Byte(1);
dat <<= 8;
dat += SGP30_IIC_Read_Byte(0);
SGP30_IIC_Stop();
return(dat);
}
sgp30.h
#ifndef __SG_H
#define __SG_H
#include "stm32f10x.h"
//#define SGP30_SCL PBout(0)
//#define SGP30_SDA PBout(1)
#define SGP30_SCL_LOW GPIO_ResetBits ( GPIOB, GPIO_Pin_0 )
#define SGP30_SCL_HIGH GPIO_SetBits ( GPIOB, GPIO_Pin_0 )
#define SGP30_SDA_LOW GPIO_ResetBits ( GPIOB, GPIO_Pin_1 )
#define SGP30_SDA_HIGH GPIO_SetBits ( GPIOB, GPIO_Pin_1 )
//#define SGP30_SDA PBout(1)
// USART GPIO 引脚宏定义
#define SGP30_SCL_GPIO_CLK RCC_APB2Periph_GPIOB
#define SGP30_SCL_GPIO_PORT GPIOB
#define SGP30_SCL_GPIO_PIN GPIO_Pin_0
#define SGP30_SDA_GPIO_SDA RCC_APB2Periph_GPIOB
#define SGP30_SDA_GPIO_PORT GPIOB
#define SGP30_SDA_GPIO_PIN GPIO_Pin_1
#define SGP30_SDA_READ() GPIO_ReadInputDataBit(SGP30_SDA_GPIO_PORT, SGP30_SDA_GPIO_PIN)
#define SGP30_read 0xb1 //SGP30的读地址
#define SGP30_write 0xb0 //SGP30的写地址
void SGP30_IIC_Start(void); //发送IIC开始信号
void SGP30_IIC_Stop(void); //发送IIC停止信号
void SGP30_IIC_Send_Byte(u8 txd); //IIC发送一个字节
u16 SGP30_IIC_Read_Byte(unsigned char ack);//IIC读取一个字节
u8 SGP30_IIC_Wait_Ack(void); //IIC等待ACK信号
void SGP30_IIC_Ack(void); //IIC发送ACK信号
void SGP30_IIC_NAck(void); //IIC不发送ACK信号
void SGP30_IIC_Write_One_Byte(u8 daddr,u8 addr,u8 data);
u8 SGP30_IIC_Read_One_Byte(u8 daddr,u8 addr);
void SGP30_Init(void);
void SGP30_Write(u8 a, u8 b);
u32 SGP30_Read(void);
#endif
main.c
#include "stm32f10x.h" // Device header
#include "OLED.h"
#include "sys.h"
#include "usart.h"
#include "delay.h"
#include "sgp30.h"
u32 CO2Data,TVOCData;//定义CO2浓度变量与TVOC浓度变量
int sgp30_dat;
int main(void)
{
OLED_Init();//初始化OLED
OLED_Clear();//清屏
delay_init();
SGP30_Init(); //初始化SGP30
delay_ms(100);
SGP30_Write(0x20,0x08);
OLED_ShowString(1, 1, "CO2:");
sgp30_dat = SGP30_Read();//读取SGP30的值
while(CO2Data == 400)
{
SGP30_Write(0x20,0x08);
sgp30_dat = SGP30_Read();//读取SGP30的值
CO2Data = (sgp30_dat & 0xffff0000) >> 16;//取出CO2浓度值
delay_ms(500);
}
while(1)
{
SGP30_Write(0x20,0x08);
sgp30_dat = SGP30_Read();//读取SGP30的值
CO2Data = (sgp30_dat & 0xffff0000) >> 16;//取出CO2浓度值
OLED_ShowNum(1, 9, CO2Data, 4);
}
}
3.ESP32驱动代码
使用单片机:ESP32-WROOM
单片机选型如下,Arduino UNO也可使用下面的代码,将单片机的种类进行更换即可
ESP32-WROOM与sgp30接线
22 SCL
21 SDA
VCC VCC
GND GND
完成接线后烧录程序后,打开串口监视器,选择波特率为115200即可查看读取的TVOC值和二氧化碳浓度值
#include <Wire.h>
#include "Adafruit_SGP30.h"
Adafruit_SGP30 sgp;
#define SCL 22
#define SDA 21
/* return absolute humidity [mg/m^3] with approximation formula
* @param temperature [°C]
* @param humidity [%RH]
*/
uint32_t getAbsoluteHumidity(float temperature, float humidity)
{
const float absoluteHumidity = 216.7f * ((humidity / 100.0f) * 6.112f * exp((17.62f * temperature) / (243.12f + temperature)) / (273.15f + temperature));
const uint32_t absoluteHumidityScaled = static_cast<uint32_t>(1000.0f * absoluteHumidity); // [mg/m^3]
return absoluteHumidityScaled;
}
void TVOC_CO2()
{
int counter = 0;
if (! sgp.IAQmeasure()) {
Serial.println("Measurement failed");
return;
}
Serial.print("TVOC "); Serial.print(sgp.TVOC); Serial.print(" ppb\t");
Serial.print("eCO2 "); Serial.print(sgp.eCO2); Serial.println(" ppm");
if (! sgp.IAQmeasureRaw()) {
Serial.println("Raw Measurement failed");
return;
}
Serial.print("Raw H2 "); Serial.print(sgp.rawH2); Serial.print(" \t");
Serial.print("Raw Ethanol "); Serial.print(sgp.rawEthanol); Serial.println("");
delay(1000);
counter++;
if (counter == 30) {
counter = 0;
uint16_t TVOC_base, eCO2_base;
if (! sgp.getIAQBaseline(&eCO2_base, &TVOC_base)) {
Serial.println("Failed to get baseline readings");
return;
}
Serial.print("****Baseline values: eCO2: 0x"); Serial.print(eCO2_base, HEX);
Serial.print(" & TVOC: 0x"); Serial.println(TVOC_base, HEX);
}
}
void setup() {
Serial.begin(115200);
Wire.begin(SDA,SCL);
if (! sgp.begin())
{
while (1);
}
Serial.print("Found SGP30 serial #");
Serial.print(sgp.serialnumber[0], HEX);
Serial.print(sgp.serialnumber[1], HEX);
Serial.println(sgp.serialnumber[2], HEX);
}
void loop()
{
TVOC_CO2();
}