使用while循环从串口读取数据

huangapple go评论78阅读模式
英文:

Reading from serial port with while-loop

问题

我用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都会被覆盖。有什么建议吗?

英文:

I’ve written a short program in Go to communicate with a sensor through a serial port:

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()
}

It works fine, but after writing to the port I have to wait about half a second before I can start reading from it. I would like to use a while-loop instead of time.Sleep to read all incoming data. My attempt doesn’t work:

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

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

    if n > 0 {
        break
    }
}

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

I guess buf gets overwritten after every loop pass. Any suggestions?

答案1

得分: 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

英文:

Your problem is that Read() will return whenever it has some data - it won't wait for all the data. See the io.Reader specification for more info

What you want to do is read until you reach some delimiter. I don't know exactly what format you are trying to use, but it looks like maybe \x0a is the end delimiter.

In which case you would use a bufio.Reader like this

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

Which will read data until the first \x0a.

答案2

得分: 1

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

我会采用在文件句柄上设置超时的方法。

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)

// 发送消息
n, _ := s.Write([]byte("Test message"))

// 接收回复
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的错误,
你可以在这里看到。

英文:

> I guess buf gets overwritten after every loop pass. Any suggestions?

Yes, buf will get overwritten with every call to Read().

A timeout on the file handle would be the approach I would take.

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))
}

Also note, if there is no more data read and there is no error, os.File.Read() will return an error of io.EOF,
as you can see here.

huangapple
  • 本文由 发表于 2013年7月12日 00:56:52
  • 转载请务必保留本文链接:https://go.coder-hub.com/17599232.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定