simonvetter / modbus

Go modbus stack (client and server)
MIT License
280 stars 89 forks source link

timeout vs protocol error in detail #30

Closed tcurdt closed 1 year ago

tcurdt commented 1 year ago

I am trying debug a connection to a VFD.

Depending on how I switch A and B I get either

failed to read request timed out

or

failed to read protocol error

I didn't quite find the protocol error in the source code. Or is this error it?

To me it sounds like "failed to read protocol error" is getting at least something back - so that's the correct A/B connection. But the data coming back is wrong and hence the protocol error.

tcurdt commented 1 year ago

Ah, I guess it's "failed to read" plus protocol error

simonvetter commented 1 year ago

Hi Torsten,

Indeed, that's "failed to read: protocol error". Usually, this means the library read something back off the serial line and either can't make sense of it or found an actual protocol violation.

I can't look at the code right now, but what's the exact message you've got? Mind posting a snippet of code? Or was it using modbus-cli?

Assuming the VFD speaks modbus RTU and the serial speed is set appropriately, did you try playing with stop bits (1 or 2) and/or parity (even, odd or none)?

On RS-485 links, inverting polarities most often results in timeouts as receivers don't see the framing they are expecting.

On 26/05/2023 15:25, Torsten Curdt wrote:

I am trying debug a connection to a VFD.

Depending on how I switch A and B I get either

failed to read request timed out

or

failed to read protocol error

I didn't quite find the protocol error in the source code. Or is this error https://github.com/simonvetter/modbus/blob/4b0c5defc4ebb0ecf197003cc73260672fafc4d9/tcp_transport.go#L155 it?

To me it sounds like "failed to read protocol error" is getting at least something back - so that's the correct A/B connection. But the data coming back is wrong and hence the protocol error.

— Reply to this email directly, view it on GitHub https://github.com/simonvetter/modbus/issues/30, or unsubscribe https://github.com/notifications/unsubscribe-auth/AAAWG3CATUY5VK6FC6AYR3TXICVNDANCNFSM6AAAAAAYQHR25Q. You are receiving this because you are subscribed to this thread.Message ID: @.***>

-- Simon Vetter

tcurdt commented 1 year ago

Thanks for the quick response!

Indeed, that's "failed to read: protocol error". Usually, this means the library read something back off the serial line and either can't make sense of it or found an actual protocol violation.

That's sounds in line with what I am seeing with another implementation. (A GRBL board that is reading only nonsense values.)

Since I now figured out the details of the error this probably goes beyond the context of the library. So feel free to close at any time.

But since you asked, I'll write down the details. Maybe it's helpful for someone else at some stage. And if you have any pointers to debugging this I'd be more than grateful.

-- I've setup a YL620A VFD. (Link to manual)

P00.13=10  # factory reset

P00.01=3   # controlled by RS485
P07.08=5   # controlled by RS485

P03.00=3   # baud (3=9600, 4=19200)
P03.01=1   # set RS485 slave id to 1
P03.02=2   # 8 N 1

So the RS485 should be set to 9600@8N1.

In theory it could be really bad EMI - which I doubt without a spindle attached or running. I wrote a quick test program to see if I can read some meaningful data.

package main

import (
    "fmt"
    "time"

    "github.com/simonvetter/modbus"
)

type Value int

func uints2value(uints []uint16, signed bool) Value {

    value := Value(0)

    if signed && uints[0]&(1<<15) != 0 {
        value = -1
    }

    for _, u := range uints {
        value = value << 16
        value = value | Value(u)
    }

    return Value(value)
}

func read(BaudRate uint) {
    PortPath := "/dev/tty.usbserial-110"

    DataBits := uint(8)
    Parity := modbus.PARITY_NONE
    StopBits := uint(1)

    client, err := modbus.NewClient(&modbus.ClientConfiguration{
        URL:      "rtu://" + PortPath,
        Speed:    BaudRate,
        DataBits: DataBits,
        Parity:   Parity,
        StopBits: StopBits,
        Timeout:  500 * time.Millisecond,
    })
    if err != nil {
        panic(fmt.Errorf("cannot create client: %w", err))
    }

    err = client.Open()
    if err != nil {
        panic(fmt.Errorf("cannot open client: %w", err))
    }

    for i:=0 ; i<5 ; i++ {

        address := 8451
        //address := 8203

        signed := false
        quantity := 1

        registers16, err := client.ReadRegisters(uint16(address), uint16(quantity), modbus.HOLDING_REGISTER)
        if err != nil {
            fmt.Println("failed to read", err)
        } else {
            value := uints2value(registers16, signed)
            fmt.Println("value", value)
        }

        time.Sleep(1000)
    }
}

func main() {
    read(uint(9600))
    read(uint(19200))
    read(uint(38400))
}

It's a bit absurd. It's not my first rodeo with this VFD. At that time it was one of the wires not having proper contact. After fixing the cable it worked back then. (Doh!) That said, I think I was getting timeouts instead of protocol errors. So when I ran into troubles this time, I switched to a new cable - but so far no luck. It's a shame there is no good way to measure if A+B are connected correctly (to my knowledge).

My first guess was also a baud rate mismatch. Unfortunately I don't have a proper oscilloscope handy to dig a bit deeper into the signals.

Anyway - if you have any ideas it would be great.

But feel free to close the ticket. The original question got answered :)

simonvetter commented 1 year ago

I'd try to call Close() on the client before switching to the next baudrate to properly close and release the serial port, even though that's probably not the root cause.

Did you place proper 120Ohm termination resistors ? If your bus is short and you only have one, then place one, but those are needed (if not included in the endpoints).

Other things I'd try, if you haven't already :

Since you mentioned not being able to use an oscilloscope, another way of sniffing the wire could be to use two USB to RS485 adapters : one would be used by the modbus client while the other would run a program merely dumping raw bytes seen on the wire.

simonvetter commented 1 year ago

I'm gonna go ahead and close this but feel free to come back and comment with your findings! HTH, good luck!

tcurdt commented 1 year ago

I'd try to call Close() on the client before switching to the next baudrate to properly close and release the serial port, even though that's probably not the root cause.

👍

Did you place proper 120Ohm termination resistors ? If your bus is short and you only have one, then place one, but those are needed (if not included in the endpoints).

I measured. It seems only the usb adapter has termination. So that's only one side of a 30cm cable. I'll try with a 2nd 120 ohm resistor when I am back with the machine.

Other things I'd try, if you haven't already :

  • configuring 2 stop bits in the client,

Even when it's configured to 1 stop bit on the VFD?

  • using a shorter and/or shielded RS-485 wire,

The cable was shielded with the shield connected to the frame. I also tried a 10cm cable.

  • wiring the COM terminal in addition to A/B terminals of the RS485 link, and connecting the shield to PE/chassis ground,

After some research it seems connecting GND (not just A+B) is important. But I had done that from the start.

  • using another, potentially different make and model USB to RS485 adapter (I've fried a couple in my life in unexpected ways, especially in industrial environments),

It started with the board having problems talking to the VFD. Only then I switched to the adapter that also showed problems. The adapter worked before. So essentially both clients would need to be fried. Possible - but not so likely.

But I might just get another usb adapter to try.

  • borrowing an oscilloscope or signal analyzer to dump what goes over the wire.

I will bring my scope the next time I am at the machine. A shame I didn't bring it in the first time.

another way of sniffing the wire could be to use two USB to RS485 adapters : one would be used by the modbus client while the other would run a program merely dumping raw bytes seen on the wire.

Uh, interesting. How would you do that? Just have it in parallel and then open it as serial port and dump the raw bytes?

simonvetter commented 1 year ago

I will bring my scope the next time I am at the machine. A shame I didn't bring it in the first time.

If you ever get a chance to do that and find what the issue is, would you be able to post the waveforms in here? Just curious.

Uh, interesting. How would you do that? Just have it in parallel and then open it as serial port and dump the raw bytes?

Yes, precisely what I had in mind :)

