IRRemote: how to use .resume and Finite State Machine

Hi all,
i'm a newbie and i'm sorry for my mistakes. I cannot figure out how to get indented if's work in arduino using an ir remote.
This is my code

#include <IRremote.h>

const int ir = 6;
IRrecv irrecv(ir);
decode_results results;
char sign;

void setup() {
  Serial.begin(9600);
  irrecv.enableIRIn();
  irrecv.blink13(true);
}

void loop() {
  if (irrecv.decode(&results)){
        Serial.println(results.value, HEX);
        irrecv.resume();
        delay(100);

  if (results.value == 0xFF52AD){
   
    Serial.println("Insert sign +/- using right/left arrow");
    irrecv.resume();
    delay(100);
    
    if(results.value == 0xFF22DD){
       sign = '+';
        Serial.println(sign);
        irrecv.resume();
        delay(100);
        }
    else if(results.value == 0xFFC23D){
      sign = '-';
      Serial.println(sign);
      irrecv.resume();
      delay(100);
        }
    else{
      Serial.println("no choice");
      irrecv.resume();
      delay(100);
        }
      }
    }
}

what happens is that, when i press # (0xFF52AD) the program doesn't wait and goes immediately at the else statement, i think i'm misunderstanding the use of irrecv.resume, i searched a lot on the forum and i tried to put them in various places in the program, but it still doesn't work. What's wrong?

Thanks!

In order to update results, you need to call irrecv.decode(&results). But you are only giving the user 100 ms to press the button so they would need to be extremely fast. irrecv.decode(&results) returns true when a new code was received so there is no need to use an arbitrary delay that the user needs to manage to press the button within. The naive implementation would be to just sit in a while loop until the code is received:

while(!irrecv.decode(&results)) {}

but that blocks anything else from happening in your program, so it might be a problem if you later wanted to add other functionality to the code. Better would to set a state variable after receive 0xFF52AD and then just let loop() run until the next code is received, then act accordingly based on the value of the state variable.

caesar753:
i think i'm misunderstanding the use of irrecv.resume, i searched a lot on the forum and i tried to put them in various places in the program

I don't understand the point of that function, but the use of it is clear if you read the IRremote library documentation:

irrecv.resume()

This must be called after irrecv.decode() to resume listening for a code.

I'd think that it would be obvious that the user wanted to start listening again after calling irrecv.decode(&results) and that would be done automatically, but maybe there's some use case I don't know about where being able to separately control listening is useful.

I could be wrong, but as I understand it resume() is needed because there is only one receive buffer (to save memory).

Once the library has identified the end of an IR transmission, the user has to decode the raw data using decode() which runs through the configured protocols. If the library was to start receiving the next IR signal before this happens, the internal buffer would be overwritten. resume() tells the library it is safe to start receiving again.

Thanks a lot, i understood the use of .decode and .resume in ir library, but now i have another trouble: i'd like to insert a multiple digit number, i googled and found something that i managed to use, but now i'd like to start the insertion of the number after a key is pressed (0xFF42BD)

This is my code

#include <IRremote.h>
/*
   Variables
*/
int RECV_PIN = 6;
const int MaxChars = 2;
char ch;
int index = 0;
int accumulVal = 0;
char strValue[3];  
int i = 0;

IRrecv irrecv(RECV_PIN);
decode_results results;

void setup()
{
  Serial.begin(9600);
  // In case the interrupt driver crashes on setup, give a clue
  // to the user what's going on.
  Serial.println("Enabling IRin");
  irrecv.enableIRIn(); // Start the receiver
  Serial.println("Enabled IRin");
}

void loop() {
  if (irrecv.decode(&results)){
    Serial.println(results.value, HEX);
    irrecv.resume(); // Receive the next value
  }
  delay(150);



   if (results.value == 0xFF42BD){ //* key on remote

    Serial.println("Insert value");    
     if (irrecv.decode(&results)){
      irrecv.resume();
      delay(2500);

  // switch / case function
      
    switch (results.value)
    {
      case 0xFF6897 : // Button '1' on my remote
        strValue[index++] = '1';
        Serial.print(index);
        Serial.print(" ,");
        Serial.println(strValue[index]);
        delay(2000);
        break;   // assign number to button press

      case 0xFF9867:  // Button '2' on my remote
        //index++;
        strValue[index++] = '2';
        Serial.print(index);
        Serial.print(" ,");
        Serial.println(strValue[index]);
        delay(2000);
        break;

      case 0xFFB04F :  // Button '3' on my remote
        //index++;
        strValue[index++] = '3';
        Serial.print(index);
        Serial.print(" ,");
        Serial.println(strValue[index]);
        delay(2000);
        break;

      case 0xFF30CF :  // Button '4' on my remote
        strValue[index++] = '4';
        Serial.print(index);
        Serial.print(" ,");
        Serial.println(strValue[index]);
        delay(2000);
        break;

      case 0xFF18E7 :  // Button '5' on my remote
        strValue[index++] = '5';
        Serial.print(index);
        Serial.print(" ,");
        Serial.println(strValue[index]);
        delay(2000);
        break;

      case 0xFF7A85 :  // Button '6' on my remote
        strValue[index++] = '6';
        Serial.print(index);
        Serial.print(" ,");
        Serial.println(strValue[index]);
        delay(2000);
        break;

      case 0xFF10EF :  // Button '7' on my remote
        strValue[index++] = '7';
        Serial.print(index);
        Serial.print(" ,");
        Serial.println(strValue[index]);
        delay(2000);
        break;

      case 0x2FF38C7 :  // Button '8' on my remote
        strValue[index++] = '8';
        Serial.print(index);
        Serial.print(" ,");
        Serial.println(strValue[index]);
        delay(2000);
        break;

      case 0xFF5AA5 :  // Button '9' on my remote
        strValue[index++] = '9';
        Serial.print(index);
        Serial.print(" ,");
        Serial.println(strValue[index]);
        delay(2000);
        break;

      case 0xFF4AB5 :  // Button '0' on my remote
        strValue[index++] = '0';
        Serial.print(index);
        Serial.print(" ,");
        Serial.println(strValue[index]);
        delay(2000);
        break;

      case 0xFFFFFFFF : // Key depressed long
        prevKey = 0;    // clear
        break;

      case 0xFF02FD : // OK button
        //strValue[index] = 0;          // terminate the string with a 0
       accumulVal = atoi(strValue);  // use atoi to convert string to an int
        Serial.print("Accumulated value: ");
        Serial.println(accumulVal, DEC);
        Serial.print("Serial print value: ");
        for (i = 0; i < 3; i++){
          Serial.print(strValue[i]);
        }
        Serial.println();
        index = 0;                    // reset index to receive other data
        i = 0;
        accumulVal = 0;
        memset(strValue, 0, 3);
        strValue[index] = 0;
        delay (2000);

    }  // end of cases

    }  
  }

} // loop end

so, if i press 0xFF42BD on remote i enter in if statement, but when i insert a digit the program exit from the if, so i have to press again 0xFF42BD to re-enter and insert another digit, how can i stay inside the if till i put in the array 3 digits?

You don't do it that way because nothing else will run properly while you are 'waiting' for the next digit to arrive. What happens if the user only enters 2 digits, or if one of the 'digits' is not a number?

The standard way to handle this is to implement a Finite State Machine (or FSM) that recognises each digit as it arrives and builds up the number with each key press. This is not difficult but you will need to learn some new concepts if you have not seen this before.

Thanks for your reply. I googled a little, andthe only "readable" thing i found is this article https://www.instructables.com/id/Finite-State-Machine-on-an-Arduino/
So is it supposed that i have to use graphical software (as yakindu) or can write directly the code?
I found some code online of FSM but it's quite difficult, and found no tutorial which introduces basically FSM.

Here's a video tutorial that seems reasonable: ✔ Multitask Arduino with State Machines (& Switch Debouncing) - YouTube

The easiest way to code simple state machines is using a switch/case statement (my prerefence) or an equivalent sequence of if/else statements. No external tools or libraries required.

