Read Modbus Register SolarEdge Wave

I want to read data from my single phase solaredge wave inverter. I want to use Modbus RTU, this is already connected and the baud rate has been changed to 9600, slave ID 02, the inverter should be the slave and the arduino the master. I will read data from the inverter with Arduino uno with ethernet shield and RS485 interface MAXX. I was able to read some power meters and a growatt 3 phase inverter. I used the basic arduino language without a modbus library. Basically With the following lines
Serial.write(request, REQ_NO_BYTES)
n = Serial.readBytes(response, RESP_NO_BYTES)
the response is read processed and transfered to a MariaDB database (LAMPP). this all works fine since september 2024. All data is stored every 5 minutes. The request in all cases have the same structure:

byte request[REQ_NO_BYTES] = { 0x01, 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00 };

1e byte slave ID
2e byte function code 04 read input register
3e byte HI address
4e byte LO address
5e byte HI no. of registers
6e byte LO no. of registers
7e byte LO CRC-16 Modbus
8e byte HI CRC-16 Modbus

The setup of the solaredge unfortunately is different, at least how I understood this.

see also https://knowledge-center.solaredge.com/sites/kc/files/se-modbus-interface-for-solaredge-terramax-inverter-technical-note.pdf
page 18.

1e field Client identifier XXXX. ?? No idea what this is.
2e field one byte Length of the following fields, 6 bytes
3e field one byte slave ID
4e function one byte code 03
5e-8e address of starting point 4 bytes.

If I want to read the AC power for instance which is stored in HI address 40083 hex 9C 93

the request would be something like { ???? 0x06 0x02 0x03 0x00 0x00 0x9C 0x93 0x?? 0x?? }
the last 2 bytes are for the CRC-16 modbus, but can only be determined when the full request is known. This is easy to determine.

The simple question is what full request (hex) do I need to send to the inverter?

The manual is a little confusing, however the Modbus command you need is Read Holding Register (0x03). The Modbus slave (aka server) address is 0x01.

To get the register address, take the 1-based decimal address (40084), remove the first digit, then subtract 1, then convert to hex. Therefore the Modbus register address is 0x00 0x53.

So using Online Modbus / Analyzer / Simulator / Parser/ I get the following

    Slave ID:                1 (decimal) |   01 (hexadecimal)
    Function:                3 (decimal) |   03 (hexadecimal)
    Register Offset:        83 (decimal) | 0053 (hexadecimal)
    Number Of Registers:     1 (decimal) | 0001 (hexadecimal)

    CRC:                  7028 (decimal) | 741B (hexadecimal)
    CRC Should Be:        7028 (decimal) | 741B (hexadecimal)

    Request: [01] [03] [0053] [0001] [741B]
             |    |    |      |      |-> CRC16 (7028)
             |    |    |      |-> Number Of Registers (1)
             |    |    |-> Register Offset (83 = 40084)
             |    |-> Function Code (3)
             |-> Slave ID (1)

Edit:

Oh I missed that. For slave ID 2, adjust accordingly.

    Slave ID:                2 (decimal) |   02 (hexadecimal)
    Function:                3 (decimal) |   03 (hexadecimal)
    Register Offset:        83 (decimal) | 0053 (hexadecimal)
    Number Of Registers:     1 (decimal) | 0001 (hexadecimal)

    CRC:                 10356 (decimal) | 7428 (hexadecimal)
    CRC Should Be:       10356 (decimal) | 7428 (hexadecimal)

    Request: [02] [03] [0053] [0001] [7428]
             |    |    |      |      |-> CRC16 (10356)
             |    |    |      |-> Number Of Registers (1)
             |    |    |-> Register Offset (83 = 40084)
             |    |-> Function Code (3)
             |-> Slave ID (2)

Hi bobcousins, thnx for the response. I tested this and the result is
02 03 02 4E 6E 48 08, I assume the value is 02 4E 6E according to me this is 151150 is that correct? The current output power is 1.9 Kw.

It took me hours testing without any result. Thanks very much I will program the solaredge from now on.

Kind regards.

The value is 4E 6E which is 20078 decimal. I now realize that you also need to read the Scale Factor from the next register (see page 10).

I would guess in this case the scale factor is 1, so the actual value is 20078/10 = 2007.8 W.

If you make the query as follows

    Slave ID:                2 (decimal) |   02 (hexadecimal)
    Function:                3 (decimal) |   03 (hexadecimal)
    Register Offset:        83 (decimal) | 0053 (hexadecimal)
    Number Of Registers:     2 (decimal) | 0002 (hexadecimal)

    CRC:                 10548 (decimal) | 3429 (hexadecimal)
    CRC Should Be:       10548 (decimal) | 3429 (hexadecimal)

    Request: [02] [03] [0053] [0002] [3429]
             |    |    |      |      |-> CRC16 (10548)
             |    |    |      |-> Number Of Registers (2)
             |    |    |-> Register Offset (83 = 40084)
             |    |-> Function Code (3)
             |-> Slave ID (2)

I would expect to see data like 4E 6E 00 01, depending on current value.

[quote="kees_arduino, post:3, topic:1360894"]
Hi bobcousins,

The result is 02 03 04 3A 2F FFFFF F5 92?? How to interpret that?

Ok that makes sense I got the sign wrong. FF FF is 2's complement for -1.

So the value would be (0x3a2f) * 10^-1 or 1489.5 W.

You could do something like the following:

void setup() {
  Serial.begin(9600);

  byte data[] = {0x02,0x03,0x04,0x3A,0x2F,0xFF,0xFF,0xF5,0x92};

  int16_t value = data [3] << 8 | data [4];
  int16_t scaleFactor = data [5] << 8 | data [5];

  float value_f = (float)value * pow (10.0f, (float)scaleFactor);

  Serial.println (value, HEX);
  Serial.println (scaleFactor, HEX);
  Serial.println (value_f);
}

void loop() 
{
}

Thnx again, understood, as far as the binary shifting and OR-ing that I will take for granted, it is a very long time ago. Kind regards Kees

Small correction, I got a FF FE as correction factor (100 Watt) and that is -2.

int16_t scaleFactor = data [5] << 8 | data [6];

Good spot!

To be sure that the data[5] not shifts all bits into oblivion (as it is a byte and some compilers do not promote automagically in such case)

int16_t scaleFactor = data[5];
scaleFactor <<=  8;
scaleFactor |= data[6];

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.