1. RS-485温湿度传感器通信基础第一次接触RS-485传感器时我被它稳定的长距离通信能力惊艳到了。相比常见的I2C和SPI接口RS-485特别适合工业环境下的数据采集。最近在做一个温室监控项目时就遇到了需要通过Python读取RS-485温湿度传感器的需求。RS-485采用差分信号传输抗干扰能力极强最远通信距离可达1200米。典型的温湿度传感器比如我使用的AHT20-485型号会遵循Modbus RTU协议通过串口与主机通信。这里有个容易踩的坑虽然RS-485和RS-232都是串口通信但电气特性完全不同必须通过转换器才能连接电脑。实际接线时你需要准备USB转RS-485转换器我用的是UT-890双绞线建议带屏蔽层终端电阻120Ω长距离时必须加装2. 搭建Python开发环境在开始编码前我们需要准备好Python环境。推荐使用Python 3.7版本我实测过3.6会有字节处理兼容性问题。核心依赖库就一个pip install pyserial如果你遇到权限问题可以加上--user参数。我习惯用VS Code开发但PyCharm的串口调试工具也很方便。这里分享一个排查设备节点的小技巧import serial.tools.list_ports print([port.device for port in serial.tools.list_ports.comports()])在Windows上会输出像COM3这样的端口号而在Linux下通常是/dev/ttyUSB0。特别注意如果同时插着多个转换器设备节点可能会变建议写死前先动态检测。3. 传感器通信协议解析以常见的Modbus RTU协议为例一个完整的请求帧包含这些部分字段设备地址功能码起始地址数据长度CRC校验示例0x010x030x00000x00020xC40B对应的Python构造代码def build_request(device_addr0x01, reg_addr0x0000, length0x0002): request [ device_addr, # 设备地址 0x03, # 读取保持寄存器功能码 reg_addr 8, # 寄存器地址高字节 reg_addr 0xFF,# 寄存器地址低字节 length 8, # 数据长度高字节 length 0xFF, # 数据长度低字节 ] crc calculate_crc(bytes(request)) # CRC计算后面会讲 return bytes(request [crc 0xFF, crc 8])传感器返回的数据帧结构更复杂些。以我的项目为例返回的温湿度数据采用16位有符号整数温度值需要除以10得到实际值01 03 04 01 18 FF 9F 7A 63 └─┬─┬─┬─┬──┬──┬──┬─────┘ │ │ │ │ │ │ └─ CRC校验 │ │ │ │ └──┴── 湿度(0x0118)→28.0% │ │ │ └────── 温度(0xFF9F)→-9.7℃ │ │ └──────── 数据字节数(0x04) │ └────────── 功能码(0x03) └──────────── 设备地址(0x01)4. 完整的数据采集实现下面是我在实际项目中验证过的完整代码包含几个关键处理技巧import serial import struct class RS485Sensor: def __init__(self, port, baudrate9600, timeout1): self.ser serial.Serial( portport, baudratebaudrate, bytesizeserial.EIGHTBITS, parityserial.PARITY_NONE, stopbitsserial.STOPBITS_ONE, timeouttimeout ) def _calculate_crc(self, data): Modbus CRC16计算 crc 0xFFFF for byte in data: crc ^ byte for _ in range(8): if crc 0x0001: crc 1 crc ^ 0xA001 else: crc 1 return crc def read_registers(self, addr, length): 读取保持寄存器 request self._build_request(addr, length) self.ser.write(request) # 读取响应头地址功能码数据长度 header self.ser.read(3) if len(header) ! 3: raise IOError(响应超时) # 读取数据部分 data_len header[2] data self.ser.read(data_len 2) # 数据CRC # 校验CRC received_crc struct.unpack(H, data[-2:])[0] calculated_crc self._calculate_crc(header data[:-2]) if received_crc ! calculated_crc: raise ValueError(CRC校验失败) return data[:-2] def read_temperature_humidity(self): 读取温湿度值 data self.read_registers(0x0000, 0x0002) # 解析16位有符号整数 humidity struct.unpack(h, data[0:2])[0] / 10.0 temp_raw struct.unpack(h, data[2:4])[0] # 处理负数补码转换 temperature temp_raw / 10.0 if temp_raw 0x8000 else (temp_raw - 0x10000) / 10.0 return temperature, humidity # 使用示例 sensor RS485Sensor(COM3) temp, humi sensor.read_temperature_humidity() print(f温度: {temp:.1f}℃, 湿度: {humi:.1f}%)这段代码有几个值得注意的技术点CRC校验采用查表法会更高效但这里用直接计算方便理解struct.unpack处理字节序时h表示大端16位有符号整数温度值的补码处理是关键否则会得到错误的正值5. 常见问题排查指南在调试RS-485通信时我遇到过不少坑这里总结几个典型问题问题1收不到任何数据检查接线A/B线是否接反可以尝试调换确认终端电阻线路超过100米时需要加120Ω电阻验证设备地址有些传感器默认地址是0xFF问题2收到乱码数据# 典型表现 b\x01\x03\x04\x01\x18\xff\x9f\x00\x00 # 最后CRC位错误检查波特率必须与传感器设置完全一致测试线路干扰改用屏蔽双绞线远离电源线确认停止位常见配置是1个停止位问题3CRC校验频繁失败建议添加调试代码打印原始数据print(原始数据:, data.hex( )) # 输出类似 01 03 04 01 18 ff 9f 7a 63对于需要长时间运行的系统建议增加重试机制def safe_read(sensor, max_retry3): for _ in range(max_retry): try: return sensor.read_temperature_humidity() except (IOError, ValueError) as e: print(f读取失败: {e}, 重试中...) time.sleep(0.5) raise Exception(超过最大重试次数)最后分享一个性能优化技巧批量读取多个寄存器可以减少通信次数。比如有些传感器同时提供温度、湿度和光照度数据可以一次性读取多个寄存器。