[SOLVED]State machines - I'm baffled

Hi all,

It’s been a while since I showed my face in here with a question, up until now I’ve done stuff that worked the way I wanted without asking here what I need.

So, my project is a moodlight that can be controlled by an Android app using bluetooth.
Communication works, basic functions (single colors) work fine, no problem.

Now I want to have the light show some fades and I can also make that work, but only once.
I’ve been looking into repeating a function and I came to the conclusion I will have to enter the world of state machines.
I started reading up here on the playground and I understand the logic behind it, but have no idea how to implement it.
I did not even try to implement one because I simply cannot understand what I will have to put into my code.

The (shortened version of the) code:

/* trial code for statemachines */

#include "Tlc5940.h"
#include "SoftwareSerial.h"
//colors on the TLC pins
int red[] = {0, 3, 6, 9, 12}; //Red LED's
int blue[] = {1, 4, 7, 10, 13}; //Blue LED's
int green[] = {2, 5, 8, 11, 14}; //Green LED's

int bright = 768;

int color = 0;

SoftwareSerial BTSerial (7, 8);

void setup() {
  BTSerial.begin(9600);
  Tlc.init(); //initialize TLC chip
  delay(1000); //this delay is in place so that the first part of the code is not skipped.
  for (int i = 0; i <= 4; i++) {
    Tlc.set(red[i], 500);
  }
  Tlc.update();
  delay(500);
  Tlc.clear();
  for (int i = 0; i <= 4; i++) {
    Tlc.set(green[i], 500);
  }
  Tlc.update();
  delay(500);
  Tlc.clear();
  for (int i = 0; i <= 4; i++) {
    Tlc.set(blue[i], 500);
  }
  Tlc.update();
  delay(500);
  Tlc.clear();
  Tlc.update();
}

void loop() {
  if (BTSerial.available() > 0) {
    color = BTSerial.read();

    if (color == 'Q') {
      rainbowfade();
      color = 0;
    }
    else if (color == 'Z') {
      Tlc.clear();
      Tlc.update();
      color = 0;
    }
  }
}

void rainbowfade() {
  Tlc.clear();
  for (int i; i <= 4; i++){  
    for (int fade = 0; fade <= 255; fade += 5){
      Tlc.set(red[i], fade);
      Tlc.update();
      delay(10);
    }
  }
  for (int i; i <= 4; i++){  
    for (int fade = 0; fade <= 255; fade += 5){
      Tlc.set(green[i], fade);
      Tlc.update();
      delay(10);
    }
  }
  for (int i; i <= 4; i++){  
    for (int fade = 0; fade <= 255; fade += 5){
      Tlc.set(blue[i], fade);
      Tlc.update();
      delay(10);
    }
  }
  for (int i; i <= 4; i++){  
    for (int fade = 255; fade >= 0; fade -= 5){
      Tlc.set(red[i], fade);
      Tlc.set(green[i], fade);
      Tlc.set(blue[i], fade);
      Tlc.update();
      delay(25);
    }
  }
}

Yes, I’m using delays to simplify things for now, I am only testing it for now. The final code will use millis, which is another thing I am playing around with to get it to work as intended, for now the delays work as intended.

Now, my question:
I have a bunch of functions, that all make the TLC chip show a single color. They stay on, that’s the way it is intended. Until I send a new command through the serial connection the color stays on. That is why I cut out all those functions (as you can probably guess, the functions are A through to P).
The fader, however will run once (all LED’s go from off to 255 one at a time, then they all go off at once).

So, I need to implement state machines and I am guessing I have to rewrite my whole code to get this to work.
Any good pointers on where to start?

I find state machines easiest to write using switch/case using the value of a state variable for the switch.

Using this structure only the code for the current state is executed each time through loop(). In the case of your fade state, then each time through loop() when in that state check whether the fade period for the current LED has elapsed. If not keep going round loop() until it has then take the next fade action and save the start time ready for the next check. Keep going like this until all the fade actions have taken place and then switch to another state.

I really would urge you to switch to millis() timing NOW as it fits perfectly with the switch/case state machine.

Right…

I just checked a few examples and basically copied what they did.

I can control the functions I have in there in the same way as before (if/else statements) but same as before, the fader only runs once and I can only get it to run again by pressing the button on my screen.

New code(with delays, millis will follow when I am a bit more awake):

/* trial code for statemachines */

#include "Tlc5940.h"
#include "SoftwareSerial.h"

//states
#define A 0
#define B 1
#define C 2
#define Q 3
#define Z 4

//colors on the TLC pins
int red[] = {0, 3, 6, 9, 12}; //Red LED's
int blue[] = {1, 4, 7, 10, 13}; //Blue LED's
int green[] = {2, 5, 8, 11, 14}; //Green LED's

int bright = 768;

int state = 'Z';

SoftwareSerial BTSerial (7, 8);

void setup() {
  BTSerial.begin(9600);
  Serial.begin(9600);
  Tlc.init(); //initialize TLC chip
  delay(1000); //this delay is in place so that the first part of the code is not skipped.
  for (int i = 0; i <= 4; i++) {
    Tlc.set(red[i], 500);
  }
  Tlc.update();
  delay(500);
  Tlc.clear();
  for (int i = 0; i <= 4; i++) {
    Tlc.set(green[i], 500);
  }
  Tlc.update();
  delay(500);
  Tlc.clear();
  for (int i = 0; i <= 4; i++) {
    Tlc.set(blue[i], 500);
  }
  Tlc.update();
  delay(500);
  Tlc.clear();
  Tlc.update();
}

void loop() {
  if (BTSerial.available() > 0) {
    state = BTSerial.read();
    Serial.println(state);

    switch (state)
    {
      case 'A':
        redled();
        break;
      case 'B':
        greenled();
        break;
      case 'C':
        blueled();
        break;
      case 'Q':
        rainbowfade();
        break;
      case 'Z':
        reset();
        break;

    }
  }
}
void redled() {
  Tlc.clear();
  for (int i; i <= 4; i++) {
    Tlc.set(red[i], bright);
  }
  Tlc.update();
}

void greenled() {
  Tlc.clear();
  for (int i; i <= 4; i++) {
    Tlc.set(green[i], bright);
  }
  Tlc.update();
}

void blueled() {
  Tlc.clear();
  for (int i; i <= 4; i++) {
    Tlc.set(blue[i], bright);
  }
  Tlc.update();
}



void rainbowfade() {
  Tlc.clear();
  for (int i; i <= 4; i++) {
    for (int fade = 0; fade <= 255; fade += 5) {
      Tlc.set(red[i], fade);
      Tlc.update();
      delay(10);
    }
  }
  for (int i; i <= 4; i++) {
    for (int fade = 0; fade <= 255; fade += 5) {
      Tlc.set(green[i], fade);
      Tlc.update();
      delay(10);
    }
  }
  for (int i; i <= 4; i++) {
    for (int fade = 0; fade <= 255; fade += 5) {
      Tlc.set(blue[i], fade);
      Tlc.update();
      delay(10);
    }
  }
  for (int i; i <= 4; i++) {
    for (int fade = 255; fade >= 0; fade -= 5) {
      Tlc.set(red[i], fade);
      Tlc.set(green[i], fade);
      Tlc.set(blue[i], fade);
      Tlc.update();
      delay(25);
    }
  }
}

void reset() {
  Tlc.clear();
  Tlc.update();
}

You have the switch/case inside the if (BTSerial.available() > 0) so the switch/case will only run again once new serial data is available.

Insert Homer Simpson.... D'OH!
Should've spotted that one.

Works great, thanks! You're my hero for this week :slight_smile: