1
votes

I am using an RGB LED with a keypad. Pressing '1' turns on a light while pressing '2' turns off the light. After I press '3' I want the LED to loop through colors and only if a different button is pressed is when the code leaves the loop. My problem is while looping the keypads state of HIGH or LOW is not changed therefore the key that is saved as pressed cannot change. I need some way to get out of this loop without stopping the loop.

#include <Keypad.h>

const int GreenLED=9;
const int BlueLED=10;
const int RedLED=11;

const byte numRows=4;
const byte numCols=4;

char keymap[numRows][numCols] = 
{
  {'1','2','3','A'},
  {'4','5','6','B'},
  {'7','8','9','C'},
};

byte rowPins[numRows] = {5,4,3,2};
byte colPins[numCols] = {13,8,7,6};

char keypressed;
boolean ledPin_stateGreen;
boolean ledPin_stateRed;
boolean ledPin_stateBlue;

Keypad myKeypad = Keypad(makeKeymap(keymap), rowPins, colPins, numRows, 
numCols);

void setup() {
  pinMode(GreenLED, OUTPUT);
  pinMode(BlueLED, OUTPUT);
  pinMode(RedLED, OUTPUT);
  ledPin_stateGreen = digitalRead(GreenLED);
  ledPin_stateRed = digitalRead(RedLED);
  ledPin_stateBlue = digitalRead(BlueLED);
  Serial.begin(9600);
}

void loop() {
      char key = myKeypad.getKey();

      if(key != NO_KEY)
      {
        Serial.println(key);
  }

  //Serial.println(myKeypad.getState());

  keypadEvent(key);
}

  void setColor(int red, int green, int blue)
    {
       #ifdef COMMON_ANODE
       red = 255 - red;
       green = 255 - green;
       blue = 255 - blue;
      #endif
       analogWrite(RedLED, red);
  analogWrite(GreenLED, green);
  analogWrite(BlueLED, blue);  
}

void keypadEvent (KeypadEvent key)
{
  switch (myKeypad.getState())
  {
    case PRESSED:
    if (key == '1')
    {
      digitalWrite(GreenLED, HIGH);
      digitalWrite(BlueLED, HIGH);
      digitalWrite(RedLED, HIGH);
    }  
    if (key == '2')
    {
     digitalWrite(GreenLED, LOW);
     digitalWrite(BlueLED, LOW);
     digitalWrite(RedLED, LOW);         
    }
    if (key == '3')
    {
      int previousState= myKeypad.getState();
      while(key == '3')
      {
        key = myKeypad.getKey();       
        setColor(255, 0, 0);  // red
        delay(200);
        Serial.println(myKeypad.getState());
        setColor (50,50,50); //white
        delay (200);
        setColor (255,40,0);
        delay(200);
        setColor(0, 255, 0);  // green
        delay(200);
        setColor(0, 0, 255);  // blue
        delay(200);
        setColor(255, 255, 0);  // yellow
        delay(200);
        setColor(80, 0, 80);  // purple
        delay(200);
        setColor(0, 255, 255);  // aqua
        delay(200);
        Serial.println(myKeypad.getState());

      }
  }
  }

}
2
please show what you have triedPiglet

2 Answers

0
votes

In your setup, you have GreenLED, BlueLED, RedLED set to OUTPUT, but then you try to digitalRead() from them...

void setup() {
  pinMode(GreenLED, OUTPUT);
  pinMode(BlueLED, OUTPUT);
  pinMode(RedLED, OUTPUT);
  ledPin_stateGreen = digitalRead(GreenLED);
  ledPin_stateRed = digitalRead(RedLED);
  ledPin_stateBlue = digitalRead(BlueLED);
  Serial.begin(9600);
}
0
votes

Your loop is preventing the keypad from being read for about 1.6 seconds. Then you have a very very small window to have the key pressed to pickup the key. Also, as you have stated, once you are in the loop you can't exit out of it due to the fact that you don't check for key presses. The tutorial on Arduino Playground states:

