| Start | End | Block description |
|---|---|---|
| 3972 | 3972 | Unknown |
| 4096 | 4111 | Brand and model name |
| 4112 | 4239 | Empty |
| 4320 | 4320 | Date and time |
| 4352 | 4415 | Same as 5120-5183 |
| 4416 | 4607 | Empty |
| 4608 | 4671 | Unknown, maybe settings (seems static) |
| 4672 | 4863 | Empty |
| 5120 | 5183 | BMS status |
| 5184 | 5199 | Empty |
| 5200 | 5215 | BMS SN |
| 5216 | 5231 | Pack 0-x - Module 0-x Voltage |
| 5232 | 5295 | Empty |
| 5296 | 5311 | Pack 0-x - Module 0-x Temperature |
| 5312 | 5375 | Empty |
| 5376 | 5503 | Pack 0 Module 0-4 Cell Voltage |
| 5504 | 6143 | ?? Pack 1-5 Module 0-4 Cell Voltage |
| 6144 | 6271 | Pack 0 Module 0-4 Cell Temperature |
| 6272 | 6911 | ?? Pack 1-5 Module 0-4 Cell Temperature |
-
-
Save pavelmaca/170c282bdb9e20b6c624e8c7633be3b2 to your computer and use it in GitHub Desktop.
| requests: | |
| # Brand, Device name, FW Version | |
| - start: 4096 | |
| end: 4106 | |
| mb_functioncode: 0x03 | |
| # Device Date and Time - TODO | |
| #- start: 4320 | |
| # end: 4325 | |
| # mb_functioncode: 0x03 | |
| # BMS Status | |
| - start: 5120 | |
| end: 5135 | |
| mb_functioncode: 0x03 | |
| # Cell/Module Max/Min Temperature and Voltage | |
| - start: 5136 | |
| end: 5151 | |
| mb_functioncode: 0x03 | |
| # Total Charge, Discharge | |
| - start: 5152 | |
| end: 5167 | |
| mb_functioncode: 0x03 | |
| # Pack, Module, Cell count | |
| - start: 5174 | |
| end: 5175 | |
| mb_functioncode: 0x03 | |
| # Device SN | |
| - start: 5200 | |
| end: 5207 | |
| mb_functioncode: 0x03 | |
| # Pack 0 Module 0-3 Voltage | |
| - start: 5216 | |
| end: 5219 | |
| mb_functioncode: 0x03 | |
| # Pack 0 Module 0-3 Temperature | |
| - start: 5296 | |
| end: 5299 | |
| mb_functioncode: 0x03 | |
| # Pack 0 Module 0-3 Cells Voltage | |
| #- start: 5376 | |
| # end: 5465 | |
| # mb_functioncode: 0x03 | |
| # Pack 0 Module 0-3 Cells Temperature | |
| #- start: 6144 | |
| # end: 6233 | |
| # mb_functioncode: 0x03 | |
| parameters: | |
| - group: Basic information | |
| items: | |
| - name: "Device SN" | |
| class: "" | |
| state_class: "" | |
| uom: "" | |
| scale: 1 | |
| rule: 5 | |
| registers: [ 5200,5201,5202,5203,5204,5205,5206,5207 ] | |
| isstr: true | |
| #- name: "Device Time" # TODO - cant parse format is [y, m, d, h, m, s] | |
| # class: "" | |
| # state_class: "" | |
| # uom: "" | |
| # scale: 1 | |
| # rule: 8 | |
| # registers: [4320,4321,4322,4323,4324,4325] | |
| # isstr: true | |
| - name: "Brand" | |
| class: "" | |
| state_class: "" | |
| uom: "" | |
| scale: 1 | |
| rule: 5 | |
| registers: [ 4096,4097,4098 ] | |
| isstr: true | |
| - name: "Device Name" | |
| class: "" | |
| state_class: "" | |
| uom: "" | |
| scale: 1 | |
| rule: 5 | |
| registers: [ 4101,4102,4103,4104,4105 ] | |
| isstr: true | |
| - name: "FW Version" | |
| class: "" | |
| state_class: "" | |
| uom: "" | |
| scale: 1 | |
| rule: 7 | |
| registers: [ 4106 ] # 00000001 00000110 = V1.6 | |
| isstr: true | |
| - name: "Total Charge" | |
| class: "energy" | |
| state_class: "total_increasing" | |
| uom: "kWh" | |
| scale: 1 | |
| rule: 1 | |
| registers: [ 5164 ] | |
| icon: 'mdi:battery-plus' | |
| - name: "Total Discharge" | |
| class: "energy" | |
| state_class: "total_increasing" | |
| uom: "kWh" | |
| scale: 1 | |
| rule: 1 | |
| registers: [ 5166 ] | |
| icon: 'mdi:battery-minus' | |
| #- name: "Battery Pack (parallel)" # not tested | |
| # class: "" | |
| # state_class: "" | |
| # uom: "" | |
| # scale: 1 | |
| # rule: 1 | |
| # registers: [5173] | |
| - name: "Battery Module (series)" | |
| class: "" | |
| state_class: "" | |
| uom: "" | |
| scale: 1 | |
| rule: 1 | |
| registers: [ 5174 ] | |
| - name: "Battery Cell (series)" | |
| class: "" | |
| state_class: "" | |
| uom: "" | |
| scale: 1 | |
| rule: 1 | |
| registers: [ 5175 ] | |
| - group: Battery | |
| items: | |
| - name: "Battery Voltage" | |
| class: "voltage" | |
| state_class: "measurement" | |
| uom: "V" | |
| scale: 0.1 | |
| rule: 1 | |
| registers: [ 5123 ] | |
| icon: 'mdi:battery' | |
| - name: "Battery Current" | |
| class: "current" | |
| state_class: "measurement" | |
| uom: "A" | |
| scale: 0.01 | |
| rule: 2 | |
| registers: [ 5125 ] | |
| icon: 'mdi:current-dc' | |
| - name: "Battery Temperature" | |
| class: "temperature" | |
| state_class: "measurement" | |
| uom: "°C" | |
| scale: 0.1 | |
| rule: 2 | |
| registers: [ 5126 ] | |
| icon: 'mdi:thermometer' | |
| - name: "Battery Charge" | |
| class: "battery" | |
| state_class: "measurement" | |
| uom: "%" | |
| scale: 1 | |
| rule: 1 | |
| registers: [ 5127 ] | |
| icon: 'mdi:battery' | |
| - name: "Battery Cycle Times" | |
| class: "" | |
| state_class: "" | |
| uom: "" | |
| scale: 1 | |
| rule: 1 | |
| registers: [ 5128 ] | |
| icon: 'mdi:battery-heart' | |
| - name: "Max Charging Voltage" | |
| class: "voltage" | |
| state_class: "measurement" | |
| uom: "V" | |
| scale: 0.1 | |
| rule: 1 | |
| registers: [ 5129 ] | |
| - name: "Max Charging Current" | |
| class: "current" | |
| state_class: "measurement" | |
| uom: "A" | |
| scale: 0.01 | |
| rule: 2 | |
| registers: [ 5131 ] | |
| icon: 'mdi:current-dc' | |
| - name: "Min Discharging Voltage" | |
| class: "voltage" | |
| state_class: "measurement" | |
| uom: "V" | |
| scale: 0.1 | |
| rule: 1 | |
| registers: [ 5132 ] | |
| - name: "Max Discharging Current" | |
| class: "current" | |
| state_class: "measurement" | |
| uom: "A" | |
| scale: 0.01 | |
| rule: 2 | |
| registers: [ 5134 ] | |
| icon: 'mdi:current-dc' | |
| - name: "Max Cell Voltage" | |
| class: "voltage" | |
| state_class: "measurement" | |
| uom: "V" | |
| scale: 0.001 | |
| rule: 1 | |
| registers: [ 5136 ] | |
| icon: 'mdi:battery' | |
| - name: "Min Cell Voltage" | |
| class: "voltage" | |
| state_class: "measurement" | |
| uom: "V" | |
| scale: 0.001 | |
| rule: 1 | |
| registers: [ 5137 ] | |
| icon: 'mdi:battery' | |
| - name: "Max Cell Voltage ID" | |
| class: "" | |
| state_class: "" | |
| uom: "" | |
| scale: 1 | |
| rule: 1 | |
| registers: [ 5138 ] | |
| - name: "Min Cell Voltage ID" | |
| class: "" | |
| state_class: "" | |
| uom: "" | |
| scale: 1 | |
| rule: 1 | |
| registers: [ 5139 ] | |
| - name: "Max Cell Temperature" | |
| class: "temperature" | |
| state_class: "measurement" | |
| uom: "°C" | |
| scale: 0.1 | |
| rule: 2 | |
| registers: [ 5140 ] | |
| icon: 'mdi:thermometer' | |
| - name: "Min Cell Temperature" | |
| class: "temperature" | |
| state_class: "measurement" | |
| uom: "°C" | |
| scale: 0.1 | |
| rule: 2 | |
| registers: [ 5141 ] | |
| icon: 'mdi:thermometer' | |
| - name: "Max Cell Temperature ID" | |
| class: "" | |
| state_class: "" | |
| uom: "" | |
| scale: 1 | |
| rule: 1 | |
| registers: [ 5142 ] | |
| - name: "Min Cell Temperature ID" | |
| class: "" | |
| state_class: "" | |
| uom: "" | |
| scale: 1 | |
| rule: 1 | |
| registers: [ 5143 ] | |
| - name: "Max Module Voltage" | |
| class: "voltage" | |
| state_class: "measurement" | |
| uom: "V" | |
| scale: 0.01 | |
| rule: 1 | |
| registers: [ 5144 ] | |
| icon: 'mdi:battery' | |
| - name: "Min Module Voltage" | |
| class: "voltage" | |
| state_class: "measurement" | |
| uom: "V" | |
| scale: 0.01 | |
| rule: 1 | |
| registers: [ 5145 ] | |
| icon: 'mdi:battery' | |
| - name: "Max Module Voltage ID" | |
| class: "" | |
| state_class: "" | |
| uom: "" | |
| scale: 1 | |
| rule: 1 | |
| registers: [ 5146 ] | |
| - name: "Min Module Voltage ID" | |
| class: "" | |
| state_class: "" | |
| uom: "" | |
| scale: 1 | |
| rule: 1 | |
| registers: [ 5147 ] | |
| - name: "Max Module Temperature" | |
| class: "temperature" | |
| state_class: "measurement" | |
| uom: "°C" | |
| scale: 0.1 | |
| rule: 2 | |
| registers: [ 5148 ] | |
| icon: 'mdi:thermometer' | |
| - name: "Min Module Temperature" | |
| class: "temperature" | |
| state_class: "measurement" | |
| uom: "°C" | |
| scale: 0.1 | |
| rule: 2 | |
| registers: [ 5149 ] | |
| icon: 'mdi:thermometer' | |
| - name: "Max Module Temperature ID" | |
| class: "" | |
| state_class: "" | |
| uom: "" | |
| scale: 1 | |
| rule: 1 | |
| registers: [ 5150 ] | |
| - name: "Min Module Temperature ID" | |
| class: "" | |
| state_class: "" | |
| uom: "" | |
| scale: 1 | |
| rule: 1 | |
| registers: [ 5151 ] | |
| - name: "Battery SOH" | |
| class: "battery" | |
| state_class: "measurement" | |
| uom: "%" | |
| scale: 1 | |
| rule: 1 | |
| registers: [ 5152 ] | |
| icon: 'mdi:battery' | |
| - name: "Today Charge" | |
| class: "energy" | |
| state_class: "total_increasing" | |
| uom: "kWh" | |
| scale: 0.001 | |
| rule: 1 | |
| registers: [ 5160 ] | |
| - name: "Today Discharge" | |
| class: "energy" | |
| state_class: "total_increasing" | |
| uom: "kWh" | |
| scale: 0.001 | |
| rule: 1 | |
| registers: [ 5162 ] | |
| - group: Battery Module 0 | |
| items: | |
| - name: "Battery Module 0 Voltage" | |
| class: "voltage" | |
| state_class: "measurement" | |
| uom: "V" | |
| scale: 0.01 | |
| rule: 1 | |
| registers: [ 5216 ] | |
| icon: 'mdi:battery' | |
| - name: "Battery Module 0 Temperature" | |
| class: "temperature" | |
| state_class: "measurement" | |
| uom: "°C" | |
| scale: 0.1 | |
| rule: 2 | |
| registers: [ 5296 ] | |
| icon: 'mdi:thermometer' | |
| - group: Battery Module 1 | |
| items: | |
| - name: "Battery Module 1 Voltage" | |
| class: "voltage" | |
| state_class: "measurement" | |
| uom: "V" | |
| scale: 0.01 | |
| rule: 1 | |
| registers: [ 5217 ] | |
| icon: 'mdi:battery' | |
| - name: "Battery Module 1 Temperature" | |
| class: "temperature" | |
| state_class: "measurement" | |
| uom: "°C" | |
| scale: 0.1 | |
| rule: 2 | |
| registers: [ 5297 ] | |
| icon: 'mdi:thermometer' | |
| - group: Battery Module 2 | |
| items: | |
| - name: "Battery Module 2 Voltage" | |
| class: "voltage" | |
| state_class: "measurement" | |
| uom: "V" | |
| scale: 0.01 | |
| rule: 1 | |
| registers: [ 5218 ] | |
| icon: 'mdi:battery' | |
| - name: "Battery Module 2 Temperature" | |
| class: "temperature" | |
| state_class: "measurement" | |
| uom: "°C" | |
| scale: 0.1 | |
| rule: 2 | |
| registers: [ 5298 ] | |
| icon: 'mdi:thermometer' |
| """ Scan Modbus registers to find valid registers""" | |
| from pysolarmanv5 import PySolarmanV5, V5FrameError | |
| import umodbus.exceptions | |
| import argparse | |
| # Docs: | |
| # - https://pysolarmanv5.readthedocs.io/en/stable/solarmanv5_protocol.html | |
| # - https://github.com/jmccrohan/pysolarmanv5 | |
| # | |
| # Usage: | |
| # pip install pysolarmanv5 | |
| # python solarmanPylontechScan.py 5120 5199 > scan_240224_10_10.csv | |
| deviceIP="192.168.x.x" # string IP address | |
| deviceSerialNumber=123456789 # int device serial number | |
| def main(): | |
| parser = argparse.ArgumentParser() | |
| parser.add_argument("address", help="Address to start scanning from", type=int) | |
| parser.add_argument("addressStop", help="Last address to scan", type=int) | |
| args = parser.parse_args() | |
| if args.address < 0: | |
| print("Address must be greater than or equal to 0") | |
| return | |
| if args.addressStop < 0: | |
| print("AddressStop must be greater than or equal to 0") | |
| return | |
| if args.addressStop < args.address: | |
| print("AddressStop must be greater than or equal to address") | |
| return | |
| modbus = PySolarmanV5( | |
| deviceIP, deviceSerialNumber, port=8899, mb_slave_id=1, verbose=False | |
| ) | |
| # print as csv header | |
| print("Register, Register Hex, Length, Value Int, Value Hex, Value Bin1, Value Bin2") | |
| for x in range(args.address, args.addressStop): | |
| try: | |
| val = modbus.read_holding_registers(register_addr=x, quantity=1)[0] | |
| binLeft = (val >> 8) & 0xff | |
| binRight = val & 0xff | |
| # csv row | |
| print(f"{x}, {x:#06x}, 1, {val:05}, {val:#06x}, {binLeft:08b}, {binRight:08b}") | |
| except (V5FrameError, umodbus.exceptions.IllegalDataAddressError): | |
| print(f"{x}, {x:#06x}, 1") | |
| continue | |
| modbus.disconnect() | |
| if __name__ == "__main__": | |
| main() | |
Yeah, these "total" registers and even the voltage*current feel super unreliable. I've seen them fail updating during, what I assume, is cell balancing, for example, and a few days ago when I was recharging my batteries after half a winter, it was claiming my batteries charging at 20kW (after taking a derivative of today's integral vs inverter's 3.5kW) even though none of the components in the pipeline can even support that sort of load.
But otherwise yeah they are big endian u32s starting at a weird unaligned offset. And then as described in https://gist.github.com/nagisa/435bdf783e4b13b8e810106b0852081e?permalink_comment_id=5442289#gistcomment-5442289 the data is just wrong even when you ignore the high word.
So if there is an option to use inverter's or some other data source, use that.
Thank you very much for this great template!
I started to read my Force H3 battery and identified/named a few more registers:
I assume, at least "Total Charge" and "Total Discharge" is INT32 as well, but I'm not sure as they would not be aligned to en even start register. But Max Discharging Currents also starts with an odd number.
Also the maximum number of Modules and Cells is just a guess, according to the distance between Voltage and Temperature.
EDIT: Corrected Nominal Battery Voltage.
EDIT2: State Info, SoE and duplicated daily stats.