4. SECS-I 串口通信基础
任务说明
本案例用 Python 通过 RS-232 串口发送一条 SECS-I 协议的 S1F1(Are You There)消息,并解析设备返回的 S1F2 消息。SECS-I 帧结构为:长度头(10 字节 ASCII 十进制,不足补 0)+ 消息体。消息体由设备 ID、W-bit、Stream/Function、Block 号、系统字节、数据长度和 SECS-II 数据组成。
环境依赖
pip install pyserial
需要一根 RS-232 串口线(或 USB 转串口),设备端已配置好 SECS-I 参数(波特率、数据位、停止位、校验位)。本案例默认 9600-8-N-1。
SECS-I 帧结构
完整代码
import serial
import struct
import time
# ---------- SECS-I 帧封装 ----------
def build_secs1_frame(device_id, stream, function, wbit=0, system_bytes=0, data=b""):
"""构造 SECS-I 帧:10字节长度头 + 消息体"""
# 消息体:设备ID(2) + W-bit(1) + S/F(1) + Block(1) + 系统字节(2) + 数据长度(2) + 数据
sf = (stream & 0x7F) << 1 | (function & 0x7F)
body = struct.pack(">H", device_id) # 设备ID 2字节
body += bytes([wbit & 0x80]) # W-bit 1字节
body += bytes([sf]) # Stream/Function 1字节
body += bytes([0]) # Block 号 1字节
body += struct.pack(">H", system_bytes) # 系统字节 2字节
body += struct.pack(">H", len(data)) # 数据长度 2字节
body += data
# 长度头:10字节 ASCII 十进制,不足补0
header = f"{len(body):010d}".encode("ascii")
return header + body
# ---------- SECS-I 帧解析 ----------
def parse_secs1_frame(raw):
"""解析 SECS-I 帧,返回字典"""
if len(raw) < 10:
raise ValueError("帧长度不足10字节")
length = int(raw[:10].decode("ascii"))
body = raw[10:10 + length]
if len(body) < 10:
raise ValueError("消息体不完整")
device_id = struct.unpack(">H", body[0:2])[0]
wbit = body[2]
sf = body[3]
stream = (sf >> 1) & 0x7F
function = sf & 0x7F
block = body[4]
system_bytes = struct.unpack(">H", body[5:7])[0]
data_len = struct.unpack(">H", body[7:9])[0]
data = body[9:9 + data_len]
return {
"device_id": device_id, "wbit": wbit,
"stream": stream, "function": function,
"block": block, "system_bytes": system_bytes,
"data_len": data_len, "data": data,
}
# ---------- 主流程 ----------
def main():
ser = serial.Serial(
port="COM3", # Linux 下改为 /dev/ttyUSB0
baudrate=9600,
bytesize=serial.EIGHTBITS,
parity=serial.PARITY_NONE,
stopbits=serial.STOPBITS_ONE,
timeout=5,
)
try:
# 发送 S1F1
frame = build_secs1_frame(device_id=0, stream=1, function=1)
ser.write(frame)
print("已发送 S1F1,等待回复...")
# 接收回复:先读10字节长度头
header = ser.read(10)
if len(header) < 10:
print("超时:未收到长度头")
return
length = int(header.decode("ascii"))
body = ser.read(length)
reply = parse_secs1_frame(header + body)
print(f"收到 S{reply['stream']}F{reply['function']} "
f"设备ID={reply['device_id']} 数据长度={reply['data_len']}")
if reply["stream"] == 1 and reply["function"] == 2:
print("验证通过:设备回复 S1F2,通信正常")
else:
print("收到非预期消息")
finally:
ser.close()
if __name__ == "__main__":
main()
运行方式
python secs1_s1f1.py
将 port 改为实际串口号(Windows 如 COM3,Linux 如 /dev/ttyUSB0)。
验证方法与通过条件
- 运行后终端打印“已发送 S1F1,等待回复...”。
- 若设备在线并支持 SECS-I,终端打印“收到 S1F2 设备ID=0 数据长度=0”及“验证通过”。
- 若 5 秒内无回复,打印“超时:未收到长度头”,说明串口参数或设备连接需检查。
- 通过条件:收到 Stream=1、Function=2 的回复帧,且长度头解析无异常。
常见问题
- 串口号错误:
serial.SerialException,确认设备管理器中端口号。 - 波特率不匹配:收到乱码或长度头解析失败,确认设备端 SECS-I 参数。
- 设备 ID 不匹配:部分设备要求非 0 的 device_id,按设备手册修改。