tcurdt commented 1 year ago

It will probably take another month until I get to it - but I will keep you posted.

tcurdt commented 1 year ago

Alright. Finally some time to do some more debugging. This really is beyond the library - but since you asked :)

Maybe you have some insights. I reaching the end of my abilities.

I double checked that the RS485 adapter works. I got another one and created a null-modem connection just fine.

Then I used very short cable of about 15cm, with A/B being twisted and connected the oscilloscope on two channels to A+GND and B+GND.

Screenshot 2023-07-18 at 22 26 16 Screenshot 2023-07-18 at 22 26 28

The base noise level doesn't seem so bad.

Screenshot 2023-07-18 at 22 26 45

When sending data it looks kind of OK to me. I just would have expected higher voltages.

Screenshot 2023-07-18 at 22 28 57

To me it confirms what the transmission LED of the adapter and the error responds suggests: It is sending - but there is just is no response. Now I guess the reasons for that could be endless. That said I am not entirely sure how identify a response coming from the VFD either.

The adapter has a 120 Ohm termination resistor. But I've also tried with another resistor right at the VFD. It didn't change much really. Since I only have GND and no VCC I don't think adding bias resistors is in the cards.

I am wondering if there is any other way to tell whether the RS485 unit on the VFD is just broken - or whether this was the the same behaviour when misconfigured and just not answering.

I could still try the 2nd parallel adapter to read what's on the bus - but if there is no response...

tcurdt commented 1 year ago

Just for kicks I also tried the 2nd parallel adapter. No dice.

Screenshot 2023-07-19 at 15 53 05 Screenshot 2023-07-19 at 15 54 20

Will order a new VFD :-/

simonvetter commented 1 year ago

But I've also tried with another resistor right at the VFD. It didn't change much really. Since I only have GND and no VCC I don't think adding bias resistors is in the cards.

Just to make sure, the termination resistors need to be placed between A and B. They're meant to pull the line to line voltage back to 0V after a transmission, they really aren't bias resistors (i.e. pulling lines towards VCC or GND). I'm mentioning this because your first scope screenshot got me thinking: either i'm not reading that properly (which might very well be the case :D) or both lines are at ~1.2V relative to GND when idle, which shouldn't be the case if nothing is driving them (they should be close to 0V).

Also, what's the pinout on that USB to RS485 thingy? aren't the 2 left pins termination resistor connections, in which case they should be tied to A and B? Are you sure GND is indeed the center pin?

Your hex captures with the 2nd adapter show valid modbus RTU requests, with register addresses matching your code, so that's a start.

tcurdt commented 1 year ago

Just to make sure, the termination resistors need to be placed between A and B.

(nod) That's how I tried it.

They're meant to pull the line to line voltage back to 0V after a transmission, they really aren't bias resistors (i.e. pulling lines towards VCC or GND).

From what I understood biasing is usually the preferred way to setup RS485. But this isn't really an option in my case. I only have A, B and GND. And I suspect this should only be become important on longer cable lengths.

I'm mentioning this because your first scope screenshot got me thinking: either i'm not reading that properly (which might very well be the case :D) or both lines are at ~1.2V relative to GND when idle, which shouldn't be the case if nothing is driving them (they should be close to 0V).

The Rigol UI really isn't great :-/ but that's also how I read it, too. If they should be close 0V - that's good information.

Also, what's the pinout on that USB to RS485 thingy? aren't the 2 left pins termination resistor connections, in which case they should be tied to A and B? Are you sure GND is indeed the center pin?

Yes, it's clearly marked. I am no longer near the machine so I cannot send an actual photo, but here is an image from the web

Screenshot 2023-07-30 at 21 24 44

Your hex captures with the 2nd adapter show valid modbus RTU requests, with register addresses matching your code, so that's a start.

Indeed. Or the ending :)

I gave in and ordered a new VFD (different brand though). RS485 seems to be working with that one.

Although I am still curious to compare the two. Just to learn what went wrong here.