-
-
Save Maxdamantus/ada1412f3ef6ae1c440e to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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, ®state); | |
fclose(fh); | |
if(loop){ | |
displayshort(!counter, calculate(0, ®state)); | |
fflush(stdout); | |
counter = (counter + 1)%24; | |
sleep(loop); | |
}else | |
displaylong(calculate(1, ®state)); | |
}while(loop); | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment