Linux C++串口设备读取问题:read()无数据时未返回0
Arduino与树莓派串口通信中read()函数无数据时不返回0的问题
我在实现Arduino和树莓派之间的数据传输,其他功能都正常,但read(usb, &buf, 1);在没有可用数据时始终不返回0。根据函数说明:
Read NBYTES into BUF from FD. Return the number read, -1 for errors or 0 for EOF.
但实际运行中从未出现返回0的情况。
Arduino端代码
#include <Arduino.h> bool blinking = false; bool led_on = false; int target_time; void setup() { Serial.begin(115200); while (!Serial) { ; // wait for serial port to connect. Needed for native USB } pinMode(LED_BUILTIN, OUTPUT); } void loop() { char c; if (Serial.available() > 0) { c = Serial.read(); switch (c) { case '0': // stop blinking blinking = false; if (led_on) { digitalWrite(LED_BUILTIN, LOW); } Serial.write("A", 1); break; case '1': // start blinking if (blinking == false) { blinking = true; digitalWrite(LED_BUILTIN, HIGH); led_on = true; target_time = millis() + 100; // turn off in 1 tenth of a second (100 milliseconds) } Serial.write("ABC\0", 4); break; default: Serial.write("E", 1); break; } } else if (blinking) { if (millis() >= target_time) { if (led_on) { digitalWrite(LED_BUILTIN, LOW); led_on = false; target_time = millis() + 100; // turn on in 1 tenth of a second (100 milliseconds) } else { digitalWrite(LED_BUILTIN, HIGH); led_on = true; target_time = millis() + 100; // turn off in 1 tenth of a second (100 milliseconds) } } } }
树莓派端相关函数代码
int readWhole(int usb, char *response){ int n = 0, spot = 0; char buf = '\0'; memset(response, '\0', sizeof response); while (1) { cout << "read\n"; n = read(usb, &buf, 1); cout << "n = " << n << "\n"; usleep(100 * 1000); if (n == -1) { if (errno != EAGAIN) { fprintf(stderr, "read() failed: (%d) %s\n", errno, strerror(errno)); return -1; } // errno == EAGAIN, loop around and read again } else { if (n == 1) { sprintf( &response[spot], "%c", buf ); spot += n; cout << "got something: " << buf << "\n"; }else{ // 0 == End Of File break; } // read 0 bytes, loop around an read again } } return spot; }
实际输出结果
read n = -1 read n = 1 got something: A read n = 1 got something: B read n = 1 got something: C read n = 1 got something: read n = -1 read n = -1 read
问题原因
- EOF的触发逻辑:
read()返回0的唯一场景是串口连接被彻底关闭(比如Arduino断电、USB物理断开),而非当前无数据可读。如果你的串口是设置为非阻塞模式,无数据时read()会返回-1并将errno设为EAGAIN,这正是你看到的输出。 - 无效字符干扰:Arduino代码中发送了
"ABC\0",其中的空字符\0会被树莓派正常读取,所以出现了got something:的空输出,这并不是EOF。
解决方法
方法1:通过超时判定无数据状态
利用EAGAIN的特性,设置连续多次读取都返回EAGAIN时,判定为当前没有更多数据,主动退出循环:
int readWhole(int usb, char *response){ int n = 0, spot = 0; char buf = '\0'; int retry_count = 0; const int MAX_RETRY = 3; // 连续3次无数据则停止读取 memset(response, '\0', sizeof(response)); while (1) { cout << "read\n"; n = read(usb, &buf, 1); cout << "n = " << n << "\n"; usleep(100 * 1000); if (n == -1) { if (errno != EAGAIN) { fprintf(stderr, "read() failed: (%d) %s\n", errno, strerror(errno)); return -1; } retry_count++; if (retry_count >= MAX_RETRY) { break; // 连续无数据,退出循环 } } else { retry_count = 0; // 读到数据重置计数器 if (n == 1) { // 可选:过滤掉空字符,避免无效存储 if (buf != '\0') { response[spot] = buf; spot += 1; cout << "got something: " << buf << "\n"; } }else{ break; // 仅连接关闭时会触发 } } } return spot; }
方法2:改用明确的结束符协议
修改Arduino的输出逻辑,不发送空字符,改用换行符\n作为数据结束标记,树莓派读到结束符就停止读取:
- Arduino端修改发送代码:
// 替换原Serial.write("ABC\0", 4); Serial.write("ABC\n", 4); // 其他发送也统一加结束符:Serial.write("A\n", 2);
- 树莓派端修改读取逻辑:
int readWhole(int usb, char *response){ int n = 0, spot = 0; char buf = '\0'; memset(response, '\0', sizeof(response)); while (1) { cout << "read\n"; n = read(usb, &buf, 1); cout << "n = " << n << "\n"; usleep(100 * 1000); if (n == -1) { if (errno != EAGAIN) { fprintf(stderr, "read() failed: (%d) %s\n", errno, strerror(errno)); return -1; } } else { if (n == 1) { if (buf == '\n') { break; // 读到结束符,停止读取 } response[spot] = buf; spot += 1; cout << "got something: " << buf << "\n"; }else{ break; } } } return spot; }
方法3:切换为阻塞模式读取
如果程序不需要同时处理其他任务,可以在打开串口时设置为阻塞模式(默认可能为阻塞),此时read()会一直等待直到有数据到来,但无数据时会卡住程序,适合简单场景。
内容的提问来源于stack exchange,提问作者Darter
相关产品推荐
相关产品推荐

