1. Modbus RTU 温湿度传感器数据采集
任务目标
通过RS485接口,使用Modbus RTU协议读取温湿度传感器的保持寄存器值,并将原始寄存器数据解析为实际的温度和湿度工程单位。本案例以标准Modbus RTU从站设备为对象,实现主站读取功能。
环境与依赖
- 硬件:RS485转USB适配器、支持Modbus RTU协议的温湿度传感器(从站地址1,波特率9600,8N1)
- 软件:Python 3.8+,
pyserial库(pip install pyserial) - 寄存器约定:温度保持寄存器地址0x0000(有符号16位,单位0.1℃),湿度保持寄存器地址0x0001(无符号16位,单位0.1%RH)
关键完整代码
import serial
import struct
import time
# Modbus RTU CRC16 计算
def crc16_modbus(data: bytes) -> int:
crc = 0xFFFF
for byte in data:
crc ^= byte
for _ in range(8):
if crc & 0x0001:
crc = (crc >> 1) ^ 0xA001
else:
crc >>= 1
return crc
# 构建读保持寄存器请求帧(功能码0x03)
def build_read_request(slave_id: int, start_addr: int, quantity: int) -> bytes:
frame = struct.pack('>BHH', slave_id, 0x03, start_addr, quantity)
crc = crc16_modbus(frame)
return frame + struct.pack('<H', crc)
# 解析响应帧,返回寄存器值列表
def parse_read_response(response: bytes, expected_quantity: int) -> list:
if len(response) < 5 + expected_quantity * 2:
raise ValueError('响应帧长度不足')
# 校验CRC
received_crc = struct.unpack('<H', response[-2:])[0]
calc_crc = crc16_modbus(response[:-2])
if received_crc != calc_crc:
raise ValueError('CRC校验失败')
byte_count = response[2]
if byte_count != expected_quantity * 2:
raise ValueError('字节数不匹配')
values = []
for i in range(expected_quantity):
values.append(struct.unpack('>H', response[3 + i*2 : 5 + i*2])[0])
return values
# 主读取函数:返回(温度℃, 湿度%RH)
def read_temp_humidity(port: str, slave_id: int = 1) -> tuple:
ser = serial.Serial(port, baudrate=9600, bytesize=8, parity='N', stopbits=1, timeout=1)
try:
# 从地址0x0000连续读2个寄存器
request = build_read_request(slave_id, 0x0000, 2)
ser.write(request)
time.sleep(0.05) # 等待从站响应
response = ser.read(7 + 2) # 从站地址+功能码+字节数+4数据字节+2CRC
if len(response) == 0:
raise ValueError('无响应')
if response[1] & 0x80:
raise ValueError(f'从站返回异常码: 0x{response[2]:02X}')
regs = parse_read_response(response, 2)
# 温度:有符号16位,单位0.1℃
temp_raw = struct.unpack('>h', struct.pack('>H', regs[0]))[0]
temperature = temp_raw / 10.0
# 湿度:无符号16位,单位0.1%RH
humidity = regs[1] / 10.0
return temperature, humidity
finally:
ser.close()
if __name__ == '__main__':
# 修改为实际串口设备名(Windows: COM3,Linux: /dev/ttyUSB0)
temp, humi = read_temp_humidity('/dev/ttyUSB0', slave_id=1)
print(f'温度: {temp:.1f} ℃')
print(f'湿度: {humi:.1f} %RH')
运行与验证
- 将RS485适配器连接传感器,确认设备地址为1、波特率9600、数据格式8N1。
- 安装依赖:
pip install pyserial - 修改代码末尾的串口设备名为实际端口,运行:
python modbus_temp_humi.py - 通过条件:程序正常输出温度和湿度数值,且数值与传感器现场显示值一致(误差在传感器精度范围内)。若输出异常码或CRC错误,检查接线、从站地址和波特率设置。
常见问题
- 无响应:检查RS485 A/B接线是否反接,确认串口设备名正确,传感器供电正常。
- CRC校验失败:可能波特率或数据格式不匹配,或线路干扰导致数据帧损坏。
- 温度值为负:寄存器按有符号数解析,若传感器输出补码格式的负温度,代码已通过
struct.unpack('>h')正确处理。