MQ2烟雾传感器是一种常见的气体传感器,其原理基于气敏材料表面吸附气体分子后电学性能的变化
一、传感器结构
MQ2主要由两部分组成:加热丝和敏感材料层。
- 加热丝的作用是为敏感材料提供合适的工作温度。通常,加热丝将传感器内部的敏感材料加热到200 - 300°C左右的工作温度。这是因为在这个温度范围内,敏感材料对烟雾等气体的吸附和反应活性较好。
- 敏感材料通常是金属氧化物半导体材料。
二、工作原理
- 吸附过程
- 当烟雾或可燃气体分子(如一氧化碳、甲烷等)存在于传感器周围环境中时,这些气体分子会被吸附到敏感材料(SnO₂)的表面。
- 在加热的条件下,吸附过程会更加迅速。由于SnO₂表面存在氧空位,氧气分子(O₂)会首先吸附在表面,并从半导体材料中捕获电子,形成化学吸附氧离子(O⁻、O²⁻等)。这个过程会使n型半导体材料(如SnO₂)的表面电子浓度降低,电阻升高。
- 反应过程
- 当烟雾或可燃气体分子吸附到表面时,它们会与化学吸附氧离子发生反应。例如,一氧化碳(CO)与O⁻反应:CO+O⁻→CO₂ + e⁻。
- 这个反应会释放出电子,导致半导体材料表面的电子浓度增加,从而使传感器的电阻降低。烟雾或可燃气体浓度越高,反应越剧烈,释放的电子越多,电阻下降得越明显。
- 检测电路
- MQ2传感器通常连接到一个简单的分压电路中。传感器的电阻变化会引起分压电路输出电压的变化。
- 当Rp(传感器电阻)由于气体吸附和反应而发生变化时,Dout也会相应地改变。通过检测Vout的变化,就可以判断周围环境中烟雾或可燃气体的浓度。
Pico使用MQ烟雾传感器代码
import machine
import time
MQ2_analog = machine.ADC(28) #烟雾传感器的Do引脚
MQ2_digital = machine.Pin(27, machine.Pin.IN) #烟雾传感器的Ao引脚
while True:
smoke_value = MQ2_analog.read_u16() #读取烟雾的值
print(smoke_value)
time.sleep(0.5)
smoke_status = MQ2_digital.value() #烟雾传感器状态,烟雾是否超过阈值
if smoke_status == 1:
print("检测到烟雾")
else:
print("未检测到烟雾")
time.sleep(0.5)
TM1637 数码管模块代码
from micropython import const
from machine import Pin
from time import sleep_us, sleep_ms
# 定义TM1637芯片的命令常量
TM1637_CMD1 = const(64) # 0x40数据命令,用于设置数据传输模式等操作
TM1637_CMD2 = const(192) # 0xC0地址命令,用于设置显示数据的地址
TM1637_CMD3 = const(128) # 0x80显示控制命令,用于控制显示的开关、亮度等
TM1637_DSP_ON = const(8) # 0x08显示开启标志,用于在显示控制命令中设置显示开启
TM1637_DELAY = const(10) # 10us延迟,用于在时钟(clk)和数据(dio)脉冲之间的延迟
TM1637_MSB = const(128) # 最高有效位,对于数码管来说可能是小数点或者冒号,取决于具体的显示模块
# 定义0 - 9、a - z、空白、短横线、星号等字符对应的七段数码管显示段码
_SEGMENTS = bytearray(b'\x3F\x06\x5B\x4F\x66\x6D\x7D\x07\x7F\x6F\x77\x7C\x39\x5E\x79\x71\x3D\x76\x06\x1E\x76\x38\x55\x54\x3F\x73\x67\x50\x6D\x78\x3E\x1C\x2A\x76\x6E\x5B\x00\x40\x63')
class TM1637(object):
"""
用于基于TM1637 LED驱动的四位七段LED模块的库
"""
def __init__(self, clk, dio, brightness=7):
# 初始化时钟和数据引脚
self.clk = clk
self.dio = dio
# 检查亮度值是否在有效范围内(0 - 7)
if not 0 <= brightness <= 7:
raise ValueError("Brightness out of range")
self._brightness = brightness
# 将时钟和数据引脚初始化为输出模式,初始值为0
self.clk.init(Pin.OUT, value=0)
self.dio.init(Pin.OUT, value=0)
sleep_us(TM1637_DELAY)
# 写入数据命令和显示控制命令
self._write_data_cmd()
self._write_dsp_ctrl()
def _start(self):
# 开始数据传输的信号
self.dio(0)
sleep_us(TM1637_DELAY)
self.clk(0)
sleep_us(TM1637_DELAY)
def _stop(self):
# 停止数据传输的信号
self.dio(0)
sleep_us(TM1637_DELAY)
self.clk(1)
sleep_us(TM1637_DELAY)
self.dio(1)
def _write_data_cmd(self):
# 写入数据命令,设置为自动地址递增、正常模式
self._start()
self._write_byte(TM1637_CMD1)
self._stop()
def _write_dsp_ctrl(self):
# 写入显示控制命令,设置显示开启并设置亮度
self._start()
self._write_byte(TM1637_CMD3 | TM1637_DSP_ON | self._brightness)
self._stop()
def _write_byte(self, b):
# 向TM1637写入一个字节的数据
for i in range(8):
self.dio((b >> i) & 1)
sleep_us(TM1637_DELAY)
self.clk(1)
sleep_us(TM1637_DELAY)
self.clk(0)
sleep_us(TM1637_DELAY)
self.clk(0)
sleep_us(TM1637_DELAY)
self.clk(1)
sleep_us(TM1637_DELAY)
self.clk(0)
sleep_us(TM1637_DELAY)
def brightness(self, val=None):
"""
设置显示亮度(0 - 7)
如果没有传入参数,则返回当前亮度值
如果传入的亮度值不在有效范围内则抛出异常
"""
if val is None:
return self._brightness
if not 0 <= val <= 7:
raise ValueError("Brightness out of range")
self._brightness = val
self._write_data_cmd()
self._write_dsp_ctrl()
def write(self, segments, pos=0):
"""
在给定位置开始显示最多6个段码
第二个段码的最高位控制第二个和第三个段码之间的冒号(如果有)
如果位置不在0 - 5范围内则抛出异常
"""
if not 0 <= pos <= 5:
raise ValueError("Position out of range")
self._write_data_cmd()
self._start()
self._write_byte(TM1637_CMD2 | pos)
for seg in segments:
self._write_byte(seg)
self._stop()
self._write_dsp_ctrl()
def encode_digit(self, digit):
"""
将0 - 9、a - f的字符转换为对应的段码
"""
return _SEGMENTS[digit & 0x0f]
def encode_string(self, string):
"""
将最多4个字符长度的包含0 - 9、a - z、空格、短横线、星号的字符串转换为段码数组
段码数组的长度与源字符串长度相同
"""
segments = bytearray(len(string))
for i in range(len(string)):
segments[i] = self.encode_char(string[i])
return segments
def encode_char(self, char):
"""
将0 - 9、a - z、空格、短横线、星号的字符转换为对应的段码
如果字符不在有效范围内则抛出异常
"""
o = ord(char)
if o == 32:
return _SEGMENTS[36] # 空格
if o == 42:
return _SEGMENTS[38] # 星号/度数符号
if o == 45:
return _SEGMENTS[37] # 短横线
if o >= 65 and o <= 90:
return _SEGMENTS[o - 55] # 大写A - Z
if o >= 97 and o <= 122:
return _SEGMENTS[o - 87] # 小写a - z
if o >= 48 and o <= 57:
return _SEGMENTS[o - 48] # 0 - 9
raise ValueError("Character out of range: {:d} '{:s}'".format(o, chr(o)))
def hex(self, val):
"""
以十六进制格式显示0x0000 - 0xffff的值,右对齐
"""
string = '{:04x}'.format(val & 0xffff)
self.write(self.encode_string(string))
def number(self, num):
"""
显示 - 999 - 9999之间的数值,右对齐
对输入的数值进行范围限制
"""
num = max(-999, min(num, 9999))
string = '{0: >4d}'.format(num)
self.write(self.encode_string(string))
def numbers(self, num1, num2, colon=True):
"""
显示两个 - 9 - 99之间的数值,有前导零并且用冒号隔开(可设置)
对输入的数值进行范围限制
"""
num1 = max(-9, min(num1, 99))
num2 = max(-9, min(num2, 99))
segments = self.encode_string('{0:0>2d}{1:0>2d}'.format(num1, num2))
if colon:
segments[1] |= 0x80 # 开启冒号显示
self.write(segments)
def temperature(self, num):
if num < -9:
self.show('lo') # 显示'lo'表示低温
elif num > 99:
self.show('hi') # 显示'hi'表示高温
else:
string = '{0: >2d}'.format(num)
self.write(self.encode_string(string))
self.write([_SEGMENTS[38], _SEGMENTS[12]], 2) # 显示度数符号(摄氏度)
def show(self, string, colon=False):
segments = self.encode_string(string)
if len(segments) > 1 and colon:
segments[1] |= 128
self.write(segments[:4])
def scroll(self, string, delay=250):
segments = string if isinstance(string, list) else self.encode_string(string)
data = [0] * 8
data[4:0] = list(segments)
for i in range(len(segments) + 5):
self.write(data[0 + i:4 + i])
sleep_ms(delay)
class TM1637Decimal(TM1637):
"""
用于基于TM1637 LED驱动的四位七段LED模块的库,
这个类主要用于十进制显示模块(每个七段LED后面有小数点的模块)
"""
def encode_string(self, string):
"""
将字符串转换为LED段码
将最多4个字符长度的包含0 - 9、a - z、空格、短横线、星号和'.'的字符串转换为段码数组
段码数组的长度与去除'.'后的源字符串长度相同
"""
segments = bytearray(len(string.replace('.', '')))
j = 0
for i in range(len(string)):
if string[i] == '.' and j > 0:
segments[j - 1] |= TM1637_MSB
continue
segments[j] = self.encode_char(string[i])
j += 1
return segments
上面代码写入tm1637.py
import tm1637
from machine import Pin
import time
# 创建TM1637对象并初始化引脚连接
tm = tm1637.TM1637(clk=Pin(0), dio=Pin(1))
# 设置数码管亮度
tm.brightness(5)
# 调用tm对象的brightness方法,将数码管的亮度设置为5。范围0-7
tm.numbers(10, 24, colon=True)
# 调用tm对象的numbers方法,在数码管上显示两个数字,这里是10和24,并且通过colon=True设置在两个数字中间显示冒号。
# 这个方法内部会对输入的数字进行范围限制(-9到99之间),然后将数字转换为对应的数码管段码并显示。
time.sleep(2)
# 在数码管上显示 "AbCd"
tm.show("AbCd", colon=False)
# 调用tm对象的show方法,将字符串 "AbCd" 转换为数码管段码并显示。
# 由于colon=False,即使字符串中没有特殊的冒号逻辑,这里也明确表示不显示冒号。
# 这个方法会将字符串中的每个字符转换为对应的数码管段码,对于不在0 - 9、a - z、空格、短横线、星号范围内的字符会抛出异常。
time.sleep(2)
# 在数码管上显示 "COOL"(通过自定义段码)
tm.write([0b00111001, 0b00111111, 0b00111111, 0b00111000])
# 调用tm对象的write方法,传入一个包含四个字节的列表,每个字节是一个自定义的数码管段码。
# 这些段码直接对应数码管上要显示的内容,这里显示的是 "COOL"。
# 这个方法会将段码写入数码管的相应位置进行显示。
time.sleep(2)
# 清除数码管显示(显示四个空格)
tm.show(" ")
# 调用show方法,传入四个空格组成的字符串。
# 这个方法会将空格转换为对应的数码管段码(空白显示),从而实现清除之前显示内容的效果。
time.sleep(2)
# 在数码管上显示十六进制值 "bEEF"
tm.hex(0xbeef)
# 调用tm对象的hex方法,将十六进制数0xbeef转换为对应的字符串,即 "beef"。
# 然后将这个字符串中的每个字符转换为数码管段码并显示在数码管上。
time.sleep(2)
# 在数码管上显示负数 "-123"
tm.number(-123)
# 调用tm对象的number方法,将 - 123转换为对应的字符串,即 " - 123"。
# 然后将字符串中的每个字符转换为数码管段码并显示,这个方法会对输入的数字进行范围限制(-999到9999之间)。
time.sleep(2)
# 在数码管上显示温度值 "24°C"
tm.temperature(24)
# 调用tm对象的temperature方法,因为24在 - 9到99的范围内,所以会将24转换为对应的字符串。
# 然后将字符串中的每个字符转换为数码管段码并显示,同时还会在后面显示度数符号(通过特定的段码)。
# 如果传入的温度值小于 -9,会显示 "lo";如果大于99,会显示 "hi"。
time.sleep(2)
# 滚动显示字符串 "RPI - ICU"
tm.scroll('RPI-ICU', delay=250)
# 调用tm对象的scroll方法,将字符串 "RPI - ICU" 转换为数码管段码。
# 然后按照一定的延迟(这里是250ms),逐步滚动显示这个字符串的内容在数码管上。