Help with arduino Interrupts (for IR receiver)

Hello Everybody,

Going straight to the point, I am trying to make a project that does something and gets interrupted when the IR sensors detect infrared commands.

The problem I am having is that when I press any button on any remote control (this triggers the interruption, arduino stops completely, I even set a Serial.println(“Something”) as the first line in the interruption function and it doesn’t even print, which makes me think the problem is not with the loops inside the interrupt function.

In this code I am not keeping the values of the IR sensor, just the time it takes between each change (LOW to HIGH or HIGH to LOW) in the code, that time information should be enough for the simple things I have in mind.

Other thing I consider to do later is to change the “while not change” loops (the whiles inside the “while not ended”) conditions and remove the timeout constant and set it as a timer interruption which should make the code more efficient since it would spend less processing time in the while. The timer interruption would just change some boolean variable that would be checked by the “while not change” loops condition. The problem of that setup is that I would have to remove the noInterrupts(), but since all interrupts would be in a controlled environment I don’t think that will be an issue. What do you guys think?

Here is my code:

#include <LiquidCrystal.h>

#define SAMPLE_SIZE 64
#define IR_TIMEOUT 500 //milisseconds

const int ledPin = 13;
const int irSensorPin = 2;

LiquidCrystal lcd(12, 11, 6, 5, 4, 3);
unsigned int changeTimes[SAMPLE_SIZE];
unsigned int changeCounter;
unsigned long initTime;
unsigned long currentTime;
boolean ended;
unsigned int i;

void setup() {
  Serial.begin(115200);
  pinMode(ledPin, OUTPUT);
  pinMode(irSensorPin, INPUT);
  digitalWrite(ledPin, HIGH);
  attachInterrupt(0, receiveIR, LOW);
  lcd.begin(16, 2);
}

void receiveIR()
{
  Serial.println("Receiving IR transmission: ");
  noInterrupts();
  currentTime = initTime = millis();
  changeCounter = 0;
  ended = false;
  while(!ended)
  {
    if (digitalRead(irSensorPin) == HIGH)
      while ((digitalRead(irSensorPin) == HIGH) && (currentTime - initTime < IR_TIMEOUT))
        currentTime = millis();
    else
      while ((digitalRead(irSensorPin) == LOW) && (currentTime - initTime < IR_TIMEOUT))
        currentTime = millis();
    changeTimes[changeCounter] = currentTime-initTime;
    if ((changeTimes[changeCounter] > IR_TIMEOUT) || (changeCounter == SAMPLE_SIZE-1))
      ended = true;
	changeCounter++;
  }
  interrupts();
  for (i = 0; i < changeCounter; i++)
    Serial.println(changeTimes[i]);
}

void loop() {
  lcd.clear();
  lcd.setCursor(0, 1);
  lcd.print(millis()/1000);
  delay(100);
}

When the IR receives anything the counter just stops completely and never comes back.

Thanks in advance for your help and support.

There are quite a few things wrong here:

  attachInterrupt(0, receiveIR, LOW);
  lcd.begin(16, 2);
}

void receiveIR()
{
  Serial.println("Receiving IR transmission: ");
  noInterrupts();
  currentTime = initTime = millis();
  • Inside an interrupt routine interrupts are already off. So you don't need noInterrupts().
  • You shouldn't turn interrupts back on - the routine might be interrupted by itself, so get rid of interrupts().
  • Serial.print won't work inside it.
  • millis() clock stops ticking, so don't keep calling that,
  • The interrupt routine should be short - yours is incredibly long

Basically rework everything. The interrupt routine should set a flag. Do the rest elsewhere, particularly the serial prints. Maybe you can measure the pulse, but forget all that debugging stuff. If you must measure time use micros() which queries the clock hardware, whereas millis() won't change inside that routine.

BTW there is an IR library, that might show some nice techniques.

Here's the place I started:

http://www.arcfn.com/2009/08/multi-protocol-infrared-remote-library.html

And you can find the BackgroundIR library I wound up with in my source code at:

http://home.comcast.net/~tomhorsley/hardware/arduino/software.html

("Background" because all the IR sending and receiving happens at interrupt level).

OK, fine. There are a lot of files in that second download. Perhaps if you incorporated some of my suggestions like getting rid of the serial prints. There are times when adding debugging actually makes things stop working.

Hey, sorry for the late reply, it has been a busy day for me, only had time to write now.

I did incorporate some of your suggestions, but I am not using any library, I made the interruption detach itself and set a flag that is checked in the loop. The flag makes arduino receive and decode the ir code, right now I only have one code from a remote control I got with an arduino kit I bought on ebay I am using it to turn the backlight LED from my LCD screen. I plan to addapt my code to make decoding other codes easier.

My code is currently in my arduino, it got lost because windows restarted before I could save the file. Is there anywhere I can find a temporary save from the arduino IDE?

Wow, thank god! I managed to save my file from the temp files!

Have spent about 2 hours of work on that code, was sad about the idea of losing it.

Here is the code, tell me what you think:

#include <LiquidCrystal.h>

#define SAMPLE_SIZE 256
#define IR_TIMEOUT 500000 //microsseconds
#define TOLERANCE 50

#include "Arduino.h"
void setup();
void interruptIR();
void receiveIR();
int decodeIR();
void loop();
const int ledPin = 13;
const int irSensorPin = 2;

LiquidCrystal lcd(12, 11, 6, 5, 4, 3);
unsigned long changeTimes[SAMPLE_SIZE];
unsigned int changeCounter;
unsigned long initTime;
unsigned long currentTime;
boolean incomingTransmission;
boolean ended;
int cmd;
boolean ledStatus;

unsigned int interruptC;
unsigned int i;
unsigned int j;
unsigned int k;
boolean b;

//IR Commands array, 1st number is the number of changes subsequent to itself
unsigned int irCommand[][SAMPLE_SIZE] = {
  {66,4493,572,556,569,555,570,555,569,558,572,552,572,553,570,556,570,555,571,1680,573,1679,570,1679,571,1680,571,1679,572,1679,568,1683,570,1680,571,1680,571,554,569,1682,572,553,570,555,570,557,569,1679,570,557,569,557,569,1680,572,554,570,1681,568,1682,570,1679,573,554,569,1681,569} //power
};

void setup() {
  Serial.begin(115200);
  pinMode(ledPin, OUTPUT);
  pinMode(irSensorPin, INPUT);
  digitalWrite(ledPin, HIGH);
  ledStatus = HIGH;
  attachInterrupt(0, interruptIR, LOW);
  incomingTransmission = false;
  lcd.begin(16, 2);
  interruptC = 0;
  cmd = -1;
}

void interruptIR()
{
  incomingTransmission = true;
  detachInterrupt(0);
  interruptC++;
}

void receiveIR()
{
  Serial.println("Receiving IR transmission: ");
  currentTime = initTime = micros();
  changeCounter = 0;
  ended = false;
  b = digitalRead(irSensorPin);
  while(!ended)
  {
    if (digitalRead(irSensorPin) == HIGH)
      while ((digitalRead(irSensorPin) == HIGH) && (currentTime - initTime < IR_TIMEOUT))
        currentTime = micros();
    else
      while ((digitalRead(irSensorPin) == LOW) && (currentTime - initTime < IR_TIMEOUT))
        currentTime = micros();
    changeTimes[changeCounter] = currentTime-initTime;
    if ((changeTimes[changeCounter] >= IR_TIMEOUT) || (changeCounter == SAMPLE_SIZE-1))
      ended = true;
    changeCounter++;
  }
  for (i = 0; i < changeCounter-1; i++)
    changeTimes[i] = changeTimes[i+1] - changeTimes[i];
  /* //Serial.println(changeTimes[i]);
  for (i = 1; i < changeCounter; i++)
  {
    //Serial.print(changeTimes[i]);
    //Serial.print("\t");
    Serial.println(changeTimes[i]-changeTimes[i-1]);
  }
  Serial.println("------------------------");*/
  for (i = 0; i < changeCounter; i++)
  {
    Serial.print(changeTimes[i]);
    Serial.print("\t");
    Serial.println(b);
    b = !b;
    Serial.print(changeTimes[i]);
    Serial.print("\t");
    Serial.println(b);
  }
  incomingTransmission = false;
}

int decodeIR()
{
  k = sizeof(irCommand)/SAMPLE_SIZE/sizeof(int);
  for(i = 0; i < k; i++)
  {
    b = true;
    for (j = 1; j <= irCommand[i][0]; j++)
      if ((changeTimes[j-1] > irCommand[i][j] + TOLERANCE) || (changeTimes[j-1] < irCommand[i][j] - TOLERANCE))
      {
        b = false;
        break;
      }
    if (b == true)
      break;
  }
  return (b == true ? i : -1);
}

void loop() {
  if (incomingTransmission)
  {
    receiveIR();
    cmd = decodeIR();
    if (cmd == 0)
    {
      ledStatus = !ledStatus;
      digitalWrite(ledPin,ledStatus);
    }
    lcd.clear();
    attachInterrupt(0, interruptIR, LOW);
  }
  //lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print(interruptC);
  lcd.setCursor(5,0);
  lcd.print(cmd);
  lcd.setCursor(0, 1);
  lcd.print(millis()/1000);
}

Here is the code, tell me what you think:

I think that Ken Shirriff has already written an IR library that does what you are attempting to do. http://www.arcfn.com/2010/11/testing-arduino-ir-remote-library.html

Apart from what PaulS said, I suggest making interruptC volatile, as it is set inside an ISR.

volatile unsigned int interruptC;

... tell me what you think:

Does it work?

Yes, it does work (but so far it only have one command, I am gonna have to configure others later), and yes, I am gonna make it volatile, thanks for the suggestion.