switch statement with timeout to next case

dear community,

I´d like to have a timeout in a switch-statment from case to case, but want to have the button-press coincidentially.

the setup would be a couple of cases doing some light-effects, on button press, the next case is called.
up to this I´m fine with an interrupt.
bute I´d like to have the arduino to switch to the next case after e.g. 2 minutes or so...

do you have an idea how to solve this? I desperately searched all forums on the internet, but could only find either switch case button OR by time....

Thank you and cheers,
Cisco

How easy it is depends a lot on the values being tested for in each of your case statements. If your various case tests are against consecutive integer values - case 1:, case 2: case 3: etc, then external to the switch you could monitor time and if 2 minutes have passed, simply increment the value that the switch is using to move to the next case.

Perhaps you can post the code you have so we can assist further.

I am not sure that I understand the problem.

Staying in a case until a time period has elapsed is simple but there is no reason why a case cannot contain code to test for button input too

case something:
  read input
  if button has become pressed
    change case
  end if

  if time period has elapsed
    change case
  end if
  break

The case will change if the button becomes pressed or the time period elapses. That sounds like it is what you want

You could do this:

case something:
  read starttime;
  while ( (now - starttime) < 2 minutes )
  {
    if (buttonpressed()) { break; }
  }
case nextsomething:
  ...
  ...

After the time limit or a button-press it’ll drop out of the while{} loop and fall through to nextsomething. There’s more than one way to skin a cat …

Case statement fall-through is a somewhat discouraged practice. In fact, you should see compiler warnings about it (if not turn up your warning level). When I do use it, I'll put comments in the code stating that the fall-through is intentional.

gfvalvo:
Case statement fall-through is a somewhat discouraged practice. ...

You're right, of course. I considered it, but it's usually ok (famous last words!). I guess I got spoilt by writing a lot of a mixture of C and assembler, cross-complied to run on a Z80 system. I always checked the assembler which the compiler produced, to see if anything was amiss or if there was a faster way of doing whatever - it was a very time-critical system. I got used to the computed gotos.

Could just change the state variable to next case and break after the while, to loop back and have another go (which is effectively what I did on another, VB.NET, system).

case something:
  read starttime;
  while ( (now - starttime) < 2 minutes )
  {
    if (buttonpressed()) { break; }
  }
case nextsomething:
  ...
  ...

Just say no to that. It is blocking code

Instead save the start time before entering the timing case then in the timing case test whether the time period has elapsed or not. DO NOT block the code.

gfvalvo:
Case statement fall-through is a somewhat discouraged practice. In fact, you should see compiler warnings about it (if not turn up your warning level). When I do use it, I'll put comments in the code stating that the fall-through is intentional.

I have recently discovered [[fallthrough]] and it works as advertised in IDE version 1.8.13.

You didn’t show any code so it is hard to be specific. How do you normally get from case to case? I’ll assume your cases are numbered. If you have names assigned to the numbers, just use the names instead.

I’m going to call the state variable “State”.

  static unsigned long State18StartTime = 0;

  switch (State)
  {
  case 17:  // Start a timer for State 18
    State18StartTime = millis()
    State = 18;
    break;

  case 18:  // Stay here until a button press or timeout
    if (buttonIsPressed) // Set up at the lop of loop()
      State = 23;  
    if (millis() - State18StartTime >= State18TimeInterval)
      State = 19;
    break;

  case 19:  // State 18 timed out
    // Do stuff
    beak;
.
.
.
  }

cisco7975:
I´d like to have a timeout in a switch-statment from case to case

I did something like that in an art project.
Post#274 here.
The "sequence" value keeps track of the case, and millis timing (and more) is done inside the case.
Leo..

UKHeliBob:
Just say no to that. It is blocking code

Guilty as charged. Don't know why I did that. Must be more careful/thoughtful before I jump in with both feet.

Thanks to all, sorry for not posting the code, I´ll give it a try here:

[code]
#include "FastLED.h"
#define NUM_LEDS 288 
CRGB leds[NUM_LEDS];
#define PIN 7
#define Brightness1 5 
#define Brightness2 2

volatile int buttonPushCounter = 0;     // counter for the number of button presses
int buttonState = 0;                  // current state of the button
int lastButtonState = 0;                // previous state of the button  
int nrProgs = 3;
const int buttonPin = 2;                // the pin that the pushbutton is attached to
volatile bool buttonPushed = false;

