Using Switch (mode) to switch functions

Hey All! I am currently working on a project using the arduino to run a high powered LED light that I built. The system uses a button and a potentiometer to read in information and change modes/speeds etc. So far, all of the different "modes" have been successfully written and work. (RGB Fades, Blinky modes, strobe modes, etc) However I've run into some trouble when it comes to switching modes. Attached is the main loop of my code where I use a debounced button and edge detection to advance the mode and then a simple switch mode machine to switch between modes. It all works okay, but you seem to have to hit the button at the perfect time to get it to change modes or, it will read the button press but not change modes for a second or two. Does anyone have any advice?

void loop() {
  val = digitalRead(btn);
  delay(10);  //Wait to make sure button press is good
  val2 = digitalRead(btn);
  
  if (val == val2){  //debounce check
    if (val != butState && val == HIGH){  //If we see a change in the button state,
      mode++;                             //we increment the mode value by 1
    }
  }
  butState = val; //update the most recent button state
  
  if (modeState != mode){
    switch (mode) {
      case 2:            //Start with Case 2 because Case 1 is considered the default 
      ColorBreath("r");  //"start-up" mode.
      break;
      case 3:
      ColorBreath("g");
      break;
      case 4:
      ColorBreath("b");
      break;
      case 5:
      RGBAllFade();
      break;
      default:
      mode = 1;
      ColorSelect();
      break;
    }
  }
}

I should also note that each function, when run independently of the switching program, results in smooth dimming of the LEDs. However, when run through the state machine, the dimming becomes choppy and erratic.

butState = val; //update the most recent button state

If you name the var buttonState you don't need to explain what butState is.

modeState is nowhere set or???

Please, can you post the complete code , because the choppy-ness might be created somewhere else.

Yeah, so that modeState variable was a line that I left out. here is the updated code. However, now the function under the mode is running once and then not doing anything, but the button works perfectly to switch modes! Any clues?

void loop() {
  val = digitalRead(btn);
  delay(10);  //Wait to make sure button press is good
  val2 = digitalRead(btn);
  
  if (val == val2){  //debounce check
    if (val != butState && val == HIGH){  //If we see a change in the button state,
      mode++;                             //we increment the mode value by 1
    }
  }
  butState = val; //update the most recent button state
  
  if (modeState != mode){
    switch (mode) {
      case 2:            //Start with Case 2 because Case 1 is considered the default 
      ColorBreath("r");  //"start-up" mode.
      break;
      case 3:
      ColorBreath("g");
      break;
      case 4:
      ColorBreath("b");
      break;
      case 5:
      RGBAllFade();
      break;
      default:
      mode = 1;
      ColorSelect();
      break;
    }
  }
  modeState = mode;
}

but the button works perfectly to switch modes!

How do you know?

Can you post the complete sketch ?

msmith419:
It all works okay, but you seem to have to hit the button at the perfect time to get it to change modes or, it will read the button press but not change modes for a second or two. Does anyone have any advice?

It sounds like your function are using delays, but that is just a guess as you seem to not want to post all of your code. If that is the case, you'll have to take a look at the Blink Without Delay example and restructure your functions to run non-blocking code with the concepts demonstrated in the aforementioned example.

I don't mind posting my entire code, it's just really long an not exactly well commented. I would like to solve the switch problem first before I solve the dimming problem. Here it is though. (Sorry, I posted the wrong code initially, here is the right code)

/******************
*  High Power LED *
*  13 April 2013  *
*  Mitchell Smith *
*******************/

//===============================================================================
// GLOBAL VARIABLES
//===============================================================================

//LED Pins
int LedR = 11, LedG = 10, LedB = 9;

//Analog Button and Potentiometer Pins
int btn = 4;
int pot = A0;

//Set LED attributes
int RBright = 0, GBright = 0, BBright = 0; //initial brightness
int Rmax = 240, Gmax = 255, Bmax = 255; //maximum brightness (necessary on R LED)

//Set fade attributes
int fadeAmount = 1; 
int fadeUpSp = 1, fadeDnSp = 1;
int chaseSpeed = 100;

//button state variables
int butState = 0; //Last Button State
int val = 0, val2 = 0; // Button Pin HIGH/LOW Status (twice for debounce)

