Hackster is hosting Hackster Holidays, Ep. 7: Livestream & Giveaway Drawing. Watch previous episodes or stream live on Friday!Stream Hackster Holidays, Ep. 7 on Friday!
monse53
Published © GPL3+

12V Battery Capacity Tester

Measuring the capacity of a battery

AdvancedFull instructions provided2,958
12V Battery Capacity Tester

Things used in this project

Hardware components

Arduino Nano R3
Arduino Nano R3
×1
RGB LCD Shield Kit, 16x2 Character Display
RGB LCD Shield Kit, 16x2 Character Display
×1

Hand tools and fabrication machines

Soldering iron (generic)
Soldering iron (generic)

Story

Read more

Schematics

Schematic

Created in TinyCad

TinyCad source file

Download TinyCad and open this file

Code

Code for Arduino Nano

C/C++
Open in Arduino IDE
#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;
    }
  }

}

Code for Battery Tester

C/C++
Open in Arduino IDE
#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;
    }
  }

}

Credits

monse53
0 projects • 1 follower

Comments