基于 Raspberry Pi Pico 2 C/C++ SDK 的 SGP30 空气质量监测器
采用设备
主控:Raspberry Pi Pico 2 (RP2350)

传感器:SGP30(I2C 地址 0x58),检测 eCO₂ 和 TVOC

显示屏:SSD1306 128×64 I2C OLED(地址 0x3C)

接线方式:
| Pico 2 引脚 | 连接设备 | 说明 |
| GPIO4 | SGP30 SDA | 软件 I2C |
| GPIO4 | SGP30 SCL | 软件 I2C |
| GPIO4 | SSD1306 SDA | 硬件 I2C0 |
| GPIO4 | SSD1306 SCL | 硬件 I2C0 |
| 3V3(OUT) | VCC (两设备) | 供电 |
| GND |
GND (两设备) |
共地 |
两设备均使用 3.3V 供电,共地
程序
一、SGP30 驱动库结构(sgp30.h + sgp30.c)
这两个文件是我从 STM32 的 HAL 库 和 Arduino UNO 的 Adafruit_SGP30 库 移植到 Pico SDK 上的。核心改动是把硬件 I2C 换成了软件模拟,并保留了所有官方命令和 CRC 校验。
头文件 sgp30.h 重点
// 可自定义引脚(默认 GPIO4/5)
#define SGP30_SDA_PIN 4
#define SGP30_SCL_PIN 5
// 所有 SGP30 命令码
#define SGP30_CMD_INIT_AIR_QUALITY 0x2003
#define SGP30_CMD_MEASURE_AIR_QUALITY 0x2008
#define SGP30_CMD_SET_HUMIDITY 0x2061
// ...
// 数据结构
typedef struct {
uint16_t co2; // eCO₂ (ppm)
uint16_t tvoc; // TVOC (ppb)
uint8_t crc_ok; // 两段 CRC 是否全部通过
} sgp30_data_t;
// 公开 API
void SGP30_Init(void);
uint8_t SGP30_Write(uint8_t cmd_high, uint8_t cmd_low);
uint8_t SGP30_Read(sgp30_data_t *data);
uint8_t SGP30_SetHumidity(uint16_t abs_humidity);
uint8_t SGP30_GetBaseline(uint16_t *eco2, uint16_t *tvoc);
uint8_t SGP30_SetBaseline(uint16_t eco2, uint16_t tvoc);
uint8_t SGP30_SoftReset(void);
uint8_t SGP30_MeasureTest(uint16_t *result);
uint8_t SGP30_GetFeatureSet(uint16_t *version);
uint8_t SGP30_MeasureRaw(uint16_t *ethanol, uint16_t *h2);
uint8_t SGP30_CRC8(const uint8_t *data, uint8_t len);
实现文件 sgp30.c 核心 – 软件 I2C
// 起始条件
static void i2c_start(void) {
SDA_HIGH(); SCL_HIGH(); DELAY_US();
SDA_LOW(); DELAY_US(); SCL_LOW(); DELAY_US();
}
// 写一个字节 + 等待 ACK
static void i2c_write_byte(uint8_t data) {
for (int i = 0; i < 8; i++) {
(data & 0x80) ? SDA_HIGH() : SDA_LOW();
data <<= 1;
DELAY_US(); SCL_HIGH(); DELAY_US(); SCL_LOW(); DELAY_US();
}
}
// 读一个字节,可选择回复 ACK 或 NACK
static uint8_t i2c_read_byte(uint8_t send_ack) { ... }
CRC‑8 校验完全按照 SGP30 数据手册实现(多项式 0x31,初始值 0xFF):
uint8_t SGP30_CRC8(const uint8_t *data, uint8_t len) {
uint8_t crc = 0xFF;
for (uint8_t i = 0; i < len; i++) {
crc ^= data[i];
for (uint8_t j = 0; j < 8; j++)
crc = (crc & 0x80) ? (crc << 1) ^ 0x31 : (crc << 1);
}
return crc;
}
二、主程序结构与功能(pico-sgp30.c)
程序结构
main()
├── stdio_init_all() // USB 串口调试输出
├── ssd1306_init() // 硬件 I2C 初始化 OLED
│ ├── i2c_init(i2c0, 400kHz)
│ ├── 发送 OLED 初始化命令序列
│ └── 清屏 + 显示标题 "SGP30 Sensor"
├── SGP30_Init() // 软件 I2C 初始化 SGP30
│ └── 发送 init_air_quality 命令
└── while(1):
├── 发送 measure_air_quality 命令
├── 读取 6 字节 + CRC 校验
├── 【预热阶段】(前 15 秒,CO₂=400, TVOC=0)
│ ├── OLED 显示倒计时
│ └── 串口打印剩余秒数
└── 【正常运行】
├── OLED 显示 eCO₂ (ppm) + TVOC (ppb)
├── 若 CRC 错误则额外显示 "CRC FAIL!"
├── 串口打印数据
└── sleep 2 秒
OLED 显示布局(128×64,8 个 page)
| Page | 内容 |
| 0 |
SGP30 Sensor (固定标题) |
| 2 |
eCO2: 400 ppm (5位右对齐) |
| 4 |
TVOC: 0 ppb (挥发性有机物浓度) |
| 6 |
CRC FAIL! (仅出错时显示) |
关键代码片段 – 预热检测
SGP30 传感器上电后前 15 秒会强制输出 CO₂=400 ppm、TVOC=0 ppb。主程序利用这一特性:
if (!warmed_up && (now - start_ms) < 15000 && data.co2 == 400 && data.tvoc == 0) {
uint32_t elapsed = (now - start_ms) / 1000;
snprintf(buf, sizeof(buf), "Warm-up %lus... ", 15 - elapsed);
ssd1306_draw_string(2, 0, buf);
printf("Warming up... %lu s remaining\n", 15 - elapsed);
sleep_ms(1000);
continue;
}
编译与烧录
1. 前置工具
Raspberry Pi Pico C/C++ SDK
https://github.com/raspberrypi/pico-sdk
ARM GCC 交叉编译器 (arm-none-eabi-gcc)
CMake (≥ 3.13) + Ninja
2. 构建命令
git clone https://github.com/jasonobama/pico-sgp30.git
cd pico-sgp30
export PICO_SDK_PATH=/path/to/pico-sdk # 设置 SDK 路径
cmake -B build -G Ninja .
cmake --build build
3. 烧录到 Pico 2
-
按住 BOOTSEL 键,插入 USB
-
将 pico-sgp30.uf2 拖入弹出的 RPI-RP2 盘符
-
程序自动运行,OLED 点亮
源码
Raspberry Pi Pico C/C++ SDK
https://github.com/Jasonobama/pico-sgp30.git
更多推荐

所有评论(0)