//Switch Mode Variables
int mode = 0; //Selector State (Initial state = everything off)
int modeState = 0; //Last Mode State

//===============================================================================
// Set-Up
//===============================================================================
void setup() {
  pinMode(btn, INPUT);
  pinMode(pot, INPUT);
  pinMode(LedR, OUTPUT);
  pinMode(LedG, OUTPUT);
  pinMode(LedB, OUTPUT);
  //butState = digitalRead(btn);
  Serial.begin(9600);
}

//===============================================================================
// PROGRAM LOOP
//===============================================================================

void loop() {
  val = digitalRead(btn);
  delay(10);  //Wait to make sure button press is good
  val2 = digitalRead(btn);
  
  if (val == val2){  //debounce check
    if (val != butState && val == HIGH){  //If we see a change in the button state,
      mode++;                             //we increment the mode value by 1
    }
  }
  butState = val; //update the most recent button state
  
  if (modeState != mode){
    switch (mode) {
      case 2:            //Start with Case 2 because Case 1 is considered the default 
      ColorBreath("r");  //"start-up" mode.
      break;
      case 3:
      ColorBreath("g");
      break;
      case 4:
      ColorBreath("b");
      break;
      case 5:
      RGBAllFade();
      break;
      default:
      mode = 1;
      ColorSelect();
      break;
    }
  }
  else {
    switch (modeState) {
      case 2:            //Start with Case 2 because Case 1 is considered the default 
      ColorBreath("r");  //"start-up" mode.
      break;
      case 3:
      ColorBreath("g");
      break;
      case 4:
      ColorBreath("b");
      break;
      case 5:
      RGBAllFade();
      break;
      default:
      mode = 1;
      ColorSelect();
      break;
    }
  }
  modeState = mode;
}

//===============================================================================
// Color Funcitons
//===============================================================================

void ColorSelect(){
  analogWrite(LedR, rWheel(getColor()));
  analogWrite(LedG, gWheel(getColor()));
  analogWrite(LedB, bWheel(getColor()));
}

void ColorBreath(String color){
  if (color == "r"){
    RFadeUp();
    delay(getChase());
    RFadeDown();
    delay(getChase());
  }
  else if (color == "g"){
    GFadeUp();
    delay(getChase());
    GFadeDown();
    delay(getChase());
  }
  else if (color == "b"){
    BFadeUp();
    delay(getChase());
    BFadeDown();
    delay(getChase());
  }
}

void RGBAllFade() {
  RFadeUp();
  BFadeDown();
  delay(getChase());
  GFadeUp();
  RFadeDown();
  delay(getChase());
  BFadeUp();
  GFadeDown();
  delay(getChase());
}



//===============================================================================
// Utility Functions
//===============================================================================

double getFade() {
  double fdVal = analogRead(pot);
  double fadeSp = map(fdVal, 0, 1023, 1, 50);
  return fadeSp;
}

double getChase(){
  double chVal = analogRead(pot);
  double chaseSp = map(chVal, 0, 1023, 0, 1000);
  return chaseSp;
}

int getColor(){
  int colVal = analogRead(pot);
  int col = map(colVal, 0, 1023, 0, 383);
  return col;
}
  
//Color Wheel Functions
double rWheel(int WheelPos){
  int r;
  switch(WheelPos/128){
    case 0:
      r = 127 - WheelPos % 128;
      break;
    case 1:
      r = 0;
      break;
    case 2:
      r = WheelPos % 128;
      break;
  }
  return r;
}
double gWheel(int WheelPos){
  int g;
  switch(WheelPos/128){
    case 0:
      g = WheelPos % 128;
      break;
    case 1:
      g = 127 - WheelPos % 128;
      break;
    case 2:
      g = 0;
      break;
  }
  return g;
}

