Low power ATmega/tiny with watchdog timer

The clone army grows

At work recently, the pranks have been escalating. I’ve decided that for my next salvo, I’m going to build the most annoying beeping device I can. I’m using an ATtiny45/85 chip, programmed using the Arduino development environment.

 

The clone army grows
The clone army grows

The device is intended to be planted somewhere near the target’s desk, and will just beep (or make some other annoying sound), every 5-8 minutes. In that respect, it’s very similar to the Annoy-a-tron that ThinkGeek sells. (Which was one of my previous salvos.)

Three primary factors influenced the design of this, in this order:

  1. Low power consumption
  2. Inexpensive
  3. Small size

It’s likely to take the subject days, if not weeks, to find it at that rate, so the battery needs to last for at least a few weeks. This means either having a huge battery pack, which would go against point #3, or making the AVR chip use very little power. Thankfully, there are some excellent resources out there about lowering power use on an AVR chip.

As I intend to make around 10 of these devices, keeping the cost down was important. In the end, this led to it drawing about 7 times as power as is possible to get it down to, but with a lower part count, which translates to lower cost. In particular, Sparkfun has an excellent tutorial on getting an AVR down to only 1 microamp of current draw, however this uses a 32KHz watch crystal, which I intended to do without.

Making this as small as possible was quite a challenge, but in the end, the largest parts were the battery and battery holder. This requirement led me away from the 28-pin DIP ATmega328 I prototyped with to an 8-pin DIP ATtiny45/85 that I used in the end. Even the piezo speaker I used is bigger than the AVR.

However, the most challenging part of this project for me was getting the power consumption down to a rate that would allow the CR2032 battery, chosen for it’s size, to power this for long enough. Most of what I learned about power saving I learned from the aforementioned Sparkfun tutorial, and the nightingale example from this page. I found there were 4 key things to getting an AVR to run at low power:

  1. Putting the chip to sleep when it’s idle. (delay() doesn’t do this.)
  2. Setting the clock speed low.
  3. Disabling any peripherals you’re not using
  4. Disabling brown-out protection.

1. Putting the chip to sleep when it’s idle.

Putting the chip to sleep is pretty easy:

set_sleep_mode(SLEEP_MODE_PWR_DOWN);
sleep_enable();
sleep_mode();

But then you need some way to wake it up. The Sparkfun tutorial uses a 32.686kHz external clock crystal to run Timer2 and wake it up every 8 seconds. Since I’m trying to keep the cost down, I’m forgoing the use of an external clock crystal, so I need a different way to wake the chip back up. Instead, I’m using the internal watchdog timer to wake it up, which I learned about from the nightingale example. I copied the setup_watchdog function from that example and I call it in my setup(), with setup_watchdog(9) so that it wakes up every 8 seconds. When it wakes up, it then calls a function, which is defined like:

ISR(WDT_vect) {
  watchdog_counter++;
}

watchdog_counter is a global variable in my program, used in loop() to keep track of how many times the watchdog has been called.

The loop looks something like:

void loop() {
  sleep_mode();
  // ZZzz....
  if (watchdog_counter >= 60) {
    watchdog_counter = 0;
    annoy();
  }
}

When sleep_mode() is called, the chip goes to sleep, and drops down to much lower current draw. Approximately 8 seconds later, the watchdog wakes it up, and calls the function defined with ISR(WDT_vect). Once that function returns, the loop() function continues again by returning from the call to sleep_mode(). At this point then, the code runs as you would expect it to. It continues on the next line, which checks the counter. If it’s high enough, it calls annoy() and makes annoying noises. When it reaches the bottom of the loop, and continues again at the top, it calls sleep_mode() and goes back to sleep. So to summarize: every 8 seconds it gets woken up, loop() comes out of it’s coma, checks if it’s time to make noise, and then goes back to sleep.

That alone has a dramatic effect on the power usage, but to really get it low, there’s three more things to do.

2. Setting the clock speed low.

The ATmega328 on the Arduino Uno runs at 16MHz. This is great when you care more about getting things done quickly than about power use. For my application, it’s much faster than I need, so I’m happy to sacrifice some speed if it’ll use less power. As a result, my chip is only running at 8MHz. It’s also using the internal oscillator for this, instead of an external clock crystal, to save parts, and lower the cost.

To change the clock speed, and to make it use the internal oscillator, you need an AVR programmer of some sort. There are many AVR programmers out there, but since I had a few spare Arduino boards kicking around, I’m using the Arduino-ISP sketch to turn one of them into a programmer. My ATMega328 is on a breadboard, with the minimum supporting hardware for it to run.

Changing things like the clock speed is done by setting “fuses”. If you haven’t set fuses before, keep this in mind: Setting fuses wrong can do (almost) irreversible things like disabling programming. With that warning in mind, Adafruit has some good info about using avrdude to set fuses.

As far as I’ve been able to determine, this only affects the power usage of the chip when it’s awake.  While it’s sleeping, the clock speed doesn’t appear to have any effect on the current draw.

I’m using E2 for lfuse on an ATyiny85, to set a 8MHz clock.  Lowering it to 1MHz didn’t make much of a difference in power use.

3. Disabling any peripherals you’re not using

With the chip asleep, and the clock at 8MHz, it was still drawing 330 microamps.

The Sparkfun tutorial had some examples of how to disable various parts of the chip.  This can all be done in the code. The code from the example is intended for an ATmega328, so not all of it applies to the ATtiny85.  This is the only part that had any effect on the current draw while asleep for me:

ADCSRA &= ~(1<<ADEN); //Disable ADC
ACSR = (1<<ACD); //Disable the analog comparator
DIDR0 = 0x3F; //Disable digital input buffers on all ADC0-ADC5 pins.

4. Disabling brown-out protection.

At this point, the current draw when asleep was 30 microamps.  Still higher than I wanted.

The trick, which again I learned from the SparkFun tutorial, was to disable brown-out protection.  It’s pretty easy to do, once you already know how to set fuses. I’m just setting efuse to 07 (or FF, which is the same thing) to disable brownout protection.

With all that done, it’s drawing 0.007ma when it’s sleeping, and about 7ma when it’s actually beeping.  7 microamps is exactly what the ATtiny85 datasheet (figure22-12) claims it should draw at 5 volts in power-down mode, with the watchdog timer running, at 25°C.

I suppose one thing that’s worth mentioning is that my circuit is ridiculously simple, so there’s nothing else that could end up drawing extra power. I have a 10k resistor from the reset pin to the Vcc pin, and a piezo speaker connected to a digital output pin, and ground. I have a stack of coin cell batteries connected to Vcc and ground, without any capacitors or anything. It’s been working just fine that way.

More info about power usage to come in a future blog post.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s