Low-battery death spiral (Or "How I learned to stop worrying and love the brown-out

Low-battery death spiral
(Or “How I learned to stop worrying and love the brown-out reset.”)

TL;DR = You should think about what you want to happen when your project’s batteries run way down; you can use an EEPROM flag to help detect when you’re hitting a “brown-out” like this.

Electronics need a certain minimum voltage to operate correctly. If the voltage drops too low (a “brown out”), they start behaving completely randomly, which can be bad. Think about a garage door controller starting to operate completely randomly. Yeah. Bad. So most microcontrollers (like Arduino) have a brown-out detector, and they automatically reset themselves if the voltage gets too low.

So suppose you have an electronics project, like my #ExcellentBirds project, which sits there ‘ready’ for a while using less power, and then does something that uses more power. This is a pretty common pattern of interactivity: wait for event, react to event.

The thing is: reacting to the event (in my case, having the Birds start playing MP3 of birdsong) uses more power than just sitting there and waiting, and drawing more current like that causes a voltage drop.

So what happens as the batteries approach the end of their useful life is this: sitting and waiting for something to happen, they are putting out “enough” voltage and all is well. But when they start playing birdsong MP3, that takes more power, and the voltage drops… below the brown-out level. This causes the brown-out detector to kick in, and the whole circuit to suddenly reset.

Of course, once it’s reset, and no longer playing an MP3, the voltage recovers, and everything is fine… until it tries playing an MP3 again. And then it browns-out and stops abruptly in the middle of the audio, etc., etc., Lather. Rinse. Repeat.

What to do about it?
I think the key to working with this situation is detecting that it is happening, which is tricky because when the Arduino is reset, it forgets almost everything. However, there is one part of the Arduino memory which persists: EEPROM. Values in EEPROM persist across reboots and resets and brown-outs.

So, here’s the pattern for detecting that you’ve browned-out due to overexertion:

  1. BEFORE your code does something that’s going to draw substantially more power, store a value in EEPROM which means “I’m doing that thing!” (e.g. “I’m playing an MP3!”)

  2. AFTER successfully completing the higher-power activity, replace that EEPROM value with a different one which means “I’m not doing that thing.” (“I finished playing the MP3”.)

  3. In your setup() function, CHECK the EEPROM flag. If it says “I’m doing that thing!” it means that (for some reason) the program was reset while it was ‘doing that thing.’ And in this case, you can use that to infer that you probably had a brown-out/reset!

If you did have a brown-out, now you have to decide what to do about it: just sit there and blink “S-O-S” on the Arduino LED? Try doing the high-power operation again, but at a reduced intensity? (e.g., quieter MP3 playback or dimmer LED animation or raise the garage door more slowly)

In my case, I don’t want the birdsong MP3 to play and then be cut off and then play again, so what I do when I’ve detected brown-out/reset is drop into a ‘fail safe’ mode, which blinks out a “please change my batteries” signal, but does not try bursting into MP3 song again until it’s been manually reset.

If I were making a battery-powered LED animation, I’d probably try cutting the maximum brightness in half, and trying again: better to have dimmer blinkies than none.

So in summary: you should think about what you want to happen when your project has a brown-out, and you can use an EEPROM flag to help detect it. After that, it’s up to you to decide what to do about it.

Thanks for sharing this great post! In your case, how do you manually reset it? How do you clear out the flag from EEPROM?

Basically, I toggle the flag back to 0 when I reboot and notice the brown-out.

When the code boots up, if the “I’m doing high current work!” flag is still set, I know that I died from brownout, and I go into “change my batteries please” mode: I set the “I’m doing high current work” EEPROM flag back to 0, and just sit in a loop blinking out an SOS code on the on-board LED.

The next reset/boot up will be because of one of two things: EITHER (1) I changed the batteries OR (2) the batteries can no longer even sustain the onboard LED. In either case, it’s fine to (try to) go back into regular operating mode again. If (1) the batteries are new, it’ll all work. If (2) the batteries are really THAT dead, nothing bad will happen, as the Arduino can’t even muster the strength to blink one small LED.

And just to be sure, I have the boot code light the LED for one second. It’s a nice confirmation that the code is live-- AND it acts as a “you must be this tall to ride this ride” gate for the batteries.

I have a more complicated scheme that I’ve used before, but all that’s needed in this case is a second (deliberate) reboot and the flag is good to go.

Ah, that makes perfect sense, thanks!

I like this. Interestingly, now that I see you post here I believe this is happening in my project. The great thing was I was already doing the EEPROM thing… However, is it possible to put a capacitor in the mix somehow to prevent the voltage drop? Perhaps a capacitor and a xener diode to prevent the power draw from vampiring from the mcu?

So if it were just a spike, a cap might help. But basically it’s a problem where it’s trying to do X amount of work with less than X amount of power available; something’s going to give.

There are, of course, other ways to deal with the general situation (eg all the way to separate power sources for controller and “high current load”), but this is nice because it’s an all-software approach that can be retrocoded into existing projects.

@Justin_Eastman did you read this? Also, think about adding the new power functions from 2.1 if you are running on the edge of the power envelope. EDIT: DOH! I see you posted before… walk on, nothing to see here :wink: