Go Down

Topic: [solved] controlling an adressable rgb strip with an IR remote (Read 241 times) previous topic - next topic

langeleipe

Hey,

My attachInterrupt doesn't work correctly. First of all, here's my code:

Code: [Select]
//Libraries from here___________________________________________________________________________

#include <IRremote.h>
#include <Adafruit_NeoPixel.h>

//Constants from here___________________________________________________________________________


//Ledstrip

const int dinPin = 2;
const int numOfLeds = 288;
const int numOfLeds2 = (numOfLeds - 1);
Adafruit_NeoPixel pixels = Adafruit_NeoPixel(numOfLeds, dinPin, NEO_GRB + NEO_KHZ800);

//Rainbow Calculations

const float a = 127.5;
const int b = 255;
const float pi = 3.1415926535897932384626433832795;
const float c_red = (pi / 3);
const float c_green = pi;
const float c_blue = (5 * pi / 3);
const float d = pi;

//Relay

int relay = 4;
volatile byte relayState = LOW;

//Remote

const int RECV_PIN = 3;
IRrecv irrecv(RECV_PIN);
decode_results results;

// RGB Color Values

float red = 0;
float green = 0;
float blue = 0;
float rgb = 0;
int red2 = 0;
int green2 = 0;
int blue2 = 0;
float red3 = 0;
float green3 = 0;
float blue3 = 0;

//Pixel Selection

volatile float increment = 0.033333333;
volatile float color = 0;
float whichled = 0;
volatile int mode = 10;
int fade = 0;
boolean up = true;
boolean left = true;
float night = 0;
int moment = 0;
float strength = 0;
int night2 = 0;

// Voids start here____________________________________________________________________________

void Colourm2() { //Calculate Color With Brightness
  red = a + b * sin(c_red + d * color);
  if (red < 0){ red = 0; } else if (red > 255){ red = 255; }
  green = a + b * sin(c_green + d * color);
  if (green < 0){ green = 0; } else if (green > 255){ green = 255; }
  blue = a + b * sin(c_blue + d * color);
  if (blue < 0){ blue = 0; } else if (blue > 255){ blue = 255; }
  rgb = red + green + blue;
  red2 = red * 255 / rgb;
  green2 = green * 255 / rgb;
  blue2 = blue * 255 / rgb;
}

void cycleDown() { //Cycles Through The Colors
  color = (color - (increment * 5));
}

void cycleUp() { //Cycles Through The Colors
  color = (color + (increment * 5));
}

void Fade() { //Cycles Through Brightness
  if (up == true) {
    fade = fade + 5;
    if (fade >= 100) {
      up = false;
    }
  } else if (up == false) {
    fade = fade - 5;
    if (fade <= 0) {
      up = true;
    }
  }
  pixels.setBrightness(fade);
}

void Mode() {
  if (irrecv.decode(&results)){
        switch(results.value){
          case 0xFF629D: break;   //^
          case 0xFF22DD: cycleDown(); break;   //<
          case 0xFF02FD: if (relayState == LOW){relayState = HIGH;} else if (relayState == HIGH){relayState = LOW;} digitalWrite(relay,relayState); break;  //OK
          case 0xFFC23D: cycleUp(); break;   //>        
          case 0xFFA857: break;   //V
          case 0xFF6897: mode = 1; break;   //1
          case 0xFF9867: mode = 2; break;   //2
          case 0xFFB04F: mode = 3; break;   //3
          case 0xFF30CF: mode = 4; break;   //4
          case 0xFF18E7: mode = 5; break;   //5
          case 0xFF7A85: mode = 6; break;   //6
          case 0xFF10EF: mode = 7; break;   //7
          case 0xFF38C7: mode = 8; break;   //8
          case 0xFF5AA5: break;   //9
          case 0xFF42BD: break;   //*
          case 0xFF4AB5: break;   //0
          case 0xFF52AD: break;   //#
        }
        irrecv.resume();
  }
}

void Show() { //Pushes RGB Values to Led
  pixels.setPixelColor(whichled, pixels.Color(red2, green2, blue2));
  pixels.show();  
}

void troubleShooting() {
      Serial.print("red ");
      Serial.println(red2);
      Serial.print("green ");
      Serial.println(green2);
      Serial.print("blue ");
      Serial.println(blue2);
      Serial.print("night ");
      Serial.println(night);
      Serial.print("whichled ");
      Serial.println(whichled);
      Serial.print("strength");
      Serial.println(strength);
}

// Setup and loop from here__________________________________________________________________

void setup(){
  pixels.begin(); // Initializes the NeoPixel library
  pixels.setBrightness(100); // Set pixel Brightness
  pinMode(relay, OUTPUT); // Sets relay pin to output
  digitalWrite(relay, relayState); // Writes the first relay state
  irrecv.enableIRIn(); // Enables the IR receiver
  irrecv.blink13(true); // Enables the Led on the IR receiver
  pinMode(RECV_PIN, INPUT_PULLUP);
  attachInterrupt(1,Mode,CHANGE);
  Serial.begin(9600); // Enables serial data output
  randomSeed(analogRead(1)); // Enables randomness
}

void loop(){
  while ((whichled < numOfLeds) && (mode == 1 )) { //Chasing Rainbow
    Colourm2();
    Show();
    color = color + increment;
    if (whichled == numOfLeds2) {
      color = color + increment;
      whichled = -1;
    }
    whichled++;
  }
  while ((whichled < numOfLeds) && (mode == 2 )) { //Full Rainbow
    Colourm2();
    Show();
    if (whichled == numOfLeds2) {
      color = color + increment;
      whichled = -1;
    }
    whichled++;
  }
  while ((whichled < numOfLeds) && (mode == 3 )) { //Static Color
    Colourm2();
    Show();
    if (whichled == numOfLeds2) {
      whichled = -1;
    }
    whichled++;
  }
  while ((whichled < numOfLeds) && (mode == 4 )) { //Fade
    Colourm2();
    Show();
    if (whichled == numOfLeds2) {
      whichled = -1;
      Fade();
    }
    whichled++;
  }
  while ((moment >= 0) && (mode == 5 )) { //Around the Globe
    Colourm2();
    red3 = red2;
    green3 = green2;
    blue3 = blue2;
    while (night < 5) {
      strength = (night / 5 + 0.2);
      red2 = (red3 * strength);
      green2 = (green3 * strength);
      blue2 = (blue3 * strength);
      night2 = night;
      whichled = (moment + night2) % (numOfLeds);
      Show();
      night++;
      delay(10);
    }
    night = -1;
    moment++;  
  }
  while ((moment >= 0) && (mode == 6 )) { //Night Rider
    Colourm2();
    red3 = red2;
    green3 = green2;
    blue3 = blue2;
    while (night < 5) {
      strength = (night / 5 + 0.2);
      red2 = (red3 * strength);
      green2 = (green3 * strength);
      blue2 = (blue3 * strength);
      night2 = night;
      whichled = (numOfLeds - (abs(((moment + night2) % (numOfLeds * 2)) - (numOfLeds))));
      Show();
      night++;
      delay(10);
    }
    night = -1;
    moment++;  
  }
  while ((moment >= 0) && (mode == 7 )) { //Around the Globe RGB
    Colourm2();
    red3 = red2;
    green3 = green2;
    blue3 = blue2;
    while (night < 5) {
      strength = (night / 5 + 0.2);
      red2 = (red3 * strength);
      green2 = (green3 * strength);
      blue2 = (blue3 * strength);
      night2 = night;
      whichled = (moment + night2) % (numOfLeds);
      Show();
      night++;
      delay(10);
    }
    night = -1;
    moment++;  
    color = color + increment;  
  }
  while ((moment >= 0) && (mode == 8 )) { //Night Rider RGB
    Colourm2();
    red3 = red2;
    green3 = green2;
    blue3 = blue2;
    while (night < 5) {
      strength = (night / 5 + 0.2);
      red2 = (red3 * strength);
      green2 = (green3 * strength);
      blue2 = (blue3 * strength);
      night2 = night;
      whichled = (numOfLeds - (abs(((moment + night2) % (numOfLeds * 2)) - (numOfLeds))));
      Show();
      night++;
      delay(10);
    }
    night = -1;
    moment++;
    color = (2 * whichled / 60);
  }
}


