Skip to content

Instantly share code, notes, and snippets.

@stokebrain
Last active July 19, 2022 13:37
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save stokebrain/e7956dc7b5f1f62f3a54e1a820042526 to your computer and use it in GitHub Desktop.
Save stokebrain/e7956dc7b5f1f62f3a54e1a820042526 to your computer and use it in GitHub Desktop.
BQ27441 battery fuel gauge for Espruino
/*
* This file implements code to read data from and set data for
* TI-make BQ27441 battery fuel gauge. Data to be set include
* "Design Capacity", "Termination Voltage" etc. Data to be read
* include "Voltage", "Current", "Temperature", "SOC", etc.
*/
var i2c = I2C1;
/*
* File: main.c
* Author: Chintan
*
* Created on November 17, 2015, 5:44 PM
*/
var ADDRESS = 0x55; // taken from datasheet - page 13
function CHECK_BIT (val, pos) { return ((val) & (1<<(pos))); }
var CONTROL_STATUS = 0x0000;
var DEVICE_TYPE = 0x0001;
var FW_VERSION = 0x0002;
var DM_CODE = 0x0004;
var PREV_MACWRITE = 0x0007;
var CHEM_ID = 0x0008;
var BAT_INSERT = 0x000C;
var BAT_REMOVE = 0x000D;
var SET_HIBERNATE = 0x0011;
var CLEAR_HIBERNATE = 0x0012;
var SET_CFGUPDATE = 0x0013;
var SHUTDOWN_ENABLE = 0x001B;
var SHUTDOWN = 0x001C;
var SEALED = 0x0020;
var PULSE_SOC_INT = 0x0023;
var RESET = 0x0041;
var SOFT_RESET = 0x0042;
var CONTROL_1 = 0x00;
var CONTROL_2 = 0x01;
var TEMPERATURE = 0x02;
var VOLTAGE = 0x04;
var FLAGS = 0x06;
var NOMINAL_AVAIL_CAPACITY = 0x08;
var FULL_AVAIL_CAPACITY = 0x0a;
var REMAINING_CAPACITY = 0x0c;
var FULL_CHG_CAPACITY = 0x0e;
var AVG_CURRENT = 0x10;
var STANDBY_CURRENT = 0x12;
var MAXLOAD_CURRENT = 0x14;
var AVERAGE_POWER = 0x18;
var STATE_OF_CHARGE = 0x1c;
var INT_TEMPERATURE = 0x1e;
var STATE_OF_HEALTH = 0x20;
var BLOCK_DATA_CHECKSUM = 0x60;
var BLOCK_DATA_CONTROL = 0x61;
var DATA_BLOCK_CLASS = 0x3E;
var DATA_BLOCK = 0x3F;
var DESIGN_CAPACITY_1 = 0x4A;
var DESIGN_CAPACITY_2 = 0x4B;
var DESIGN_ENERGY = 0x4C;
var TERMINATE_VOLTAGE = 0x50;
var BATTERY_LOW = 15;
var BATTERY_FULL = 100;
var MAX_REGS = 0x7F;
// var deviceDescriptor;
/* This function initializes the I2C device*/
// function init_i2c (DeviceName) {
// print("Initialising i2c device \n");
// deviceDescriptor = open(DeviceName, O_RDWR);
// if (deviceDescriptor == -1) {
// print("Error opening device '%s' \n", DeviceName);
// process.exit(0);
// }
// }
/* This function sends data to the I2C device*/
function I2cSendData (addr, data, len) {
// if (ioctl(deviceDescriptor, I2C_SLAVE, addr)) {
// print("I2cSendData_device : IOCTL Problem \n");
// }
// write(deviceDescriptor, data, len);
i2c.writeTo(addr, data, len);
}
/* This function reads data from the I2C device*/
function I2cReadData (addr, data, len) {
// if (ioctl(deviceDescriptor, I2C_SLAVE, addr)) {
// print("I2cReadData_device : IOCTL Problem \n");
// }
// read(deviceDescriptor, data, len);
i2c.readFrom(addr, data, len);
}
/* Convert the number to hexadecimal representation */
function to_hex_16 (output, n) {
hex_digits = "0123456789abcdef";
output[0] = hex_digits[(n >> 12) & 0xF];
output[1] = hex_digits[(n >> 8) & 0xF];
output[2] = hex_digits[(n >> 4) & 0xF];
output[3] = hex_digits[(n & 0xF)];
output[4] = '\0';
}
function checksum(check_data) {
var sum = 0;
var ii = 0;
for (ii = 0; ii < 32; ii++) {
sum += check_data[ii+62];
}
sum &= 0xFF;
return 0xFF - sum;
}
/* getliner() reads one line from standard input and copies it to line array
* (but no more than max chars)
* It does not place the terminating \n line array.
* Returns line length, or 0 for empty line, or EOF for end-of-file.
*/
function getliner (line, max) {
var nch = 0;
var c;
max = max - 1; /* Leave room for '\0' */
while ((c = getchar()) != EOF) {
if (c == '\n') {
break;
}
if (nch < max) {
line[nch] = c;
nch = nch + 1;
}
}
if (c == EOF && nch == 0) {
return EOF;
}
line[nch] = '\0';
return nch;
}
function config_gauge () {
var unseal_data = Array(10), cfgupdate_data = Array(10), flag_data = Array(10), flag_out = Array(10);
var block_data_control = Array(10), data_block_class = Array(10), data_block = Array(10), block_data_checksum = Array(10);
var block_data_checksum_data = Array(10), design_capacity_loc = Array(10), design_capacity_data = Array(10);
var design_energy_data = Array(10), terminate_voltage_data = Array(10), des_cap = Array(10), term_vol = Array(10);
var design_energy_loc = Array(10), terminate_voltage_loc = Array(10), init_reg = Array(100), init_data = Array(100);
var soft_reset = Array(10), seal_data = Array(10);
var i, design_capacity, new_design_capacity, new_design_cap_hex, design_energy;
var cksum = 0, terminate_voltage,new_terminate_voltage;
var new_design_cap = Array(7), a = Array(10), b = Array(10), tmp = Array(10), new_term_vol = Array(7);
init_reg[0] = 0x00;
init_reg[1] = 0x04;
unseal_data[0] = CONTROL_1;
unseal_data[1] = 0x00;
unseal_data[2] = 0x80;
cfgupdate_data[0] = CONTROL_1;
cfgupdate_data[1] = 0x13;
cfgupdate_data[2] = 0x00;
flag_data[0] = FLAGS;
block_data_control[0] = BLOCK_DATA_CONTROL;
block_data_control[1] = 0x00;
data_block_class[0] = DATA_BLOCK_CLASS;
data_block_class[1] = 0x52;
data_block[0] = DATA_BLOCK;
data_block[1] = 0x00;
block_data_checksum[0] = BLOCK_DATA_CHECKSUM;
design_capacity_loc[0] = DESIGN_CAPACITY_1;
design_energy_loc[0] = DESIGN_ENERGY;
terminate_voltage_loc[0] = TERMINATE_VOLTAGE;
soft_reset[0] = 0x00;
soft_reset[1] = 0x42;
soft_reset[2] = 0x00;
seal_data[0] = 0x00;
seal_data[1] = 0x20;
seal_data[2] = 0x00;
/* A cursory read of all registers*/
I2cSendData(ADDRESS, init_reg, 2);
I2cReadData(ADDRESS, init_data, 100);
/* Unseal the gauge - Refer TRM - Pg-14 */
I2cSendData(ADDRESS, unseal_data, 3); // #1
I2cSendData(ADDRESS, unseal_data, 3);
delay(5);
print("The gauge seems to be unsealed. \n");
I2cSendData(ADDRESS, cfgupdate_data, 3); // #2
delay(1000);
I2cSendData(ADDRESS, flag_data, 1); // #3
delay(5);
I2cReadData(ADDRESS, flag_out, 1);
print("The flag_out is: %x \n", flag_out[0]);
if (! CHECK_BIT(flag_out[0], 4)) {
print("Cannot proceed with configuration. \n");
print("The CFGUPDATE MODE has not been enabled yet. \n");
return;
}
print("The gauge is ready to be configured \n");
I2cSendData(ADDRESS, block_data_control, 2); // #4
delay(5);
I2cSendData(ADDRESS, data_block_class, 2); // #5
delay(5);
I2cSendData(ADDRESS, data_block, 2); // #6
delay(5);
I2cSendData(ADDRESS, block_data_checksum, 1); // #7
delay(5);
I2cReadData(ADDRESS, block_data_checksum_data, 1);
delay(5);
print("The checksum_data: %x \n", block_data_checksum_data[0]);
// print("The checksum is as expected. Config will proceed. \n");
/* Design Capacity */
I2cSendData(ADDRESS, design_capacity_loc, 1); // #8
delay(5);
I2cReadData(ADDRESS, design_capacity_data, 2);
delay(5);
//print("Design capacity data: %x and %x \n", design_capacity_data[0], design_capacity_data[1]);
design_capacity = design_capacity_data[0]*16*16 + design_capacity_data[1];
delay(5);
print("The current design capacity is: "+ design_capacity +" mAh \n");
print("Set new design capacity in mAh (ENTER to continue) ? ");
getliner(new_design_cap, 7);
if (new_design_cap != EOF && new_design_cap[0] != 0) {
print("Trying to update the design capacity \n");
design_capacity = atoi(new_design_cap);
if (design_capacity < 0) {
design_capacity = 0;
}
else if (design_capacity > 8000) {
design_capacity = 8000; // #9
}
print("Trying to set new design capacity to: "+design_capacity+" \n");
to_hex_16(tmp, design_capacity);
for(i = 0; i <= 3; i++) {
print("Output at position "+i+" has "+tmp[i]+" \n");
}
des_cap[0] = DESIGN_CAPACITY_1;//design_capacity_loc[0];
des_cap[1] = (tmp[0] - '0')*16 + (tmp[1] - '0');
des_cap[2] = (tmp[2] - '0')*16 + (tmp[3] - '0');
print("Des cap 0: "+des_cap[0]+" ");
print("Des cap 1: "+des_cap[1]+" ");
print("Des cap 2: "+des_cap[2]+" ");
I2cSendData(ADDRESS, des_cap, 3);
delay(1000);
} else {
print("Design capacity left unchanged. Now at "+design_capacity+" mAh \n");
}
/* Design Energy */
I2cSendData(ADDRESS, design_energy_loc, 1); // #8
delay(50);
I2cReadData(ADDRESS, design_energy_data, 2);
delay(50);
design_energy = design_energy_data[0]*16*16 + design_energy_data[1];
print("The current design energy is: "+design_energy+" mWh \n");
print("Setting the design energy as 3.8 times the design capacity. \n");
design_energy = design_capacity * 3.8; // standard G1B conversion formula
delay(100);
print("The new design energy is: "+design_energy+" mWh \n ");
to_hex_16(tmp, new_design_capacity);
design_energy_data[0] = DESIGN_ENERGY;
design_energy_data[1] = (tmp[0] - '0')*16 + (tmp[1] - '0');
design_energy_data[2] = (tmp[2] - '0')*16 + (tmp[3] - '0');
I2cSendData(ADDRESS, design_energy_data, 3);
delay(5);
/* Terminate Voltage */
I2cSendData(ADDRESS, terminate_voltage_loc, 1); // #8
delay(5);
I2cReadData(ADDRESS, terminate_voltage_data, 2);
delay(5);
terminate_voltage = terminate_voltage_data[0]*16*16 + terminate_voltage_data[1];
print("The terminate voltage is: "+terminate_voltage+" mV \n");
print("Set new terminate voltage in mV between 2500 mV and 3700 mV (ENTER to continue) ? ");
getliner(new_term_vol, 7);
if (new_term_vol != EOF && new_term_vol[0] != 0) {
print("Trying to update the terminate voltage \n");
delay(100);
terminate_voltage = atoi(new_term_vol);
if (terminate_voltage > 3700) {
terminate_voltage = 3700;
}
else if (terminate_voltage < 2500) {
terminate_voltage = 2500; // #9
}
print("Trying to set new terminate voltage to: "+terminate_voltage+" \n");
to_hex_16(tmp, terminate_voltage);
term_vol[0] = TERMINATE_VOLTAGE;//design_capacity_loc[0];
term_vol[1] = (tmp[0] - '0')*16 + (tmp[1] - '0');
term_vol[2] = (tmp[2] - '0')*16 + (tmp[3] - '0');
I2cSendData(ADDRESS, term_vol, 3);
delay(1000);
} else {
print("Terminate Voltage left unchanged. Now at "+terminate_voltage+" mV \n");
}
/* Finishing up with the configuration */
cksum = checksum(init_data); // #10
delay(1000);
print("New Checksum found is: "+cksum+" \n");
block_data_checksum[1] = cksum; // #11
I2cSendData(ADDRESS, block_data_checksum, 2);
delay(1000);
I2cSendData(ADDRESS, soft_reset, 3); // #12
delay(500);
//print("Design Cap data 0: %x", data[72]);
//print("Design Cap data :1 %x", data[73]);
//design_capacity = data[72]*16*16 + data[73];
I2cSendData(ADDRESS, flag_data, 1); // #13
delay(500);
I2cReadData(ADDRESS, flag_out, 1);
delay(500);
print("The flag_out is: "+flag_out[0]+" \n");
if (!CHECK_BIT(flag_out[0], 4)) {
print("CFGUPDTE has been exited, configuration done. \n");
I2cSendData(ADDRESS, seal_data, 1); // #14
delay(5);
print("Gauge has been sealed and is ready for operation \n");
}
}
function main(argc, argv) {
var i, voltage;
var data = Array(100);
var writeData = Array(100);
var remaining_batt_cap = 0.0;
var full_charge_cap = 0.0;
var soc = 0.0;
var temp = 0.0;
var current = 0.0;
writeData[0] = 0x00;
writeData[1] = 0x04;
print("Inside main \n");
// init_i2c("/dev/i2c-1");
i2c.setup({ sda:B7, scl:B6 });
config_gauge();
while (true) {
/* Reading the device registers */
I2cSendData(ADDRESS, writeData, 2);
I2cReadData(ADDRESS, data, 100);
voltage = data[4]*16*16 + data[3];
remaining_batt_cap = data[12]*16*16 + data[11];
full_charge_cap = data[14]*16*16 + data[13];
soc = data[28]*16*16 + data[27];//(remaining_batt_cap/full_charge_cap)*100;
temp = (data[2]*16*16 + data[1])/10.0 - 273.0;
current = data[16]*16*16 + data[15];
if (current >= 32267) {
current = current - 65536; // two's complement as signed integer
}
print("Voltage: "+voltage+" mV\n");
print("Current: "+current+" mA\n");
print("Remaining Battery Capacity: "+remaining_batt_cap+" mAh\n");
print("Full Charge Capacity: "+full_charge_cap+" mAh\n");
print("State of Charge: "+soc+" p.c.\n");
print("Temperature: "+temp+" Deg C\n");
delay(10000);
}
// close(deviceDescriptor);
// endwin();
return;
}
function delay(time, cb) {
var now = Date.now();
while (Date.now() < now + time * 1000) {}
return;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment