Demo Mode for LEDs

Hi…I have a led set-up where I have 10 different lights programs with a new one called with a interrupt which incriments to the next program via a switch-case list. Here’s the current interrupt that does this for me

void modeSelector() {
  static unsigned long last_interrupt_time = 0;
  unsigned long interrupt_time = millis();
  // If interrupts come faster than 100ms, assume it's a bounce and ignore
  if (interrupt_time - last_interrupt_time > 100) {
    
    switch (lightMode) {
      case 0: lightMode = 1;
      break;
      case 1: lightMode = 2;
      break;
      case 2: lightMode = 3;
      break;
      case 3: lightMode = 4;
      break;
      case 4: lightMode = 5;
      break;
      case 5: lightMode = 6;
      break;
      case 6: lightMode = 7;
      break;
      case 7: lightMode = 8;
      break;
      case 8: lightMode = 9;
      break;
      case 9: lightMode = 10;
      break;
      case 10: lightMode = 0;
      break;
      default: lightMode = 0;
    }
  }    
    last_interrupt_time = interrupt_time;
 }

Each lightMode is then called in the main loop via a if statement…basically
if (lightMode == 2) { do the fancy cool light business;}

I’m trying to see if it’s possible to make it do a “demo mode”…basically do each lightMode for 3 or so times before moving on to the next one on its own, and doing this over and over. I thought of doing a basic “if-for” statement like this-

if (lightMode == 11) {
for (int j = 0; j < 3; j++) {
lightMode = 1;
break;
}
for (int j = 0; j < 3; j++) {
lightMode = 2;
break;
}
}

But this just calls that lightMode over and over without changing on it’s own. Can anyone think of a way to do this?

If you implement this as a Finite State Machine you could add a demo state which cycles through all modes.

Or, you could add a if: [pseudo code]

if (demo){ cycle; }else{ displayCurrentMode; }

Ok thanks Alpha Beta for the suggestion and the link to the FSM article (which I read but the lightbulb did not come on upstairs if you know what I mean), but I still am unable to come up with a solution to my code problem. Here’s what I have so far minus some array info i don’t think is needed to show as there’s a lot of it and lightModes 3-10 as they work the same way, just a different pattern for each.

//Pin connected to ST_CP of 74HC595
const int latchPin = 8;  //green-LATCH
//Pin connected to SH_CP of 74HC595
const int clockPin = 6;  //yellow-CLOCK
////Pin connected to DS of 74HC595
const int dataPin = 7;  //blue-DATA
int randNumber;
volatile int lightMode = 0;
volatile int demoCounter = 0;

