Using an interrupt for my IR receiver

Hello

I have a routine that detects incoming IR (IRremote library). When it's the only thing in the main loop, it works fine.

But, when the loop starts doing anything else, it misses the start of the incoming IR data, therefore reporting an unknown IR code.

So, I need to add an interrupt to catch any incoming IR, but I just cannot get the code correct.
The whole program is long and messy, but here is the relevant part (The interrupt is working on pin 2 - checked that with a serial.print statement).

#include <IRremote.h>                                        // IR remote library

const int RECV_PIN = 2;                                       // IR pin
IRrecv irrecv(RECV_PIN);                                      // Set up the IR decoder
decode_results results;

// IR variables ---------------------------

byte IRcodeA;
byte IRcodeB;
byte IRcodeC;
byte IRcodeD;
unsigned long ReassembledData;
byte IRbits;                                                                                 // Length of the IR code in bits
int toggle = 0;                                                                             // The RC5/6 toggle state


void setup() {

Serial.begin(9600);  
pinMode(RECV_PIN, INPUT_PULLUP);

IrReceiver.begin(RECV_PIN);
attachInterrupt(digitalPinToInterrupt(2), DecodeIR, FALLING); 

}

void loop(){
    //if (irrecv.decode(&results)) {
    //  Serial.println("Incoming IR code...");
    //  DecodeIR();                                                 // Go get IR code if not using interrupt.
    }
}

void DecodeIR() {

  if (irrecv.decode(&results)) {                                                         // Get the make of the signal
    switch (results.decode_type) {
      case NEC: Serial.println("Signal type: NEC"); break ;
      case SONY: Serial.println("Signal type:SONY"); break ;
      case RC5: Serial.println("Signal type:RC5"); break ;
      case RC6: Serial.println("Signal type:RC6"); break ;
      case DISH: Serial.println("Signal type:DISH"); break ;
      case SHARP: Serial.println("Signal type:SHARP"); break ;
      case JVC: Serial.println("Signal type:JVC"); break ;
      case SANYO: Serial.println("Signal type:SANYO"); break ;
     case MITSUBISHI: Serial.println("Signal type:MITSUBISHI");break ;
      case SAMSUNG: Serial.println("Signal type:SAMSUNG"); break ;
      case LG: Serial.println("Signal type:LG"); break ;
      case WHYNTER: Serial.println("Signal type:WHYNTER"); break ;
      case AIWA_RC_T501: Serial.println("Signal type:AIWA_RC_T501"); break ;
      case PANASONIC: Serial.println("Signal type:PANASONIC"); break ;
      case DENON: Serial.println("Signal type:DENON"); break ;
      default:
      case UNKNOWN: Serial.println("Signal type:UNKNOWN"); break ;
    }
    irrecv.resume();
  }

  delay(200);

  IRcodeA = ((results.value >> 0) & 0xFF);                                                                                                      // Convert to 4x bytes for storage in EEPROM/SD card
  IRcodeB = ((results.value >> 8) & 0xFF);
  IRcodeC = ((results.value >> 16) & 0xFF);
  IRcodeD = ((results.value >> 24) & 0xFF);
  IRbits = (results.bits);                                                                                                                      // Get the length of the IR code

  Serial.print("IR data: "); Serial.println(results.value, HEX);

  ReassembledData = ((IRcodeA << 0) & 0xFF) + ((IRcodeB << 8) & 0xFF00) + ((IRcodeC << 16) & 0xFF0000) + ((IRcodeD << 24) & 0xFF000000);        // Reassemble the bytes into a HEX code

  Serial.print("IR code length = "); Serial.println(results.bits);

  Serial.print("The decimal value of IRcodeA is: "); Serial.println(IRcodeA);
  Serial.print("The decimal value of IRcodeB is: "); Serial.println(IRcodeB);
  Serial.print("The decimal value of IRcodeC is: "); Serial.println(IRcodeC);
  Serial.print("The decimal value of IRcodeD is: "); Serial.println(IRcodeD);

  Serial.print("The re-assembled HEX value from the decoded bytes is: "); Serial.println(ReassembledData, HEX);

  irrecv.resume();                                                                                                                              // Receive the next value
}

I just have not got something right somewhere. It goes to the routine, but reports no data.

void loop(){
    //if (irrecv.decode(&results)) {
    //  Serial.println("Incoming IR code...");
    //  DecodeIR();                                                 // Go get IR code if not using interrupt.
    }
}

