Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 0254cc1efa | |||
| f10848f47f |
@@ -163,6 +163,8 @@ const (
|
||||
ErrorEnumeratingPorts
|
||||
// PortClosed the port has been closed while the operation is in progress
|
||||
PortClosed
|
||||
// Timeout the read operation timed out
|
||||
Timeout
|
||||
// FunctionNotImplemented the requested function is not implemented
|
||||
FunctionNotImplemented
|
||||
)
|
||||
@@ -192,6 +194,8 @@ func (e PortError) EncodedErrorString() string {
|
||||
return "Could not enumerate serial ports"
|
||||
case PortClosed:
|
||||
return "Port has been closed"
|
||||
case Timeout:
|
||||
return "Read timeout"
|
||||
case FunctionNotImplemented:
|
||||
return "Function not implemented"
|
||||
default:
|
||||
|
||||
@@ -119,7 +119,8 @@ func (port *unixPort) Read(p []byte) (int, error) {
|
||||
if port.readTimeout == NoTimeout {
|
||||
continue // 无超时设置,继续等待
|
||||
}
|
||||
return 0, nil // 返回0字节表示超时
|
||||
// 超时应返回 Timeout 错误,而不是 (0, nil)
|
||||
return 0, &PortError{code: Timeout, causedBy: unix.ETIMEDOUT}
|
||||
}
|
||||
|
||||
n, err := unix.Read(port.handle, p)
|
||||
@@ -352,7 +353,9 @@ func nativeOpen(portName string, mode *Mode) (*unixPort, error) {
|
||||
return nil, &PortError{code: InvalidSerialPort, causedBy: fmt.Errorf("error configuring port: %w", err)}
|
||||
}
|
||||
|
||||
unix.SetNonblock(h, false)
|
||||
// Set non-blocking mode to ensure read() doesn't block after select() returns
|
||||
// The select() call provides the timeout mechanism, not the read() call
|
||||
unix.SetNonblock(h, true)
|
||||
|
||||
port.acquireExclusiveAccess()
|
||||
|
||||
@@ -513,7 +516,14 @@ func setRawMode(settings *unix.Termios) {
|
||||
settings.Oflag &^= unix.OPOST
|
||||
|
||||
// Block reads until at least one char is available (no timeout)
|
||||
settings.Cc[unix.VMIN] = 1
|
||||
// VMIN=1: Block until at least 1 byte is available
|
||||
// VTIME=0: No inter-character timeout (not used with VMIN=1)
|
||||
//
|
||||
// NOTE: When readTimeout is set, the select() system call provides the timeout
|
||||
// and VMIN/VTIME settings are not used for timeout purposes.
|
||||
// However, VMIN=1 can cause issues with select() on some platforms/
|
||||
// terminal drivers. Using VMIN=0 allows select() to work reliably.
|
||||
settings.Cc[unix.VMIN] = 0
|
||||
settings.Cc[unix.VTIME] = 0
|
||||
}
|
||||
|
||||
|
||||
95
test/goserial/main.go
Normal file
95
test/goserial/main.go
Normal file
@@ -0,0 +1,95 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"log"
|
||||
"time"
|
||||
|
||||
"go.bug.st/serial"
|
||||
)
|
||||
|
||||
const (
|
||||
portName = "/dev/ttyS9"
|
||||
baudRate = 115200
|
||||
timeout = 5 * time.Second
|
||||
reopenDelay = 500 * time.Millisecond
|
||||
)
|
||||
|
||||
func main() {
|
||||
log.Println("goserial test tool started")
|
||||
log.Printf("configuration: port=%s baud=%d", portName, baudRate)
|
||||
|
||||
for {
|
||||
if err := runTest(); err != nil {
|
||||
log.Printf("test run failed, will reopen port after delay: %v", err)
|
||||
time.Sleep(reopenDelay)
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func runTest() error {
|
||||
log.Println("opening serial port")
|
||||
|
||||
// 打开端口
|
||||
port, err := serial.Open(portName, &serial.Mode{
|
||||
BaudRate: baudRate,
|
||||
})
|
||||
if err != nil {
|
||||
log.Printf("failed to open serial port %s: %v", portName, err)
|
||||
return err
|
||||
}
|
||||
defer func() {
|
||||
log.Println("closing serial port")
|
||||
if err := port.Close(); err != nil {
|
||||
log.Printf("failed to close serial port: %v", err)
|
||||
}
|
||||
}()
|
||||
|
||||
// 设置读取超时
|
||||
if err := port.SetReadTimeout(timeout); err != nil {
|
||||
log.Printf("failed to set read timeout: %v", err)
|
||||
return err
|
||||
}
|
||||
|
||||
log.Printf("read timeout set: %v", timeout)
|
||||
log.Println("start reading from serial port (synchronous mode)")
|
||||
|
||||
// 创建缓冲区用于读取数据
|
||||
buffer := make([]byte, 24)
|
||||
|
||||
// 同步读取循环
|
||||
for {
|
||||
n, err := port.Read(buffer)
|
||||
if err != nil {
|
||||
// 检查是否是超时错误
|
||||
if err.Error() == "i/o timeout" {
|
||||
log.Println("serial port read timeout occurred, exiting read loop")
|
||||
// 超时后退出循环,重新打开端口
|
||||
break
|
||||
}
|
||||
|
||||
// 使用类型断言检查是否是PortError
|
||||
var portErr *serial.PortError
|
||||
if errors.As(err, &portErr) {
|
||||
// 检查错误代码是否为PortClosed
|
||||
if portErr.Code() == serial.PortClosed {
|
||||
log.Println("port closed, exiting read loop")
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// 其他错误
|
||||
log.Printf("error reading from serial port: %v", err)
|
||||
return err
|
||||
}
|
||||
|
||||
if n > 0 {
|
||||
log.Printf("data received: bytes=%d data=%v", n, buffer[:n])
|
||||
}
|
||||
}
|
||||
|
||||
// 正常退出读取循环
|
||||
log.Println("read loop exited normally")
|
||||
return nil
|
||||
}
|
||||
Reference in New Issue
Block a user