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
相关产品推荐
相关产品推荐

