4、传感器驱动开发:称重传感器(HX711)驱动原理与代码实现、红外传感器(避障/人体感应)驱动、霍尔传感器(药盒开盖检测)驱动、温湿度传感器(DHT22)驱动
好,咱们进入实战课程最硬核的一章——传感器驱动开发。
药盒安全用药系统,说白了就是靠这些传感器来感知世界的。称重传感器告诉你药吃没吃,红外传感器告诉你人来了没,霍尔传感器告诉你盖子关没关,温湿度传感器告诉你环境适不适合存药。每个传感器都有自己的脾气,摸透了,系统就稳了。
4.1 称重传感器(HX711)驱动原理与代码实现
称重传感器,核心是应变片。受力后电阻变化,但变化极其微弱,微伏级别。这时候就需要HX711这个24位高精度ADC芯片来放大和转换。
驱动原理
HX711和单片机通信只需要两根线:DOUT(数据输出)和SCK(时钟)。时序上,我习惯这么理解:
- DOUT拉低,表示数据准备好了。
- SCK每给一个脉冲,就读一位数据,从高位到低位,共24位。
- 最后再给25-27个脉冲,用来选择增益和通道。
嗯,这里要注意:HX711内部有个稳定时间,上电后要等几百毫秒再读数,否则数据会飘。我在项目中遇到过,刚上电就读,结果重量显示忽大忽小,排查了半天才发现是初始化时序问题。
代码实现(MicroPython)
from machine import Pin, Timer
import time
class HX711:
def __init__(self, dout_pin, sck_pin):
self.dout = Pin(dout_pin, Pin.IN)
self.sck = Pin(sck_pin, Pin.OUT)
self.sck.value(0)
self.offset = 0
self.scale = 1
def read_raw(self):
# 等待DOUT拉低
while self.dout.value() == 1:
pass
data = 0
for i in range(24):
self.sck.value(1)
time.sleep_us(1)
data = data << 1
if self.dout.value():
data |= 1
self.sck.value(0)
time.sleep_us(1)
# 第25个脉冲,设置通道A增益128
self.sck.value(1)
time.sleep_us(1)
self.sck.value(0)
# 处理符号位(24位有符号数)
if data & 0x800000:
data |= ~0xFFFFFF
return data
def tare(self, times=10):
# 去皮操作
total = 0
for i in range(times):
total += self.read_raw()
time.sleep_ms(10)
self.offset = total // times
def get_weight(self):
raw = self.read_raw()
return (raw - self.offset) / self.scale
def set_scale(self, scale):
self.scale = scale
4.2 红外传感器(避障/人体感应)驱动
红外传感器分两种:避障型和人体感应型。别看都带“红外”俩字,原理完全不同。
避障型红外传感器
说白了就是发射管一直发红外光,遇到障碍物反射回来,接收管收到信号,输出低电平。没障碍物时输出高电平。就这么简单。
驱动代码更简单,就是个数字输入读取:
from machine import Pin
ir_sensor = Pin(12, Pin.IN) # 假设接在GPIO12
def is_obstacle():
return ir_sensor.value() == 0 # 低电平表示有障碍物
你想想看,这种传感器用在药盒上干嘛?我一般用它检测药盒是否被拿起,或者检测抽屉是否打开。但要注意,它检测距离有限,一般10-30cm,太远就不灵了。
人体感应型(PIR)传感器
这个就高级一点。它检测的是人体发出的红外辐射变化。人一走动,热释电元件感应到变化,输出高电平。
驱动代码:
pir_sensor = Pin(13, Pin.IN) # 假设接在GPIO13
def detect_human():
return pir_sensor.value() == 1 # 高电平表示有人
4.3 霍尔传感器(药盒开盖检测)驱动
霍尔传感器,检测磁场用的。药盒盖子上贴个小磁铁,盖子合上时霍尔传感器靠近磁铁,输出低电平;盖子打开,磁铁远离,输出高电平。
为什么用霍尔而不用干簧管?我个人习惯,霍尔传感器寿命更长,没有机械触点,不会氧化。药盒每天开合几十次,用干簧管一年后可能就失灵了。
驱动代码:
hall_sensor = Pin(14, Pin.IN, Pin.PULL_UP) # 内部上拉
def is_lid_closed():
return hall_sensor.value() == 0 # 低电平表示盖子闭合
def is_lid_opened():
return hall_sensor.value() == 1 # 高电平表示盖子打开
4.4 温湿度传感器(DHT22)驱动
DHT22,精度比DHT11高不少。温度精度±0.5°C,湿度精度±2%RH。药盒里放这个,主要是监测环境是否适合存药。有些药怕潮,湿度超过60%就得报警。
通信协议
DHT22用的是单总线协议,一根线既当数据线又当时钟线。时序比较讲究:
- 主机拉低总线至少18ms,然后拉高20-40us,等待DHT22响应。
- DHT22拉低80us,再拉高80us,表示准备好发送数据。
- 然后连续发送40位数据:16位湿度、16位温度、8位校验和。
- 每位数据:拉低50us,然后拉高26-28us表示0,拉高70us表示1。
嗯,这里要注意时序的微秒级控制。MicroPython的time.sleep_us()精度有限,我建议用机器定时器或者直接循环等待。
代码实现
import machine
import time
class DHT22:
def __init__(self, pin):
self.pin = machine.Pin(pin, machine.Pin.OUT)
self.pin.value(1)
def read(self):
# 发送开始信号
self.pin.value(0)
time.sleep_ms(20)
self.pin.value(1)
time.sleep_us(30)
# 切换为输入
self.pin = machine.Pin(self.pin, machine.Pin.IN)
# 等待响应
count = 0
while self.pin.value() == 1 and count < 100:
count += 1
time.sleep_us(1)
while self.pin.value() == 0 and count < 100:
count += 1
time.sleep_us(1)
# 读取40位数据
data = []
for i in range(40):
while self.pin.value() == 0:
pass
time.sleep_us(50)
if self.pin.value() == 1:
data.append(1)
else:
data.append(0)
while self.pin.value() == 1:
pass
# 解析数据
humidity = 0
for i in range(16):
humidity = (humidity << 1) | data[i]
temperature = 0
for i in range(16, 32):
temperature = (temperature << 1) | data[i]
checksum = 0
for i in range(32, 40):
checksum = (checksum << 1) | data[i]
# 校验
if (humidity + temperature) & 0xFF != checksum:
return None, None # 校验失败
# 转换实际值
humidity = humidity / 10.0
if temperature & 0x8000:
temperature = -(temperature & 0x7FFF) / 10.0
else:
temperature = temperature / 10.0
return temperature, humidity
4.5 传感器驱动整合思路
四个传感器都讲完了,怎么整合到药盒系统里?我建议这样:
| 传感器 | 检测内容 | 触发动作 |
|---|---|---|
| 称重传感器 | 药片是否取出 | 记录用药时间、剂量 |
| 红外避障传感器 | 药盒是否被拿起 | 唤醒系统、点亮屏幕 |
| 霍尔传感器 | 药盒盖子状态 | 检测是否异常开盖 |
| 温湿度传感器 | 环境温湿度 | 超限报警、记录日志 |
每个传感器驱动写成一个类,主程序里统一调度。用定时器轮询,比如每100ms读一次霍尔和红外,每2秒读一次温湿度,称重则在检测到开盖时才读取,省电。
好了,这一章内容不少。四个传感器,每个都有自己的脾气。你写代码的时候,多想想实际场景,多留点调试接口。传感器数据用串口打印出来看看,确认没问题再集成到主程序里。下一章咱们讲数据融合和异常报警逻辑,到时候这些传感器就要协同作战了。