/*
Codigo criado por: sasiskas
Dispovivel em: https://www.instructables.com/DIY-Li-On-Capacity-Tester-and-More/

Modificado e traduzido por: Mario Parente
*/







//-----------------------------------------------------Testador de capacidade da bateria e mais V2.0.1-----------------------------------------------------------
#include <SPI.h>
#include <stdlib.h>
#include <LiquidCrystal_I2C.h>
LiquidCrystal_I2C lcd(0x27, 16, 2);
unsigned long previousMillis = 0;            
unsigned long millisPassed = 0;
unsigned long BatFull = 0;
float BatVoltQuiescent = 0;                                             // Tensão da bateria quando não há carga na bateria
float Capacity = 0.0;                                                   // Capacidade calculada da bateria, medida durante a descarga
float mA=0;
float SetI = 0;                                                         // Ponto de ajuste para a corrente de descarga. Lembre-se que você pode ajustar a corrente de descarga usando o trimpot 500K.
float ChargeI = 0;
float CurTP4056 = 0;
float ResTP4056 = 0;
float ratioRI = 0;
float BatLevel = 0;                                                     // Porcentagem de carga/nível da baterial
float BatVoltCorrected = 0;                                             // Tensão da bateria, medida com base na referência interna do arduino
float A2VoltCorrected = 0;
float VoltTP4056Pin2  = 0;
float Rprog = 0; 
float BatRes = 0;                                                       // Resistência interna da bateria
const float RES = 3.3;                                                  // Valor do seu resistor. O valor sugerido é 1 Ohm. Altere-o, se você usar um valor de resistor diferente
#define clk 2
#define dt 3
#define sw 4
#define MOSFET_Discharge 9                                              // É um N-MOSFET, então HIGH está ON e LOW está OFF
#define MOSFET_Charge 8                                                 // É um P-MOSFET, então HIGH está OFF e LOW está ON
char screen = 0;
char arrowpos = 0;
char charging = 0;
char discharging = 0;
char storage = 0;
char storagecharge = 0;
char storagedischarge = 0;
char BatVoltQuiescentValue = 0;                                          // Se existe um valor para "tensão da bateria sem carga" ou não. 1 para sim e 0 para não.
const unsigned char PS_128 = (1 << ADPS2) | (1 << ADPS1) | (1 << ADPS0);
volatile boolean TurnDetected = false;
volatile boolean up = false;
volatile boolean button = false;


#define LED 13


ISR(PCINT2_vect) {
  if (digitalRead(sw) == LOW) {
    button = true;
  }
}

void isr0 ()  {
  TurnDetected = true;
  up = (digitalRead(clk) == digitalRead(dt));
}

void setup() {
  lcd.begin(16, 2);
  lcd.setCursor(0, 0);
  pinMode(MOSFET_Discharge, OUTPUT);
  pinMode(MOSFET_Charge, OUTPUT);
  digitalWrite(MOSFET_Discharge, LOW); 
  digitalWrite(MOSFET_Charge, HIGH); 
  pinMode(sw, INPUT_PULLUP);
  pinMode(clk, INPUT);
  pinMode(dt, INPUT);
  PCICR |= 0b00000100;
  PCMSK2 |= 0b00010000;   // desliga PCINT20(D4)
  attachInterrupt(0, isr0, RISING);
  lcd.clear();
  screen0();
  lcd.setCursor(0, 0);
  lcd.write(126);
  lcd.backlight();

  pinMode(LED, OUTPUT);
  digitalWrite(LED, HIGH); 
}

