Feed on

I have some rechargeable batteries which are on their way out as they only seem to last 1-3 weeks but I never quite know when they are flat when they aren’t being used. So Instead of buying new rechargeable batteries (which is the easy solution), I thought why not make a simple project out of it.

(Testing with only 1 AA battery)

Introducing the Low Voltage Battery Monitor (LVBM) – a 4x AA battery monitor which will blink an LED every 4 seconds if a battery is found flat and checks good batteries every 15 minutes.

AA Battery drop off voltage / ADC

Firstly we need to find the voltage when an AA battery drops off, we can find this on an AA battery datasheet: http://data.energizer.com/PDFs/nh15-2300.pdf.

The drop off seems to happen around 1.1 volts, now we just need to detect that using the ADC. As we previously learned, we should use the internal 1.1V analog reference so our readings don’t rely on our MCU’s supply voltage.

The maximum voltage we’ll ever need is 1.5 volts, lets round it up to 2 volts. We can easily use a voltage divider to divide that by 2 and we’ll get a maximum of 1 volt to our ADC. We’ll use 2x 100K resistors so the maximum draw at any time will be 10uA for a 2 volt battery. So 1.1 volts / 2 = 0.55 volts which would mean 1024 ADC / 2 = 512 is the value we need to be less than.

MCU Selection / Schematic

Now we know what we need to do it’s time to pick the MCU. I originally went with the ATtiny85 paired with a shift register however I found the ATtiny24A (14 pins) which has more pins, the same functionality as the ATtiny25/45/85 and was priced the same as the ATtiny25, so we can remove the need for the shift register.

The “A” part in the ATtiny24A means it uses the Atmel picopower technology which reduces some power consumption compared to the standard ATtiny24.

So how can we use a MCU to measure 4x AA’s voltage without the need for multiple resistor dividers? Easy, we can use 4x Mosfets and have each gate connected to a pin on the MCU.

Here’s the schematic for the LVBM. We have enough pins for the 4x Mosfets / 4x LEDs and some spare. We use a 100K resistor to tie the Mosfet’s gate to source so that it doesn’t stay on when we want it to turn off. Notice that there is only 1 resistor for the LEDs, this is because we’ll only be turning 1 LED on at a time.


Now we can jump into the code.

#define F_CPU 1000000 // 1 MHz clock

#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/sleep.h>
#include "setup.c"

int voltagePin = 7; // (ADC7) PA7
#define batt1 (1<<PA0)
#define batt2 (1<<PA1)
#define batt3 (1<<PA2)
#define batt4 (1<<PA3)
#define led1 (1<<PA4)
#define led2 (1<<PA5)
#define led3 (1<<PA6)
#define led4 (1<<PB2)

#define COUNTSTART 226
#define LOWVOLT 0

// Battery count down counters, used so when know when to re-check voltage
byte batteries[4] = {1, 1, 1, 1};

We have the standard includes and we define the batteries (battx), LEDs (ledx) and have 2 defines which we’ll use later. We have an array of 4 elements, one for each battery which we’ll use to find out when to re-check battery voltage or when a battery is found low

int main(void) {


  // Set Outputs
  DDRA |= (batt1 | batt2 | batt3 | batt4 | led1 | led2 | led3);
  DDRB |= (led4);

  // Turn on pull-up resistors to save power on un-used ports
  PORTB |= ((1<<PB0) | (1<<PB1));

Standard stuff, set outputs and pull-ups.

  while(1) {

    int battNum = 0;
    for (battNum = 0; battNum < 4; battNum++) {
      if (batteries[battNum] <= TIMETOCHECK) { // Time to check battery
        PORTA |= (1<<battNum); // Turn on Mosfet for the corresponding battery (PA0-3)
        setup_watchdog(T32MS); // Wait for mosfet to turn on

        int voltageValue = analogRead(voltagePin);
        PORTA = 0x00; // Turn off Mosfet

We loop through all 4 batteries and on our first run each battery’s value in the array is 1 which means it’s time to check the voltage. We turn on the Mosfet of the first battery, sleep for 32ms with the watchdog timer to give enough plenty of time for the Mosfet to turn on, use the ADC and then turn off the Mosfet.

        if (voltageValue > 0 && voltageValue < 512) { // If more than 0V but less than 1.1V
          batteries[battNum] = LOWVOLT; // Battery low, keep re-checking this battery every 4 seconds
          // Light LED for 0.5 seconds
          if (battNum < 3) {
            PORTA |= (1<<(battNum + 4));
          else {
            PORTB |= led4;


          PORTA = 0x00; // LEDs off
          PORTB &= ~led4;
        else if (voltageValue > 0) {
          batteries[battNum] = COUNTSTART; // Battery ok, wait 5 minutes before re-checking this battery
        else {
          batteries[battNum] = TIMETOCHECK; // No battery present, check again in 4 seconds
      else {

We need to check if a battery is even connected, so we check if the ADC value returned is more than 0 and then less than 512 (1.1 volts), if it is we set the batteries array value to indicate it has low voltage (0), otherwise we set it to check again in 15 minutes (226). We light up the corresponding LED up for 500ms using the watchdog and turn off LEDs.

We light up the LEDs by shifting 4 to the left of the battery we are checking starting from 0, e.g. batt0 = PA0 shifting left by 4 gives us (00010000) which corresponds to PA4 (LED1).

On the next run, any low voltage batteries will be checked because we have the “if” statement checking if it’s less or equal to 1. For batteries which passed (voltage >= 512), we just de-increment the value until it would reach 1 like in our first run. If there wasn’t a battery connected (= 0), we set it to TIMETOCHECK so it’ll check them again in the next run.


ISR(WDT_vect) { }

Now we sleep for 4 seconds with the watchdog and have our blank WDT interrupt. If any batteries were set to 226 (COUNTSTART) meaning that they checked out fine, we would check them again in 226 – 1 = 225. 225 x 4 watchdog sleep seconds = 900 seconds. 900 / 60 seconds = 15 minutes.


Download Low_Voltage_Battery_Monitor_v1.0

Leave a Reply