第4章:串口通信实战:RS232/RS485原理、串口参数配置与Python编程
各位工程师朋友,大家好。今天我们来聊聊串口通信。
说实话,在MES系统实施中,串口通信是我打交道最多的协议之一。别看它“老”,但工业现场里,90%以上的老旧设备、称重仪表、条码枪、PLC,都靠它跟MES“说话”。
我刚开始做项目时,总觉得串口简单,不就是两根线嘛。结果第一次在现场调一台德国老秤,死活收不到数据,折腾了一下午。最后发现是波特率配错了——设备默认是9600,我配成了19200。嗯,从那以后,我再也不敢小看串口参数配置了。
核心认知:串口通信是MES数据采集的“最后一公里”。搞不定串口,你的MES就是空中楼阁。
4.1 RS232与RS485:两种“说话”方式
先搞清楚这两个东西。说白了,RS232和RS485是两种不同的电气标准,决定了设备之间怎么“喊话”。
RS232:点对点,短距离
- 通信方式:一对一。一台电脑只能连一台设备。
- 传输距离:15米左右。再远信号就衰减得不行了。
- 信号电平:±12V。电压高,抗干扰能力一般。
- 典型应用:老式PC串口、近距离仪表、条码枪。
我在一个仓库项目里,遇到过一台RS232的电子秤。电脑就在秤旁边,距离不到2米,用RS232完全没问题。但如果你想把数据传到50米外的服务器机房,那RS232就歇菜了。
RS485:多点,远距离,抗干扰
- 通信方式:一对多。一条总线上可以挂32个设备(甚至更多)。
- 传输距离:1200米以上。加中继器还能更远。
- 信号电平:差分信号(±2V~±6V)。抗干扰能力强。
- 典型应用:工厂产线、远程数据采集、PLC网络。
你想想看,一条产线上几十台设备,如果都用RS232,电脑得装多少个串口?RS485就是为这种场景设计的。我做过一个汽车零部件的产线项目,一条总线上挂了16台拧紧枪,全部走RS485,一根双绞线搞定。
我的经验:现场布线时,RS485一定要用双绞屏蔽线。我曾经图省事用了普通网线,结果电机一启动,数据全乱码。屏蔽层单端接地,切记。
4.2 串口参数配置:波特率、数据位、停止位、校验位
这是串口通信的“四要素”。两边必须完全一致,否则就是鸡同鸭讲。
| 参数 | 说明 | 常见值 | 我的建议 |
|---|---|---|---|
| 波特率 | 每秒传输的比特数。说白了就是“说话速度”。 | 9600、19200、38400、115200 | 工业设备大多用9600或19200。别盲目追求高速,距离越远,速度要越低。 |
| 数据位 | 每个数据包中实际数据的位数。 | 7、8 | 现在基本都用8位。7位是历史遗留,除非设备手册明确要求。 |
| 停止位 | 数据包结束的标志位。 | 1、1.5、2 | 绝大多数设备用1位停止位。如果通信不稳定,可以试试2位。 |
| 校验位 | 用于检测数据是否在传输中出错。 | None、Even、Odd | 工业现场建议用Even(偶校验)。None虽然简单,但抗干扰差。 |
避坑指南:我曾经在调试一台注塑机时,设备手册写的是“9600,8,N,1”,但怎么都连不上。最后用示波器抓波形才发现,设备实际用的是“9600,8,E,1”。手册是错的!所以,永远不要100%相信手册,用串口调试工具先抓包验证。
4.3 Python串口编程实战
好了,理论讲完,咱们来点实际的。Python的pyserial库是串口编程的标配。我所有MES数据采集项目,底层通信都用它。
4.3.1 安装pyserial
pip install pyserial
4.3.2 基础代码:打开串口并读取数据
import serial
import serial.tools.list_ports
# 第一步:列出所有可用串口
ports = serial.tools.list_ports.comports()
for port in ports:
print(f"发现串口: {port.device} - {port.description}")
# 第二步:配置并打开串口
ser = serial.Serial(
port='COM3', # Windows下用COM口,Linux下用/dev/ttyUSB0
baudrate=9600, # 波特率
bytesize=8, # 数据位
parity='N', # 校验位:N=None, E=Even, O=Odd
stopbits=1, # 停止位
timeout=1 # 超时时间(秒)
)
# 第三步:读取数据
if ser.is_open:
print(f"串口 {ser.port} 已打开")
data = ser.read(100) # 读取100个字节
print(f"收到原始数据: {data}")
# 通常需要解码
text = data.decode('ascii', errors='ignore')
print(f"解码后: {text}")
# 第四步:关闭串口
ser.close()
我的习惯:生产环境中,我从来不用ser.read(100)这种固定长度读取。因为你不知道设备什么时候发完数据。我一般用read_until(),指定结束符(比如换行符\n),或者用in_waiting属性判断缓冲区有多少数据。
4.3.3 实战:读取电子秤数据
这是我在MES项目中最常见的场景。电子秤一般会持续发送重量数据,格式类似:ST,+000.500kg\r\n。
import serial
import time
def read_scale(port='COM3', baud=9600):
try:
ser = serial.Serial(port, baud, timeout=2)
print(f"已连接电子秤: {port}")
while True:
# 读取一行数据(直到换行符)
line = ser.readline()
if line:
# 解码并去除首尾空白
data = line.decode('ascii', errors='ignore').strip()
print(f"秤数据: {data}")
# 解析重量值(假设格式:ST,+000.500kg)
if data.startswith('ST,'):
try:
weight_str = data.split(',')[1].replace('kg', '')
weight = float(weight_str)
print(f"解析重量: {weight} kg")
# 这里可以写入MES数据库
except:
print("数据解析失败")
time.sleep(0.1) # 避免CPU占用过高
except serial.SerialException as e:
print(f"串口错误: {e}")
finally:
if 'ser' in locals() and ser.is_open:
ser.close()
if __name__ == '__main__':
read_scale()
4.3.4 实战:向PLC发送指令
有时候MES需要主动控制设备,比如让PLC启动一个工位。Modbus RTU是工业上最常用的协议,但底层还是串口。
import serial
import struct
def send_modbus_command(ser, slave_id, function_code, start_addr, quantity):
"""
发送Modbus RTU读取指令
"""
# 构建报文
message = struct.pack('>B', slave_id) # 从站地址
message += struct.pack('>B', function_code) # 功能码
message += struct.pack('>H', start_addr) # 起始地址
message += struct.pack('>H', quantity) # 寄存器数量
# 计算CRC校验
crc = calculate_crc(message)
message += struct.pack('<H', crc)
# 发送
ser.write(message)
print(f"发送: {message.hex()}")
# 读取响应
response = ser.read(8) # 根据协议长度调整
print(f"接收: {response.hex()}")
return response
def calculate_crc(data):
"""Modbus CRC16计算"""
crc = 0xFFFF
for byte in data:
crc ^= byte
for _ in range(8):
if crc & 0x0001:
crc = (crc >> 1) ^ 0xA001
else:
crc = crc >> 1
return crc
# 使用示例
ser = serial.Serial('COM3', 9600, timeout=1)
response = send_modbus_command(ser, slave_id=1, function_code=3,
start_addr=0, quantity=2)
ser.close()
注意:Modbus RTU的CRC校验是必须的。我曾经见过一个新手工程师,自己写协议忘了加CRC,结果设备偶尔响应、偶尔不响应,排查了整整两天。CRC计算代码网上很多,但建议你自己手写一遍,理解原理。
4.4 本章知识体系
下面这张图,是我对串口通信知识体系的总结。你可以把它当作一个“地图”,遇到问题就知道该查哪一块。
4.5 常见问题与排查思路
最后,分享几个我踩过的坑,希望能帮你少走弯路。
- 串口打不开:检查端口号是否正确,有没有被其他程序占用。Windows下可以用“设备管理器”查看。
- 收到乱码:90%是波特率不匹配。用串口调试工具(比如SSCOM)先确认设备实际参数。
- 数据时有时无:检查接线,特别是RS485的A/B线有没有接反。另外,终端电阻(120Ω)在长距离通信时一定要加。
- 程序卡死:设置合理的超时时间(timeout)。我一般设1-3秒,别设0(非阻塞)也别设None(一直等)。
我的工具箱:现场调试串口,我必带三样东西:USB转RS232/485转换器、串口调试助手(SSCOM或Putty)、示波器(抓波形用)。没有示波器的话,逻辑分析仪也行,几十块钱的就能用。
好了,串口通信就讲到这里。记住一句话:串口不难,但细节决定成败。参数配对了,线接对了,数据自然就来了。下一章,我们会聊更高级的工业以太网协议,但串口这个基本功,值得你花时间彻底搞透。