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

Arduino串口监视器输入无输出及字符丢失问题求助

Arduino串口接收数据异常:字符丢失、输出异常问题排查与修复

我是Arduino新手,学习USB串口接收数据时遇到功能异常:

  • 初始代码输入"ggggg"曾出现递减输出:ggggg、gggg、ggg、gg、g,修改后无法复现
  • 初始代码中在打印循环后添加Serial.println();会出现乱码和大量换行
  • 修改后的代码虽能运行,但存在随机字符丢失,比如输入"qwerty"有时输出qwety、qwery、qwert或qert

初始代码

struct arrayData{
    char* array;
    int size;
};

void setup() {
    // put your setup code here, to run once:
    Serial.begin(9600);
}

void loop() {
  // put your main code here, to run repeatedly:

    //Create struct
    struct arrayData* myStruct = (arrayData*) errorHandling(malloc(sizeof(arrayData)));
    if(myStruct == NULL) return;
    myStruct -> size = 0;
    myStruct -> array = (char*) errorHandling(malloc(sizeof(char)));
    if(myStruct -> array == NULL) return;
    myStruct -> array[0] = '\0';
    // Read serial input buffer
    myStruct = readBuffer(myStruct);
    if(myStruct == NULL) return;
 
    // Print each byte
    for(int i = 0;i < myStruct -> size;i++){
        Serial.print(myStruct -> array[i]);
    }
    
    // Free
    free(myStruct -> array);
    myStruct -> array = NULL;
    free(myStruct);
    myStruct = NULL;
    
}

arrayData* readBuffer(arrayData* arrayMeta){
    // NOT RECOMMENDED!! abstracted malloc may cause mem leak
    // If array is NULL create it
    if(arrayMeta -> array == NULL){
        arrayMeta -> array = (char*) errorHandling(malloc(sizeof(char)));
        if(arrayMeta -> array == NULL) return NULL;
        arrayMeta -> size = 1;
    }
    // If Serial input buffer is not empty read
    if(Serial.available() > 0){
        char byte = Serial.read();
        int x = 0;
        // While buffer is not empty and we have not recieved end of transmission
        while(byte != '\n' && Serial.available() > 0){
        
            // Make array the right size
            arrayMeta -> array = (char*) errorHandling(realloc(arrayMeta -> array, (x+1) * sizeof(char)));
            if(arrayMeta -> array == NULL) return NULL;
            // Update the size member of struct
            arrayMeta -> size = x+1;
            // Write into array
            arrayMeta -> array[x] = byte;
            // Read next byte
            byte = Serial.read();
            x++;
        }
    }
    return arrayMeta;
}

// Return ptr on success and NULL on failure
void* errorHandling(void* ptr){
    if(ptr == NULL){
        Serial.println("Malloc failed");
        return NULL;
    }
    return ptr;
}

修改后代码

#define PERFORMANCE 10

struct arrayData{
    char* array;
    int size;
};
 struct arrayData* myStruct;

void setup() {
    // put your setup code here, to run once:
    Serial.begin(9600);
    //Create struct
    myStruct = (arrayData*) errorHandling(malloc(sizeof(arrayData)),'0');
    if(myStruct == NULL) return;
    myStruct->size = 0;
    myStruct->array= NULL;
}

void loop() {
  // put your main code here, to run repeatedly:
    
    // Read serial input buffer
    myStruct = readBuffer(myStruct);
    if(myStruct == NULL) return;
 
    // Print each byte
    if(myStruct->array != NULL){
        for(int i = 0;i < myStruct->size;i++){
            Serial.print(myStruct->array[i]);
        }
    }
    
    // Free
    free(myStruct->array);
    myStruct->array = NULL;
}

