Turn off a led with a fade effect and timer

Hello, i’m a beginnner at arduino and i’m completely stuck at the moment with this problem. I hope someone can help.

What i’m trying to achieve :

  • When i press the switch button, the led turn on with a fade effect for 5 seconds and then turn off.
  • If i press again the same switch button in the range of 5 seconds, i want to instantly turn off the led.

Problem :
Currently, when i press again the same button, the led turn off and back on again for 5 seconds.

Hope someone could help :slight_smile:

#define led_pin 11
#define button_pin 2

int buttonState = 0;
int lastButtonState = 0;

volatile int ledState = 0;

unsigned long ledIsOn = 0;
unsigned long ledStart = 0;

int hasTimedOut = false;
unsigned long luminosity;

void setup()
{
  Serial.begin(115200);
  pinMode(led_pin,OUTPUT); // LED output
  pinMode(button_pin,INPUT_PULLUP); // Button input
}

void loop()
{
  buttonState = digitalRead(button_pin);

        if (!ledState && buttonState != lastButtonState) { // // Turn on if button pressed
            Serial.println("Button fired: turn on");
            ledStart = millis();
          }

    timeOn();
    timeOut();

    analogWrite(led_pin, ledState); // Update led state

    lastButtonState = buttonState;

}

/* TURN ON LED */

void timeOn() {
          ledIsOn = (millis()-ledStart);
          luminosity = map(ledIsOn, 0, 5000, 0, 255);
          ledState = luminosity;

        if (ledStart != 0 ) {
          //Serial.println(luminosity);
          Serial.println(ledState);
          delay(50);
        }

}

/* TURN OFF TIMER AFTER 10 SC */

void timeOut() {

  hasTimedOut = ledIsOn >= 5000;

  if (hasTimedOut) {
  ledState = 0;
  Serial.println(ledState);
  delay(50);
  }

  if (buttonState != lastButtonState && ledState) {
    ledState = 0;
    Serial.println(ledState);
    delay(50);
  }

}

Break your thinking into 3 stages. LEDOn, LEDFading, LEDOff. To help with this.. "enum" is your friend!

enum states { LEDOn, LEDFading, LEDOff };

states ourState = LEDOff;

Here is an example..

enum states { LEDOn, LEDFading, LEDOff };  // The possible things we can do doing.

states ourState;  // We save our current state here. 

// In setup we set our initial state.
void setup(void) {

   //.. typical setup things then..
   
   ourState = LEDOff;   // We start with the LED off.. note it.
}

// In loop we FIRST look at our state then from there we know what we should be doing.
void loop(void) {

   switch (ourState) {
      case LEDOff    :  doStuffWeDoWhenItsOff();      break;
      case LEDOn     :  doStuffWeDoWhenItsOn();       break;
      case LEDFading :  doStuffWeDoWhenItsFading();   break;
   }
}

This splits up the problem. You can use your button prresses to change the state and keep things from getting mixed up.

Hope this helps..

-jim lee

Check out the FadeLed library in the IDE library manager.

jimLee is right, you have to make the 'state' the most important variable of the sketch.

In the loop(), first do the State Change Detection without the led state. I use a 'bool' variable that I make 'true' when a button was pressed.

I think you have only two states: "FADING" and "OFF", each have their own block of code.

Please press Ctrl+T in the Arduino IDE, and make the text layout look good. Put every space, every comma, every new line, every indent in the right place. Use one format for the curly brackets.

Give the variables better names.
The name 'ledState' is used for the brightness. Put 'brightness' or 'luminosity' or 'pwm' in the name.
The name 'ledIsOn' is used for the timing. Put 'time' or 'elapsed time' or 'millis' in the name.
You can remove the 'volatile' keyword, that is only used in certain situations when interrupts are used.

