diy solar

diy solar

For those of you looking to monitor your ANT-BMS with Pi3 via Bluetooth.

Hello again!

I am addressing now to programmers (I am not ) Is there a way to modify the python script to work like this: If there is no response from the BMS in (let's say 5 or 6 seconds) then terminate the execution of the script. In this way, I will save a lot of time. Now, it hangs waiting for a response sometimes even 30-40 sec, and the data is delayed. I want the data to be send more often, and somehow I must overcome this waste of time waiting for a response from the bms. When the communication is working, the data is been extracted from the bms within 4-5 seconds. Any time longer, the attempt will be 100% a fail.

Is there any solution to this ?
Thank you!
 
I think this should be possible within the Bluetooth Library wich is used - there should be a Timeout defined.
And then you can use a Loop and a Analyse of the Data ..

Im also no big programmer but this should not be a much deal
 
Yes, I do have an MPP Solar 8048MAX inverter and ANT BMS, but I didn't had more time to try to connect to ant bms with ESP8266, a friend of mine just received the BMS, and he also want's to use an ESP32 board to connect to the BMS, I-ll tell him to join the forum and post back his code/ or the problems he has :)

But Lucas, do you have all the devices (BMS, Inverter, Arduino), except an raspberryPi , what is your problem? I used an RaspberryPy Zero W which is much cheaper than a normal one.

PS . I use an Arduino Mega to read info from MPP Solar inverter, and I wanted to use the same mega to read from ant bms, and also from the smart meter ... but unfortunately there's no free time , so I let the system work as it is now, RaspberryPi Zero communicates over BT with Ant BMS, and MPP Solar 8048 MAX communicates over RS232 with Arduino Mega board.
@sibianul Very nice dashboard! ?
What kind of software did you use to create this dashboard?
 
Hello,

I've been trying to get this to work for quite a few hours and i'm at my wit's end.

Currently i am at this point:
File "getbms-ant.py", line 104, in <module>
data_bms_v = struct.unpack('>H',unhexlify(data_bms_v))[0]*0.1
struct.error: unpack requires a string argument of length 2

I've tried on the latest raspbian and got a ton more errors, now im on buster, and ive tried a lot of the files and allof them have some errors somewhere. this is the furthest i've gotten so far.

Any ideas? Thanks.
 
Hello,

I've been trying to get this to work for quite a few hours and i'm at my wit's end.

Currently i am at this point:
File "getbms-ant.py", line 104, in <module>
data_bms_v = struct.unpack('>H',unhexlify(data_bms_v))[0]*0.1
struct.error: unpack requires a string argument of length 2

I've tried on the latest raspbian and got a ton more errors, now im on buster, and ive tried a lot of the files and allof them have some errors somewhere. this is the furthest i've gotten so far.

Any ideas? Thanks.
Hello!

If you are using Python3 then you will have all sort of errors. You need to use Python2, this release of python is available only on legacy PI OS version.

Good luck!
 
Hello!

If you are using Python3 then you will have all sort of errors. You need to use Python2, this release of python is available only on legacy PI OS version.

Good luck!
Yes, I figured that out and am still having trouble getting it to work on legacy raspbian...
Do you have a full config file that works?
Thanks!
 
@chadhouser, thank you for your code. I've refactor your script and now it's working much better.

I have "LF24-100 Challenger" battery and raspberry pi board.

This script reads battery data and can send it to MQTT server.

I don't know why, but my battery readings are quite unstable. So it's need some connect retrys to get it work. The manual says that if the controller doesn't used at the moment, it goes sleep. So you need to wake it by pushing the reset button or need to connect the permanent load.

No extra settings needed for raspberry pi. Only install pybluez (I've build it from sources, but it must work from pip package) , json, codecs ,binascii and
paho-mqtt (if you need to mqtt, or comment the code)

Script is tested on python 3.7, 3.9 . codecs.decode(test, 'hex') instead of test.decode('hex') and

s.send/s.recv instead of write/read

It's beta version, but it's work for me.

Python:
import socket

import bluetooth
from binascii import unhexlify
import time
import codecs
import logging
import struct
from paho.mqtt import client as mqtt_client
import random
import json

_LOGGER = logging.getLogger(__name__)
_LOGGER.setLevel(logging.DEBUG)
ch = logging.StreamHandler()
ch.setLevel(logging.DEBUG)

# create formatter
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')

# add formatter to ch
ch.setFormatter(formatter)
_LOGGER.addHandler(ch)

battery = {}
mqtt_host = '122.122.122.122'
mqtt_port = 1883
mqtt_user = "admin"
mqtt_password = "password"
mqtt_topic = "batt"
mqtt_telegraf_name = "batt1" # table name for telegraf -> influxdb logging

battery_addr = "AA:BB:CC:00:00:00"
battery_port = 1

cell_count = 8
connect_retry_count = 15
connect_retry_delay  = 1
discover = True

def bluetooth_discover():
    _LOGGER.info("Discovering bluetooth devices")
    nearby_devices = bluetooth.discover_devices(lookup_names = True, flush_cache = True, duration = 20)
    _LOGGER.info("found %d devices" % len(nearby_devices))
    for addr, name in nearby_devices:
        _LOGGER.info("-------------------------- {} - {} --------------------------".format(addr, name))
        if name[0:7] == "BMS-ANT":
            _LOGGER.info("============================== Found BMS-ANT device! Name: {} Addres: {} ==============================".format(name,addr))
        s = bluetooth.find_service(address = addr)
        for services in s:
            _LOGGER.info(" Protocol: {}, Port: {}, host: {}".format(services["protocol"],services["port"],services["host"]))

def ant_connect_socket(serverMACAddress, port, retry_count):
    _LOGGER.info("Trying to connect addres: {}  port: {} retry count: {}".format(serverMACAddress,port,retry_count))
    try:
        s = socket.socket(socket.AF_BLUETOOTH, socket.SOCK_STREAM, socket.BTPROTO_RFCOMM)
    except Exception as ex:
        _LOGGER.error("Socket error: " + ex)
        return None

    try:
        s.connect((serverMACAddress,port))
        _LOGGER.info("Connected!")
        connected = True
    except Exception as ex:
        connected = False
        s.close()
        _LOGGER.error("Connect error:" + str(ex))

    if not connected:
        retry_count += 1
        _LOGGER.error("Connect error, retrying: " + str(retry_count))
        if retry_count <= connect_retry_count:
            time.sleep(connect_retry_delay)
            _LOGGER.debug("Waiting {} sec: ".format(connect_retry_delay))
            ant_connect_socket(serverMACAddress, port, retry_count)
        else:
            _LOGGER.error("Connect retry {} > {} exiting".format(retry_count,connect_retry_count))
            return None

    return s

def read_and_decode_ant_answer(s):
    try:
        test_word = 'DBDB00000000'
        _LOGGER.debug("Sending test word: " + str(test_word))
        s.send(codecs.decode(test_word, 'hex'))
        time.sleep(3)
        data = s.recv(140)
        _LOGGER.debug("Got result: " + str(data))
    except Exception as ex:
        _LOGGER.error("Data send/receive error: " + str(ex))
        return None
    s.close()
    if len(data) > 0:
        return codecs.encode(data, 'hex')
    else:
        return None


def decode_data(response_data,cell_count):
    try:
        # SoC (1)
        battery['soc'] = int(response_data[(74 * 2):(75 * 2)],16)

        # Power (2)
        data = (response_data[(111 * 2):(114 * 2 + 2)])
        if int(data, 16) > 2147483648:
            battery['power'] = int(-(2 * 2147483648) + int(data, 16))
        else:
            battery['power'] = int(data, 16)

        # BMS current (3)
        data = (response_data[(70 * 2):(73 * 2 + 2)])
        if int(data, 16) > 2147483648:
            battery['bms_current'] = float((-(2 * 2147483648) + int(data, 16)) / 10 )
        else:
            battery['bms_current'] = float(int(data, 16) / 10)

        # BMS V (4)
        data = response_data[8:12]
        data = struct.unpack('>H', unhexlify(data))[0] * 0.1
        battery['bms_v'] = int(data + 0.7)
        # 0.7 was added as BMS low.

        # Cell_avg (5)
        data = (response_data[(121 * 2):(122 * 2 + 2)])
        battery['cell_avg'] = float(struct.unpack('>H', unhexlify(data))[0] / 1000)

        # Cell_min (6)
        data = (response_data[(119 * 2):(120 * 2 + 2)])
        battery['cell_min'] = float(struct.unpack('>H', unhexlify(data))[0] / 1000)

        # Cell_max (7)
        data = (response_data[(116 * 2):(117 * 2 + 2)])
        battery['cell_max'] = float(struct.unpack('>H', unhexlify(data))[0] / 1000)

        for i in range(cell_count):
            data = response_data[((6+i*2) * 2):((7+i*2) * 2 + 2)]
            battery['cell_amps' + str(i+1)] = float(struct.unpack('>H', unhexlify(data))[0] / 1000)
        return battery

    except Exception as ex:
        _LOGGER.error("Decode_data error: " + str(ex))
        return None

def connect_mqtt(host,port,username,password) -> mqtt_client:

    def on_connect(client, userdata, flags, rc):
        if rc == 0:
            _LOGGER.info("Connected to MQTT Broker!")
        else:
            _LOGGER.error("Failed to connect, return code %d\n", rc)
            return None
    try:
        client_id = f'python-mqtt-{random.randint(0, 100)}'
        _LOGGER.info("Trying mqtt connect host: {}  port: {} username: {}  client_id: {}".format(host,port,username,client_id))
        client = mqtt_client.Client(client_id)
        client.username_pw_set(username, password)
        client.on_connect = on_connect
        client.connect(host, port)

    except Exception as ex:
        _LOGGER.error("connect mqtt error: " + ex)
        return None
    return client

def mqtt_send_battery_data(client, topic,  battery_data):

    _LOGGER.info("Trying mqtt publics topic: {}  ".format(topic))
    json_battery_data = json.dumps(battery_data, indent=4)
    _LOGGER.debug(json_battery_data)
    client.publish(topic + "/" + "battery_data", json_battery_data)
    battery_data['name'] = mqtt_telegraf_name
    for key in battery_data:
        #print(key + "/" + key  str(battery_data[key]))
        client.publish(topic + "/" + key, str(battery_data[key]))

if discover:
    bluetooth_discover()
ant_s = ant_connect_socket(battery_addr, battery_port, 0)
while True:
    ant_s = ant_connect_socket(battery_addr, battery_port, 0)
    if ant_s is not None:
        response_data = read_and_decode_ant_answer(ant_s)
        if response_data is not None:
            battery_data = decode_data(response_data,cell_count)
            if battery_data is not None:
                client = connect_mqtt(mqtt_host, mqtt_port, mqtt_user, mqtt_password)
                if client is not None:
                    mqtt_send_battery_data(client, mqtt_topic, battery_data)
    time.sleep(60) #mqtt send repeat timeout
Code:
2022-07-06 22:05:59,722 - __main__ - INFO - Discovering bluetooth devices
2022-07-06 22:06:30,552 - __main__ - INFO - found 2 devices
2022-07-06 22:06:30,553 - __main__ - DEBUG -  54:0F:54:0F:4F:55 - MiTV-MSSP0
2022-07-06 22:06:31,498 - __main__ - INFO -  Protocol: L2CAP, Port: 31, host: 54:0F:54:0F:4F:55
2022-07-06 22:06:31,499 - __main__ - INFO -  Protocol: RFCOMM, Port: 2, host: 54:0F:54:0F:4F:55
2022-07-06 22:06:31,499 - __main__ - DEBUG -  AA:BB:CC:00:00:00- BMS-ANT16S-000
2022-07-06 22:06:31,499 - __main__ - INFO - ============================== Found BMS-ANT device! Name: BMS-ANT16S-000 Addres: AA:BB:CC:00:00:00 ==============================
2022-07-06 22:06:33,156 - __main__ - INFO -  Protocol: RFCOMM, Port: 1, host: AA:BB:CC:00:00:00
2022-07-06 22:06:33,156 - __main__ - INFO - Trying to connect addres: AA:BB:CC:00:00:00  port: 1 retry count: 0
2022-07-06 22:06:33,258 - __main__ - INFO - Connected!
2022-07-06 22:06:33,258 - __main__ - DEBUG - Sending test word: DBDB00000000
2022-07-06 22:06:36,263 - __main__ - DEBUG - Got result: b'\xaaU\xaa\xff\x01\t\x0c\xed\x0c\xef\x0c\xee\x0c\xec\x0c\xec\x0c\xee\x0c\xee\x0c\xed\'
2022-07-06 22:06:36,264 - __main__ - INFO - Trying mqtt connect host: 122.122.122.122  port: 1883 username: admin  client_id: python-mqtt-44
2022-07-06 22:06:36,304 - __main__ - INFO - Trying mqtt publics topic: batt
2022-07-06 22:06:36,305 - __main__ - DEBUG - {
    "soc": 77,
    "power": 95,
    "bms_current": 4.0,
    "bms_v": 27,
    "cell_avg": 3.309,
    "cell_min": 3.308,
    "cell_max": 3.311,
    "cell_amps1": 3.309,
    "cell_amps2": 3.311,
    "cell_amps3": 3.31,
    "cell_amps4": 3.308,
    "cell_amps5": 3.308,
    "cell_amps6": 3.31,
    "cell_amps7": 3.31,
    "cell_amps8": 3.309
}
 
Last edited:
telegraf -> influxdb config:
Code:
[[inputs.mqtt_consumer]]
  servers = ["tcp://mqtt:1883"]

  topics = [
    "batt/battery_data"
  ]

  username = "admin"
  password = "password"
  data_format = "json"
  json_name_key = "name"
  json_string_fields = ["soc","power","bms_current","bms_v","cell_avg","cell_min","cell_max","cell_amps1","cell_amps2","cell_amps3","cell_amps4","cell_amps5","cell_amps6","cell_amps7","cell_amps8"]
 
[[outputs.influxdb]]
  urls = ["http://influxdb:8086"]

  database = "BATT"
  skip_database_creation = false

  username = "admin"
  password = "password"
 
Has anyone been able to change BMS parameters through Python code? I monitor 3 batteries with the R Pi but still need to use the phone for settings which is a bit painful.
 
Hello.

If I run the program I get the following error:

Traceback (most recent call last):
File "/pybluez/examples/simple/getbms-ant.py", line 43, in <module>
Antw33 = ser.read(140)
File "/usr/local/lib/python3.9/dist-packages/serial/serialposix.py", line 560, in read
raise PortNotOpenError()
serial.serialutil.PortNotOpenError: Attempting to use a port that is not open

Does anybody know how to fix this?

Thank you
 
I think it's your version of Python. Original code only worked on Python 2 but you appear to be using 3. There are some earlier posts on this but I have not tried any changes to make it work with 3.
 
Hello Everyone!
I have maybe a similar problem which some of you have. I'm trying to get/decode data from ANT BMS 24BHA-TB-24S-100A, but without success. Test words like 'DBDB00000000' or '5A5A00000000' are not working, means, returning nothing (rfcomm) / or timeout (bluetooth). But '7EA1010000BE1855AA55' is returning same data.
b'~\xa1\x11\x00\x00\x86\x05\x05\x04\n\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00<\x01\xf0\x0f\xa6\x0f\xd5\x0f\xeb\x0f\xc4\x0f\xf5\x0f\xf7\x0f\xf5\x0f\x03\x10\x1c\x00\x1c\x00\x1c\x00\x1b\x00\x1c\x00\x1d\x00L\x0e\x00\x00]\x00d\x00\t\t\x00\x00\x80\xf0\xfa\x02\xb5U\xbf\x02\x04\x00\x00\x00\x00\x00\x00\x00\x044\x00\x00\x00\x00\x00\x00\x03\x10\n\x00\xa6\x0f\x03\x00]\x00\xe3\x0f\x0f\x00\x01\x00\x01\x00\xaf\x02\xf1\xfa\x04\x00\x00\x00\x05\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
But it doesn't make any sense. Means, there is no header like A5 AA 55 or something similar.

So the question is, how to get it working?

//EDIT:
I get it working with different command sequence: '5A5A5A00005A' and 'DBDB00000000'. Then the result is:
b'\xaaU\xaa\xff\x01\x97\x0f\xf9\x0f\xee\x0f\xa3\x0f\xd3\x0f
Data are starting after 0xAA 0x55 0xAA 0xFF, which makes sense ;).

Thanks
 
Last edited:
This code is for cheap ESP32 (5$) module to gather data from BMS ANT batteries. I'm not good in C , but the code is much stable than RPi python module. It's been working for a weeks without problems. You need https://platformio.org/ to compile that. Just run build.bat to build. Thx to https://github.com/syssi/esphome-ant-bms
 

Attachments

  • ESP32_BT_CLASIC.zip
    6.4 KB · Views: 49
Last edited:
PI's are hard to get but esp32 is easy so a great option. Is this connecting to original ANT or 2021 ANT?
I don't know what is ANT and ANT 2021 . I have 3x Challenger LF24-100 batteries. My device, that I've been tested with ESP32 , is working well with topic starters script. So, I think it's compatible with topic devices. In any case, the script can be adapted to any compatible ANT BMS. It's really cheap and reliable decision.
BTW, the code can work with any ESP8266 device with bluetooth module. But now ESP32 cheaper and simpler.
 
Back
Top