You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

串口通信: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转换。只需要设计带缓冲区的接收逻辑,根据收到的第二个字节判断整帧长度,攒够完整数据再处理即可:

  1. 所有列表的第一个字节都是0,可以作为简单帧头校验,避免杂散字节干扰
  2. 收到第二个字节后,根据值判断需要累计接收的总长度:
    • 值为0 → 共需接收7字节
    • 值为1 → 共需接收4字节
    • 值为2 → 共需接收3字节
  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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.27 20:27:01