The pin numbers are preferred as this (instead of the #define):

const int led_pin = 11;
const int button_pin = 2;

Hello ! Thanks Koepel and jim ! I was able to achieve my goal as you can see with the code below even if i wasn't able to use the switch case :confused: I'm a beginner and even after checking the doc, i wasn't confortable enough to work with it.

@Koepel : Thanks a lot for the tips : Cmd +T and State change detection, also for the tips about naming (you can see in my code below that i have changed the name)

@jim i have some questions :

  1. i had so trouble to understand the way you use enum :
enum states { LEDOn, LEDFading, LEDOff };  // The possible things we can do doing.[color=#222222][/color]
states ourState;  // We save our current state here

I've check some documentation but can you confirm what is written above is the same as below because i wasn't able to confirm this.

int states;
int LEDOn;
int LEDFading;
int LEDOff;[color=#222222][/color]
states = ourState;
  1. Is that the intended way to use the ourState variable with switch
   switch (ourState) {
   case LEDOff:
   digitalWrite(led_pin, LOW);
   break;

   case LEDFading :  
    digitalWrite(led_pin, HIGH); // its not fading but just test it with a simple light on
   break;
   }
  1. Should i change the value of the ourState var when the button is pressed ?
    For example :
  • if counter button = 1, then ourState = LEDOff;
  • if counter button = 2, then ourState = LEDFading;

Thanks a lot for your help

int states;
int LEDOn;
int LEDFading;
int LEDOff;[color=#222222][/color]
states = ourState;

No, this way they all end up with the same value, 0. Also you mixed up the variable ourState and the type, states.

You want:

int ourState;
const int LEDOn = 0;
const int LEDFading = 1;
const int LEDOff = 2;
ourState = LEDOff;

but the enum is better because it's type safe.

int anInt; // This can hold integers. Typically -30k something to +30k something.

states ourState; // This can only hold.. LEDOn, LEDFading, LEDOff.

Why? Because that was the list I put into the emum. Its handy because

A) Your list can have names that mean something.

and..

B) The compiler knows about it, so you can't "cheat" and tell it to store something that's NOT on the list. Things like switch(ourState) statements get all fussy if you don't give cases for the entire set. And, they just won't compile of you try to put in something that's NOT on the list.

So enums are VERY popular for breaking up your code into different states.

Make sense?

-jim lee

Changing states..

Sometimes its trivial to change a state sometimes it gets complcated. Lets pretend your is complex. (They all start out that way. Then you get used to it.)

When something happens and you need to change states, you can go into a routine that is basically"I'm in state A and need to change to state B".

In your case the change is that a button was hit. Ok..

switch (ourState) {
      case LEDOff    :
         ...
         steps you take if its in LEDOff state and the button was hit.
         ourState = whatever state happens when the button was hit in this state.
      break;
      case LEDOn     :
         ...
         steps you take if its in LEDOn state and the button was hit.
         ourState = whatever state happens when the button was hit in this state.
      break;
      case LEDFading :
         ...
         steps you take if its in LEDFading state and the button was hit.
         ourState = whatever state happens when the button was hit in this state.
      break;
   }

This is different than the switch (ourState) in your loop() that one is used for "I have some time, I'm doing X, what needs doing right now?".

The switch(ourState) above is where you "shift gears" from state A to state B.

Does that make sense?

-jim lee

Thanks for the explanation @jill ! Its clearer now even if i'll need some time to really digest your explanations i guess :slight_smile:

Here the code with a simple led on (instead of fading) for now because i try to keep thing simple before moving forward. Is it a right way to use switch or can i improve something ?

Thanks

const int  buttonPin = 2;    // the pin that the pushbutton is attached to
const int ledPin = 11;       // the pin that the LED is attached to

// Variables will change:
int buttonPushCounter = 0;   // counter for the number of button presses
int buttonState = 1;         // current state of the button
int lastButtonState = 1;     // previous state of the button

enum states { LEDFading, LEDOff };  // The possible things we can do doing.[color=#222222][/color]
states ourState;  // We save our current state here


void setup() {

  // initialize the button pin as a input:
  pinMode(buttonPin, INPUT_PULLUP);
  // initialize the LED as an output:
  pinMode(ledPin, OUTPUT);
  // initialize serial communication:
  Serial.begin(9600);
  //ourState = LEDOff;   // We start with the LED off.. note it.
}

void loop() {

  // read the pushbutton input pin:
  buttonState = digitalRead(buttonPin);

  if (buttonState != lastButtonState) {
    // if the state has changed, increment the counter
    if (buttonState == LOW) {
      // if the current state is HIGH then the button went from off to on:
      buttonPushCounter++;
      Serial.println("on");
      //Serial.print("number of button pushes: ");
      // Serial.println(buttonPushCounter);
    } else {
      // if the current state is LOW then the button went from on to off:
      Serial.println("off");
    }
    // Delay a little bit to avoid bouncing
    delay(50);
  }
  // save the current state as the last state, for next time through the loop
  lastButtonState = buttonState;

  switch (ourState) {
    case LEDOff    :
      if (buttonPushCounter % 2 == 0 && buttonState==0) {
        ourState = LEDFading;
        Serial.println("I'm in LEDOff");
        break;
      }
    case LEDFading :
      if (buttonPushCounter % 2 == 0 && buttonState==0) {
        ourState = LEDOff;
        Serial.println("I'm in LEDFading");
        break;
      }
  }
  digitalWrite(ledPin, ourState);
  Serial.println(ourState);
  delay(200);
}

You kinda need 2 switch statements. One in loop() that lets you decide what to do given the state you are in, And the other being called when the switch gets clicked to let to decide how to change states.

I'd write a switchCheck() function that can contain all the logic of watching your button etc. Then, if clicked, do the state change action. You can call it from loop(). This will get a lot of the complexity out of your loop() and should make the code easier to do.

-jim lee

@jim ! Sorry for late answer, here is the new code with your previous recommandation. I think its cleaner, what do you think ? Next step for me will be to make the lade fade and add the timer :slight_smile:

const int  buttonPin = 2;    // the pin that the pushbutton is attached to
const int ledPin = 11;       // the pin that the LED is attached to

// Variables will change:
int buttonPushCounter = 0;   // counter for the number of button presses
int buttonState = 1;         // current state of the button
int lastButtonState = 1;     // previous state of the button

enum states { LEDFading, LEDOff };
states ourState;  // We save our current state here


void setup() {

  // initialize the button pin as a input:
  pinMode(buttonPin, INPUT_PULLUP);
  // initialize the LED as an output:
  pinMode(ledPin, OUTPUT);
  // initialize serial communication:
  Serial.begin(9600);
  //ourState = LEDOff;   // We start with the LED off.. note it.
}

void loop() {

  // read the pushbutton input pin:
  buttonState = digitalRead(buttonPin);

  if (buttonState != lastButtonState) {
    // if the state has changed, increment the counter
    if (buttonState == LOW) {
      buttonPushCounter++;
      Serial.println(buttonPushCounter);
      //Serial.println("Appuyer");
    } else {
      //Serial.println("Relacher");
    }
    // Delay a little bit to avoid bouncing
    delay(50);
  }
  // save the current state as the last state, for next time through the loop
  lastButtonState = buttonState;


  switch (buttonPushCounter % 2) {
    case 0:
      Serial.println("i'm in case 0");
      myLedFading ();
      delay(50);
      break;
    default:
      // if nothing else matches, do the default
      // default is optional
      Serial.println("i'm in case default");
      myLEDTurnOff();
      delay(50);
      break;
  }
digitalWrite(ledPin, ourState);
delay(50);

}


void myLedFading () {
  // This function WILL make the LED Fade
  ourState = LEDFading;
}

void myLEDTurnOff() {
  // This function turn off the LED
  ourState = LEDOff;
}
  buttonState = digitalRead(buttonPin);

  if (buttonState != lastButtonState) {
    // if the state has changed, increment the counter
    if (buttonState == LOW) {
      buttonPushCounter++;
...
    delay(50);
  }
  // save the current state as the last state, for next time through the loop
  lastButtonState = buttonState;

I believe this is wrong. This code means, the last line ensures that lastButtonState is always compared in the ‘if’ statement, with the last value of the input pin, regardless of whether it was determined to be valid or not. If the ‘if’ statement is found false, the delay(50) is skipped and so that determination is not debounced. You should only store a ‘lastButtonState’ that has passed the checks for a valid state change, like this:

  buttonState = digitalRead(buttonPin);

  if (buttonState != lastButtonState) {
    // save the current state as the last state, for next time through the loop
    lastButtonState = buttonState;
    // if the state has changed, increment the counter
    if (buttonState == LOW) {
      buttonPushCounter++;
...
    // Delay a little bit to avoid bouncing
    delay(50);
  }

Also, to be really “clean” to use your word, you should eliminate the delay() and use millis() for the debounce interval.

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