void setup() {
  pinMode(latchPin, OUTPUT);
  randomSeed(analogRead(0));
  
  attachInterrupt(1, modeSelector, RISING);  //button on pin 3
  attachInterrupt(0, backwardsModeSelector, RISING);  //button on pin 2
void loop() {
  
  if (lightMode == 0) {
    digitalWrite(latchPin, 0);
    shiftOut(dataPin, clockPin, 0);
    digitalWrite(latchPin, 1);
  }
  
  if (lightMode == 1) {
    int var4 = 0;
    if (demoCounter == 1) {
      if (var4 < 3) {
        var4++;
        }
      else {
        lightMode++;
        demoCounter = 2;
        }
    }
      

    for (int j = 0; j < 7; j++) {
    //load the light sequence you want from array
    dataRED = dataArrayRED[j];
    dataGREEN = dataArrayGREEN[0];    
    //ground latchPin and hold low for as long as you are transmitting
    digitalWrite(latchPin, 0);
    //move 'em out
    shiftOut(dataPin, clockPin, dataGREEN);   
    shiftOut(dataPin, clockPin, dataRED);
    //return the latch pin high to signal chip that it 
    //no longer needs to listen for information
    digitalWrite(latchPin, 1);
    delay(75); 
  }
  for (int j = 0; j < 7; j++) {
    //load the light sequence you want from array
    dataRED = dataArrayRED[6];
    dataGREEN = dataArrayGREEN[j];   
    //ground latchPin and hold low for as long as you are transmitting
    digitalWrite(latchPin, 0);
    //move 'em out
    shiftOut(dataPin, clockPin, dataGREEN);   
    shiftOut(dataPin, clockPin, dataRED);
    //return the latch pin high to signal chip that it 
    //no longer needs to listen for information
    digitalWrite(latchPin, 1);
    delay(75);
  }
 for (int j = 1; j < 7; j++) {
    //load the light sequence you want from array
    dataRED = dataArrayRED[6];
    dataGREEN = dataArrayGREEN[7-j];   
    //ground latchPin and hold low for as long as you are transmitting
    digitalWrite(latchPin, 0);
    //move 'em out
    shiftOut(dataPin, clockPin, dataGREEN);   
    shiftOut(dataPin, clockPin, dataRED);
    //return the latch pin high to signal chip that it 
    //no longer needs to listen for information
    digitalWrite(latchPin, 1);
    delay(75);
  }
 for (int j = 1; j < 7; j++) {
    //load the light sequence you want from array
    dataRED = dataArrayRED[7-j];
    dataGREEN = dataArrayGREEN[0];   
    //ground latchPin and hold low for as long as you are transmitting
    digitalWrite(latchPin, 0);
    //move 'em out
    shiftOut(dataPin, clockPin, dataGREEN);   
    shiftOut(dataPin, clockPin, dataRED);
    //return the latch pin high to signal chip that it 
    //no longer needs to listen for information
    digitalWrite(latchPin, 1);
    delay(75);
   }
  
 }
  
  if (lightMode == 2) {
    if (demoCounter == 2) {
      int var5 = 0;
      if (var5 < 3) {
        var5++;
        }
      else {
        lightMode++;
        demoCounter = 3;
        }
      }
    for (int j = 0; j < 8; j++) {
    lightShiftPinA(j);
    lightShiftPinA(7-j);
    delay(90);
  }
 }
if (lightMode == 11) {
   demoCounter = 1;
   lightMode = 1;
 }
}
void backwardsModeSelector() {
  static unsigned long last_interrupt_time = 0;
  unsigned long interrupt_time = millis();
  // If interrupts come faster than 100ms, assume it's a bounce and ignore
  if (interrupt_time - last_interrupt_time > 100) {
    
    switch (lightMode) {
      case 0: lightMode = 11; demoCounter = 1;
      break;
      case 11: lightMode = 10; demoCounter = 0;
      break;
      case 10: lightMode = 9; demoCounter = 0;
      break;
      case 9: lightMode = 8; demoCounter = 0;
      break;
      case 8: lightMode = 7; demoCounter = 0;
      break;
      case 7: lightMode = 6; demoCounter = 0;
      break;
      case 6: lightMode = 5; demoCounter = 0;
      break;
      case 5: lightMode = 4; demoCounter = 0;
      break;
      case 4: lightMode = 3; demoCounter = 0;
      break;
      case 3: lightMode = 2; demoCounter = 0;
      break;
      case 2: lightMode = 1; demoCounter = 0;
      break;
      case 1: lightMode = 0; demoCounter = 0;
      break;
      default: lightMode = 0;
    }
  }    
    last_interrupt_time = interrupt_time;
 }

So having it loop through the lightModes automatically with 3 times for each mode is my goal. I only want it to do this when I select so I added lightMode 11 to try to have this happen when I select that mode. I thought this attempt would have a chance, it’s running the lights but it’s not moving to the next lightMode on it’s own once it gets sent to lightMode #1 from Mode #11.

Any thoughts on this one would be appreciated.

Just because I love FSMs and because I think your problem has a potentionally clean solution using FSMs I will try to get the light switch turn on the light one more time ;) [Being this is online, I just needed to remind you that no offence was intended, purely a joke related to your comment on the lightbulb. Sure you understood, but now I've clarified]

The basic idea is that you declare a name for each 'state' or rather, task, your program should do. (Say, for simplicitys sake, that you aim to write a program with three patterns and a demo, this adds up to four tasks your program will have to execute. You will then declare those states in whatever format you want, I prefer enums for simple state machines.) Now you'll have to keep track of the machines current status, and act upon that state.

This is the way I usually define the states of a FSM:

typedef enum {
    LIGHTS_OFF,
    LIGHTS_ON,
    LIGHTS_STROBE,
    LIGHTS_DEMO
} LightState

Now it is important that you have a varible that has scope for all functions that will need to know the current state.

LightState currentState = LIGHTS_OFF;

The next step is to control transitions, that is, how and when the currentState changes its value. The most commonly used transition is called deferred transition, this simply means that the actual change of state happens some place down the timeline, and not when the change request is made. This is to ensure that all functions that rely on the current state all act on the same state.

For the following example I will use immediate external transitions. This means that currentState will newer change unless the user tells it to, and it will change exactly when the request for a change is made.

For changing states we will use void immediateTransitionTo( LightState& thisState, LightState nextState ); This will handle all the action needed for a state change.

The next function our program will use is void stateMachine( LightState currentLightState ); This function will be the one responsible for calling the correct function according to the currentState.

Assuming you'll want a increase / decrease state control, I suggest adding two functions to the state machine:

LightState getNextState( LightState currentLightState );
LightState getPreviousState( LightState currentLightState );

Additionally I usually create a function for each state, so that results in:

void lightsOff();
void lightsOn();
void lightsStrobe();
void lightsDemo();

As a Post Scriptum I would suggest making the state machine a class. :)

I will post my complete arduino sketch in the next post, but keep in mind it is completely untested and is meant to serve as an example. If you decide to go for a FSM implementation just mail or PM me, or post here if you want input, feedback, tips or help. :)

Happy Coding!

I will post my complete arduino sketch in the next post

Client:

/*
|| A Simple Sample State Machine Client
||
|| Contributed:
|| Alexander Brevig
*/

#define DEBUG true //debug is enabled
#define BAUDRATE 9600
#define STATE_WRAPAROUND true
#define DEMO_ITERATIONS 3

#include “LightStateMachine.h”

#define PULLUP true//internal pullup is enabled
#define BUTTON_PRESS (!PULLUP)