Ok, thanks a lot for your suggestions!! I hope i designed correctly an FSM, this is my code

  #include <IRremote.h>
  /*
     Variables
  */
  int RECV_PIN = 6;
  const int MaxChars = 2;
  char strValue[MaxChars];   // String for 3 digits + null char
  char ch;
  int index = 0;
  int accumulVal = 0;
  //int newKey = 0;
  //int prevKey = 0;
  int i = 0;
  int state = 1;
  char sign; 
  
  IRrecv irrecv(RECV_PIN);
  decode_results results;
  
  void setup()
  {
    Serial.begin(9600);
    // In case the interrupt driver crashes on setup, give a clue
    // to the user what's going on.
    Serial.println("Enabling IRin");
    irrecv.enableIRIn(); // Start the receiver
    Serial.println("Enabled IRin");
  }
  
  void loop() {

   if (results.value == 0xFF42BD){ // * button on remote 
      state = 2;
    }
  
   else if (results.value == 0xFF52AD){ //# button on remote
      state = 1;
    }
     
     if (irrecv.decode(&results)){
      Serial.println(results.value, HEX);
      irrecv.resume(); // Receive the next value
    delay(150);}

    if (state == 1) {
        Serial.println("insert sign usign left/right arrow button");
      if (irrecv.decode(&results)){
      irrecv.resume();
      delay(500);}
      
      switch (results.value){
          case 0xFF22DD: //left button:
          sign = '-';
          Serial.println(sign);
          delay (1000);
          state = 2;
        break;
        case 0xFFC23D: //right button:
          sign = '+';
         Serial.println(sign);
         delay (1000);
         state = 2;
        break;
        case 0xFF6897:
        state = 2;
        break;
        default:
         Serial.println("no choice");
        break;        
      }
     
   }
        
  if (state == 2){ 
  
      Serial.println("Insert a three digit value:");    
      
    if (irrecv.decode(&results)){
        irrecv.resume();
        delay(250);}
        
      do{
        if (irrecv.decode(&results)){
        irrecv.resume();
        delay(250);}
                
        switch (results.value){
        case 0xFF6897 : // Button '1' on my remote
          strValue[index++] = '1';
          Serial.print(index);
          Serial.print(" ,");
          Serial.println(strValue[index]);
          //delay(200);
          break;   // assign number to button press
  
        case 0xFF9867:  // Button '2' on my remote
          //index++;
          strValue[index++] = '2';
          Serial.print(index);
          Serial.print(" ,");
          Serial.println(strValue[index]);
          //delay(200);
          break;
  
        case 0xFFB04F :  // Button '3' on my remote
          //index++;
          strValue[index++] = '3';
          Serial.print(index);
          Serial.print(" ,");
          Serial.println(strValue[index]);
          //delay(200);
          break;
  
        case 0xFF30CF :  // Button '4' on my remote
          strValue[index++] = '4';
          Serial.print(index);
          Serial.print(" ,");
          Serial.println(strValue[index]);
          //delay(200);
          break;
  
        case 0xFF18E7 :  // Button '5' on my remote
          strValue[index++] = '5';
          Serial.print(index);
          Serial.print(" ,");
          Serial.println(strValue[index]);
          //delay(200);
          break;
  
        case 0xFF7A85 :  // Button '6' on my remote
          strValue[index++] = '6';
          Serial.print(index);
          Serial.print(" ,");
          Serial.println(strValue[index]);
          //delay(200);
          break;
  
        case 0xFF10EF :  // Button '7' on my remote
          strValue[index++] = '7';
          Serial.print(index);
          Serial.print(" ,");
          Serial.println(strValue[index]);
          //delay(200);
          break;
  
        case 0x2FF38C7 :  // Button '8' on my remote
          strValue[index++] = '8';
          Serial.print(index);
          Serial.print(" ,");
          Serial.println(strValue[index]);
          //delay(200);
          break;
  
        case 0xFF5AA5 :  // Button '9' on my remote
          strValue[index++] = '9';
          Serial.print(index);
          Serial.print(" ,");
          Serial.println(strValue[index]);
          //delay(200);
          break;
  
        case 0xFF4AB5 :  // Button '0' on my remote
          strValue[index++] = '0';
          Serial.print(index);
          Serial.print(" ,");
          Serial.println(strValue[index]);
          //delay(200);
          break;
  
        /*case 0xFFFFFFFF : // Key depressed long
          prevKey = 0;    // clear
          break;*/
          }         
        } while (index < 3);

        
      if (index >= 3){
        state = 3;}
      }

      if (state == 3){        
        
        if (irrecv.decode(&results)){
        irrecv.resume();
        delay(200);}
                
  
         if (results.value == 0xFF52AD){
            index = 0;                    // reset index to receive other data
            i = 0;
            accumulVal = 0;
            memset(strValue, 0, 3);
            strValue[index] = 0;
            state = 1;          
          } 

          else {
            accumulVal = atoi(strValue);  // use atoi to convert string to an int
            Serial.print("Accumulated value: ");
            Serial.println(accumulVal, DEC);
            Serial.print("Serial print value: ");
            for (i = 0; i < 3; i++){
              Serial.print(strValue[i]);
            }
            Serial.println();            
            delay (1000);}
        }
     }
     
} // loop end

