Reading from serial port with while-loop

go
14,047

Solution 1

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.

Solution 2

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.

Share:
14,047

Related videos on Youtube

laserbrain
Author by

laserbrain

Updated on September 14, 2022

Comments

  • laserbrain
    laserbrain over 1 year

    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?