6.7 KiB
6.7 KiB
串口通信协议
一个完整的Python串口通信协议实现,支持两台设备之间可靠的数据传输,可传输结构化数据(用户名、线路名称、站点编号等)。
特性
- ✅ 完整的协议帧结构 - 包含帧头、长度、命令、数据、CRC校验、帧尾
- ✅ CRC16校验 - 使用CRC-16/MODBUS算法确保数据完整性
- ✅ 异步接收 - 多线程接收,不阻塞主程序
- ✅ 多种命令类型 - 心跳、数据传输、控制命令、应答等
- ✅ 结构化数据 - 支持JSON格式的站点数据传输(用户名、线路名称、站点编号)
- ✅ 易于使用 - 简洁的API,开箱即用
协议帧格式
+------+------+--------+------+--------+------+
| HEAD | LEN | CMD | DATA | CRC | TAIL |
+------+------+--------+------+--------+------+
| 0xAA | 2B | 1B | N字节 | 2B | 0x55 |
+------+------+--------+------+--------+------+
字段说明
- HEAD: 帧头标识 (1字节, 0xAA)
- LEN: 数据长度 (2字节, 大端序, 包含CMD+DATA)
- CMD: 命令字 (1字节)
- DATA: 数据内容 (N字节)
- CRC: CRC16校验 (2字节, 大端序)
- TAIL: 帧尾标识 (1字节, 0x55)
安装
pip install -r requirements.txt
命令类型
| 命令 | 值 | 说明 |
|---|---|---|
| HEARTBEAT | 0x01 | 心跳包 |
| DATA_QUERY | 0x02 | 数据查询 |
| DATA_RESPONSE | 0x03 | 数据响应 |
| CONTROL | 0x04 | 控制命令 |
| ACK | 0x05 | 应答 |
| NACK | 0x06 | 否定应答 |
站点数据模型
StationData 类
用于传输站点信息的数据模型:
from data_models import StationData
# 创建站点数据
station = StationData(
username="张三", # 用户名
line_name="1号线", # 线路名称
station_no=5 # 第几站
)
# 序列化为字节(用于发送)
data_bytes = station.to_bytes()
# 从字节反序列化(接收后解析)
restored = StationData.from_bytes(data_bytes)
使用方法
基本示例
from serial_protocol import SerialProtocol, Command
# 创建协议实例
device = SerialProtocol(port='COM1', baudrate=115200)
# 打开串口
if device.open():
# 发送数据
device.send_data(b"Hello, World!")
# 发送心跳
device.send_heartbeat()
# 发送控制命令
device.send_control(0x10, b"\x01\x02")
# 关闭串口
device.close()
异步接收数据
def on_receive(cmd, data):
print(f"收到命令: 0x{cmd:02X}, 数据: {data.hex()}")
device = SerialProtocol(port='COM1', baudrate=115200)
device.open()
# 启动接收线程
device.start_receive(on_receive)
# ... 主程序运行 ...
device.close()
完整示例
设备A (发送端)
运行 device_a.py,设备A会定期发送:
- 心跳包
- 站点数据(包含用户名、线路名称、站点编号)
- 接收设备B的字典响应
输出示例:
============================================================
循环 1
============================================================
✓ 发送心跳包
准备发送站点数据:
用户: 李四, 线路: 2号线, 站点: 第1站
✓ 站点数据发送成功
[设备A] 收到数据:
命令: 0x03 (DATA_RESPONSE)
📥 收到设备B的响应字典:
1: "李四"
2: "2号线"
3: "1"
4: "已接收"
5: "设备B确认"
运行命令:
python device_a.py
设备B (接收端)
运行 device_b.py,设备B会:
- 接收站点数据并解析显示
- 自动应答心跳包
- 返回统一格式的响应字典
{1:"xxx", 2:"xxx", 3:"xxx", ...}
输出示例:
============================================================
[设备B] 收到数据
============================================================
命令类型: 0x03 (DATA_RESPONSE)
📍 站点数据详情:
用户名称: 李四
线路名称: 2号线
站点编号: 第1站
📤 设备B统一响应格式:
1: "李四"
2: "2号线"
3: "1"
4: "已接收"
5: "设备B确认"
<<< 已发送响应字典
运行命令:
python device_b.py
设备B统一响应格式
设备B接收到站点数据后,会返回统一格式的字典响应:
{
1: "用户名",
2: "线路名称",
3: "站点编号",
4: "已接收",
5: "设备B确认"
}
字段说明:
1: 接收到的用户名2: 接收到的线路名称3: 接收到的站点编号(字符串)4: 固定值 "已接收"5: 固定值 "设备B确认"
硬件连接
方案1: 使用两个USB转串口模块
设备A (COM1) <---RX/TX交叉---> 设备B (COM2)
TX ----------------------> RX
RX <---------------------- TX
GND <---------------------> GND
方案2: 使用虚拟串口 (测试用)
Windows: 使用 com0com 或 Virtual Serial Port Driver Linux: 使用 socat
# Linux创建虚拟串口对
socat -d -d pty,raw,echo=0 pty,raw,echo=0
# 会创建 /dev/pts/X 和 /dev/pts/Y
API文档
SerialProtocol 类
初始化
SerialProtocol(port: str, baudrate: int = 115200, timeout: float = 1.0)
方法
open() -> bool- 打开串口close()- 关闭串口send_frame(cmd: int, data: bytes) -> bool- 发送数据帧receive_frame() -> Optional[dict]- 接收数据帧(阻塞)start_receive(callback)- 启动异步接收stop_receive()- 停止异步接收send_heartbeat() -> bool- 发送心跳包send_data(data: bytes) -> bool- 发送数据send_control(code: int, params: bytes) -> bool- 发送控制命令send_ack() -> bool- 发送应答send_nack() -> bool- 发送否定应答
静态方法
calc_crc16(data: bytes) -> int- 计算CRC16校验值build_frame(cmd: int, data: bytes) -> bytes- 构建数据帧parse_frame(frame: bytes) -> Optional[dict]- 解析数据帧
常见问题
1. 如何修改串口号?
编辑 device_a.py 和 device_b.py,修改 port 参数:
- Windows:
'COM1','COM2', etc. - Linux:
'/dev/ttyUSB0','/dev/ttyS0', etc.
2. 如何修改波特率?
修改 baudrate 参数,常用值:9600, 19200, 38400, 57600, 115200
3. 数据长度限制?
理论最大65535字节,但建议单帧数据不超过1024字节以提高可靠性。
4. 如何处理超时?
设置 timeout 参数控制读取超时时间。
应用场景
- 🤖 机器人通信
- 📡 传感器数据采集
- 🎮 设备控制
- 📊 工业自动化
- 🔌 嵌入式系统互联
许可证
MIT License
作者
Created with ❤️ for reliable serial communication