手里正好有个Jetson项目用到二氧化碳传感器SGP30,本着“独乐乐不如众乐乐”的心态,拿出来与大家分享。
初识二氧化碳传感器
如下图所示,这片SGP30是气体传感器,用于检测二氧化碳浓度。
很显然,这是一片Adafruit的产品。那么好消息就是:一方面产品质量有保证,另一方面是可以使用Adafruit相关的Python库来简化实现了。
通讯协议
这片SGP30使用的是I2C总线通讯,SCL与SDA需要与Jetson对应的引脚相连,可参考下图。
一般使用Jetson 40pin的3、5这一对I2C物理引脚(SDA、SCL)。
硬件连接
Jetson与SGP30物理连接参考下图。
依赖库准备
在正式编码前,记得装一下Adafruit的相应的库。
pip install adafruit-circuitpython-sgp30
adafruit的库会自动侦测0x58地址,即SGP30的I2C总线上的地址。
代码实现
由于使用I2C设备时,可能有异常导致I2C设备访问异常,记得使用软复位的方式解锁I2C总线。
def reset_i2c():
try:
# 创建 I2C 对象
i2c = busio.I2C(board.SCL, board.SDA)
# 尝试锁定 I2C 总线
i2c.try_lock()
# 向地址为 0x58 的设备发送软复位命令
i2c.writeto(0x58, bytes([0x30, 0xA2]))
# 解锁 I2C 总线
i2c.unlock()
except Exception as e:
# 若复位过程中出现异常,打印错误信息
print("Reset error:", e)
finally:
# 等待 0.5 秒,确保复位操作完成
time.sleep(0.5)
否则,第二次使用I2C设备时,很可能就找不到相应的地址了。
完整实现如下:
import time
import board
import busio
import adafruit_sgp30
# 该函数用于对 I2C 总线进行软复位操作
def reset_i2c():
try:
# 创建 I2C 对象
i2c = busio.I2C(board.SCL, board.SDA)
# 尝试锁定 I2C 总线
i2c.try_lock()
# 向地址为 0x58 的设备发送软复位命令
i2c.writeto(0x58, bytes([0x30, 0xA2]))
# 解锁 I2C 总线
i2c.unlock()
except Exception as e:
# 若复位过程中出现异常,打印错误信息
print("Reset error:", e)
finally:
# 等待 0.5 秒,确保复位操作完成
time.sleep(0.5)
# 该函数用于初始化 SGP30 传感器
def initialize_sgp30():
# 创建 I2C 对象,并设置频率为 100000
i2c = busio.I2C(board.SCL, board.SDA, frequency=100000)
try:
# 创建 Adafruit_SGP30 传感器实例
sgp30 = adafruit_sgp30.Adafruit_SGP30(i2c)
return sgp30
except Exception as e:
# 若初始化传感器时出现异常,打印错误信息并退出程序
print(f"初始化 SGP30 传感器时出错: {e}")
exit(1) # 该函数用于初始化传感器的基线
def initialize_baseline(sgp30):
try:
# 初始化传感器的空气质量指数
sgp30.iaq_init()
# 设置传感器的空气质量基线
sgp30.set_iaq_baseline(0x8973, 0x8AAE)
except Exception as e:
# 若初始化基线时出现异常,打印错误信息
print(f"初始化传感器基线时出错: {e}")
# 该函数用于对传感器进行预热操作
def preheat_sensor():
# 打印提示信息,告知用户传感器正在预热
print("SGP30 传感器正在预热,请等待...")
# 循环 15 次,模拟 15 秒的预热时间
for i in range(15):
# 每次循环等待 1 秒
time.sleep(1)
# 显示预热进度
print(".", end="", flush=True)
# 预热完成后,打印完成信息
print(" 预热完成!")
# 该函数用于从传感器读取二氧化碳和 TVOC 数据
def read_data(sgp30):
try:
# 调用传感器的 iaq_measure 方法读取数据
eCO2, TVOC = sgp30.iaq_measure()
# 打印读取到的二氧化碳和 TVOC 数据
print(f"eCO₂: {eCO2} ppm, TVOC: {TVOC} ppb")
# 等待 1 秒后进行下一次读取
time.sleep(1)
except Exception as e:
# 打印错误信息
print(f"发生错误: {e}")
# 主函数,程序的入口点
def main():
# 调用 reset_i2c 函数对 I2C 总线进行软复位
reset_i2c()
# 调用 initialize_sgp30 函数初始化 SGP30 传感器
sgp30 = initialize_sgp30()
# 调用 initialize_baseline 函数初始化传感器的基线
initialize_baseline(sgp30)
# 调用 preheat_sensor 函数对传感器进行预热
preheat_sensor()
try:
# 进入无限循环,持续读取传感器数据
while True:
# 调用 read_data 函数读取并打印数据
read_data(sgp30)
except KeyboardInterrupt:
# 若用户按下 Ctrl+C 中断程序,打印提示信息
print("程序异常")
finally:
# 无论程序是否正常结束,都释放 I2C 资源
sgp30._i2c.deinit()
if __name__ == "__main__":
# 调用主函数启动程序
main()
效果展示
程序执行效果如下:
这个效果,我还是相当满意的。