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

Teensy 4.1通过Python读取SD卡大文件数据截断问题求助

问题分析与解决方案

核心问题根源

  1. Python串口默认接收缓冲区(约32KB)过小,Teensy快速发送大文件时会触发缓冲区溢出,导致后续数据丢失
  2. readline()依赖换行符分割数据,若文件中换行稀少或缓冲区满后触发超时,会提前终止接收
  3. Teensy端无流控机制,持续发送速度超过Python的处理能力;同时存在文件名解析的潜在错误(命令中的空格会被带入文件名)

解决方案1:增大Python串口接收缓冲区

取消注释缓冲区设置代码,将接收缓冲区调整为足够容纳大文件的尺寸:

def connect(self):
    self.ser = serial.Serial(
        self.serial_port, baudrate=self.baud_rate, timeout=self.timeout, bytesize=self.bytesize, stopbits=self.stopbit)
    # 增大接收缓冲区到1.6MB,可根据实际文件大小调整
    self.ser.set_buffer_size(rx_size=1600000, tx_size=1600000)
    self.ser.reset_input_buffer()

解决方案2:替换readline()为批量读取逻辑

readline()效率低下且依赖换行符,改为循环读取所有字节直到收到结束标记:

def get_file(self, filename):
    # 去掉命令中的空格,避免Teensy解析文件名出错
    self.write_command(f"get,{filename}\n")
    time.sleep(0.1)  # 短延迟确保Teensy启动发送
    data = b''
    while True:
        # 每次读取尽可能多的字节,而非逐行等待
        chunk = self.ser.read(self.ser.in_waiting or 1024)
        if not chunk:
            continue
        data += chunk
        # 检查是否包含结束标记
        if b'EndOfMessage' in data:
            # 分离有效数据与结束标记
            data = data.split(b'EndOfMessage')[0]
            break
    # 若需要按行处理,最后统一拆分
    lines = data.split(b'\n')
    return lines

解决方案3:修复Teensy端文件名解析问题

Python发送的命令带空格会导致Teensy读取错误文件名,修改代码跳过空格:

else if (!strncmp(command, "get,", 4)) {
    ExFile putFile;
    uint8_t r;
    // 跳过"get,"后的所有空格,获取正确文件名
    char* filename = &command[4];
    while (*filename == ' ') filename++;
    
    // 打开文件
    putFile = sd.open(filename, O_READ);

    if (putFile == NULL) {
      Serial.println("File not found");
      return;
    }
    // 移除无用的ready变量,简化循环逻辑
    while (putFile.available()) {
      r = putFile.read();
      Serial.write(r);
    }
    // 关闭文件并发送结束标记
    putFile.close();
    Serial.println("EndOfMessage");
}

解决方案4:添加流控机制(针对超大文件)

如果文件尺寸超过缓冲区上限,通过"发送-确认"机制避免数据丢失:

Teensy端修改:

else if (!strncmp(command, "get,", 4)) {
    // ... 文件名解析与文件打开逻辑 ...

    const uint32_t CHUNK_SIZE = 32768; // 32KB块大小
    uint8_t buffer[CHUNK_SIZE];
    uint32_t bytesRead;

    while ((bytesRead = putFile.read(buffer, CHUNK_SIZE)) > 0) {
        Serial.write(buffer, bytesRead);
        // 等待Python发送确认信号
        while (!Serial.available());
        char ack = Serial.read();
        if (ack != 'A') break; // 确认字符约定为'A'
    }

    putFile.close();
    Serial.println("EndOfMessage");
}

Python端对应修改:

def get_file(self, filename):
    self.write_command(f"get,{filename}\n")
    time.sleep(0.1)
    data = b''
    while True:
        chunk = self.ser.read(self.ser.in_waiting or 1024)
        if not chunk:
            continue
        data += chunk
        if b'EndOfMessage' in data:
            data = data.split(b'EndOfMessage')[0]
            break
        # 发送确认信号,允许Teensy继续发送下一块
        self.ser.write(b'A')
    lines = data.split(b'\n')
    return lines

内容的提问来源于stack exchange,提问作者RvN

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.13 09:16:04