


| Chemistry | Charging Method |
| Alkaline | Pulsed current, ∼ 200 Hz (?) |
| Lead Acid | Constant voltage, 14.4 V for 12 h, then 13.8 V |
| Li-Ion | Constant current (UBat < URef), Constant voltage (UBat ≥ URef), URef ∼ 4.1 V |
| Li-Polymer | Constant voltage, 4.2 V / cell (4.1 V doubles lifetime) |
| LiFePO4 |
1A, Single Cell LiFePO4 Linear Battery Charger with 4.9 V, 50 mA LDO Design A Low-Cost LiFePO4 Battery Charger With MCP73123 |
| NiCd | Constant current |
| NiMH | NN |
/* //////////////////////////////////////////////////////////////////
ARDUINO/Genuino (UNO) Lead Acid Battery Tester
https://www.changpuak.ch/electronics/Arduino-LeadAcidBatteryTester.php
Software Version 1.0,
23.06.2016, Alexander C. Frank
////////////////////////////////////////////////////////////////// */
#include <LiquidCrystal.h>
LiquidCrystal lcd(9, 8, 6, 5, 4, A5);
const int TemperaturePin = A2;
float TemperatureValue = 0.0;
const int KeyBoardPin = A0;
unsigned int KeyPressed = 0;
const int VoltagePin = A3;
float Voltage = 0.0;
const int CurrentPin = A1;
const float ZeroCurrent = 2.495;
float Current = 0.0 ; // MILLIAMPS
const int ChargerOn = A4;
const int CS = 13;
const int SCLK = 12;
const int SDAT = 11;
const int LOAD = 10;
float ChargeWattHour = 0.0; // in Wh
unsigned long DeltaTime = 0;
unsigned long LastTime = 0;
unsigned long DisplayTime = 0;
unsigned long StartTime = 0;
unsigned long StopTime = 0;
unsigned long TimeBase = 9999; // 10 sec
unsigned long TargetCurrent = 0;
// //////////////////////////////////////////////////////////////////
const unsigned long ChargeTime = 18000; // SECONDS
const unsigned long Pause = 600; // SECONDS
const float StopDischargeVoltage = 10.5 ;
const unsigned long DischargeCurrent = 700 ; // MILLIAMPS
boolean ChargeFirst = true ; // NO GO DIRECTLY TO DISCHARGE TEST
// //////////////////////////////////////////////////////////////////
void UpdateTemperature()
{
unsigned int aux = 0;
for (int i=0; i<2; i++){ aux += analogRead(TemperaturePin); delay(10);}
// LM35 gives 10mV per degree. We amplify by 5. 5V = 100 deg
TemperatureValue = aux / ( 2.0 * 10.24 );
}
// //////////////////////////////////////////////////////////////////
void UpdateCurrentRead()
{
unsigned int aux = 0;
for (int i=0; i<32; i++){ aux += analogRead(CurrentPin); delay(10);}
// AD8210 gives 20 x U (= 20 * I * 0.1 Ohms)
Current = ((5.0 * aux / ( 32.0 * 1023 )) - ZeroCurrent) / 2.0 ;
Current = 1000.0 * abs(Current);
}
// //////////////////////////////////////////////////////////////////
void UpdateCurrentWrite(unsigned int TargetCurrent)
{
// WE USE THE DAC OF CHANNEL A, SHUNT IS 1 OHM
unsigned int pointer = 0x8000;
unsigned int DACA = (int)( TargetCurrent );
DACA |= 0x1000; // GAIN = 2, DON'T SLEEP, Write to DACA
digitalWrite(CS, LOW);
for (int i=0; i < 16; i++) {
if ((DACA & pointer) > 0)
{ digitalWrite(SDAT, HIGH); }
else { digitalWrite(SDAT, LOW); }
digitalWrite(SCLK, HIGH);
digitalWrite(SCLK, LOW);
pointer = pointer >> 1;
}
digitalWrite(CS, HIGH);
digitalWrite(LOAD, LOW);
digitalWrite(LOAD, HIGH);
}
// //////////////////////////////////////////////////////////////////
void UpdateVoltage()
{
unsigned int aux = 0;
for (int i=0; i<4; i++){ aux += analogRead(VoltagePin); delay(10);}
// Resistor Divider: A3 = U * 3/(3+12)
Voltage = 1.0134861 * aux / ( 4.0 * 40.92 );
}
// //////////////////////////////////////////////////////////////////
void UpdateChargingScreen()
{
unsigned int HH, MM, SS, REST ;
lcd.clear();
lcd.setCursor(0,0); lcd.print("*** CHARGING ***");
lcd.setCursor(0,1); lcd.print("TOGO ");
HH = DisplayTime / 3600 ;
REST = DisplayTime - HH * 3600 ;
MM = REST / 60 ;
SS = REST % 60 ;
if(HH < 10) lcd.print("0"); lcd.print(HH); lcd.print(":");
if(MM < 10) lcd.print("0"); lcd.print(MM); lcd.print(":");
if(SS < 10) lcd.print("0"); lcd.print(SS); lcd.print(" ");
lcd.setCursor(0,2);
if(Voltage < 10) lcd.print(" ");
lcd.print(Voltage,2); lcd.print(" V ");
lcd.setCursor(9,2);
if(Current < 1000) lcd.print(" ");
if(Current < 100) lcd.print(" ");
if(Current < 10) lcd.print(" ");
lcd.print(Current,0);lcd.print(" mA ");
lcd.setCursor(0,3); lcd.print(TemperatureValue,1); lcd.print(" ");
lcd.print((char)223); lcd.print("C ");
}
// //////////////////////////////////////////////////////////////////
void UpdateDischargeScreen()
{
unsigned int HH, MM, SS, REST ;
lcd.clear();
lcd.setCursor(0,0); lcd.print("*** TESTING ***");
lcd.setCursor(0,1); lcd.print("ELAPSED ");
HH = DisplayTime / 3600 ;
REST = DisplayTime - HH * 3600 ;
MM = REST / 60 ;
SS = REST % 60 ;
if(HH < 10) lcd.print("0"); lcd.print(HH); lcd.print(":");
if(MM < 10) lcd.print("0"); lcd.print(MM); lcd.print(":");
if(SS < 10) lcd.print("0"); lcd.print(SS); lcd.print(" ");
lcd.setCursor(0,2);
if(Voltage < 10) lcd.print(" ");
lcd.print(Voltage,2); lcd.print(" V ");
lcd.setCursor(9,2);
if(Current < 1000) lcd.print(" ");
if(Current < 100) lcd.print(" ");
if(Current < 10) lcd.print(" ");
lcd.print(Current,0);lcd.print(" mA ");
lcd.setCursor(0,3); lcd.print(TemperatureValue,1); lcd.print(" ");
lcd.print((char)223); lcd.print("C ");
}
// //////////////////////////////////////////////////////////////////
void UpdatePauseScreen()
{
unsigned int HH, MM, SS, REST ;
lcd.clear();
lcd.setCursor(0,0); lcd.print("*** PAUSING *** ");
lcd.setCursor(0,1); lcd.print("TOGO ");
HH = DisplayTime / 3600 ;
REST = DisplayTime - HH * 3600 ;
MM = REST / 60 ;
SS = REST % 60 ;
if(HH < 10) lcd.print("0"); lcd.print(HH); lcd.print(":");
if(MM < 10) lcd.print("0"); lcd.print(MM); lcd.print(":");
if(SS < 10) lcd.print("0"); lcd.print(SS); lcd.print(" ");
lcd.setCursor(0,2);
if(Voltage < 10) lcd.print(" ");
lcd.print(Voltage,2); lcd.print(" V ");
lcd.setCursor(9,2);
if(Current < 1000) lcd.print(" ");
if(Current < 100) lcd.print(" ");
if(Current < 10) lcd.print(" ");
lcd.print(Current,0);lcd.print(" mA ");
lcd.setCursor(0,3); lcd.print(TemperatureValue,1); lcd.print(" ");
lcd.print((char)223); lcd.print("C ");
}
// //////////////////////////////////////////////////////////////////
unsigned int ReadKeyBoard()
{
unsigned int aux = 0;
unsigned int ReturnValue = 0;
// Trigger value is decimal 341 and 682
delay(10);
aux = analogRead(KeyBoardPin);
// NO KEY
ReturnValue = 0;
// STOP KEY
if ((aux > 241) && (aux < 441)) ReturnValue = 1;
// START KEY
if ((aux > 582) && (aux < 782)) ReturnValue = 2;
// BOTH KEY
if ((aux > 440) && (aux < 583)) ReturnValue = 3;
return ReturnValue;
}
// //////////////////////////////////////////////////////////////////
void setup() {
// LCD
lcd.begin(16, 4);
lcd.clear();
lcd.setCursor(0,0); lcd.print("ARDUINO/GENUINO ");
lcd.setCursor(0,1); lcd.print("SHIELD 'LEO' ");
lcd.setCursor(0,2); lcd.print("ADD-ON 'BRUNO' ");
lcd.setCursor(0,3); lcd.print("BATTERY-TESTER ");
// OUTPUT PINS
pinMode(ChargerOn, OUTPUT);
digitalWrite(ChargerOn, LOW);
pinMode(LOAD, OUTPUT);
digitalWrite(LOAD, LOW);
pinMode(CS, OUTPUT);
digitalWrite(CS, LOW);
pinMode(SDAT, OUTPUT);
digitalWrite(SDAT, LOW);
pinMode(SCLK, OUTPUT);
digitalWrite(SCLK, LOW);
// SET DISCHARGE CURRENT TO ZERO :-)
UpdateCurrentWrite(TargetCurrent);
Serial.begin(9600);
delay(2000);
}
void loop() {
// WAITING FOR START KEY PRESSED
unsigned int pc = 0;
lcd.clear();
lcd.setCursor(5,1); lcd.print("PRESS");
lcd.setCursor(5,2); lcd.print("START");
KeyPressed = 0;
do {
pc += 1;
if (pc == 60) { lcd.noDisplay() ; }
if (pc == 120) { lcd.display() ; pc = 0 ; }
delay(1);
KeyPressed = ReadKeyBoard();
} while ( KeyPressed == 0 );
StartTime = millis();
StopTime = StartTime + ChargeTime * 1000 ;
lcd.display() ;
// CHARGING THE BATTERY
if ( ChargeFirst != true ) StopTime = millis();
while ( millis() < StopTime )
{
DeltaTime = StopTime - millis() ;
DisplayTime = DeltaTime / 1000 ;
UpdateTemperature();
UpdateVoltage();
UpdateCurrentRead();
delay(TimeBase);
UpdateChargingScreen();
digitalWrite(ChargerOn, HIGH);
}
digitalWrite(ChargerOn, LOW);
StopTime = millis() + Pause * 1000 ;
// SHORT PAUSE
while ( millis() < StopTime )
{
DeltaTime = StopTime - millis() ;
DisplayTime = DeltaTime / 1000 ;
UpdateTemperature();
UpdateVoltage();
UpdateCurrentRead(); // YES, THIS SHOULD BE ZERO NOW :-)
delay(TimeBase);
UpdatePauseScreen();
}
// MEASURING THAT BATTERY
// RAMP-UP THE CURRENT TO 100%
UpdateCurrentRead();
UpdateDischargeScreen();
while ( Current < DischargeCurrent )
{
UpdateCurrentWrite(TargetCurrent); // SETPOINT
TargetCurrent += 1;
delay(1);
UpdateCurrentRead(); // MEASURES THE REAL VALUE
UpdateDischargeScreen();
}
StartTime = millis();
// NOW DISCHARGE UNTIL VOLTAGE LIMIT
while ( Voltage > StopDischargeVoltage )
{
DeltaTime = millis() - StartTime ;
DisplayTime = DeltaTime / 1000 ;
UpdateTemperature();
UpdateVoltage();
UpdateCurrentRead();
Serial.print(DeltaTime);Serial.print(",");
Serial.print(Voltage,2);Serial.print(",");
Serial.print(Current,0);Serial.print(",");
Serial.println(TemperatureValue,1);
UpdateDischargeScreen();
delay(TimeBase);
}
// VOLTAGE LIMIT REACHED. SET CURRENT TO ZERO
UpdateCurrentWrite(0);
}
// //////////////////////////////////////////////////////////////////
// END OF FILE.
// //////////////////////////////////////////////////////////////////
| CHF/1 | Distributor | Capacity [Ah] | [Ah]/CHF | |
| 3.38 | Interdiscount | 2.7355 | 0.8093 | |
| 2.24 | Interdiscount | under test | - | |
| 1.74 | COOP | under test | - | |
| 0.60 | D-PHYS | under test | - | |
| 1.74 | COOP | 2.1999 | 1.2643 | |
| 0.50 | COOP | 2.2016 | 4.4032 | |
| 0.30 | IKEA | under test | - |
VARTA says 1) is optimised for Digital Cameras, 2) for Flashlights, 3) for Toys, 4) for Remote Controls.
DURACELL says 5) is suiteable for Radios, Remote Controls, Clocks.
PRIX Garantie says 6) Do not throw into fire, keep away from children ?!?!
IKEA says 7) is suiteable for MP3-Player, Digitalcameras, Toys, Clocks, Remote Controls and more.
// ////////////////////////////////////////////////////////////////// const unsigned long ChargeTime = 1 ; // SECONDS const unsigned long Pause = 1 ; // SECONDS const float StopDischargeVoltage = 0.8 ; const unsigned long DischargeCurrent = 100 ; // MILLIAMPS boolean ChargeFirst = false ; // GO DIRECTLY TO DISCHARGE TEST // //////////////////////////////////////////////////////////////////