串口通信:Python3向Arduino Leonardo发送变长列表的还原问题
问题根因
- 你当前Arduino输出的数值序列(98、39、92、120、48...)完全对应你Python终端打印的
b'\x00\x00\x01\xb0\x01\x00\x00'这段文本的逐字符ASCII码:98是字符b、39是单引号'、92是反斜杠\、120是字符x。这说明当前串口传输的根本不是打包后的原始二进制字节,而是这段文本字符串的内容,不是真正的二进制数据流。 - 你的Python端代码存在逻辑错误:原本的打包、写入操作被放在遍历列表的for循环内部,会导致列表每遍历一个元素,就把整个列表完整打包重发一次。比如长度为7的列表,会连续重复发送7次相同的7字节二进制流,最终产生49字节的乱序拼接数据,Arduino根本无法正常解析。
- 你设计的三类列表本身天然适配变长传输:三类列表的第二个字节分别是0、1、2,和对应的总长度7、4、3一一绑定,不需要额外设计复杂的变长协议。
Python端修正代码
把重复循环的错误去掉,只打包、发送一次完整数据:
import serial import struct # 示例待发送数据,属于三类长度中的任意一种均可 Datas_To_Send = [0, 0, 1, 176, 1, 0, 0] with Serial(port=port, baudrate=9600, timeout=1, writeTimeout=1) as port_serie: if port_serie.isOpen(): # 按列表长度打包为无符号单字节二进制,仅执行一次 s = struct.pack('!%dB' % len(Datas_To_Send), *Datas_To_Send) port_serie.write(s)
修正后,串口实际传输的就是每个0-255数值对应的单字节原始二进制值,不会带b'、\x这类文本字符。
Arduino端实现逻辑
Serial.read()本身返回的就是0-255范围的无符号单字节值,读出来的结果和Python端发送的数值一一对应,不需要做ASCII转换。只需要设计带缓冲区的接收逻辑,根据收到的第二个字节判断整帧长度,攒够完整数据再处理即可:
- 所有列表的第一个字节都是0,可以作为简单帧头校验,避免杂散字节干扰
- 收到第二个字节后,根据值判断需要累计接收的总长度:
- 值为0 → 共需接收7字节
- 值为1 → 共需接收4字节
- 值为2 → 共需接收3字节
- 缓冲区攒够对应长度的字节后,数组内的内容就是还原完成的原始列表
参考Arduino代码:
// 三类列表最长为7字节,预留缓冲区空间 const uint8_t MAX_BUF_LEN = 8; uint8_t recv_buf[MAX_BUF_LEN]; uint8_t recv_index = 0; void setup() { Serial.begin(9600); // Leonardo为USB虚拟串口,需等待串口连接完成,避免上电丢数 while (!Serial) {} } void loop() { changeSettings(); } void changeSettings() { while (Serial.available() > 0) { uint8_t current_byte = Serial.read(); // 接收状态判定 if (recv_index == 0) { // 匹配帧头(所有列表首字节都是0) if (current_byte == 0) { recv_buf[recv_index] = current_byte; recv_index++; } } else if (recv_index == 1) { // 第二个字节是类型标识,记录后准备按长度接收 recv_buf[recv_index] = current_byte; recv_index++; // 校验标识合法性,不合法则重置接收状态 if (recv_buf[1] != 0 && recv_buf[1] != 1 && recv_buf[1] != 2) { recv_index = 0; } } else { // 持续接收剩余字节 recv_buf[recv_index] = current_byte; recv_index++; // 根据类型计算整帧需要的总长度 uint8_t expected_len; if (recv_buf[1] == 0) expected_len = 7; else if (recv_buf[1] == 1) expected_len = 4; else expected_len = 3; // 收满整帧长度,输出还原结果 if (recv_index >= expected_len) { Serial.print("Restored list: ["); for (uint8_t i = 0; i < recv_index; i++) { if (i != 0) Serial.print(", "); Serial.print(recv_buf[i]); } Serial.println("]"); // 重置缓冲区,准备接收下一帧 recv_index = 0; } } } }
调试注意事项
- 验证串口实际发送的二进制内容:可以在Python端发送完成后,用串口监听工具直接抓原始字节,确认没有b'、\x这类文本字符,保证发的是纯二进制流。
- 不要在Arduino端收到一个字节就立刻打印结果:串口存在缓冲延迟,单字节读取直接打印的方式很容易把重复帧、不完整帧误当成有效数据。
内容的提问来源于stack exchange,提问作者Fanch
相关产品推荐
相关产品推荐

