I was playing with a small battery-powered circuit that intends to periodically flash an LED. It's for amusement's sake, really, so nothing fancy.
The 'problem' (observation) I'm having is that the ATtiny85 is drawing far more current in deep sleep than anticipated. The mcu draws about 185uA; I would expect it to draw about 10uA according to the datasheet.
The hardware is DIY and I'm currently working with a prototype/test PCB that has some bug fixes:
The schematic of the circuit as built (including modifications) is as follows:
Some remarks:
R5 is really just a piece of wire.
SW1 is a "touch" pad which is simply a GND and a signal trace wrapped around each other on the PCB; see photo above.
Yes, LED1 has no current limiting resistor. This is intentional. No, it doesn't damage the microcontroller or the LED when run properly (i.e. brief flashes of a couple dozen ms). It's also not the cause of the surprisingly high deep sleep current draw.
Yes, I could probably shave off another 8uA by removing R8.
Yes, I could probably shave off some more by changing some resistors to higher values, but I'm well-stocked up to 1Meg and very poorly so above that...
Nothing is connected to the SPI header when taking the measurements.
Current is measured with a cheap DMM in the negative lead going to the battery. I cross-checked the measurements with a second DMM; they measure virtually the same current consumption.
The voltage and current measurements are taken when running on battery power with the mcu in deep sleep ('power down') with only the watchdog timer running (well, that's what I think, at least... )
I've made a minimum demonstrator code that produces the measurements as reported:
#include <avr/sleep.h>
#include <avr/power.h>
#include <avr/wdt.h>
#include <avr/interrupt.h>
void setup() {
ADCSRA &= ~ADEN; // ADC off
MCUCR |= BODS; //Disable brown-out detector during sleep
while (1) {
system_sleep();
}
}
void loop() {
}
//Modified from https://www.marcelpost.com/wiki/index.php/ATtiny85_WDT_sleep
void setup_watchdog(int ii)
{
// 0=16ms, 1=32ms,2=64ms,3=128ms,4=250ms,5=500ms
// 6=1 sec,7=2 sec, 8=4 sec, 9= 8sec
uint8_t bb;
if (ii > 9 ) ii=9;
bb=ii & 7;
if (ii > 7) bb|= (1<<5);
bb|= (1<<WDCE);
MCUSR &= ~(1<<WDRF); //Clear WDT interrupt flag
WDTCR |= (1<<WDCE) | (1<<WDE); //Start timed sequence
WDTCR = bb; // set new watchdog timeout value
WDTCR |= _BV(WDIE); //WDT interrupt enable
}
//Modified from https://www.marcelpost.com/wiki/index.php/ATtiny85_WDT_sleep
// system wakes up when watchdog is timed out
void system_sleep()
{
DIDR0 &= ~(AIN1D | AIN0D); //Disable ADC digital inputs
setup_watchdog(9); // approximately 32ms sleep
set_sleep_mode(SLEEP_MODE_PWR_DOWN); // sleep mode is set here ; SLEEP_MODE_IDLE, SLEEP_MODE_ADC, SLEEP_MODE_PWR_DOWN
sleep_enable();
sei(); // Enable the Interrupts so the wdt can wake us up
power_all_disable(); //Disable all peripherals
sleep_mode(); // System sleeps here
sleep_disable(); // System continues execution here when watchdog timed out
}
// Watchdog Interrupt Service / is executed when watchdog timed out
ISR(WDT_vect)
{
asm("NOP");
}
It's modified from the slightly larger sketch that also flashes the LED etc. In this version above the microcontroller does nothing except sleep, and then sleep some more.
I've also desoldered the ATtiny85 from the circuit to verify that the current consumption from the rest of the switching hardware (the bunch of transistors etc.) is not the culprit; indeed, it isn't; everything except the uC draws something like 75uA. The 'excess' current is really drawn by the controller itself.
In the system as built I run the uC from the internal 1MHz clock, but it doesn't matter since the clock is disabled most of the time in the sketch above. Indeed, running a faster 8MHz internal RC doesn't change a thing, as expected. I'm using ATtinyCore.
The most plausible explanation I can come up with so far is that the controller is counterfeit. I purchased this one a few years ago (2019?) from AliExpress. I've used several from the same batch and they all work fine, but this is the first time I try minimizing power use with one. I can very well imagine that these are reasonably good copies that only fall through when they're pushed to the limits - in this case in terms of dissipation.
Here's the chip btw:
The alternative explanations would be:
- Somehow there are peripherals enabled/running (e.g. internal voltage reference?) that I'm not aware of. I've gone through the relevant bits of the datasheet, but there may be stuff happening 'under the hood' of the Arduino core that I'm overlooking.
- The uC is drawing current through the circuit through a route I'm overlooking in my measurements. But for the life of me, I'm not seeing where this would be happening.
Any thoughts/musing/ramblings are welcome. As said, it's not a real/serious project, so I'm OK if this happens to be the end of the line for this circuit.
I mainly wanted to mess with a touch-activated circuit that doesn't rely on a uC for keeping a power assert enabled (so it can sleep with all of its GPIO's floating, and then wake up from the WDT) and at the same time use the touch pad as a user input while the circuit runs. It does all this, alright. Power draw with Q1 not conducting is below the measurement threshold, the circuit wakes up nicely when the touch pad is touched, and the 'button' input also works fine. So basically this is already a success, but it would be a nice touch if I could get the current draw down a little further.
This is the whole circuit, but running the code that includes button read and LED flashes (note the LED that's lit in the photo). It's running off an 'empty' CR2032 here, flashing the LED every 5 seconds.