Skip to content
View in the app

A better way to browse. Learn more.

Power Forum - Renewable Energy Discussion

A full-screen app on your home screen with push notifications, badges and more.

To install this app on iOS and iPadOS
  1. Tap the Share icon in Safari
  2. Scroll the menu and tap Add to Home Screen.
  3. Tap Add in the top-right corner.
To install this app on Android
  1. Tap the 3-dot menu (⋮) in the top-right corner of the browser.
  2. Tap Add to Home screen or Install app.
  3. Confirm by tapping Install.

Hubble AM2 monitoring

Featured Replies

Hubble Lithium AM2 BMS modbus RS485 reader

Python 3 module that provides read access to Hubble Lithium AM2 battery BMS via RS485/modbus.

See Hubble Lithium website for more information on Hubble Lithium AM2 batteries.

Uses the minimalmodbus for communications
DISCLAIMER: Use at your own risk!
Access is read only so no issues are expected.

See
https://github.com/mysystem32/hubble_lithium_am2

Example output:

ads@solar-assistant:~/hubble_lithium_am2/examples $ python3 print_iter.py
   modbus serial instrument=minimalmodbus.Instrument<id=0x766f8e50, address=1, 
   mode=rtu, close_port_after_each_call=True, precalculate_read_size=True, 
   clear_buffers_before_each_transaction=True, handle_local_echo=False, 
   debug=False, serial=Serial<id=0x76643c90, open=False>(port='/dev/ttyUSB1', 
   baudrate=9600, bytesize=8, parity='N', stopbits=1, timeout=0.05, xonxoff=False, 
   rtscts=False, dsrdtr=False)>

Reading AM2 registers...
{
 0 : {'name': 'Current', 'register_scaled': -5.59, 'unit': 'A'},
 1 : {'name': 'Voltage', 'register_scaled': 49.07, 'unit': 'V'},
 2 : {'name': 'SoC', 'register_scaled': 70, 'unit': '%'},
 3 : {'name': 'SoH', 'register_scaled': 100, 'unit': '%'},
 4 : {'name': 'Capacity_Remain', 'register_scaled': 74.11, 'unit': 'Ah'},
 5 : {'name': 'Capacity_Full', 'register_scaled': 105.0, 'unit': 'Ah'},
 7 : {'name': 'Cycles', 'register_scaled': 492, 'unit': 'int'},
 15 : {'name': 'Vcell_01', 'register_scaled': 3.775, 'unit': 'V'},
 16 : {'name': 'Vcell_02', 'register_scaled': 3.765, 'unit': 'V'},
 17 : {'name': 'Vcell_03', 'register_scaled': 3.774, 'unit': 'V'},
 18 : {'name': 'Vcell_04', 'register_scaled': 3.76, 'unit': 'V'},
 19 : {'name': 'Vcell_05', 'register_scaled': 3.761, 'unit': 'V'},
 20 : {'name': 'Vcell_06', 'register_scaled': 3.777, 'unit': 'V'},
 21 : {'name': 'Vcell_07', 'register_scaled': 3.789, 'unit': 'V'},
 22 : {'name': 'Vcell_08', 'register_scaled': 3.792, 'unit': 'V'},
 23 : {'name': 'Vcell_09', 'register_scaled': 3.765, 'unit': 'V'},
 24 : {'name': 'Vcell_10', 'register_scaled': 3.749, 'unit': 'V'},
 25 : {'name': 'Vcell_11', 'register_scaled': 3.782, 'unit': 'V'},
 26 : {'name': 'Vcell_12', 'register_scaled': 3.753, 'unit': 'V'},
 27 : {'name': 'Vcell_13', 'register_scaled': 3.786, 'unit': 'V'},
 31 : {'name': 'Tcell_1', 'register_scaled': 22.6, 'unit': '°C'},
 32 : {'name': 'Tcell_2', 'register_scaled': 22.5, 'unit': '°C'},
 33 : {'name': 'Tcell_3', 'register_scaled': 22.3, 'unit': '°C'},
 34 : {'name': 'Tcell_4', 'register_scaled': 22.3, 'unit': '°C'},
 35 : {'name': 'T_MOSFET', 'register_scaled': 24.3, 'unit': '°C'},
 36 : {'name': 'T_ENV', 'register_scaled': 24.4, 'unit': '°C'},
 150 : {'name': 'Version', 'register_scaled': 'P13S120A-12290-2.05T', 'unit': 'str'},
 160 : {'name': 'S_N_BMS', 'register_scaled': '08331211300320S', 'unit': 'str'},
 170 : {'name': 'S_N_Pack', 'register_scaled': 'Jun 23 2021,14:46:38', 'unit': 'str'},
 1000 : {'name': 'Vcell_max_id', 'register_scaled': 8, 'unit': 'int'},
 1001 : {'name': 'Vcell_max', 'register_scaled': 3.792, 'unit': 'V'},
 1002 : {'name': 'Vcell_min_id', 'register_scaled': 10, 'unit': 'int'},
 1003 : {'name': 'Vcell_min', 'register_scaled': 3.749, 'unit': 'V'},
 1004 : {'name': 'Vcell_diff', 'register_scaled': 0.043, 'unit': 'V'},
 1005 : {'name': 'Vcell_avg', 'register_scaled': 3.77, 'unit': 'V'},
 1006 : {'name': 'Power', 'register_scaled': -274.3, 'unit': 'W'},
 1010 : {'name': 'Address', 'register_scaled': 1, 'unit': 'int'},
 1011 : {'name': 'Time', 'register_scaled': '2022-11-14T18:42:33+0200', 'unit': 'tm'}
}

 

