Voltage divider with a 3:5 split

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 A2on 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
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

float readVcc() {
  bitClear(PRR, PRADC); ADCSRA |= bit(ADEN); // Enable the ADC

  int sensorValue = 0;

  Sleepy::loseSomeTime(100);
  for (int i = 0; i < 5; i++) {
    sensorValue += analogRead(BATT_PIN);
    Sleepy::loseSomeTime(20);
  }
  
  ADCSRA &= ~ bit(ADEN); bitSet(PRR, PRADC); // Disable the ADC to save power
  
  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 converterAdd yours →

    1. 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

Leave a Reply to Anders Cancel reply

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

%d bloggers like this: