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

ReadCoils endianness #47

Open
arkadiiemelianov-brt opened this issue Sep 27, 2024 · 4 comments
Open

ReadCoils endianness #47

arkadiiemelianov-brt opened this issue Sep 27, 2024 · 4 comments

Comments

@arkadiiemelianov-brt
Copy link

Hi!

I'm currently experimenting with Modbus RTU Relay 32ch connected to the ethernet via RS485_TO_ETH.
I'm using rtuovertcp:// scheme to connect and send commands to the RTU Relay.

Your library works perfectly controlling Relays/Coils, but I have a problems when reading status of the relays using ReadCoils API call. It looks like either library uses wrong endianness when parsing ReadCoils response or my hardware uses wrong endianness when reporting the status of the coils.

Here is the Wireshark captures (I'm setting coil number 12 on, zero-based 11, and the requesting the status):
write_coil_request
write_coil_response
readcoils_response

The TCP steam is following:

0105000bff00fdf8       # write coil with address 11
0105000bff00fdf8       # write coil response
0101000000203dd2       # read coils request
01010400000800fc11     # read coils response

Decoded, the response looks like this:
ReadCoils response (the bit number 19 is set):

[]bool{
    false, false, false, false, false, false, false, false,
    false, false, false, false, false, false, false, false,
    false, false, false, true, false, false, false, false,
    false, false, false, false, false, false, false, false
}

But effectively response should look like this:
ReadCoils corrected (the bit number 12 is set):

[]bool{
    false, false, false, false, false, false, false, false,
    false, false, false, true, false, false, false, false,
    false, false, false, false, false, false, false, false,
    false, false, false, false, false, false, false, false
}

For correction the response of ReadCoils API response I'm currently using following:

func bitsToCoils(bits []bool) []bool {
	val := bitsToUint32(bits)
	buf := make([]byte, 4)
	binary.LittleEndian.PutUint32(buf, val)
	res := binary.BigEndian.Uint32(buf)
	return uint32ToBits(res)
}

func bitsToUint32(bits []bool) uint32 {
	var r uint32 = 0
	for i, bit := range bits {
		if bit {
			r |= 1 << i
		}
	}
	return r
}

func uint32ToBits(bits uint32) []bool {
	var r []bool
	for i := 0; i < 32; i++ {
		r = append(r, bits&(1<<i) > 0)
	}
	return r
}

Shall library take endianness into account when parsing ReadCoil status or it's just my hardware firmware is broken?

@simonvetter
Copy link
Owner

simonvetter commented Nov 14, 2024

Hi Arkadii,

i believe that the modbus spec does specify endianness/bit ordering for coils and binary inputs, but I could be wrong. I'll look into it and post back here with my findings.

@simonvetter
Copy link
Owner

So here's what the spec has to say:
image

As I understand it, coils/discrete inputs are packed on byte boundaries.
Payload byte index 0 in the reply will hold the first 8 coils (coil #0 at LSB, coil #7 at MSB).
Payload byte index 1 will hold the next 8 coils (coil #8 at LSB, coil #15 at MSB), and so on.

So there's no latitude for byte or bit-order interpretation here, and the library seems to do what it should (see encoding.go).
That seems to square with your wireshark captures, where the modbus decoder shows coil #19 set when reading even though you're setting coil #11 in the write request.

I'm tempted to say that the fault might lie with your device, but I don't really know anything about it. What make/model is it?

@arkadiiemelianov-brt
Copy link
Author

arkadiiemelianov-brt commented Nov 22, 2024

@simonvetter thanks for looking into it, I have listed the hardware in the original message:

The connectivity is following:

client <-> eth to rtu gateway <-> rtu relay

Since my compensation code works fine and I don't have any other relays available to test I would suggest to close this issue unless we had more confidence where the root of the problem is.

@simonvetter
Copy link
Owner

simonvetter commented Nov 27, 2024

The ETH to RTU gateway should be fine. I've got a few in production and they mostly work as they should.

The RTU relay board seem to have it backwards, though, and even documented it:
https://www.waveshare.com/wiki/Modbus_RTU_Relay_32CH

Read Relay Status
[...]
Send: 01 01 00 00 00 20 3D D2
Return: 01 01 04 00 00 00 00 FB D1 //All relays off
Send: 01 01 00 00 00 20 3D D2
Return: 01 01 04 00 00 00 01 3A 11 //NO.0 relay on, others off
Send: 01 01 00 00 00 20 3D D2
Return: 01 01 04 00 00 00 41 3B E1 //NO.0 and NO.6 relays on, others off

That's exactly what you're describing: relay #0 status is at bit #0 of byte #6 of the reply above (counting from 0). According to the modbus spec referenced above, it should be at bit #0 of byte #3.

This is all confirmed by your wireshark analysis which shows coil #19 being set after a write to coil #11.

You may be able to get them to fix the firmware by pointing to this issue and/or explaining it to them in better words :)

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

2 participants