olivergregorius / sun2000_modbus

Library for reading Huawei Sun2000 inverter metrics via Modbus TCP
MIT License
27 stars 12 forks source link

Reading range of registers #21

Closed Finn0811 closed 1 year ago

Finn0811 commented 1 year ago

Hi,

thanks for providing this library. Reading a single register works pretty well but I'm having issues to propery read a range of registers and parsing the return bytestring.

This example code seems to work:

inverter = inverter.Sun2000(host='192.168.178.147')
inverter.connect()
if inverter.isConnected():
    range_register = inverter.read_range(32016, end_address=32023)

    list_register = [
        registers.InverterEquipmentRegister.PV1Voltage,
        registers.InverterEquipmentRegister.PV1Current,
        registers.InverterEquipmentRegister.PV2Voltage,
        registers.InverterEquipmentRegister.PV2Current,
        registers.InverterEquipmentRegister.PV3Voltage,
        registers.InverterEquipmentRegister.PV3Current,
        registers.InverterEquipmentRegister.PV4Voltage,
        registers.InverterEquipmentRegister.PV4Current,
        # registers.InverterEquipmentRegister.InputPower
    ]

    i = 0
    for reg in list_register:
        register_value = range_register[i:i + reg.value.quantity + 1]

        v = datatypes.decode(register_value, reg.value.data_type)
        v = (v / reg.value.gain) if reg.value.gain else v
        print(f'{v} {reg.value.unit}')

        i += reg.value.quantity + 1
        print("#########################")

    print(" ")

Output:

712.4 V
#########################
7.94 A
#########################
0.0 V
#########################
0.0 A
#########################
0.0 V
#########################
0.0 A
#########################
0.0 V
#########################
0.0 A
#########################

However, reading battery registers seems to produce wrong numbers like 782.9A BusCurrent.

    battery_registers = [
        registers.BatteryEquipmentRegister.SOC,
        registers.BatteryEquipmentRegister.RunningStatus,
        registers.BatteryEquipmentRegister.BusVoltage,
        registers.BatteryEquipmentRegister.BusCurrent,
        registers.BatteryEquipmentRegister.ChargeDischargePower
    ]

    range_register = inverter.read_range(37760, end_address=37767)

    print(range_register)

    i = 0
    for reg in battery_registers:
        register_value = range_register[i:i + reg.value.quantity + 1]
        v = datatypes.decode(register_value, reg.value.data_type)

        v = (v / reg.value.gain) if reg.value.gain else v

        print(f'{v} {reg.value.unit}')

        i += reg.value.quantity + 1
        print("#########################")

    print(" ")

Output:

b'\x03\xe8\x00\x00\x00\x02\x1e\x95\x00\x00\x00\x00\x00\x00\x00\x00'
100.0 %
#########################
0.0 None
#########################
0.2 V
#########################
782.9 A
#########################
0.0 W
#########################

What am I doing wrong?

olivergregorius commented 1 year ago

Hi @Finn0811, the problem here is that the registers you are trying to read out are not subsequently ordered:

BatteryEquipmentRegister.SOC has address 37760 and consists of 1 register (2 bytes) BatteryEquipmentRegister.RunningStatus has address 37762 and consists of 1 register (2 bytes)

Between those two registers there is some kind of a 1 register (2 bytes) gap, so if you are reading the byte array byte by byte you will get a shift of 2 bytes. Thus the 782.9 A seem to be the value of the BatteryEquipmentRegister.BusVoltage in volts.

Please also note that as already stated above each register has the size of 2 bytes. Especially for the BatteryEquipmentRegister.ChargeDischargePower register that is important because it consists of 2 registers => 4 bytes must be read.

Finn0811 commented 1 year ago

Thanks for pointing that out! Looks like I got it now.

    battery_registers = [
        {'quantity': 1, 'register': registers.BatteryEquipmentRegister.SOC, 'field': 'soc'},
        {'quantity': 1, 'skip': True},
        {'quantity': 1, 'register': registers.BatteryEquipmentRegister.RunningStatus, 'field': 'running_status'},
        {'quantity': 1, 'register': registers.BatteryEquipmentRegister.BusVoltage, 'field': 'bus_voltage'},
        {'quantity': 1, 'register': registers.BatteryEquipmentRegister.BusCurrent, 'field': 'bus_current'},
        {'quantity': 2, 'register': registers.BatteryEquipmentRegister.ChargeDischargePower, 'field': 'charge_discharge_power'}
    ]

    range_register = inverter.read_range(37760, end_address=37766)

    print(range_register)

    i = 0
    for reg in battery_registers:
        if 'skip' in reg and reg['skip']:
            i += reg['quantity'] * 2
            continue 

        register_value = range_register[i:i + reg['quantity'] * 2]
        v = datatypes.decode(register_value, reg['register'].value.data_type)
        v = (v / reg['register'].value.gain) if reg['register'].value.gain else v

        print(f'{v} {reg["register"].value.unit}')

        i += reg['quantity'] * 2
        print("#########################")

    print(" ")
17.0 %
#########################
2.0 None
#########################
767.2 V
#########################
-0.7 A
#########################
-447.0 W
#########################

These are valid numbers. :)

olivergregorius commented 1 year ago

@Finn0811 Perfect, can this ticket be closed?

Finn0811 commented 1 year ago

Yes, thanks again 👍

svenbluege commented 12 months ago

@Finn0811 I try to replace the reading of 12 single values with the range function. With the default timeouts/waits, I often get a connection error when reading a range. This does not happen when reading the values one-by-one. Did you adjust those values and can share what is working for you?