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.
@old-plumber Try this
if (millis() - previousMillis > interval) {// save the last time you blinked the LEDpreviousMillis = millis();
First computer 1959. Retired from my own computer company 2004.
Hardware - Expert in 1401, 360, fairly knowledge in PC plus numerous MPU's & 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.
My personal scorecard is now 1 PC hardware fix (circa 1982), 1 open source fix (at age 82), and 2 zero day bugs in a major OS.
@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, 360, fairly knowledge in PC plus numerous MPU's & 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.
My personal scorecard is now 1 PC hardware fix (circa 1982), 1 open source fix (at age 82), and 2 zero day bugs in a major OS.
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.
@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, 360, fairly knowledge in PC plus numerous MPU's & 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.
My personal scorecard is now 1 PC hardware fix (circa 1982), 1 open source fix (at age 82), and 2 zero day bugs in a major OS.
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
Bert ( @shunt ) from Montreal
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
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.
@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, 360, fairly knowledge in PC plus numerous MPU's & 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.
My personal scorecard is now 1 PC hardware fix (circa 1982), 1 open source fix (at age 82), and 2 zero day bugs in a major OS.
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.
@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, 360, fairly knowledge in PC plus numerous MPU's & 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.
My personal scorecard is now 1 PC hardware fix (circa 1982), 1 open source fix (at age 82), and 2 zero day bugs in a major OS.
@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