7

我在 Go 中编写了一个简短的程序,通过串行端口与传感器通信:

package main

import (
    "fmt"
    "github.com/tarm/goserial"
    "time"
)

func main() {
    c := &serial.Config{Name: "/dev/ttyUSB0", Baud: 9600}
    s, err := serial.OpenPort(c)

    if err != nil {
            fmt.Println(err)
    }

    _, err = s.Write([]byte("\x16\x02N0C0 G A\x03\x0d\x0a"))

    if err != nil {
            fmt.Println(err)
    }

    time.Sleep(time.Second/2)

    buf := make([]byte, 40)
    n, err := s.Read(buf)

    if err != nil {
            fmt.Println(err)
    }

    fmt.Println(string(buf[:n]))

    s.Close()
}

它工作正常,但在写入端口后,我必须等待大约半秒才能开始读取它。我想使用 while 循环而不是time.Sleep读取所有传入数据。我的尝试不起作用:

buf := make([]byte, 40)
n := 0

for {
    n, _ := s.Read(buf)

    if n > 0 {
        break
    }
}

fmt.Println(string(buf[:n]))

我猜buf每次循环通过后都会被覆盖。有什么建议么?

4

2 回答 2

11

您的问题是Read()只要有一些数据就会返回 - 它不会等待所有数据。有关更多信息,请参阅io.Reader 规范

你想要做的是阅读,直到你到达一些分隔符。我不确切知道您要使用什么格式,但看起来可能\x0a是结束分隔符。

在这种情况下,您将使用这样的bufio.Reader

reader := bufio.NewReader(s)
reply, err := reader.ReadBytes('\x0a')
if err != nil {
    panic(err)
}
fmt.Println(reply)

它将读取数据直到第一个\x0a.

于 2013-07-11T18:46:22.243 回答
1

我猜 buf 在每次循环通过后都会被覆盖。有什么建议么?

是的,buf每次调用都会被覆盖Read()

文件句柄超时将是我将采取的方法。

s, _ := os.OpenFile("/dev/ttyS0", syscall.O_RDWR|syscall.O_NOCTTY|syscall.O_NONBLOCK, 0666)

t := syscall.Termios{
    Iflag:  syscall.IGNPAR,
    Cflag:  syscall.CS8 | syscall.CREAD | syscall.CLOCAL | syscall.B115200,
    Cc:     [32]uint8{syscall.VMIN: 0, syscall.VTIME: uint8(20)}, //2.0s timeout
    Ispeed: syscall.B115200,
    Ospeed: syscall.B115200,
}

// syscall
syscall.Syscall6(syscall.SYS_IOCTL, uintptr(s.Fd()),
    uintptr(syscall.TCSETS), uintptr(unsafe.Pointer(&t)),
    0, 0, 0)

// Send message
n, _ := s.Write([]byte("Test message"))

// Receive reply
for {
    buf := make([]byte, 128)
    n, err = s.Read(buf)
    if err != nil { // err will equal io.EOF
        break
    }
    fmt.Printf("%v\n", string(buf))
}

另请注意,如果没有更多数据读取并且没有错误,则 os.File.Read() 将返回错误io.EOF如您在此处看到的。

于 2016-04-06T20:36:43.617 回答