Rust serialport库如何使用read_line函数及解决读取超时问题
问题1:read_to_end/read_to_string超时的原因
- 串口是持续开放的字节流,本身不存在EOF(流结束标记):只有当串口端口被主动关闭时,才会返回EOF信号。你发送的空终止符
\0只是业务层面约定的字符串结束标记,和Rust标准库中read_to_end/read_to_string要求的EOF完全是两个概念。 - 这两个方法会持续阻塞读取直到收到EOF才停止,而你的串口端口始终处于开启状态,自然会一直读到触发串口配置的超时时间返回错误。
问题2:使用BufRead实现read_line/自定义结束标记读取
完全可以借助BufRead trait实现按行/按自定义结束标记读取,serialport库的串口类型已经实现了Read trait,直接用标准库的BufReader包装即可使用BufRead的所有方法:
- 如果你需要按
\n换行符读取行,直接调用read_line方法即可 - 如果你是要按你发送的空终止符
\0读取内容,调用read_until(0, &mut buf)方法即可
参考实现代码:
use std::io::{BufRead, BufReader}; use serialport::SerialPort; fn main() -> Result<(), Box<dyn std::error::Error>> { // 打开串口,注意配置合适的波特率、超时时间,建议超时设置略大于你预期的单帧数据最大间隔 let mut port = serialport::new("/dev/ttyUSB0", 115200) .timeout(std::time::Duration::from_millis(500)) .open()?; // 用BufReader包装串口实例 let mut reader = BufReader::new(port); let mut line_buf = String::new(); // 按换行符读取一行 match reader.read_line(&mut line_buf) { Ok(n) => println!("读取到{}字节的行内容:{}", n, line_buf.trim_end()), Err(e) if e.kind() == std::io::ErrorKind::TimedOut => { // 超时属于正常情况,说明指定时间内没有收到完整的一行,按需处理即可 println!("读取超时,无新行数据"); } Err(e) => return Err(e.into()), } // 如果要按空终止符读取,使用read_until let mut null_buf = Vec::new(); match reader.read_until(0, &mut null_buf) { Ok(n) => { // 去掉末尾的空终止符再转字符串 let content = String::from_utf8_lossy(&null_buf[..n-1]); println!("读取到空终止符结尾的内容:{}", content); } Err(e) if e.kind() == std::io::ErrorKind::TimedOut => { println!("读取超时,无空终止符结尾数据"); } Err(e) => return Err(e.into()), } Ok(()) }
注意事项
- 串口的超时时间要合理配置:不能太短导致没传完一帧就超时,也不要太长导致程序响应太慢,建议设置为单帧数据最大传输耗时的1.5~2倍
- 如果BufReader的读缓存区有残留数据,下一次读取会先读缓存区的内容,符合正常的流读取逻辑,不需要额外处理
- 如果你需要同时读写串口,可以用
try_clone()方法克隆串口实例,一个给BufReader做读取,另一个用来写数据,避免所有权冲突
内容的提问来源于stack exchange,提问作者andber1
相关产品推荐
相关产品推荐

