Problem with timer overflow interrupts

#include <IRremote.h>

int input_pin = 2; // IR port
IRrecv irrecv(input_pin); //IR port
decode_results results;
unsigned long currentButtonCode;
unsigned long lastButtonCode;

void setup()
{
  Serial.begin(9600);
  irrecv.enableIRIn(); // turn on IR
  pinMode(6, OUTPUT); //motor PWM signal
  pinMode(7, OUTPUT); //motor output
  pinMode(8, OUTPUT); //motor output
  timerinit();
}

void timerinit() {
  TCCR1A = 0x00; //Normal port operation
  TIMSK1 |= (1 << TOIE1); //enable overflow interrupts
  sei(); //enable global interrupts
}

void start() {
  TCNT1 = 20535; //65535-40000 from calculator
  TCCR1B = 0x0000011; //64 prescaler
}

void stop() {
  TCCR1B = 0x00; // (timer stopped)
}

ISR(TIMER1_OVF_vect) { turn off motor

  analogWrite(6, 0); //
  digitalWrite(7, LOW);
  digitalWrite(8, LOW);
  stop();
}

void loop() {

  if (irrecv.decode(&results)) {
    currentButtonCode = results.value; //ignoring reapeat 0xFFFFFF

    if (currentButtonCode == 0xFFFFFFFF) { //ignoring reapeat 0xFFFFFF
      currentButtonCode = lastButtonCode; //ignoring reapeat 0xFFFFFF
    } else {
      lastButtonCode = currentButtonCode; ///ignoring reapeat 0xFFFFFF
    }
    Serial.print(F("result = 0x")); //received code
    Serial.println(currentButtonCode, HEX); // conversion to HEX
    
    irrecv.blink13(true); // blink LEDs when code is received

    if (results.value == 0xFF18E7 ) { //go up
      start();
      analogWrite(6, 255); //high speed
      digitalWrite(7, HIGH); //move up
      digitalWrite(8, LOW);

      delay(10);
    }

    if (results.value == 0xFF4AB5) { //move down
      start();
      analogWrite(6, 255); //high speed
      digitalWrite(7, LOW); //move down
      digitalWrite(8, HIGH);

      delay(10);
    }

    if (results.value == 0xFF38C7) { //optional stop button 

      analogWrite(6, 0); 
      digitalWrite(7, LOW);
      digitalWrite(8, LOW);
      delay(10);

    }
    irrecv.resume();
  }

}

I want to control DC motor with IR remote - motor should keep rotating as long as i hold a button on remote.

I've got a problem with my timer and interrupts. My idea was to start timer everytime when the code is received from remote. Remote sends codes as long as i hold a button, so timer should keep reseting as long as i hold a button. When i release button, timer should be overflow (after few ms), so interrupt should turn off my motor.

Code works only for the first "iteration". Motor keep rotating when i hold button, but when i release button and hold it again - motor start moving and turn off immediately.
What is wrong with my idea and code? Is there any easier way to solve my problem?

You don't need a timer and interrupts... just use millis()

Get a boolean that tells you if the motor is on (motorIsON) which you set in your code when you need to go up and down and reset when you stop.

You also need an unsigned long variable (lastChrono) that you set to millis() whenever you get a remote button press.

then in the loop(), after checking for an IR event you can do

if(motorIsON  && millis() - lastChrono >= 100) {
  // stop motor
  motorStop(); // to be written, includes  motorIsON = false;
}

When i release button, timer should be overflow (after few ms), so interrupt should turn off my motor.

A few ms? In my calculation it's about 180ms.

Code works only for the first "iteration". Motor keep rotating when i hold button, but when i release button and hold it again - motor start moving and turn off immediately.

What serial output do you get?

Is there any easier way to solve my problem?

If you use the millis() call you can eliminate the use of the timer interrupt. This removes code executed in interrupt context and makes the code better readable.

Somebody experienced with clear Atmegas and AVR programming recommended me method with interrupts, thanks your help guys. I edited a code, now it looks like this:

#include <IRremote.h>
int input_pin = 2; 
IRrecv irrecv(input_pin);
decode_results results;
unsigned long currentButtonCode;
unsigned long lastButtonCode;
unsigned long LastChrono;

boolean MotorON = 0;

void setup()
{

  Serial.begin(9600);
  irrecv.enableIRIn(); 

  pinMode(6, OUTPUT); //
  pinMode(7, OUTPUT); //
  pinMode(8, OUTPUT);
  }

