第13章:温度与湿度传感器
本章深入讲解 DS18B20 单总线温度传感器和 DHT11/XHT11 温湿度传感器的工作原理、通信协议与数据解码。
13.1 DS18B20 温度传感器
13.1.1 传感器介绍
DS18B20 是一款数字温度传感器:
| 参数 | 数值 |
|---|---|
| 测温范围 | -55°C ~ +125°C |
| 精度 | ±0.5°C(-10°C ~ +85°C) |
| 分辨率 | 9-12 位可配置 |
| 通信方式 | 单总线(1-Wire) |
| 供电方式 | 3.0V ~ 5.5V 或寄生供电 |
13.1.2 单总线协议原理
单总线(1-Wire)协议只需要一根数据线(加地线):
主机 ─────┬───── DS18B20
│
[4.7K] 上拉电阻
│
VCC
时序特点:
- 主机通过控制时序发送/接收数据
- 数据位通过高低电平持续时间编码
13.1.3 通信时序
复位脉冲 (Reset Pulse):
主机: ──────────┐ ┌────────────
└───────────┘
480μs+
存在脉冲 (Presence Pulse):
设备: ──────┐ ┌──────────
└─────┘
60-240μs
写"0":
主机: ──────┐ ┌────────
└─────────────────┘
60-120μs
写"1":
主机: ──────┐ ┌───────────────────
└─────┘
1-15μs 45-105μs
读位:
主机: ──────┐ ┌──────────────────────
└──┘
1μs ← 采样点 (15μs内)
13.1.4 ROM 命令
| 命令 | 代码 | 说明 |
|---|---|---|
| 搜索 ROM | 0xF0 | 枚举所有设备 |
| 读 ROM | 0x33 | 读取唯一 64 位 ROM |
| 匹配 ROM | 0x55 | 选择特定设备 |
| 跳过 ROM | 0xCC | 跳过寻址(单设备时) |
13.1.5 功能命令
| 命令 | 代码 | 说明 |
|---|---|---|
| 温度转换 | 0x44 | 启动温度转换 |
| 读暂存器 | 0xBE | 读取 9 字节暂存器 |
13.1.6 数据编码格式
DS18B20 返回 9 字节暂存器数据:
字节 0: 温度 LSB
字节 1: 温度 MSB
字节 2: 高温报警阈值
字节 3: 低温报警阈值
字节 4: 配置寄存器
字节 5-7: 保留
字节 8: CRC 校验
温度数据解码:
# 12 位分辨率时:
# 温度 = (MSB << 8 | LSB) / 16
# 示例: 0x0191 = 401
# 温度 = 401 / 16 = 25.0625°C
# 负温度 (二进制补码):
# 0xFF5E = -162 (有符号)
# 温度 = -162 / 16 = -10.125°C
13.1.7 MicroPython 代码
'''
实验:DS18B20 温度读取
接线:数据线连接 GP3
'''
import machine
import onewire
import ds18x20
import time
# 初始化单总线
ds_pin = machine.Pin(3)
ds_sensor = ds18x20.DS18X20(onewire.OneWire(ds_pin))
# 扫描设备
roms = ds_sensor.scan()
print(f'发现 DS18B20 设备: {roms}')
if not roms:
print("未找到传感器!")
else:
while True:
# 启动温度转换
ds_sensor.convert_temp()
time.sleep_ms(750) # 12位分辨率需要 750ms
# 读取所有传感器
for rom in roms:
temp = ds_sensor.read_temp(rom)
rom_hex = ''.join(f'{b:02x}' for b in rom)
print(f"ROM: {rom_hex} 温度: {temp:.2f}°C")
time.sleep(1)
13.1.8 多传感器系统
'''
实验:多个 DS18B20 并联
'''
import machine
import onewire
import ds18x20
import time
ds_pin = machine.Pin(3)
ds_sensor = ds18x20.DS18X20(onewire.OneWire(ds_pin))
# 传感器 ROM 映射到位置名称
SENSOR_NAMES = {}
def discover_sensors():
"""发现并命名传感器"""
roms = ds_sensor.scan()
print(f"发现 {len(roms)} 个传感器")
for i, rom in enumerate(roms):
rom_hex = ''.join(f'{b:02x}' for b in rom)
name = input(f"传感器 {rom_hex} 的名称: ")
SENSOR_NAMES[rom_hex] = name
return roms
def read_all_temperatures():
"""读取所有传感器温度"""
ds_sensor.convert_temp()
time.sleep_ms(750)
results = {}
for rom in ds_sensor.scan():
rom_hex = ''.join(f'{b:02x}' for b in rom)
name = SENSOR_NAMES.get(rom_hex, rom_hex)
temp = ds_sensor.read_temp(rom)
results[name] = temp
return results
roms = discover_sensors()
while True:
temps = read_all_temperatures()
for name, temp in temps.items():
print(f"{name}: {temp:.2f}°C")
print("---")
time.sleep(2)
13.2 DHT11/XHT11 温湿度传感器
13.2.1 传感器介绍
DHT11/XHT11 是低成本温湿度传感器:
| 参数 | DHT11 | DHT22 |
|---|---|---|
| 湿度范围 | 20-90% RH | 0-100% RH |
| 湿度精度 | ±5% RH | ±2% RH |
| 温度范围 | 0-50°C | -40~80°C |
| 温度精度 | ±2°C | ±0.5°C |
| 采样周期 | ≥1秒 | ≥2秒 |
13.2.2 单总线协议
DHT 使用自定义单总线协议(与 DS18B20 不同):
通信流程:
1. 主机发送起始信号 (拉低 18ms+)
2. 主机释放总线
3. DHT 响应 (拉低 80μs + 拉高 80μs)
4. DHT 发送 40 位数据
数据格式:
[湿度整数][湿度小数][温度整数][温度小数][校验和]
8位 8位 8位 8位 8位
13.2.3 位时序编码
数据 "0":
DHT: ────┐ ┌─────────────
└─────┘
50μs 26-28μs
数据 "1":
DHT: ────┐ ┌───────────────
└─────┘
50μs 70μs
解码方法:测量高电平持续时间
- < 40μs → 数据位 0
-
40μs → 数据位 1
13.2.4 数据校验
# 校验和 = (湿度整数 + 湿度小数 + 温度整数 + 温度小数) & 0xFF
checksum = (data[0] + data[1] + data[2] + data[3]) & 0xFF
if checksum == data[4]:
print("校验通过")
13.2.5 MicroPython 代码
'''
实验:DHT11/XHT11 温湿度读取
接线:数据线连接 GP19
'''
import machine
import dht
import time
# 初始化
pin = machine.Pin(19, machine.Pin.OUT, machine.Pin.PULL_DOWN)
sensor = dht.DHT11(pin)
while True:
try:
sensor.measure()
temp = sensor.temperature()
hum = sensor.humidity()
print(f"温度: {temp}°C 湿度: {hum}%")
except OSError as e:
print(f"读取失败: {e}")
time.sleep(1.5) # DHT11 最小采样间隔 1 秒
13.2.6 带重试机制
'''
实验:DHT 传感器稳定读取
'''
import machine
import dht
import time
sensor = dht.DHT11(machine.Pin(19))
def read_dht_with_retry(max_retries=5):
"""带重试的读取"""
for attempt in range(max_retries):
try:
sensor.measure()
return sensor.temperature(), sensor.humidity()
except OSError:
time.sleep(0.5)
return None, None
while True:
temp, hum = read_dht_with_retry()
if temp is not None:
print(f"温度: {temp}°C 湿度: {hum}%")
else:
print("读取失败")
time.sleep(2)
13.3 NTC 热敏电阻
13.3.1 工作原理
NTC(负温度系数)热敏电阻的阻值随温度升高而降低:
R(T) = R25 × exp[B × (1/T - 1/298.15)]
R25: 25°C 时的标称电阻(通常 10KΩ)
B: B 值常数(通常 3950K)
T: 绝对温度 (开尔文)
13.3.2 电路与计算
'''
实验:NTC 热敏电阻温度测量
'''
from machine import ADC
import math
import time
ntc = ADC(26)
# NTC 参数
R25 = 10000 # 25°C 时电阻
B = 3950 # B 值
R_REF = 10000 # 分压电阻
VCC = 3.3
def read_temperature():
"""读取 NTC 温度"""
adc = ntc.read_u16()
voltage = adc * VCC / 65535
# 计算 NTC 阻值
if voltage <= 0 or voltage >= VCC:
return None
r_ntc = R_REF * voltage / (VCC - voltage)
# 计算温度 (Steinhart-Hart 简化公式)
temp_k = 1 / (1/298.15 + math.log(r_ntc/R25) / B)
temp_c = temp_k - 273.15
return temp_c
while True:
temp = read_temperature()
if temp:
print(f"温度: {temp:.1f}°C")
time.sleep(1)
13.4 LM35 模拟温度传感器
13.4.1 传感器特性
LM35 是精密模拟温度传感器:
- 输出电压与温度成线性关系
- 灵敏度:10mV/°C
- 范围:-55°C ~ +150°C
13.4.2 计算方法
'''
实验:LM35 温度读取
'''
from machine import ADC
import time
lm35 = ADC(26)
def read_lm35():
"""读取 LM35 温度"""
adc = lm35.read_u16()
voltage = adc * 3.3 / 65535
# 温度 = 电压 / 0.01V/°C
temp = voltage / 0.01
return temp
while True:
temp = read_lm35()
print(f"温度: {temp:.1f}°C")
time.sleep(1)
13.5 温湿度数据应用
13.5.1 舒适度指数
'''
实验:温湿度舒适度指数
'''
def comfort_index(temp, humidity):
"""计算舒适度指数"""
# 热指数公式 (Heat Index)
if temp < 27:
return temp # 温度不高时直接返回
hi = (-42.379 +
2.04901523 * temp +
10.14333127 * humidity -
0.22475541 * temp * humidity -
6.83783e-3 * temp**2 -
5.481717e-2 * humidity**2 +
1.22874e-3 * temp**2 * humidity +
8.5282e-4 * temp * humidity**2 -
1.99e-6 * temp**2 * humidity**2)
return hi
def comfort_level(temp, humidity):
"""判断舒适等级"""
hi = comfort_index(temp, humidity)
if hi < 27:
return "舒适"
elif hi < 32:
return "略热"
elif hi < 41:
return "较热,注意补水"
elif hi < 54:
return "酷热,避免剧烈活动"
else:
return "极度危险"
13.5.2 露点温度
import math
def dew_point(temp, humidity):
"""计算露点温度"""
a = 17.27
b = 237.7
alpha = ((a * temp) / (b + temp)) + math.log(humidity/100.0)
dew = (b * alpha) / (a - alpha)
return dew
# 使用
temp = 25
hum = 60
dp = dew_point(temp, hum)
print(f"露点温度: {dp:.1f}°C")
13.5.3 温控系统
'''
实验:简易温控系统
'''
from machine import Pin
import dht
import time
sensor = dht.DHT11(Pin(19))
heater = Pin(0, Pin.OUT) # 加热器继电器
cooler = Pin(1, Pin.OUT) # 制冷器继电器
TARGET_TEMP = 25
HYSTERESIS = 2
def control_temperature():
"""温度控制逻辑"""
try:
sensor.measure()
temp = sensor.temperature()
if temp < TARGET_TEMP - HYSTERESIS:
heater.value(1)
cooler.value(0)
status = "加热中"
elif temp > TARGET_TEMP + HYSTERESIS:
heater.value(0)
cooler.value(1)
status = "制冷中"
else:
heater.value(0)
cooler.value(0)
status = "保持"
print(f"温度: {temp}°C 目标: {TARGET_TEMP}°C 状态: {status}")
except OSError:
print("传感器读取失败")
while True:
control_temperature()
time.sleep(2)
13.6 本章小结
本章介绍了温度与湿度传感器:
- DS18B20:
- 单总线数字温度传感器
- 12 位分辨率,精度 ±0.5°C
- 支持多传感器并联
- DHT11/XHT11:
- 温湿度一体传感器
- 40 位数据格式 + 校验和
- 采样间隔 ≥1 秒
- 数据编码:
- DS18B20:16 位有符号,除以 16 得温度
- DHT11:整数+小数,带校验
- 应用场景:
- 舒适度评估
- 露点计算
- 温控系统
下一章将学习气体检测传感器。