Switch case not continuing to cycle as expected

I've got an interesting issue, where when a button is held for a short time the switch case progresses as expected.

However, if the button is continuously held instead of the switch case continuing to progress as expected and instead gets stuck on a single case.

Serial Monitor readout

Start Solid
Solid Mode =   1
Solid Mode =   1
Solid Mode =   1
Solid Mode =   1
Solid Mode =   1
Solid Mode =   1
Start Mode 2
Mode 2 =   1
Mode 2 =   1
Mode 2 =   1
Mode 2 =   1
Mode 2 =   1
Mode 2 =   1
Start Mode 3
Mode 3 =   1
Mode 3 =   1
Mode 3 =   1
Mode 3 =   1
Mode 3 =   1
Mode 3 =   1
Start Solid
Solid Mode =   1
Solid Mode =   1
Solid Mode =   1
Solid Mode =   1
Solid Mode =   1
Solid Mode =   1
Start Mode 2
Mode 2 =   1
Mode 2 =   1
Mode 2 =   1
Mode 2 =   1
Mode 2 =   1
Mode 2 =   1
Start Mode 3
Mode 3 =   1
Mode 3 =   1
Mode 3 =   1
Mode 3 =   1
Mode 3 =   1
Mode 3 =   1
Start Solid
Solid Mode =   1
Solid Mode =   1
Solid Mode =   1
Solid Mode =   1
Solid Mode =   1
Solid Mode =   1
Start Mode 2
Mode 2 =   1
Mode 2 =   1
Mode 2 =   1
Mode 2 =   1
Mode 2 =   1
Mode 2 =   1
Start Mode 3
Mode 3 =   1
Mode 3 =   1
Mode 3 =   1
Mode 3 =   1
Mode 3 =   1
Mode 3 =   1
Start Solid
Solid Mode =   1
Start Solid
Solid Mode =   1
Start Solid
Solid Mode =   1
Start Solid
Solid Mode =   1
Start Solid
Solid Mode =   1
Start Solid
Solid Mode =   1
Start Solid
Solid Mode =   1

#include "FastLED.h"

#define NUM_LEDS      110
#define LED_TYPE   WS2812B
#define COLOR_ORDER   GRB
#define DATA_PIN        12
#define VOLTS          5
#define MAX_MA       1325 //Dont exceed for stability reasons.

const int buttonPin = 4;

int SolidColourCount = 0;
int ColourMode = 0;
int CurrentButtonState;
int PreviousButtonState = LOW;
int ButtonState;


unsigned long CurrentMillis = 0;
unsigned long PreviousMillis = 0;
unsigned long HoldMillis = 0;


bool HomeMode = true;
bool SolidMode = false;
bool Mode2 = false;
bool Mode3 = false;

CRGBArray<NUM_LEDS> leds;


void setup() {
  FastLED.setMaxPowerInVoltsAndMilliamps( VOLTS, MAX_MA);
  FastLED.addLeds<LED_TYPE, DATA_PIN, COLOR_ORDER>(leds, NUM_LEDS).setCorrection(TypicalLEDStrip);
  Serial.begin(9600);
  pinMode(buttonPin, INPUT);
  leds = CRGB::Black;
      FastLED.show();

}

void loop() {
MainLoop();
}


void MainLoop() {
  HomeMode = true;
  
  while (HomeMode == true) {

    int CurrentMillis = millis();
    int  CurrentButtonState = digitalRead(buttonPin);

    if (CurrentButtonState == HIGH && (CurrentMillis - PreviousMillis >1000)) {
      ColourMode++;
      PreviousMillis = CurrentMillis;
    }

    if (ColourMode > 3) {
      ColourMode = 1;
    }



    switch (ColourMode) {
      case 1:
        PreviousMillis = CurrentMillis;
        Serial.println("Start Solid");
        HomeMode = false;
        SolidMode = true;
        SolidLightingMode();
        break;

      case 2:
        PreviousMillis = CurrentMillis;
        Serial.println("Start Mode 2");
        HomeMode = false;
        Mode2 = true;
        Mode2LightingMode();
        break;

      case 3:
        PreviousMillis = CurrentMillis;
        Serial.println("Start Mode 3");
        HomeMode = false;
        Mode3 = true;
        Mode3LightingMode();
        break;


    }
  }
}

