ATtiny85 programming/sleep modes/toggle button

Hey all,

Now that I've got my ATtiny85 programmable, I wanted to run a little more complex code. First I'll describe what it is I'm intending to do.

I would like a 1 sec hold button press to both wake and put the ATtiny85 to sleep.
I would also like that same button to be able to run some toggle functions, after turning the ATtiny85 on.
The button will press through a few different functions like a bike light. Bright/dim/flash, that sort of thing.

This is as far as I've gotten with my code. The light remains on. When pressing the button the light does one of two things, either it will get brighter until the button is released or it will go off until the button is released.

the set up runs swell, but trouble with the function. I thought if I could first turn turn the ATtiny85 to sleep and on from the button call it would be a start but I'm having trouble doing that.

#include<avr/sleep.h>
#include<avr/interrupt.h>

const int switchPin=2;
const int statusLED=1;
int buttonState=0;
int lastButtonState=0;
int buttonPushCounter=0;
uint8_t debounceRead(int pin)
{
  uint8_t pinState=digitalRead(switchPin);
  uint32_t timeout=millis();
  while((millis()-timeout)<10)
  {
    if(digitalRead(switchPin)!=pinState)
    {
      pinState=digitalRead(switchPin);
      timeout=millis();
    }
  }//while
  return pinState;
}//uint8_t

void setup() {
  // put your setup code here, to run once:
  pinMode(switchPin,INPUT);
digitalWrite(switchPin,HIGH);
pinMode(statusLED,OUTPUT);
//Flash quick sequence so we know setup has started
for(int k=0;k<10;k=k+1){
  if(k%2==0){
    digitalWrite(statusLED,HIGH);
  }
  else{
    digitalWrite(statusLED,LOW);
  }
  delay(250);
}//for
}//setup

void sleep(){
  GIMSK|=_BV(PCIE);//enable pin change interrupts
  PCMSK|=_BV(PCINT2); //Use PB2 as interrupt pin
  ADCSRA&=~_BV(ADEN);//ADC off
  set_sleep_mode(SLEEP_MODE_PWR_DOWN);//replaces above statment

  sleep_enable();//sets the sleep enable bit in the MCUCR Register (SE BIT)
  sei();//enable interrupts
  sleep_cpu();//sleep

  cli();//disable interrupts
  PCMSK&=~_BV(PCINT2);//turn off PB2 as interrupt pin
  sleep_disable();//Clear SE bit
  ADCSRA|=_BV(ADEN);//ADC on

  sei();//enable interrupts
}//sleep

ISR(PCINT0_vect){
  //This is called when the interrupt occurs, but it is not mandatory to do anything with it
}
void loop() {
  sleep();
  //read the pushbutton input pin:
  buttonState=debounceRead(switchPin);
  //compare the buttonState to its previous state
  if(buttonState!=lastButtonState){
  //check if the pushbutton is pressed.
  //if it is, the buttonState is HIGH:
  if(buttonState==HIGH){
    buttonPushCounter++;
    }
    //turn LED on:
    //digitalWrite(statusLED,HIGH);
    }else{
      //turn LED off:
     lastButtonState=buttonState;
    }
     // digitalWrite(statusLED,LOW);
    //sleep();
    if(buttonPushCounter%2==0){
      digitalWrite(statusLED,HIGH);
    }else{
      digitalWrite(statusLED,LOW);
    sleep();
    }
}

Thanks Arduino forums,
-Jesse

Hey all,

Still having difficulty executing this code properly. Some assistance would be greatly appreciated.

-Jesse

I modified the code from the original from something I've read off inscrutables: http://www.instructables.com/id/Beginner-Arduino/step15/Button-As-Toggle-Switch/

And it all seems to work in turning the LED from HIGH to LOW and recording the pin state. However the LED itself never actually shuts off entirely it just goes extremely dim. Does this have to do with debouncing of the pin?

I assume if I wanted to give this button additional functions I would have to uses a Switch command is this correct?

#include<avr/sleep.h>
#include<avr/interrupt.h>

const int switchPin=2;
const int statusLED=1;
int buttonState=LOW;
int lastButtonState=LOW;
int ledState=LOW;
//int buttonPushCounter=0;
uint8_t debounceRead(int pin)
{
  uint8_t pinState=digitalRead(switchPin);
  uint32_t timeout=millis();
  while((millis()-timeout)<10)
  {
   if(digitalRead(switchPin)!=pinState)
    {
      pinState=digitalRead(switchPin);
      timeout=millis();
    }
  }//while
  return pinState;
}//uint8_t

void setup() {
  // put your setup code here, to run once:
  pinMode(switchPin,INPUT);
digitalWrite(switchPin,HIGH);
pinMode(statusLED,OUTPUT);
//Flash quick sequence so we know setup has started
for(int k=0;k<10;k=k+1){
  if(k%2==0){
    digitalWrite(statusLED,HIGH);
  }
  else{
    digitalWrite(statusLED,LOW);
  }
  delay(250);
}//for
}//setup

void sleep(){
  GIMSK|=_BV(PCIE);//enable pin change interrupts
  PCMSK|=_BV(PCINT2); //Use PB2 as interrupt pin
  ADCSRA&=~_BV(ADEN);//ADC off
  set_sleep_mode(SLEEP_MODE_PWR_DOWN);//replaces above statment

  sleep_enable();//sets the sleep enable bit in the MCUCR Register (SE BIT)
  sei();//enable interrupts
  sleep_cpu();//sleep

  cli();//disable interrupts
  PCMSK&=~_BV(PCINT2);//turn off PB2 as interrupt pin
  sleep_disable();//Clear SE bit
  ADCSRA|=_BV(ADEN);//ADC on

  sei();//enable interrupts
}//sleep

ISR(PCINT0_vect){
  //This is called when the interrupt occurs, but it is not mandatory to do anything with it
}
void loop() {
  buttonState=digitalRead(switchPin);
  if(buttonState==HIGH&&lastButtonState==LOW){//if button has just been pressed
    //toggle the state of the LED
    if(ledState==HIGH){
      digitalWrite(statusLED,LOW);
      ledState=LOW;
      sleep();
    }else{
      digitalWrite(statusLED,HIGH);
      ledState=HIGH;
    }
   }
  lastButtonState=buttonState;
}

Again, any and all help is greatly appreciated.
-Jesse

Hey Arudino forums,

Making a second post after zero feedback on my last, I have continued to edit the code but am still running into trouble.

I've added a switch statement to the code but it doesn't really function as I would hope.

The ATiny85 runs through the set up sequence fine as usual. Then the light goes extremely dim but not completely off.

It remains that way for exactly 6 button presses, where on the sixth press it goes into case 6 of the switch statement.

Perhaps the light gets slightly brighter between the first 5 button presses but it is so faint it is near impossible to tell.

I can't seem to figure out why the light wont turn off when sent a LOW signal.

To reiterate my intentions I would like the button to switch between several LED settings. While a prolonged press puts the tiny85 into sleep and turns off the light.

I would like to also power the tiny85 from sleep with a prolonged button press.

Please help me arduino forums, I've been at it for the past two days with little to no luck.

#include<avr/sleep.h>
#include<avr/interrupt.h>

const int switchPin=2;
const int statusLED=1;
int buttonState=LOW;
int lastButtonState=LOW;
int ledState=LOW;
int buttonPushCounter=0;
long millis_held=0;
long secs_held;
long prev_secs_held;
byte previous=HIGH;
unsigned long firstTime;
uint8_t debounceRead(int pin)
{
  uint8_t pinState=digitalRead(switchPin);
  uint32_t timeout=millis();
  while((millis()-timeout)<10)
  {
   if(digitalRead(switchPin)!=pinState)
    {
      pinState=digitalRead(switchPin);
      timeout=millis();
    }
  }//while
  return pinState;
}//uint8_t

void setup() {
  // put your setup code here, to run once:
  pinMode(switchPin,INPUT);
digitalWrite(switchPin,HIGH);
pinMode(statusLED,OUTPUT);
//Flash quick sequence so we know setup has started
for(int k=0;k<10;k=k+1){
  if(k%2==0){
    digitalWrite(statusLED,HIGH);
  }
  else{
    digitalWrite(statusLED,LOW);
  }
  delay(250);
}//for
}//setup

void sleep(){
  GIMSK|=_BV(PCIE);//enable pin change interrupts
  PCMSK|=_BV(PCINT2); //Use PB2 as interrupt pin
  ADCSRA&=~_BV(ADEN);//ADC off
  set_sleep_mode(SLEEP_MODE_PWR_DOWN);//replaces above statment

  sleep_enable();//sets the sleep enable bit in the MCUCR Register (SE BIT)
  sei();//enable interrupts
  sleep_cpu();//sleep

  cli();//disable interrupts
  PCMSK&=~_BV(PCINT2);//turn off PB2 as interrupt pin
  sleep_disable();//Clear SE bit
  ADCSRA|=_BV(ADEN);//ADC on

  sei();//enable interrupts
}//sleep

ISR(PCINT0_vect){
  //This is called when the interrupt occurs, but it is not mandatory to do anything with it
}
void loop() {
  sleep();
  buttonState=debounceRead(switchPin);
  buttonState=digitalRead(switchPin);
  if(buttonState==LOW&&previous==HIGH&&(millis()-firstTime)>200){
    firstTime=millis();
  }
  millis_held=(millis()-firstTime);
  secs_held=millis_held/1000;
  if(millis_held>50){
    if(buttonState==LOW&&secs_held>prev_secs_held){
      buttonState==LOW;
    }
  }
  if(buttonState!=lastButtonState){
    if(buttonState==HIGH){
      buttonPushCounter++;
      if(buttonPushCounter==7){
        buttonPushCounter=1;
      }
      }
    }
    lastButtonState=buttonState;
    switch(buttonPushCounter)
    {
      case 1:
      digitalWrite(statusLED,LOW);
      sleep();
      break;
      case 2:
      digitalWrite(statusLED,HIGH);
      break;
      case 3:
      analogWrite(statusLED,127);
      break;
      case 4:
      analogWrite(statusLED,85);
      break;
      analogWrite(statusLED,65);
      break;
      case 6:
      digitalWrite(statusLED,HIGH);
      delay(50);
      digitalWrite(statusLED,LOW);
      delay(50);
      break;
    }
    if(buttonPushCounter>0||buttonPushCounter<7&&millis_held>=1000){
      digitalWrite(statusLED,LOW);
      sleep();
    }
   }

As always much appreciated,

-Jesse

justliketheyusedto:
Making a second post after zero feedback on my last...

After a few hours? Don't do that again.

Threads merged.

My apologies for the urgency. This is one of two circuits I've been working on. That have been proving to be difficult. They've begun to get to me.

Again my apologies.
-Jesse

