Introduction: Reducing Battery Power Consumption for Digispark ATtiny85

About: Arduino addict

or: Running an Arduino with a 2032 coin cell for 2 years.

Using your Digispark Arduino Board out of the box with an Arduino program it draws 20 mA at 5 volt.

With a 5 volt power bank of 2000 mAh it will only run for 4 days.

Step 1: Reducing Supply Voltage by Using a Li-ion Battery

If you use a Li-ion battery with 3.7 volt as supply, the Digispark board draws 13 mA.

With a 2000 mAh Li-ion battery it will now run for 6 days.

Step 2: Reduce CPU Clock

If you do not use USB connection, heavy math or fast polling in your program, you can reduce the clock speed of your application. Even the heavy polling infrared receiving library IRremote runs well at 4 MHz and does not require the full 16 MHz speed.

At 1 MHz the Digispark board draws 9 mA. With 2000 mAh it will now run for 9 days.

Step 3: Remove the on Board Power LED and Power Regulator

Disable the power LED by breaking the copper wire that connects the power LED to the diode with a knife or remove / disable the 102 resistor. The LED runs at 1.9 volt, so with 3.7 volt Li-ion there are 1.8 volt at the 1k resistor resulting in 1.8 mA for the LED.

Since you are using a Li-ion battery now, you can also remove the on board power regulator IC. First lift the outer pins with the help of a solder iron and a pin. Then solder the big connector and remove the regulator. For small regulators, use much solder and heat up all 3 pins together, then remove it. I use the new free space for a reset button using the ground plane to fix it with solder.

The regulator internally has a 2k2 resistor between its output and ground, using 1.7 mA.

At 1 MHz and 3.7 volt the Digispark now draws 5.5 mA. With a battery of 2000 mAh it will run for 15 days.

Step 4: Optional - Disconnecting the USB D- Pullup Resistor (marked 152) From 5 Volt (VCC) and Connect It to USB V+

This step removes the pullup resistor connecting VCC and the 3.7 volt zener diode. This resistor is 1k5 on micro USB boards and 1k0 on USB A boards.

At 5 volt it saves 1.6 mA, with a 3.7 volt Li-ion supply, it still saves between 1.1 mA @4.2 V and 0.7 mA @ 3.5 V!

The saving for 3.5 V is due to the non perfect Zener diode characteristic, which requires 0.7 mA to reach 3.5 volt.

This modification is compatible with all 1.x versions of the micronucleus bootloader. If you already have a new 2.x bootloader on your board, you must upgrade to one of the 2.6 versions with "activePullup" in its name. The easiest way to do this, is to install the new digispark board package and burn the bootloader with the recommended (!!!not the default or aggressive!!!) version.

Break the copper wire on the side of the resistor that points to the ATtiny.

This disables the USB interface and in turn the possibility to program the Digispark board via USB. To enable it again, but still save power, connect the resistor (marked 152 on micro USB boards and 102 on USB A boards) directly to the USB V+ that is easily available at the outer side of the shottky diode.

The diode and its correct sides can be found by using a continuity tester. One side of this diode is connected to pin 8 of the ATtiny (VCC) and Digispark 5V. The other side is connected to the USB V+.

Now the USB pullup resistor is only activated if the Digispark board is connected to USB e.g. during programming.

At 1 MHz and 3.7 volt the Digispark now draws 1.6 mA. With a battery of 2000 mAh it will run for 52 days.

The internal LED requires additional 1.8 mA if on.

Step 5: Use Sleep Instead of Delay()

Instead of long delays you can use power saving CPU sleep. Sleeps can last from 15 milliseconds to 8 seconds in steps of 15, 30, 60, 120, 250, 500 milliseconds and 1, 2, 4, 8 seconds.

Since startup time from sleep is 65 milliseconds with the factory digispark fuse settings, only delays bigger than 80 ms can be replaced by sleep.

During sleep the ATtiny85 draws 260 µA. 240 µAare drawn by the ADC. Disabling it with

ADCSRA = 0;

reduces current to 20 µA. With a 200 mAh button cell 2032 it will sleep for 13.5 month. The ADC is automatically enabled at the next conversion or can be enabled with:

ADCSRA = ADEN;


To be correct, the ATtiny85 must at least wake up every 8 seconds, running for at least 65 milliseconds and drawing around 2 mA current. This leads to an average current of 40 µA and 6 month. In this scenario, it makes almost no difference if your program runs for 10 milliseconds (every 8 seconds).

The code for using sleep as delay is:

#include <avr/sleep.h>
#include <avr/wdt.h>
volatile uint16_t sNumberOfSleeps = 0;
extern volatile unsigned long millis_timer_millis;

void setup() {
sleep_enable();
set_sleep_mode(SLEEP_MODE_PWR_DOWN); // deepest sleep mode
...
}

void loop(){
...
sleepWithWatchdog(WDTO_250MS, true); // sleep for 250 ms
...
sleepWithWatchdog(WDTO_2S, true); // sleep for 2 s
...
}

/*
 * @param aWatchdogPrescaler (see wdt.h) can be one of WDTO_15MS, 30, 60, 120, 250, WDTO_500MS, WDTO_1S to WDTO_8S
 *                                                    0 (15 ms) to 3(120 ms), 4 (250 ms) up to 9 (8000 ms)
*/
uint16_t computeSleepMillis(uint8_t aWatchdogPrescaler) {
uint16_t tResultMillis = 8000;
for (uint8_t i = 0; i < (9 - aWatchdogPrescaler); ++i) {
tResultMillis = (tResultMillis / 2);
}
return tResultMillis + 65; // + 65 for the startup time
}

/*
 * @param aWatchdogPrescaler (see wdt.h) can be one of WDTO_15MS, 30, 60, 120, 250, WDTO_500MS, WDTO_1S to WDTO_8S
 * @param aAdjustMillis if true, adjust the Arduino internal millis counter the get quite correct millis()
 * results even after sleep, since the periodic 1 ms timer interrupt is disabled while sleeping.
*/
void sleepWithWatchdog(uint8_t aWatchdogPrescaler, bool aAdjustMillis) {
MCUSR = 0; // Clear MCUSR to enable a correct interpretation of MCUSR after reset
ADCSRA &= ~ADEN; // disable ADC just before sleep -> saves 200 uA
// use wdt_enable() since it handles that the WDP3 bit is in bit 5 of the WDTCR register
wdt_enable(aWatchdogPrescaler);
WDTCR |= _BV(WDIE) | _BV(WDIF); // Watchdog interrupt enable + reset interrupt flag -> needs ISR(WDT_vect)
sei(); // Enable interrupts
sleep_cpu(); // The watchdog interrupt will wake us up from sleep

// We wake up here :-)
wdt_disable(); // Because next interrupt will otherwise lead to a reset, since wdt_enable() sets WDE / Watchdog System Reset Enable
ADCSRA |= ADEN;
/*
* Since timer clock may be disabled adjust millis only if not slept in IDLE mode (SM2...0 bits are 000)
*/
if (aAdjustMillis && (MCUCR & ((_BV(SM1) | _BV(SM0)))) != 0) {
millis_timer_millis += computeSleepMillis(aWatchdogPrescaler);
}
}

/*
* This interrupt wakes up the cpu from sleep
*/
ISR(WDT_vect) {
sNumberOfSleeps++;
}

Step 6: Modify the Fuses

22 µA of the the 27 µA are drawn by the BOD (BrownOutDetection/undervoltage detection) and the internal voltage reference used for its function. The BOD can only be disabled by reprogramming the fuses, which can only be done with an ISP programmer. Using this script you can reduce current down to 5.5 µA and also reduce startup time from sleep from 65 to 4 milliseconds.

If you disable the BOD and use the 1.1 V reference for analog conversions, keep in mind that the reference has a startup time of 40 to 70 µs.

5 of the remaining 5.5 µA are drawn by the active watchdog counter. If you can use external resets or pin change interrupts for wake up, current consumpion can go down to the 0.3 µA as stated in the datasheet.

If you are not able to reach this value, the reason can be, that the reverse current of the schottky diode between VCC and the pullup is too high. Keep in mind that a 12 MOhm resistor also draws 0.3 µA at 3.7 volt.

If you process data every 8 seconds for 3 milliseconds like here, this ends up in an average current consumption of 9 µA (2.5 years with a 200 mAh button cell 2032)

Step 7: Further Information

Battery Powered Contest

Participated in the
Battery Powered Contest