Python解析嵌入式系统UART串口缓冲区字符串的最优方案
Python脚本解析UART传来的结构化命令
我刚好做过类似的UART命令解析项目,结合你给出的命令格式,给你整理了一套完整的可运行脚本,附带详细说明:
准备工作
首先得安装串口操作的Python库,打开终端跑这条命令:
pip install pyserial
核心思路
你的命令格式是S;SEND;40;{"ID":"asg01","T":1,"P":{"T":180}};E,结构很清晰:
- 以
S开头,E结尾,用分号;分隔字段 - 字段依次是:起始标记、命令类型、载荷长度、JSON载荷、结束标记
我们的处理逻辑要做到:
- 从UART读取字节流,处理可能的粘包问题(比如一次读不全一条命令)
- 识别完整的命令(从
S到E的完整片段) - 拆分字段并做合法性校验(开头结尾、载荷长度匹配)
- 解析JSON载荷,执行对应操作
完整代码示例
import serial import json from time import sleep def init_uart(port='COM3', baudrate=115200, timeout=1): """初始化UART串口""" try: ser = serial.Serial(port, baudrate, timeout=timeout) print(f"成功打开串口: {port}") return ser except Exception as e: print(f"串口打开失败: {str(e)}") return None def parse_uart_command(raw_data): """解析单条UART命令""" # 先把字节转成字符串,去掉无关的空白字符 command_str = raw_data.decode('utf-8').strip() # 校验开头和结尾 if not (command_str.startswith('S;') and command_str.endswith(';E')): print(f"无效命令格式: {command_str}") return None # 拆分字段 parts = command_str.split(';') # 正常拆分后应该是 ['S', 'SEND', '40', '{"ID":"asg01",...}', 'E'] 共5个元素 if len(parts) != 5: print(f"命令字段数量错误: {command_str}") return None start_tag, cmd_type, payload_len_str, payload_str, end_tag = parts # 校验载荷长度 try: payload_len = int(payload_len_str) if len(payload_str) != payload_len: print(f"载荷长度不匹配: 预期{payload_len},实际{len(payload_str)}") return None except ValueError: print(f"载荷长度格式错误: {payload_len_str}") return None # 解析JSON载荷 try: payload = json.loads(payload_str) except json.JSONDecodeError as e: print(f"JSON解析失败: {str(e)}") return None return { 'command': cmd_type, 'payload': payload } def execute_command(parsed_cmd): """根据解析后的命令执行对应操作""" if not parsed_cmd: return cmd_type = parsed_cmd['command'] payload = parsed_cmd['payload'] if cmd_type == 'SEND': # 这里写你要执行的操作,比如打印参数、调用其他函数 print(f"收到SEND命令,设备ID: {payload['ID']},类型T: {payload['T']},参数P: {payload['P']}") # 示例:如果T=1,执行某个动作 if payload['T'] == 1: print(f"执行温度相关操作,目标温度: {payload['P']['T']}℃") else: print(f"未处理的命令类型: {cmd_type}") def main(): # 初始化串口,注意端口号要改成你实际的(Windows是COMx,Linux是/dev/ttyUSBx) ser = init_uart(port='COM3') if not ser: return # 缓存串口数据,处理粘包 buffer = b'' try: while True: # 读取串口数据 if ser.in_waiting > 0: buffer += ser.read(ser.in_waiting) # 检查是否有完整的命令(以;E结尾) while b';E' in buffer: # 拆分出第一条完整命令 cmd_end_idx = buffer.index(b';E') + 2 # +2是包含;E的长度 raw_cmd = buffer[:cmd_end_idx] buffer = buffer[cmd_end_idx:] # 剩下的留在缓存里处理下一条 # 解析并执行命令 parsed_cmd = parse_uart_command(raw_cmd) execute_command(parsed_cmd) sleep(0.1) except KeyboardInterrupt: print("\n用户终止程序") except Exception as e: print(f"运行出错: {str(e)}") finally: if ser.is_open: ser.close() print("串口已关闭") if __name__ == '__main__': main()
代码细节说明
- 串口初始化:
init_uart函数负责打开串口,你需要把port参数改成你实际的串口(比如Linux下的/dev/ttyUSB0,Windows下的COM3) - 粘包处理:用
buffer缓存收到的字节流,每次找到;E结尾就拆分出完整命令,剩下的继续留在缓存里,避免因为一次读不全导致解析失败 - 命令校验:严格检查开头结尾、字段数量、载荷长度,保证只处理合法的命令
- JSON解析:把载荷转成Python字典,方便后续操作
- 命令执行:
execute_command函数里可以根据不同的命令类型(比如你的SEND)添加自定义的业务逻辑
注意事项
- 确保串口参数和嵌入式系统一致:波特率115200,数据位8,停止位1,无校验(默认就是这个配置,如果你的系统有修改,要在
serial.Serial里加对应的参数) - 处理异常:代码里加了串口打开失败、JSON解析错误、键盘中断等异常处理,避免程序崩溃
- 调试建议:如果遇到解析问题,可以在代码里加打印,输出原始的buffer内容,排查数据是否正确
内容的提问来源于stack exchange,提问作者Federico
相关产品推荐
相关产品推荐