void SolidLightingMode() {

  while (SolidMode == true) {
    CurrentMillis = millis();
    int  CurrentButtonState = digitalRead(buttonPin);


    Serial.print("Solid Mode =   ");
    Serial.print(SolidMode);
    Serial.println();


    leds = CRGB::Green;
    FastLED.show();
    delay(300);
    leds = CRGB::Blue;
    FastLED.show();
    delay(300);

    if (CurrentButtonState == LOW) {
      PreviousMillis = CurrentMillis;
    }

    if (CurrentButtonState == HIGH && (CurrentMillis - PreviousMillis > 3000)) {
      SolidMode = false;
      HomeMode = true;
      MainLoop();

    }
  }
}


void Mode2LightingMode() {


  while (Mode2 == true) {

    CurrentMillis = millis();
    int  CurrentButtonState = digitalRead(buttonPin);


    Serial.print("Mode 2 =   ");
    Serial.print(Mode2);
    Serial.println();



    leds = CRGB::Red;
    FastLED.show();
    delay(300);
    leds = CRGB::Blue;
    FastLED.show();
    delay(300);

    if (CurrentButtonState == LOW) {
      PreviousMillis = CurrentMillis;
    }

    if (CurrentButtonState == HIGH && (CurrentMillis - PreviousMillis > 3000)) {
      Mode2 = false;
      HomeMode = true;
      MainLoop();
    }
  }
}

void Mode3LightingMode() {

  while (Mode3 == true) {

    CurrentMillis = millis();
    int  CurrentButtonState = digitalRead(buttonPin);


    Serial.print("Mode 3 =   ");
    Serial.print(Mode3);
    Serial.println();



    leds = CRGB::Green;
    FastLED.show();
    delay(300);
    leds = CRGB::Red;
    FastLED.show();
    delay(300);

    if (CurrentButtonState == LOW) {
      PreviousMillis = CurrentMillis;
    }

    if (CurrentButtonState == HIGH && (CurrentMillis - PreviousMillis > 3000)) {
      Mode3 = false;
      HomeMode = true;
      MainLoop();
    }
  }
}

A10_Global.zip (2.9 KB)

There’s quite a bit of duplication & redundancy in the code…

Walk through the logic with your finger, and you might see some opportunities… like -
Solid mode, Mode 1,2,3 could all be squished into a single ‘mode’ variable…

Likewise within each mode in the switch(), you could pull all the common code rather than duplicate it both before and inside the switch statement.

It is overly simplistic for now at least.
I've purposefully set each lighting mode to be in it's own function and class on purpose.

I've removed some of the redundant code as you mentioned but, the issue still continues.

looks like the ColourMode is only advanced after the button is released

The point was to strip down and simplify the code so fault-finding would be easier.

Are the leds lighting properly? Setting leds = CRGB::color does not seem to be a proper way to set the strip color.

I checked this and but the ColourMode does increase even if the button is held, and not released.

Be careful calling mainloop from within the functions, recursive functions can quickly use up all the memory

It does all light up correctly, just one of the many ways available in FastLED

Actually, you might be onto something, if the memory is filled up this could explain it.

not sure how that is possible

should the pin be configured as INPUT_PULLUP?

Could be due to who I've wired the button, but I've got no answer on that sorry.

Absolutely right a simply reset function and that bug's squashed.

I don't see any unexpected behaviour. If the button is held down, colourMode increments and SolidLightingMode is called. It loops and prints "Solid Mode = 1" several times until the 3 second timeout expires and MainLoop is called (recursively) again.

The code is confusing though, especially having shadow copies of currentMillis and as noted above, will likely eventually run out of stack and crash.

I don't think so. You can't assign a CRGB::HTMLColorCode enum value (which is just an integer type) to an entire array:

#include "Arduino.h"
#include "FastLED.h"

CRGB leds[10];

void setup() {
  leds = CRGB::Green;
}

