ATTiny85 crashes after a few minutes - SOLVED

See solution at bottom

I would like to use an ATTiny85 to control an LED with button clicks. The device would need to run for - at most - 18 hours continuously.

If the LED is off, one click causes it to fade on and back off. If the LED is fading, one click causes it to come fully on and stay on. If the LED is fully on, one click turns it off.

I have included my code below. It works as expected ... for a few minutes. Then the other three LEDs (which I included for debugging) start blinking randomly and the circuit doesn't respond to button presses. If I power cycle the circuit, I get another three minutes of normal behavior.

I currently have the circuit built on a breadboard. I'm powering it with a 5V portable power supply from Adafruit. If it's not clear from the diagram I attached, there's a 0.1uF capacitor connecting the ATTiny's power and ground - with nothing between it and the ATTiny - and a 10K resistor connecting the RESET pin to VCC.

I'm using a Radio Shack Learning Lab as my breadboard. The built-in LEDs also have built-in resistors, but the documentation doesn't tell me what value they are.

I'm using version 0.3.0 of the AbleButtons library. It's included with the Arduino IDE. The library automatically enables the pullup resistor on the button's pin - that's why INPUT_PULLUP isn't specified in setup().

I'm programming the ATTiny85 using the Sparkfun USB Tiny Programmer and the Arduino IDE. The clock is set to 1MHz (internal).

Any help is greatly appreciated. Thank you!

#include <AbleButtons.h>

using Button = AblePullupClickerButton;

#define TRIGGER_PIN 2
Button trigger(TRIGGER_PIN);

//Main LED
#define LED1 0
bool led1 = false;
unsigned int brightness = 0;
//three phases of fade cycle
const unsigned int increaseDuration = 500;
const unsigned int steadyDuration = 500;
const unsigned int decreaseDuration = 500;
//used to calculate brightness
unsigned long increaseElapsed = 0;
unsigned long decreaseElapsed = 0;
//these are modes that determine whether the LED at some point in the fade cycle, steadily on in photo mode,
//or off (it's only entirely off when awaiting input)
#define DARK 0
#define INCREASE 1
#define BRIGHT 2
#define DECREASE 3
#define PHOTO 4
#define CYCLE 5
int mode = DARK;
int cycleMode = INCREASE;

unsigned long cycleStart = 0;
unsigned long cycleDecreaseStart = 0;

//Debug LEDs
#define DEBUG1 3
bool debug1 = false;
#define DEBUG2 1
bool debug2 = false;
#define DEBUG3 4
bool debug3 = false;

void setup() {
  // put your setup code here, to run once:
  pinMode(LED1, OUTPUT);
  pinMode(DEBUG1, OUTPUT);
  pinMode(DEBUG2, OUTPUT);
  pinMode(DEBUG3, OUTPUT);

  //don't need to set the trigger pin as a pullup because the library does that
  trigger.begin();
  cycleStart = millis();
  mode = DARK;
}

void loop() {
  // put your main code here, to run repeatedly:
  trigger.handle();
  //lets us know ATTiny is alive and sane
  digitalWrite(DEBUG1, HIGH);

  if (trigger.resetClicked()) {
    //a little sanity checking
    debug2 = !debug2;
    digitalWrite(DEBUG2, debug2);
    if (mode == DARK) {
      mode = CYCLE;
      cycleMode = INCREASE;
      cycleStart = millis();
    } else if (mode == CYCLE) {
      mode = PHOTO;
    } else if (mode == PHOTO) {
      mode = DARK;
    } else {
      //shouldn't happen
      digitalWrite(DEBUG3, HIGH);
    }
  }

  if (mode == CYCLE) {
    if (cycleMode == INCREASE) {
      increaseElapsed = millis() - cycleStart;
      brightness = 255 * (increaseElapsed / float(increaseDuration));
      if (brightness > 255) {
        //if millis() rolls over, calculated brightness might be oddly high
        brightness = 255;
      }
      analogWrite(LED1, brightness);
      if ((millis() - cycleStart) < increaseDuration) {
        return;
      } else {
        cycleMode = BRIGHT;
      }
    }  else if (cycleMode == BRIGHT) {
      analogWrite(LED1, 255);
      if ((millis() - cycleStart) < (increaseDuration + steadyDuration)) {
        return; 
      } else {
        cycleMode = DECREASE;
        cycleDecreaseStart = millis();
      }
    } else if (cycleMode == DECREASE) {
      decreaseElapsed = millis() - cycleDecreaseStart;
      brightness = 255 - (255 * (decreaseElapsed / float(decreaseDuration)));
      if (brightness > 255) {
        //if millis() rolls over, calculated brightness might be oddly high
        brightness = 0;
      }
      analogWrite(LED1, brightness);
      if ((millis() - (cycleDecreaseStart) < decreaseDuration)) {
        return;
      } else {
        cycleMode = INCREASE;
        brightness = 0;
        mode = DARK;
      }
    } else {
      //shouldn't happen
      //illegal cycle sub-mode
      digitalWrite(DEBUG3, HIGH);
    }
  } else if (mode == PHOTO) {
    analogWrite(LED1, 255);
  } else if (mode == DARK) {
    analogWrite(LED1, 0);
  } else {
    //illegal mode
    //shouldn't happen
    digitalWrite(DEBUG3, HIGH);
  }
}

attiny85 circuit improved

Solution
Summary: Circuit didn't draw enough power to keep power supply awake.

The LiPo battery I'm planning to use for this project wasn't here yet, so I thought I'd at least test the code on a 5V portable power supply I use for other portable projects. However, I'd forgotten that this power supply goes to sleep under low loads - in the past, I'd always used it with servos, NeoPixels, etc., that kept it awake!

The power supply doesn't shut off altogether when it's asleep, but when the ATtiny was starved for power it would flicker lights randomly - I could see that it was powered up but not working.

Thank you to everyone who responded!

Your schematic shows it connected to ground. That would prevent the attiny from running it's code.

It's an error in the schematic. I've removed it and added an accurate one.

Be careful when using anything other than unsigned long in calculations involving millis(). Also ensure that such calculations handle the millis() rollover correctly.

1 Like

I've changed the code so it'll handle the millis() rollover, and changed the unsigned int variables I was using to check the difference between timestamps to unsigned long. (I've also updated the original post with the new code.)

I still have the same problem with the ATTiny85 losing its miniature mind after a few minutes - but at least now my code is following best practices! :slightly_smiling_face:

I tried your code with Attinycore and Arduino IDE 1.8.18 and it works fine for at least 10 minutes. Does exactly what you described.
(these are my settings)

And if you get rid of the floats you can save almost 1KByte

#include <AbleButtons.h>

using Button = AblePullupClickerButton;

#define TRIGGER_PIN 2
Button trigger(TRIGGER_PIN);

//Main LED
#define LED1 0
bool led1 = false;
unsigned int brightness = 0;
//three phases of fade cycle
const unsigned int increaseDuration = 500;
const unsigned int steadyDuration = 500;
const unsigned int decreaseDuration = 500;
//used to calculate brightness
unsigned long increaseElapsed = 0;
unsigned long decreaseElapsed = 0;
//these are modes that determine whether the LED at some point in the fade cycle, steadily on in photo mode,
//or off (it's only entirely off when awaiting input)
#define DARK 0
#define INCREASE 1
#define BRIGHT 2
#define DECREASE 3
#define PHOTO 4
#define CYCLE 5
int mode = DARK;
int cycleMode = INCREASE;

unsigned long cycleStart = 0;
unsigned long cycleDecreaseStart = 0;

//Debug LEDs
#define DEBUG1 3
bool debug1 = false;
#define DEBUG2 1
bool debug2 = false;
#define DEBUG3 4
bool debug3 = false;

void setup() {
  // put your setup code here, to run once:
  pinMode(LED1, OUTPUT);
  pinMode(DEBUG1, OUTPUT);
  pinMode(DEBUG2, OUTPUT);
  pinMode(DEBUG3, OUTPUT);

  //don't need to set the trigger pin as a pullup because the library does that
  trigger.begin();
  cycleStart = millis();
  mode = DARK;
}

void loop() {
  // put your main code here, to run repeatedly:
  trigger.handle();
  //lets us know ATTiny is alive and sane
  digitalWrite(DEBUG1, HIGH);

  if (trigger.resetClicked()) {
    //a little sanity checking
    debug2 = !debug2;
    digitalWrite(DEBUG2, debug2);
    if (mode == DARK) {
      mode = CYCLE;
      cycleMode = INCREASE;
      cycleStart = millis();
    } else if (mode == CYCLE) {
      mode = PHOTO;
    } else if (mode == PHOTO) {
      mode = DARK;
    } else {
      //shouldn't happen
      digitalWrite(DEBUG3, HIGH);
    }
  }

  if (mode == CYCLE) {
    if (cycleMode == INCREASE) {
      increaseElapsed = millis() - cycleStart;
      brightness = (255 * increaseElapsed) / increaseDuration;
      if (brightness > 255) {
        //if millis() rolls over, calculated brightness might be oddly high
        brightness = 255;
      }
      analogWrite(LED1, brightness);
      if ((millis() - cycleStart) < increaseDuration) {
        return;
      } else {
        cycleMode = BRIGHT;
      }
    }  else if (cycleMode == BRIGHT) {
      analogWrite(LED1, 255);
      if ((millis() - cycleStart) < (increaseDuration + steadyDuration)) {
        return; 
      } else {
        cycleMode = DECREASE;
        cycleDecreaseStart = millis();
      }
    } else if (cycleMode == DECREASE) {
      decreaseElapsed = millis() - cycleDecreaseStart;
      brightness = 255 - ((255 * decreaseElapsed) / decreaseDuration);
      if (brightness > 255) {
        //if millis() rolls over, calculated brightness might be oddly high
        brightness = 0;
      }
      analogWrite(LED1, brightness);
      if ((millis() - (cycleDecreaseStart) < decreaseDuration)) {
        return;
      } else {
        cycleMode = INCREASE;
        brightness = 0;
        mode = DARK;
      }
    } else {
      //shouldn't happen
      //illegal cycle sub-mode
      digitalWrite(DEBUG3, HIGH);
    }
  } else if (mode == PHOTO) {
    analogWrite(LED1, 255);
  } else if (mode == DARK) {
    analogWrite(LED1, 0);
  } else {
    //illegal mode
    //shouldn't happen
    digitalWrite(DEBUG3, HIGH);
  }
}
1 Like

@hmeijdam - this turned out to be a power supply issue. (I've updated the post with details.)

Thank you for confirming that this wasn't a code issue - and for the advice about the floats!

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.