Consider, though, when you are writing your code that every delay() you use will take processing time away from the keypad. Something as short as delay(250) can make the keypad seem very unresponsive. And the same thing will happen if you sprinkle a bunch of delay(10)'s all through your code.

One way to solve this problem is to remove the delays from your loop and turn your program into a state machine which continuously polls the keypad for key presses. Now there are many ways to do this, of which I have chosen only one, and actually I really have turned it into 2 state machines. The top level one is the overall state of your program (i.e. what is our state based on the key pressed). The second one is a state machine to represent your loop which changes the colors of your LEDs. You can learn more about state machines here.

Here is your program turned into the above state machines:

#include <Keypad.h>

const int GreenLED=9;
const int BlueLED=10;
const int RedLED=11;

const byte numRows=4;
const byte numCols=4;

char keymap[numRows][numCols] = 
{
  {'1','2','3','A'},
  {'4','5','6','B'},
  {'7','8','9','C'},
};

byte rowPins[numRows] = {5,4,3,2};
byte colPins[numCols] = {13,8,7,6};

char keypressed;
boolean ledPin_stateGreen;
boolean ledPin_stateRed;
boolean ledPin_stateBlue;

enum MyState {
  LIGHT_ON,
  LIGHT_OFF,
  LIGHT_LOOPING
};

enum LightState {
  COLOR_1,
  COLOR_2,
  COLOR_3,
  COLOR_4,
  COLOR_5,
  COLOR_6,
  COLOR_7,
  COLOR_8
};

//Our current state for lights
MyState currentState = LIGHT_LOOPING;
LightState currentLightState = COLOR_1;
//The previous time in milliseconds
unsigned long prevTimeMS = 0;

Keypad myKeypad = Keypad(makeKeymap(keymap), rowPins, colPins, numRows, 
numCols);

void setup() {
  pinMode(GreenLED, OUTPUT);
  pinMode(BlueLED, OUTPUT);
  pinMode(RedLED, OUTPUT);
  ledPin_stateGreen = digitalRead(GreenLED);
  ledPin_stateRed = digitalRead(RedLED);
  ledPin_stateBlue = digitalRead(BlueLED);
  Serial.begin(9600);
  prevTimeMS = millis();
}

void loop() {
      char key = myKeypad.getKey();

      if(key != NO_KEY)
      {
        Serial.println(key);
  }

  //Serial.println(myKeypad.getState());
  //This function is really checking to see if we need to perform a state
  //transition or not for the currentState
  keypadEvent(key);

  //Do stuff based on the state We are in
  unsigned long currentTimeMS = millis();
  switch(currentState)
  {
    case LIGHT_ON:
      //Don't really need to do anything since we perform
      //the work on the state transition
      break;

    case LIGHT_OFF:
      //Don't really need to do anything since we perform
      //the work on the state transition
      break;

    case LIGHT_LOOPING:
      //Now switch based on the current color state to see if we
      //need to change to the next state
      switch(currentLightState)
      {
        case COLOR_1:
          if(checkDelay(currentTimeMS,prevTimeMS,200))
          {
            //We need to transition to the next state
            transitionLightState(COLOR_2);
          }
          break;

        case COLOR_2:
          if(checkDelay(currentTimeMS,prevTimeMS,200))
          {
            //We need to transition to the next state
            transitionLightState(COLOR_3);
          }
          break;

        case COLOR_3:
          if(checkDelay(currentTimeMS,prevTimeMS,200))
          {
            //We need to transition to the next state
            transitionLightState(COLOR_4);
          }
          break;

        case COLOR_4:
          if(checkDelay(currentTimeMS,prevTimeMS,200))
          {
            //We need to transition to the next state
            transitionLightState(COLOR_5);
          }
          break;

        case COLOR_5:
          if(checkDelay(currentTimeMS,prevTimeMS,200))
          {
            //We need to transition to the next state
            transitionLightState(COLOR_6);
          }
          break;

        case COLOR_6:
          if(checkDelay(currentTimeMS,prevTimeMS,200))
          {
            //We need to transition to the next state
            transitionLightState(COLOR_7);
          }
          break;

        case COLOR_7:
          if(checkDelay(currentTimeMS,prevTimeMS,200))
          {
            //We need to transition to the next state
            transitionLightState(COLOR_8);
          }
          break;

        case COLOR_8:
          if(checkDelay(currentTimeMS,prevTimeMS,200))
          {
            //We need to transition to the next state
            //which is back to the first state so we loop
            transitionLightState(COLOR_1);
          }
          break;
      }
      break;
  }
}