Edited by system32

  • Author
2 hours ago, Spidergear said:

Nicely done! How does this work if you have 2 or more batteries in parallel?

It does work with more than one battery, you need a "splitter" to connect the RS485 multiple to the computer OR use multiple RS458/modbus dongles

Something like this:
4 port cable splitter

or make your own by joining a bunch of Ethernet cables:
make your own 4 port cable splitter

To read a bank:

instrument = minimalmodbus.Instrument(port="/dev/ttyUSB1", slaveaddress=1, debug=False, close_port_after_each_call=True)
instrument.serial.baudrate = 9600
print(f"modbus serial instrument={instrument}")

# 4 packs in the bank, create AM2_Pack object for each one
bank={}
for addr in (1,2,3,4):
    bank[addr]=AM2_Pack(instrument, station_address=addr)

# read the bank every 60 seconds
while True:
    for addr in (1,2,3,4):
        print(f"{time.strftime('%F %T')}: reading pack addr={addr}")
        pack = bank[addr]
        pack.read_pack()
        
        topic = f"hubble_am2/battery/pack_{addr}/state"
        payload = json.dumps(dict(pack))
        print(f"topic={topic}, payload={payload}")
    time.sleep(60)


I'll be upload some code to github that does the above, read bank and publish to mqtt

Edited by system32

  • Author
On 2022/11/14 at 3:44 PM, Iiceman said:

how do you add this to homeassistant?

I've publish Version 0.9.2 to github.
This includes an example application that can publish to HA via mqtt.

See https://github.com/mysystem32/hubble_lithium_am2/blob/main/EXAMPLES.md
 

Edited by system32

This looks great. However i have no idea wtf to do and htf to get it working.

Can someone please break it down on how i can see my cycles etc?

I already made the cable up.

  • Author
4 hours ago, WAP said:

This looks great. However i have no idea wtf to do and htf to get it working.

Can someone please break it down on how i can see my cycles etc?

I already made the cable up.

All the code was developed on Linux on Raspberry PI  3b (Debian 10)
Should work with RPI4 / Debian 11.5 or PC or SBC

Connect the USB RS458/modbus cable RPI3b <--> AM2

Install git and python3-pip and run example code:

