Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Transaction causes device to spew newlines #160

Open
biohazduck opened this issue Jun 12, 2023 · 8 comments
Open

Transaction causes device to spew newlines #160

biohazduck opened this issue Jun 12, 2023 · 8 comments

Comments

@biohazduck
Copy link

biohazduck commented Jun 12, 2023

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 console 123.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?

@Darkskald
Copy link

Big upvote for this here. I am implementing a driver for a laboratory balance, facing EXACTLY the same issue as above.

@cmaglie
Copy link
Member

cmaglie commented Feb 16, 2024

Please provide the source code to reproduce the issue.

@biohazduck
Copy link
Author

biohazduck commented Feb 16, 2024

@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?

@cmaglie
Copy link
Member

cmaglie commented Feb 18, 2024

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

@biohazduck
Copy link
Author

biohazduck commented Feb 18, 2024 via email

@soypat
Copy link

soypat commented Feb 18, 2024

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.

@cmaglie
Copy link
Member

cmaglie commented Feb 18, 2024

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.

@cmaglie
Copy link
Member

cmaglie commented Feb 18, 2024

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants