智能硬件项目中的传感器实验,核心价值并不只是"读到一个数值"。更重要的是让程序具备感知环境变化的能力,并根据检测结果驱动外部设备做出反馈。烟雾检测就是一个非常典型的场景:传感器负责感知空气状态,开发板负责读取信号,程序负责判断是否异常,蜂鸣器负责把异常状态转化成可听见的报警提示。
本实验使用 CanMV K210 开发板读取烟雾传感器的 AO 和 DO 两种输出信号。DO 数字量输出直接接入 GPIO,用来判断是否达到报警阈值;AO 模拟量输出接入 PCF8591 模数转换模块,用来观察烟雾浓度变化趋势。程序运行后,会循环读取烟雾传感器状态,当 DO 触发烟雾报警条件时,蜂鸣器会按照设定节奏间歇鸣叫;环境恢复正常时,蜂鸣器关闭,串口持续输出当前检测状态。
| 学习目标 | 说明 |
|---|---|
| 理解 AO/DO 双路输出 | 区分烟雾传感器模拟量输出和数字量输出的作用 |
| 掌握 GPIO 输入检测 | 使用 GPIO 读取 DO 数字触发信号 |
| 掌握 ADC 模拟量采集 | 通过 PCF8591 读取 AO 模拟量变化 |
| 理解报警输出控制 | 使用 GPIO 控制有源蜂鸣器进行间歇报警 |
| 建立环境监测思路 | 将传感器输入、状态判断和外设反馈组合成完整检测流程 |
本实验用于课程学习和传感器原理演示,不能替代专业消防报警设备。实验时应保持通风,避免使用明火或危险气体进行测试。
文章目录
理论基础
烟雾传感器模块常见输出方式包括 AO 和 DO。AO 是模拟量输出,会随着气体浓度或烟雾状态变化而连续变化,适合观察趋势;DO 是数字量输出,由模块上的比较器和电位器决定触发阈值,适合快速判断是否达到报警条件。AO 更适合"看变化",DO 更适合"做判断"。
普通 GPIO 只能稳定读取高低电平,适合读取 DO 这种数字信号。AO 属于模拟电压信号,不能直接通过普通 GPIO 精确读取,因此本实验使用 PCF8591 模数转换模块把 AO 转换成数字值。程序通过 I2C 访问 PCF8591,再读取 AIN0 通道得到烟雾传感器的模拟量参考值。
蜂鸣器属于报警输出设备。当前代码使用的是有源蜂鸣器,不需要程序输出 PWM 音调,只需要通过 GPIO 输出触发电平即可控制响与停。代码中 BUZZER_ACTIVE_LEVEL = 0、BUZZER_SILENT_LEVEL = 1,表示当前蜂鸣器按低电平触发方式编写。
下面这张流程图从电路控制链路理解本实验。烟雾传感器同时输出 AO 和 DO,DO 进入 GPIO 做报警判断,AO 经过 PCF8591 转换后用于观察变化趋势,最终由程序决定是否启动蜂鸣器报警。
空气环境
烟雾 / 气体变化
烟雾传感器模块
AO / DO 双路输出
DO 数字输出
达到阈值后电平变化
CanMV K210
物理引脚 8
GPIOHS0 输入
read_gas_do()
报警判断
is_gas_detected()
AO 模拟输出
浓度变化趋势
PCF8591 AIN0
ADC 模数转换
I2C 通信
SCL=6 / SDA=7
AO 数值读取
read_gas_adc()
状态逻辑
环境安全 / 检测到烟雾
蜂鸣器控制
alarm_beep()
有源蜂鸣器
间歇报警
模块供电
VCC
公共地线
GND
从这条链路可以看出,本实验不是单纯读取一个传感器值,而是同时使用了数字量输入、模拟量采集和声音输出。DO 用于判断是否报警,AO 用于观察环境变化,蜂鸣器用于把异常状态反馈给使用者。这种结构很接近真实环境监测设备的基本工作方式。
硬件设施
本实验真正使用到的硬件包括 CanMV K210 开发板、烟雾传感器、PCF8591 模数转换模块和有源蜂鸣器。烟雾传感器的 DO 直接由 GPIO 读取,AO 不能直接被普通 GPIO 读取,需要借助 PCF8591 转换成数字值。蜂鸣器作为报警输出设备,由 GPIO 控制高低电平实现开启和关闭。
接线关系可以先通过下面这张图建立整体印象。烟雾传感器 DO 接入 CanMV K210 的物理引脚 8,AO 接入 PCF8591 的 AIN0,PCF8591 通过 SCL 和 SDA 与开发板通信,蜂鸣器接入物理引脚 10 用于报警输出。

