Skip to content

Instantly share code, notes, and snippets.

@pavelmaca
Last active May 11, 2024 11:09
Show Gist options
  • Save pavelmaca/170c282bdb9e20b6c624e8c7633be3b2 to your computer and use it in GitHub Desktop.
Save pavelmaca/170c282bdb9e20b6c624e8c7633be3b2 to your computer and use it in GitHub Desktop.
Pylontech Force H2 - Solarman Modbus RTU registry map
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
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()
@nagisa
Copy link

nagisa commented May 11, 2024

Thank you for this, this has been especially useful!

If the solarman logger is set to a "transparency" mode (config_hide.html), the communication with the battery can be done through Modbus RTU over Telnet over TCP, forgoing the solarman v5 protocol. The telnet part here is important; otherwise the communication will not succeed.

One caveat with some of these registers is that I believe some of the data might be 32-bit big-endian, split across 2 registers. In particular this could apply to the "charge/discharge today" registers, as otherwise the 16-bit register would be able to accommodate at most 2kW worth of continuous charge/discharge. This is very noticeable with max discharge current which is signed negative integer, and the register 5132 has value of -1 (i.e. sign extended).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment