Blocking code with IR Receiver and Fast Led library

Hello,

I am trying to write a sketch to pilot an LED strip (WS2811B) with an IR controller.

To do so I’ve created a function to get the IR signal and debounce it:

byte IRRemote(){

  static unsigned long lastDebounceTime = 0;
  static unsigned long debounceDelay = 500;
  
  byte irVal = 255;
  byte irValBefore = 254;
  if (IrReceiver.decode()){ 
    if ((currentMillis - lastDebounceTime) > debounceDelay){
      lastDebounceTime = millis();   
      irVal = (IrReceiver.decodedIRData.command);
      Serial.print("IR Val Before: ");Serial.println(irValBefore);
      Serial.print("IR Val: ");Serial.println(irVal);

        if (irVal == 67 && ledMode == 0 && irVal!=irValBefore){    
          ledMode = 1;
          Serial.println("LED Mode ON");
          irValBefore = irVal;      
        }
        if (irVal == 67 && ledMode == 1 && irVal!=irValBefore){    
          ledMode = 0;
          Serial.println("LED Mode OFF");
          irValBefore = irVal;      
        }
      }
    IrReceiver.resume();
  }
}

One to pilot the LED:

void FLed (byte ledMode){
  byte gHue = 0;
  if( ledMode == 1){
    fill_rainbow( leds, NUM_LEDS, gHue, 7);
    FastLED.show();
  }
  if (ledMode == 0){
    FastLED.clear();
    FastLED.show();
  }
}

Here is the main tab:

#include <IRremote.h>
#include <FastLED.h>

// IR Variables
const byte IR_RECEIVE_PIN = 12;

// FastLED Variables
#define NUM_LEDS 46
#define DATA_PIN 5
#define LED_TYPE    WS2811
#define COLOR_ORDER BRG
CRGB leds[NUM_LEDS];
#define BRIGHTNESS          96
#define FRAMES_PER_SECOND  120

byte ledMode = 0;

// Time Variable
unsigned long currentMillis;

// Methods
byte  IRRemote();
void  FLed();

void setup() {
   delay(3000); 
   Serial.begin(9600);
   Serial.println("IR and LED TEST");
   IrReceiver.begin(IR_RECEIVE_PIN, ENABLE_LED_FEEDBACK);

   FastLED.addLeds<LED_TYPE,DATA_PIN,COLOR_ORDER>(leds, NUM_LEDS);
   FastLED.clear();
   for(int led = 0; led < NUM_LEDS; led++) { 
    leds[led] = CRGB::White; 
   }
   FastLED.setMaxRefreshRate(400);
   FastLED.show();
   delay(1000);
   FastLED.clear();
   FastLED.show();

}

void loop() {
IRRemote();
FLed(ledMode);
currentMillis = millis();

}

Now, the problem is that when I only use the IR function (commenting “// FLed (ledMode)” in void loop()), the signal is read correctly and the states switch correctly.
When I enable FLed(ledMode) the code blocks in a weird (I mean, I cannot understand why…) way: the IR signal is read but, regardless of the button I press, it always return 0.

I clearly understand that the problem is the FLed() coding as it blocks the rest.
I see that I can solve the second part of FLed() (to swich the strip off) by adding a state machine BUT I cannot find a way to do the same for the first part, when I need the LEDs to keep on going while ledMode is ON.
I could use a while cycle but again, I would create blocking code and I don’t want to do that…

I am going crazy over this…
Thank you very much for your precious help!

Ale

Try removing the 'debounce' timing. Or at least update 'currentMillis' BEFORE you use it in 'IRRemote()'.

Thank you for your answer!

If I don't debounce the signal I always end up with multiple readings.

I tried this:

byte IRRemote(){
  currentMillis = millis();
  static unsigned long lastDebounceTime = 0;
  static unsigned long debounceDelay = 500;
  
  byte irVal = 255;
  byte irValBefore = 254;
  if (IrReceiver.decode()){ 
    if ((currentMillis - lastDebounceTime) > debounceDelay){
      lastDebounceTime = millis();   
      irVal = (IrReceiver.decodedIRData.command);
      Serial.print("IR Val Before: ");Serial.println(irValBefore);
      Serial.print("IR Val: ");Serial.println(irVal);

        if (irVal == 67 && ledMode == 0 && irVal!=irValBefore){    
          ledMode = 1;
          Serial.println("LED Mode ON");
          irValBefore = irVal;      
        }
        if (irVal == 67 && ledMode == 1 && irVal!=irValBefore){    
          ledMode = 0;
          Serial.println("LED Mode OFF");
          irValBefore = irVal;      
        }
      }
    IrReceiver.resume();
  }
}

and removed currentMillis = millis() from void loop() but nothing has changed.

Any ideas about how I could solve the FLed first part problem..?

Many thanks!

Put an 'else' between the two 'if's. The first 'if' sees that (irVal == 67 && ledMode == 0 && irVal != 254) so it changes ledMode to 1. Then the second 'if' sees that (irVal == 67 && ledMode == 1 && irVal != 254) so it immediately changes the ledMode BACk to 0. Put an 'else' between the two so it only does the second one if it doesn't do the first one.

Another way to do it:

        if (irVal == 67){
          if (ledMode == 0) {     
            ledMode = 1;
            Serial.println("LED Mode ON");
          } else {
            ledMode = 0;
            Serial.println("LED Mode OFF"); 
          }
        }

I tried but it still doesn't work...
Actually it was switching between the two modes before without problems but I see your point.

Still, once I enable the function FLed() it reads the IR signal always as "0".

Thanks a lot for your help!

As far as i know, once IrReceiver.resume();has been called, interrupts need on all the time until if (IrReceiver.decode()){is called. FastLED.show();Turns them off for the time that it takes to send the signal to the LEDs. I am not saying that it can't be done, but it is hard to get an IR remote in combination with ws281x strip to work reliably. Anyway, ideally you don't resume reading the IR until show() has been called, and don't call show again until decode() has been called.

Thank you for all your answers!

I have tried this:

#include <IRremote.h>
#include <FastLED.h>

// IR Variables
const byte IR_RECEIVE_PIN = 12;

// FastLED Variables
#define NUM_LEDS 46
#define DATA_PIN 5
#define LED_TYPE    WS2811
#define COLOR_ORDER BRG
CRGB leds[NUM_LEDS];
#define BRIGHTNESS          96
#define FRAMES_PER_SECOND  120

byte MusicMode = 0;
byte ledColor = 0;
// Time Variable
unsigned long currentMillis;

// Methods
byte  IRRemote();
//void  FLed();
void staticLed();

void setup() {
   delay(3000); 
   Serial.begin(9600);
   Serial.println("IR and LED TEST");
   IrReceiver.begin(IR_RECEIVE_PIN, ENABLE_LED_FEEDBACK);

   FastLED.addLeds<LED_TYPE,DATA_PIN,COLOR_ORDER>(leds, NUM_LEDS);
   FastLED.clear();
   for(int led = 0; led < NUM_LEDS; led++) { 
    leds[led] = CRGB::White; 
   }
   FastLED.setMaxRefreshRate(400);
   FastLED.show();
   delay(1000);
   FastLED.clear();
   FastLED.show();

}

void loop() {
IRRemote();
//FLed(MusicMode);
staticLed();
}

byte IRRemote(){
  currentMillis = millis();
  static unsigned long lastDebounceTime = 0;
  static unsigned long debounceDelay = 300;
  
  byte irVal = 255;
  byte irValBefore = 254;    // NOT USED IN THIS CASE
  if (IrReceiver.decode()){ 
    if ((currentMillis - lastDebounceTime) > debounceDelay){
      lastDebounceTime = millis();   
      irVal = (IrReceiver.decodedIRData.command);
      Serial.print("IR Val Before: ");Serial.println(irValBefore);
      Serial.print("IR Val: ");Serial.println(irVal);

        if (irVal == 67 && MusicMode == 0){    
          MusicMode = 1;
          Serial.println("LED Mode ON");
          irValBefore = irVal;      
        }
        else if (irVal == 67 && MusicMode == 1){    
          MusicMode = 0;
          Serial.println("LED Mode OFF");
          irValBefore = irVal;      
        }
        else if (irVal == 9){
            irValBefore = irVal;
            ledColor++;
            if (ledColor == 7){ledColor = 0;}
            Serial.print("ledColor: ");Serial.println (ledColor);       
        }
      }
    IrReceiver.resume();
  }
}

/*void FLed (byte MusicMode){
  byte gHue = 0;
  if( MusicMode == 1){
    fill_rainbow( leds, NUM_LEDS, gHue, 7);
    FastLED.show();
  }
  if (MusicMode == 0){
    FastLED.clear();
    FastLED.show();
  }
}*/

void staticLed(){
  static byte ledColorBefore = 255;
  if (ledColor != ledColorBefore){
    if (ledColor == 0){
      for(int dot = 0; dot < NUM_LEDS; dot++) { 
        leds[dot] = CRGB::Black;
        FastLED.show();
      }
    }
    else if (ledColor == 1){
      for(int dot = 0; dot < NUM_LEDS; dot++) { 
        leds[dot] = CRGB::Red;
        FastLED.show();
      }
    }
    else if (ledColor == 2){
      for(int dot = 0; dot < NUM_LEDS; dot++) { 
        leds[dot] = CRGB::Green;
        FastLED.show();
      }
    }
    else if (ledColor == 3){
      for(int dot = 0; dot < NUM_LEDS; dot++) { 
        leds[dot] = CRGB::Blue;
        FastLED.show();
      }
    }
    else if (ledColor == 4){
      for(int dot = 0; dot < NUM_LEDS; dot++) { 
        leds[dot] = CRGB::Cyan;
        FastLED.show();
      }
    }
    else if (ledColor == 5){
      for(int dot = 0; dot < NUM_LEDS; dot++) { 
        leds[dot] = CRGB::Magenta;
        FastLED.show();
      }
    }
    else if (ledColor == 6){
      for(int dot = 0; dot < NUM_LEDS; dot++) { 
        leds[dot] = CRGB::Yellow;
        FastLED.show();
      }
    }
  }
}

But I really don’t get why my code gets blocked.
I know the part that is creating problem is staticLed() but I don’t understand why…
I’ve implemented it with a state machine solution which recalls different if/else if solutions accordingly to the change of the variables ledColor / ledColorBefore but the leds do not turn as, regardless of which button I press on the IR transmitter it is always read 0.

How do I implement such a solution Deva_Rishi?
I’m not an expert at all, I thought that, if I wrote two functions and called them one after another in void loop() they would “play” sequentially, therefore, when one is played the other one is not, isn’t it that way?

Many thanks for all the explanations!

I’ve found this article…
I therefore guess there is no way around with the arduino nano I am using…

Ale_V:
I've found this article...
I therefore guess there is no way around with the arduino nano I am using....

I think there are two good choices for you, as mentioned in that article: (1) Move to SPI-based LEDs such as APA102. (2) Move to a processor that can drive WS2811B-type LEDs using DMA. Besides the ESP chips mentioned, there is also a library for for ARM-based Teensy boards to drive them from a USART via DMA. This library integrates well with FastLED.

(1) Move to SPI-based LEDs such as APA102.

I have seen that option tried out before, and it turns out FastLED still turns the interrupts off (only momentarily though)

Move to a processor that can drive WS2811B-type LEDs using DMA. Besides the ESP chips mentioned,

That would be the preferred choice imo. Still i would switch to NeoPixelBus library. There is the issue of the ESP boards using a logic level of 3.3v which is actually not enough (usually) but a simple TTL chip is an easy solution.

I thought that, if I wrote two functions and called them one after another in void loop() they would "play" sequentially, therefore, when one is played the other one is not, isn't it that way?

Yes it is, but the IrRemote library uses interrupts to measure the length of the pulses that come in and does all this in the background. Of course the IR reception only gets broken if the show() command gets send during it. Adding a delay (25ms or so) in your code to reduce the framerate manually will probably already increase the responsiveness.

Deva_Rishi:
I have seen that option tried out before, and it turns out FastLED still turns the interrupts off (only momentarily though)

I've looked at the code (for Teensy anyway) and the percentage of time they're shut off (to set up the SPI transfer) is inconsequential. And, the interrupts would be queued and serviced immediately afterward. Totally unnoticeable on the human timescale.

EDIT:
PS -- I've used Teensy 3.2 + FastLED + APA102 in a POV project: GitHub - gfvalvo/TeensyPOV: POV Project on Teensy 3.2
The code services over 10,000 timer interrupts per second while updating the APA102 string 512 times per blade revolution.

Totally unnoticeable on the human timescale.

We've had this discussion before in another topic, and then it turned out that FastLED did turn the interrupts 'off' even if it was only briefly. You were in that topic if i am not mistaken. Mind you the OP of that topic was using FastLED.delay(), which is just a really bad idea. What bugs me with this is that interrupts are being turned off at all, since there should really be no need. We are not dealing with a human timescale. IR is not visible to the human eye, and at the speed that it is being transferred, it wouldn't be visible to the human eye even if it was in a visible part of the spectrum. I am not saying you can't get it to work, i am just saying that if you are willing to spend the money for a teensy, feel free, but if you can find 'any' and i do mean any ESP and use neopixelbus DMA You will have totally solved the interrupts issue, though i admit i am not even sure that the IrRemote library is supported on the ESP core. A teensy has quite a powerful processor, and you do have to pay for that.

Thank you very much for all your responses!
I have to admit that many thing you are discussing are way above my current knowledge and so I guess I've grasped the general idea but not the specific content.

In the meantime I've found a solution that it is not ideal from a technical poit of view but it works for my little project.
Basically, as when the LEDs are switched ON the IR receiver still correctly get the signal, but it always return "0", I've just created an IF statement which switch the LEDs off when the signal is 0...pretty silly but it works for what I need...

Thank you very much for your support!

Also,

as I was getting sometimes a "0" signal even if no button were pressed I've found this scheme, in this topic here on the forum.
I've tried if but, the IR receiver couldn't read the signal any longer.
Just to make sure I did it right, I have a 5V connected to the Vcc pin or the receiver and, in between, I should put a 100 ohms resistor and a capacitor 100uF (the negative pin of the capacitor goes to ground). Is it correct?

Eventually I've solved the problem by not powering the receiver directly from arduino but from the step down converter that I use to bring the 12V current, coming from an AC/DC transformer, to 5V but still I was curious about this scheme.

Thanks a lot!

This topic was automatically closed 120 days after the last reply. New replies are no longer allowed.