树莓派学习2-读取I2C设备数据

1. 本章学习目标

  • 掌握SHT45温湿度传感器的硬件接线方法

  • 理解SHT45特有的"命令式"通信机制

  • 使用两种不同的Python方式读取SHT45的温度和湿度数据

  • 掌握原始数据的解析和换算方法

2. SHT45传感器简介

SHT45是瑞士Sensirion公司出品的一款高精度数字温湿度传感器,属于SHT4x系列中的旗舰型号。其主要特性如下:

参数 规格
温度精度 ±0.1 °C(0~75 °C)
湿度精度 ±1.0% RH(25~75% RH)
供电电压 1.08 V ~ 3.6 V
I2C地址 0x44
工作温度范围 -40°C ~ 125°C
平均电流 0.4 μA

💡 小知识:SHT45基于"电容式湿度传感+带隙温度传感"的双核心技术,内部集成了信号处理电路,可将物理量直接转换为数字信号通过I2C接口输出-。SHT40、SHT41、SHT45三款传感器的接线方式和代码完全通用,本教程可直接用于SHT40和SHT41。

3. 硬件接线

3.1 引脚连接对照表

将SHT45模块与树莓派进行连接,请按照下表操作:

SHT45模块引脚 树莓派GPIO 物理引脚号
VCC 3.3V 引脚1
GND GND 引脚6
SDA GPIO2 引脚3
SCL GPIO3 引脚5

3.2 接线注意事项

  1. 电源选择 :SHT45的供电电压范围为1.08V至3.6V,请连接至树莓派的3.3V引脚,切勿连接5V引脚,以免损坏传感器。

  2. 线路检查:完成接线后,请再次确认SDA接GPIO2、SCL接GPIO3,GND和VCC不要接反。

  3. 上拉电阻:树莓派的GPIO引脚内部已集成I2C上拉电阻,一般情况下无需外接上拉电阻。

4. 启用树莓派I2C接口

在进行Python编程之前,需要确保树莓派的I2C接口已正确启用,如未启用才考我树莓派学习笔记1。

4.1 安装I2C工具和Python库

重启后,打开终端,安装所需的工具和库:

python 复制代码
# 更新软件包列表
sudo apt update

# 安装I2C工具和Python SMBus库
sudo apt install i2c-tools python3-smbus -y

📖 说明i2c-tools提供了i2cdetecti2cget等命令行调试工具;python3-smbus则提供了Python中操作I2C的底层接口-53

4.2 验证设备识别

安装完成后,执行以下命令扫描I2C总线上的设备:

复制代码
sudo i2cdetect -y 1
复制代码
结果如图:

⚠️ 注意:SHT4x系列的I2C地址出厂时固定为0x44,无法更改-。如果扫描不到该地址,请重新检查接线。

5. 理解SHT45的通信方式

在编写Python代码之前,需要先理解SHT45独特的通信机制。这与传统的寄存器型I2C设备有很大不同。

5.1 SHT45没有寄存器地址

SHT45与传统的I2C设备(如EEPROM、PCF8591等)不同,它没有寄存器地址的概念。SHT45的通信是通过发送"命令码(Command Code)"来完成的,不同的命令码对应不同的操作-。

5.2 常用命令码

SHT45支持以下测量命令码-:

命令码(十六进制) 功能说明 等待时间
0xFD 高精度测量(推荐) 约10 ms
0xF6 中精度测量 约5 ms
0xE0 低精度测量 约2 ms

5.3 I2C通信时序

读取SHT45数据的完整通信流程如下:

复制代码
步骤1: 主机发送START信号
步骤2: 主机发送设备地址0x44 + 写标志位(0x88)
步骤3: 主机发送命令码(如0xFD)
步骤4: 主机发送STOP信号
步骤5: 等待传感器完成测量(10~20毫秒)
步骤6: 主机发送START信号
步骤7: 主机发送设备地址0x44 + 读标志位(0x89)
步骤8: 从机返回6字节数据(温湿度各2字节+CRC各1字节)
步骤9: 主机发送STOP信号

💡 提示 :步骤3发送命令码时,需要注意SHT45没有寄存器地址,因此不能使用write_byte_data()这类带寄存器地址的函数,而应使用write_byte()函数直接发送命令码。

6. 方法一:使用smbus2库手动读取

6.1 安装smbus2库

python 复制代码
sudo apt install python3-smbus2

6.2 完整代码实现

创建Python脚本文件read_sht45.py,输入以下代码:

python 复制代码
#!/usr/bin/env python3
# -*- coding: utf-8 -*-

import smbus2
import time

# 配置常量
I2C_BUS = 1                    # 树莓派I2C总线编号
SHT45_ADDRESS = 0x44           # SHT45的I2C设备地址

# SHT45命令码定义
CMD_MEASURE_HIGH_PRECISION = 0xFD   # 高精度测量
CMD_MEASURE_MEDIUM_PRECISION = 0xF6 # 中精度测量
CMD_MEASURE_LOW_PRECISION = 0xE0    # 低精度测量

def crc8(data):
    """
    计算SHT45使用的CRC-8校验值
    多项式: x^8 + x^5 + x^4 + 1 (0x31)
    初始值: 0xFF
    """
    crc = 0xFF
    for byte in data:
        crc ^= byte
        for _ in range(8):
            if crc & 0x80:
                crc = (crc << 1) ^ 0x31
            else:
                crc <<= 1
        crc &= 0xFF
    return crc

def read_sht45_data(bus, cmd=CMD_MEASURE_HIGH_PRECISION):
    """
    从SHT45读取温湿度数据
    
    参数:
        bus: smbus2总线对象
        cmd: 测量命令码(可选高/中/低精度)
    
    返回:
        (温度, 湿度) 元组,失败时返回(None, None)
    """
    try:
        # 步骤1: 发送测量命令(注意:SHT45没有寄存器地址,直接发送命令码)
        bus.write_byte(SHT45_ADDRESS, cmd)
        
        # 步骤2: 等待传感器完成测量
        # 根据不同的命令码等待不同时间
        if cmd == CMD_MEASURE_HIGH_PRECISION:
            time.sleep(0.015)      # 高精度约15ms
        elif cmd == CMD_MEASURE_MEDIUM_PRECISION:
            time.sleep(0.007)      # 中精度约7ms
        else:
            time.sleep(0.003)      # 低精度约3ms
        
        # 步骤3: 读取6字节数据(温度2字节+CRC1字节+湿度2字节+CRC1字节)
        # 注意:SHT45没有寄存器地址,所以第二个参数使用0x00作为占位符
        data = bus.read_i2c_block_data(SHT45_ADDRESS, 0x00, 6)
        
        # 数据格式: data[0]=温度高8位, data[1]=温度低8位, data[2]=温度CRC
        #          data[3]=湿度高8位, data[4]=湿度低8位, data[5]=湿度CRC
        
        # 验证温度数据的CRC校验
        temp_crc = crc8(data[0:2])
        if temp_crc != data[2]:
            print("温度数据CRC校验失败")
            return None, None
        
        # 验证湿度数据的CRC校验
        hum_crc = crc8(data[3:5])
        if hum_crc != data[5]:
            print("湿度数据CRC校验失败")
            return None, None
        
        # 解析原始数值
        temp_raw = (data[0] << 8) | data[1]
        humidity_raw = (data[3] << 8) | data[4]
        
        # 转换为实际物理值
        # 温度转换公式: T = -45 + 175 * (temp_raw / 65535)
        temperature = -45.0 + 175.0 * (temp_raw / 65535.0)
        # 湿度转换公式: RH = -6 + 125 * (humidity_raw / 65535)
        humidity = -6.0 + 125.0 * (humidity_raw / 65535.0)
        
        # 湿度限制在0~100%范围内
        humidity = max(0.0, min(100.0, humidity))
        
        return temperature, humidity
        
    except Exception as e:
        print(f"读取数据失败: {e}")
        return None, None

