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. // //////////////////////////////////////////////////////////////////
TYPE/BRAND | CHF/1 | Distributor | Capacity [Ah] | [Ah]/CHF |
VARTA LITHIUM1 | 3.38 | Interdiscount | 2.7355 | 0.8093 |
VARTA MAX TECH2 | 2.24 | Interdiscount | under test | - |
VARTA HIGH ENERGY3 | 1.74 | COOP | under test | - |
VARTA LONGLIFE4 | 0.60 | D-PHYS | under test | - |
DURACELL SIMPLY5 | 1.74 | COOP | 2.1999 | 1.2643 |
PRIX Garantie6 | 0.50 | COOP | 2.2016 | 4.4032 |
ALKALISK7 | 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 // //////////////////////////////////////////////////////////////////