/*
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);
//}