1. 项目概述
SHT31是Sensirion公司推出的一款高精度数字温湿度传感器,在工业控制、环境监测、智能家居等领域有着广泛应用。这次我们要在ESP32开发板上实现SHT31的MicroPython驱动开发,让这个不足指甲盖大小的传感器模块发挥出它的全部潜力。
我选择ESP32作为开发平台,主要考虑到它兼具WiFi/蓝牙功能和MicroPython支持,非常适合物联网应用场景。而MicroPython作为Python的精简实现,既保留了Python的易用性,又能满足嵌入式开发的性能要求。通过这个项目,你将掌握从硬件连接到软件调试的完整开发流程。
2. 硬件准备与连接
2.1 所需材料清单
- ESP32开发板(推荐使用ESP32-WROOM-32)
- SHT31传感器模块(注意区分I2C和SPI接口版本)
- 4.7kΩ上拉电阻×2(部分模块已内置)
- 面包板及杜邦线若干
- USB数据线(用于供电和调试)
提示:购买SHT31时务必确认是I2C接口版本,市面上也有SPI接口的变种。我推荐使用Grove接口的SHT31模块,已经内置了必要的上拉电阻,接线更加方便。
2.2 电路连接详解
SHT31与ESP32的标准I2C连接方式如下:
| SHT31引脚 | ESP32引脚 | 说明 |
|---|---|---|
| VCC | 3.3V | 电源正极 |
| GND | GND | 电源地 |
| SDA | GPIO21 | I2C数据线 |
| SCL | GPIO22 | I2C时钟线 |
实际接线时要注意:
- 如果模块没有内置上拉电阻,需要在SDA和SCL线上各接一个4.7kΩ电阻到3.3V
- 电源电压严格使用3.3V,SHT31不兼容5V电平
- 线长尽量控制在20cm以内,过长可能导致通信不稳定
3. MicroPython驱动开发
3.1 I2C总线初始化
首先我们需要初始化ESP32的I2C外设。MicroPython提供了简洁的I2C类:
python复制from machine import I2C, Pin
i2c = I2C(0, scl=Pin(22), sda=Pin(21), freq=100000)
这里有几个关键参数需要注意:
- 第一个参数0表示使用I2C0外设(ESP32有两个I2C控制器)
- freq设置为100kHz是SHT31的标准通信速率
- 如果通信失败,可以尝试降低频率到50kHz
3.2 SHT31寄存器解析
SHT31通过I2C寄存器进行控制,主要寄存器包括:
| 命令 | 代码 | 功能 |
|---|---|---|
| 单次测量高精度模式 | 0x2C06 | 启动单次测量,重复性高 |
| 单次测量中精度模式 | 0x2C0D | 启动单次测量,重复性中 |
| 单次测量低精度模式 | 0x2C10 | 启动单次测量,重复性低 |
| 读取状态寄存器 | 0xF32D | 读取传感器状态 |
| 软复位 | 0x30A2 | 复位传感器 |
在代码中我们可以这样定义这些命令:
python复制class SHT31:
MEASURE_HIGH = const(0x2C06)
MEASURE_MEDIUM = const(0x2C0D)
MEASURE_LOW = const(0x2C10)
READ_STATUS = const(0xF32D)
RESET = const(0x30A2)
3.3 完整驱动实现
下面是一个完整的SHT31驱动类实现:
python复制import utime
class SHT31:
def __init__(self, i2c, addr=0x44):
self.i2c = i2c
self.addr = addr
self.reset()
def reset(self):
self._write_command(self.RESET)
utime.sleep_ms(10)
def measure(self, mode=MEASURE_HIGH):
self._write_command(mode)
utime.sleep_ms(20) # 等待测量完成
data = self.i2c.readfrom(self.addr, 6)
return self._parse_data(data)
def _write_command(self, cmd):
cmd_bytes = bytearray(2)
cmd_bytes[0] = (cmd >> 8) & 0xFF
cmd_bytes[1] = cmd & 0xFF
self.i2c.writeto(self.addr, cmd_bytes)
def _parse_data(self, data):
temp_raw = (data[0] << 8) | data[1]
temp = -45 + 175 * (temp_raw / 65535.0)
humi_raw = (data[3] << 8) | data[4]
humi = 100 * (humi_raw / 65535.0)
return (temp, humi)
这个驱动实现了以下功能:
- 传感器初始化与复位
- 三种精度模式的单次测量
- 原始数据到实际温湿度的转换
- CRC校验(实际项目中建议添加)
4. 实际应用与优化
4.1 基础使用示例
驱动开发完成后,使用起来非常简单:
python复制sensor = SHT31(i2c)
temp, humi = sensor.measure(SHT31.MEASURE_HIGH)
print("温度: %.2f ℃, 湿度: %.2f %%" % (temp, humi))
4.2 性能优化技巧
在实际项目中,我们可以通过以下方式优化驱动:
- 缓存策略:对于不需要实时数据的应用,可以缓存测量结果,减少传感器访问次数
- 错误处理:添加CRC校验和超时处理,提高稳定性
- 低功耗模式:在不测量时让传感器进入休眠
- 滤波算法:对连续测量结果进行滑动平均滤波
优化后的测量代码示例:
python复制def measure_filtered(sensor, count=5):
temps = []
humis = []
for _ in range(count):
t, h = sensor.measure()
temps.append(t)
humis.append(h)
utime.sleep_ms(100)
return (sum(temps)/count, sum(humis)/count)
4.3 物联网应用集成
结合ESP32的WiFi功能,我们可以轻松将传感器数据上传到云平台:
python复制import network
import urequests
def upload_to_cloud(temp, humi):
url = "http://api.example.com/sensor"
data = {
"device_id": "esp32_001",
"temperature": temp,
"humidity": humi
}
response = urequests.post(url, json=data)
return response.json()
5. 常见问题与调试技巧
5.1 I2C通信失败排查
如果传感器没有响应,可以按照以下步骤排查:
- 使用
i2c.scan()检查设备地址- SHT31的默认地址是0x44,有些模块可能是0x45
- 检查电源电压是否稳定
- 确认上拉电阻已正确连接
- 降低I2C频率测试
- 检查线路是否有接触不良
5.2 数据异常处理
当出现以下情况时,建议重置传感器:
- 温度值固定在-45℃或175℃
- 湿度值固定在0%或100%
- 连续多次读取失败
可以在驱动中添加自动恢复机制:
python复制def safe_measure(sensor, retry=3):
for _ in range(retry):
try:
return sensor.measure()
except:
sensor.reset()
utime.sleep_ms(100)
raise RuntimeError("Sensor read failed")
5.3 精度与稳定性提升
- 避免将传感器放置在发热元件附近
- 测量时保持空气流通
- 定期校准(建议每6个月一次)
- 对于高精度应用,建议使用金属外壳屏蔽电磁干扰
6. 进阶功能扩展
6.1 报警功能实现
我们可以扩展驱动,当温湿度超出阈值时触发报警:
python复制class SHT31Alarm(SHT31):
def __init__(self, i2c, temp_range=(10,30), humi_range=(30,70)):
super().__init__(i2c)
self.temp_range = temp_range
self.humi_range = humi_range
def check_alarm(self, temp, humi):
alarms = []
if not self.temp_range[0] <= temp <= self.temp_range[1]:
alarms.append("temperature")
if not self.humi_range[0] <= humi <= self.humi_range[1]:
alarms.append("humidity")
return alarms
6.2 数据记录器
结合ESP32的Flash存储,可以实现简单的数据记录功能:
python复制import ujson
class DataLogger:
def __init__(self, max_records=100):
self.records = []
self.max_records = max_records
def add_record(self, temp, humi):
record = {
"time": utime.time(),
"temp": temp,
"humi": humi
}
self.records.append(record)
if len(self.records) > self.max_records:
self.records.pop(0)
def save_to_file(self, filename="data.json"):
with open(filename, "w") as f:
ujson.dump(self.records, f)
6.3 低功耗优化
对于电池供电的应用,我们需要特别注意功耗优化:
- 延长测量间隔(如每5分钟测量一次)
- 在不测量时关闭传感器电源
- 使用ESP32的深度睡眠模式
- 降低CPU频率
示例代码:
python复制from machine import deepsleep
def run_low_power():
sensor = SHT31(i2c)
temp, humi = sensor.measure()
upload_to_cloud(temp, humi)
# 进入深度睡眠5分钟
deepsleep(5*60*1000)