Categories
Statistics
Flag Counter
Since 08.08.2014
Counts only, if "DNT = disabled".

Your IP is 3.229.122.112
ec2-3-229-122-112.compute-1.
Info
Valid HTML 4.01 Transitional Creative Commons Lizenzvertrag
rss
เราจะทำแบบวิศวกรผู้ยิ่งใหญ่
We love the King
19. March 2024
Your valuable opinion :
5 stars

Avg. 6 from 23 votes.



Arduino-LeadAcidBatteryTester.php    29345 Bytes    12-02-2018 11:22:22


Arduino/Genuino Lead Acid Battery Tester


Shield "LEO" and the Add-on "BRUNO"



After a "Lab-Cleaning-Day", boxes with lead-acid batteries appear out of nowhere in my office. They are usually in our Uninterruptible Power Supplies (UPS), so their well-being has high priority and weaklings must be replaced. The main idea of this project is, that the Arduino can run a complete cycle without beeing kept a watch on every minute. A battery charger is connected directly. Evaluation of the measurements is done with a spreadsheet.


Arduino/Genuino Lead Acid Battery Tester

<< PRESS START >> - to get clarity about ...
... those unclaimed lead acid batteries !




✈ Charging Batteries - A short overview




Wikipedia knows far more than me about lead-acid batteries. Read the original article here. Instead we put together a list with charging methods depending on battery chemistry.


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


MERKBLATT BATTERIEN UND AKKUS, Dr.Rolf Zinniker, ETH, Institut für Elektronik




✈ The "Shield"




The shield (LEO) was originally designed for another project (yet unpublished), but is recycled here to keep the development time short. It is an universal I/O board. You don't absolutely need it, but it made my life easier :-)






✈ The "Add-on"




The second board is more or less the 'power-stage'. It can switch the charger on or off. A DAC (MCP4822-E/P) sets the current to discharge the battery with a constant current. The current and the voltage of the battery are measured during the charging and discharging period. With that, the amount of charge can be estimated. (ISA-PLAN® 1 Ω Precision resistor and High Voltage, Bidirectional Current Shunt Monitor AD8210). After the start button has been pressed, the battery is charged for 12 hours with a constant voltage of 13.8 volt. Then the charger is disconnected and a small pause is granted to the battery. After that it will be discharged until the voltage drops below 10.8 volt. The sketch will send a group of measurements every 60 sec. Therefore the Serial Plotter which is available from version 1.6.6 (Arduino.cc) mayst be unable to cope and we suggest you copy and paste the values into the spreadsheet of your choice. A temperature monitoring is also available. When testing lead-acid batteries, this is not absolutely necessary - and so we use it to monitor the temperature of the heatsink.


Left (below) the UNO with shield "LEO", above the Add-on "BRUNO", on the right an APU







✈ Arduino Sketch - The Code



Double click on code to select ...


/* ////////////////////////////////////////////////////////////////// 
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.
// ////////////////////////////////////////////////////////////////// 




✈ Measurements on a 12 V / 7 Ah Battery (new/used)




It is advantageous to copy and paste the data rows (from the serial monitor) into a textfile. This can then be opened with the spreadsheet program of your choice.


A new Yuasa 7P12 delivers 700 mA for almost 6 hrs. (Charging cycle not shown)



A used battery is charged for 4 hrs. Then it was able to supply a current of 700 mA for ≈ 15 mins.



From the "labelled" 7 Ah, one would expect the battery to deliver 7 Amps during one hour. Or 700 mA during ten hours. But the (new) battery failed to do so. Responsible for this behavior is Peukert's law, (Wilhelm Peukert, 1897). It expresses approximately the change in capacity of rechargeable lead-acid batteries at different rates of discharge. As the rate increases, the battery's available capacity decreases.


Capacity as a function of load current at T = 20 °C. Drawing courtesy of VARTA.




✈ Battery Comparison (Primary Elements) Size LR06, UM3, AA




This test shows - not only that this nifty tool can also handle small voltages and currents, but also which NoName device has striking similiar performance with a Bolls Boys Brand. The current has been set to 100 mA and the end-of-measurement is reached, when the voltage goes below 0.8 V (A value we found in many datasheets of batteries). As the load is a constant current (100 mA here), it is very comfortable to calculate the capacity of such a battery, as Q = I * t. As you knew already, that the one who lasts longer is better, we also set this in relation to the price. The following types have been tested : [July 2016]


   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.



Some small changes have been made to the sketch :


// ////////////////////////////////////////////////////////////////// 

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 

// ////////////////////////////////////////////////////////////////// 



The discharging curves of the batteries mentioned above. Horizontal: seconds



Note : Yes, we know that measuring 1.0 units of each type is statistically not that significant. It is quite possible, that exactly our battery has been produced on a monday morning. One should also take into account, that different applications have different power needs and therefore the use of another battery type may be more satisfactory. We discharged with a constant current of 100 mA until the voltage reached 0.8 V.




✈ Limitations




With the used 0.1 Ω Shunt (PBV-R100-F1-1.0, Isabellenhütte) and the gain of the AD8210 (20x), the maximum current is limited to ± 1250 mA. If you have larger currents, the shunt should be lowered in value. My AD8210 survived short overloads, but the display will not go above 1250 mA.

In case there is no (low-impedance) load, the voltage displays ≈ 3.70 V. This voltage comes from the AD8210. Looks like this is the bias of the internal amplifier.

There is no fuse in the load circuit. The plan is, that in case the transistor T1 (IRLZ44NPBF) fails, the resistor R9 (1Ω, 5W) becomes high impedance an the current stops. (Not tested ;-)

In Excel 2010, the maximum worksheet size is 1048576 rows by 16384 columns. Text files used by Notepad should be no larger than 45k.




✈ Share your thoughts



The webmaster does not read these comments regularely. Urgent questions should be send via email. Ads or links to completely uncorrelated things will be removed.


Your Browser says that you allow tracking. Mayst we suggest that you check that DNT thing ?

 
t1 = 6459 d

t2 = 300 ms

★ ★ ★  Copyright © 2006 - 2024 by changpuak.ch  ★ ★ ★

PRchecker.info Impressum