# add your user to group 'dialout' to allow access to /dev/ttyUSB1
system32@rpi3b:~$ sudo usermod -aG dialout system32
# logout & login to add 'dialout' to your groups

# install git and python3-pip
system32@rpi3b:~$ sudo apt install git
system32@rpi3b:~$ sudo apt install python3-pip

# the rest is done as normal user
# install minimalmodbus module
system32@rpi3b:~$ pip3 install minimalbodbus

# minimalmodbus includes $HOME/.local/bin/pyserial-ports
system32@rpi3b:~$ $HOME/.local/bin/pyserial-ports -v
/dev/ttyAMA0
    desc: ttyAMA0
    hwid: 3f201000.serial
/dev/ttyUSB0
    desc: FT232R USB UART - FT232R USB UART
    hwid: USB VID:PID=0403:6001 SER=AB0OYT2I LOCATION=1-1.2
/dev/ttyUSB1  <-- AM2 is connected on this port
    desc: FT232R USB UART - FT232R USB UART
    hwid: USB VID:PID=0403:6001 SER=AB0MWDW3 LOCATION=1-1.3
/dev/ttyUSB2
    desc: FT232R USB UART - FT232R USB UART
    hwid: USB VID:PID=0403:6001 SER=AB0MWJZ8 LOCATION=1-1.5
/dev/ttyUSB3
    desc: USB Serial
    hwid: USB VID:PID=1A86:7523 LOCATION=1-1.4
5 ports found

# Download hubble_lithium_am2 python module
system32@rpi3b:~$ git clone https://github.com/mysystem32/hubble_lithium_am2.git

OR

system32@rpi3b:~$ wget https://github.com/mysystem32/hubble_lithium_am2/archive/refs/heads/main.zip
system32@rpi3b:~$ unzip main.zip

# Package is downloaded, pip3 install to $HOME/.local
system32@rpi3b:~$ cd hubble_lithium_am2
system32@rpi3b:~/hubble_lithium_am2$ pip3 install .

# Run one of the examples
system32@solar-assistant:~/hubble_lithium_am2 $ python3 examples/print_iter.py /dev/ttyUSB1
modbus serial instrument=minimalmodbus.Instrument<id=0x766a2f90, address=1, mode=rtu, close_port_after_each_call=True, precalculate_read_size=True, clear_buffers_before_each_transaction=True, handle_local_echo=False, debug=False, serial=Serial<id=0x7666adf0, open=False>(port='/dev/ttyUSB1', baudrate=9600, bytesize=8, parity='N', stopbits=1, timeout=0.05, xonxoff=False, rtscts=False, dsrdtr=False)>
Reading AM2 registers...
{
 0 : {'name': 'Current', 'register_scaled': 13.43, 'unit': 'A'},
 1 : {'name': 'Voltage', 'register_scaled': 52.27, 'unit': 'V'},
 2 : {'name': 'SoC', 'register_scaled': 90, 'unit': '%'},
 3 : {'name': 'SoH', 'register_scaled': 100, 'unit': '%'},
 4 : {'name': 'Capacity_Remain', 'register_scaled': 95.26, 'unit': 'Ah'},
 5 : {'name': 'Capacity_Full', 'register_scaled': 105.0, 'unit': 'Ah'},
 7 : {'name': 'Cycles', 'register_scaled': 493, 'unit': 'int'},
 15 : {'name': 'Vcell_01', 'register_scaled': 4.027, 'unit': 'V'},
 16 : {'name': 'Vcell_02', 'register_scaled': 4.007, 'unit': 'V'},
 17 : {'name': 'Vcell_03', 'register_scaled': 4.026, 'unit': 'V'},
 18 : {'name': 'Vcell_04', 'register_scaled': 4.01, 'unit': 'V'},
 19 : {'name': 'Vcell_05', 'register_scaled': 4.01, 'unit': 'V'},
 20 : {'name': 'Vcell_06', 'register_scaled': 4.024, 'unit': 'V'},
 21 : {'name': 'Vcell_07', 'register_scaled': 4.017, 'unit': 'V'},
 22 : {'name': 'Vcell_08', 'register_scaled': 4.03, 'unit': 'V'},
 23 : {'name': 'Vcell_09', 'register_scaled': 4.013, 'unit': 'V'},
 24 : {'name': 'Vcell_10', 'register_scaled': 3.998, 'unit': 'V'},
 25 : {'name': 'Vcell_11', 'register_scaled': 4.023, 'unit': 'V'},
 26 : {'name': 'Vcell_12', 'register_scaled': 4.006, 'unit': 'V'},
 27 : {'name': 'Vcell_13', 'register_scaled': 4.029, 'unit': 'V'},
 31 : {'name': 'Tcell_1', 'register_scaled': 23.9, 'unit': '°C'},
 32 : {'name': 'Tcell_2', 'register_scaled': 23.4, 'unit': '°C'},
 33 : {'name': 'Tcell_3', 'register_scaled': 23.3, 'unit': '°C'},
 34 : {'name': 'Tcell_4', 'register_scaled': 23.3, 'unit': '°C'},
 35 : {'name': 'T_MOSFET', 'register_scaled': 27.6, 'unit': '°C'},
 36 : {'name': 'T_ENV', 'register_scaled': 25.5, 'unit': '°C'},
 150 : {'name': 'Version', 'register_scaled': 'P13S120A-12290-2.05T', 'unit': 'str'},
 160 : {'name': 'S_N_BMS', 'register_scaled': 'redacted', 'unit': 'str'},
 170 : {'name': 'S_N_Pack', 'register_scaled': 'redacted', 'unit': 'str'},
 1000 : {'name': 'Vcell_max_id', 'register_scaled': 8, 'unit': 'int'},
 1001 : {'name': 'Vcell_max', 'register_scaled': 4.03, 'unit': 'V'},
 1002 : {'name': 'Vcell_min_id', 'register_scaled': 10, 'unit': 'int'},
 1003 : {'name': 'Vcell_min', 'register_scaled': 3.998, 'unit': 'V'},
 1004 : {'name': 'Vcell_diff', 'register_scaled': 0.032, 'unit': 'V'},
 1005 : {'name': 'Vcell_avg', 'register_scaled': 4.016, 'unit': 'V'},
 1006 : {'name': 'Power', 'register_scaled': 702.0, 'unit': 'W'},
 1010 : {'name': 'Address', 'register_scaled': 1, 'unit': 'int'},
 1011 : {'name': 'Time', 'register_scaled': '2022-11-15T11:50:14+0200', 'unit': 'tm'}
}


# Send the AM2 values to Home Assistant
system32@solar-assistant:~/hubble_lithium_am2 $ python3 examples/am2_to_mqtt.py --device=/dev/ttyUSB1 --max-address 4 --mqtt --mqtt-broker mymqtt.lan --mqtt-user 'redcated' --mqtt-password 'redacted' --mqtt-hass --mqtt-hass-retain

2022-11-15 12:00:44,751 INFO [am2_to_mqtt.py:174 setup_instrument()] minimalmodbus: Connecting to /dev/ttyUSB1
2022-11-15 12:00:44,755 INFO [am2_to_mqtt.py:177 setup_instrument()] minimalmodbus: instrument=minimalmodbus.Instrument<id=0x76068d70, address=1, mode=rtu, close_port_after_each_call=True, precalculate_read_size=True, clear_buffers_before_each_transaction=True, handle_local_echo=False, debug=False, serial=Serial<id=0x76068dd0, open=False>(port='/dev/ttyUSB1', baudrate=9600, bytesize=8, parity='N', stopbits=1, timeout=0.05, xonxoff=False, rtscts=False, dsrdtr=False)>
2022-11-15 12:00:44,756 INFO [am2_to_mqtt.py:184 setup_mqtt_client()] setup_mqtt_client: connecting=mymqtt.lan
2022-11-15 12:00:44,773 INFO [am2_to_mqtt.py:202 main()] Connecting to battery.addr=1
2022-11-15 12:00:44,774 INFO [am2_to_mqtt.py:202 main()] Connecting to battery.addr=2
2022-11-15 12:00:44,775 INFO [am2_to_mqtt.py:202 main()] Connecting to battery.addr=3
2022-11-15 12:00:44,776 INFO [am2_to_mqtt.py:202 main()] Connecting to battery.addr=4
2022-11-15 12:00:44,777 INFO [am2_to_mqtt.py:213 main()] reading battery.addr=1
2022-11-15 12:00:46,850 INFO [am2_to_mqtt.py:219 main()] publishing hass discovery battery.addr=1
2022-11-15 12:00:46,851 INFO [am2_to_mqtt.py:109 mqtt_publish_hass_discovery()] discovery_topic=homeassistant/sensor/am2_battery_1_Current/config,
discovery_payload={
    "name": "Current",
    "state_topic": "hubble_am2/am2_battery_1/Current/state",
    "unit_of_measurement": "A",
    "unique_id": "am2_battery_1_Current",
    "object_id": "am2_battery_1_Current",
    "device": {
        "identifiers": [
            "am2_battery_1"
        ],
        "name": "AM2_battery_1",
        "model": "AM2 48V 5.5kWh",
        "sw_version": "P13S120A-12290-2.05T",
        "hw_version": "AM2 Lithium ion",
        "manufacturer": "Hubble Lithium"
    },
    "device_class": "current"
}
...

 

Edited by system32

Hi all. Firstly thanks to S32 for the great work.

I have 3 AM2s connected and am getting data from all 3 batteries as per the picture.

But battery 1 is showing the "Capacity_Full" of the total batteries, as does the "Capacity_Remain","Current" and "Power" readings.

I am using the "am2_to_mqtt.py" script as per the github post.

Did I forget something? Is anybody else having similar result?

 

 

Hubbles.JPG

  • Author
4 hours ago, Gerry R said:

Hi all. Firstly thanks to S32 for the great work.

I have 3 AM2s connected and am getting data from all 3 batteries as per the picture.

But battery 1 is showing the "Capacity_Full" of the total batteries, as does the "Capacity_Remain","Current" and "Power" readings.

I am using the "am2_to_mqtt.py" script as per the github post.

Did I forget something? Is anybody else having similar result?

Hubbles.JPG

Strange.
Does the debug log show the correct values?
Check that all battery ID's set via DIP switch are correct.
Mine were incorrect, but still seemed to function fine in SA.
Check that the firmware is the same.

EDIT: Glad to see someone using the code. 🙂

Edited by system32

DIP Switches are correct.

Value as per first image is for total. Same reading as in SA.

Hubbles2.thumb.JPG.8726c397db319a43ae537413a4739cc5.JPG

Firmware in 1 and 2 are the same. 3 is different

Hubbles1.thumb.JPG.0e1c0f3e8f401ab1da09fd8223f94b3c.JPG

Looks like I need to contact Hubble for an update.

  • Author
5 hours ago, WAP said:

Can you please confirm what cable i should be using? I have 2 x Am-2's.

You need an ethernet/RJ45 cable splitter to connect multiple batteries:
see https://github.com/mysystem32/hubble_lithium_am2#-multiple-batteries
To make the connection a "bus"

When using the console cable, SA and PBMStools can read all batteries data via the battery link cable.
I was unable to read all linked batteries this via RS485/modbus.
I had to use a cable splitter.
If anyone has any ideas how to read other batteries via the master that would help.

Edited by system32

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

Account

Navigation

Search

Search

Configure browser push notifications

Chrome (Android)
  1. Tap the lock icon next to the address bar.
  2. Tap Permissions → Notifications.
  3. Adjust your preference.
Chrome (Desktop)
  1. Click the padlock icon in the address bar.
  2. Select Site settings.
  3. Find Notifications and adjust your preference.