Open biohazduck opened 1 year ago
Big upvote for this here. I am implementing a driver for a laboratory balance, facing EXACTLY the same issue as above.
Please provide the source code to reproduce the issue.
@Darkskald The way I ended up solving this issue is by adding a mutex to the balance struct and making sure I write once every 1 second minimum. Writing faster will trigger the amok state (though have not tested at what frequency the bug pops up).
I am not using a read timeout, but am wrapping the serial device with a cereal.NonBlocking
to not block on a failed message, which happens often.
The code to reproduce would look somewhat like
mode := &serial.Mode{
BaudRate: 1200,
Parity: serial.NoParity,
DataBits: 8,
StopBits: serial.OneStopBit,
}
port, _ := serial.Open(ports[0], mode) // Got port from listports
var buffer [1024]byte
cmd := []byte("D05\r\n") // Read scale command.
port.Write(cmd)
n, _ :=port.Read(buffer[:])
port.Write(cmd) // Two fast writes should be enough to trigger error
n, _ =port.Read(buffer[:])
port.Write(cmd)
n, _ =port.Read(buffer[:])
port.Write(cmd)
n, _ =port.Read(buffer[:])
fmt.Println(buffer[:n]) // Get newlines?
It could be that you get partial Reads. May you try to print the results of all Read operations?
if err := port.Write(cmd); err != nil {
fmt.Println("Error writing to port: ", err)
}
n, err := port.Read(buffer[:])
fmt.Printf("READ: n=%d err=%v data:>%s<", n, err, buffer[:n])
// The content of the buffer[:n] here may be a partial response
// You may need to read again to get the remainder of the full response
if err := port.Write(cmd); err != nil {
fmt.Println("Error writing to port: ", err)
}
n, err = port.Read(buffer[:])
fmt.Printf("READ: n=%d err=%v data:>%s<", n, err, buffer[:n])
if err := port.Write(cmd); err != nil {
fmt.Println("Error writing to port: ", err)
}
n, err = port.Read(buffer[:])
fmt.Printf("READ: n=%d err=%v data:>%s<", n, err, buffer[:n])
if err := port.Write(cmd); err != nil {
fmt.Println("Error writing to port: ", err)
}
n, err := port.Read(buffer[:])
fmt.Printf("READ: n=%d err=%v data:>%s<", n, err, buffer[:n])
fmt.Println(buffer[:n])
The partial reads are not material to the issue of newline spewing. The issue I'm having is that I must be extremely careful when using bugst compared to cutecom and pyserial since writing too frequently with bugst will cause the scale to irrecoverably go into a state where all it does is write newlines until I unplug and plug again.
Normally I'd attribute this to bad firmware or hardware... but I then observed cutecom and pyserial never run into the issue so I began wondering if it was a problem in how linux port is handled by bugst.
On Sun, Feb 18, 2024, 13:16 Cristian Maglie @.***> wrote:
It could be that you get partial Reads. May you try to print the results of all Read operations?
if err := port.Write(cmd); err != nil { fmt.Println("Error writing to port: ", err) } n, err := port.Read(buffer[:]) fmt.Printf("READ: n=%d err=%v data:>%s<", n, err, buffer[:n]) // The content of the buffer[:n] here may be a partial response // You may need to read again to get the remainder of the full response
if err := port.Write(cmd); err != nil { fmt.Println("Error writing to port: ", err) } n, err = port.Read(buffer[:]) fmt.Printf("READ: n=%d err=%v data:>%s<", n, err, buffer[:n])
if err := port.Write(cmd); err != nil { fmt.Println("Error writing to port: ", err) } n, err = port.Read(buffer[:]) fmt.Printf("READ: n=%d err=%v data:>%s<", n, err, buffer[:n])
if err := port.Write(cmd); err != nil { fmt.Println("Error writing to port: ", err) } n, err := port.Read(buffer[:]) fmt.Printf("READ: n=%d err=%v data:>%s<", n, err, buffer[:n]) fmt.Println(buffer[:n])
— Reply to this email directly, view it on GitHub https://github.com/bugst/go-serial/issues/160#issuecomment-1951374411, or unsubscribe https://github.com/notifications/unsubscribe-auth/A5BK55VAXBZN63YQG4WBWXDYUISOTAVCNFSM6AAAAAAZDQNIX2VHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMYTSNJRGM3TINBRGE . You are receiving this because you authored the thread.Message ID: @.***>
I'll also note that I started work on a pyserial syscall-identical serial implementation in Go to test this out here, still not finished since reading syscall python code is quite hard to follow.
Normally I'd attribute this to bad firmware or hardware... but I then observed cutecom and pyserial never run into the issue so I began wondering if it was a problem in how linux port is handled by bugst.
In my opinion, is the firmware that is buggy and goes into this weird loop state if you try to send a command while the scale is still writing the response of the previous command. In this sense getting a partial reply means that you go into sending the next command while the previous response is still being written.
To support my hypothesis you may try to use this function instead of the simple Read
:
func ReadLine(port serial.Port) (string, error) {
buf := make([]byte, 100)
line := ""
for {
n, err := port.Read(buf[:])
if err != nil {
return "", err
}
if n == 0 {
return line, errors.New("timeout")
}
line += string(buf[:n])
if i := strings.Index(line, "\n\r"); i != -1 {
return line[:i], nil
}
}
}
For example, I'm working with a electronic scale. When I open it with cutecom and select 1200 baud, 8 bits data, no parity, 1 stop bit I can send the "Get Weight" command which is
D05\r\n
, then I see in console123.032g\r\n
.When I try to use go-serial and writing the command after opening it with same mode parameters, setting the read timeout to 1 second I get my buffer filled with newlines as soon as I send the command. Switching to Cutecom and opening the port in this state causes cutecom to crash due to the newlines filling the log. The only way to reset the devices gone-amok state is by disconnecting and connecting the USB cable.
Why would go-serial give different behavior to pySerial and cutecom? What could be making the device to enter this tilted/"stuck" state?