def main():
    # 初始化I2C总线
    bus = smbus2.SMBus(I2C_BUS)
    print(f"SHT45温湿度传感器已初始化(地址: 0x{SHT45_ADDRESS:02X})")
    print("开始读取数据...")
    print("-" * 40)
    
    try:
        while True:
            # 读取温湿度数据
            temp, hum = read_sht45_data(bus, CMD_MEASURE_HIGH_PRECISION)
            
            if temp is not None and hum is not None:
                print(f"温度: {temp:.2f} °C | 相对湿度: {hum:.1f} %RH")
            else:
                print("读取数据失败,请检查传感器连接")
            
            # 每隔1秒读取一次
            time.sleep(1)
            
    except KeyboardInterrupt:
        print("\n程序已停止")
    finally:
        bus.close()

if __name__ == "__main__":
    main()

6.3 代码逐行解析

导入模块
python 复制代码
import smbus2   # 提供I2C通信功能
import time     # 提供延时功能
定义常量
python 复制代码
I2C_BUS = 1                    # 树莓派I2C总线编号
SHT45_ADDRESS = 0x44           # SHT45的I2C设备地址
CMD_MEASURE_HIGH_PRECISION = 0xFD   # 高精度测量命令

💡 注意:SHT45的命令码是直接通过I2C总线发送的,不需要像传统I2C设备那样指定寄存器地址-。

🔒 CRC-8校验函数
python 复制代码
def crc8(data):
    crc = 0xFF
    for byte in data:
        crc ^= byte
        for _ in range(8):
            if crc & 0x80:
                crc = (crc << 1) ^ 0x31
            else:
                crc <<= 1
        crc &= 0xFF
    return crc

SHT45返回的6字节数据中包含两个CRC校验字节,分别校验温度数据和湿度数据的完整性。CRC多项式为x^8 + x^5 + x^4 + 1(0x31),初始值为0xFF。在数据解析时验证CRC可以确保通信过程中没有发生错误。

📖 数据读取与解析
python 复制代码
# 步骤1: 发送测量命令
bus.write_byte(SHT45_ADDRESS, cmd)

# 步骤2: 等待测量完成
time.sleep(0.015)

# 步骤3: 读取6字节数据
data = bus.read_i2c_block_data(SHT45_ADDRESS, 0x00, 6)

⚠️ 关键点read_i2c_block_data()的第二个参数是寄存器地址,由于SHT45没有寄存器,我们使用0x00作为占位符。实际通信时,该函数会先发送设备地址+写标志(准备读取),然后发送一个寄存器地址字节(0x00),最后再重新发送设备地址+读标志来读取数据。对于SHT45,中间的0x00字节会被传感器忽略。

📐 数据转换公式
python 复制代码
# 温度转换: T = -45 + 175 * (原始值 / 65535)
temperature = -45.0 + 175.0 * (temp_raw / 65535.0)

# 湿度转换: RH = -6 + 125 * (原始值 / 65535)
humidity = -6.0 + 125.0 * (humidity_raw / 65535.0)

这是SHT4x系列传感器的标准转换公式,其中65535是16位无符号整数的最大值。

6.4 运行代码

在终端中执行:

python 复制代码
python3 read_sht45.py

预期输出示例:

复制代码
SHT45温湿度传感器已初始化(地址: 0x44)
开始读取数据...
----------------------------------------
温度: 24.56 °C | 相对湿度: 45.2 %RH
温度: 24.58 °C | 相对湿度: 45.3 %RH
温度: 24.55 °C | 相对湿度: 45.1 %RH
...

Ctrl + C可停止程序。

7. 方法二:使用Adafruit CircuitPython库(推荐)

如果你希望代码更简洁,可以使用Adafruit官方提供的CircuitPython库,它将底层细节封装得更加完善。

7.1 安装Adafruit库

python 复制代码
# 安装Adafruit Blinka(CircuitPython的Linux兼容层)
sudo pip3 install adafruit-blinka

# 安装SHT4x传感器库
sudo pip3 install adafruit-circuitpython-sht4x

7.2 简洁版代码

创建脚本read_sht45_simple.py

python 复制代码
#!/usr/bin/env python3
# -*- coding: utf-8 -*-

import time
import board
import adafruit_sht4x

# 初始化I2C总线
i2c = board.I2C()  # 自动使用board.SCL和board.SDA