arrayData* readBuffer(arrayData* arrayMeta){
    // NOT RECOMMENDED!! abstracted malloc may cause mem leak
    // If array is NULL create it
    if(arrayMeta->array == NULL && Serial.available() > 0){
        arrayMeta->array = (char*) errorHandling(malloc(sizeof(char)),'2');
        if(arrayMeta->array == NULL) return NULL;
        arrayMeta->size = 1;
    }
    // If Serial input buffer is not empty read
    if(Serial.available() > 0){
        char byte;
        int x = 0;
        // While buffer is not empty and we have not recieved end of transmission
        while(Serial.available() > 0){
            // Read next byte
            byte = Serial.read();
            if (byte == '\n') break;
            // Make array the right size
            if(arrayMeta->size == x){
                arrayMeta->array = (char*) errorHandling(realloc(arrayMeta->array, (x  + PERFORMANCE) * sizeof(char)),'3');
                if(arrayMeta->array == NULL) return NULL;
                // Update the size member of struct
                arrayMeta->size++;
            }
            // Write into array
            arrayMeta->array[x] = byte;

            x++;
        }
        if(arrayMeta->size != x){
                arrayMeta->array = (char*) errorHandling(realloc(arrayMeta->array, (x) * sizeof(char)),'4');
                if(arrayMeta->array == NULL) return NULL;
                // Update the size member of struct
                arrayMeta->size = x;
            }
    }
    return arrayMeta;
}

// Return ptr on success and NULL on failure
void* errorHandling(void* ptr, char error){
    if(ptr == NULL){
        Serial.println("Malloc failed, error:");
        Serial.println(error);
        return NULL;
    }
    return ptr;
}

问题根源与修复方案

1. 初始代码核心问题

  • 串口读取逻辑漏洞:先读取一个字节再进入循环,若最后一个字节是\n或缓冲区读完,该字节会被丢弃;循环依赖Serial.available(),字节分批到达时会提前终止读取,导致数据拆分。
  • 频繁内存操作:每次loop都创建并释放结构体,小内存环境下易产生碎片,且readBuffer返回NULL时会泄漏结构体内存。

2. 修改后代码字符丢失原因

  • 扩容逻辑错误:扩容时只将size加1,后续x增长超过size时无法及时扩容,导致字符无法写入。
  • 未处理完整数据:读取到\n就终止循环,剩余字节留到下次loop读取,导致数据拆分输出。

修复后的完整代码

#define BUFFER_INIT_SIZE 32  // 初始缓冲区大小,减少realloc次数

struct arrayData{
    char* array;
    int size;       // 当前存储的字符数
    int capacity;   // 缓冲区总容量
};

struct arrayData* myStruct;

void setup() {
    Serial.begin(9600);
    // 初始化结构体
    myStruct = (arrayData*)malloc(sizeof(arrayData));
    if(myStruct == NULL){
        Serial.println("结构体内存分配失败");
        while(1); // 卡死避免后续错误
    }
    myStruct->size = 0;
    myStruct->capacity = BUFFER_INIT_SIZE;
    myStruct->array = (char*)malloc(BUFFER_INIT_SIZE * sizeof(char));
    if(myStruct->array == NULL){
        Serial.println("缓冲区内存分配失败");
        free(myStruct);
        while(1);
    }
}

void loop() {
    // 读取直到换行符
    while(Serial.available()){
        char byte = Serial.read();
        if(byte == '\n'){
            // 读取到换行,输出数据
            myStruct->array[myStruct->size] = '\0'; // 添加字符串结束符
            Serial.println(myStruct->array);
            // 重置缓冲区,准备下次读取
            myStruct->size = 0;
            break;
        }
        // 检查缓冲区是否已满,需要扩容
        if(myStruct->size >= myStruct->capacity - 1){ // 留一个位置给'\0'
            myStruct->capacity *= 2; // 容量翻倍
            char* temp = (char*)realloc(myStruct->array, myStruct->capacity * sizeof(char));
            if(temp == NULL){
                Serial.println("缓冲区扩容失败");
                free(myStruct->array);
                free(myStruct);
                while(1);
            }
            myStruct->array = temp;
        }
        // 写入字符
        myStruct->array[myStruct->size++] = byte;
    }
}

// 程序结束时释放内存(可选,Arduino断电自动释放)
void end(){
    free(myStruct->array);
    free(myStruct);
}

修复说明

  • 简化内存管理:仅在setup中分配一次结构体和缓冲区,避免频繁malloc/free,减少内存碎片和泄漏风险。
  • 完整读取逻辑:循环读取所有串口字节,直到遇到\n才停止,确保一次读取完整一行数据。
  • 合理扩容策略:缓冲区不足时翻倍扩容,减少realloc调用次数,同时预留字符串结束符位置,避免乱码。
  • 避免字符丢失:每读取一个有效字符就检查缓冲区容量,确保有足够空间存储。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.22 08:37:35