Help With IR remote hardware interrupts

Hello, I am currently working on a project where I would use an IR remote to deploy an hardware interrupt within my project. So far, my goal is to have a DC motor turn on when it's dark, and off when there's light. I got everything to work, with the exception of the IR remote. The goal is for the power button on the Elegoo remote work as a hardware interrupt, completely stopping everything, then the play button on the remote starting everything back up again. So far, here is the code I am using:


const int enable = 5;    //Motor
const int motor2 = 4;
const int motor1 = 3;
#include <IRremote.hpp>         //IR REmote
const byte IR_RECEIVE_PIN = 2;  //IR Pin 2  
volatile bool interruptShutoff = false; // Flag to shut off interrupts
void hardwareInterrupt() {   // Interrupt service routine (ISR) for a hardware event
  noInterrupts();   // Disable interrupts globally
  interruptShutoff = true; // Set shutoff flag
}
void hardwareInterruptOverride() {
  interruptShutoff = false; // Reset shutoff flag
  interrupts(); // Re-enable interrupts globally
}

void setup() {
pinMode(enable, OUTPUT);
pinMode(motor1, OUTPUT);
pinMode(motor2, OUTPUT);
analogRead(A0);
Serial.begin(9600); //for remote
IrReceiver.begin(IR_RECEIVE_PIN, ENABLE_LED_FEEDBACK); //activate remote
 pinMode(2, INPUT_PULLUP); // Example: External interrupt on pin 2
  attachInterrupt(digitalPinToInterrupt(2), hardwareInterrupt, FALLING); // Trigger on falling edge
  attachInterrupt(digitalPinToInterrupt(2), hardwareInterruptOverride, FALLING); // Trigger on falling edge
}


void loop() {

int Value = analogRead(A0); //Analog Sensor
int Brightness = map(Value, 0, 1023, 0, 100); //Analog to Brightness
Serial.println(Brightness); //Print Brightness
delay(1000);


int Freq = map(Brightness, 0, 100, 0, 950) + 50; //Tone based off of Brightness
if (Brightness <= 50) {
digitalWrite(enable, HIGH);
digitalWrite(motor1, HIGH);
digitalWrite(motor2, LOW); }
else (digitalWrite(enable, LOW));

if (IrReceiver.decode()) { //Power button on remote shuts off everything
  if (IrReceiver.decodedIRData.command == 0x45) {
  if (interruptShutoff) {
    digitalWrite(5, LOW); 
    digitalWrite(4, LOW);
    digitalWrite(3, LOW);
    while (true); // Halt the system
    }}}

if (IrReceiver.decode()) { //Play button on remote turns on everything
  if (IrReceiver.decodedIRData.command == 0x40) {
  if (interruptShutoff) {
    digitalWrite(5, HIGH); 
    digitalWrite(4, HIGH);
    digitalWrite(3, HIGH);
    while (true); // Halt the system
    }}}
}
  

I have tried to remove all but one of the digital pins within the code, and it still didn't work. Please share any feedback you have, because I've been stuck on this for hours.

This won't work at all. The IR library is using pin 2 to read the sensor for the IR remote. When you press a button, a bunch of IR pulses get sent. When those hit the sensor it goes HIGH and LOW with each pulse and those pulses are read by the IR library to get the code.

If you attach an interrupt to fire everytime the pin goes LOW, then it will fire that interrupt many many times no matter which button you press on the remote.

This is really not a place where you need an interrupt though. You really just need to get rid of that delay call and run this as a free running loop. Check against the millis clock to handle your 1 second timing.

2 Likes

Explain what is meant by this.

BTW, you can't attach two interrupts to the same vector. How will it know which one to fire when? In reality, the second one replaces the first one. So only the second one gets attached.

It's amazing how often members not knowing the interrupt mechanism think it's the miracle problem solving way.
Interrupts will bring the novice into unimaginable real time trouble never solved.

