Download the pdf button

I have quite a few rechargable AA and AAA batteries kicking around that I’d like to use in various projects, but I am very aware that some of them are a little weak for lack of exercise. Rather than identify the batteries by having my slope soaring glider fall out of the sky when the battery pack voltage gets too low, I’d like to know what batteries need some TLC before-hand. A device that conducts a controlled discharge of the battery cells and logs the voltages seemed like a good way of doing this. One of my friends is also obtaining old dumped laptop battery packs and recovering the cells from them to build large “Power walls” for his solar electricity system and battery packs for powering an electric go-kart. So this system, with a little modification, will be useful for him to test the health of the cells he recovers. To test pairs of recovered 3.7V 18650 Lithium Ion batteries (3100mAh) the circuit would need to be modified a little by introducing a voltage divider to keep the voltage at the Arduino pins less than 5V and adjust the discharge resistors to suit the capacity.

What it Does

The battery discharge tester is quite simple. All it does is discharge a battery across a load resistor that has been sized to discharge a battery at its rated capacity over the course of five hours. An Arduino monitors the voltage at the battery terminal and records this to a file on an SD Card. When the battery discharges to a threshold level where it is considered to be “empty” without causing damage to the battery from over discharging it, then the Arduino opens a relay and stops the battery from discharging further. The Arduino then writes an artificial zero reading to the file to mark the end of the test for that particular cell. There is a button to stop the test for all cells, and buttons to allow the user to replace a discharged cell, and start a new discharge test on another cell installed in that test station. If all cells have finished, then the Arduino will ensure all relays are open and the file is closed. The CSV file with the record of times and voltages for each battery test station, can then be analysed in a spreadsheet to calculate the currents, the actual energy discharged, and plot the voltage profiles. Weak and damaged cells will generally show up by having short discharge times.

Below is a snippet of the output file content, and a plot of the traces.

Snippet of the CSV record

Plot of Battery Discharge Curves

Station 1, which contained a NiCd battery, automatically disconnected the load after the test had been running for about 3.5hours. Station 3 had a particularly sick NiMh battery that was known to be shoddy and disconnected very early. The other two NiMh batteries continued to discharge and then be disconnected from the load over the course of the following few hours. All batteries used in this test had been charged prior to the test.

You will notice in the plot there are some vertical drops in the curves whenever one of the batteries has completed its discharge test and been switched out. This is just an artifact from the arrangement of my code which creates a blank line whenever a battery switches out. Because it does not interfere with the operation of the device or the analysis I consider it fit for purpose, and have not bothered to fix this up.

The analysis spreadsheet can be found here: BatteryDischargeTrial.ods and BatteryDischargeTrial.xls

The python script to create the plot from a .CSV file can be found here: This requires the Numpy library and the Matplotlib library, both of which can be found in Pyzo.

About the System

The system described here has been designed to test four single AA or AAA NiMh and NiCd cells simultaneously. There are three load resistors on each testing station which are selected using a jumper to suit the battery type and capacity.

Cell Description Capacity (mAh) Load Resistor
AA NiMh 2500 - 2800 2.7Ω 5W
AAA NiMh 900 6.8Ω 1W
AA NiCd 650 10Ω 1W

The highest battery voltage it can measure is 5V. So a single cell LiPo battery would be okay, but a 2 cell LiPo battery pack would not. In order to use this for high voltage multicell battery packs you will need to install an appropriate voltage divider, modify the code to suit, and modify the resistors to discharge the battery in five hours. For larger capacity cells, you will need to modify the resistors for a different discharge rate.

The SD Card module used is the same as used in the Simple Pressure and Temperature logger project. You can find them at (SKU-205308). It works with the standard SD library that comes with the Arduino IDE, and uses the SPI connections on pins D11 through to D13 (as they are on the Arduino Nano) with the Chip Select assigned to D10.

Relays are used to control the battery discharge because they have a very low resistance connection and so will not interfere with the discharge test. They are connected with the switch in the Normally Open position to disconnect the batteries if the system fails.

This device tests four batteries simultaneously and pretty much uses all of the available pins on an Arduino Nano. To test more batteries, an Arduino Mega would be required.

Like the Simple Pressure and Temperature logger project the timestamp used is just the number of seconds since the sketch started running. I did not see much point in adding extra complexity through having a real time clock providing exact time of day and dates.

While the code includes serial communication, this is only for initial testing and debugging. The system is designed to run in a stand-alone mode from a 5V power supply and does not require a computer to be connected.

The Circuit

Here is the circuit diagram for a 5V supply and using 5V relays.

Battery Discharge Tester 5V Version Circuit Diagram
Click on the image for a high resolution version.

Below is a variation of the design that uses a 12V supply and 12V relays.

Battery Discharge Tester 12V Version Circuit Diagram
Click on the image for a high resolution version.

The Layout

