Using multiple millis() instead of delays()

Hi everyone,

I am busy with a project that uses a button to change the behavior of a RGB LED. I have created several states that say what the led does:
State 1: led is off
State 2: led and vibration motor run a sequence
State 3: led is blue

I am quite new to this and I have used delays during the sequence. I figured that nothing will happen when you press the button during State 2 because of these delays and I have to change them to millis().

I want to change this so I have been doing a lot of research and studied the BlinkWithoutDelay. However, because I need multiple millis (for the vibration motor (which also has different times) and led), I cant figure out how to implement it correctly.

This is the code I have now (including delays):

#include <FastLED.h>

//connections of the ChainableLED
#define DATA_PIN 5
#define CLOCK_PIN 4
#define NUM_LEDS 1

// connections buzzer en button
int MoPin = 3;
const int button1Pin = 2;     // the number of the pushbutton pin
boolean b1LastTimePressed = false;

//an array to hold the led data
CRGB leds[NUM_LEDS];

enum program_state{
  // The state names are defined here. It helps to give them a descriptive name.
  STATE1,
  STATE2,
  STATE3,
};

program_state current_state = STATE1; // This lines declares the variable that will hold the value of the state and defines an initial value.

void setup()
{
  // This line tells the code to execute the function goToState. It is defined later in the code.
  goToState(STATE1); 
  
  // initialize the buzzer pin as an output:
  pinMode(MoPin, OUTPUT);
  // initialize the pushbutton pin as an input:
  pinMode(button1Pin, INPUT);
  FastLED.addLeds<P9813, DATA_PIN, CLOCK_PIN, RGB>(leds, NUM_LEDS);
}


void loop()
{

  switch(current_state){ // Depending on the value of current_state the switch will execute the corresponding section of code defined in the cases below. Each loop, the switch will check the value of current_state.

    case STATE1 :
     //turn LED off
     leds[0].setRGB( 0, 0, 0);
     FastLED.show();

    if(button1Pressed()){
      // Code that should run only once when moving to another state.
      goToState(STATE2); 
    }
    break;  // end STATE1

    case STATE2 :
  // Turn LED to green
    leds[0].setRGB( 0, 255, 0);
    FastLED.show();
    delay(5000);
   
   // Turn LED to orange
     leds[0].setRGB( 255, 50, 0);
     FastLED.show();
  
  //vibrate 2 times
    digitalWrite(MoPin, HIGH);
    delay(500);
    digitalWrite(MoPin, LOW);
    delay(500);
    digitalWrite(MoPin, HIGH);
    delay(500);
    digitalWrite(MoPin, LOW);
    delay(5000);
 
  //vibrate
    digitalWrite(MoPin,HIGH);
    delay(500);
    digitalWrite(MoPin,LOW);
    delay(5000);

  // turn LED to red
  leds[0].setRGB( 255, 0, 0);
  FastLED.show();

  //vibrate 3 times
    digitalWrite(MoPin,HIGH);
    delay(500);
    digitalWrite(MoPin,LOW);
    delay(500);
    digitalWrite(MoPin, HIGH);
    delay(500);
    digitalWrite(MoPin, LOW);
    delay(500);
    digitalWrite(MoPin, HIGH);
    delay(500);
    digitalWrite(MoPin, LOW);
    delay(5000);

    if(button1Pressed()){
      // Code to run once when moving to STATE3
      goToState(STATE3);
    }
    break;  // end STATE2

    case STATE3 :
     // turn LED to blue
     leds[0].setRGB( 0, 0, 255);
      FastLED.show();
    
    if(button1Pressed()){
      // Code to run once when moving to STATE2
      goToState(STATE2);
    }
    break;  // end STATE3
  }

}

// Function that checks if button1 is pressed while before not being pressed

boolean button1Pressed() {
  boolean b1FirstTimePressed = false;
  boolean b1Pressed = digitalRead(button1Pin);

  if (b1Pressed && !b1LastTimePressed) {
    b1FirstTimePressed = true;
  }

  b1LastTimePressed = b1Pressed;
  return b1FirstTimePressed;

}

void goToState(program_state state){ // The goToState function takes one variable, the desired STATE. It changes the state and prints the current state to the serial monitor
  current_state=state;
  Serial.print("going to state:");
  Serial.print('\t');
  Serial.println(current_state);
}

I hope it is clear what I mean and somebody can explain to me how you can use different millis() in my code.

Thanks!

CodeV3.ino (3.4 KB)

The demo Several Things at a Time is an extended example of BWoD and illustrates the use of millis() to manage timing without blocking. It may help with understanding the technique.

Have a look at Using millis() for timing. A beginners guide if you need more explanation.

...R

I hope it is clear what I mean and somebody can explain to me how you can use different millis() in my code.

Your program has a number states and is written as a state machine, which is good. You could use the same principle inside your states

Take STATE2 for example. It has several sub states each of which could be a case of its own using switch/case inside STATE2. The program will stay in STATE2 until all of the sub states have occurred.

Each of the delay()s can be implemented as a state of its own.
Take the period when the LED is green. Lets name it GREEN5000
Set the LED to green and save the start time. Next time through loop() the program will be still be in STATE2 and in substate GREEN5000. If 5 seconds has elapsed (millis() - start time) then turn the LED to orange, save the start time and move to substate VIBRATE. Keep going through substates until they are all done then move to the next major state

you could create a sub-function to toggle a specified pin. that function can either delay or keep track of time until an interval has passed before toggling the pin again.

since the same state will be executed each time thru loop(), it only needs to be called once for that state. Presumably it won't be called in other states.