void setup()
{
  FastLED.addLeds<WS2811, PIN, GRB>(leds, NUM_LEDS).setCorrection( TypicalLEDStrip );
  FastLED.setBrightness(Brightness2);
  pinMode(buttonPin, INPUT_PULLUP);
  allOff();
  attachInterrupt(digitalPinToInterrupt(buttonPin), ISR_button, RISING);
  Serial.begin(9600);
  Serial.println("Testing Strobe-Routines; Press Button To Start");
}

void loop(){ 
  if (buttonPushCounter > nrProgs){
    buttonPushCounter = 0;}
    Serial.println(buttonPushCounter);
    
  switch (buttonPushCounter) {
    case 1:
      Strobe1(0xff, 0xff, 0xff, 4, 25, 25);
      Serial.println("executing Routine 1");
      break;
    case 2:
      Strobe2(0xff, 0xff, 0xff, 4, 25, 25);
      Serial.println("executing Routine 2");
      break;
    case 3:
      Strobe3(0xff, 0xff, 0xff, 4, 25, 25);
      Serial.println("executing Routine 3");
      break;
    default:
      setAll(0,0,0);
      showStrip();
      Serial.println("Testing Strobe-Routines; Press Button To Start");
      break;
  }
}
  
void ISR_button(){
 Serial.println("Button Push Detected, switching to next routine"); // ONLY FOR DEBUGGING
 static unsigned long last_interrupt_time = 0;
 unsigned long interrupt_time = millis();
 if (interrupt_time - last_interrupt_time > 200)    // If interrupts come faster than 200ms, assume it's a bounce and ignore
 {
  buttonPushCounter++;
 }
 last_interrupt_time = interrupt_time;

}

void Strobe1(byte red, byte green, byte blue, int StrobeCount, int FlashDelay, int EndPause){
   for(int j = 0; j < StrobeCount; j++) {
    setRight(255,0,0);
    showStrip();
    delay(FlashDelay);
    setAll(0,0,0);
    showStrip();
    delay(FlashDelay); }
    
  for(int j = 0; j < StrobeCount; j++) {
    setLeft(0,0,255);
    showStrip();
    delay(FlashDelay);
    setAll(0,0,0);
    showStrip();
    delay(FlashDelay);}
  }
  
void Strobe2(byte red, byte green, byte blue, int StrobeCount, int FlashDelay, int EndPause){
   for(int j = 0; j < StrobeCount; j++) {
    setNearRight(255,0,0);
    setFarLeft(0,0,255);
    showStrip();
    delay(FlashDelay);
    setAll(0,0,0);
    showStrip();
    delay(FlashDelay);}

  for(int j = 0; j < StrobeCount; j++) {
    setNearLeft(0,0,255);
    setFarRight(255,0,0);
    showStrip();
    delay(FlashDelay);
    setAll(0,0,0);
    showStrip();
    delay(FlashDelay);}
  }
  
void Strobe3(byte red, byte green, byte blue, int StrobeCount, int FlashDelay, int EndPause){
setFarLeft(255,255,255);
setFarRight(255,255,255);
   for(int j = 0; j < StrobeCount; j++) {
    setNearLeft(255,0,0);
    showStrip();
    delay(FlashDelay);
    setNearLeft(0,0,0);
    showStrip();
    delay(FlashDelay);}

  for(int j = 0; j < StrobeCount; j++) {
    setNearRight(0,0,255);
    showStrip();
    delay(FlashDelay);
    setNearRight(0,0,0);
    showStrip();
    delay(FlashDelay);}
  }

void colorWipe(byte red, byte green, byte blue, int SpeedDelay) {
  for(uint16_t i=143, k=144; i>0, k<288; i--, k++) {
      setPixel(i, red, green, blue);
      setPixel(k, red, green, blue);
      showStripLow();
  }
}
void showStrip() {
 #ifdef ADAFRUIT_NEOPIXEL_H 
   // NeoPixel
   strip.show();
 #endif
 #ifndef ADAFRUIT_NEOPIXEL_H
   // FastLED
   FastLED.show(Brightness1);
 #endif
}

void showStripLow() {
 #ifdef ADAFRUIT_NEOPIXEL_H 
   // NeoPixel
   strip.show();
 #endif
 #ifndef ADAFRUIT_NEOPIXEL_H
   // FastLED
   FastLED.show(Brightness2);
 #endif
}