| 硬件 / 软件 | 作用 | 说明 |
|---|---|---|
| CanMV K210 开发板 | 实验运行平台 | 负责执行 MicroPython 程序,读取传感器信号并控制蜂鸣器 |
| 烟雾传感器 | 环境检测模块 | 提供 DO 数字输出和 AO 模拟输出,DO 用于报警判断,AO 用于观察浓度趋势 |
| PCF8591 模数转换模块 | 模拟量采集模块 | 将烟雾传感器 AO 模拟信号转换成程序可读取的数字值 |
| 有源蜂鸣器 | 报警提示设备 | 当检测到烟雾时,由 GPIO 输出控制蜂鸣器间歇报警 |
maix.GPIO |
GPIO 控制模块 | 用于创建数字输入对象和蜂鸣器输出对象 |
machine.I2C |
I2C 通信模块 | 用于建立 CanMV 与 PCF8591 之间的软件 I2C 通信 |
fpioa_manager.fm |
引脚功能映射模块 | 用于将物理引脚绑定到 GPIOHS 功能 |
pcf8591 |
ADC 驱动模块 | 用于读取 PCF8591 指定通道的模拟量数据 |
time |
延时控制模块 | 用于控制采样间隔和蜂鸣器鸣叫节奏 |
实验中用到的核心模块如下。烟雾传感器负责感知环境变化,PCF8591 负责读取 AO 模拟量,有源蜂鸣器负责报警提示。接线时要区分 AO、DO、VCC、GND,避免把模拟输出和数字输出接反。

接线关系可以根据代码中的硬件配置区和初始化函数进行整理。当前程序已经明确标注烟雾传感器 DO、蜂鸣器、I2C SCL、I2C SDA 和 PCF8591 ADC 通道,因此下表按照代码配置进行说明。
| 物理引脚 / 接口 | 功能信号 | 代码变量 | 对应硬件 | 说明 |
|---|---|---|---|---|
| 物理引脚 8 | GPIOHS0 | GAS_DO_PIN / gas_do |
烟雾传感器 DO | 用于读取烟雾传感器数字触发信号,代码中启用上拉输入 |
| 物理引脚 10 | GPIOHS3 | BUZZER_PIN / buzzer |
有源蜂鸣器 | 用于输出控制信号,低电平时蜂鸣器发声 |
| 物理引脚 6 | I2C SCL | I2C_SCL_PIN |
PCF8591 SCL | 软件 I2C 时钟线,用于 ADC 模块通信 |
| 物理引脚 7 | I2C SDA | I2C_SDA_PIN |
PCF8591 SDA | 软件 I2C 数据线,用于 ADC 模块通信 |
| PCF8591 AIN0 | ADC 通道 0 | ADC_CHANNEL |
烟雾传感器 AO | 读取烟雾传感器模拟量输出,用于观察浓度变化 |
| VCC | 模块供电 | 未在代码中体现 | 烟雾传感器、PCF8591、蜂鸣器 | 根据模块标识连接对应电源 |
| GND | 公共地线 | 未在代码中体现 | 所有模块地线 | CanMV、传感器、PCF8591 和蜂鸣器需要共地 |
完成接线后的整体效果如下。检查时重点关注四组连接:烟雾传感器 DO 是否接到物理引脚 8,AO 是否接到 PCF8591 AIN0,PCF8591 的 SCL/SDA 是否接到物理引脚 6/7,蜂鸣器信号线是否接到物理引脚 10。

| 实验现象 | 正常表现 | 异常提示 |
|---|---|---|
| 程序启动 | 串口输出初始化完成,蜂鸣器默认关闭 | 如果蜂鸣器上电一直响,检查触发电平或蜂鸣器接线 |
| 环境正常 | 串口显示环境安全,蜂鸣器保持关闭 | 如果一直报警,检查烟雾传感器电位器和 DO 触发电平 |
| AO 数值变化 | 串口中的 AO 模拟值随环境变化而变化 | 如果 AO 不变,检查 AO 到 AIN0、SCL、SDA 和 PCF8591 供电 |
| DO 触发 | 串口显示检测到烟雾,蜂鸣器间歇鸣叫 | 如果 DO 触发但蜂鸣器不响,单独检查蜂鸣器控制 |
| 环境恢复 | 蜂鸣器停止,状态回到环境安全 | 如果不恢复,调整传感器阈值或等待传感器恢复 |
| 停止程序 | 蜂鸣器关闭,串口输出停止提示 | 如果停止后仍响,需要确认异常退出时是否执行 buzzer_off() |
软件代码
本实验代码围绕烟雾检测和蜂鸣器报警展开。程序结构可以分成硬件配置、对象初始化、蜂鸣器控制、传感器读取、状态显示和主循环检测几个部分。DO 负责判断是否报警,AO 负责提供辅助观察值,蜂鸣器负责把异常状态转化成声音提醒。
| 软件环境 | 作用 | 检查重点 |
|---|---|---|
| CanMV IDE | 编辑、运行和调试 K210 程序 | 能识别开发板串口,并能运行基础 print() 测试 |
| CanMV 固件 | 提供 maix.GPIO、machine.I2C、fpioa_manager 等模块 |
固件环境需要支持 GPIO、I2C 和 FPIOA 写法 |
| USB 串口驱动 | 让电脑识别开发板串口 | 串口工具或 IDE 中能看到对应端口 |
pcf8591.py |
PCF8591 驱动文件 | 需要能够正常 import pcf8591 |
| 串口终端 | 查看检测状态和 AO 数值 | 能看到环境安全、检测到烟雾、报警提示等输出 |
| 有源蜂鸣器 | 输出报警声音 | 检测到烟雾时能够按节奏间歇鸣叫 |
python
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
CanMV K210 烟雾传感器实验 Demo
实验说明:
烟雾传感器通常同时提供 AO 和 DO 两种输出。
AO:模拟量输出,需要通过 PCF8591 读取,可观察烟雾浓度变化趋势。
DO:数字量输出,可直接通过 GPIO 读取,由模块电位器决定触发阈值。
本 Demo 以 DO 作为报警判断依据,以 AO 作为辅助观察数据。
当检测到烟雾时,蜂鸣器会间歇报警。
"""
import time
from maix import GPIO
from machine import I2C
from fpioa_manager import fm
import pcf8591
# =========================
# 硬件配置区
# =========================
GAS_DO_PIN = 8 # 烟雾传感器 DO 接开发板物理引脚 8
BUZZER_PIN = 10 # 有源蜂鸣器接开发板物理引脚 10
I2C_SCL_PIN = 6 # PCF8591 SCL
I2C_SDA_PIN = 7 # PCF8591 SDA
ADC_CHANNEL = 0 # 烟雾传感器 AO 接 PCF8591 的 AIN0
GAS_ACTIVE_LEVEL = 0 # 烟雾传感器 DO 低电平表示触发
BUZZER_ACTIVE_LEVEL = 0 # 有源蜂鸣器低电平发声
BUZZER_SILENT_LEVEL = 1 # 有源蜂鸣器高电平关闭
# =========================
# 全局对象
# =========================
gas_do = None
buzzer = None
gas_adc = None
# =========================
# 初始化函数
# =========================
def setup():
"""初始化 GPIO、I2C、PCF8591 和蜂鸣器"""
global gas_do, buzzer, gas_adc
fm.register(GAS_DO_PIN, fm.fpioa.GPIOHS0, force=True)
fm.register(BUZZER_PIN, fm.fpioa.GPIOHS3, force=True)
i2c = I2C(
I2C.I2C_SOFT,
freq=400000,
scl=I2C_SCL_PIN,
sda=I2C_SDA_PIN,
gscl=fm.fpioa.GPIOHS1,
gsda=fm.fpioa.GPIOHS2
)
gas_do = GPIO(GPIO.GPIOHS0, GPIO.IN, GPIO.PULL_UP)
buzzer = GPIO(GPIO.GPIOHS3, GPIO.OUT)
gas_adc = pcf8591.PCF8591(i2c)
buzzer_off()
print("烟雾传感器初始化完成")
print("DO 用于报警判断,AO 用于观察浓度变化")
# =========================
# 基础控制函数
# =========================
def buzzer_on():
"""打开蜂鸣器"""
buzzer.value(BUZZER_ACTIVE_LEVEL)
def buzzer_off():
"""关闭蜂鸣器"""
buzzer.value(BUZZER_SILENT_LEVEL)
def beep(on_ms=100, off_ms=100, repeat=1):
"""蜂鸣器按指定节奏鸣叫"""
for _ in range(repeat):
buzzer_on()
time.sleep_ms(on_ms)
buzzer_off()
time.sleep_ms(off_ms)
def alarm_beep():
"""烟雾报警提示音"""
beep(120, 120, 2)
# =========================
# 传感器读取函数
# =========================
def read_gas_do():
"""读取烟雾传感器数字信号"""
return gas_do.value()
def read_gas_adc(samples=5):
"""
多次读取 AO 模拟量并取平均值
可以减少单次采样抖动
"""
total = 0
for _ in range(samples):
total += gas_adc.read(ADC_CHANNEL)
time.sleep_ms(10)
return total // samples
def is_gas_detected(do_value):
"""根据 DO 电平判断是否检测到烟雾"""
return do_value == GAS_ACTIVE_LEVEL
# =========================
# 状态显示函数
# =========================
def show_status(gas_detected, adc_value):
"""打印当前烟雾检测状态"""
if gas_detected:
print("状态:检测到烟雾 | AO模拟值:{}".format(adc_value))
else:
print("状态:环境安全 | AO模拟值:{}".format(adc_value))
# =========================
# 主循环
# =========================
def loop():
"""
循环检测烟雾状态
状态变化时打印提醒
检测到烟雾时启动蜂鸣器报警
"""
last_status = None
while True:
adc_value = read_gas_adc()
do_value = read_gas_do()
gas_detected = is_gas_detected(do_value)
if gas_detected != last_status:
show_status(gas_detected, adc_value)
last_status = gas_detected
if gas_detected:
print("报警中:烟雾浓度异常,蜂鸣器提示")
alarm_beep()
else:
buzzer_off()
time.sleep_ms(500)
# =========================
# 程序入口
# =========================
if __name__ == '__main__':
try:
setup()
loop()
except KeyboardInterrupt:
buzzer_off()
print("程序已停止,蜂鸣器已关闭")
这段代码的配置区把硬件连接关系集中放在开头,例如 GAS_DO_PIN 表示烟雾传感器 DO 所连接的物理引脚,BUZZER_PIN 表示蜂鸣器控制引脚,I2C_SCL_PIN 和 I2C_SDA_PIN 表示 PCF8591 的 I2C 通信线。后续更换接线时,只需要修改配置区,不需要在函数内部到处查找引脚编号。
setup() 是整个程序的初始化入口。它通过 fm.register() 把物理引脚 8 和物理引脚 10 分别绑定到 GPIOHS0 和 GPIOHS3。烟雾传感器 DO 使用 GPIO.IN 输入模式,并开启 GPIO.PULL_UP 上拉;蜂鸣器使用 GPIO.OUT 输出模式,由程序主动控制电平。PCF8591 通过软件 I2C 初始化,程序通过 SCL 和 SDA 两根线与 ADC 模块通信。
传感器读取部分把 DO 和 AO 分开处理。read_gas_do() 读取数字电平,适合做是否报警的判断;read_gas_adc() 多次读取 PCF8591 的 AIN0 通道并取平均值,用来减少单次采样抖动。is_gas_detected() 把电平值和触发规则隔离出来,当前代码中 GAS_ACTIVE_LEVEL = 0,表示 DO 低电平时认为检测到烟雾。
蜂鸣器控制部分使用了三个层次的封装。buzzer_on() 和 buzzer_off() 分别控制蜂鸣器发声和静音,beep() 在此基础上加入延时和重复次数,alarm_beep() 再把报警声音固定为 120 毫秒响、120 毫秒停、重复 2 次。这样的结构比在主循环中直接写高低电平更清晰,也方便后续调整报警节奏。
| 函数名 | 功能 | 对应现象 |
|---|---|---|
setup() |
初始化 GPIO、I2C、PCF8591 和蜂鸣器 | 程序完成硬件准备,蜂鸣器默认关闭 |
buzzer_on() |
打开蜂鸣器 | 有源蜂鸣器发声 |
buzzer_off() |
关闭蜂鸣器 | 有源蜂鸣器停止发声 |
beep() |
按指定节奏控制蜂鸣器鸣叫 | 蜂鸣器按照响停节奏间歇提示 |
alarm_beep() |
封装烟雾报警提示音 | 检测到烟雾时发出固定报警声 |
read_gas_do() |
读取烟雾传感器数字输出 | 获取是否达到阈值的触发信号 |
read_gas_adc() |
多次读取 AO 模拟量并取平均 | 串口显示相对稳定的烟雾浓度参考值 |
is_gas_detected() |
根据 DO 电平判断是否检测到烟雾 | 将低电平触发转换成布尔判断结果 |
show_status() |
打印检测状态和 AO 数值 | 串口显示环境安全或检测到烟雾 |
loop() |
循环读取传感器并控制报警 | 环境异常时蜂鸣器报警,正常时保持关闭 |
主循环中的 last_status 用于记录上一次烟雾检测状态。程序只有在状态发生变化时才调用 show_status() 输出状态提醒,避免串口被重复信息刷屏。检测到烟雾时,程序打印报警信息并调用 alarm_beep();未检测到烟雾时,蜂鸣器关闭,并延时 500 毫秒进入下一轮检测。这种写法把"持续采集""状态变化提示"和"异常动作执行"分成了清晰的层次。
扩展应用
烟雾传感器实验涉及数字输入、模拟量采集、I2C 通信和蜂鸣器输出,问题排查需要结合电源、接线、电平触发方式和 ADC 通道逐步确认。DO 和 AO 的作用不同,DO 不触发不代表 AO 没有变化,AO 有变化也不一定代表 DO 已经达到报警阈值。
| 问题现象 | 可能原因 | 处理思路 |
|---|---|---|
| 程序运行后无任何输出 | 脚本未执行、串口未连接、初始化阶段报错 | 检查 CanMV IDE 或串口终端输出,确认 setup() 是否正常执行 |
| 蜂鸣器一直响 | 蜂鸣器触发电平设置相反、DO 一直处于触发状态 | 核对 BUZZER_ACTIVE_LEVEL 和 BUZZER_SILENT_LEVEL,观察 DO 实际电平 |
| 检测到烟雾但蜂鸣器不响 | 蜂鸣器接线错误、蜂鸣器类型不匹配、GPIO 输出电平不符合模块要求 | 单独调用 buzzer_on() 测试蜂鸣器,确认是否为有源蜂鸣器 |
| 环境正常却一直报警 | 烟雾传感器阈值过低、模块电位器调节不合适、DO 触发电平设置错误 | 调节烟雾传感器电位器,并确认 GAS_ACTIVE_LEVEL 是否符合实际模块输出 |
| AO 数值一直不变 | AO 未接入 PCF8591 AIN0、I2C 接线错误、ADC 通道配置错误 | 检查 AO 到 AIN0 的连接,确认 SCL/SDA 是否对应物理引脚 6 和 7 |
| 程序卡在初始化附近 | PCF8591 驱动异常、I2C 设备未连接或地址不匹配 | 检查 pcf8591.py 是否存在,确认 PCF8591 供电和 I2C 接线 |
| AO 数值抖动明显 | 模拟量本身存在波动、采样次数偏少、传感器预热不足 | 适当增大 read_gas_adc(samples=5) 的采样次数,并给传感器预热时间 |
| 按 Ctrl+C 停止后蜂鸣器仍响 | 程序异常退出前没有关闭蜂鸣器 | 当前代码已在 KeyboardInterrupt 中调用 buzzer_off(),其他异常场景可增加更完整的异常保护 |
| DO 不触发但 AO 有明显变化 | 模块阈值设置较高,AO 变化尚未达到 DO 比较器触发点 | 调节模块电位器,或在程序中增加基于 AO 的软件阈值判断 |
烟雾检测实验可以自然扩展到环境安全监测、智能家居告警和设备状态联动等场景。当前代码已经具备输入检测、模拟量读取、阈值判断、状态记录、报警输出和异常退出保护等基础结构。后续增加其他模块时,可以继续保留 DO 判断和 AO 观察的设计思路,把烟雾状态作为系统控制条件,驱动更多硬件或软件动作。
| 应用场景 | 实现思路 | 可扩展能力 |
|---|---|---|
| 烟雾报警器 | 使用 DO 判断是否触发阈值,检测到烟雾时控制蜂鸣器报警 | 可增加报警灯、继电器或远程通知 |
| 环境安全监测 | 使用 AO 数值观察烟雾浓度变化趋势,结合 DO 判断是否异常 | 可扩展数据记录功能,形成简单的环境变化曲线 |
| 智能家居联动 | 将烟雾检测结果作为自动控制条件 | 后续可扩展风扇、排气装置或网络消息推送 |
| 教学演示实验 | 通过 DO 和 AO 对比数字量与模拟量的区别 | 适合讲解传感器输出、电平触发、ADC 采样和阈值判断 |
| 设备自检反馈 | 程序启动后输出初始化状态,并保持蜂鸣器默认关闭 | 可扩展开机自检提示音或状态灯显示 |
| 多传感器安全系统 | 保留当前烟雾检测结构,再接入温湿度、火焰或人体感应模块 | 可扩展为多条件综合判断系统 |
| 按键消音功能 | 检测到报警后允许通过按键临时关闭蜂鸣器 | 可扩展为更接近真实产品的报警交互 |
| LCD 状态显示 | 将 AO 数值、DO 状态和报警状态显示在屏幕上 | 可形成可视化环境监测面板 |
从工程角度看,当前代码已经具备比较清楚的分层结构。硬件连接集中在配置区,初始化逻辑集中在 setup(),蜂鸣器动作由独立函数管理,传感器读取和状态判断也被拆成独立函数。这样的结构方便后续增加 LCD 显示当前 AO 数值、增加按键手动静音、增加 LED 状态提示,或者把报警信息上传到 Web 服务。当前实验虽然只完成烟雾检测和蜂鸣器报警,但程序骨架已经接近一个小型环境监测项目。
总结
本实验通过 CanMV K210 开发板完成了烟雾传感器 AO/DO 双路检测与蜂鸣器报警控制。代码中涉及 FPIOA 引脚映射、GPIO 输入、GPIO 输出、软件 I2C、PCF8591 模拟量读取、低电平触发判断、函数封装、循环检测和异常退出保护等核心能力。DO 信号让程序能够快速判断是否触发报警阈值,AO 信号提供了更细的浓度变化参考,蜂鸣器则把程序判断转化成真实世界中的声音反馈。
这类实验非常适合作为传感器课程的入门案例。数字量输入体现了"是否触发"的判断逻辑,模拟量采集体现了"变化趋势"的观察方式,蜂鸣器控制体现了"检测结果驱动外设动作"的硬件控制思维。后续课程可以在此基础上继续扩展 LCD 显示、LED 状态灯、按键消音、数据记录、无线报警、AI 摄像头识别等方向,让单一传感器实验逐步演变成完整的智能安全监测系统。