ModbusTCP layer for high-speed batched modbus requests.
- Reads register definitions from CSV-Input.
- Bulks modbus requests to bunches of up to 120 registers for optimized access
- Requests are handled in one transaction.
- Supports access of different unit-ids in one batch.
- Modbus raw-results will be converted to python data-types (integer, float, utf8-string).
- Configurable retry mechanism
- Little Endian double words aka "wordswap" is supported.
- Reads holding and input registers, no coils, no write.
- An offset for register addressing may be given.
Cyclic polling of (many) inverter modbus registers via TCP.
sudo pip install git+https://github.com/hburkert/modbusBatch.git
import time
from modbusBatch.mbBatch import MbBatch
mb = MbBatch(host="localhost",
port=502,
retry=3,
reg_offset=1,
reg_wordswap=True,
file_type="csv",
file_path="./SungrowSHxxRT.csv",
debug=False,
csv_dlm=',',
batch_size=110)
# endless application loop
while mb.process_batches(close_socket=True):
# do_some_thing_with(mb.results)
print(mb.results)
#store(mb.results)
#check_end_of_loop() and exit(0)
time.sleep(20)
exit( 1 )
# <- this is a comment line
# First non-comment must be csv.header
# Hdr:
reg_type, reg_number, reg_name, reg_desc, measurement_unit, data_type, data_length, unit_id, scaling_factor, reserved
i,4990,serial_number,"Seriennummer",,CHR,5,1,1,
i,5003,daily_output_energy,"This day PV generation and battery discharge",kWh,U16,,1,0.1,
i,5004,total_output_energy,"Total PV generation and battery discharge",kWh,U32,,1,0.1,
i,5008,inside_temperature,"Inside Temperature","deg C",S16,,1,0.1,
i,5009,total_apparent_power,"Scheinleistung gesamt","VA",U32,,1,1,
i,5011,mppt1_voltage,"MPPT1 Voltage","V",U16,,1,0.1,
...
h,5000,system_clock_year,,,U16,1,1,1,
h,5001,system_clock_month,,,U16,1,1,1,
h,5002,system_clock_day,,,U16,1,1,1,
h,5003,system_clock_hour,,,U16,1,1,1,
h,5004,system_clock_minute,,,U16,1,1,1,
h,5005,system_clock_second,,,U16,1,1,1,
....
# registers from unit_id 200:
i,10743,battery_temperature,"","C",S16,,200,0.1,
i,10744,battery_level_abs,"absolute battery level","%",U16,,200,0.1,
Order of rows is arbitrary. Internally they will be sorted by unit_id
, reg_type
and reg_number
.
reg_type
must be first column, order of other columns is arbitrary.
Columns reg_type
reg_number
reg_name
are always required. Other columns have defaults.
data_len
should be given for data_type
'str' or 'chr'. Length is expressed in number of registers. Length = 5 results in a string of 10 bytes.
Warning - Definitions should not overlap:
i,1000,foo,'',,,chr,100,,,,
i,1099,bar,'',,,u16,1,,,, <=== overlaps with prev register
Implements class ModbusTcpRaw.
Parent class is pyModbusTCP.client.ModbusClient from https://github.com/sourceperl/pyModbusTCP.
Provides two new functions:
- read_holding_raw
- read_input_raw
They work like "read_holding_registers" and "read_input_registers" but return the raw modbus-result-buffer instead of an integer array. This avoids unnecessary conversion steps.
Implements class MbBatch
MbBatch(host="localhost", port=502, retry=3, reg_offset=0, reg_wordswap=True, file_type="csv", file_path="registers.csv", csv_dlm=';', batch_size=110)
where:
- host - name or address of modbus host
- port - port number, usually 502
- retry - maximum retries before returning an error
- reg_offset - offset for register addressing. This offset will be subtracted from external register-number.
- reg_wordswap - True/False. True means U32/S32 register pairs are little endian encoded (Sungrow, SolaX, ...)
- file_type - only "csv" is supported
- file_path - file with register definitions
- debug - debug flag for modbusTcpRaw
- csv_dlm - defaults to ','
- batch_size - maximum number of registers to batch, 60 - 120
Function __init__
stores register specifications from csv-file in
internal dataclasses (see below) and generates optimized modbus requests.
-
dataclass
MbReg
- one Modbus register from CSV -
- reg_type - i=input, h=holding register
-
- reg_number - external register number includes offset
-
- reg_name - name of register, python name, preferable snake_case
-
- reg_desc - optional descriptive text
-
- measurement_unit - optional, e.g. W, A, V, kWh
-
- data_type - data type: u16, s16, u32, s32, str, chr
-
- data_length - needed for str, chr; otherwise derived from data_type
-
- scaling_factor - default 1
-
- unit_id - modbus unit-id, default 1, there may be different unit-ids (Sungrow!)
-
- precision - precision of register value, derived from scaling_factor
-
list
MbRegs
- list ofMbReg
, all modbus registers -
dataclass
MbReq
- represents one modbus request -
- unit_id - modbus unit-id, default 1, there may be different unit-ids (Sungrow!)
-
- reg_type - i=input, h=holding register
-
- address - modbus address (reg_number - offset)
-
- quantity - number of registers to read, max. 120
-
- from_x - index of first corresponding register in
MbRegs
- from_x - index of first corresponding register in
-
- to_x - index of first corresponding register in
MbRegs
Modbus requests are grouped by
unit_id
andreg_type
and limited to 120 registers.
Warning: Overlapping register definitions may lead to misbehaviour. - to_x - index of first corresponding register in
-
list
MbReqs
- list ofMbReq
, all modbus requests -
dict
results
- after successful retrieval of all registers you will find them here. -
function
process_batches(close_socket: bool = True)
This polling function returns True on success, False on failure.
Parameterclose_socket=True
closes socket connection after processing the batch.close_socket=False
leads to malfunction with Sungrow Inverters. Try it.