LED Strip using Interrupts for colour and power

Hi,

I’ve recently started a project creating a light for my room. Currently I have a nano attached to an LED strip a capacitive button and a push button.

I have managed to code the capacitive button as an interrupt which changes the colours in a fadeColour function written. Meaning the user can cycle through different changes of colours. This is using D2 (interrupt pin 1).

I also have a two pronged push button which I have one end attached to ground and the other to D3. I am struggling to set up an interrupt using the push switch which I can use as an ON/OFF button for the rest of the circuit or even just the LEDs.

I have tried using the onOffButton Interrupt to change the next colour to fade to black (0,0,0) this works but then won’t retrigger.

What I really want is the button to instantly turn the lights on and off.

Sorry I’m pretty new to Arduino and I’m not super experienced coding hardware.

If you’re interested the fadeColour function takes any colour (int[3] array) and uses a sine wave to fade between the colours in a non-linear fashion. This creates a more interesting LED fade.

Thanks
Arthur

Here is my code:

#include <Adafruit_NeoPixel.h>

#define PIN       7
#define N_LEDS    20
int inputPin = 2;
int incrementColor = 0;
int onOffPin = 3;
int buttonCheck = 0;
int breakLoop =0;

// Colour arrays (going from blue to red)
int blue[3] = {0, 0, 255};
int red[3] = {255, 0, 0};
int purple[3] = {75, 0, 130};
int pink[3] = {255, 20, 147};
int green[3] = {0, 200, 0};
int turquoise[3] = {175, 238, 238};
int yellow[3] = {255, 255, 0};
int orange[3] = {255, 128, 0};
int black[3] = {0,0,0};

int currentColour1;
int currentColour2;
int currentColour3;


// set initial colour
int redVal = red[0];
int greenVal = red[1];
int blueVal = red[2];
int pinTouchVal = 0;

// intiialise
int prevR = redVal;
int prevG = greenVal;
int prevB = blueVal;

int DEBUG = 0;
int loopCount = 0;

int colorState = 0;
int lastColorButtonState = 0;
int colorButtonState = 0;
int switchButtonState = 0;
bool onOffButton;

Adafruit_NeoPixel strip = Adafruit_NeoPixel(N_LEDS, PIN, NEO_GRB + NEO_KHZ800);


void setup() {
  // put your setup code here, to run once:
  strip.begin();
  Serial.begin(9600);
  pinMode(inputPin, INPUT);
  attachInterrupt(digitalPinToInterrupt(inputPin), buttonPressed, FALLING);
  pinMode(onOffPin, INPUT_PULLUP);
  attachInterrupt(digitalPinToInterrupt(onOffPin), turnOffOn, FALLING);

  if (DEBUG) {
    Serial.begin(9600);
  }
}

void buttonPressed() {
  noInterrupts();
  if (switchButtonState < 6) {
    switchButtonState += 1;
    switchColourState();
  } else {
    switchButtonState = 0;
    switchColourState();
  }
//  Serial.println(switchButtonState);
  interrupts();
}

void turnOffOn() {

}

void loop() {
  // put your main code here, to run repeatedly:
  // fading between selected colours.
  fadeColour(currentColour1);
  fadeColour(currentColour2);
  fadeColour(currentColour3);

}


// code to switch colours
void switchColourState() {
  switch (switchButtonState) {
    case 0:
      currentColour1 = red;
      currentColour2 = blue;
      currentColour3 = yellow;
      break;
    case 1:
      currentColour1 = green;
      currentColour2 = blue;
      currentColour3 = turquoise;
      break;
    case 2:
      currentColour1 = blue;
      currentColour2 = purple;
      currentColour3 = purple;
      break;
    case 3:
      currentColour1 = orange;
      currentColour2 = red;
      currentColour3 = red;
      break;
    case 4:
      currentColour1 = pink;
      currentColour2 = purple;
      currentColour3 = blue;
      break;
    case 5:
      currentColour1 = orange;
      currentColour2 = red;
      currentColour3 = yellow;
      break;
    case 6:
      currentColour1 = black;
      currentColour2 = black;
      currentColour3 = black;
  }
}

int calculateStep(int prevValue, int endValue) {
  int step = endValue - prevValue;
  if (step) {
    step = 1020 / step;
  }
  return step;
}

float calculateValue(int step, float val, int i) {

  if ((step) && i % step == 0) {  // if step is non zero and its time to chnge a value
    float out;
    static float in = 4.712;
    if (in < 7.853) {
      in = in + 0.01;
    }
    out = sin(in) * 5.5;
    if (step > 0) {
      val += out;
    }
    else if (step < 0) {
      val -= out;
    }
  }
  if (val > 255) {
    val = 255;
  }
  else if (val < 0) {
    val = 0;
  }
  return val;
}

void fadeColour(int colour[3]) {

  int R = colour[0];
  int G = colour[1];
  int B = colour[2];

  int stepR = calculateStep(prevR, R);
  int stepG = calculateStep(prevG, G);
  int stepB = calculateStep(prevB, B);

  for (int i = 0; i <= 1020; i++) {
    redVal = calculateValue(stepR, redVal, i);
    greenVal = calculateValue(stepG, greenVal, i);
    blueVal = calculateValue(stepB, blueVal, i);

    for (int LEDS = 0; LEDS < N_LEDS; LEDS++) {
      strip.setPixelColor(LEDS, redVal, greenVal, blueVal);
      strip.show();

    }
    delay(0);
    if (DEBUG) { // If we want serial output, print it at the
      if (i == 0 or i % loopCount == 0) { // beginning, and every loopCount times
        Serial.print("Loop/RGB: #");
        Serial.print(i);
        Serial.print(" | ");
        Serial.print(redVal);
        Serial.print(" / ");
        Serial.print(greenVal);
        Serial.print(" / ");
        Serial.println(blueVal);
      }
      DEBUG += 1;
    }
  }
  // update current values for next loop
  prevR = redVal;
  prevG = greenVal;
  prevB = blueVal;
  delay(0);
}

Hi and welcome to the forum. +1 karma for using code tags in your first post.

Using interrupts for buttons is not the right answer. It's like going into hospital and having a general anaesthetic for having your toe nails trimmed. All you need to do is use digitalRead().

Anyway, here's the problem:

void turnOffOn() {

}

Need some code in there...

Thanks for the reply and the karma!

Ah yeah I did have code in there I just removed it as it wasn't working the way I wanted.

This doesn't have the desired effect as the button causes the LED strip to fade to black rather than switch off and turning back on doesn't work.

Is there a way to cut power using the button or to have it switch the LED strip off instantly?

void turnOffOn() {
if (buttonCheck) {
     currentColour1 = currentColour2 = currentColour3 = black;
     buttonCheck = 0;
   } else {
     switchColourState();
     buttonCheck = 1;
   }
}

Thank you!

vectorspaces:
Is there a way to cut power using the button or to have it switch the LED strip off instantly?

That is not what interrupts are for!

As a beginner, it is incredibly unlikely that interrupts will be useful to you.

A common "newbie" misunderstanding is that an interrupt is a mechanism for altering the flow of a program - to execute an alternate function. Nothing could be further from the truth! :astonished:

An interrupt is a mechanism for performing an action which can be executed in "no time at all" with an urgency that it must be performed immediately or else data - information - will be lost or some harm will occur. It then returns to the main task without disturbing that task in any way though the main task may well check at the appropriate point for a "flag" set by the interrupt.

Now these criteria are in a microprocessor time scale - microseconds. This must not be confused with a human time scale of tens or hundreds of milliseconds or indeed, a couple of seconds. A switch operation is in this latter category and even a mechanical operation perhaps several milliseconds; the period of a 6000 RPM shaft rotation is ten milliseconds.

Unless it is a very complex procedure, you would expect the loop() to cycle many times per millisecond. If it does not, there is most likely an error in code planning; while the delay() function is provided for testing purposes, its action goes strictly against effective programming methods. The loop() will be successively testing a number of contingencies as to whether each requires action, only one of which may be whether a particular timing criteria has expired. Unless an action must be executed in the order of microseconds, it will be handled in the loop().

So what sort of actions do require such immediate attention? Well, generally those which result from the computer hardware itself, such as high speed transfer of data in UARTs(, USARTs) or disk controllers.

An alternate use of interrupts, for context switching in RTOSs, is rarely relevant to this category of microprocessors as it is more efficient to write cooperative code as described above.


TL;DR?

You need to write code that checks the button on a regular basis within the animation code and branches out to the code which clears the strip.

This would be within the "for" loop of your "fadeColour" function where you could exit the loop and set a return status to the function that would be picked up wherever that function is called in order to swap to the blanking code.

Hi,

Okay thanks for the clarification, that makes sense and helps me understand what interrupts are for.

I'll add a check using digitalRead() in the animation loop. Unfortunately the loop does take a non-negligible time to execute so adding these sorts of reads will still lead to a delay between switching on and off.

Thank you for the explanation

Arthur

You shouldn’t be able to notice the delay since the microcontroller should be running through the loop function many times per second, assuming your code isn’t blocking. You cannot use the delay function.

vectorspaces:
I'll add a check using digitalRead() in the animation loop. Unfortunately the loop does take a non-negligible time to execute so adding these sorts of reads will still lead to a delay between switching on and off.

Then remove the loop. It is "blocking code". You only have one processor core to use, so if you want to monitor for other events like button presses, you cannot use blocking code. Only loops which execute very quickly can be used.

Part of your problem is where you put strip.show(). That is a classic mistake. It should not be inside that for-loop, I suspect. Calling strip.show() updates all 20 LEDs in your strip. If you put it inside a loop that repeats 20 times, it's like updating a strip of 400 LEDs.

PaulRB:
Then remove the loop. It is “blocking code”. You only have one processor core to use, so if you want to monitor for other events like button presses, you cannot use blocking code. Only loops which execute very quickly can be used.

Referring to the “for loop”:

 for (int i = 0; i <= 1020; i++) {

The test for the button should be inside this loop with the ability to exit the loop and return a function value to trigger a blanking.

PaulRB:
Part of your problem is where you put strip.show(). That is a classic mistake. It should not be inside that for-loop, I suspect. Calling strip.show() updates all 20 LEDs in your strip. If you put it inside a loop that repeats 20 times, it’s like updating a strip of 400 LEDs.

You are referring to this code:

    for (int LEDS = 0; LEDS < N_LEDS; LEDS++) {
      strip.setPixelColor(LEDS, redVal, greenVal, blueVal);
      strip.show();
    }

Should obviously be:

    for (int LEDS = 0; LEDS < N_LEDS; LEDS++) {
      strip.setPixelColor(LEDS, redVal, greenVal, blueVal);
    }
    strip.show();

Or simply:

    for (int LEDS = 0; LEDS < N_LEDS; LEDS++) strip.setPixelColor(LEDS, redVal, greenVal, blueVal);
    strip.show();

Thanks to both of you. This was the problem, the code was blocking and moving the strip.show() outside of that for loop means I can now test for button interactions in that loop.

  • karma thanks :slight_smile:

Paul__B:
The test for the button should be inside this loop with the ability to exit the loop and return a function value to trigger a blanking.

I'll have a look at putting the button code in there.

I would still recommend removing that loop also.