Notifications
Clear all

IR Sender Debounce - Sort Of

12 Posts
3 Users
2 Likes
209 Views
Stephen
(@old-plumber)
Member
Joined: 2 years ago
Posts: 7
Topic starter  

Hello All,

I have access to many IR remotes from old appliances and was excited when Bill did the recent "IR Remotes Revisited" tutorial.

I was able to decode numerous unidentified IR senders and have been able to adapt the attached code to include and extra LED and switch it on/off.

My issue is similar to what others have mentioned, i.e. The sender's repeated pulses cause erratic results with the receiver code. In other words, only a VERY quick press of the buttons MIGHT get a single toggle of the pin output.

I inserted "delay()" in various places in the code with various values, but these had no effect.

Finally I found a clear explanation of using "millis()" function, posted on YouTube by Becky Stern.

This I inserted at the start of the "void loop()". This seemed to work for the first button press but then went erratic again.

/*
  IR Remote Emulator - Receiving
   20240320 Works OK, receiver responds to every repeated command, needs delay somewhere !!
  DroneBot Workshop 2023
   https://dronebotworkshop.com 
*/
// Specify protocol(s) - Must be listed before IRremote.hpp
#define DECODE_NEC
// Include required libraries
#include <Arduino.h>
#include <IRremote.hpp>
// IR Receiver pin
#define IR_RECEIVE_PIN 2
// LED Pin
#define LED_PIN 5
#define LED_PIN2 6
// Data received variable
volatile bool irDataReceived = false;
// Lamp variables
volatile bool lampPower = false;
volatile int lampLevel = 255;
volatile bool led2Power = false;
volatile int led2Level = 123;
// Button code variables (Using Pioneer Remote Controller)
uint16_t codePower = 0xD; // Remote Button "A"
uint16_t codeMinus = 0xB; // Remote Button "Volume -"
uint16_t codePlus = 0xA; // Remote Button "Volume +"
uint16_t codeLED2 = 0x12;  // Remote Button "B"
// Debounce IR variables
unsigned long previousMillis = 0;
const long interval = 200; // interval to process IR pulse before second pulse
// Receive Callback
void ReceiveCallbackHandler() {
  // Decode IR data
  IrReceiver.decode();
  //delay(20); // Made no difference !
  // See if it matches one of our control codes
  if (IrReceiver.decodedIRData.command == codePower) {
    // Power button
    // Toggle lamp power variable
    lampPower = !lampPower;
     } else if (IrReceiver.decodedIRData.command == codeMinus) {
    // Minus Button
    // Only change level if lamp is on
    if (lampPower) {
      lampLevel = lampLevel - 16;
      if (lampLevel < 3) {
        // Don't let level drop to zero
        lampLevel = 3;
      }
    }
  } else if (IrReceiver.decodedIRData.command == codePlus) {
    // Plus Button
    // Only change level if lamp is on
    if (lampPower) {
      lampLevel = lampLevel + 16;
        // Don't let level go above 255
      if (lampLevel > 255) {
        lampLevel = 255;
      }
    }
  } else if (IrReceiver.decodedIRData.command == codeLED2) {
        // LED2 button
        // Toggle LED2 power variable
       
        //delay(20); // Made no difference !
       
        led2Power = !led2Power;
        }
     
  // Set data received flag true
  irDataReceived = true;
  // Resume receiving
  IrReceiver.resume();
  // delay(500); // Made no difference !
}
void setup() {
  // Serial monitor
  Serial.begin(115200);
  // Set LED as an output
  pinMode(LED_PIN, OUTPUT);
  pinMode(LED_PIN2, OUTPUT);
  // Set LED off
  digitalWrite(LED_PIN, LOW);
  digitalWrite(LED_PIN2, LOW);
  // Start IR Receiver with LED feedback on BUILTIN LED
  IrReceiver.begin(IR_RECEIVE_PIN, ENABLE_LED_FEEDBACK);
  // Attach Callback Handler
  IrReceiver.registerReceiveCompleteCallback(ReceiveCallbackHandler);
}
void loop() {
//delay(5); // Made no difference !
unsigned long currentMillis = millis();
if (currentMillis - previousMillis >= interval) {
    // save the last time you blinked the LED
    previousMillis = currentMillis;
  // See if new data is available
  if (irDataReceived) {
          // Print variable values
    Serial.print("Power: ");
    Serial.println(lampPower);
    Serial.print("Level: ");
    Serial.println(lampLevel);
    Serial.print("LED2 Power: ");
    Serial.println(led2Power);
    Serial.print("LED2 Level: ");
    Serial.println(led2Level);
       // Set LED Power and Level
    if (lampPower) {
      // Lamp is powered, set level
      analogWrite(LED_PIN, lampLevel);
    } else {
      // Lamp is off
      digitalWrite(LED_PIN, LOW);
    }
        // Set LED2 Power and Level
    if (led2Power) {
      // LED2 is powered, set level
      analogWrite(LED_PIN2, led2Level);
    } else {
      // Lamp is off
      digitalWrite(LED_PIN2, LOW);
    }
    // Set data received flag false
    irDataReceived = false;
  }
}
}

