# Measuring battery level on ATTiny when using a step-up converter

When powering a circuit directly from the power source the battery level is easily monitored from a micro controller in the circuit, to notify the user of low battery state. However, when powering a circuit directly off a battery, the voltage level will not be constant and chances are that it will quickly drop below the operating range for one or more components in the circuit, although the battery might have a reasonable amount of power left.

The solution to this is to use a step-up converter or boost converter, which can output a constant voltage level from varying input levels. This is an ideal part of any circuit that is powered by batteries to e.g. drive a `3v3` circuit from a pair of AA batteries.

The problem with introducing a step-up converter in a circuit is that the battery level cannot be readily measured because the supply voltage will always be at the output level of the converter (minding its tolerance).

Luckily, there is a solution to this, which requires only a few passive components and some code for the micro controller – although at the cost of a single analog input to the micro controller. This method, quite simply, measures the voltage level from the battery on the designated analog input, say `A2`on an Arduino, and compares that to a steady reference voltage.
Now, the difficult part is finding a suitable reference voltage that is steady. It is not possible to use the `vref` of the micro controller – which is at the same level as the output of the step-up converter – because it is rated higher than the maximum supply level of two AA batteries (`3v3` vs. `~3v` – for alkaline batteries and even lower for rechargeables). Fortunately, ATTiny – and other Atmel AVRs – has an internal `1v1` reference voltage, called the bandgap, that can be used to calibrate analog readings, so that the ADC aligns its 10 bit (for ATTiny) resolution to an input range of `0-1v1`. Thus, the battery level can be measured in steps of `1.1/1023v`. To set the internal reference voltage, the `analogReference` method should be used with a suitable argument. For ATTiny, this argument is `INTERNAL`, but for ATMega it is `INTERVAL1V1`, so for the ATTiny the command would be

`analogReference(INTERNAL);`

Unless you have requirements for other analog reference voltages, this call can be put in the `setup()` method of the Arduino sketch.

Now, that the micro controller has a well defined way of reading the battery level, we need to align the battery supply level to lie between the same interval, `0-1v1`. This can be done using a voltage divider, basically consisting of two resistors in series between `GND` and the positive pole on the battery, `vbat`, with the junction connected to the analog input on the micro controller. The resistance should be as high as possible to reduce the current draw of the resistors and thereby to minimise their power consumption. This is important, because this part of the circuit will always be on. The following figure shows this set up, in which we can see that `R2` is connected between the micro controller and `GND` and `R1` is connected between `vbat` and the micro controller. Voltage divider with a 3:5 split

A good starting point for the values of `R1` is `1MΩ`. In order to decrease the largest possible value (`vmax = 3v`) of `vbat` to as close to `vref` as possible, we need to find a suitable value for `R2`. Knowing `vmax`, `vref` and `R1`, we can calculate `R2` by rewriting the formula for calculating `vmax`:

```vmax 											= vref * (R1 + R2) / R2
R2 * vmax									= vref * (R1 + R2)
R2 * vmax									= (vref * R1) + (vref * R2)
(R2 * vmax) - (vref * R2)	= vref * R1
R2 * (vmax - vref)				= vref * R1
R2												= (vref * R1) / (vmax - vref)```

Then, for the set up used in this article, `R2` must be

`R2 = (1.1 * 1e6) / (3 - 1.1) = ~578.94kΩ`

Clearly, there exists no standard resistor with a value close to this value, but we can combine several to get a resistance close by, e.g. two `1MΩ` resistors in parallel followed by a `100kΩ` resistor in series, which will yield a combined resistance of `600kΩ`. Using the same formula as above to calculate the highest measurable voltage, `vmax'` based on this approximation, we get

`vmax' = 1.1 * (1e6 + 600e3) / 600e3 = 2.9998v`

This is a very good approximation to `vmax`, which we can use to calibrate the calculation of `vbat` from the analog value read by the micro controller.