It's quite a long code, designed to control a ws2812b led strip to have multiple modes of lighting.
It uses a power supply unit to power the strip (not the arduino), which is turned on and off with a relay.
An IR receiver receives commands from a remote, which are processed in the void "Mode".
The constants for the remote are annotated with // Remote, the behaviour of the pin (including the interrupt) is defined in the void "setup".

I'd like the strip to act the following way. I press a button on the remote, the receiver registers, and tells the arduino to change the mode of the strip. The mode of the strip changes almost immediately.

How it currently behaves: The remote works while mode == 0, so when the led strip isn't displaying. While this is true, I can turn on and off the relay, and with it the power supply. When mode is not 0, so the led strip is displaying something, the arduino no longer responds to the remote. The light on the IR receiver does light up.

When I remove the attachInterrupt from the script, the arduino doens't respond at all to the remote, which is to be expected.

thehardwareman

Check Arduino reference.
On a nano or Uno you can only use port 2 or 3 when using attachInterrupt.

langeleipe

#2
Jan 26, 2020, 02:30 pm Last Edit: Jan 26, 2020, 02:37 pm by langeleipe
Thanks for the reply.

I am aware not all pins are able to interrupt.
I am using an arduino mega, with the ir receiver plugged in to pin 3.
In the attachInterrupt command I've used interrupt pin 1 (pin 0 is digital pin 2, pin 1 is digital pin 3).
From what I read pin 3 on the mega should be able to interrupt, and this is a correct way to define it (although not the most used way). This exactly is stated in your link.

gfvalvo

#3
Jan 26, 2020, 02:56 pm Last Edit: Jan 26, 2020, 03:12 pm by gfvalvo
First, the programming structures you refer to as "voids" are Functions. The "void" designation simply states that the particular function does not return a value.

Second, there are no such things as "Interrupt Pins". The argument of the attachInterrupt is the Interrupt Number as defined by the processor's architecture. Digital Pins are sections of processor I/O Ports connected to physical pins on the board. Their numbers typically match those shown on the board's silk screen. While you can use the raw interrupt number in the attachInterrupt function, it's much better to go through the digitalPinToInterrupt macro which takes the physical pin number as its argument. That way, the number in your code will match the number on the board.

Finally, attachInterrupt does work exactly as documented. The problem you're seeing may be due to the fact that the Adafruit_NeoPixel library must disable interrupts while sending data to the LEDs. This is to ensure stringent timing protocol of these addressable LEDs is met.
No technical questions via PM. They will be ignored. Post your questions in the forum so that all may learn.

johnwasser

Maybe try without the interrupt and without while loops:

Code: [Select]

//Libraries from here___________________________________________________________________________


#include <IRremote.h>
#include <Adafruit_NeoPixel.h>


//Constants from here___________________________________________________________________________




//Ledstrip


const int dinPin = 2;
const int numOfLeds = 288;
const int numOfLeds2 = (numOfLeds - 1);
Adafruit_NeoPixel pixels = Adafruit_NeoPixel(numOfLeds, dinPin, NEO_GRB + NEO_KHZ800);


//Rainbow Calculations


const float a = 127.5;
const int b = 255;
const float pi = 3.1415926535897932384626433832795;
const float c_red = (pi / 3);
const float c_green = pi;
const float c_blue = (5 * pi / 3);
const float d = pi;


//Relay


int relay = 4;
volatile byte relayState = LOW;


//Remote


const int RECV_PIN = 3;
IRrecv irrecv(RECV_PIN);
decode_results results;


// RGB Color Values


float red = 0;
float green = 0;
float blue = 0;
float rgb = 0;
int red2 = 0;
int green2 = 0;
int blue2 = 0;
float red3 = 0;
float green3 = 0;
float blue3 = 0;


//Pixel Selection


volatile float increment = 0.033333333;
volatile float color = 0;
float whichled = 0;
volatile int mode = 10;
int fade = 0;
boolean up = true;
boolean left = true;
float night = 0;
int moment = 0;
float strength = 0;
int night2 = 0;


// Voids start here____________________________________________________________________________


void Colourm2() { //Calculate Color With Brightness
  red = a + b * sin(c_red + d * color);
  if (red < 0) {
    red = 0;
  } else if (red > 255) {
    red = 255;
  }
  green = a + b * sin(c_green + d * color);
  if (green < 0) {
    green = 0;
  } else if (green > 255) {
    green = 255;
  }
  blue = a + b * sin(c_blue + d * color);
  if (blue < 0) {
    blue = 0;
  } else if (blue > 255) {
    blue = 255;
  }
  rgb = red + green + blue;
  red2 = red * 255 / rgb;
  green2 = green * 255 / rgb;
  blue2 = blue * 255 / rgb;
}


void cycleDown() { //Cycles Through The Colors
  color = (color - (increment * 5));
}


void cycleUp() { //Cycles Through The Colors
  color = (color + (increment * 5));
}


void Fade() { //Cycles Through Brightness
  if (up == true) {
    fade = fade + 5;
    if (fade >= 100) {
      up = false;
    }
  } else if (up == false) {
    fade = fade - 5;
    if (fade <= 0) {
      up = true;
    }
  }
  pixels.setBrightness(fade);
}


void Show() { //Pushes RGB Values to Led
  pixels.setPixelColor(whichled, pixels.Color(red2, green2, blue2));
  pixels.show();
}


void troubleShooting() {
  Serial.print("red ");
  Serial.println(red2);
  Serial.print("green ");
  Serial.println(green2);
  Serial.print("blue ");
  Serial.println(blue2);
  Serial.print("night ");
  Serial.println(night);
  Serial.print("whichled ");
  Serial.println(whichled);
  Serial.print("strength");
  Serial.println(strength);
}


// Setup and loop from here__________________________________________________________________


void setup() {
  pixels.begin(); // Initializes the NeoPixel library
  pixels.setBrightness(100); // Set pixel Brightness
  pinMode(relay, OUTPUT); // Sets relay pin to output
  digitalWrite(relay, relayState); // Writes the first relay state
  irrecv.enableIRIn(); // Enables the IR receiver
  irrecv.blink13(true); // Enables the Led on the IR receiver
  pinMode(RECV_PIN, INPUT_PULLUP);
  Serial.begin(9600); // Enables serial data output
  randomSeed(analogRead(1)); // Enables randomness
}


void loop() {
  if (irrecv.decode(&results)) {
    switch (results.value) {
      case 0xFF629D: break;   //^
      case 0xFF22DD: cycleDown(); break;   //<
      case 0xFF02FD: if (relayState == LOW) {
          relayState = HIGH;
        } else if (relayState == HIGH) {
          relayState = LOW;
        } digitalWrite(relay, relayState); break; //OK
      case 0xFFC23D: cycleUp(); break;   //>
      case 0xFFA857: break;   //V
      case 0xFF6897: mode = 1; break;   //1
      case 0xFF9867: mode = 2; break;   //2
      case 0xFFB04F: mode = 3; break;   //3
      case 0xFF30CF: mode = 4; break;   //4
      case 0xFF18E7: mode = 5; break;   //5
      case 0xFF7A85: mode = 6; break;   //6
      case 0xFF10EF: mode = 7; break;   //7
      case 0xFF38C7: mode = 8; break;   //8
      case 0xFF5AA5: break;   //9
      case 0xFF42BD: break;   //*
      case 0xFF4AB5: break;   //0
      case 0xFF52AD: break;   //#
    }
    irrecv.resume();
  }
  
  switch (mode)
  {
    case 1:
      { //Chasing Rainbow
        Colourm2();
        Show();
        color = color + increment;
        if (whichled == numOfLeds2) {
          color = color + increment;
          whichled = -1;
        }
        whichled++;
      }
      break;


    case 2:
      { //Full Rainbow
        Colourm2();
        Show();
        if (whichled == numOfLeds2) {
          color = color + increment;
          whichled = -1;
        }
        whichled++;
      }
      break;


    case 3:
      { //Static Color
        Colourm2();
        Show();
        if (whichled == numOfLeds2) {
          whichled = -1;
        }
        whichled++;
      }
      break;


    case 4:
      { //Fade
        Colourm2();
        Show();
        if (whichled == numOfLeds2) {
          whichled = -1;
          Fade();
        }
        whichled++;
      }
      break;


    case 5:
      { //Around the Globe
        Colourm2();
        red3 = red2;
        green3 = green2;
        blue3 = blue2;
        while (night < 5) {
          strength = (night / 5 + 0.2);
          red2 = (red3 * strength);
          green2 = (green3 * strength);
          blue2 = (blue3 * strength);
          night2 = night;
          whichled = (moment + night2) % (numOfLeds);
          Show();
          night++;
          delay(10);
        }
        night = -1;
        moment++;
      }
      break;


    case 6:
      { //Night Rider
        Colourm2();
        red3 = red2;
        green3 = green2;
        blue3 = blue2;
        while (night < 5) {
          strength = (night / 5 + 0.2);
          red2 = (red3 * strength);
          green2 = (green3 * strength);
          blue2 = (blue3 * strength);
          night2 = night;
          whichled = (numOfLeds - (abs(((moment + night2) % (numOfLeds * 2)) - (numOfLeds))));
          Show();
          night++;
          delay(10);
        }
        night = -1;
        moment++;
      }
      break;


    case 7:
      { //Around the Globe RGB
        Colourm2();
        red3 = red2;
        green3 = green2;
        blue3 = blue2;
        while (night < 5) {
          strength = (night / 5 + 0.2);
          red2 = (red3 * strength);
          green2 = (green3 * strength);
          blue2 = (blue3 * strength);
          night2 = night;
          whichled = (moment + night2) % (numOfLeds);
          Show();
          night++;
          delay(10);
        }
        night = -1;
        moment++;
        color = color + increment;
      }
      break;


    case 8:
      { //Night Rider RGB
        Colourm2();
        red3 = red2;
        green3 = green2;
        blue3 = blue2;
        while (night < 5) {
          strength = (night / 5 + 0.2);
          red2 = (red3 * strength);
          green2 = (green3 * strength);
          blue2 = (blue3 * strength);
          night2 = night;
          whichled = (numOfLeds - (abs(((moment + night2) % (numOfLeds * 2)) - (numOfLeds))));
          Show();
          night++;
          delay(10);
        }
        night = -1;
        moment++;
        color = (2 * whichled / 60);
      }
      break;
  }
}
Send Bitcoin tips to: 1G2qoGwMRXx8az71DVP1E81jShxtbSh5Hp

langeleipe

Thanks for your reply gfvalvo.

I'm sorry for not being familiar with the correct names for things. I'm not a native english speaker and not an experienced programmer, but I'll edit my posts shortly.

I've tried the syntax you suggested "digitalPinToInterrupt(3)" instead of "1", but it yields the same results. It is easier for the user to use digitalPinToInterrupt(3), but the arduino doesn't mind, I believe.

Finally, I've used the Adafruit_NeoPixel library before with 60 leds and 2 push buttons instead of a remote. That worked flawlessly, suggesting the library doesn't disable interrupts.

I believe the primairy difference between my last setup and the current setup, about which my question is about, is the data on the interrupt pin. With the push buttons, the information the pin received was either high or low, pushed or not pushed. With the remote it is more complicated. I thought that may be the explanation for why this doesn't work. But it works when the leds aren't receiveing data, so the interrupt works sometimes. That's what makes it hard to understand for me.

Danois90

First you attach an IR-receiver to pin 3 (RECV_PIN), then you change the pin's mode and finally you attach an interrupt to the pin as well. Cannot see how that is supposed to work?
Instead of mocking what's wrong, teach what's right! ;)
When you get help, remember to thank the helper and give some karma!
Please, do NOT send me any private messages!!

gfvalvo

That worked flawlessly, suggesting the library doesn't disable interrupts.
I can guarantee you that the Adafruit_NeoPixel library disables interrupts while sending data to the LEDs. I'm looking its source code right now.
No technical questions via PM. They will be ignored. Post your questions in the forum so that all may learn.

langeleipe

Thanks for the reply Johnwasser,

Let me start to say that this is a genius way to rewrite the code.
But, the program still acts the same. While the mode is 0, I can turn the relay on and off, and select a mode once. After that, the mode is not 0, and the arduino doesn't respond to the remote anymore.

Although the code doesn't resolve my problem, thank you for rewriting it (you shouldn't have, you should've told me to do it). It gives me more insight in the problem, without the interrupt, the problem persists. Thus the interrupt is not to be blamed. But, I can't say it's clear to me what is.

langeleipe

First you attach an IR-receiver to pin 3 (RECV_PIN), then you change the pin's mode and finally you attach an interrupt to the pin as well. Cannot see how that is supposed to work?
Thanks for the reply.
I've spent a few days googling this problem, and someone suggested on a similar problem to define the pin's mode to as input_pullup. I've also seen examples where people get flamed because they don't define their pins well enough. They suggested It's mandatory to define the pin's mode in setup.
Defining the pin is interrupting seperately is also common practise, so I don't entirely get what the problem is with the way it's all defined in my code.

langeleipe

I can guarantee you that the Adafruit_NeoPixel library disables interrupts while sending data to the LEDs. I'm looking its source code right now.

Thank you for your reply.
Although this sounds odd to me, given that my previous setup worked flawlessly, it would still not solve my problem. Even when I hold the button on the remote, so I'm sure to hit the delay portion in some parts of my code, the mode still doesn't change. Also, Johnwasser has rewritten the code, which doesn't use interrupts, but the code still behaves the same.

The conclusion so far is that the code is faulty, but it isn't the interrupt that causes it.

david_2018

From the README file in the IRremote library:

Quote
Whether you use the Adafruit Neopixel lib, or FastLED, interrupts get disabled on many lower end CPUs like the basic arduinos. In turn, this stops the IR interrupt handler from running when it needs to. There are some solutions to this on some processors, [see this page from Marc MERLIN](http://marc.merlins.org/perso/arduino/post_2017-04-03_Arduino-328P-Uno-Teensy3_1-ESP8266-ESP32-IR-and-Neopixels.html)
I'm surprised it works at all from within an interrupt service routine, since interrupts are disabled at that point.

Your previous code with push buttons works with interrupts because the arduino will detect the interrupt condition and execute the interrupt as soon as interrupts are re-enabled, and a button press is slow enough that you don't have to deal with the possibility of multiple interrupts occurring while interrupts are disabled.  An IRremote is a much faster signal, and could generate multiple interrupts while the LEDs are being updated, and the interrupts have to be handled quickly since the exact timing of the interrupt is significant in processing the IR signal.

langeleipe

Thanks for your reply.

That explains the problems I am experiencing perfectly. I'll see if I can get the code of Johnwasser to work then, as it doesn't rely on interrupt to work, and it is geniously written, as stated earlier.

Danois90

I've spent a few days googling this problem, and someone suggested on a similar problem to define the pin's mode to as input_pullup. I've also seen examples where people get flamed because they don't define their pins well enough. They suggested It's mandatory to define the pin's mode in setup.
Defining the pin is interrupting seperately is also common practise, so I don't entirely get what the problem is with the way it's all defined in my code.
AFAICT the library sets the pin mode to INPUT. This means that it is LOW by default and HIGH when the IR is receiving a signal. Setting the pin mode to INPUT_PULLUP after the IR-receiver is initialized will have the cause that the library always sees the IR as receiving.

Attaching an interrupt to the same pin as the IR will ensure that the interrupt handler is triggered every time the pin state changes - this means that the handler is executed multiple times during reception of one command, this is not really a good idea. But since the pin is pulled high, its state probably never changes anyway, so....
Instead of mocking what's wrong, teach what's right! ;)
When you get help, remember to thank the helper and give some karma!
Please, do NOT send me any private messages!!

langeleipe

AFAICT the library sets the pin mode to INPUT. This means that it is LOW by default and HIGH when the IR is receiving a signal. Setting the pin mode to INPUT_PULLUP after the IR-receiver is initialized will have the cause that the library always sees the IR as receiving.

Attaching an interrupt to the same pin as the IR will ensure that the interrupt handler is triggered every time the pin state changes - this means that the handler is executed multiple times during reception of one command, this is not really a good idea. But since the pin is pulled high, its state probably never changes anyway, so....
Thanks for your reply.
I used to have it defined in constants, and the interrupt in the setup. This worked fine with my previous setup with push buttons instead of a remote. After that didn't work, I tried defining it as an input in the setup, and as input_pullup after. It all made no differnce in how it all worked. The remote still functioned fine as long as mode was 0 (this is explained by david_2018 and probably solved by johnwasser, but I still have to look into that).
So the problems you expect to happen don't happen. The arduino is a pretty forgiving kind of device, I find.

Go Up