double bWheel(int WheelPos){
  int b;
  switch(WheelPos/128){
    case 0:
      b = 0;
      break;
    case 1:
      b = WheelPos % 128;
      break;
    case 2:
      b = 127 - WheelPos % 128;
      break;
  }
  return b;
}
//Fade Up Commands
void RFadeUp() {
   while(RBright < Rmax) {
    analogWrite(LedR, RBright);
    RBright = RBright + fadeAmount;
    delay(getFade());
  }
}
void GFadeUp() {
  while(GBright < Gmax) {
    analogWrite(LedG, GBright);
    GBright = GBright + fadeAmount;
    delay(getFade());
  }
}
void BFadeUp() {
  while(BBright < Bmax) {
    analogWrite(LedB, BBright);
    BBright = BBright + fadeAmount;
    delay(getFade());
  }
}

//Fade Down Commands
void RFadeDown() {
  while(RBright > 0) {
    analogWrite(LedR, RBright);
    RBright = RBright - fadeAmount;
    delay(getFade());
  }
}
void GFadeDown() {
  while(GBright > 0) {
    analogWrite(LedG, GBright);
    GBright = GBright - fadeAmount;
    delay(getFade());
  }
}
void BFadeDown() {
  while(BBright > 0) {
    analogWrite(LedB, BBright);
    BBright = BBright - fadeAmount;
    delay(getFade());
  }
}
  delay(getChase());

Could those be your problem?

why would they be my problem. All they do is read an analogue value from the POT pin and map it. Sorry that seems mean. Is there a better way to do that? I want the dimming curves to be fully adjustable during the cycle, not just at the beginning and end. Also, when the functions are run independently as the main loop, the dimming is smooth and the delay doesn't cause problems.

While in a delay you can't see the change in the button state which is what you are complaining about!. See blink without delay.

Mark

double getChase(){
delay(getChase());

You shouldn't be using "delay()" at all, but even if you wanted to, it doesn't take a "float" as a parameter.
Worse, "float"s are slow, and here unnecessary.

Okay.... but it's reading the value before the delay starts in order to get a length for the delay. Also, when I run the functions independently, they work! What I mean is shown in the code below.

void loop() {
  ColorBreath("r");
}

it's reading the value before the delay starts in order to get a length for the delay.

I know that, but I'm telling you you shouldn't be using "delay()" at all.
It's painful, initially, but necessary.
Have you looked a "blink without delay", as suggested?

then use var in delay? I

You seem to be stuck on using "delay".

Please, change that mindset - you don't need, and shouldn't be using "delay".

That why we're telling you to look a the "blink without delay" example.

I took a look at "blink without delay." I think I understand what's happening here. would replacing all of my "delays" with something like this work?

where f is the mapped input from the potentiometer. Any place I would normally use a delay would be replaced with an if statement to see if the delay had been met. (at this rate, I am using the word delay as a word for what I want to program to do, not as the actual function. I understand that delay is not what I should be using.)

boolean cusDelay;

boolean myDelay(double f) {
   long currentMillis = millis();
   long previousMillis = 0;
   if (currentMillis - previousMillis > f){
     previousMillis = currentMillis;
     if (cusDelay == false){
       cusDelay = true;
     }
     else{
       cusDelay = false;
     }
   }
   return cusDelay;
}

No, I don't think your proposed solution will work.
What you really need is a finite state machine or a small number of separate finite state machines.
I'm sorry, but there is no easy fix - a rewrite is necessary.

Do you have any code suggestions other than just saying it's wrong? How would I go about implementing something like you are suggesting? I've gone through every tutorial that I thought was even remotely relevant but I'm still having issues. Has anyone else ever used a finite state machine with LED dimming on the PWM outputs?

Do you have any code suggestions other than just saying it's wrong?

Yes, I do.
Take the technique set out in the "blink without delay" example, substitute "micros" for "millis", and write a sketch that implements a slow fade up and down of the on-board LED on pin 13.
(this pin isn't PWM-capable, remember)

This is a learning exercise, and hopefully will give you an insight into doing what you want to achieve.

Edit: Mission creep: Make the fade period variable by using a pot.

So you're saying that in order to properly fade I will have to not only create a function to step the brightness of the LED up and down (fading) but I also need to rewrite the PWM function that is already built in to the arduino?

No, I never said it was necessary to rewrite analogWrite, just that you can easily write code to perform two simple tasks operating at different rates without resorting to "delay()"
For the task described, if you get to sixty lines of code, you've gone too far.