# 初始化SHT45传感器
sht = adafruit_sht4x.SHT4x(i2c)
print("SHT45传感器已初始化")

# 可选:设置测量模式
# sht.mode = adafruit_sht4x.Mode.NO_HEAT_HIGH   # 高精度模式(默认)

while True:
    # 读取温湿度数据
    temperature, relative_humidity = sht.measurements
    
    print(f"温度: {temperature:.2f} °C")
    print(f"相对湿度: {relative_humidity:.1f} %RH")
    print("")
    
    time.sleep(1)

7.3 两种方法的对比

特性 smbus2手动实现 Adafruit库
代码复杂度 较高(需手动处理CRC和转换) 极低(仅3行核心代码)
可控性 完全可控,适合学习底层原理 底层细节已封装
CRC校验 需要手动实现 库内部已处理
适用场景 教学演示、深度定制 项目开发、快速原型

💡 建议:如果你是初学者或希望快速完成项目开发,推荐使用方法二(Adafruit库);如果你是希望深入学习I2C通信原理,建议仔细研究第一种方法的实现细节。

8. 故障排查指南

8.1 扫描不到0x44地址

故障现象 可能原因 解决方法
i2cdetect输出全是-- I2C未启用 执行sudo raspi-config重新启用I2C
扫描不到0x44 接线错误 检查SDA/SCL是否接反或接触不良
扫描不到0x44 供电问题 确认SHT45连接的是3.3V引脚,不是5V
扫描到其他地址 地址冲突 SHT45地址固定为0x44,若有冲突需更换设备

8.2 读取数据时返回错误

错误信息 可能原因 解决方法
[Errno 121] Remote I/O error 通信超时 检查接线,增加等待时间
CRC校验失败 数据传输错误 检查是否有电磁干扰,缩短连线长度
温度值异常(如-45°C) 未正确发送测量命令 确认使用的是write_byte()而非write_byte_data()
湿度值为负数 数据解析错误 检查转换公式中的除数和乘数是否正确

8.3 数据读取不稳定的解决方法

  • 增加等待时间 :将time.sleep(0.015)适当增加至20~30毫秒

  • 检查电源稳定性:使用树莓派自带的3.3V供电,避免与其他大功率设备共用

  • 降低I2C速率 :某些SHT45模块对时钟频率敏感,可在/boot/config.txt中添加dtparam=i2c_arm_baudrate=100000将速率降至100kHz

9. 本章小结

本章围绕SHT45温湿度传感器,详细介绍了树莓派通过I2C接口读取温湿度数据的完整流程。主要知识点包括:

  1. SHT45硬件接线:VCC→3.3V、GND→GND、SDA→GPIO2、SCL→GPIO3

  2. I2C接口启用 :通过raspi-config配置和i2c-tools验证

  3. SHT45通信机制:命令码式通信(无寄存器地址),先发命令再读数据

  4. Python编程方法

    • 底层手动实现:使用smbus2库,掌握CRC校验和数据转换

    • 高层库实现:使用Adafruit CircuitPython库,快速上手

  5. 数据转换公式:温度T = -45 + 175×(原始值/65535),湿度RH = -6 + 125×(原始值/65535)

如欲继续学习,可参考本书后续章节关于将温湿度数据写入数据库、搭建Web监控界面或结合机器学习进行环境预测等内容。

相关推荐
2301_805962932 小时前
树莓派学习1-I2C配置与设备状态检测
嵌入式硬件·学习
摇滚侠11 小时前
如何选择 nodejs 版本,nodejs 版本号详解
学习
醇氧11 小时前
【学习】IP地址:数字世界的“门牌号”怎么读?
网络协议·学习·tcp/ip
talen_hx29612 小时前
《零基础入门Spark》学习笔记 Day 11
笔记·学习·spark
ZhiqianXia13 小时前
gem5 模拟器学习笔记(1):核心术语整理
笔记·学习
GHL28427109014 小时前
MCP学习
学习·ai
凌波粒14 小时前
D2L学习笔记:安装、张量与数据处理
笔记·python·学习·pandas
chools15 小时前
Java后端拥抱AI开发之个人学习路线 - - Spring AI【第一期】
java·人工智能·学习·spring·ai