2 Likes
const int enable = 5;    //Motor
const int motor2 = 4;
const int motor1 = 3;
#include <IRremote.hpp>         //IR REmote
const byte IR_RECEIVE_PIN = 2;  //IR Pin 2  
byte state = 0;

void setup() {
pinMode(enable, OUTPUT);
pinMode(motor1, OUTPUT);
pinMode(motor2, OUTPUT);
analogRead(A0);
Serial.begin(9600); //for remote
IrReceiver.begin(IR_RECEIVE_PIN, ENABLE_LED_FEEDBACK); //activate remote
 pinMode(2, INPUT); // no need for pullup
}


void loop() {
if (IrReceiver.decode()){
if (IrReceiver.decodedIRData.command == 0x45){
state = 1;
}
else if (IrReceiver.decodedIRData.command == 0x40){
state = 2;
}
// Here's where to add a third remote command
// state = 0;
}
switch(state){
case 0:
defaultOps;
break;
case 1:
digitalWrite(5, LOW);
digitalWrite(4, LOW);
digitalWrite(3, LOW);
break;
case 2:
digitalWrite(5, HIGH);
digitalWrite(4, HIGH);
digitalWrite(3, HIGH);
break;
default:
defaultOps;
break;
}
delay(30);
IrReceiver.resume();
}
void defaultOps{
int Value = analogRead(A0); //Analog Sensor
int Brightness = map(Value, 0, 1023, 0, 100); //Analog to Brightness
Serial.println(Brightness); //Print Brightness
delay(1000);


int Freq = map(Brightness, 0, 100, 0, 950) + 50; //Tone based off of Brightness
if (Brightness <= 50) {
digitalWrite(enable, HIGH);
digitalWrite(motor1, HIGH);
digitalWrite(motor2, LOW); }
else (digitalWrite(enable, // oops I deleted the rest
   
}

Here's a general idea. I'm on my phone so the code formatting is incomplete and I accidentally deleted the end of a line in the function at the end. Your compiler will show where, just add in what you used to have, since doing this on my phone is clunky for me.

Edit: you will probably want a third button to set the state to 0, which you would just make the same as your default, so you can switch around easily, defaultOps; under the currently empty case of 0 in the state machine.

I apologize for not specifying. I am completely new to Arduino, and for a school project I need an interrupt command. What I had in my head was that an IR remote would control the interrupt, and the interrupt would halt the entire system. I basically just squeezed together several things I found online, as I have no idea how anything truly works.

I am using an L-Bridge connected to my DC motor. I read somewhere online that one of the input pins (in this case, pin 5) is in charge of turning on/off the Motor. I am completely new to Arduino, so I really have no idea what to do, and am taking shots in the dark.

Sorry if I didn't specify. I am completely new to Arduino, and for a project I need to use an interrupt. What I had in mind was that one button on a remote would control the interrupt, and another would turn off the interrupt. I really have no idea what I am doing, although thanks for the feedback!

Wow, thank you for your effort! I plugged in the code that you provided, and fixed a few bugs here and there, but it appears that defaultOps was not declared? I am completely new to Arduino, and I have no idea what any of these error code means. So far, with your code added, it appears as:

const int enable = 5;    //Motor
const int motor2 = 4;
const int motor1 = 3;
#include <IRremote.hpp>         //IR REmote
const byte IR_RECEIVE_PIN = 2;  //IR Pin 2  
byte state = 0;

void setup() {
pinMode(enable, OUTPUT);
pinMode(motor1, OUTPUT);
pinMode(motor2, OUTPUT);
analogRead(A0);
Serial.begin(9600); //for remote
IrReceiver.begin(IR_RECEIVE_PIN, ENABLE_LED_FEEDBACK); //activate remote
 pinMode(2, INPUT); // no need for pullup
}


void loop() {
if (IrReceiver.decode()){
if (IrReceiver.decodedIRData.command == 0x45){
state = 1;
}
else if (IrReceiver.decodedIRData.command == 0x40){
state = 2;
}
else if (IrReceiver.decodedIRData.command == 0x16){
state = 0;
}
switch(state){
case 0:
defaultOps;
break;
case 1:
digitalWrite(5, LOW);
digitalWrite(4, LOW);
digitalWrite(3, LOW);
break;
case 2:
digitalWrite(5, HIGH);
digitalWrite(4, HIGH);
digitalWrite(3, HIGH);
break;
default:
defaultOps;
break;
}
delay(30);
IrReceiver.resume();
}
void defaultOps{
int Value = analogRead(A0); //Analog Sensor
int Brightness = map(Value, 0, 1023, 0, 100); //Analog to Brightness
Serial.println(Brightness); //Print Brightness
delay(1000);


int Freq = map(Brightness, 0, 100, 0, 950) + 50; //Brightness controlling fan
if (Brightness <= 50) {
digitalWrite(enable, HIGH);
digitalWrite(motor1, HIGH);
digitalWrite(motor2, LOW); }
else (digitalWrite(enable, LOW));
}

It also comes with several error codes, mainly that defaultOps is not declared:

`C:\Users\Owner\AppData\Local\Temp\.arduinoIDE-unsaved20241021-19276-8je1kq.bw29q\sketch_nov21a\sketch_nov21a.ino: In function 'void loop()':
C:\Users\Owner\AppData\Local\Temp\.arduinoIDE-unsaved20241021-19276-8je1kq.bw29q\sketch_nov21a\sketch_nov21a.ino:32:1: error: 'defaultOps' was not declared in this scope
 defaultOps;
 ^~~~~~~~~~
C:\Users\Owner\AppData\Local\Temp\.arduinoIDE-unsaved20241021-19276-8je1kq.bw29q\sketch_nov21a\sketch_nov21a.ino:32:1: note: suggested alternative: 'decltype'
 defaultOps;
 ^~~~~~~~~~
 decltype
C:\Users\Owner\AppData\Local\Temp\.arduinoIDE-unsaved20241021-19276-8je1kq.bw29q\sketch_nov21a\sketch_nov21a.ino:51:6: error: variable or field 'defaultOps' declared void
 void defaultOps{
      ^~~~~~~~~~
C:\Users\Owner\AppData\Local\Temp\.arduinoIDE-unsaved20241021-19276-8je1kq.bw29q\sketch_nov21a\sketch_nov21a.ino:52:1: error: expected primary-expression before 'int'
 int Value = analogRead(A0); //Analog Sensor
 ^~~
C:\Users\Owner\AppData\Local\Temp\.arduinoIDE-unsaved20241021-19276-8je1kq.bw29q\sketch_nov21a\sketch_nov21a.ino:52:1: error: expected '}' before 'int'
C:\Users\Owner\AppData\Local\Temp\.arduinoIDE-unsaved20241021-19276-8je1kq.bw29q\sketch_nov21a\sketch_nov21a.ino:53:22: error: 'Value' was not declared in this scope
 int Brightness = map(Value, 0, 1023, 0, 100); //Analog to Brightness
                      ^~~~~
C:\Users\Owner\AppData\Local\Temp\.arduinoIDE-unsaved20241021-19276-8je1kq.bw29q\sketch_nov21a\sketch_nov21a.ino: At global scope:
C:\Users\Owner\AppData\Local\Temp\.arduinoIDE-unsaved20241021-19276-8je1kq.bw29q\sketch_nov21a\sketch_nov21a.ino:65:1: error: expected declaration before '}' token
 }
 ^

exit status 1

Compilation error: 'defaultOps' was not declared in this scope
`

As I am trying to figure this out, I want to thank you again for your help.

Oops, adapting to my phone for this is not my forte

defaultOps();

Is the correct way to call the function of the same name.

You might try that interrupt again if your assignment needs one, to read one other button to reset the global variable state to 0, which should return your sketch to default after you toggle the devices like you were originally trying to do with the original two remote buttons.
So the effect of your interrupt routine will simply be state = 0;
Edit: as others have said though, you're better off not using interrupts for this type of project. I was merely suggesting somewhere you might try to include one if you need one.
A simple button press might be easier to implement for that ISR, though, again just using a button reading to set state = 0;

Is this for a school assignment?

This would be a poor use of an interrupt. A good interrupt example would be the code that reads the IR. If you did away with the IR library and used an interrupt to read the pulses from the IR sensor directly, then that would be a place where an interrupt is appropriate.

For what you have described there is an interrupt being used, but it is being used by the IR library to read the sensor. There's not really any place to add any additional interrupt to this. There aren't any signals to catch.

Understood, thank you for your feedback. I will probably just code the remote to turn on and off an LED, and have two buttons control the interrupt.

Thank you for your help. Now that I am looking at my project, using an IR remote may be impractical for an interrupt. Instead, I will alter my code and setup so that the remote will control an LED, and the interrupt will be controlled by two buttons.

One button if you go the interrupt route. Just increment the state variable by one. One button, one ISR as @Delta_G already mentioned.
Double check that the interrupt pin used by your board doesn't conflict with any of the others you're already using.

state += 1; 

Then check at the bottom of void loop()
(before the final curly brace) that the state variable is within bounds of the state machine.

if (state >= 3) state = 0;

Hopefully this gets you on the right track.
Good luck.

Edit edit: I'm only about 50% sure incrementing a global variable inside an ISR will work, if you opt to go with my suggestions instead of anyone else's here.
Maybe one of the other helpers here can comment on that.

It works as long as the global is marked volatile

Otherwise it works 'sometimes', and then sometimes the compiler optimizes the code and deletes a variable because it doesn't notice that the interrupt might have a side effect.

1 Like

Try this. Revised from post #6 now that I'm home and have access to my PC and electronics bench. Read all the notes, take the time to understand what's happening. Any questions, ask.

Note that all the pins on the Arduino have been moved around for various reasons (assuming you were using an Uno R3). Again, see the notes.

/*
====== GENERAL NOTE ==============
I moved all the pins around and changed some stuff up
so you can learn. Most of the changes are commonly
accepted conventions in Arduino and their basis is well
founded and for good reason I won't go into here.
A few of the changes are stylistic changes I made since
that's how I roll.
The reason for the pin changes explained below.

====== NOTE ON INTERRUPTS =======
The arduino Uno R3, often folks' first introductory board into 
Arduino, has two hardware interrupts: vector 0 and 1.
Vector 0 corresponds to digital pin 2 and vector 1 corresponds
to digital pin 3. 
Therefore, in its simplest use, these pins should not be shared
with other devices.

That is why I moved the global pin definitions below around. 
Don't share pins unless you really know what you're doing.

======= NOTE ON SERIAL =========
Serial is a necessary tool in Arduino projects.
It is typically used for code debugging and also 
great for general info to the designer or user.

In void setup(), I added the line "Serial.println("myProject_V1");"
to illustrate this. As your code grows and changes, it's useful
to have the Arduino tell you what sketch it's running, 
so replace the text in this line with whatever your sketch is called.

You should also use Serial.println("someVal = ");Serial.println(someValue);
often throughout your code as you develop it. In fact, every time something
changes, if you do this, when things don't work as expected (and they won't)
it will really save a lot of time and headache trying to narrow down 
the issue. Trust me, it's 99% user error, not the Arduino, causing
the issues. Ask me how I know that...

======== HARDWARE AND TEST RESULTS ========
Tested on Arduino Mega, will work the same on Uno R3.

Tested with the following hardware:
Potentiometer; led; pushbutton with no external resistors;
and Elegoo remote communicating with IR Receiver model TSOP38238.

Motors were not used in testing, led used instead to simulate speed control

By Hallowed31
2024-11-22
*/

#include <IRremote.hpp>  // libraries are conventionally at the top of a sketch

const int analogSensor = A0;  // you should name your devices
const int interruptButton = 2;
const int IR_RECEIVE_PIN = 7;  // byte isn't wrong but do you why it was byte?
const int led = 11;            // PWM pin to allow brightness control

const int enable = 10;  //Motor
const int motor2 = 9;
const int motor1 = 6;  // pins 10, 9 and 6 are also PWM capable

volatile byte state = 0;       // thanks to Delta_G for the assist
unsigned long currentMillis;   // unsigned long because you don't want the clock to run out
unsigned long previousMillis;  // store last time we checked millis() free running clock
const int delayTime = 1000;    // const because we always want the delay this long

int Value = 0;
int Brightness = 0;
int LedBrightness = 0;
int Freq = 0;

void hardwareInterrupt() {  // Interrupt service routine (ISR) for a hardware event
  state++;
  if (state >= 3) {
    state = 0;
  }
}

void setup() {
  Serial.begin(9600);
  Serial.println("myProject_V1");  // see note
  Serial.println();                // blank line to readability
  delay(1000);                     // good use of blocking delay - a little time to read this
  pinMode(analogSensor, INPUT);
  pinMode(interruptButton, INPUT_PULLUP);  // uses Uno internal resistor but logic reversed
  pinMode(IR_RECEIVE_PIN, INPUT);          // use the name you gave it, not the pin number
  pinMode(enable, OUTPUT);
  pinMode(motor1, OUTPUT);
  pinMode(motor2, OUTPUT);
  pinMode(led, OUTPUT);
  digitalWrite(led, LOW);
  attachInterrupt(digitalPinToInterrupt(interruptButton), hardwareInterrupt, FALLING);
  IrReceiver.begin(IR_RECEIVE_PIN, ENABLE_LED_FEEDBACK);  //activate remote
}

void loop() {
  noInterrupts();
  if (IrReceiver.decode()) {
    if (IrReceiver.decodedIRData.command == 0x45) {
      Serial.println("Power"); // power button
      Serial.println();
      delay(250); // short 1/4 second delay to read...
      state = 1;
    } else if (IrReceiver.decodedIRData.command == 0x40) {
      Serial.println("Play"); // play button
      Serial.println();
      delay(250); // ...and also limit extra button press signals
      state = 2;
    } else if (IrReceiver.decodedIRData.command == 0x16) {
      Serial.println("Resume Default"); // button 0
      Serial.println();
      delay(250);
      state = 0;
    }
    IrReceiver.resume();
  }
  interrupts();
  switch (state) {
    case 0:
      defaultOps();
      break;
    case 1:
      digitalWrite(enable, LOW);
      digitalWrite(motor1, LOW);
      digitalWrite(motor2, LOW);
      digitalWrite(led, LOW);
      break;
    case 2:
      digitalWrite(enable, HIGH);
      digitalWrite(motor1, HIGH);
      digitalWrite(motor2, HIGH);
      digitalWrite(led, HIGH);
      break;
    default:
      defaultOps();
      break;
  }
}
void defaultOps() {
  // interesting you're using "brightness" to
  // control the speed of a motor.
  Value = analogRead(analogSensor);
  Brightness = map(Value, 0, 1023, 0, 100);     //Analog to Brightness
  LedBrightness = map(Value, 0, 1023, 0, 127);  // from 0 - half bright
  analogWrite(led, LedBrightness);
  // start our named (currentMillis), built in, free running, no blocking timer (millis() function)
  currentMillis = millis();
  if (currentMillis - previousMillis > delayTime) {
    previousMillis = currentMillis;
    Serial.print("Brightness: ");
    Serial.print(Brightness);           //Print Brightness - how do you know? ^
    Serial.print("\tLedBrightness: ");  // the \t is a control character, tab right
    Serial.println(LedBrightness);      //Print Brightness - how do you know? ^
    Serial.println();                   // blank line
    // delay(1000);  // this is a long blocking delay that blocks hardware events. Not ideal. 
  }

  Freq = map(Brightness, 0, 100, 0, 950) + 50;  //Brightness controlling fan
  if (Brightness <= 50) {
    digitalWrite(enable, HIGH);
    digitalWrite(motor1, HIGH);
    digitalWrite(motor2, LOW);
  } else (digitalWrite(enable, LOW));
}