void loop() {

  if (irrecv.decode(&results)) {
    currentButtonCode = results.value; //ignoring reapeat 0xFFFFFF

    if (currentButtonCode == 0xFFFFFFFF) { //ignoring reapeat 0xFFFFFF
      currentButtonCode = lastButtonCode; //ignoring reapeat 0xFFFFFF
    } else {
      lastButtonCode = currentButtonCode; ///ignoring reapeat 0xFFFFFF
    }
    Serial.print(F("result = 0x")); //received code
    Serial.println(currentButtonCode, HEX); // conversion to HEX

    irrecv.blink13(true); // blink LEDs when code is received

    if (results.value == 0xFF18E7 ) { //GO UP
      LastChrono = millis();
     

      MotorON = 1;
      analogWrite(6, 255); //high speed PWM
      digitalWrite(7, HIGH); //go up
      digitalWrite(8, LOW);

      delay(10);
    }

    if (results.value == 0xFF4AB5) { //go down
      
      LastChrono = millis();
      MotorON = 1;
      analogWrite(6, 255); //high speed PWM
      digitalWrite(7, LOW); //go down
      digitalWrite(8, HIGH);

      delay(10);
    }

    if (results.value == 0xFF38C7) { //optional stop button

      analogWrite(6, 0); 
      digitalWrite(7, LOW);
      digitalWrite(8, LOW);
      delay(10);
      MotorON = 0;

    }

    if (MotorON = 1  && millis() - LastChrono >= 200) {
      
      MotorON = false;
      analogWrite(6, 0); 
      digitalWrite(7, LOW);
      digitalWrite(8, LOW);
    }
    irrecv.resume();
  }
}

Something is wrong, but i am not sure what. Any suggestions?

What serial output do you get?

HEX codes from button as long as i hold a button.

A few ms? In my calculation it's about 180ms.

