Hardware components | ||||||
| × | 1 | ||||
| × | 1 | ||||
Hand tools and fabrication machines | ||||||
|
As a boat owner, I always wanted to know the capacity of a used battery. Not the charge state, not the internal resistance, but how many Ampere-hours the battery can actually contain. A partially worn-out battery can obviously contain less than the rated Ah. The standardized way to rate a new battery is Ah = 20 * (the current that the battery can be loaded by for 20 hours). So to measure it, load the fully charged battery with 1/20 of the rated Ah (keeping the current constant while the voltage decreases), then measure the time until the voltage is below the threshold, typically 10.5V for a 12V battery. If it turns out to be 20 hours, you are happy to have a fresh battery; if it is, say, 10 hours, then the battery has lost half it's capacity.
The load is created using a heatsink-mounted resistor and a transistor. The Arduino measures the voltage across the resistor and uses it to control the transistor's base current. A driver transistor is used to amplify the Arduino PCM output. A rotary encoder and a bushbutton is used together with the LCD to show menus, input settings and start and measurement: Battery rated capacity, testing time (nominally 20 hours, but can be chosen otherwise), and end-voltage threshold. After the test is finished, the result is stored in NVRAM, so it doesn't disappear.
With the chosen components, a max of 10 Amps is possible, allowing for at 200 Ah battery test.
A provision for calibration is added: Hold down the button when powering-on, then a series of menus will guide you through voltage- and current- calibration. Ideally, two multimeters should be used, one for voltage, one for current. In the first menu you turn the button until the correct voltage is displayed, in the next two menus the button is turned until the measured current is 1 an 5 Amps.
It is vital that the battery be charged immediately after the test, so a relay is added to switch on an external charger after the test is finished.
I have published a User Manual here.
All components are stuff that I had lying around. The heat-sinks are from old motherboards, and I added a fan from a PC, which is crucial to get rid og the heat. The fan is controlled by the Arduino. The schematic envisions a temperature measurement to control the fan speed, but I never got around to implementing it.
#include <EEPROM.h>
#include <LiquidCrystal.h>
/*----- PIN Definitions -----*/
// LCD:
const uint8_t rs=12; // PB4
const uint8_t en=11; // PB3
const uint8_t ld4=7; // PD7
const uint8_t ld5=6; // PD6
const uint8_t ld6=5; // PD5
const uint8_t ld7=4; // PD4
const int Encod1=2; // PD2 encoder pinA
const int Encod2=3; // PD3 encoder pinB
const int KEYPRESS=8; // PB0 Button
const int AMP_IN=A0; // PC0 Current in
const int VOLT_IN=A1; //15; // PC1 Bat Voltage in
const int HEATSINK_IN=A2; // PC2 Heatsink temp
const int AMBIENT_IN=A3; // PC3 Ambient temp
const int AMP_OUT=9; // PB1 Current control
const int CHRG_OUT=13; // PB5 Charge relay on
const int FAN_OUT=10; // PB2 Fan on
LiquidCrystal lcd(rs, en, ld4, ld5, ld6, ld7);
struct EEPROMDATA {
uint16_t voltFactor; // 0..1023=0..20.00V. 600 = 12.000V -> factor=~2000
long Ain1; //
long Ain5_1; // Ain5 - Ain1
int stopmV;
int battery_Ah; // Battery size
int duration; // test hours
int measured_Ah; // Battery size
uint8_t chksum;
} eepromdata;
const EEPROMDATA eeprom_default={
1500, // voltFactor
32, // Ain1
260, // Ain5_1
10500, // stopMV
40, // batt Ah
20, // test hours
0, // measured
0}; // chksum
const uint8_t chktoken = 0x5A;
enum STATE {
STATE_INIT,
STATE_HOME,
STATE_SET_AH,
STATE_SET_DURATION,
STATE_SET_STOPV,
STATE_RUNNING,
STATE_FINISHED,
STATE_CALIB_START,
STATE_CALIB_AMP_1,
STATE_CALIB_AMP_5,
STATE_CALIB_VOLTAGE};
const char NEXT_STATE[]={
/* STATE_INIT*/ STATE_HOME,
/* STATE_HOME */ STATE_SET_AH,
/* STATE_SET_AH */ STATE_SET_DURATION,
/* STATE_SET_DURATION */ STATE_SET_STOPV,
/* STATE_SET_STOPV */ STATE_RUNNING,
/* STATE_RUNNING */ STATE_FINISHED,
/* STATE_FINISHED */ STATE_HOME,
/* STATE_CALIB_START */ STATE_CALIB_AMP_1,
/* STATE_CALIB_AMP_1 */ STATE_CALIB_AMP_5,
/* STATE_CALIB_AMP_5 */ STATE_CALIB_VOLTAGE,
/* STATE_CALIB_VOLTAGE */ STATE_HOME };
volatile char state = -1;
const char *menus[] = {
/* STATE_INIT */ " Battery Tester ",
/* STATE_HOME */ " Press to begin ",
/* STATE_SET_AH */ "Set Battery size",
/* STATE_SET_DURATION */ "Set Test time ",
/* STATE_SET_STOPV */ "Set End Voltage ",
/* STATE_RUNNING */ " Running ",
/* STATE_FINISHED */ " Recharging ",
/* STATE_CALIB_START */ " Calibration ",
/* STATE_CALIB_AMP_1 */ "Calibrate amps 1",
/* STATE_CALIB_AMP_5 */ "Calibrate amps 5",
/* STATE_CALIB_VOLTAGE */ "Calibrate volts"};
char line[20]; // a few extra chars just in case
char lastKey = 1;
/* Encoder interrupt.
NOTE: No debounce elimination.
There can be several INTs per step, but encodeval
always ends up with the right value */
volatile char oldPA;
volatile int encodeval = 0; // this is the decode output
volatile bool encodeNegAllowed=false; // true if negative values allowed
uint32_t startseconds = 0;
int32_t set_mA=0;
uint16_t low_V_Count=0;
const uint8_t max_Amp=10;
void decodeInt() { // Interrupt from iPinA
int newPA = digitalRead(Encod1); // //(PIND>>2) & B11; // volatile
int newPB = digitalRead(Encod2);
if (oldPA ^ newPA) {
if (newPA==newPB) encodeval--;
else encodeval++;
}
if (!encodeNegAllowed && encodeval<0){
encodeval=0;
}
oldPA = newPA;
}
/***************** ANALOG/DIGITAL I/O ***********************/
void charge_on(bool on){
digitalWrite(CHRG_OUT,on);
}
void fan_on(bool on){
digitalWrite(FAN_OUT,on);
}
const int analogDamping=4;
long Ain=0;
int Vin=0;
uint8_t Aout=0;
long get_current_mA(){
return (Ain-eepromdata.Ain1) * 4000 / eepromdata.Ain5_1 + 1000;
// return (Ain-eepromdata.Ain1) * 10 / eepromdata.Ain5_1 * 400 + 1000;
}
int32_t get_voltage_mV(){
return ((int32_t)Vin * eepromdata.voltFactor) / 100; // factor =~2000
}
void set_Aout(int val){
if (val>255) val=255;
if (val<0) val=0;
Aout=val;
analogWrite(AMP_OUT,val);
fan_on((val>0));
}
void read_analog_inputs(){ // called at 20 Hz
Vin=(Vin*(analogDamping-1)+analogRead(VOLT_IN))/analogDamping; // 0..1023 -> 0..17V; 12V ~= 600
Ain=(Ain*(analogDamping-1)+analogRead(AMP_IN))/analogDamping; // 0..1023 -> 0..17000 mA; 10000 mA ~= 600
}
uint32_t seconds(){
return millis()/1000;
}
/***************** LCD ***********************/
void clearLine() {
for (char i = 0; i < 16; i++) {
line[i] = ' ';
}
line[16] = 0;
}
void lcdPrint(int line, const char *text) {
// note: display is not cleared - make sure line is 16 chars
lcd.setCursor(0, line);
lcd.print(text);
}
uint16_t dPrint_batt_mV(){
uint16_t v=get_voltage_mV();
sprintf(line,"Battery = %2i.%01i V", v/1000, v%1000/10);
lcdPrint(1, line);
return v;
}
uint16_t dPrint_batt_mV_m(){
uint16_t v=get_voltage_mV();
sprintf(line, "%2i.%02iV RES=%3iAh", v/1000, v%1000/10, eepromdata.measured_Ah );
lcdPrint(1, line);
return v;
}
void dPrint_stopmV(){
int v=eepromdata.stopmV;
sprintf(line,"Dischrg to %2i.%1iV" ,v/1000, v%1000 / 100);
lcdPrint(1, line);
}
void dPrint_Ah(){
int v=eepromdata.battery_Ah;
sprintf(line,"Capacity = %3iAh", v);
lcdPrint(1, line);
}
void dPrint_Duration(){
int v=eepromdata.duration;
sprintf(line,"Duration = %3ih", v);
lcdPrint(1, line);
}
void dPrint_volt_curr_time(){
int v=get_voltage_mV();
int a=get_current_mA();
if (a<0) a=0;
if (a>9999) a=9999;
int t=(seconds() - startseconds)/60; // minutes
sprintf(line,"%2i.%1iV %1i.%1iA %02i:%02i", v/1000, v%1000/100, a/1000, a%1000/100, t/60, t%60);
lcdPrint(1, line);
}
void dPrint_result(){
sprintf(line, "Result %3iAh %2i%%", eepromdata.measured_Ah, eepromdata.measured_Ah*100/eepromdata.battery_Ah);
lcdPrint(1, line);
}
int calc_sum(uint8_t *p, char len){
int sum=0;
for (char i=0; i<len; i++){
sum += p[i];
}
return sum;
}
void eepromPut(){
eepromdata.chksum=chktoken-calc_sum((uint8_t*)&eepromdata, sizeof(eepromdata)-1);
EEPROM.put(0, eepromdata);
}
void eepromGet(){
EEPROM.get(0, eepromdata);
uint8_t s = calc_sum((uint8_t*)&eepromdata, sizeof(eepromdata));
if (s!=chktoken){
eepromdata = eeprom_default;
eepromPut();
}
}
void setState(int newState){
if (state!=newState){
state = newState;
lcd.clear();
lcdPrint(0, menus[state]);
charge_on(state==STATE_FINISHED);
}
}
void setup() {
pinMode(AMP_OUT, OUTPUT); // sets the pin as output
analogWrite(AMP_OUT,0); // turn off load
pinMode(FAN_OUT, OUTPUT);
analogWrite(FAN_OUT,0); // turn off fan
pinMode(CHRG_OUT, OUTPUT);
digitalWrite(CHRG_OUT,0); // turn off charger
pinMode(VOLT_IN,INPUT);
Vin=analogRead(VOLT_IN);
pinMode(HEATSINK_IN, INPUT);
pinMode(AMBIENT_IN, INPUT);
// Serial.begin(9600);
lcd.begin(16, 2);
pinMode(KEYPRESS, INPUT_PULLUP);
pinMode(Encod1, INPUT_PULLUP);
pinMode(Encod2, INPUT_PULLUP);
oldPA = digitalRead(Encod1);
eepromGet();
attachInterrupt(digitalPinToInterrupt(Encod1), decodeInt, CHANGE);
lastKey = digitalRead(KEYPRESS);
if (!lastKey) {
setState(STATE_CALIB_START);
} else {
setState(STATE_INIT);
lcdPrint(1, "www.state.dk/bct");
}
}
void loop() {
int32_t v, a, d;
char k;
int press_duration=0;
delay(50); // max 20 runs per sec to avoid key bounce
read_analog_inputs();
k = digitalRead(KEYPRESS); //readKey();
if (!k) press_duration++;
else press_duration=0;
if (press_duration>100) setState(STATE_CALIB_START); // 5 sec press -> calibrate
bool clicked = !k && lastKey;
lastKey=k;
switch (state) {
case STATE_INIT:
break;
case STATE_HOME:
dPrint_batt_mV_m();
break;
case STATE_SET_AH:
if (encodeval>200) encodeval=200;
eepromdata.battery_Ah=encodeval;
dPrint_Ah();
break;
case STATE_SET_DURATION:
if (encodeval<1) encodeval=1;
if (encodeval < eepromdata.battery_Ah/max_Amp) encodeval = eepromdata.battery_Ah/max_Amp;
eepromdata.duration=encodeval;
dPrint_Duration();
break;
case STATE_SET_STOPV:
eepromdata.stopmV=encodeval*10;
dPrint_stopmV();
break;
case STATE_RUNNING:
v=get_voltage_mV();
a=get_current_mA(); //if (a>9999) a=9999;
d = set_mA - a;
dPrint_volt_curr_time();
if (abs(d) > 50){
if (d>0) set_Aout(Aout+1);
else set_Aout(Aout-1);
}
if (v<eepromdata.stopmV){
low_V_Count++;
if (low_V_Count>100){ // 5 sec
setState(STATE_FINISHED);
set_Aout(0);
eepromdata.measured_Ah= (seconds() - startseconds) / eepromdata.duration * eepromdata.battery_Ah / 3600;
eepromPut();
dPrint_result();
}
} else{
low_V_Count=0;
}
break;
case STATE_FINISHED:
break;
case STATE_CALIB_START:
break;
case STATE_CALIB_AMP_1:
set_Aout(encodeval);
eepromdata.Ain1 = Ain;
sprintf(line,"Set 1A %03i %03i", encodeval, Ain);
lcdPrint(1, line);
break;
case STATE_CALIB_AMP_5:
set_Aout(encodeval);
eepromdata.Ain5_1 = Ain - eepromdata.Ain1;
sprintf(line,"Set 5A %03i %03i", encodeval, Ain);
lcdPrint(1, line);
break;
case STATE_CALIB_VOLTAGE:
eepromdata.voltFactor = encodeval;
dPrint_batt_mV();
break;
}
if (clicked) { // Last thing to do just before acting on a click
set_Aout(0);
switch (state) {
case STATE_SET_AH:
case STATE_SET_DURATION:
case STATE_SET_STOPV:
case STATE_CALIB_AMP_1:
case STATE_CALIB_AMP_5:
case STATE_CALIB_VOLTAGE:
eepromPut();
break;
}
setState(NEXT_STATE[state]); // on to next state
switch (state) { // First thing to do after setting new state
case STATE_HOME:
break;
case STATE_SET_AH:
encodeval=min(200,eepromdata.battery_Ah);
break;
case STATE_SET_DURATION:
encodeval=eepromdata.duration;
break;
case STATE_SET_STOPV:
encodeval=max(9000, eepromdata.stopmV)/10; // resolution 10 mV
break;
case STATE_RUNNING:
Aout=0;
set_mA = ((eepromdata.battery_Ah * 100) / eepromdata.duration) * 10; // split *1000 to avoid overrun
low_V_Count = 0;
startseconds=seconds();
break;
case STATE_FINISHED: // user interrupted
eepromdata.measured_Ah=0;
eepromPut();
setState(STATE_HOME);
break;
case STATE_CALIB_AMP_1:
encodeval=40;
break;
case STATE_CALIB_AMP_5:
encodeval=120;
break;
case STATE_CALIB_VOLTAGE:
encodeval=eepromdata.voltFactor;
break;
}
}
}
#include <EEPROM.h>
#include <LiquidCrystal.h>
/*
12V battery capacity tester v. 2.3
By Christen Monberg chr@monberg.com
May, 2022
See description at
https://create.arduino.cc/projecthub/monse53/12v-battery-capacity-tester-fb95b9
*/
/*----- PIN Definitions -----*/
// LCD:
const uint8_t rs=12; // PB4
const uint8_t en=11; // PB3
const uint8_t ld4=7; // PD7
const uint8_t ld5=6; // PD6
const uint8_t ld6=5; // PD5
const uint8_t ld7=4; // PD4
const int Encod1=2; // PD2 encoder pinA
const int Encod2=3; // PD3 encoder pinB
const int KEYPRESS=8; // PB0 Button
const int AMP_IN=A0; // PC0 Current in
const int VOLT_IN=A1; //15; // PC1 Bat Voltage in
const int HEATSINK_IN=A2; // PC2 Heatsink temp
const int AMBIENT_IN=A3; // PC3 Ambient temp
const int AMP_OUT=9; // PB1 Current control
const int CHRG_OUT=13; // PB5 Charge relay on
const int FAN_OUT=10; // PB2 Fan on
LiquidCrystal lcd(rs, en, ld4, ld5, ld6, ld7);
struct EEPROMDATA {
uint16_t voltFactor; // 0..1023=0..20.00V. 600 = 12.000V -> factor=~2000
long Ain1; //
long Ain5_1; // Ain5 - Ain1
int stopmV;
int battery_Ah; // Battery size
int duration; // test hours
int measured_Ah; // Battery size
uint8_t chksum;
} eepromdata;
const EEPROMDATA eeprom_default={
1500, // voltFactor
32, // Ain1
260, // Ain5_1
10500, // stopMV
40, // batt Ah
20, // test hours
0, // measured
0}; // chksum
const uint8_t chktoken = 0x5A;
enum STATE {
STATE_INIT,
STATE_HOME,
STATE_SET_AH,
STATE_SET_DURATION,
STATE_SET_STOPV,
STATE_RUNNING,
STATE_FINISHED,
STATE_CALIB_START,
STATE_CALIB_AMP_1,
STATE_CALIB_AMP_5,
STATE_CALIB_VOLTAGE};
const char NEXT_STATE[]={
/* STATE_INIT*/ STATE_HOME,
/* STATE_HOME */ STATE_SET_AH,
/* STATE_SET_AH */ STATE_SET_DURATION,
/* STATE_SET_DURATION */ STATE_SET_STOPV,
/* STATE_SET_STOPV */ STATE_RUNNING,
/* STATE_RUNNING */ STATE_FINISHED,
/* STATE_FINISHED */ STATE_HOME,
/* STATE_CALIB_START */ STATE_CALIB_AMP_1,
/* STATE_CALIB_AMP_1 */ STATE_CALIB_AMP_5,
/* STATE_CALIB_AMP_5 */ STATE_CALIB_VOLTAGE,
/* STATE_CALIB_VOLTAGE */ STATE_HOME };
volatile char state = -1;
const char *menus[] = {
/* STATE_INIT */ " Battery Tester ",
/* STATE_HOME */ " Press to begin ",
/* STATE_SET_AH */ "Set Battery size",
/* STATE_SET_DURATION */ "Set Test time ",
/* STATE_SET_STOPV */ "Set End Voltage ",
/* STATE_RUNNING */ " Running ",
/* STATE_FINISHED */ " Recharging ",
/* STATE_CALIB_START */ " Calibration ",
/* STATE_CALIB_AMP_1 */ "Calibrate amps 1",
/* STATE_CALIB_AMP_5 */ "Calibrate amps 5",
/* STATE_CALIB_VOLTAGE */ "Calibrate volts"};
char line[20]; // a few extra chars just in case
char lastKey = 1;
/* Encoder interrupt.
NOTE: No debounce elimination.
There can be several INTs per step, but encodeval
always ends up with the right value */
volatile char oldPA;
volatile int encodeval = 0; // this is the decode output
volatile bool encodeNegAllowed=false; // true if negative values allowed
uint32_t startseconds = 0;
int32_t set_mA=0;
uint16_t low_V_Count=0;
const uint8_t max_Amp=10;
void decodeInt() { // Interrupt from iPinA
int newPA = digitalRead(Encod1); // //(PIND>>2) & B11; // volatile
int newPB = digitalRead(Encod2);
if (oldPA ^ newPA) {
if (newPA==newPB) encodeval--;
else encodeval++;
}
if (!encodeNegAllowed && encodeval<0){
encodeval=0;
}
oldPA = newPA;
}
/***************** ANALOG/DIGITAL I/O ***********************/
void charge_on(bool on){
digitalWrite(CHRG_OUT,on);
}
void fan_on(bool on){
digitalWrite(FAN_OUT,on);
}
const int analogDamping=4;
long Ain=0;
int Vin=0;
uint8_t Aout=0;
long get_current_mA(){
return (Ain-eepromdata.Ain1) * 4000 / eepromdata.Ain5_1 + 1000;
// return (Ain-eepromdata.Ain1) * 10 / eepromdata.Ain5_1 * 400 + 1000;
}
int32_t get_voltage_mV(){
return ((int32_t)Vin * eepromdata.voltFactor) / 100; // factor =~2000
}
void set_Aout(int val){
if (val>255) val=255;
if (val<0) val=0;
Aout=val;
analogWrite(AMP_OUT,val);
fan_on((val>0));
}
void read_analog_inputs(){ // called at 20 Hz
Vin=(Vin*(analogDamping-1)+analogRead(VOLT_IN))/analogDamping; // 0..1023 -> 0..17V; 12V ~= 600
Ain=(Ain*(analogDamping-1)+analogRead(AMP_IN))/analogDamping; // 0..1023 -> 0..17000 mA; 10000 mA ~= 600
}
uint32_t seconds(){
return millis()/1000;
}
/***************** LCD ***********************/
void clearLine() {
memset(line,0,20);
}
void lcdPrint(int line, const char *text) {
// note: display is not cleared - make sure line is 16 chars
lcd.setCursor(0, line);
lcd.print(text);
}
uint16_t dPrint_batt_mV(){
uint16_t v=get_voltage_mV();
sprintf(line,"Battery = %2i.%01i V", v/1000, v%1000/10);
lcdPrint(1, line);
return v;
}
uint16_t dPrint_batt_mV_m(){
uint16_t v=get_voltage_mV();
sprintf(line, "%2i.%02iV RES=%3iAh", v/1000, v%1000/10, eepromdata.measured_Ah );
lcdPrint(1, line);
return v;
}
void dPrint_stopmV(){
int v=eepromdata.stopmV;
sprintf(line,"Dischrg to %2i.%1iV" ,v/1000, v%1000 / 100);
lcdPrint(1, line);
}
void dPrint_Ah(){
int v=eepromdata.battery_Ah;
sprintf(line,"Capacity = %3iAh", v);
lcdPrint(1, line);
}
void dPrint_Duration(){
int v=eepromdata.duration;
sprintf(line,"Duration = %3ih", v);
lcdPrint(1, line);
}
void dPrint_volt_curr_time(){
int v=get_voltage_mV();
int a=get_current_mA();
if (a<0) a=0;
if (a>9999) a=9999;
int t=(seconds() - startseconds)/60; // minutes
sprintf(line,"%2i.%1iV %1i.%1iA %02i:%02i", v/1000, v%1000/100, a/1000, a%1000/100, t/60, t%60);
lcdPrint(1, line);
}
void dPrint_result(){
sprintf(line, "Result %3iAh %2i%%", eepromdata.measured_Ah, eepromdata.measured_Ah*100/eepromdata.battery_Ah);
lcdPrint(1, line);
}
int calc_sum(uint8_t *p, char len){
int sum=0;
for (char i=0; i<len; i++){
sum += p[i];
}
return sum;
}
void eepromPut(){
eepromdata.chksum=chktoken-calc_sum((uint8_t*)&eepromdata, sizeof(eepromdata)-1);
EEPROM.put(0, eepromdata);
}
void eepromGet(){
EEPROM.get(0, eepromdata);
uint8_t s = calc_sum((uint8_t*)&eepromdata, sizeof(eepromdata));
if (s!=chktoken){
eepromdata = eeprom_default;
eepromPut();
}
}
void setState(int newState){
if (state!=newState){
state = newState;
lcd.clear();
lcdPrint(0, menus[state]);
charge_on(state==STATE_FINISHED);
}
}
void setup() {
pinMode(AMP_OUT, OUTPUT); // sets the pin as output
analogWrite(AMP_OUT,0); // turn off load
pinMode(FAN_OUT, OUTPUT);
analogWrite(FAN_OUT,0); // turn off fan
pinMode(CHRG_OUT, OUTPUT);
digitalWrite(CHRG_OUT,0); // turn off charger
pinMode(VOLT_IN,INPUT);
Vin=analogRead(VOLT_IN);
pinMode(HEATSINK_IN, INPUT);
pinMode(AMBIENT_IN, INPUT);
// Serial.begin(9600);
lcd.begin(16, 2);
pinMode(KEYPRESS, INPUT_PULLUP);
pinMode(Encod1, INPUT_PULLUP);
pinMode(Encod2, INPUT_PULLUP);
oldPA = digitalRead(Encod1);
eepromGet();
attachInterrupt(digitalPinToInterrupt(Encod1), decodeInt, CHANGE);
lastKey = digitalRead(KEYPRESS);
if (!lastKey) {
setState(STATE_CALIB_START);
} else {
setState(STATE_INIT);
lcdPrint(1, "www.state.dk/bct");
}
}
void loop() {
int32_t v, a, d;
char k;
int press_duration=0;
delay(50); // max 20 runs per sec to avoid key bounce
read_analog_inputs();
k = digitalRead(KEYPRESS); //readKey();
if (!k) press_duration++;
else press_duration=0;
if (press_duration>100) setState(STATE_CALIB_START); // 5 sec press -> calibrate
bool clicked = !k && lastKey;
lastKey=k;
switch (state) {
case STATE_INIT:
break;
case STATE_HOME:
dPrint_batt_mV_m();
break;
case STATE_SET_AH:
if (encodeval>200) encodeval=200;
eepromdata.battery_Ah=encodeval;
dPrint_Ah();
break;
case STATE_SET_DURATION:
if (encodeval<1) encodeval=1;
if (encodeval < eepromdata.battery_Ah/max_Amp) encodeval = eepromdata.battery_Ah/max_Amp;
eepromdata.duration=encodeval;
dPrint_Duration();
break;
case STATE_SET_STOPV:
eepromdata.stopmV=encodeval*10;
dPrint_stopmV();
break;
case STATE_RUNNING:
v=get_voltage_mV();
a=get_current_mA(); //if (a>9999) a=9999;
d = set_mA - a;
dPrint_volt_curr_time();
if (abs(d) > 50){
if (d>0) set_Aout(Aout+1);
else set_Aout(Aout-1);
}
if (v<eepromdata.stopmV){
low_V_Count++;
if (low_V_Count>100){ // 5 sec
setState(STATE_FINISHED);
set_Aout(0);
eepromdata.measured_Ah= (seconds() - startseconds) / eepromdata.duration * eepromdata.battery_Ah / 3600;
eepromPut();
dPrint_result();
}
} else{
low_V_Count=0;
}
break;
case STATE_FINISHED:
break;
case STATE_CALIB_START:
break;
case STATE_CALIB_AMP_1:
set_Aout(encodeval);
eepromdata.Ain1 = Ain;
sprintf(line,"Set 1A %03i %03i", encodeval, Ain);
lcdPrint(1, line);
break;
case STATE_CALIB_AMP_5:
set_Aout(encodeval);
eepromdata.Ain5_1 = Ain - eepromdata.Ain1;
sprintf(line,"Set 5A %03i %03i", encodeval, Ain);
lcdPrint(1, line);
break;
case STATE_CALIB_VOLTAGE:
eepromdata.voltFactor = encodeval;
dPrint_batt_mV();
break;
}
if (clicked) { // Last thing to do just before acting on a click
set_Aout(0);
switch (state) {
case STATE_SET_AH:
case STATE_SET_DURATION:
case STATE_SET_STOPV:
case STATE_CALIB_AMP_1:
case STATE_CALIB_AMP_5:
case STATE_CALIB_VOLTAGE:
eepromPut();
break;
}
setState(NEXT_STATE[state]); // on to next state
switch (state) { // First thing to do after setting new state
case STATE_HOME:
break;
case STATE_SET_AH:
encodeval=min(200,eepromdata.battery_Ah);
break;
case STATE_SET_DURATION:
encodeval=eepromdata.duration;
break;
case STATE_SET_STOPV:
encodeval=max(9000, eepromdata.stopmV)/10; // resolution 10 mV
break;
case STATE_RUNNING:
Aout=0;
set_mA = ((eepromdata.battery_Ah * 100) / eepromdata.duration) * 10; // split *1000 to avoid overrun
low_V_Count = 0;
startseconds=seconds();
break;
case STATE_FINISHED: // user interrupted
eepromdata.measured_Ah=0;
eepromPut();
setState(STATE_HOME);
break;
case STATE_CALIB_AMP_1:
encodeval=40;
break;
case STATE_CALIB_AMP_5:
encodeval=120;
break;
case STATE_CALIB_VOLTAGE:
encodeval=eepromdata.voltFactor;
break;
}
}
}
Comments