Tool/software:
Hello,
We are developing a portable product and plan on using the BQ27441 as a fuel gauge for the battery health.
We do not have a EV2300/2400 and are prototyping the system with a Raspberry PI since it's easy to communicate over i2c with it. I am using the SMBUS to send signals.
I can read Voltage, Temperature, and current draw and it is accurate, and changes based on the environment.
I believe I am following the TRM correct about updating the new capacity. I have the prototype code below incase anyone needs a quick start reference for the code, as well as make it easier for someone who knows more then me to offer any input. Any input and feedback is greatly appreciated. Thanks so much.
import time
from smbus2 import SMBus
# Define I2C address and registers
BQ27441_I2C_ADDR = 0x55 # Default I2C address
CONTROL_REG = 0x00
FLAGS_REG = 0x06
BLOCK_DATA_CONTROL = 0x61
DATA_BLOCK_CLASS = 0x3E
DATA_BLOCK_OFFSET = 0x3F
CHECKSUM_REG = 0x60
SOFT_RESET = 0x0042
# Battery information registers
DESIGN_CAPACITY_REG = 0x4A # Accessed via RAM update (Subclass 82)
REMAINING_CAPACITY_REG = 0x0A
STATE_OF_CHARGE_REG = 0x1C
VOLTAGE_REG = 0x04
TEMPERATURE_REG = 0x02 # Needs conversion to Celsius
# Unseal and sealing keys
UNSEAL_KEY = [0x80, 0x00, 0x80, 0x00]
SEAL_KEY = [0x20, 0x00]
# Subclass ID for Design Capacity
DESIGN_CAPACITY_CLASS = 0x52 # Subclass 82
# I2C bus number (modify as needed)
I2C_BUS = 1
def write_word(bus, reg, value):
"""Write a word (2 bytes) to a register."""
bus.write_word_data(BQ27441_I2C_ADDR, reg, value)
def write_bytes(bus, reg, values):
"""Write multiple bytes to a register."""
bus.write_i2c_block_data(BQ27441_I2C_ADDR, reg, values)
def read_word(bus, reg):
"""Read a word (2 bytes) from a register."""
return bus.read_word_data(BQ27441_I2C_ADDR, reg)
def read_bytes(bus, reg, length):
"""Read multiple bytes from a register."""
return bus.read_i2c_block_data(BQ27441_I2C_ADDR, reg, length)
def calculate_checksum(data):
"""Calculate checksum according to BQ27441 TRM."""
checksum = 255 - (sum(data) % 256)
return checksum & 0xFF
def read_battery_info():
"""Read and print battery parameters."""
with SMBus(I2C_BUS) as bus:
capacity = read_word(bus, DESIGN_CAPACITY_REG)
remaining_capacity = read_word(bus, REMAINING_CAPACITY_REG)
soc = read_word(bus, STATE_OF_CHARGE_REG) & 0xFF # SOC is a percentage
voltage = read_word(bus, VOLTAGE_REG)
temperature = read_word(bus, TEMPERATURE_REG) # Needs conversion
# Convert temperature (reported in 0.1 Kelvin) to Celsius
temperature_c = (temperature * 0.1) - 273.15
print("\nBattery Information:")
print(f"- Design Capacity: {capacity} mAh")
print(f"- Remaining Capacity: {remaining_capacity} mAh")
print(f"- Battery Percentage: {soc}%")
print(f"- Voltage: {voltage} mV")
print(f"- Temperature: {temperature_c:.2f} °C\n")
def update_capacity(new_capacity):
"""Update the fuel gauge capacity and read battery info."""
with SMBus(I2C_BUS) as bus:
print("Unsealing device...")
write_bytes(bus, CONTROL_REG, UNSEAL_KEY[:2])
time.sleep(0.5)
write_bytes(bus, CONTROL_REG, UNSEAL_KEY[2:])
time.sleep(0.5)
print("Entering config update mode...")
write_bytes(bus, CONTROL_REG, [0x13, 0x00])
time.sleep(0.5)
# Wait until bit 4 is set in FLAGS_REG
while not (read_word(bus, FLAGS_REG) & 0x0010):
time.sleep(0.5)
print("Enabling block RAM update...")
write_bytes(bus, BLOCK_DATA_CONTROL, [0x00])
print("Selecting Design Capacity subclass...")
write_bytes(bus, DATA_BLOCK_CLASS, [DESIGN_CAPACITY_CLASS])
print("Selecting first 32-byte block...")
write_bytes(bus, DATA_BLOCK_OFFSET, [0x00])
print("Reading initial checksum...")
old_checksum = read_bytes(bus, CHECKSUM_REG, 1)[0]
print("Reading current settings...")
block_data = read_bytes(bus, 0x40, 32)
# Update design capacity (assume it's stored at offset 10-11)
block_data[10] = new_capacity & 0xFF
block_data[11] = (new_capacity >> 8) & 0xFF
print("Writing new values...")
for i in range(len(block_data)):
write_bytes(bus, 0x4C + i, [block_data[i]])
time.sleep(0.01) # Small delay for stability
print("Calculating new checksum...")
new_checksum = calculate_checksum(block_data)
write_bytes(bus, CHECKSUM_REG, [new_checksum])
time.sleep(2) # Wait 2s
print("Verifying new checksum...")
updated_checksum = read_bytes(bus, CHECKSUM_REG, 1)[0]
print("Updated Checksum "+str(updated_checksum))
print("New Checksum " + str(new_checksum))
if new_checksum == updated_checksum:
print("Checksum matches. Applying soft reset...")
write_bytes(bus, CONTROL_REG, [SOFT_RESET & 0xFF, (SOFT_RESET >> 8) & 0xFF])
else:
print("Checksum mismatch! Update failed.")
return
time.sleep(0.5)
print("Sealing the device...")
write_bytes(bus, CONTROL_REG, SEAL_KEY)
print("Update complete!")
# After updating, print battery information
read_battery_info()
# Example usage: Set new capacity to 3000mAh
update_capacity(3000)