#define INCREASE_BUTTON 11
#define DECREASE_BUTTON 10

void setup(){
pinMode(INCREASE_BUTTON,INPUT);
pinMode(DECREASE_BUTTON,INPUT);
if (PULLUP){
digitalWrite(INCREASE_BUTTON,HIGH);
digitalWrite(DECREASE_BUTTON,HIGH);
}
if (DEBUG){ Serial.begin(BAUDRATE); }
}

void loop(){
//set current state
if( digitalRead(INCREASE_BUTTON) == BUTTON_PRESS ){
immediateTransitionTo( currentState , getNextState( currentState ) );
}
if( digitalRead(DECREASE_BUTTON) == BUTTON_PRESS ){
immediateTransitionTo( currentState , getPreviousState( currentState ) );
}
//execute according to state
stateMachine( currentState );
}

Finite State Machine:

/*
|| A Simple Sample State Machine
||
|| Contributed:
|| Alexander Brevig
*/

#include “WProgram.h”

#ifndef DEBUG
#define DEBUG false
#endif

#ifndef STATE_WRAPAROUND
#define STATE_WRAPAROUND true
#endif

#ifndef DEMO_ITERATIONS
#define DEMO_ITERATIONS 3
#endif

//provide states for the statemachine
enum LightState{
LIGHTS_OFF,
LIGHTS_ON,
LIGHTS_STROBE,
LIGHTS_DEMO
};

//variables
LightState currentState = LIGHTS_OFF;
LightState demoState = LIGHTS_OFF;
byte demoIteration = 0; //the number of times a state is displayed

void lightsOff();
void lightsOn();
void lightsStrobe();
void lightsDemo();

//this function could recieve the current state insted of depending on it having global scope
LightState getNextState( LightState currentLightState ){
switch (currentLightState){
case LIGHTS_OFF: return LIGHTS_ON; break;
case LIGHTS_ON: return LIGHTS_STROBE; break;
case LIGHTS_STROBE: return LIGHTS_DEMO; break;
case LIGHTS_DEMO: return (STATE_WRAPAROUND ? LIGHTS_OFF : LIGHTS_DEMO); break;
}
}

//this function could recieve the current state insted of depending on it having global scope
LightState getPreviousState( LightState currentLightState ){
switch (currentLightState){
case LIGHTS_OFF: return (STATE_WRAPAROUND ? LIGHTS_DEMO : LIGHTS_OFF); break;
case LIGHTS_ON: return LIGHTS_OFF; break;
case LIGHTS_STROBE: return LIGHTS_ON; break;
case LIGHTS_DEMO: return LIGHTS_STROBE; break;
}
}

//change state immediatly
void immediateTransitionTo( LightState& thisState, LightState nextState ){ thisState = nextState; }

//execute according to state
void stateMachine( LightState currentLightState ){
switch (currentLightState){
//the state machine is asked to act upon the LIGHTS_OFF state, proceed to switch them off.
case LIGHTS_OFF:
lightsOff();
break;

//the state machine is asked to act upon the LIGHTS_ON state, proceed to switch them on.
case LIGHTS_ON:
lightsOn();
break;

//the state machine is asked to act upon the LIGHTS_STROBE state, proceed to toogle light states.
case LIGHTS_STROBE:
lightsStrobe();
break;

//the state machine is asked to act upon the LIGHTS_DEMO state.
case LIGHTS_DEMO:
lightsDemo();
break;
}
}

/*
STATE TOOLS
*/

//provide the state machine a tool for acting upon the LIGHTS_OFF state
void lightsOff(){
//TODO - implement state
if (DEBUG) {Serial.println(“lights off”);}
}

//provide the state machine a tool for acting upon the LIGHTS_ON state
void lightsOn(){
//TODO - implement state
if (DEBUG) {Serial.println(“lights on”);}
}

//provide the state machine a tool for acting upon the LIGHTS_STROBE state
void lightsStrobe(){
//TODO - implement state
if (DEBUG) {Serial.println(“lights strobe”);}
}

//provide the state machine a tool for acting upon the LIGHTS_DEMO state
void lightsDemo(){
//TODO - implement all states
if (DEBUG) {Serial.println(“lights demo”);}
if (demoIteration<DEMO_ITERATIONS){
demoIteration++;
} else {
demoIteration=0;
immediateTransitionTo( demoState, getNextState(demoState) );
}
switch (demoState){
case LIGHTS_OFF:
lightsOff();
break;
case LIGHTS_ON:
lightsOn();
break;
case LIGHTS_STROBE:
lightsStrobe();
break;
}
}

Those #ifndef #define #endif are just to make sure that the configuration regarding the state machine and debugging is made.

Thank you so much for the steering in a right direction and an example to build upon! I have to say I've never seen a program setup such as this one (I'm fairly new to coding so that's probably no surprise :) ) I've learned a good deal in a short amount of time and hopefully I'll be able to make this work for me as well. From quickly reading through the code I understand most of the individual functions in general, I'll just have to spend some time tonight when I'm free to run through it more closely and hopefully see the whole picture come together. My Best...thanks again