So this is what this FSM should do:

  • it enters in state 1: you have to choose a sign (+/-) using left/right key on remote
  • if the choice is done it FSM goes to state 2, else state 1 keeps running
  • in state 2 you have to insert a 3 digit value (do...while loop), when this is inserted FSM goes to state 3
  • in state 3 is shown the accumulated sign and the value, if user press 0xFF52AD (# button) the variables are cleared and the program returns to state 1

is it all correct? can i improve something?

Ok, but the code works also without that do-while loop, transition to next state is allowed only of index>3, right?

The thing that i can notice is that there is a very poor response from the ir input: many times i get 0xFFFFFF (key pressed to long) or for the same button different code (one with 5 hexadecimal digits, which i have yet mapped, and one with 7 hexadecimal digits), has anyone any idea about this strange behaviour?

Yes, It has but only after irrecv.resume because if I delete the delay the code registers only the first digit three times. I diminished the delay till 50ms buy glitches are still present

So, i remapped the ir codes assignign to each button two possible hexadecimal values and use the accumulated integer to drive a stepper motor, this is my code

  #include <IRremote.h>
  #include <Stepper.h>
  #include <IRremote.h>

// Number of steps per revolution of INTERNAL motor in 4-step mode
#define STEPS_PER_MOTOR_REVOLUTION 32   

// Number of steps per revolution of OUTPUT SHAFT (= gear reduction; 2048 steps)
#define STEPS_PER_OUTPUT_REVOLUTION 32 * 64

// Declare 'small_stepper' variable
Stepper small_stepper(STEPS_PER_MOTOR_REVOLUTION, 8, 10, 9, 11);

// Declare 'Steps2Take' variable
int  Steps2Take;
  /*
     Variables
  */
  int RECV_PIN = 6;
  const int MaxChars = 5;
  char strValue[MaxChars];   // String for 4 digits + null char
  char ch;
  int index = 1;
  int accumulVal = 0;
  //int newKey = 0;
  //int prevKey = 0;
  int i = 0;
  int state = 1;
  char sign; // senso in cui girerà il motore
  
  IRrecv irrecv(RECV_PIN);
  decode_results results;
  
  void setup()
  {
    Serial.begin(9600);
    // In case the interrupt driver crashes on setup, give a clue
    // to the user what's going on.
    Serial.println("Enabling IRin");
    irrecv.enableIRIn(); // Start the receiver
    Serial.println("Enabled IRin");
  }
  
  void loop() {

   if (results.value == 0xFF42BD){ //tasto * sul telecomando
      state = 2;
    }
  
   else if (results.value == 0xFF52AD){
      state = 1;
    }
     
     /*if (irrecv.decode(&results)){
      Serial.println(results.value, HEX);
      irrecv.resume(); // Receive the next value
    delay(150);}*/

    if (state == 1) {
        Serial.println("Inserire segno utilizzando i tasti destro/sinistro");
      if (irrecv.decode(&results)){
      irrecv.resume();
      delay(500);
      
      switch (results.value){
          case 0xFF22DD: //left button:
          case 0x52A3D41F: 
          sign = '-';
          strValue[0] = '-';
          Serial.println(sign);
          delay (1000);
          state = 2;
        break;
        
        case 0xFFC23D: //right button:
        case 0x20FE4DBB:
          sign = '+';
          strValue[0] = '+';
         Serial.println(sign);
         delay (1000);
         state = 2;
        break;
        
        case 0xFF6897:
        state = 2;
        break;
        
        default:
         Serial.println("nessuna scelta effettuata");
        break;        
      }
     }
   }
        
  if (state == 2){ 
  
      Serial.println("Inserire valore");    
      
    if (irrecv.decode(&results)){
        irrecv.resume();
        delay(150);
        
                
        switch (results.value){
        case 0xFF6897:
        case 0xC101E57B: // Button '1' on my remote
          strValue[index++] = '1';
          Serial.print(index);
          Serial.print(" ,");
          Serial.println(strValue[index]);
          //delay(200);
          break;   // assign number to button press
         //case 0xC101E57B
  
        case 0xFF9867:
        case 0x97483BFB:  // Button '2' on my remote
          //index++;
          strValue[index++] = '2';
          Serial.print(index);
          Serial.print(" ,");
          Serial.println(strValue[index]);
          //delay(200);
          break;
  
        case 0xFFB04F:
        case 0xF0C41643:  // Button '3' on my remote
          //index++;
          strValue[index++] = '3';
          Serial.print(index);
          Serial.print(" ,");
          Serial.println(strValue[index]);
          //delay(200);
          break;
  
        case 0xFF30CF:
        case 0x9716BE3F:  // Button '4' on my remote
          strValue[index++] = '4';
          Serial.print(index);
          Serial.print(" ,");
          Serial.println(strValue[index]);
          //delay(200);
          break;
  
        case 0xFF18E7:
        case 0x3D9AE3F7:  // Button '5' on my remote
          strValue[index++] = '5';
          Serial.print(index);
          Serial.print(" ,");
          Serial.println(strValue[index]);
          //delay(200);
          break;
  
        case 0xFF7A85:
        case 0x6182021B:  // Button '6' on my remote
          strValue[index++] = '6';
          Serial.print(index);
          Serial.print(" ,");
          Serial.println(strValue[index]);
          //delay(200);
          break;
  
        case 0xFF10EF:
        case 0x8C22657B:  // Button '7' on my remote
          strValue[index++] = '7';
          Serial.print(index);
          Serial.print(" ,");
          Serial.println(strValue[index]);
          //delay(200);
          break;
  
        case 0x2FF38C7:
        case 0x488F3CBB:  // Button '8' on my remote
          strValue[index++] = '8';
          Serial.print(index);
          Serial.print(" ,");
          Serial.println(strValue[index]);
          //delay(200);
          break;
  
        case 0xFF5AA5:
        case 0x449E79F:  // Button '9' on my remote
          strValue[index++] = '9';
          Serial.print(index);
          Serial.print(" ,");
          Serial.println(strValue[index]);
          //delay(200);
          break;
  
        case 0xFF4AB5:
        case 0x1BC0157B:  // Button '0' on my remote
          strValue[index++] = '0';
          Serial.print(index);
          Serial.print(" ,");
          Serial.println(strValue[index]);
          //delay(200);
          break;
  
        /*case 0xFFFFFFFF : // Key depressed long
          prevKey = 0;    // clear
          break;*/
          }
        //} while (index < 3);
    
       /*if (irrecv.decode(&results)){
        irrecv.resume();
        delay(500);}*/
        //delay(1000);
        }
      if (index >= 5){
        state = 3;}
      }

      if (state == 3){        
        
        if (irrecv.decode(&results)){
        irrecv.resume();
        delay(150);
        
      /*if (results.value == 0xFF02FD){ // OK button*/
      
          //strValue[index] = 0;          // terminate the string with a 0
        
  
         if (results.value == 0xFF52AD || results.value == 0x3EC3FC1B){ //button # on my remote
            index = 1;                    // reset index to receive other data
            i = 0;
            accumulVal = 0;
            memset(strValue, 0, 3);
            strValue[index] = 0;
            state = 1;          
          } 

          else {
            accumulVal = atoi(strValue);  // use atoi to convert string to an int
            Serial.print("Accumulated value: ");
            //Serial.print(sign);
            Serial.println(accumulVal, DEC);
            Serial.print("Serial print value: ");
            //Serial.print(sign);
            for (i = 0; i < 5; i++){
              Serial.print(strValue[i]);
            }
            Serial.println();

            Steps2Take  =  accumulVal;  // Rotate CCW 1/2 turn  
            small_stepper.setSpeed(1000);  // 2000 a good max speed??
           small_stepper.step(Steps2Take);
            delay(2000);
            //delay (1000);
            accumulVal = 0;
            }
        }
      }
     
} // loop end