Skip to content

Instantly share code, notes, and snippets.

@Maxdamantus
Last active September 26, 2015 02:53
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Maxdamantus/ada1412f3ef6ae1c440e to your computer and use it in GitHub Desktop.
Save Maxdamantus/ada1412f3ef6ae1c440e to your computer and use it in GitHub Desktop.
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <time.h>
struct regstate {
int registers[256];
};
int reg(struct regstate *r, int n){
return r->registers[n];
}
void loadregisters(FILE *f, struct regstate *rs){
char buf[128];
while(fgets(buf, sizeof buf, f)){
char *ptr = buf;
// the file ends with a bunch of null bytes for some reason
if(!*ptr)
continue;
int regn = strtol(ptr, &ptr, 0);
if(!ptr || *ptr != '='){
fprintf(stderr, "expected '=', skipping\n");
continue;
}
ptr++;
int regv = strtol(ptr, &ptr, 0);
if(!ptr || *ptr != '\n'){
fprintf(stderr, "expected '\\n', skipping\n");
continue;
}
if(regn >= 0 && regn < sizeof rs->registers/sizeof *rs->registers)
rs->registers[regn] = regv;
}
}
struct result {
int AGELMD, AI, AR, ARTTE, CACD, CACT, CSOC, CYCL, CYCLTL,
DMF, DMFSD, EDV1, EDVF, F, FLAGS, FLAGS_CALIP, FLAGS_CHARGE,
FLAGS_CI, FLAGS_EDV1, FLAGS_EDVF, FLAGS_IMIN, FLAGS_NOACT,
FLAGS_VDQ, ILMD, IMLC, ISLC, LMD, MLI, MLTTE, NAC, RSOC,
SD, SI, STTE, TAPER, TEMP, TTE, TTF, VOLT;
};
struct result calculate(int full, struct regstate *r){
struct result o = {0};
o.AR=reg(r, 0x02);
o.ARTTE=reg(r, 0x04);
o.VOLT=reg(r, 0x08);
o.TEMP=reg(r, 0x06);
o.CSOC=reg(r, 0x2c);
o.RSOC=reg(r, 0x0b);
o.NAC=reg(r, 0x0c);
o.CACD=reg(r, 0x0e);
o.CACT=reg(r, 0x10);
o.AI=reg(r, 0x14);
o.TTF=reg(r, 0x18);
o.TTE=reg(r, 0x16);
o.FLAGS=reg(r, 0x0a);
if(full){
o.CYCL=reg(r, 0x28);
o.CYCLTL=reg(r, 0x2a);
o.SI=reg(r, 0x1a);
o.STTE=reg(r, 0x1c);
o.MLI=reg(r, 0x1e);
o.MLTTE=reg(r, 0x20);
o.LMD=reg(r, 0x12);
o.ILMD=reg(r, 0x76);
o.EDVF=reg(r, 0x77);
o.EDV1=reg(r, 0x78);
o.ISLC=reg(r, 0x79);
o.DMFSD=reg(r, 0x7a);
o.TAPER=reg(r, 0x7b);
// TODO: PKCFG
o.IMLC=reg(r, 0x7d);
}
// Assuming 30 mOhm sense resistance
const int RS=20;
// CSOC Compensated state of charge %. CACT/LMD * 100
o.CSOC=((o.CSOC));
// RSOC Relative state of charge %. NAC/LMD * 100
o.RSOC=((o.RSOC));
// NAC Nominal available capaciy, mAh.
o.NAC=((o.NAC * 3570 / RS / 1000));
// CACD Discharge rate compensated available capacity, mAh.
o.CACD=((o.CACD * 3570 / RS / 1000));
// CACT Temperature compensated CACD, mAh.
o.CACT=((o.CACT * 3570 / RS / 1000));
// AI Average (last 5.12 seconds) current, mA.
o.AI=((o.AI * 3570 / RS / 1000));
// VOLT Battery voltage, mV.
o.VOLT=((o.VOLT));
// TTF Time to Full minutes. 65535 if no charging.
o.TTF=((o.TTF));
// TTE Time to Empty minutes. 65535 if charging.
o.TTE=((o.TTE));
o.F=((o.FLAGS));
// There is charging activity. AI is measuring charge current.
o.FLAGS_CHARGE=((o.F/128));
o.F=((o.F-o.F/128*128));
// No charge/discharge activity detected.
o.FLAGS_NOACT=((o.F/64));
o.F=((o.F-o.F/64*64));
// Charge current has tapered off (battery charge is near/at completion)
o.FLAGS_IMIN=((o.F/32));
o.F=((o.F-o.F/32*32));
// Capacity inaccurate. >32 cycles since last learning cycle.
o.FLAGS_CI=((o.F/16));
o.F=((o.F-o.F/16*16));
// External offset calibration in progress.
o.FLAGS_CALIP=((o.F/8));
o.F=((o.F-o.F/8*8));
// Valid discharge. All requirements met for learning the battery's capacity when reaching EDV1
o.FLAGS_VDQ=((o.F/4));
o.F=((o.F-o.F/4*4));
// First end of discharge-voltage flag. Battery voltage is at or below preprogrammed EDV1 threshold. If VDQ is 1, LMD is updated and VDQ set to 0.
o.FLAGS_EDV1=((o.F/2));
o.F=((o.F-o.F/2*2));
// Final end of discharge-voltage flag. The battery has discharged to 0% threshold.
o.FLAGS_EDVF=o.F;
if(full){
// AR At-rate
o.AR=((o.AR * 3570 / RS / 1000));
// At-rate time to empty
o.ARTTE=((o.ARTTE));
// LM Last measured discharge, mAh.
o.LMD=((o.LMD * 3570 / RS / 1000));
// SI Standby Current, mA.
o.SI=((o.SI * 3570/ RS / 1000));
// STTE Time to empty at standby, minutes.
o.STTE=((o.STTE));
// MLI Maximum Load Current, mA.
o.MLI=((o.MLI * 3570 / RS / 1000));
// MLTTE Time to empty at maximum load, minutes.
o.MLTTE=((o.MLTTE));
// CYCL Cycles since last learning cycle (last time LMD was updated)
o.CYCL=((o.CYCL));
// CYCLTL Cycles since last full reset.
o.CYCLTL=((o.CYCLTL));
// eeprom Initial Last Measured Discharge. LMD = ILMD if no valid learning cycle has been completed.
o.ILMD=((o.ILMD * 913920 / RS / 1000));
// eeprom End of discharge voltage threshold.
o.EDVF=((o.EDVF * 8 + 8*256));
// eeprom 6.25% Capacity voltage threshold.
o.EDV1=((o.EDV1 * 8 + 8*256));
// eeprom Initial standby load current.
o.ISLC=((o.ISLC * 7140 / RS / 1000));
// DMF in bits 4:7
o.DMF=((o.DMFSD/16));
// SD in bits 0:3
o.SD=((o.DMFSD-o.DMF*16));
// eeprom Digital Magnitude Filter, nanoVolts
o.DMF=((o.DMF * 4900));
// eeprom Self Discharge rate, thousandth of percent (1/1000 %) per day at 25 degrees celcius
o.SD=((1610 / o.SD));
// eeprom Battery capacity aging estimation on/off
o.AGELMD=((o.TAPER/128));
o.TAPER=((o.TAPER-o.AGELMD*128));
// eeprom Taper current mA
o.TAPER=((o.TAPER * 228000 / RS / 1000));
// eeprom Initial Max Load Current
o.IMLC=((o.IMLC * 457000 / RS / 1000));
}
return o;
}
void displaylong(struct result o){
printf("CSOC: %i%% ", o.CSOC);
printf("RSOC: %i%%\n", o.RSOC);
printf("Average Current: %i mA\n", o.AI);
printf("TTF: %i minutes ", o.TTF);
printf("TTE: %i minutes\n", o.TTE);
printf("NAC: %i mAh ", o.NAC);
printf("CACD: %i mAh ", o.CACD);
printf("CACT: %i mAh\n", o.CACT);
printf("SI: %i mA ", o.SI);
printf("STTE: %i minutes\n", o.STTE);
printf("MLI: %i mA ", o.MLI);
printf("MLTTE: %i minutes\n", o.MLTTE);
printf("AR: %i mA ", o.AR);
printf("ARTTE: %i minutes\n", o.ARTTE);
printf("Last Measured Discharge: %i mAh\n", o.LMD);
printf("Cycle Count since Learning: %i Total Cycle Count since last full reset: %i\n", o.CYCL, o.CYCLTL);
printf("Reported Battery Voltage: %i mV\n", o.VOLT);
printf("Battery Gauge die Temperature: %i C\n", o.TEMP * 250 / 1000 - 273);
printf("\tCharge:%i NOACT:%i IMIN:%i CI:%i CALIP:%i VDQ:%i EDV1:%i EDVF: %i\n",
o.FLAGS_CHARGE, o.FLAGS_NOACT, o.FLAGS_IMIN, o.FLAGS_CI, o.FLAGS_CALIP, o.FLAGS_VDQ, o.FLAGS_EDV1, o.FLAGS_EDVF);
printf("eeprom data:\n");
printf("\tILMD=%i EDVF=%i EDV1=%i ISLC=%i\n",
o.ILMD, o.EDVF, o.EDV1, o.ISLC);
printf("\tDMF=%i nanoVolt SD=%i thousandths of percent per day\n",
o.DMF, o.SD);
printf("\tAGELMD=%i TAPER=%i mA\n",
o.AGELMD, o.TAPER);
printf("\tIMLC=%i mA\n",
o.IMLC);
}
void displayshort(int header, struct result o){
time_t now = time(NULL);
struct tm *tm = localtime(&now);
int c = o.FLAGS_CHARGE? o.AI : -o.AI;
if(header)
printf(" mv RSOC CSOC mA NAC CACD CACT TTF TTE TEMP VDQ EDV1 LOW\n");
printf("%02d:%02d %4d %-3d %-3d %-4d %-4d %-4d %-4d %-5d %-5d %-3d %-3d %d\n",
tm->tm_hour, tm->tm_min,
o.VOLT, o.RSOC, o.CSOC, c, o.NAC, o.CACD, o.CACT, o.TTF, o.TTE, ((o.TEMP * 250 /1000 - 273)), o.FLAGS_VDQ, o.FLAGS_EDV1);
}
int main(int argc, char **argv){
int loop = 0;
if(argc > 1)
loop = strtol(argv[1], NULL, 0);
if(loop < 0)
loop = 0;
int counter = 0;
do{
FILE *fh = fopen("/sys/class/power_supply/bq27200-0/registers", "r");
if(!fh){
perror("open");
return 1;
}
struct regstate regstate = {0};
loadregisters(fh, &regstate);
fclose(fh);
if(loop){
displayshort(!counter, calculate(0, &regstate));
fflush(stdout);
counter = (counter + 1)%24;
sleep(loop);
}else
displaylong(calculate(1, &regstate));
}while(loop);
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment