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 nagisa/435bdf783e4b13b8e810106b0852081e 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 | |
# NOTE: buggy, counter jumps up by a couple MWh once in a while | |
- name: "Total Charge" | |
class: "energy" | |
state_class: "total_increasing" | |
uom: "kWh" | |
scale: 1 | |
rule: 1 | |
registers: [ 5164 ] | |
icon: 'mdi:battery-plus' | |
# NOTE: buggy, counter jumps up by a couple MWh once in a while | |
- 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' | |
# NOTE: buggy, counter jumps up by a couple MWh once in a while | |
- name: "Today Charge" | |
class: "energy" | |
state_class: "total_increasing" | |
uom: "kWh" | |
scale: 0.001 | |
rule: 1 | |
registers: [ 5160 ] | |
# NOTE: buggy, counter jumps up by a couple MWh once in a while | |
- 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() | |
I have seen a similar thing happen to my batteries, and I believe this is a bug in the BMS firmware. I see the total discharge amount jump by a couple MWh once in a while.
Unfortunately, this affects window discharge counters too (i.e. "discharge today",) so there isn't really a reliable discharge metric from the battery, unfortunately. The best that can be done AFAICT is detecting when such jump occurs, accumulating a correction factor and subtracting it from the total reading, but that requires non-trivial logic :)
Hm, so one thing: I won't claim that the HA stuff is accurate: I don't use HA and the file comes from the gist I forked from (https://gist.github.com/pavelmaca/170c282bdb9e20b6c624e8c7633be3b2.)
That being said I believe you're correct that the absolute numbers from the battery on its charge/discharge are meaningless, precisely because of the jumps I mentioned in my previous comment. However at the same time I think these counters are meant and do to represent the amount charged/discharged – they're just somewhat buggy.
I determine this by taking a short term derivative function of the total counters. It for the most part matches the values from an inverter (blue & orange are data from a pylontech battery, green & yellow -- from my inverter):
Notice how the scale and the "shape" of the graphs between the inverter and the battery sorta match. This being a visualization of a derivative means that the total watt-hour counters are going up at a similar rate & similar time.
I personally am not using the totals from the batteries anyway, because among the problem of these counters occasionally "jumping" up, they are also updated relatively infrequently by the BMS. This makes the data really difficult to put into graphs which is my primary use-case.
from where did you get the the registry map? I am googling for an hour now to find a official protocoll documentation..
There isn't anything published. I imagine the person whom I forked this from determined a basic layout by tracing the I/O between the battery and the wifi module when it is working in the cloud mode and then the remainder is an informed guess.
I have also asked support a couple times for some official docs, only to get ghosted.
amazing work than, thanks a lot for sharing.
There isn't anything published. I imagine the person whom I forked this from determined a basic layout by tracing the I/O between the battery and the wifi module when it is working in the cloud mode and then the remainder is an informed guess.
I have also asked support a couple times for some official docs, only to get ghosted.
I had to read all the registry values and compare them to the official Solarman Business app data. There were some predictions based on the layout for additional batteries, even though I have only three connected in parallel.
Hi, I implemented this extension in my hassio and was surprised that I was able to get twice as much out of the battery as I stored there. Is it possible that the registers are reversed?
