distributed / sers

Serial port access for the Go programming language.
MIT License
38 stars 7 forks source link

Custom baud rates do not work in Windows #8

Closed james-portman closed 4 years ago

james-portman commented 4 years ago

Hi,

I am trying to use this with WIndows, at the moment it is failing to set the baud rate properly, it is staying at 9600. I saw you did some work on the Linux side to allow custom baud rates already.

package main

import (
    "encoding/hex"
    "fmt"
    "log"
    "time"

    "github.com/distributed/sers"
)

func main() {
    portname := "COM3"
    rb, err := readFirstBytesFromPort(portname)
    if err != nil {
        log.Fatal(err)
    }

    fmt.Printf("got %d bytes from %s:\n%s", len(rb), portname,
        hex.Dump(rb))
}

func readFirstBytesFromPort(fn string) ([]byte, error) {
    sp, err := sers.Open(fn)
    if err != nil {
        return nil, err
    }
    defer sp.Close()

    err = sp.SetMode(10400, 8, sers.N, 1, sers.NO_HANDSHAKE)
    if err != nil {
        return nil, err
    }

    // setting:
    // minread = 0: minimal buffering on read, return characters as early as possible
    // timeout = 1.0: time out if after 1.0 seconds nothing is received
    err = sp.SetReadParams(0, 1.0)
    if err != nil {
        return nil, err
    }

    mode, err := sp.GetMode()
    fmt.Println(mode)

    sp.SetBreak(false)
    time.Sleep(2000 * time.Millisecond)
    sp.SetBreak(true)
    time.Sleep(25 * time.Millisecond)
    sp.SetBreak(false)
    time.Sleep(25 * time.Millisecond)

    sp.Write([]byte {0x81})
    time.Sleep(3 * time.Millisecond)
    sp.Write([]byte {0x13})
    time.Sleep(3 * time.Millisecond)
    sp.Write([]byte {0xF7})
    time.Sleep(3 * time.Millisecond)
    sp.Write([]byte {0x81})
    time.Sleep(3 * time.Millisecond)
    sp.Write([]byte {0x0C})

    time.Sleep(1000 * time.Millisecond)

    var rb [128]byte
    n, err := sp.Read(rb[:])

    if rb[0] == 0x00 {
        fmt.Println("Looks like we got our break echo")
    }
    if n <= 6 {
        fmt.Println("Looks like ECU didn't respond")
    }

    return rb[:n], err
}

output:

9600,8n1,none
...

expected:

10400,8n1,none
distributed commented 4 years ago

Hey there!

The windows port of sers has had support for setting arbitrary baud rates since the first version. This is to say that sers propagates the desired baudrate to the OS. Whether the driver and the hardware actually support with acceptable precision is another story. In my use, non-canonical baudrates have worked under Windows in the past. Let's find out what's the problem in your case.

Reading your reproduction program and output, I assume that the primary problem is that your sers.Open(), SetMode(), GetMode() sequence is not returning the baud rate value you expect, i.e. 9600 instead of 10400. Is this correct?

Two notes about your program. You are not checking/reporting the error from mode, err := sp.GetMode(). Further down, you are reporting on the contents of rb[0] even you are not sure whether rb[0] contains read data or just the default value, 0.

The sers library comes with a set of programs to diagnose the kind of issue you are seeing. The one of use would be under verification/setgetmode/setgetmode.go.

I have a couple of questions back:

james-portman commented 4 years ago

Hi Michael, The code is just a ripped-off version of sers_test.go, don't worry about that. I know it is not very tidy but it was enough to start to prove what was happening. After posting I did carry on looking through all of the code and I agree with you - it is sending the desired baud rate straight through to Windows in the end.

These are Prolific PL2303 HXA cables - potentially fake chips in them as well which isn't helping, and using an old windows driver since prolific have broken the new driver for all fake/real cables.

1 - yes I have run setgetmode.go which gives the same result as my test:

go run setgetmode.go COM3 10400,8n1
10400
set "10400,8n1,none"
read mode: 9600,8n1,none

2 - I suspect the driver is the issue at this point like you suggest. The hardware works perfectly under Linux with the Linux driver. I have also fixed a driver for Android devices which works fine now with custom baud rates too. The Linux/Android drivers use some special way to reach the custom baud rates e.g. baudrate = 12M * 32 / (mantissa * 4^exponent), I know the value I end up sending in android for 104000 rate is 0x80000890, so I could just try setting the baud rate to that? Hope I don't have to try and make a windows driver from scratch...

3 - I can tell the baud rate is wrong as the module I am communicating with is sending back garbage data e.g. the first reply byte shows as 0x83 instead of expected 0x03.

4 - yes these cables can do 10400 baud depending on the driver as point 2 above. I have however just tried an FTDI cable and it works as expected.. really pointing towards the prolific drivers at this point.

distributed commented 4 years ago

Ah, rougly back when I started out with sers I had the PL2303 driver regularly crash my windows machine. From then on I avoided these cables like the plague. But that's a tangential story ;)

  1. Thanks for running it.

  2. The Linux/Android drivers use some special way to reach the custom baud rates e.g. baudrate = 12M 32 / (mantissa 4^exponent), I know the value I end up sending in android for 104000 rate is 0x80000890, so I could just try setting the baud rate to that?

In what piece of software does this calculation pop up? I have used sers on Android (GOOS=android GOARCH=arm64) and I believe the non-canonical baudrates were working. The mechanism used is the same as under Linux, i.e. struct termios2.

  1. I think this makes sense. If you send 0x03 at 10400 baud and receive it at 9600 baud, the stop bit can be interpreted as the MSB.

  2. That's a tough spot as you pointed out you don't necessarily want to upgrade the driver :S

So if we conclude that it's indeed a driver problem, are you fine to close the issue?

distributed commented 4 years ago

If you have any issues that are not clearly in the driver, feel free to open issues :) Same goes for Android, of course.

james-portman commented 4 years ago

OK thanks, I had to make my own driver for android so I'm not sure if it would work the same or not with Go. I was just using java/android studio for that, I didn't realise Go would compile for it even!

distributed commented 4 years ago

Why are you not able to use the Linux driver on Android?

Cross compilation for android works pretty well even though there's a gotcha. Of course you have to specify GOOS=android and your GOARCH. To link your executable, a cross compiler is needed and you neet so set CGO_ENABLED=1. I have no idea why it's not on by default for Android if it is needed. For the crosscompiler, usually I just point CC to a clang or gcc in an ndk.