That code doesn't compile, so no surprise it doesn't do anything
(No, I didn't try)

Don't do Serial I/O in interrupt context

Some time back, I wrote an NEC IR library with a bare minimum of functionality.

It uses an interrupt.

You can try it or use it as a basis for something new.

You are using IRRemote control library, I think it adds interrupt already

What version of the IRremote library do you have installed? The posted code is written for an older version (<3.0) and may not work well with the new library.

I have literally tried all the variations of that library!
That code is lifted from the examples (except the interrupt part)

Don't think it does use interrupts? Maybe I am wrong.

The commented out part of the code works (when you don't declare the interrupt at the start).
It jumps out of the main loop and decodes the data fine.

But... if I add other commands to the main loop, then it appears to 'miss' the start of the data (well, it reports unknown data all the time).
I can only assume it's missing the start of the transmitted signal.

Not sure what you mean by 'Don't do Serial I/O in interrupt context'... the serial.print command was just to check it was working.

I'll try some other libraries

Just tried IRlib.h instead.
Had to change the interrupt pin in IRlibtimer.h to get it working (timer clash)

Same issue.

It's the data going to my RGB ws2812 leds... they are messing it up. Stop the data stream and the IR reports correctly.

Hmm

OK. Thanks for everyones assistance.
I have come to the conclusion that the Mega just isn't up to the task of running RGB leds, decoding IR and a few other tasks.

Probably going to shift the project over to a Teensy 3.2 or a Raspberry Pi Zero.

Thanks

I mean "don't do Serial reads or writes in a function that is called from any interrupt vector, either directly or indirectly".

You didn't mention RGB LEDs in your original post.
The timing for these is critical, and interrupts are turned off when they're being updated, so it's not surprising the IR receiver didn't work.

Sorry. Didn't realise the RGB's would be an issue.

Well I would assume then if I am pretty much constantly updating the state/program of some RGB leds, then I cannot successfully decode an IR signal.

Is that because they may share an interrupt, or that they are clashing over who has priority.
In which case, is it possible to assign an interrupt to have priority?

If it's not possible, then either the LEDS or the IR needs shipping off to it's own processor.
Probably easier to just buy/make a separate IR receiver circuit.

Hmm

Interrupts are prioritised according to their position in the vector table, but no, the LEDs don't themselves use interrupts, the driver disables all interrupts during updates (because of extremely tight timing requirements) - the longer the LED string, the longer that interrupts are disabled.
Could you arrange for the updates to arrive via serial rather than IR?

If you use a raspberry Pi Pico processor PR2040 then the programmable I/O can be made into a state machine and can run sending data into the WS2812 without using any CPU time at all.
Mind you these are not that easy to program.

Thanks all.

I think these are my options then...

1/ Seperate Mini Pro to control the RGB leds, and link it to the Mega via serial to select the mode/output I require.

2/ Bluetooth module instead of IR (serial). Make a bluetooth remote instead.

3/ Err

So, looking like option 1. I want to use a cheap IR remote and not have to make a bluetooth app for this thing.

I am having the occasional moment where the RGBs are doing some random twinkling effect, even though the first command is to clear the LEDS. Not figured that out yet.
If I run the RGB's off their own Mini Pro, then I can 'switch on' the Pro Mini when required.

Just makes programming a bit more of a pain that's all.

Thanks chaps. Learn something new each day and all that

The snag with this is that reception of serial data is also disrupted by sending data to the WS2812 LEDs.

If you are using two processors you will have to communicate through SPI. I used an ATtiny to actually send the data to the LEDs and communicated the data using a bit banged SPI protocol. This worked because the SPI protocol is not disturbed by interrupting it to handle interrupts.

Goddam these RBG leds are a pain in the butt.

I have a mega.... and the Pro mini is doing nothing but running the Leds.
I'll just link them with half a dozen logic lines and use a logic table for the mode selection

You only need three, Data, Clock & Enable as you are only communicating in one direction.

Thats very helpful Mike. Many thanks :ok_hand:

OK. After an afternoon rewiring, it's worse than ever!

Found a Nano lying in my parts box. Installed the Nano, and moved all the code for the RGB leds onto that. All worked fine.

Linked it back to the main Mega using SPI and now I have all kinds of stuff occuring.

My project has 4x 8 digit LED displays running on Max7219's. These are those pre-made boards that just need an SPI bus.
They ran using the "LedControl.h" library. They are now not working, as calling SPI.begin() crashes them entirely.

I can only assume the LedControl library has already called all the SPI commands, because they communicate over SPI and the LedControl library examples don't call specific SPI commands (such as begin).

More annoying is the mega now will not upload unless I power down the project and remove then replace the USB programming lead everytime Otherwise it can't find the Mega.

Arduino's cause me so many issues (all user error I am sure). The time wasted is biblical.

So, I think I am going to abandon the Mega and go with something else. Not sure what yet.
Life's too short for this 'fix one thing and 3 others break' routine!

This library uses soft SPI! Try to use other than the hard SPI pins for it, then it should not interfere with SPI.begin() !

I think you are getting mixed up. I never said use the built in SPI, but bit bang the SPI. Do you want the code that goes with the schematic I posted?