void loop() {
}
C:\Users\TR001221\AppData\Local\Temp\arduino_modified_sketch_248320\sketch_dec06a.ino: In function 'void setup()':
sketch_dec06a:7:16: error: incompatible types in assignment of 'CRGB::HTMLColorCode' to 'CRGB [10]'
   leds = CRGB::Green;
                ^~~~~
exit status 1
incompatible types in assignment of 'CRGB::HTMLColorCode' to 'CRGB [10]'

Just check out the Blink example in the FastLED library.

#include <FastLED.h>


#define NUM_LEDS 10
#define DATA_PIN 3


CRGB leds[NUM_LEDS];

void setup() { 
    FastLED.addLeds<WS2812B, DATA_PIN, RGB>(leds, NUM_LEDS);  
    
}

void loop() { 
  // Turn the LED on, then pause
  leds[0] = CRGB::Red;
  FastLED.show();
  delay(500);
  // Now turn the LED off, then pause
  leds[0] = CRGB::Black;
  FastLED.show();
  delay(500);
}

Well ..... it turns out this:

  leds[0] = CRGB::Red;

is not the same as this:

  leds = CRGB::Red;

You're trying to do the latter and it's invalid.

EDIT:
Now I see. Your original code is using the CRGBArray class which does overload the assignment operator.

The Blink example in the FastLED library uses an array of CRGB objects. Assignment to the entire array won't work for that case.

Here is a version that might work better. It uses only one "Mode" variable to keep track of the mode.

#include "FastLED.h"

#define NUM_LEDS      110
#define LED_TYPE   WS2812B
#define COLOR_ORDER   GRB
#define DATA_PIN      12
#define VOLTS          5
#define MAX_MA       1325 //Dont exceed for stability reasons.

const byte ButtonPin = 4;

enum {HOME, SOLID, MODE2, MODE3} Mode = HOME;

unsigned long PreviousButtonMillis = 0;

CRGBArray<NUM_LEDS> leds;

void setup()
{
  FastLED.setMaxPowerInVoltsAndMilliamps( VOLTS, MAX_MA);
  FastLED.addLeds<LED_TYPE, DATA_PIN, COLOR_ORDER>(leds, NUM_LEDS).setCorrection(TypicalLEDStrip);
  Serial.begin(9600);
  pinMode(ButtonPin, INPUT);
  leds = CRGB::Black;
  FastLED.show();
}

void loop()
{
  unsigned long currentMillis = millis();
  int  currentButtonState = digitalRead(ButtonPin);

  if (currentButtonState == LOW)  // Not pressed?
  {
    PreviousButtonMillis = currentMillis;
  }

  boolean switchState = (currentButtonState == HIGH && (currentMillis - PreviousButtonMillis > 3000));

  if (switchState)
  {
    PreviousButtonMillis = millis();
  }

  switch (Mode)
  {
    case HOME:
      // Do Nothing
      if (switchState)
      {
        Mode = SOLID;
        Serial.println("Switched to Mode = SOLID");
      }
      break;


    case SOLID:
      if (switchState)
      {
        Mode = MODE2;
        Serial.println("Switched to Mode = MODE2");
      }
      SolidLightingMode();
      break;

    case MODE2:
      if (switchState)
      {
        Mode = MODE3;
        Serial.println("Switched to Mode = MODE3");
      }
      Mode2LightingMode();
      break;

    case MODE3:
      if (switchState)
      {
        Mode = HOME;
        Serial.println("Switched to Mode = HOME");
      }
      Mode3LightingMode();
      break;
  }
}

void SolidLightingMode()
{
  // Alternating Green and Blue
  leds = CRGB::Green;
  FastLED.show();
  delay(300);
  leds = CRGB::Blue;
  FastLED.show();
  delay(300);
}


void Mode2LightingMode()
{
  // Alternating Red and Blue
  leds = CRGB::Red;
  FastLED.show();
  delay(300);
  leds = CRGB::Blue;
  FastLED.show();
  delay(300);
}

void Mode3LightingMode()
{
  // Alternating Green and Red
  leds = CRGB::Green;
  FastLED.show();
  delay(300);
  leds = CRGB::Red;
  FastLED.show();
  delay(300);
}

I'll admit that is a fair bit more elegant.