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

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

Reading from serial port with while-loop

问题

我用Go语言编写了一个简短的程序,通过串口与传感器进行通信:

  1. package main
  2. import (
  3. "fmt"
  4. "github.com/tarm/goserial"
  5. "time"
  6. )
  7. func main() {
  8. c := &serial.Config{Name: "/dev/ttyUSB0", Baud: 9600}
  9. s, err := serial.OpenPort(c)
  10. if err != nil {
  11. fmt.Println(err)
  12. }
  13. _, err = s.Write([]byte("\x16\x02N0C0 G A\x03\x0d\x0a"))
  14. if err != nil {
  15. fmt.Println(err)
  16. }
  17. time.Sleep(time.Second/2)
  18. buf := make([]byte, 40)
  19. n, err := s.Read(buf)
  20. if err != nil {
  21. fmt.Println(err)
  22. }
  23. fmt.Println(string(buf[:n]))
  24. s.Close()
  25. }

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

  1. buf := make([]byte, 40)
  2. n := 0
  3. for {
  4. n, _ := s.Read(buf)
  5. if n > 0 {
  6. break
  7. }
  8. }
  9. fmt.Println(string(buf[:n]))

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

英文:

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

  1. package main
  2. import (
  3. "fmt"
  4. "github.com/tarm/goserial"
  5. "time"
  6. )
  7. func main() {
  8. c := &serial.Config{Name: "/dev/ttyUSB0", Baud: 9600}
  9. s, err := serial.OpenPort(c)
  10. if err != nil {
  11. fmt.Println(err)
  12. }
  13. _, err = s.Write([]byte("\x16\x02N0C0 G A\x03\x0d\x0a"))
  14. if err != nil {
  15. fmt.Println(err)
  16. }
  17. time.Sleep(time.Second/2)
  18. buf := make([]byte, 40)
  19. n, err := s.Read(buf)
  20. if err != nil {
  21. fmt.Println(err)
  22. }
  23. fmt.Println(string(buf[:n]))
  24. s.Close()
  25. }

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:

  1. buf := make([]byte, 40)
  2. n := 0
  3. for {
  4. n, _ := s.Read(buf)
  5. if n > 0 {
  6. break
  7. }
  8. }
  9. fmt.Println(string(buf[:n]))

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

答案1

得分: 11

你的问题是Read()会在有一些数据时返回,它不会等待所有的数据。请参考io.Reader规范获取更多信息。

你想要做的是读取直到达到某个分隔符。我不知道你尝试使用的确切格式,但看起来可能\x0a是结束分隔符。

在这种情况下,你可以使用bufio.Reader,像这样:

  1. reader := bufio.NewReader(s)
  2. reply, err := reader.ReadBytes('\x0a')
  3. if err != nil {
  4. panic(err)
  5. }
  6. 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

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

Which will read data until the first \x0a.

答案2

得分: 1

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

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

  1. s, _ := os.OpenFile("/dev/ttyS0", syscall.O_RDWR|syscall.O_NOCTTY|syscall.O_NONBLOCK, 0666)
  2. t := syscall.Termios{
  3. Iflag: syscall.IGNPAR,
  4. Cflag: syscall.CS8 | syscall.CREAD | syscall.CLOCAL | syscall.B115200,
  5. Cc: [32]uint8{syscall.VMIN: 0, syscall.VTIME: uint8(20)}, //2.0s timeout
  6. Ispeed: syscall.B115200,
  7. Ospeed: syscall.B115200,
  8. }
  9. // syscall
  10. syscall.Syscall6(syscall.SYS_IOCTL, uintptr(s.Fd()),
  11. uintptr(syscall.TCSETS), uintptr(unsafe.Pointer(&t)),
  12. 0, 0, 0)
  13. // 发送消息
  14. n, _ := s.Write([]byte("Test message"))
  15. // 接收回复
  16. for {
  17. buf := make([]byte, 128)
  18. n, err = s.Read(buf)
  19. if err != nil { // err will equal io.EOF
  20. break
  21. }
  22. fmt.Printf("%v\n", string(buf))
  23. }

另外请注意,如果没有更多数据可读且没有错误,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.

  1. s, _ := os.OpenFile("/dev/ttyS0", syscall.O_RDWR|syscall.O_NOCTTY|syscall.O_NONBLOCK, 0666)
  2. t := syscall.Termios{
  3. Iflag: syscall.IGNPAR,
  4. Cflag: syscall.CS8 | syscall.CREAD | syscall.CLOCAL | syscall.B115200,
  5. Cc: [32]uint8{syscall.VMIN: 0, syscall.VTIME: uint8(20)}, //2.0s timeout
  6. Ispeed: syscall.B115200,
  7. Ospeed: syscall.B115200,
  8. }
  9. // syscall
  10. syscall.Syscall6(syscall.SYS_IOCTL, uintptr(s.Fd()),
  11. uintptr(syscall.TCSETS), uintptr(unsafe.Pointer(&t)),
  12. 0, 0, 0)
  13. // Send message
  14. n, _ := s.Write([]byte("Test message"))
  15. // Receive reply
  16. for {
  17. buf := make([]byte, 128)
  18. n, err = s.Read(buf)
  19. if err != nil { // err will equal io.EOF
  20. break
  21. }
  22. fmt.Printf("%v\n", string(buf))
  23. }

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:

确定