Interrupting a function when serial data is received

Hi,

I am building a BT operated mood light (with an Android app) that has a few fading patterns.
When I send a fixed color from the app and I want to change it, the response is instantaneous, but when there is a fader pattern running and I want to pick a color or different pattern, it ends the current pattern first and then calls the new function.
I've been looking around on how to do this, but I can't seem to find what I am looking for.

The idea is that as soon as the program receives new serial data, it interrupts it's current operation and executes whatever function is called by the app.

To stick to the rules, here is my code:

/* This code is to operate an RGB mood light driven by TLC5940 LED driver, controlled through an android app over BT
   Current status:
   All desired colors implemented
   BT communication up and running
   Android APP in development

   Libraries used:
   Tlc5940 for the TLC chip (LED driver)
   SoftwareSerial: To enable BT communication and uploading code to the Arduino chip simulataniously

   Hardware used:
   Atmega 328 chip with Arduino bootloader (or Arduino Uno/Nano)
   Texas Instruments TLC5940 LED Driver
   5 Common Anode RGB LED's
   HC-06 BT module
   various resistors
*/

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

//states
#define A 0
#define B 1
#define C 2
#define D 3
#define E 4
#define F 5
#define G 6
#define H 7
#define I 8
#define J 9
#define K 10
#define L 11
#define M 12
#define N 13
#define O 14
#define P 15
#define Q 16
#define Z 17

//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 = 2048;
byte 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;
 //deleted some states for length
   case 'Q':
      basicfade();
      break;
      case 'R':
      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();
}
//deleted a bunch of functions for length but you get the idea

void basicfade() {
  Tlc.clear();
    for (int fade = 0; fade <= bright; fade += 10) {
      Tlc.set(0, fade);
      Tlc.set(3, fade);
      Tlc.set(6, fade);
      Tlc.set(9, fade);
      Tlc.set(12, fade);
      Tlc.update();
    delay(20);
    }

  for (int fade = 0; fade <= bright; fade += 10) {
      Tlc.set(1, fade);
      Tlc.set(4, fade);
      Tlc.set(7, fade);
      Tlc.set(10, fade);
      Tlc.set(13, fade);
      Tlc.update();
    delay(20);
  }

  for (int fade = 0; fade <= bright; fade += 10) {
    Tlc.set(2, fade);
    Tlc.set(5, fade);
    Tlc.set(8, fade);
    Tlc.set(11, fade);
    Tlc.set(14, fade);
    Tlc.update();
    delay(20);
  }

for (int fade = bright; fade >= 0; fade -= 5) {
  Tlc.setAll(fade);
  Tlc.update();
  delay(20);
}
}

void rainbowfade() {
  //red
  for (int fade = 0; fade <= bright; fade += 10) {
    Tlc.set(0, fade);
      Tlc.set(3, fade);
      Tlc.set(6, fade);
      Tlc.set(9, fade);
      Tlc.set(12, fade);
      
    Tlc.update();
    delay(10);
  }
//orange
  for (int fade = 0; fade <= 1147; fade += 10) {
    Tlc.set(1, fade);
      Tlc.set(4, fade);
      Tlc.set(7, fade);
      Tlc.set(10, fade);
      Tlc.set(13, fade);
      Tlc.update();
    delay(10);
  }
//yellow
  for (int fade = 1147; fade <= bright; fade += 10) {
    Tlc.set(1, fade);
      Tlc.set(4, fade);
      Tlc.set(7, fade);
      Tlc.set(10, fade);
      Tlc.set(13, fade);
      Tlc.update();
    delay(10);
  }
//green
  for (int fade = bright; fade >= 0; fade -= 10) {
    Tlc.set(0, fade);
      Tlc.set(3, fade);
      Tlc.set(6, fade);
      Tlc.set(9, fade);
      Tlc.set(12, fade);
      
    Tlc.update();
    delay(10);
  }
//blue
  for (int fade = 0; fade <= bright; fade += 10) {
    Tlc.set(2, fade);
    Tlc.set(5, fade);
    Tlc.set(8, fade);
    Tlc.set(11, fade);
    Tlc.set(14, fade);
    Tlc.update();
    delay(10);
  }
  for (int fade = bright; fade >= 0; fade -= 10) {
    Tlc.set(1, fade);
      Tlc.set(4, fade);
      Tlc.set(7, fade);
      Tlc.set(10, fade);
      Tlc.set(13, fade);
      Tlc.update();
    delay(10);
  }
//indigo
  for (int fade = 0; fade <= 594; fade += 10) {
    Tlc.set(0, fade);
      Tlc.set(3, fade);
      Tlc.set(6, fade);
      Tlc.set(9, fade);
      Tlc.set(12, fade);
      Tlc.update();
    delay(10);
  }
  for (int fade = 255; fade >= 1044; fade -= 10) {
    Tlc.set(2, fade);
    Tlc.set(5, fade);
    Tlc.set(8, fade);
    Tlc.set(11, fade);
    Tlc.set(14, fade);
    Tlc.update();
    delay(10);
  }
//violet
  for (int fade = 594; fade <= 1904; fade += 10) {
    Tlc.set(0, fade);
      Tlc.set(3, fade);
      Tlc.set(6, fade);
      Tlc.set(9, fade);
      Tlc.set(12, fade);
      Tlc.update();
    delay(10);
  }
  for (int fade = 0; fade <= 1044; fade += 10) {
    Tlc.set(1, fade);
      Tlc.set(4, fade);
      Tlc.set(7, fade);
      Tlc.set(10, fade);
      Tlc.set(13, fade);
      Tlc.update();
    delay(10);
  }
  for (int fade = 1044; fade <= 1905; fade += 10) {
    Tlc.set(2, fade);
    Tlc.set(5, fade);
    Tlc.set(8, fade);
    Tlc.set(11, fade);
    Tlc.set(14, fade);
    Tlc.update();
    delay(10);
  }
Tlc.clear();
Tlc.update();
delay(50);


  
}

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

To make your sketch more responsive, you need to get rid of the calls to delay() and make your whole sketch a state machine (or collection of state machines)

Have a look at the blink without delay example in the IDE.

I did take a look at that, but I can't for the life of me think of a way to implement millis() in this code.
When I use the blinkwithoutdelay code and implement it the way I figured would be correct, there was no pause between the fade steps, and the LED's would just light up at max brightness and shut down again.

Like this:

currentMillis = 0;
if (currentMillis - previousMillis >= interval){
    previousMillis = currentMillis;
  for (int fade = 0; fade <= bright; fade += 10) {
    Tlc.set(2, fade);
    Tlc.set(5, fade);
    Tlc.set(8, fade);
    Tlc.set(11, fade);
    Tlc.set(14, fade);
    Tlc.update();
    //delay(20);
  }
}

I declared the variables/constants, all compiles, interval is set at 1000 so I can easily see if it is working
Also, when I places the if statement inside the for loop, it doesn't work.

I'm kind of lost in your second comment.
Last week I rewrote most of the code to make it a state machine, so I could make the functions run over and over instead of just once (I had a series of if/else statements to call the functions, I rewrote it into what you see in the first post).
Any pointers?

If I lose the for loop, how would I know if the LED's have reached max brightness?
For the basic fader this is quite simple, since it can go up to the max brightness set in the code (which will be a value controlled by the app later on, once I get this working as it should).
When I call the rainbowfader, they are values that change per for loop, as I need to generate some colors (to make it look like a rainbow).

If I lose the for loop, how would I know if the LED's have reached max brightness?

Because the brightness variable would exceed the maximum value of an eight bit variable.

OK, let me try to write something up and see if I understand what you guys mean.
Thanks for the suggestions!

Right, the function I'm working with is the below one:

void basicfade() {
  unsigned long currentMillis = millis();
  Tlc.clear();
  if (fade <= bright) {
    fade += 2;
    Tlc.set(0, fade);
    Tlc.set(3, fade);
    Tlc.set(6, fade);
    Tlc.set(9, fade);
    Tlc.set(12, fade);
    Tlc.update();}
  if (currentMillis - previousMillis >= interval){
    previousMillis = currentMillis;
    //delay(20);
  }
  if (fade <= bright) {
    fade +=2;
    Tlc.set(1, fade);
    Tlc.set(4, fade);
    Tlc.set(7, fade);
    Tlc.set(10, fade);
    Tlc.set(13, fade);
    Tlc.update();}
    //delay(20);
  if (currentMillis - previousMillis >= interval){
    previousMillis = currentMillis;
  }

  if (fade <= bright) {
    fade +=2;
    Tlc.set(2, fade);
    Tlc.set(5, fade);
    Tlc.set(8, fade);
    Tlc.set(11, fade);
    Tlc.set(14, fade);
    Tlc.update();}
    if (currentMillis - previousMillis >= interval){
    previousMillis = currentMillis;

    //delay(20);
  }

  previousMillis = 0;
  if (fade >= 0) {
    fade -=2;
    Tlc.setAll(fade);
    Tlc.update();}
    if (currentMillis - previousMillis >= interval){
    previousMillis = currentMillis;
    //delay(20);
  }
}

If I run this, it gives the whole neighborhood epilepsy, as it flickers high speed.

What it should do:
Turn the red channels up to max brightness with increments of 2 (currently it sits at 2048)
Then turn the green channels up
Then turn the blue channels up.

Where am I going wrong?
(the good thing is I can interrupt it directly, so I don't have to look at 10 minutes of flicker)

I had it different before, but that didn't do a thing.
Right now I have this:

void basicfade() {
  unsigned long currentMillis = millis();
  Tlc.clear();
  if (fade <= bright) {
    fade += 2;
  if (currentMillis - previousMillis >= interval){
    previousMillis = currentMillis;
    Tlc.set(0, fade);
    Tlc.set(3, fade);
    Tlc.set(6, fade);
    Tlc.set(9, fade);
    Tlc.set(12, fade);
    Tlc.update();}
    //delay(20);
  }
    if (fade <= bright) {
    fade +=2;
  if (currentMillis - previousMillis >= interval){
    previousMillis = currentMillis;
    Tlc.set(1, fade);
    Tlc.set(4, fade);
    Tlc.set(7, fade);
    Tlc.set(10, fade);
    Tlc.set(13, fade);
    Tlc.update();}
    //delay(20);
  }

  if (fade <= bright) {
    fade +=2;
  if (currentMillis - previousMillis >= interval){
    previousMillis = currentMillis;
    Tlc.set(2, fade);
    Tlc.set(5, fade);
    Tlc.set(8, fade);
    Tlc.set(11, fade);
    Tlc.set(14, fade);
    Tlc.update();}
    //delay(20);
  }

  previousMillis = 0;
  if (fade >= 0) {
    fade -=2;
  if (currentMillis - previousMillis >= interval){
  previousMillis = currentMillis;
    Tlc.setAll(fade);
    Tlc.update();}
    //delay(20);
    }
}

This makes all three channels fade in quite quickly at the same time, which is not the intention.
Also, it goes really quick, but the interval value is at 1000.
If I add a zero it stays off for a while and then all three channels come on at max brightness at the same time.
It also doesn't turn off again, and if I pick a different option and let it run the fader again, it just takes the fade value it was on last and stays at that brightness level.

Oh, I forgot. When changing the if statements, nothing happens.

I'm really in the dark with this, which is weird when you are working with bright LED's.

Read your PM, please

I read the PM, but that appears to be a real different route again.

While loop is relatively non blocking since it continually monitors the condition (waits) but stays in current code.

start time, delay time,current state , current time
do
{
do { this needs to run only once per fade
// run one fade code
Tlc.set(0, fade);
Tlc.set(3, fade);
Tlc.set(6, fade);
Tlc.set(9, fade);
Tlc.set(12, fade);
Tlc.update();
}
do{
wait for one fade pass top complete , keep tab on !Serial.available())
} while (cutter time - start time < delay time AND !Serial.available())
bump fade
} while ( fade < some brightens limit AND !Serial.available())
// the above will terminate when fade reached the desired limit or Serial.available() is true
// now check why the above code terminated
else if( Serial.avaiable())
return form function immediately
if(fade @ limit )
do next sequence if necessary

If there is no new command, the fade is supposed to run over and over. It doesn't end, so that doesn't help either.

The code I am working with now kind of does what I want it to, but it executes everything at the same time and that is not what I want.
I want the red LED's to fade in until max brightness is reached (so that is TLC 0, 3, 6, 9, 12), then green, then blue.
Now it does them all at once and that I need to prevent without losing the possibility to interrupt the fade loop.

You're adding 2 to fade whether or not it is time to update your leds. So fade gets 2 added every time loop spins and then every second the leds get updated with whatever value fade happens to have at that moment.

It seems like you'd only want to change fade once for each time you update the leds.

I swapped those two around (the if statement that adds 2 and the timer code) but it doesn't react as expected.

What is happening:

  • All LED's are updated at the same time.
  • The behavior is highly erratic. Red is fading ok'ish, but the other two are flashing really quick
  • The fade out is not running at all, so there is no repeat of the function
  • when I change to a different function and back, 'fade' is not reset to 0.

If I had hair, I'd be pulling it out by now :slight_smile:

void basicfade() {
  unsigned long currentMillis = millis();
  Tlc.clear();
  if (currentMillis - previousMillis >= interval){
    previousMillis = currentMillis;
    if (fade <= bright) {
    fade += 2;
    Tlc.set(0, fade);
    Tlc.set(3, fade);
    Tlc.set(6, fade);
    Tlc.set(9, fade);
    Tlc.set(12, fade);
    Tlc.update();}
  }
  if (currentMillis - previousMillis >= interval){
    previousMillis = currentMillis;
    if (fade <= bright) {
    fade += 2;
    Tlc.set(1, fade);
    Tlc.set(4, fade);
    Tlc.set(7, fade);
    Tlc.set(10, fade);
    Tlc.set(13, fade);
    Tlc.update();}
  }
  if (currentMillis - previousMillis >= interval){
    previousMillis = currentMillis;
    if (fade <= bright) {
    fade += 2;
    Tlc.set(2, fade);
    Tlc.set(5, fade);
    Tlc.set(8, fade);
    Tlc.set(11, fade);
    Tlc.set(14, fade);
    Tlc.update();}
  }
  if (currentMillis - previousMillis >= interval){
  previousMillis = currentMillis;
  if (fade >= 0) {
    fade -=2;
    Tlc.setAll(fade);
    Tlc.update();}
    }
}

*removed the timer reset, this didn't help as well..

Alright, last post before it's sleepytime.

Current code (for just this function that I am working with now):

void basicfade() {
  unsigned long currentMillis = millis();
  Tlc.clear();
  if (currentMillis - previousMillis >= interval){
    previousMillis = currentMillis;
    if (fade <= bright) {
    fade += 2;
    Tlc.set(0, fade);
    Tlc.set(3, fade);
    Tlc.set(6, fade);
    Tlc.set(9, fade);
    Tlc.set(12, fade);
    Tlc.update();}
  }
  if (currentMillis - previousMillis >= interval){
    previousMillis = currentMillis;
    if (fade <= bright) {
    fade += 2;
    Tlc.set(1, fade);
    Tlc.set(4, fade);
    Tlc.set(7, fade);
    Tlc.set(10, fade);
    Tlc.set(13, fade);
    Tlc.update();}
  }
  if (currentMillis - previousMillis >= interval){
    previousMillis = currentMillis;
    if (fade <= bright) {
    fade += 2;
    Tlc.set(2, fade);
    Tlc.set(5, fade);
    Tlc.set(8, fade);
    Tlc.set(11, fade);
    Tlc.set(14, fade);
    Tlc.update();}
  }
  if (currentMillis - previousMillis >= interval){
  previousMillis = currentMillis;
  if (fade >= 0) {
    fade -=5;
    Tlc.setAll(fade);
    Tlc.update();}
    }
}

