Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
Arduino on Battery: How to Measure VCC ?
Next one in series of articles Arduino on Battery is how to know when running off battery and when off external power - that is, if hardware does not provide signal to read. Assuming external power is always around 5V while battery is either 3.7V Lithium cell, 3V button battery or 2x 1.5V AA cells, etc. If you use 4x NiMH 1.25V = 5V, you cannot tell by measuring VCC and will need to measure USBVCC and/or VIN, which will require additional hardware and I/Os.

Atmel CPUs allows using Vcc as voltage reference and measure it's internal 1.1V using this reference. So instead of V = Vref * N / 1024, we will do Vref = V * 1024 / N

NOTE: The 1.1V reference is rather crude (datasheet spoecifies 1.0-1.2, which is ±9% spread) as its main purpose is brown-out detector and datasheet makes no representations about time, voltage and temperature stability. Because the reference is located on the same chip as CPU, it is subject to internal temperature, which will vary widely depending on the program, what peripherals are activated and how much current flows through I/Os. For more serious measurements, consider investing $0.20 into external reference like TL431A, which is usually 50-100ppm/'C (depending on manufacturer and submodel). Notice in datasheet Figure 1, that area around 25'C is horizontally flat, so at ±5'C room temperature the device will have virtually no temperature error. The output impedance is 0.2 ohm, so change of Vcc between 3-5V with 470ohm resistor will result in error (5-3)/470*0.2 / 2495mV * 100 = 0.85mV / 2495mV * 100 = 0.034%. Many manufacturers make this one, not just TI. Before you go and stock up on cheap Chinese clones, consider AZ431AZ-ATRG1 from Diodes Incorporated. This is the BMW of x431 references at the price of Volkswagen. It sports 20ppm temperature stability and < 0.4% initial accuracy (i.e. 2.5V ±10mV) so for 99% of applications no need to calibrate at all. Even dynamic resistance, which was already not an issue in TI case is slightly improved.

If you got side-tracked by my note, let's get back to the topic. The following code is taken from this article except original had a tiny mistake in calculation. If you are doing other measurements, dont forget to switch back references when done (not included in below code):

word readVcc() 
    // Read 1.1V reference against AVcc
    // set the reference to Vcc and the measurement to the internal 1.1V reference
#if defined(__AVR_ATmega32U4__) || defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__)
    ADMUX = _BV(REFS0) | _BV(MUX4) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1);
#elif defined (__AVR_ATtiny24__) || defined(__AVR_ATtiny44__) || defined(__AVR_ATtiny84__)
    ADMUX = _BV(MUX5) | _BV(MUX0);
#elif defined (__AVR_ATtiny25__) || defined(__AVR_ATtiny45__) || defined(__AVR_ATtiny85__)
    ADMUX = _BV(MUX3) | _BV(MUX2);
    ADMUX = _BV(REFS0) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1);

    delay(2); // Wait for Vref to settle
    ADCSRA |= _BV(ADSC); // Start conversion
    while (bit_is_set(ADCSRA,ADSC)); // measuring

    uint8_t low  = ADCL; // must read ADCL first - it then locks ADCH  
    uint8_t high = ADCH; // unlocks both

    word result = (high<<8) | low;
    result = 1126400L / result; // Calculate Vcc (in mV); 1126400L = 1100mV * 1024
    return myVcc=result; // Vcc in millivolts

Then determining if we are running off batteries is as simple as this:

    bool isOnBattery() { return myVcc < 4500; }

    bool isOnPower() { return !isOnBattery(); }
Hi Roman,
i am not sure where i might use this , but nice to read through and know it exists.
Thanks for posting the article.
Bob D

Forum Jump:

Users browsing this thread: 1 Guest(s)