Hello, I find some time to setup my own environment.
I’m using armbian on a former tvbox device, in pratice it is a debian installation on arm.
I installed dnsmasq for dns forwarding and dhcp server and mosquitto as MQTT broker, plus the device is acting as a wireless access point.
Of course there are several ways to hijack mqtt traffic, one is via netfilter as you did, I choose instead to hijack the dns. The inverter tries to contact mqtt.saj-solar.com, so I assigned that DNS name to my local server;
mosquitto MQTT broker is happy to accept the connection from the inverter and the inverter publish its data with the topic saj/serial_number/realtime_data
(where serial_number is the actual serial of the inverter that starts with H1S2…) with no encryption and no cypher at all. That’s bad for privacy, for good for reverse engineering.
mosquitto_sub
utility also is very handy to receive, on the other side, the messages sent by the inverter, which are 1200 bytes each of binary data:
mosquitto_sub -h 192.168.16.1 -t saj/serial_number/realtime_data
The essential knowledge is that all the values are in big-endian format.
The first four bytes of the packet looks like a sequential number, which increases on each sent packet.
The second four bytes are an Epoch timestamp.
I’m just looking into the rest of the data, but the great news is that, starting from the packet address 0x86, the data looks like being mapped directly to the description of realtime data on the modbus protocol documentation of paragraph “4.3 Definitions of Realtime Data Registers”. The PDF is available somewhere in this thread in a post pointing to a telegram discussion from spanish friends.
So this little formula can be used to map the realtime data attribute from modbus protocol to the MQTT packet:
addr = ((modbus_address - 0x4031) * 2) + 0x86
0x4031
is the first modbus address that contains valuable attributes and 0x86
is the corresponding offset in the MQTT packet.
The multiplication by two * 2
is due to the fact that each modbus register contains a 2-byte word. Most of the realtime data are signed or unsigned 16 bit short integers (=> one modbus register), but some are 32 bit integers too (=> two modbus registers). I don’t see any floats around.
That formula above is just what I guessed in a quick two hour study over the documentation, so it is not written in stone and may change with further findings.
Just some quick random examples:
Grid voltage: modbus 0x4031
→ mqtt offset 0x86
= uint16(2426) * 10^-1 = 242,6 Volts
Photovoltaic Array 1 power: modbus 0x4073
→ mqtt offset 0x10a
= uint16(1596) * 10^0= 1596 Watts
Battery voltage: modubs 0x4069
→ mqtt offset 0xf6
= uint16(526) * 10^-1 = 52,6 Volts