Maybe, i dont remember exact values from calculator :slight_smile:

    if (MotorON = 1  && millis() - LastChrono >= 200) {

ooopss (compare with what I proposed and think how to check for equality versus assignment)

(and better use true and false instead of 0 and 1 for boolean in C++)

Oooppps :slight_smile:

#include <IRremote.h>
int input_pin = 2; 
IRrecv irrecv(input_pin);
decode_results results;
unsigned long currentButtonCode;
unsigned long lastButtonCode;
unsigned long LastChrono;

boolean MotorON = false;

void setup()
{

  Serial.begin(9600);
  irrecv.enableIRIn(); 

  pinMode(6, OUTPUT); //
  pinMode(7, OUTPUT); //
  pinMode(8, OUTPUT);
  }

void loop() {

  if (irrecv.decode(&results)) {
    currentButtonCode = results.value; //ignoring reapeat 0xFFFFFF

    if (currentButtonCode == 0xFFFFFFFF) { //ignoring reapeat 0xFFFFFF
      currentButtonCode = lastButtonCode; //ignoring reapeat 0xFFFFFF
    } else {
      lastButtonCode = currentButtonCode; ///ignoring reapeat 0xFFFFFF
    }
    Serial.print(F("result = 0x")); //received code
    Serial.println(currentButtonCode, HEX); // conversion to HEX

    irrecv.blink13(true); // blink LEDs when code is received

    if (results.value == 0xFF18E7 ) { //GO UP
      LastChrono = millis();
     

      MotorON = true;
      analogWrite(6, 255); //high speed PWM
      digitalWrite(7, HIGH); //go up
      digitalWrite(8, LOW);

      delay(10);
    }

    if (results.value == 0xFF4AB5) { //go down
      
      LastChrono = millis();
      MotorON = true;
      analogWrite(6, 255); //high speed PWM
      digitalWrite(7, LOW); //go down
      digitalWrite(8, HIGH);

      delay(10);
    }

    if (results.value == 0xFF38C7) { //optional stop button

      analogWrite(6, 0); 
      digitalWrite(7, LOW);
      digitalWrite(8, LOW);
      delay(10);
      MotorON = false;

    }

    if (MotorON == true  && millis() - LastChrono >= 200) {
      
      MotorON = false;
      analogWrite(6, 0); 
      digitalWrite(7, LOW);
      digitalWrite(8, LOW);
    }
    irrecv.resume();
  }
}

Sadly, program still doesnt work as it should. When i hold a button - motor start rotating and turn off after 200ms.

Changing:

if (MotorON == true  && millis() - LastChrono >= 200)

to:

if (MotorON == true  && millis() - LastChrono >=1000)

changes that motor will turn off after 1s, so something is wrong with logic.

When i press a button (1 short click), motor keeps rotating until i dont click button again :o

Remove your delay() calls, they are not needed and may disturb the other time handling.

You want to perform the test against millis() always in the loop, not just when you got a key press and why do you check results.value? don't you want to use currentButtonCode?

give meaningful names to those 6/7/8 pins. What do they represent (enablePin, forwardPin, backwardPin?)

there is no need to send again the forward oder if you are already going forward (not an issue but not "nice")

write a stop(), goUp() and goDown() function, that will make the code nicer. These functions would include the change to LastChrono and motorIsON
You could also use a switch statement

    switch (currentButtonCode) {
      case 0xFF18E7: //GO UP
        goUp();
        break;

      case 0xFF4AB5: //go down
        goDown();
        break;

      case 0xFF38C7: //optional stop button
        stop();
        break;
    }

don't you want to use currentButtonCode?

Yeah, You are right - fixed.

and you want to perform the test against millis() always in the loop, not just when you got a key press

like this?

void loop() {
millis();
.....
}

I've tried to change currentButtonCode after turning on motor, so when i stop receiving new codes, currentButtonCode should have 0x000000 value, but it didnt help. Like this:

  if (currentButtonCode == 0xFF18E7 ) { // go up
      LastChrono = millis()
      MotorON = true;
      analogWrite(6, 255); //high PWm
      digitalWrite(7, HIGH); //go up
      digitalWrite(8, LOW);
      currentButtonCode = 0x000000; //when no new code, currentbuttoncode is 0x00000
    }

Sadly, my motor keep rotating even after your instructions.

#include <IRremote.h>

int input_pin = 2; // 
IRrecv irrecv(input_pin);
decode_results results;
unsigned long currentButtonCode;
unsigned long lastButtonCode;
unsigned long LastChrono;

boolean MotorON = false;

void setup()
{
  Serial.begin(9600);
  irrecv.enableIRIn(); // 
   pinMode(6, OUTPUT); // PWM
  pinMode(7, OUTPUT); //motor pins
  pinMode(8, OUTPUT);
}

void loop() {
   
  if (irrecv.decode(&results)) {
    millis(); //turning on millis
    currentButtonCode = results.value; //ignoring reapeat 0xFFFFFF

    if (currentButtonCode == 0xFFFFFFFF) { //ignoring reapeat 0xFFFFFF
      currentButtonCode = lastButtonCode; //ignoring reapeat 0xFFFFFF
    } else {
      lastButtonCode = currentButtonCode; ///ignoring reapeat 0xFFFFFF
    }
    Serial.print(F("result = 0x")); //received code
    Serial.println(currentButtonCode, HEX); // conversion to HEX

    irrecv.blink13(true); // blink LEDs when code is received

    if (currentButtonCode == 0xFF18E7 ) { //go up
      LastChrono = millis();
      MotorON = true;
      analogWrite(6, 255); //high speed
      digitalWrite(7, HIGH); //go up
      digitalWrite(8, LOW);
      currentButtonCode = 0x000000; //if no new code, currentbuttoncode = 0x00000
    }

    if (currentButtonCode == 0xFF4AB5) { //go down
      
      LastChrono = millis();
      MotorON = true;
      analogWrite(6, 255); //pwm
      digitalWrite(7, LOW); //go down
      digitalWrite(8, HIGH);
    }

    if (currentButtonCode == 0xFF38C7) { //optional stop 

      analogWrite(6, 0); 
      digitalWrite(7, LOW);
      digitalWrite(8, LOW);
      MotorON = false;
    }

    if (MotorON == true  && millis() - LastChrono >= 100) {
      analogWrite(6, 0); 
      digitalWrite(7, LOW);
      digitalWrite(8, LOW);
      MotorON = false;
    }
 irrecv.resume();
}
}

give meaningful names to those 6/7/8 pins. What do they represent (enablePin, forwardPin, backwardPin?)

PWM pin (0-255 value, it changes speed of motor)
Digital PIN1 (go forward)
Digital PIN2 (go backward)

hardtofindanynick:
like this?

void loop() {

millis();
.....
}

NO !!! that's totally useless. I mean check the loop() structure. if you receive a key, you do something. if you don't you loop. Ask yourself when do you want to stop the motor if there is NO key pressed for a while? where should that part of the code be? do you think there is any chance for it to work if you do that only when there is actually a key pressed?

have a look at this (typed here from your code, so totally untested)

// Motor pins
const byte speedPin  = 6; // PWM pin (0-255 value, it changes speed of motor)
const byte goUpPin   = 7; // Digital PIN1 (go forward)
const byte goDownPin = 8; // Digital PIN2 (go backward)

// IR library
#include <IRremote.h>
const byte IRpin = 2; // input pin
const unsigned long RepeatCode  = 0xFFFFFFFF;
const unsigned long GoUpCode    = 0xFF18E7;
const unsigned long GoDownCode  = 0xFF4AB5;
const unsigned long StopCode    = 0xFF38C7;

IRrecv irrecv(IRpin);
decode_results results;
unsigned long lastButtonCode;

// utilities
unsigned long LastChrono;
boolean motorIsON = false;

void goUp()
{
  analogWrite(speedPin, 255); //high speed
  digitalWrite(goDownPin, LOW); // better to stop one direction before activating the other one
  digitalWrite(goUpPin, HIGH); //go up
  motorIsON = true;
  LastChrono = millis();
}

void goDown()
{
  analogWrite(speedPin, 255); //pwm
  digitalWrite(goUpPin, LOW);
  digitalWrite(goDownPin, HIGH);
  motorIsON = true;
  LastChrono = millis();
}

void stop()
{
  analogWrite(speedPin, 0);
  digitalWrite(goUpPin, LOW);
  digitalWrite(goDownPin, LOW);
  motorIsON = false;
  LastChrono = millis();
}

void setup()
{
  Serial.begin(115200); // why go at 9600 bauds ?? 115200 is totally fine (can even go faster)
  pinMode(speedPin, OUTPUT);
  pinMode(goUpPin, OUTPUT);
  pinMode(goDownPin, OUTPUT);
  irrecv.enableIRIn();
}

void loop()
{
  if (irrecv.decode(&results)) {
    if (results.value == RepeatCode)  results.value = lastButtonCode; //overwrite, ignoring reapeat
    else lastButtonCode = results.value;

    switch (results.value) {
      case GoUpCode:    goUp();   break;
      case GoDownCode:  goDown(); break;
      case StopCode:    stop();   break;
    }
    irrecv.resume();
  }
  // is it time to stop the motor if we have not received a key for a while?
  if (motorIsON  && millis() - LastChrono >= 100) stop();
}

Oh man, moving

if (motorIsON  && millis() - LastChrono >= 100) stop();

two brackets below (like in your last code) fixed my code, now it works perfectly fine! Thank you very much, i am really grateful.
Final code:

#include <IRremote.h>
int input_pin = 2; 
IRrecv irrecv(input_pin);
decode_results results;
unsigned long currentButtonCode;
unsigned long lastButtonCode;
unsigned long LastChrono;
boolean MotorON = false;
void setup()
{
  Serial.begin(9600);
  irrecv.enableIRIn(); // włączenie odbierania danych
  pinMode(6, OUTPUT); //Sygnał PWM silnika nr 1
  pinMode(7, OUTPUT); //Sygnały sterujące kierunkiem obrotów silnika nr 1
  pinMode(8, OUTPUT);
}
void GoUp() {
  LastChrono = millis();
  MotorON = true;
  analogWrite(6, 255); //wysoka prędkość obrotów
  digitalWrite(7, LOW); //Silnik nr 1 - roleta w dół
  digitalWrite(8, HIGH);
}
void GoDown() {
  LastChrono = millis();
  MotorON = true;
  analogWrite(6, 255); //wysoka prędkość obrotów
  digitalWrite(7, HIGH); //Silnik nr 1 - roleta w dół
  digitalWrite(8, LOW);
}
void Stop() {
  analogWrite(6, 0); //Niska prędkość obrotów
  digitalWrite(7, LOW);
  digitalWrite(8, LOW);
  MotorON = false;
}
void loop() {
  if (irrecv.decode(&results)) {
    currentButtonCode = results.value; //ignoring reapeat 0xFFFFFF
    if (currentButtonCode == 0xFFFFFFFF) { //ignoring reapeat 0xFFFFFF
      currentButtonCode = lastButtonCode; //ignoring reapeat 0xFFFFFF
    } else {
      lastButtonCode = currentButtonCode; ///ignoring reapeat 0xFFFFFF
    }
    Serial.print(F("result = 0x")); //received code
    Serial.println(currentButtonCode, HEX); // conversion to HEX
    irrecv.blink13(true); // blink LEDs when code is received
    switch (currentButtonCode) {
      case 0xFF18E7: //GO UP
        GoUp();
        break;
      case 0xFF4AB5: //GO DOWN
        GoDown();
        break;
      case 0xFF38C7:
        Stop();
        break;
    }
       irrecv.resume();
  }
    if (MotorON == true  && millis() - LastChrono >= 100) {
    Stop();
  }
}

Now that you have a working code, don't keep it ugly, use PIN numbers, use constants for the codes and indent to code (ctrl-T) ! :slight_smile:
(also don't set a pin HIGH for the direction before making sure you have put the other one to LOW - double check with my code)

usually in Arduino Land we use camelCase, function names starts with a lower cap letter and then a capital letter for each word in the name. so that would be goDown() not GoDown() for example.