I'm wondering whether it would be less complicated to have separate buttons for on and off,

rather than toggling. I have spare buttons on most of my IR Senders.

If someone could point out my errors in this sketch I would be forever grateful.


   
Quote
Ron
 Ron
(@zander)
Father of a miniature Wookie
Joined: 3 years ago
Posts: 6972
 

@old-plumber Try this

if (millis() - previousMillis > interval) {
// save the last time you blinked the LED
previousMillis = millis();
 

 

 

 

 

First computer 1959. Retired from my own computer company 2004.
Hardware - Expert in 1401, and 360, fairly knowledge in PC plus numerous MPU's and MCU's
Major Languages - Machine language, 360 Macro Assembler, Intel Assembler, PL/I and PL1, Pascal, Basic, C plus numerous job control and scripting languages.
Sure you can learn to be a programmer, it will take the same amount of time for me to learn to be a Doctor.


   
ReplyQuote
Ron
 Ron
(@zander)
Father of a miniature Wookie
Joined: 3 years ago
Posts: 6972
 

@old-plumber I was playing with your code and came up with the following. There were a couple errors that I fixed, and I took the liberty of applying some of my style of code writing. I changed the variables to do with millis code to unsigned long to avoid type conversions and because I am picky.

Yes, I do invoke the millis() call twice, but that is a super fast call and may even be faster than the alternative of saving the millis. In any case it is trivial and eliminates one variable. I also put the update to the previousMillis at the top of the if block, rather than the end. Both have their place, but in your case I think this is more what you want, it is effectively equivalent to allocating an interval block of time to perform 'the stuff' which is different from check again after I do some stuff. Feel free to modify that if I misunderstood your intentions.

void loop() {
  unsigned long previousMillis = 0;
  const unsigned long interval = 200;
#define NOW millis()

  if (NOW > (previousMillis + interval))
  {
    previousMillis = NOW;
    if (irDataReceived) {
      // do stuff
      irDataReceived = false;
    }
  }
}

 

First computer 1959. Retired from my own computer company 2004.
Hardware - Expert in 1401, and 360, fairly knowledge in PC plus numerous MPU's and MCU's
Major Languages - Machine language, 360 Macro Assembler, Intel Assembler, PL/I and PL1, Pascal, Basic, C plus numerous job control and scripting languages.
Sure you can learn to be a programmer, it will take the same amount of time for me to learn to be a Doctor.


   
Inst-Tech reacted
ReplyQuote
Stephen
(@old-plumber)
Member
Joined: 2 years ago
Posts: 7
Topic starter  

Thanks for the efficient code snippet @zander, I've inserted it at various locations, primarily where you advised and then before the relevant command line controlling the desired led.

As expected, all operations in the loop function were slowed, not what's intended.

Somehow the second pulse is getting processed at the same rate as the Sender is repeating the pulse.

I have now included ON and OFF buttons and deleted the "toggle" command, seems to be working.

I appreciate the advice to using millis() instead of delay.

Regards,

Stephen.

 


   
ReplyQuote
Ron
 Ron
(@zander)
Father of a miniature Wookie
Joined: 3 years ago
Posts: 6972
 

@old-plumber Good to hear, but I don't understand the following

As expected, all operations in the loop function were slowed, not what's intended.

Since this is the equivalent of 'bounce', the only way I know of to eliminate that is to wait for the bouncing to stop. That is normally measured in millisecs. Also, I don't see any other useful work being performed in the loop to be 'slowed'.

Good luck.

 

 

 

First computer 1959. Retired from my own computer company 2004.
Hardware - Expert in 1401, and 360, fairly knowledge in PC plus numerous MPU's and MCU's
Major Languages - Machine language, 360 Macro Assembler, Intel Assembler, PL/I and PL1, Pascal, Basic, C plus numerous job control and scripting languages.
Sure you can learn to be a programmer, it will take the same amount of time for me to learn to be a Doctor.


   
ReplyQuote
 Bert
(@shunt)
Member
Joined: 5 months ago
Posts: 15
 

@old-plumber 

Hello Old Plumber,

I have fought this issue for a while, trying different delay approach without success as well.

I have not analysed the library ( to much stuff in there for my electrical brain ) but if the subroutine is called by an interrupt, maybe it explain why adding delays anywhere is not fixing the issue, could be wrong.

Separating the On/Off with 2 buttons also worked for me but hard headed, I wanted to keep the single toggle button. I have change the code a bit and came up with this which worked for me. Hope it will also work for you.

try this and pls let me know what you get

 
good luck
 
 

Bert ( @shunt ) from Montreal


   
ReplyQuote
Stephen
(@old-plumber)
Member
Joined: 2 years ago
Posts: 7
Topic starter  

Hello Bert,

Sorry for slow reply, I have some caring priorities at home hence could not attend to coding.

Also got your private message but it seems I'm not allowed to reply, not old enough in the forum.

I will apply your code this arvo and let you know how it went.

Stephen @old-plumber Barrington NSW Australia


   
ReplyQuote
Stephen
(@old-plumber)
Member
Joined: 2 years ago
Posts: 7
Topic starter  

Hello again Bert,

Your code worked brilliantly. I applied it to my setup, added a second led as well.

I was able to shorten the delay to 100ms before it started to record multiple sender pulses.

Your code is more clear to me as well. I'm not too familiar with interrupts as yet.

I next want to see if I can adapt the code to an AtTiny 412.

That's about as miniature as I can handle.

Thanks for all the interest and help from you and everyone else on this topic.

 

Stephen.


   
ReplyQuote
Ron
 Ron
(@zander)
Father of a miniature Wookie
Joined: 3 years ago
Posts: 6972
 

@old-plumber @shunt There are no interrupt service routines in that code Bert gave you. There is no need for the volatile attribute either, you may want to remove that and see how that changes the behaviour.

Side Note: When replying to someone, click the Reply link on their post so they are notified that there is a post for them to read. It is bottom right next to Quote. You can also add additional nicknames as I did for this post similar to CC in an e-mail.

First computer 1959. Retired from my own computer company 2004.
Hardware - Expert in 1401, and 360, fairly knowledge in PC plus numerous MPU's and MCU's
Major Languages - Machine language, 360 Macro Assembler, Intel Assembler, PL/I and PL1, Pascal, Basic, C plus numerous job control and scripting languages.
Sure you can learn to be a programmer, it will take the same amount of time for me to learn to be a Doctor.


   
ReplyQuote
Stephen
(@old-plumber)
Member
Joined: 2 years ago
Posts: 7
Topic starter  

@zander 

Heeding your advice, I removed the "volatile" qualifiers and, as you say, appear to be unnecessary as there was no change to the code behaviour that I noticed.

Thanks also for the side note.


   
ReplyQuote
Ron
 Ron
(@zander)
Father of a miniature Wookie
Joined: 3 years ago
Posts: 6972
 

@old-plumber volatile tells the compiler to place the variable in RAM and NOT a register. It is especially crucial for variables of type long as it can take more than 1 instruction cycle to access that variable. This can happen if used in an ISR (Interrupt Service Routine). There is more to the subject of ISRs but that is enough for now.

I suspect that at one time there was an ISR in that sample code. It was subsequently changed but not the volatile. You are unlikely to notice any difference, but each time those variables were used the elapsed time was on the order of a few thousandths or greater of a second quicker.

First computer 1959. Retired from my own computer company 2004.
Hardware - Expert in 1401, and 360, fairly knowledge in PC plus numerous MPU's and MCU's
Major Languages - Machine language, 360 Macro Assembler, Intel Assembler, PL/I and PL1, Pascal, Basic, C plus numerous job control and scripting languages.
Sure you can learn to be a programmer, it will take the same amount of time for me to learn to be a Doctor.


   
ReplyQuote
 Bert
(@shunt)
Member
Joined: 5 months ago
Posts: 15
 

@old-plumber @zander.  Hi Stephen , I’m glad to hear that my code worked for you. I have used it to control a remote light in the house and indeed , works real good. My next move is to use it for my kitchen counter leds. 

if I have left a volatile variable from the original code, Ron is right, not needed anymore .

 

enjoy

Bert ( @shunt ) from Montreal


   
Ron reacted
ReplyQuote