We recently had a Growatt MIN 11400TL-XH-US inverter and associated battery system installed as part of our solar power system. It all seems quite reasonable, except that its IoT presence is aggressively tied to the cloud. I’d like it not to be. There’s https://github.com/johanmeijer/grott, for an off-the-shelf approach to this problem, but I was not sufficiently enamored of its implementation to want to run it myself. Nevertheless, it has been a useful resource for cross-checking my understanding of Growatt’s protocols.

Please understand that there may be mistakes in the following and it is still mostly the result of educated guesses from packet dumps. There are no warranties associated with this text, not even implied ones.

Connectivity

The inverter’s datalogger – apparently fully integrated, in the case of MIN devices – can be programmed to connect to one of four endpoints, by DNS:

  1. server.growatt.com

  2. server-us.growatt.com

  3. server-cn.growatt.com

  4. server.smten.com

It does so on ports 5279 and 5280. I do not know what transpires on port 5280.

Modified Modbus Header

The TCP stream established to port 5279 is reminiscent of “Modbus over TCP/IP”. Every message, in each direction, begins with an 8-byte header and ends with a 2-byte checksum. Multi-byte fields in the header are MSB-first (that is, network-endian).

The checksum is performed with the standard Modbus CRC-16 polynomial, but is, unlike in Modbus RTU serial streams, here transmitted MSB-first.

Protocols

Growatt uses protocol identifiers 6 (and 5, apparently?) to indicate a… let’s say lightly obfuscated payload. Specifically, all post-header non-checksum bytes are xor’d with the mask “Growatt” (in ASCII). The checksum is computed using the obfuscated contents, not their “plaintext”.

Function Codes

0x04: “Input Registers”

The datalogger periodically generates, without in-band stimulus, function code 0x04 messages. The payload of these messages appear to consist of a 67-byte header followed by an array of register report structures. The initial header contains…

  • A 30-byte, NUL-terminated, right NUL-padded ASCII string of the datalogger’s serial number.

  • A 30-byte, NUL-terminated, right NUL-padded ASCII string of the inverter’s serial number.

  • A 6-byte date and timestamp. The format for this is Y M D H M S with most fields being straightforward. Y takes the year 2000 as its zero value. M encodes January as 1 and counts from there; D also counts from 1.

  • A 1-byte count of subsequent register report structures.

A register report structure consists of a 4-byte fixed header followed by an array of 2-byte, MSB-first register values. The header contains two 2-byte, MSB-first fields, the minimum (inclusive) and then maximum (inclusive) register indices to follow. See the document “Growatt PV Inverter Modbus RS485 RTU Protocol v120” for the defined list of registers.

0x16: Ping

Ping messages will be echoed exactly by the remote end, including an unchanged transaction identifier.

In practice, it looks like the datalogger sends the same 30-byte string encoding of its serial number followed by two zero bytes.

0x19: Datalogger Identification

These short messages consist of the same 30-byte string encoding of the datalogger’s serial number followed by a 4-byte header and a variable length payload. The header is two 2-byte MSB-first fields: a key and the length of the payload string. The known keys are as follows:

Key

Payload

4

Update interval in minutes, as decimal value (e.g., “5.0”)

8

Datalogger alias (defaults to serial number)

14

Datalogger IP address (dotted quad format)

16

Datalogger MAC address

18

Server TCP port

19

Server DNS name

21

Firmware version string