//This will return true if the correct amount of time has passed
boolean checkDelay(unsigned long currentMS, unsigned long prevMS, unsigned long delayMS)
{
  if((currentMS - prevMS) >= delayMS)
  {
    return true;
  }

  return false;
}

void transitionMyState(MyState newState)
{
  switch(newState)
  {
    case LIGHT_ON:
      digitalWrite(GreenLED, HIGH);
      digitalWrite(BlueLED, HIGH);
      digitalWrite(RedLED, HIGH);
      break;

    case LIGHT_OFF:
      digitalWrite(GreenLED, LOW);
      digitalWrite(BlueLED, LOW);
      digitalWrite(RedLED, LOW);
      break;

    case LIGHT_LOOPING:
      //We want to transition to the COLOR_1 state here
      transitionLightState(COLOR_1);
      break;
  }

  currentState = newState;
  //need to save off a new prevTimeMS
  prevTimeMS = millis();
}

void transitionLightState(LightState newState)
{
  //perform the action for the state transition
  switch(newState)
  {
    case COLOR_1:
      setColor(255, 0, 0);  // red
      break;

    case COLOR_2:
      setColor (50,50,50); //white
      break;

    case COLOR_3:
      setColor (255,40,0);
      break;

    case COLOR_4:
      setColor(0, 255, 0);  // green
      break;

    case COLOR_5:
      setColor(0, 0, 255);  // blue
      break;

    case COLOR_6:
      setColor(255, 255, 0);  // yellow
      break;

    case COLOR_7:
      setColor(80, 0, 80);  // purple
      break;

    case COLOR_8:
      setColor(0, 255, 255);  // aqua
      break;
  }

  currentLightState = newState;
  //need to save off a new prevTimeMS
  prevTimeMS = millis();
}

void setColor(int red, int green, int blue)
{
  #ifdef COMMON_ANODE
  red = 255 - red;
  green = 255 - green;
  blue = 255 - blue;
  #endif
  analogWrite(RedLED, red);
  analogWrite(GreenLED, green);
  analogWrite(BlueLED, blue);  
}

void keypadEvent (KeypadEvent key)
{
  switch (myKeypad.getState())
  {
    case PRESSED:
    if (key == '1')
    {
      transitionMyState(LIGHT_ON);
    }  
    if (key == '2')
    {
     transitionMyState(LIGHT_OFF);         
    }
    if (key == '3')
    {
      transitionMyState(LIGHT_LOOPING);
    }
  }
}

The implementation is more for readability then minimizing code by the way. I added 2 state variables which hold what state we are in for the key handler state machine and the looping state machine. The important thing to note about this implementation is that key presses are checked every iteration of the loop and there are no delays implemented. This allows for us to break out of the looping color state machine at anytime. You could also say that the looping of colors changed into a poll of "Do I need to change my color now?"

The other important thing to note is that now the time needs to be tracked so the looping state machine can determine when the right amount of time has passed in order to know when to change states. When a state transition happens then the current time is saved off. Note: The rollover of the millis counter is not taken into account in this example. This happens roughly every 50 days according to the Arduino docs. So every 50 days you will get a glitch if you are in the LIGHT_LOOPING state.