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

R中serial::read.serialConnection()返回空值问题求助

R serial包read.serialConnection读取ESP8266串口返回空值的问题及解决方案

问题背景

  • 长期使用serial::read.serialConnection()读取USB连接的ESP8266串口输出,2024年6月26日起在两台Windows 11设备上均返回空值
  • Arduino软件、Python脚本读取同一串口均正常,排除硬件及串口本身故障
  • 重装R及serial包后问题仍存在
  • serial::nBytesInQueue()显示缓冲区数据正常增长,但直接执行tcltk::.Tcl("gets ${sdev_COM7}")返回空值

原运行代码:

library(serial)
con <- serialConnection(port = "COM7"
                        ,mode = "115200,n,8,1"
                        ,newline = 1
                        ,translation = "crlf"
)
open(con)
read.serialConnection(con, n = 0)

临时修复(需修改包内函数)

查看read.serialConnection源码,将第26行的:

comStr <- paste("gets ${sdev_", con$var, "}", sep = "")

替换为:

comStr <- paste("read ${sdev_", con$var, "}", sep = "")

修改后函数可恢复正常,但该操作需要修改包内部代码,不适合低R经验用户。

无需修改包函数的替代解决方案

方案1:指定读取字节数并自行处理换行

放弃n=0(默认读取到换行符)的调用方式,指定具体读取字节数,再手动分割行数据:

library(serial)
con <- serialConnection(port = "COM7"
                        ,mode = "115200,n,8,1"
                        ,translation = "crlf"
)
open(con)

# 循环读取并处理行数据
buffer <- ""
while(TRUE) {
  # 每次读取64字节
  chunk <- read.serialConnection(con, n = 64)
  if(nchar(chunk) > 0) {
    buffer <- paste0(buffer, chunk)
    # 按CRLF分割行
    lines <- strsplit(buffer, "\r\n")[[1]]
    if(length(lines) > 1) {
      # 输出完整行,保留未完成的部分到缓冲区
      for(line in lines[-length(lines)]) {
        print(line)
      }
      buffer <- lines[length(lines)]
    }
  }
  Sys.sleep(0.01)
}

方案2:自定义读取函数调用Tcl的read命令

封装一个替代读取函数,直接调用Tcl的read命令而非原函数的gets命令:

library(serial)
library(tcltk)

read_serial_fix <- function(con, n = 0) {
  if(n == 0) {
    # 读取缓冲区所有可用数据
    com_str <- paste("read ${sdev_", con$var, "}", sep = "")
  } else {
    # 读取指定字节数
    com_str <- paste("read ${sdev_", con$var, "}", n, sep = " ")
  }
  result <- .Tcl(com_str)
  # 按原连接配置转换换行符
  if(con$translation == "crlf") {
    result <- gsub("\r\n", "\n", result)
  }
  return(result)
}

# 使用示例
con <- serialConnection(port = "COM7"
                        ,mode = "115200,n,8,1"
                        ,translation = "crlf"
)
open(con)
# 读取所有可用数据
read_serial_fix(con)

方案3:切换至其他串口包

改用uart包(底层实现不依赖Tcl/Tk),避免原serial包的Tcl命令兼容问题:

install.packages("uart")
library(uart)

# 打开串口
con <- uart_open("COM7", baudrate = 115200, parity = "none", databits = 8, stopbits = 1)
# 读取所有可用数据
data <- uart_read(con, n = 0)
print(data)
# 关闭连接
uart_close(con)

问题根源推测

原函数使用Tcl的gets命令读取串口,该命令会等待换行符才返回数据。可能由于Tcl版本更新、ESP8266输出的换行符格式细微变化,导致gets无法识别换行符,一直处于等待状态而返回空值;而Tcl的read命令会直接读取缓冲区所有可用数据,不受换行符限制,因此能正常返回结果。

内容的提问来源于stack exchange,提问作者Gabriel Silva Pires

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.21 18:22:37