More editing of the code, found out why the LED would not turn off .... GND was disconnected :-[.

I removed the initial sleep from the void loop which seems to let the program run through the 6 switch statements, But I'm unsure how to tell if the ATtiny goes to sleep on case 0:

I've connected a multi meter to Vin and GND the voltage remains steady through each button press around 4.5V. Also I connected it to PWM1 where it fluctuates with the intensity of the light from 2.4mV to 3.7V. Leading me to believe either I'm connecting the multi meter leads in the wrong places, or that the tiny is not entering sleep.

The program seems to run case 1 on start up and the LED is HIGH instead of the default, or even case 0, why is that?

The default statement doesn't seem to work at all. This statement was an attempt at turning the ATtiny and LED off at any point in the program with a prolonged button press.

void loop() {
  buttonState=debounceRead(switchPin);
  buttonState=digitalRead(switchPin);
  if(buttonState==LOW&&previous==HIGH&&(millis()-firstTime)>200){
    firstTime=millis();
  }
  millis_held=(millis()-firstTime);
  secs_held=millis_held/1000;
  if(millis_held>50){
    if(buttonState==LOW&&secs_held>prev_secs_held){
      buttonState==LOW;
    }
  }
  if(buttonState!=lastButtonState){
    if(buttonState==HIGH){
      buttonPushCounter++;
      if(buttonPushCounter==6){
        buttonPushCounter=0;
      }
      }
    }
    lastButtonState=buttonState;
    switch(buttonPushCounter)
    {
    
      break;
      case 0:
      digitalWrite(statusLED,LOW);
      sleep();
      break;
      case 1:
      digitalWrite(statusLED,HIGH);
      break;
      case 2:
      analogWrite(statusLED,127);
      break;
      case 3:
      analogWrite(statusLED,85);
      break;
      case 4:
      analogWrite(statusLED,65);
      break;
      case 5:
      digitalWrite(statusLED,HIGH);
      delay(50);
      digitalWrite(statusLED,LOW);
      delay(50);
      break;
        default:
    if(buttonPushCounter>0||buttonPushCounter<6&&millis_held>=1000){
      digitalWrite(statusLED,LOW);
      sleep();
    }
    }
}

Getting closer!
Apologies again for my persistence.

You limited your 'buttonPushCounter' to the range 0 .. 5 so default (what it runs if the 'switch' is outside of all your cases) will never run.

if the value was outside 0 .. 5 then default would execute.

I'm not sure I follow I adjusted the parameters from 0..5 to 0..6 but now there is just an additional LOW button press after case 5.

I've also tried running default as case 6. It does the same double LOW setting.

   if(buttonState==HIGH){
      buttonPushCounter++;
      if(buttonPushCounter==6){
        buttonPushCounter=0;

buttonPushCounter can't be greater than 5 (at 6 you reset to 0) and it starts at 0 unless you preset it.

You were complaining that default never execute, right?
I am explaining how/why default never executes...

if(buttonState!=lastButtonState){
    if(buttonState==HIGH){
      buttonPushCounter++;
      if(buttonPushCounter==7){
        buttonPushCounter=0;
      }
      }
    }
    lastButtonState=buttonState;
    switch(buttonPushCounter)
    {
    
      break;
      case 0:
      digitalWrite(statusLED,LOW);
      sleep();
      break;
      case 1:
      digitalWrite(statusLED,HIGH);
      break;
      case 2:
      analogWrite(statusLED,127);
      break;
      case 3:
      analogWrite(statusLED,85);
      break;
      case 4:
      analogWrite(statusLED,65);
      break;
      case 5:
      digitalWrite(statusLED,HIGH);
      delay(50);
      digitalWrite(statusLED,LOW);
      delay(50);
      break;
       default:
    if(buttonPushCounter>0||buttonPushCounter<7&&millis_held>=1000){
      digitalWrite(statusLED,LOW);
      sleep();
      break;
    }
    }
}

But I receive the same outcome I've expanded the scope to accommodate for the default statement. Is that what you're suggesting?

Well, you also have your logic such that riding a button cannot timeout because you can only run the switch on buttonState change:

  if(buttonState!=lastButtonState){  // IF THE BUTTON STATE CHANGED
    if(buttonState==HIGH){
      buttonPushCounter++;
      if(buttonPushCounter==6){
        buttonPushCounter=0;
      }
      }
    }
    lastButtonState=buttonState;  //THIS PREVENTS RE-ENTRANCE IF THE BUTTON STATE DOESN'T CHANGE

So are you suggesting to write something like if lastButtonState was held for x amount of time execute LED LOW, and sleep?

In replace of lastButtonState=buttonState;

No, put the logic to put the thing to sleep outside the section of code that tests :

if (buttonState != lastButtonState )
  {
  .
  .
  }

you could put it before or after that block of code, just not inside it.

This is the code you need to move:

    if(buttonPushCounter>0||buttonPushCounter<7&&millis_held>=1000){
      digitalWrite(statusLED,LOW);
      sleep();

now, I did not check your code for correctness, just move it and debug from there.

Hey Dan95,

Thanks for the help really appreciate it. Moved that portion of code tried it both before and after the block of code suggested.

Now the LED remains off and when pressed the light flashes quickly while the light seems to vary in intensity (all of which is very dim) for 6 presses before restarting the loop.

Check the specs on your unit for what pins are capable of Pwm. Pin 2 on yours may not be correct. Analogwrite appears to output pwm.

Pin 2 is analog 1 you're correct but how would this affect the code?

It certainly wouldn't affect the code, only possibly the dim state of your LED.

By the way for analogWrite(pin, 255) would be full brightness.

In post #15 you said :
"Now the LED remains off and when pressed the light flashes quickly while the light seems to vary in intensity (all of which is very dim) for 6 presses before restarting the loop."

So, is that working correctly?

No far from it. I was advise to build this code bit by bit as most of this has been piece-meld together from various other codes I had written or read. I'm trying to get the logic down for the long button hold, before implementing the sleep and switch statements.

This is what I've got. Can I use pin from the debounceRead in this manner instead of switchPin?

What I have written at the moment is only in the initialization of the variables. That wouldn't work would it? Does the second while statement need to be in the void loop() with different variable names then those from uint_8 debounceRead()?

 const int pin=2;
int statusLED=LOW;
int buttonState=0;
int buttonStatus=LOW;
uint8_t debounceRead(int pin)
{
  uint8_t pinState=digitalRead(pin);
  uint32_t timeout=millis();
  while((millis()-timeout)<10)
  {
    if(digitalRead(pin)!=pinState)
    {
      pinState=digitalRead(pin);
      timeout=millis();
    }
  }
  while(pinState==HIGH){
    timeout++;
    delay(100);
    pinState=digitalRead(pin);
    if(timeout==10){
      statusLED=HIGH;
      break;
    } 
  }
  if(timeout<10){
    statusLED=LOW;
 }
  return pinState;
}

I believe my lack of understanding is showing. =[