I used perforated prototyping board for this, and struggled to come up with a tidy arrangement.
I apologise for the appalling mess this is. It was a toss up between a tidy circuit diagram and messy installation, or a messy circuit diagram and a tidy installation. I opted for a tidy circuit diagram because I figured nobody would be looking to build exactly what I have done anyway. The colours used are supposed to match the colours used in the circuit diagram so hopefully it is all reasonably clear.

Battery Discharge Tester 5V Version Protoboard Layout
Click on the image for a high resolution version.

It took me a while to solder up because it was a bit tedious.

The Code

The only library used is for the SD card module. This library is included in the standard Arduino IDE, and so you will not need to hunt around for any extra stuff in order to make this work. You can download the sketch from here: Batt_Dischargerv4.ino.

This code was developed on the Arduino IDE 1.6.8 which has a number of updated libraries in it. After battling with converting float values to strings so they may be written to the SD Card as was done with the Simple Pressure and Temperature logger project, I found that it was possible to write the float values directly to the SD Card with a print statement. So much easier! I owe a vote of thanks to the person who updated the SD library to included this functionality, and to Adafruit’s Bill Earl and Lady Ada for highlighting the ability to do this on their Data Logger Shield project walkthrough.

Battery Discharge Tester - Four Cell edition
Hamish Trolove - 1 July 2016

This sketch logs four 1.2V NiMh or NiCd battery
as it discharges across a resistor selected to give a
standard 5hr discharge test.

A 5V relay is used to disconnect the battery under test and
stop the battery discharging too far.

The log is written to an SD card in a CSV format for later processing.
The file is closed either by pressing an "all stop" button, or when
all batteries have finished discharging.

When a battery has discharged to a Threshhold voltage it is switched out by the relay
to prevent further discharge and spare the battery from getting
damaged by being over-discharged.  The data recorded is artifically set to zero
after this point to make it clear that the test is over.

Another battery can be added if a battery has finished discharging, and
pushing a button associated with the tests station will begin discharging
that battery.  The data recorded will be added to the end of the data from
the previous battery and so need to be seperated when analysing the
recorded file.

Connections are:

SD Module
GND to Arduino Nano GND Pin
3.3V to Arduino Nano 3.3V Pin
5V to Arduino Nano 5V pin
SDCS (Chip select) to Arduino Nano D10 Pin
MOSI to Arduino Nano D11 Pin (MOSI)
MISO to Arduino Nano D12 Pin (MISO)
SCK to Arduino Nano D13 Pin

Battery Monitor points:
Station 1: Nano Pin A3
Station 2: Nano Pin A2
Station 3: Nano Pin A1
Station 4: Nano Pin A0

Test Restart Button:
Station 1: Nano Pin A7
Station 2: Nano Pin A6
Station 3: Nano Pin A5
Station 4: Nano Pin A4

Relay Control:
Station 1: Nano Pin D6
Station 2: Nano Pin D7
Station 3: Nano Pin D8
Station 4: Nano Pin D9

Stop button on Arduino Nano D4 Pin

An LED is also hooked onto the relay trigger to light while the 
discharge test is occuring.  At the end of the discharge
cycle, this LED will go out.

Note: The Arduino Nano has a 5V Reference Voltage.

The SD Library seems to be able to handle Floats automatically in
Arduino IDE 1.6.8.


#include <SD.h>      //SD Card Library

const int chipSelect = 10;  //SD Card Connection
const int StopPin = 4;    //Stop the process pin.
const int refVolts = 5; //The Arduino Nano has a 5V reference voltage

const int Relay1Pin = 6;  //Relay transistor control pin for Battery1.
const int Relay2Pin = 7;  //Relay transistor control pin for Battery1.
const int Relay3Pin = 8;  //Relay transistor control pin for Battery3.
const int Relay4Pin = 9;  //Relay transistor control pin for Battery4.

const int TestRestartBatt1 = A7; //Button to restart Station 1 test
const int TestRestartBatt2 = A6; //Button to restart Station 2 test
const int TestRestartBatt3 = A5; //Button to restart Station 3 test
const int TestRestartBatt4 = A4; //Button to restart Station 4 test

const int Bat1VoltPin = A3;  //Battery voltage read from Analogue Pin A3. 
const int Bat2VoltPin = A2;  //Battery voltage read from Analogue Pin A2.
const int Bat3VoltPin = A1;  //Battery voltage read from Analogue Pin A1.
const int Bat4VoltPin = A0;  //Battery voltage read from Analogue Pin A0.

boolean ProcessRun = true; //Toggle to run the process until button pushed.
boolean Battery1Test = true; //Toggle to run the process on Battery 1.
boolean Battery2Test = true; //Toggle to run the process on Battery 2.
boolean Battery3Test = true; //Toggle to run the process on Battery 3.
boolean Battery4Test = true; //Toggle to run the process on Battery 4.

String dataString = "";  //make a string to hold the timestamp.    
const String BatteryZeroString = "0.0,";  //A simulated zero for the log once battery disconnected.
const String BatteryZeroStringStn4 = "0.0"; //This is the end column therefore no comma

int timestamp = 0;     //The is the seconds since starting the process.

int BatRaw = 0;  // variable to hold raw analogue reading from Batteries
float BatVolts = 0;  // variable to hold processed voltage reading from Batteries

float BatTHRESH = 1.0; //Threshhold voltage to stop test at.

File LogFile; //The file object we will log to.

int RdgDelay = 1000;  //1 second between readings
unsigned long RdgTime = 0; //Time of last reading.

void setup() 
  delay(5000); //The usual 5 second delay to allow upload of other programs
  Serial.begin(9600);  //This is only really for proving the system, not for general
  // use because this system does not require a computer to be attached.
  pinMode(chipSelect, OUTPUT);  //Make sure default chip select is set to output.
  if (!SD.begin(chipSelect))
    Serial.println("SD initialization failed!");
  LogFile ="BattLog.csv",FILE_WRITE);  //Open the file to log data to.
  Serial.println("Beginning to Log"); 

void loop() 
      LogFile.close();  //If the button is pressed close the file.
      ProcessRun = false;
      Battery1Test = false;  //Just to make sure they are all stopped.
      Battery2Test = false;
      Battery3Test = false;
      Battery4Test = false;
      digitalWrite(Relay1Pin,LOW);  // disconnect all batteries
    if(millis() > RdgTime+RdgDelay)
      RdgTime = millis(); //Capture the time readings start.
      timestamp = RdgTime/1000;  //Time since start in seconds.
      dataString = String(timestamp) + ",";
      if(Battery1Test)  //If Battery Station 1 set to run, then...
        digitalWrite(Relay1Pin,HIGH);  //Connect the battery to load
        BatRaw = analogRead(Bat1VoltPin);
        BatVolts = (BatRaw/1023.0)*refVolts;
        if(BatVolts < BatTHRESH)
          Battery1Test = false;
          digitalWrite(Relay1Pin,LOW);  // disconnect the battery
          Serial.print("Battery 1 Finished");
      if(Battery2Test)  //If Battery Station 2 set to run, then...
        digitalWrite(Relay2Pin,HIGH);  //Connect the battery to load
        BatRaw = analogRead(Bat2VoltPin);
        BatVolts = (BatRaw/1023.0)*refVolts;
        if(BatVolts < BatTHRESH)
          Battery2Test = false;
          digitalWrite(Relay2Pin,LOW);  // disconnect the battery
          Serial.print("Battery 2 Finished");
      if(Battery3Test)  //If Battery Station 3 set to run, then...
        digitalWrite(Relay3Pin,HIGH);  //Connect the battery to load
        BatRaw = analogRead(Bat3VoltPin);
        BatVolts = (BatRaw/1023.0)*refVolts;
        if(BatVolts < BatTHRESH)
          Battery3Test = false;
          digitalWrite(Relay3Pin,LOW);  // disconnect the battery
          Serial.print("Battery 3 Finished");
      if(Battery4Test)  //If Battery Station 4 set to run, then...
        digitalWrite(Relay4Pin,HIGH);  //Connect the battery to load
        BatRaw = analogRead(Bat4VoltPin);
        BatVolts = (BatRaw/1023.0)*refVolts;
        if(BatVolts < BatTHRESH)
          Battery4Test = false;
          digitalWrite(Relay4Pin,LOW);  // disconnect the battery
          Serial.println("Battery 4 Finished");
      if(!Battery1Test && !Battery2Test && !Battery3Test && !Battery4Test)  //All batteries discharged
	      ProcessRun = false;  //Set the run flag to false
      //Test to see if the Test restart button has been pushed.
      if(!Battery1Test && digitalRead(TestRestartBatt1)==HIGH) 
	      Battery1Test = true;  //Set the reloaded battery in station 1 to discharge.
	      digitalWrite(Relay1Pin,HIGH);  // Restart the test in station 1. 
      if(!Battery2Test && digitalRead(TestRestartBatt2)==HIGH)
	      Battery2Test = true;  //Set the reloaded battery in station 2 to discharge.
	      digitalWrite(Relay2Pin,HIGH);  // Restart the test in station 2. 
      if(!Battery3Test && digitalRead(TestRestartBatt3)==HIGH)
	      Battery3Test = true;  //Set the reloaded battery in station 3 to discharge.
	      digitalWrite(Relay3Pin,HIGH);  // Restart the test in station 3. 
      if(!Battery4Test && digitalRead(TestRestartBatt4)==HIGH)
	      Battery4Test = true;  //Set the reloaded battery in station 4 to discharge.
	      digitalWrite(Relay4Pin,HIGH);  // Restart the test in station 4. 
  LogFile.close();  //Once the ProcessRun flag has been tripped, close the file.


Creative Commons Graphic Attribution, Non commercial, and Share Alike

The Battery Discharge Tester project described above is provided by Hamish Trolove under a creative commons license.

Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License.