It fades the red LED's, and then stops.
It doesn't restart the function, it just stops when the red LED's are at the value set in 'bright'.

When I pick a different function (fixed color) and then go back to the fader, nothing changes. It remains at the previous function.

I have no clue what I am doing wrong, it is probably extremely obvious but I can't seem to figure out where my mistake is.
I'm leaving it for today hoping for a brainwave somewhere between now and tomorrow, or someone who can point it out to me.
This is not a 'fix my code' request, more a request to point me in the right direction.

Vaclav:
Benji,
I guess clarification is in order.
When I post something on PM I do not expect it to be posted here.
That is why it is called Private Message
Please edit your post and remove ALL of my posting from it.
You can continue to use "blink without delay" style of coding but it will not allow you to exit the current function when serial data becomes available, which is what you have asked for.

You cannot control your code flow by serial data availability without checking for such availability.

I appreciate your help, Vaclav, but the whole reason for posting on a public forum is so that others can learn from what is being discussed.
Sending private messages is not my style, especially not when I ask a question in public.
I will not be ordered around in a topic that I opened, where I discuss my problem by anyone but a moderator or administrator.
Also, your statement is wrong. Even though the function does not do what it is supposed to do fully, I can exit from it at any time and return to it when I want.
I just need to find out where my mistake is in the last code snippet I posted.

This certainly shows what you mean, thanks.
There is a lot to adapt since I'm working with quite different hardware but I think this will help.
I'll try it out over the next few days probably as this means another rewrite.

Right, I'm definitely a lot of steps further.
The fader works, it keeps on going as it should and it can be interrupted at any time as wanted.
However (there always seems to be a however in programming), the program starts of where it was whenever it was interrupted.
Can this be prevented?

My code:

void basicfade() {
  switch(basicfader){
    case setclear:
    reset();
    break;
    case rfade:
    redfade();
    break;
    case gfade:
    greenfade();
    break;
    case bfade:
    bluefade();
    break;
    case outfade:
    fadeout();
    break;
  }
}

void redfade() {
  unsigned long currentMillis = millis();
    if (currentMillis - previousMillis >= interval){
    previousMillis = currentMillis;
    if(fade1 <= bright) {
    fade1 += 5;
    Tlc.set(0, fade1);
    Tlc.set(3, fade1);
    Tlc.set(6, fade1);
    Tlc.set(9, fade1);
    Tlc.set(12, fade1);
    Tlc.update();}
    }
    if (fade1 >= bright) {
    basicfader = gfade;
  }
}

void bluefade() {
  unsigned long currentMillis = millis();
    if (currentMillis - previousMillis >= interval){
    previousMillis = currentMillis;
    if(fade1 <= bright) {
    fade2 += 5;
    Tlc.set(1, fade2);
    Tlc.set(4, fade2);
    Tlc.set(7, fade2);
    Tlc.set(10, fade2);
    Tlc.set(13, fade2);
    Tlc.update();}
    }
    if (fade2 >= bright){
    basicfader = outfade;
  }
}

void greenfade() {
  unsigned long currentMillis = millis();
    if (currentMillis - previousMillis >= interval){
    previousMillis = currentMillis;
    if(fade1 <= bright) {
    fade3 += 5;
    Tlc.set(2, fade3);
    Tlc.set(5, fade3);
    Tlc.set(8, fade3);
    Tlc.set(11, fade3);
    Tlc.set(14, fade3);
    Tlc.update();}
    }
    if (fade3 >= bright) {
    basicfader = bfade;
  }
}

void fadeout() {
  unsigned long currentMillis = millis();
    if (currentMillis - previousMillis >= interval){
    previousMillis = currentMillis;
    if(fade1 >= 0) {
      fade1 -= 5;
      Tlc.setAll(fade1);
      Tlc.update();}
      if(fade1 <= 0) {
        fade1 = 0;
        fade2 = 0;
        fade3 = 0;
        basicfader = setclear;
      }
    }
}

I can switch to any other function I want, but when I return to the above it always goes back where it left off.
Somehow I need to 'reset' the state machine. I tried this by calling my reset() function, which contains the following(now):

void reset() {
  Tlc.clear();
  Tlc.update();
  fade1 = 0;
  fade2 = 0;
  fade3 = 0;
  basicfader = rfade;
}

But to no avail.
Any clues? Or doesn't it work like that?

Vaclav:
Benji,
I guess clarification is in order.
When I post something on PM I do not expect it to be posted here.

Vaclav, I strongly disagree with you providing technical support via PMs. PMs are for personal messages. For example, "do you want to come around for dinner?".

I don't like the way you try to "advise" people by PM, bypassing peer-group checking of your advice. If what you are writing is sensible, it should be publicly posted. This is, after all, intended as a knowledge-base. If someone posts a question, and you reply by PM, and your reply works, then no-one benefits (apart from the one person you replied to).

Back to the question: I haven't waded through all the code, but a State Machine seems appropriate here.

Or even, a simple change. :slight_smile:

Something like this from the original post:

for (int fade = bright; fade >= 0; fade -= 5) {
  Tlc.setAll(fade);
  Tlc.update();
  delay(20);
}

I gather you want to drop out of this if there is serial data incoming. Well, why not test for that?

for (int fade = bright; fade >= 0; fade -= 5) {
  Tlc.setAll(fade);
  Tlc.update();
  delay(20);
  if (Serial.available ())
    break;
}

Similarly, inside the basicfade function, between each group of fades, test if there is serial data and just return, eg.

void basicfade() {
  Tlc.clear();
    for (int fade = 0; fade <= bright; fade += 10) {
      Tlc.set(0, fade);
      Tlc.set(3, fade);
      Tlc.set(6, fade);
      Tlc.set(9, fade);
      Tlc.set(12, fade);
      Tlc.update();
      if (Serial.available ())
        return;
    delay(20);
    }

  for (int fade = 0; fade <= bright; fade += 10) {
      Tlc.set(1, fade);
      Tlc.set(4, fade);
      Tlc.set(7, fade);
      Tlc.set(10, fade);
      Tlc.set(13, fade);
      Tlc.update();
      if (Serial.available ())
        return;
    delay(20);
  }

  for (int fade = 0; fade <= bright; fade += 10) {
    Tlc.set(2, fade);
    Tlc.set(5, fade);
    Tlc.set(8, fade);
    Tlc.set(11, fade);
    Tlc.set(14, fade);
    Tlc.update();
    if (Serial.available ())
      return;
    delay(20);
  }

for (int fade = bright; fade >= 0; fade -= 5) {
  Tlc.setAll(fade);
  Tlc.update();
  if (Serial.available ())
    return;
  delay(20);
}
}

That not only exits the loop (with the delays in it) but exits the function, so you can do something else.

Thanks Nick, for the reply.
The problem of getting out of the program is solved by using a state machine, the most recent code I posted works fine.
Problem is, when I go back to that function, it restarts where it was. This is not the desired behaviour, it should start at 0 when I come back to the function from something else.

I haven't looked at your code in depth, but when you "go back to that function" you could reset the state.