Over at MySensors they agree on using a `1MΩ` resistor for `R1`, but suggest using a `470kΩ` resistor for `R2` for circuits powered by two AA batteries. I do not agree with this because it yields a `vmax` of `3.44v`, which is way more than we need and what two AA batteries can supply, thus there will be a gap in the upper range of the ADC readings accounting for the `.44` difference between the actual `vmax` (`3v`) and the theoretical `vmax` (`3.44v`).

The analog reading will be between 0 and 1023 because of the resolution of the ADC. To calculate the actual input voltage level, `vbat'`, this reading must be adjusted according to the ADC value of `vbat` (called `ADCbat` in the formula below) and `vmax'`:

`vbat' = (vmax' / 1023) * ADCbat`

So, if the batteries are brand new efficient alkalines, `ADCbat` will be 1023 and the measured voltage level thus

`(2.9998 / 1023) * 1023 = 2.9998v`

To get the battery level in percentage, the following formula can be applied:

`percent = ((vbat' - vmin) / (vmax' - vmin)) * 100.`

`vmin` is the minimal voltage that the circuit can operate under, so, if the step-up converter shuts down at `.5v`, then `vmin` should be `.5`. Assuming this, the full battery scenario from before gives us a percentage reading of

`((2.9998 - 0.5) / (2.9998 - 0.5)) * 100. = 100%`

A sample program could look like this:

```#include <JeeLib.h> // https://github.com/jcw/jeelib

#define BATT_PIN          A2
#define VMIN              0.5
#define VMAX              3
#define R1                1e6
#define R2                600e3
#define VREF              1.1

ISR(WDT_vect) { Sleepy::watchdogEvent(); } // interrupt handler for JeeLabs Sleepy power saving

int sensorValue = 0;

Sleepy::loseSomeTime(100);
for (int i = 0; i < 5; i++) {
Sleepy::loseSomeTime(20);
}

sensorValue /= 5;

float Vmax = ((R1 + R2) / R2) * VREF;
float Vbat = (Vmax / 1023) * sensorValue;

return Vbat;
}

void setup() {
analogReference(INTERNAL);

PRR = bit(PRTIM1); // only keep timer 0 going
ADCSRA &= ~ bit(ADEN); bitSet(PRR, PRADC); // Disable and power down the ADC to save power

pinMode(BATT_PIN, INPUT);

Serial.begin(9600);
}

void loop() {
float Vbat = readVcc();

float supplyV = static_cast(Vbat * 1000); // Get supply voltage
float supplyVPcnt = static_cast(((Vbat - VMIN) / (VMAX-VMIN)) * 100.); //Supply voltage in per cent

Serial.print("Supply voltage: ");
Serial.print(supplyV);
Serial.print("v (");
Serial.print(supplyVPcnt);
Serial.println("%)");

Sleepy::loseSomeTime(1000);
}
```

# Conclusion

Using a simple voltage divider and some math makes it easy to measure external voltage using a micro controller, such as the ATTiny. This method is ideal for battery powered devices, such as IoT sensors and draws only a minimum of power if the resistors are chosen wisely.

According to a-lurker over at the Vera forum, the following pros and cons should be taken into consideration when deciding on whether to use a step-up converter or not.

Pros

• Operating Voltage is held at 3v3 versus the brownout detector threshold of 2v7 kicking in for a direct connection – noting the latter can be disabled
• Batteries can be sucked totally dry using the regulator

Cons

• Regulator is 80% efficient while a direction connection is 100%

Including a voltage divider and a step-up converter in your sensor design allows for power options that are cheaper and more safe than using LiPo batteries and you will probably always have some fresh spare batteries lying around when a sensor needs new energy.

## 2 comments on “Measuring battery level on ATTiny when using a step-up converter” Add yours →

1. Nic Roche says:

Is this circuit still accurate if a step-up converter is not used?

1. Anders says:

Hi Nic,

Yes, indeed. However, if you connect the battery directly to the microcontroller, you might not need this circuit because the MC will probably have some internal mechanism for measuring the supply voltage. Of course, this depends entirely on the MC.

Cheers, Anders

This site uses Akismet to reduce spam. Learn how your comment data is processed.