void setPixel(int Pixel, byte red, byte green, byte blue) {
 #ifdef ADAFRUIT_NEOPIXEL_H 
   // NeoPixel
   strip.setPixelColor(Pixel, strip.Color(red, green, blue));
 #endif
 #ifndef ADAFRUIT_NEOPIXEL_H 
   // FastLED
   leds[Pixel].r = red;
   leds[Pixel].g = green;
   leds[Pixel].b = blue;
 #endif
}

void setAll(byte red, byte green, byte blue) {
  for(int i = 0; i < NUM_LEDS; i++ ) {
    setPixel(i, red, green, blue); 
  }
  showStrip();
}

void setRight(byte red, byte green, byte blue) {
  for(int i = NUM_LEDS/2; i < NUM_LEDS; i++ ) {
    setPixel(i, red, green, blue); 
  }
  showStrip();
}

void setLeft(byte red, byte green, byte blue) {
  for(int i = 0; i < NUM_LEDS/2; i++ ) {
    setPixel(i, red, green, blue); 
  }
  showStrip();
}

void setCenter(byte red, byte green, byte blue) {
  for(int i = NUM_LEDS/4; i < (NUM_LEDS/2)+(NUM_LEDS/4); i++ ) {
    setPixel(i, red, green, blue); 
  }
  showStrip();
}

void setNearRight(byte red, byte green, byte blue) {
  for(int i = NUM_LEDS/2; i < (NUM_LEDS/2)+(NUM_LEDS/4); i++ ) {
    setPixel(i, red, green, blue); 
  }
  showStrip();
}

void setNearLeft(byte red, byte green, byte blue) {
  for(int i = NUM_LEDS/4; i < NUM_LEDS/2; i++ ) {
    setPixel(i, red, green, blue); 
  }
  showStrip();
}

void setFarRight(byte red, byte green, byte blue) {
  for(int i = (NUM_LEDS/2)+(NUM_LEDS/4); i < NUM_LEDS; i++ ) {
    setPixel(i, red, green, blue); 
  }
  showStrip();
}

void setFarLeft(byte red, byte green, byte blue) {
  for(int i = 0; i < NUM_LEDS/4; i++ ) {
    setPixel(i, red, green, blue); 
  }
  showStrip();
}

void allOff() {
  setAll(0,0,0);
  showStrip();
}

[/code]

Hope it went well… I think I read somewhere to post the complete code, so here it is:-) ummm, it requires explanation:
This is just some testing with some LED stripes, police strobe patterns, the real thing would be some christmas decorations, but the setup will be the same.

thank You again and cheeres,
Cisco

Your code uses delay which is blocking. As long as a delay is executed nothing else can be done.
So your code must be changed to non-blocking timing based on the function millis()

imagine baking a frosted pizza
the cover says for preparation heat up oven to 200°C
then put pizza in baking time 10 minutes

You are estimating heating up needs 3 minutes
You take a look onto your watch it is 13:02
You start reading the newspaper and from time to time looking onto your watch
watch 13:02 not yet time
watch 13:03 not yet time
watch 13:04 not yet time
watch 13:05 when did I start 13:02 OK 13:05 - 13:02 = 3 minutes time to put pizza into the oven

New basetime 13:05
watch 13:06 not yet time
watch 13:07 not yet time
watch 13:08 not yet time
watch 13:09 not yet time
watch 13:10 not yet time
watch 13:11 not yet time
watch 13:12 not yet time
watch 13:13 not yet time
watch 13:14 not yet time
watch 13:15 when did I start 13:05 OK 13:15 - 13:05 = 10 minutes time to eat pizza (yum yum)

You did a repeated comparing how much time has passed by
This is what non-blocking timing does

This is what the if-condition
if (currentTime - previousTime >= Period)
is doing

this means a fundamental re-design of your existing code.
each for-loop must be replaced by a little statemachine that executes the code inside the (former) for-loops

there will only one big loop where

void loop() {
  DoJustOneStepOfFlashpattern();
  CheckForButtonPress();  
}

is done

the other approach would be to use an interrupt-service-routine (in short ISR) that sets a flag-variable and each
for-loop gets an condition if BreakFlagSet == true break

this would mean that stop execution can only be done after each delay
As long as the delay endures the µC just "delays"

best regards Stefan