I think I got confused somewhere along the way. I do not think the protocol is actually MODBUS as Modbus does not appear to use a start-of frame bit and specifies a CRC rather than a simple checksum.
The water heater is a Reliance DHPST hybrid water heater (electric + Heat Pump)
Data on RS485 is transmitted at 19200 baud
Data is 1 startbit, 8 data bits, 1 'start of frame' bit, and 1 stop bit/
The 'start of frame' bit is 1 for the 1st byte, and 0 for all remaining bytes.
The last byte is a single 1-byte sum of all previous bytes of the frame.
This represents most of the packets I have seen (I have removed the 'start of packet' bit:
(3) : 90 00 90
(64) : 40 08 1e 00 00 00 03 00 78 00 00 00 00 00 00 71 44 74 6a 49 47 30 00 02 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 69 cd 00 00 00 00 16 02 00 00 00 09 00 00 00 00 00 00 41 08 00 00 50 00 26
(30) : 40 0b 0d 02 00 62 00 5a 00 5e 00 52 00 00 00 ff ff 00 00 00 00 00 00 00 00 00 01 00 02 c7
(3) : 8c 00 8c
(3) : 82 00 82
(8) : 40 00 02 05 00 b2 00 f9
(28) : 40 0d 0c 00 01 00 3c 00 00 00 10 00 78 00 78 01 01 00 00 00 00 00 01 00 00 00 00 99
For anyone interested, the easiest way to read this in linux is to set the
INPCK and PARMRK, and then read with 'SPACE' parity. this will generate an error whenever the parity bit is set, that can then be converted into a 'start of frame' bit.
Here is some example python code:
import serial
import termios
i
ser = serial.Serial(port, 19200, parity=serial.PARITY_SPACE)
iflag, oflag, cflag, lflag, ispeed, ospeed, cc = termios.tcgetattr(ser)
iflag |= termios.INPCK | termios.PARMRK
termios.tcsetattr(ser, termios.TCSANOW, [iflag, oflag, cflag, lflag, ispeed, ospeed, cc])
buf = []
while True:
try:
b = ord(ser.read(1))
if b == 0xff :
b = ord(ser.read(1))
if b == 0x00:
b = 0x100 | ord(ser.read())
buf.append(b)
except SerialException as e:
print(e)
I'll follow-up with the bytes I've figured out so far.