void loop() {
  int in0 = analogRead(A0);                                           //lê o valor do pino A0
  int sensorValue1 = analogRead(A1);                                  //lê o valor do pino A1
  float val0 = in0 * 5.0 / 1024.0;               
  float supply = readVcc() / 1000.0;
  SetI = sensorValue1 * (5.00 / 1024.00) / RES * 1000;                // Calcular a taxa de descarga da bateria
  if (SetI <= 1200){          
    SetI = SetI;
  }
  else{
    SetI = 1200;
  }							  
  BatVoltCorrected = supply / 5 * val0 * 4.35;                         //Caso você use divisores de tensão diferentes, você precisa alterar o número 4.3 de acordo.
  millisPassed = millis() - previousMillis;
  previousMillis = millis();
  //------------------------------------------------------------------- calculates the charging current ------------------------------------------------------------------------

  int in2 = analogRead(A2);                                             //leia o valor do pino A2
  int in3 = analogRead(A3);                                             //leia o valor do pino A3
  float val2 = in2 * 5.0 / 1024.0; 
  float val3 = in3 * 5.0 / 1024.0; 
  A2VoltCorrected = supply / 5 * val2;                                  //calcula a leitura do pino A2 com base na referência de tensão interna
  VoltTP4056Pin2 = supply / 5 * val3;                             		  //calcula a leitura do pino A3 com base na referência de tensão interna

  Rprog = 1220 + (A2VoltCorrected * 1220 / (VoltTP4056Pin2 - A2VoltCorrected));  //calcula o Rprog do módulo TP4056

                                                                                 //calcula a corrente de carregamento com base nos números da folha de dados TP4056
  if (Rprog >= 10000) { 
	CurTP4056 =130 ;
	ResTP4056 =10000 ;
	ratioRI = 0.107 ;
  }
  else if (Rprog < 10000 && Rprog >= 5000){
  CurTP4056 = 250 ;
	ResTP4056 = 5000 ;
	ratioRI = 0.024 ;
  }
  else if (Rprog < 5000 && Rprog >= 4000){
  CurTP4056 = 300 ;
	ResTP4056 = 4000 ;
	ratioRI = 0.050 ;
  }
  else if (Rprog < 4000 && Rprog >= 3000){
  CurTP4056 = 400 ;
	ResTP4056 = 3000 ;
	ratioRI = 0.100 ;
  }
  else if (Rprog < 3000 && Rprog >= 2000){
  CurTP4056 = 580 ;
	ResTP4056 = 2000 ;
	ratioRI = 0.180 ;
  }
  else if (Rprog < 2000 && Rprog >= 1660){
  CurTP4056 = 690 ;
	ResTP4056 = 1660 ;
	ratioRI = 0.324 ;
  }
  else if (Rprog < 1660 && Rprog >= 1500){
  CurTP4056 = 780 ;
	ResTP4056 = 1500 ;
	ratioRI = 0.563 ;
  }
  else if (Rprog < 1500 && Rprog >= 1330){
  CurTP4056 = 900 ;
  ResTP4056 = 1330 ;
  ratioRI = 0.706 ;
  }
  else if (Rprog >= 1220){
  CurTP4056 = 995 ;
  ResTP4056 = 1220 ;
  ratioRI = 0.864 ;
  }
   
  if (Rprog < 1220){
  ChargeI = 1000 ;
  }
  else {
  ChargeI = (CurTP4056 - ((Rprog - ResTP4056) * ratioRI));
  }
  
  if (ChargeI > 0){
  ChargeI = ChargeI;
  }
  else{
  ChargeI = 0;
  }
  //------------------------------------------------------------------- porcentagem da bateria - cálculo linear baseado na tensão (linear NÃO representa a porcentagem REAL)  
  if (BatVoltCorrected < 2.5) {                                       
  BatLevel = 0;
  }
  else if (BatVoltCorrected > 4.2)
  {BatLevel = 100;
  }
  else {BatLevel = (BatVoltCorrected - 2.5 ) / 1.7 * 100 ;
  }
  //----------------------------------------------------------------------------------------------------------------------------------------------------------------------
  if (BatVoltCorrected <= 3.70 && BatVoltCorrected >= 3.65){          // Para baterias de 3400mAh, a tensão de armazenamento é de 3,6-3,7V, para baterias abaixo de 2600mAh, deve ser maior.
  storagedischarge = 0;
  storagecharge = 0;
  }
  if (BatVoltCorrected <= 3.75 && BatVoltCorrected > 3.7){            // Defina esses números + 0,5 V da tensão de armazenamento das baterias
  storagedischarge = 1;
  storage = 2;
  storagecharge = 0;
  }
  if (BatVoltCorrected >= 3.6 && BatVoltCorrected < 3.65){            // Defina esses números -0,5V da tensão de armazenamento das baterias
  storagecharge = 1;
  storage = 2;
  storagedischarge = 0;
  }
  if (BatVoltCorrected > 3.75 || BatVoltCorrected < 3.6){             // Defina esses números +0,5V e -0,5V da tensão de armazenamento das baterias
  storage = 0;
  storagedischarge = 0;
  storagecharge = 0;
  }
  if (BatVoltCorrected <= 3.75 && BatVoltCorrected >= 3.6){           // Defina esses números +0,5V e -0,5V da tensão de armazenamento das baterias
  storage = 1;
  }
  //------------------------------------------------------------------------------------------------------------------------------------------------------
  if (screen == 0) {
  //delay(500);
  BatVoltQuiescent = BatVoltCorrected;
  BatVoltQuiescentValue = 1;
  BatFull = 0;
  }
//---------------------------------------------------------------------------------Modo de teste de capacidade-------------------------------------------------------  
  if (screen == 1) {
      if (BatVoltCorrected <= 2.3){
        Capacity = Capacity;
        BatVoltQuiescentValue = 0;
        digitalWrite(MOSFET_Discharge, LOW);
//      buz();
        lcd.setCursor(0,0);
        lcd.print("V:");
        lcd.print(BatVoltCorrected);
        lcd.print("V ");
        lcd.setCursor(8,0);
        lcd.print("I:");
        lcd.print(SetI,0); 
        lcd.print("mA  ");
        lcd.setCursor(0,1);
        lcd.print("C:");
        lcd.print(Capacity,0);
        lcd.print("mAh   ");
        lcd.setCursor(10,1);
        lcd.print("R:");
        lcd.print(BatRes,0);
        lcd.print("m");
        lcd.write(244);
        delay(100);
      }
      if (BatVoltCorrected > 2.3 && BatVoltQuiescentValue == 0 ) {
        BatRes = (BatVoltQuiescent - BatVoltCorrected) / (BatVoltCorrected / SetI);  //Calcula a resistência interna da bateria em mOhms  
        Capacity = Capacity + (SetI * (millisPassed / 3600000.0));
        BatVoltQuiescent = BatVoltCorrected;
        BatVoltQuiescentValue = 1;
        digitalWrite(MOSFET_Discharge, HIGH);
        lcd.setCursor(0,0);
        lcd.print("V:");
        lcd.print(BatVoltCorrected);
        lcd.print("V ");
        lcd.setCursor(8,0);
        lcd.print("I:");
        lcd.print(SetI,0); 
        lcd.print("mA  ");
        lcd.setCursor(0,1);
        lcd.print("C:");
        lcd.print(Capacity,0);
        lcd.print("mAh   ");
        lcd.setCursor(10,1);
        lcd.print("R:");
		    lcd.print(BatRes,0);
        lcd.print("m");
        lcd.write(244);
        delay(500);
      }  
        else if (BatVoltCorrected > 2.3 && BatVoltQuiescentValue == 1 ) {
        BatRes = (BatVoltQuiescent - BatVoltCorrected) / (BatVoltCorrected / SetI);  //Calcula a resistência interna da bateria em mOhms    
        Capacity = Capacity + (SetI * (millisPassed / 3600000.0));
        digitalWrite(MOSFET_Discharge, HIGH);
        lcd.setCursor(0,0);
        lcd.print("V:");
        lcd.print(BatVoltCorrected);
        lcd.print("V ");
        lcd.setCursor(8,0);
        lcd.print("I:");
        lcd.print(SetI,0); 
        lcd.print("mA  ");
        lcd.setCursor(0,1);
        lcd.print("C:");
        lcd.print(Capacity,0);
        lcd.print("mAh   ");
        lcd.setCursor(10,1);
        lcd.print("R:");
        lcd.print(BatRes,0);
        lcd.print("m");
        lcd.write(244);
        delay(500);
      }  
  }
  //---------------------------------------------------------------------------------------Modo de carga-------------------------------------------------------  
  else if (screen == 2) {
       if (BatVoltCorrected >= 4.15) {                    
           lcd.setCursor(0,0);
           lcd.print("Bateria em "); // 12 Caracteres
           lcd.setCursor(11,0);
           lcd.print("100% ");
           lcd.setCursor(0,1);
           lcd.print("V:");
           lcd.print(BatVoltCorrected);
           lcd.print("V         ");
		       lcd.setCursor(8,1);
           lcd.print("I:");
           lcd.print(ChargeI,0);
           lcd.print("mA         ");
           digitalWrite(MOSFET_Charge, HIGH);
           delay(500);
		       BatFull = BatFull + 1;
      }
	    else if (BatVoltCorrected == 0 ){
          BatFull = 0;
	        lcd.setCursor(0,0);
          lcd.print("Sem bateria "); // 12 Caracteres
          lcd.setCursor(12,0);
          lcd.print(BatLevel,0);
          lcd.print("%   ");
          lcd.setCursor(0,1);
          lcd.print("V:");
          lcd.print(BatVoltCorrected);
          lcd.print("V         ");
		      lcd.setCursor(9,1);
          lcd.print("I:");
          lcd.print(ChargeI,0);
          lcd.print("mA         ");
          digitalWrite(MOSFET_Charge, HIGH);
          //delay(9000);
          //digitalWrite(MOSFET_Charge, HIGH);
          delay(500);
		}
	  else if (BatVoltCorrected < 4.15 && BatFull < 10 && BatVoltCorrected > 0 )  { 
	        lcd.setCursor(0,0);
          lcd.print("Carregando  "); // 12 Caracteres
          lcd.setCursor(12,0);
          lcd.print(BatLevel,0);
          lcd.print("% ");
          lcd.setCursor(0,1);
          lcd.print("V:");
          lcd.print(BatVoltCorrected);
          lcd.print("V         ");
		      lcd.setCursor(8,1);
          lcd.print("I:");
          lcd.print(ChargeI,0);
          lcd.print("mA         ");
          digitalWrite(MOSFET_Charge, LOW);
          //delay(9000);
          //digitalWrite(MOSFET_Charge, HIGH);
          delay(500);
	  }
      else if (BatVoltCorrected < 4.15 && BatFull >= 10) {
           lcd.setCursor(0,0);
           lcd.print("Bateria em "); // 12 Caracteres
           lcd.setCursor(11,0);
           lcd.print("100% ");
           lcd.setCursor(0,1);
           lcd.print("V:");
           lcd.print(BatVoltCorrected);
           lcd.print("V         ");
		       lcd.setCursor(8,1);
           lcd.print("I:");
           lcd.print(ChargeI,0);
           lcd.print("mA         ");
           digitalWrite(MOSFET_Charge, HIGH);
           delay(500);
       }
  }
  //----------------------------------------------------------------------------------------Modo de armazenamento-----------------------------------------------------------------------------------------
  //Você precisa ajustar suas tensões de acordo com suas baterias, para baterias de 3400mAh a tensão de armazenamento (50% da capacidade) é de 3,6-3,7V, para baterias abaixo de 2600mAh deve ser maior.
   // Para mais informações, visite: https://lygte-info.dk/info/BatteryChargePercent%20UK.html
  else if (screen == 3) {
    delay(100);
        if (BatVoltCorrected <= 1.5 ) {
          lcd.setCursor(0,0);
          lcd.print("Sem bateria     ");
          lcd.setCursor(0,1);
          lcd.print("V:");
          lcd.print(BatVoltCorrected);
          lcd.print("V            ");
          digitalWrite(MOSFET_Discharge, LOW);      
          digitalWrite(MOSFET_Charge, HIGH);
          charging = 0;  
          discharging = 0;
          storage = 0;
          storagecharge = 0;
          storagedischarge = 0;
         }
         else if (BatVoltCorrected > 3.75 && charging == 0) {                            // Verifica se a "tensão quiescente" da bateria está acima de 3,7V e inicia a descarga.
              lcd.setCursor(0,0);
              lcd.print("Descarregando     ");
              lcd.setCursor(0,1);
				      lcd.setCursor(0,1);
				      lcd.print("V:");
				      lcd.print(BatVoltCorrected);
		      		lcd.print("V        ");
      				lcd.setCursor(9,1);
	      			lcd.print("I:");
	      			lcd.print(SetI,0); 
	      			lcd.print("mA    ");
	      			digitalWrite(MOSFET_Charge, HIGH); 
	      			delay(100); 
              discharging = 1;  
	      			digitalWrite(MOSFET_Discharge, HIGH);              
      				delay(9000);
              digitalWrite(MOSFET_Discharge, LOW);              
      }
         else if (storagedischarge == 1 && discharging == 1){                             // Etapa intermediária antes de parar a descarga
              lcd.setCursor(0,0);
              lcd.print("RDY armazenamento");
              lcd.setCursor(0,1);
              lcd.print("V:");
              lcd.print(BatVoltCorrected);
              lcd.print("V                            "); 
              discharging = 1;  
              digitalWrite(MOSFET_Charge, HIGH); 
              delay(100);         
              digitalWrite(MOSFET_Discharge, HIGH);              
              delay(9000);
              digitalWrite(MOSFET_Discharge, LOW);  
      }
      else if (BatVoltCorrected >= 1.5 && BatVoltCorrected < 3.6) {                // Verifica se a "tensão quiescente" da bateria está entre 1,5V e 3,6V
			        if (discharging == 1 && BatVoltCorrected < 3.0){                     // Interrompe a descarga se a "tensão de descarga" da bateria estiver abaixo de 3,0V
              lcd.setCursor(0,1);
              lcd.print("V:");
              lcd.print(BatVoltCorrected);
              lcd.print("V        ");
              lcd.setCursor(9,1);
              lcd.print("I:");
              lcd.print(SetI,0); 
              lcd.print("mA    ");
              digitalWrite(MOSFET_Discharge, LOW);          
			        }
			        if (discharging == 1 && BatVoltCorrected > 2){                        // Impede o carregamento (continua descarregando) mesmo se a "ensão quiescente" estiver abaixo de 3,6V
              lcd.setCursor(0,1);
              lcd.print("V:");
              lcd.print(BatVoltCorrected);
              lcd.print("V        ");
              lcd.setCursor(9,1);
              lcd.print("I:");
              lcd.print(SetI,0); 
              lcd.print("mA    "); 
			        }
		          else if (discharging == 0 && BatVoltCorrected < 3.6){                  // Comece a carregar a bateria
			    	  lcd.setCursor(0,0);
			    	  lcd.print("Carregando       ");
			    	  lcd.setCursor(0,1);
	    			  lcd.print("V:");
		    		  lcd.print(BatVoltCorrected);
	    			  lcd.print("V              ");
		    	  	digitalWrite(MOSFET_Discharge, LOW);
		    		  delay(100);          
		    	  	digitalWrite(MOSFET_Charge, LOW);
		    	    charging = 1;
              delay(1000);
		          }
              else {                                                                   // usado para depuração
              lcd.setCursor(0,0);
              lcd.print("error?       ");
		    	}
        }

       else if (storagecharge == 1 && charging == 1){                                  // Etapa intermediária antes de parar a carga. (na verdade, não parando a carga)
              lcd.setCursor(0,0);
              lcd.print("RDY armazenamento");
              lcd.setCursor(0,1);
              lcd.print("V:");
              lcd.print(BatVoltCorrected);
              lcd.print("V                       "); 
              charging = 1;
              digitalWrite(MOSFET_Discharge, LOW);
              delay(100);          
              digitalWrite(MOSFET_Charge, LOW);
              delay(1000);
              }     
       else if (charging == 1 && BatVoltCorrected > 3.65 ){                            // Para de carregar. Esta etapa é usada se a parada intermediária de carga foi usada.
        	lcd.setCursor(0,0);
	    		lcd.print("RDY armazenamento");
		    	lcd.setCursor(0,1);
		    	lcd.print("V:");
		    	lcd.print(BatVoltCorrected);
	    		lcd.print("V           "); 
		    	digitalWrite(MOSFET_Discharge, LOW);
	    		digitalWrite(MOSFET_Charge, HIGH);
	    		delay(1000);
        }
        else if (storage == 1 && charging == 0){                                       // Para baterias de 3400mAh, a tensão de armazenamento é de 3,6-3,7V, para baterias abaixo de 2600mAh, deve ser maior.
          lcd.setCursor(0,0);                                                          // Esta etapa é usada se a parada intermediária de carga NÃO foi usada. Ou após parada intermediária da alta.
          lcd.print("RDY armazenamento");
          lcd.setCursor(0,1);
          lcd.print("V:");
          lcd.print(BatVoltCorrected);
          lcd.print("V           "); 
          digitalWrite(MOSFET_Discharge, LOW);
          digitalWrite(MOSFET_Charge, HIGH);
          delay(1000);
          }

	  }

//-------------------------------------------------------------------------------- mudança -------------------------------------------------------------------------------------------------------
  if (TurnDetected) {
    delay(200);
    switch (screen) {
      case 0:
        switch (arrowpos) {
           case 0:
            if (!up) {
              screen0();
              lcd.setCursor(0, 1);
              lcd.write(126);
              arrowpos = 1;
            }
            else {
              screen0();
              lcd.setCursor(6, 1);
              lcd.write(126);
              arrowpos = 2;
            }
            break;
          case 1:
            if (up) {
              screen0();
              lcd.setCursor(0, 0);
              lcd.write(126);
              arrowpos = 0;
            }
            else {
              screen0();
              lcd.setCursor(6, 1);
              lcd.write(126);
              arrowpos = 2;
            }
            break;
           case 2:
            if (up) {
              screen0();
              lcd.setCursor(0, 1);
              lcd.write(126);
              arrowpos = 1;
            }
            else {
              screen0();
              lcd.setCursor(0, 0);
              lcd.write(126);
              arrowpos = 0;
            }
            break;
        }
        break;
     }
    TurnDetected = false;
  }
//--------------------------------------------------------------------------------------------BOTÃO PRESSIONADO--------------------------------------------------------------------------------------------
  if (button) {
    delay(200);
    switch (screen) {
      case 0:
        if (arrowpos == 0) {
          screen = 1;
          screen1();
        }
        else if (arrowpos == 1){
          screen = 2;
          screen2();
        }
        else {
		      screen = 3;
          screen3();
          		  }
        break;
      case 1:
            screen = 0;
            screen0();
            lcd.setCursor(0, 0);
            lcd.write(126);
        break;        
      case 2:
            screen = 0;
            screen0();
            lcd.setCursor(0, 0);
            lcd.write(126);  
        break;   
        case 3:
            screen = 0;
            screen0();
            lcd.setCursor(0, 0);
            lcd.write(126); 
        break;   
    }
    arrowpos = 0;
    button = false;
  }
}
//-----------------------------------------------------------------------------------------------------TELAS-------------------------------------------------------------------------------------
void screen0() {
  lcd.clear();
  lcd.setCursor(1, 0);
  lcd.print("Tester Bateria");
  lcd.setCursor(1, 1);
  lcd.print("Carga");
  lcd.setCursor(7, 1);
  lcd.print("Armazenar");
  digitalWrite(MOSFET_Discharge, LOW);      
  digitalWrite(MOSFET_Charge, HIGH);
  charging = 0;  
  discharging = 0;
  storage = 0;
  storagecharge = 0;
  storagedischarge = 0;
}

void screen1() {
  lcd.clear();
}

void screen2() {
   lcd.clear();
}

void screen3() {
  lcd.clear();
  }
  
//-------------------------------------------------------------------tensão com base na referência interna-------------------------------------------------------------
long readVcc() {
  long result;
  // Leia a referência de 1,1 V contra AVcc
  #if defined(__AVR_ATmega32U4__) || defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__)
  ADMUX = _BV(REFS0) | _BV(MUX4) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1);
  #elif defined (__AVR_ATtiny24__) || defined(__AVR_ATtiny44__) || defined(__AVR_ATtiny84__)
  ADMUX = _BV(MUX5) | _BV(MUX0);
  #elif defined (__AVR_ATtiny25__) || defined(__AVR_ATtiny45__) || defined(__AVR_ATtiny85__)
  ADMUX = _BV(MUX3) | _BV(MUX2);
  #else
  ADMUX = _BV(REFS0) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1);
  #endif
  delay(2);                                                // Aguarde Vref resolver
  ADCSRA |= _BV(ADSC); // Convert
  while (bit_is_set(ADCSRA, ADSC));
  result = ADCL;
  result |= ADCH << 8;
  result = 1126400L / result;                              // Calcular Vcc (em mV); 1126400 = 1,1*1024*1000
  return result;
}

//void buz()
//  {
//  digitalWrite(9, HIGH);
//  delay(100);          
//  digitalWrite(9, LOW);  
//  delay(10);            
//}