I am working on a generic "read and use any surplus IR remote" inspired by Bill's Youtube video. It works fine with a couple of remotes, but a couple of others (an old LG one and the one that comes with the Elegoo Robot kit) are driving me insane! I finally went back to basics and tried the "problem remotes" using Bill's first simple demo:
/*
IR Receiver Demonstration 1
IR-Rcv-Demo1.ino
Demonstrates IR codes with IR Receiver
Displays results on Serial Monitor
DroneBot Workshop 2017
http://dronebotworkshop.com
*/
// Include IR Remote Library by Ken Shirriff
#include <IRremote.h>
// Define sensor pin
const int RECV_PIN = 2;
// Define IR Receiver and Results Objects
IRrecv irrecv(RECV_PIN);
decode_results results;
void setup(){
// Serial Monitor @ 9600 baud
Serial.begin(9600);
// Enable the IR Receiver
irrecv.enableIRIn();
}
void loop(){
if (irrecv.decode(&results)){
// Print Code in HEX
Serial.println(results.value, HEX);
irrecv.resume();
}
}
All this does is read and display the IR code in hex. It uses the library IRremote.h by Ken Shirriff that is recommended by the demo. Here's an example of the results it gives when successively pressing the same key:
2FB2625F
FFFFFFFF
20DFA85
FFFFFFFF
20DFA857
FFFFFFFF
20DFA857
FFFFFFFF
20DFA857
FFFFFFFF
20DFA857
FFFFFFFF
20DFA857
FFFFFFFF
FFFFFFFF
FFFFFFFF
FFFFFFFF
20DFA857
FFFFFFFF
20DFA857
FFFFFFFF
FFFFFFFF
20DFA857
2FB2625F
FFFFFFFF
20DFA857
FFFFFFFF
2FB2625F
FFFFFFFF
As we can see, they bounce around. We can ignore the FFFFFFFF code because that's just the repeat indication if you hole the key down too long. The problem is 20DFA857, 20DFA85 and 2FB2625F with the same key. Same sort of thing with the Elegoo remote.
I am guessing that the IRremote.h library has a defect or glitch that is causing this. I say so because it's Bill's program that's pretty simple and works fine (consistent results) with a Sony remote. I think that eliminates hardware as I first felt my IR detection module might be iffy. I think the Sony remote has removed that possibility as it is always consistent. You can press it for an hour and still get the same results for each key!
Another reason to suspect the IRremote.h library is the following warnings when it compiles:
/Users/dunphyp/Documents/Arduino/libraries/IRremote/IRremote.cpp: In function 'int MATCH(int, int)':
/Users/dunphyp/Documents/Arduino/libraries/IRremote/IRremote.cpp:58:32: warning: suggest braces around empty body in an 'else' statement [-Wempty-body]
DBG_PRINTLN(F("?; FAILED"));
^
/Users/dunphyp/Documents/Arduino/libraries/IRremote/IRremote.cpp:57:3: warning: this 'else' clause does not guard... [-Wmisleading-indentation]
else
^~~~
/Users/dunphyp/Documents/Arduino/libraries/IRremote/IRremote.cpp:59:3: note: ...this statement, but the latter is misleadingly indented as if it were guarded by the 'else'
return passed;
^~~~~~
/Users/dunphyp/Documents/Arduino/libraries/IRremote/IRremote.cpp: In function 'int MATCH_MARK(int, int)':
/Users/dunphyp/Documents/Arduino/libraries/IRremote/IRremote.cpp:84:32: warning: suggest braces around empty body in an 'else' statement [-Wempty-body]
DBG_PRINTLN(F("?; FAILED"));
^
/Users/dunphyp/Documents/Arduino/libraries/IRremote/IRremote.cpp:83:3: warning: this 'else' clause does not guard... [-Wmisleading-indentation]
else
^~~~
/Users/dunphyp/Documents/Arduino/libraries/IRremote/IRremote.cpp:85:3: note: ...this statement, but the latter is misleadingly indented as if it were guarded by the 'else'
return passed;
^~~~~~
/Users/dunphyp/Documents/Arduino/libraries/IRremote/IRremote.cpp: In function 'int MATCH_SPACE(int, int)':
/Users/dunphyp/Documents/Arduino/libraries/IRremote/IRremote.cpp:110:32: warning: suggest braces around empty body in an 'else' statement [-Wempty-body]
DBG_PRINTLN(F("?; FAILED"));
^
/Users/dunphyp/Documents/Arduino/libraries/IRremote/IRremote.cpp:109:3: warning: this 'else' clause does not guard... [-Wmisleading-indentation]
else
^~~~
/Users/dunphyp/Documents/Arduino/libraries/IRremote/IRremote.cpp:111:3: note: ...this statement, but the latter is misleadingly indented as if it were guarded by the 'else'
return passed;
^~~~~~
All this to ask, does anyone know if there is a better IR library than the one by Ken Shirriff?Β I am using the latest version of the Shirriff library, 2.2.3.
I'd like to get this sorted out because I have a neat project that works well, storing the codes to EEPROM between power cycles, Arduino resets, etc.Β
Paul VE1DX
For what it's worth, this seems limited to remotes that use the NEC codes.Β They are 32-bit and Sony is 12-bit.Β I tried using long int and unsigned long int variables to hold the contents of results.value temporarily before printing. It doesn't seem to make any difference, so I think it's a small defect in IRremote.h..Β This may be unsolvable if the library is inconsistent in handling some models, and I also only have a limited number of remotes to try.Β Oh well! 😀Β
Paul VE1DX
does anyone know if there is a better IR library
Not for the arduino, but if you want to try your project with something like an Rpi using python then the github tompreston/python-lirc may be of interest and I have used it a couple of years ago. Β
@byron thanks for the suggestion.Β I hate to abandon the Arduino approach, but I seem to be out of options.Β I do have a spare Pi, so that might be the best way to proceed.Β IRremote.h simply doesn't work consistently with NEC code based remotes, in my view.Β I shall overcome it!Β Giving up is not something I usually do.
Paul VE1DX
Problem solved! It was hardware. The IR receiver I am using is one from an Elegoo 37-part kit and not very good.Β The problem was exacerbated by my spare IR remote types. The old Sony remote works fine, but the cheaper LG and such remotes don't emit such a strong signal. You have to hold them an inch or two from the receiver for consistent codes to be picked up by the Elegoo detector. If I hold them a foot or two away (or point them a bit sideways) they miss a bit or two, which changes the code that is read and sent to the Arduino.
The IRremote.h library is fine. I suspected as much as it's been around for a decade, has had many revisions and is widely used. Apologies to Ken Shirriff and all the others who added to it on GitHub.
Paul VE1DX
Oh, the errors on the IRremote.h compile is not an issue.Β They are part of an unimplemented debugging code section that seem to not get along with the Arduino compiler.Β That's documented in the IRremote.h source code.
Paul VE1DX
@ve1dx - oh thats good news to hear your project is back on track. Β Now I can earmark a one master control to rule again when you release your project to the wide world. Β What with all this corona virus crashing the stock market just when I was about to liquidate a few shares to pay for some more house improvements, I will probably have to put that project on hold soon. Β Bad news for the wife who wants it all finished, good news for more time to spend on the electronics, bots et al front 😀Β
@byron (and anyone else that is interested.)Β
Finally got my universal remote control programmer thing finished. I have to credit Bill's video for inspiring this. What this thing does is read 6 IR codes from any remote control and "memorize" them. I like to use dights 1,2,3,4,5,6 -- but I suppose any six keys will work. I achieve this by writing the codes as long integers to the EEPROM memory on the Ardunio board. Each one takes 4 bytes, but since the UNO and NANO each have 1024 bytes of EEPROM memory, there's plenty of room. This memory holds through resets and power-offs, as one would expect. I used an Elegoo UNO clone, but I am sure a NANO would work just as well, and the whole thing would fit on a single breadboard.
Β
I've tried it with five different old surplus ones, and they all perform the same. The "gotcha" I struggled with was stray light and/or weaker signals from cheap remotes. To solve this, I carefully bent the receiver "LED" downwards to about 45 degrees, and I put a little homemade cardboard box over it, with one end open. It seems to catch the codes consistently and from 5-8 feet away or more. I haven't had a miss in over five days of testing and tweaking the program.
Β
The outputs toggle LEDs now, but there's no reason why these signals couldn't trigger relays or power transistors if you want to use your old VCR remote to turn lamps on and off, etc.
To program it, put the toggle in the "learn" position. You go from "run" or "operate" mode to "learn" mode by flipping a toggle switch. This toggle turns on a red mode indicator LED and then sequentially flashes the six operational LEDs indicating it's ready to listen. Press a key on your remote, and the flashing led is programmed to react to that code. As soon as it recognizes the code, it rapidly flashes the mode indicator LED for a second or two (so fast it looks like it's vibrating!) and moves on to the next LED. This continues until all six are programmed. Then the mode indicator LED flashes slowly at one-second intervals for 10-seconds indicating the system should be toggled to the run mode. If you don't do this within 10-seconds, it restarts the learning process at LED number 1.
Additionally, I found when programming it, the most common mistake was to forget to move your finger to the next digit on the remote, and it's thus easy to program two outputs with the same code. Every time it starts up, it looks for duplicates, and if it finds them, it flashes the "offending" LEDs a few times to suggest re-programming might be necessary. However, a person may want several keys doing the same thing, so it doesn't force re-programming.
Finally, it's worth mentioning all the information indicated by the flashing LEDs, etc., is sent to the serial monitor. I set it up to use LED sequences of flashes so it could be used without a computer connected. The messages sent to the serial monitor are more natural to follow than memorizing what flashing LEDs at various speeds mean. Since this would typically only be programmed a few times, it probably is more natural to hook it up to your PC and watch the text on the serial monitor.
Paul VE1DX
/*
Toggle 6 LEDs using an IR remote to illustrate
how to re-purpose surpulus remotes so they can be
used to control almost any device.
This sketch learns the manufacturer's codes for
the 1,2,3,4,5,6 and 0 buttons on any IR remote
when the toggle switch is in the "Learn" or program
position. These values are saved in the Arduino's
EEPROM. When the toggle switch is put in the "Run" mode,
these values are used to determine which key is pressed.
6 associated LEDs are then be toggled on and off by
the remote. This is to demonstrate how to learn and
the codes regardless of the type of IR remote. In addition
to LEDs, the outputs can be expanded to turn on or off
devices with relays, high-power transistors,etc.
Paul M Dunphy
March 2020
*/
// Include IR Remote Library developed by Ken Shirriff
#include <IRremote.h>
#include <EEPROM.h>
long int intIRCode;
long int savedIRCodes[6];
long int dupeCheck[6];
// Define pin for the IR sensor
const int Recv_Pin = 2;
// Define pin constants to toggle the LEDs
const int PinOne = 12;
const int PinTwo = 11;
const int PinThree = 10;
const int PinFour = 9;
const int PinFive = 8;
const int PinSix = 7;
// Define pin constants to read and indicate the
// status of Run/Learn toggle switch.
const int switchPin = 4;
const int statusPin = 5;
boolean learnMode; // Used to keep track of which
// mode we are in as per the toggle switch.
// Define integers to remember toggle states of each LED
int togglestate1;
int togglestate2;
int togglestate3;
int togglestate4;
int togglestate5;
int togglestate6;
int current_remote_code;
int remote_code_1;
int remote_code_2;
int remote_code_3;
int remote_code_4;
int remote_code_5;
int remote_code_6;
// Define IR Receiver and Results Objects
IRrecv irrecv(Recv_Pin);
decode_results results;
void EEPROMWritelong(int address, long value)
// Write a 4 byte (32bit) long integer to the EEPROM
// Since they are 4 bytes long, they are stored at
// address to address + 3
{
//Decomposition from a long to 4 bytes by using bitshift.
//One = Most significant -> Four = Least significant byte
byte four = (value & 0xFF);
byte three = ((value >> 8) & 0xFF);
byte two = ((value >> 16) & 0xFF);
byte one = ((value >> 24) & 0xFF);
EEPROM.write(address, four);
EEPROM.write(address + 1, three);
EEPROM.write(address + 2, two);
EEPROM.write(address + 3, one);
}
long EEPROMReadlong(long address)
// Read a 4 byte (32bit) long integer from the EEPROM.
// Since they are 4 bytes long, they were stored at
// address to address + 3
{
long four = EEPROM.read(address);
long three = EEPROM.read(address + 1);
long two = EEPROM.read(address + 2);
long one = EEPROM.read(address + 3);
//Assemble the bytes into a long integer and return
return ((four << 0) & 0xFF) + ((three << 8) & 0xFFFF) + ((two << 16) & 0xFFFFFF) + ((one << 24) & 0xFFFFFFFF);
}
int Flip_LED(int led, int toggle_state)
{
if(toggle_state==0)
{
digitalWrite(led, HIGH);
toggle_state=1;
}
else
{
digitalWrite(led, LOW);
toggle_state=0;
}
return toggle_state;
}
void Reset()
{
// Turn all LEDs off and set the toggle
// flags to off (0)
digitalWrite(PinOne, LOW);
digitalWrite(PinTwo, LOW);
digitalWrite(PinThree, LOW);
digitalWrite(PinFour, LOW);
digitalWrite(PinFive, LOW);
digitalWrite(PinSix, LOW);
togglestate1 = 0;
togglestate2 = 0;
togglestate3 = 0;
togglestate4 = 0;
togglestate5 = 0;
togglestate6 = 0;
}
int learnCodeRead(int pinCode)
{
if (irrecv.decode(&results))
{
pinCode = results.value;
}
return pinCode;
}
void Confirm()
{
int i;
for(i=0; i<=20; i++)
{
digitalWrite(statusPin, HIGH);
delay(50);
digitalWrite(statusPin, LOW);
delay(50);
}
digitalWrite(statusPin, HIGH); // Leave "Learn" LED high
}
void Learn_Mode()
{
const long int repeatKey = 0xFFFFFFFF;
boolean goodCode;
int i, j;
int location;
int pins[6] = {12,11,10,9,8,7};
// Start listening for each in sequence
intIRCode = 0;
location = 0;
goodCode = true;
j = 0;
while ((goodCode=true) and (j<=5))
{
for(i=0; i<=25; i++)
{
digitalWrite(pins[j], HIGH);
delay(200);
intIRCode = learnCodeRead(intIRCode);
digitalWrite(pins[j], LOW);
delay(200);
intIRCode = learnCodeRead(intIRCode);
goodCode = ((intIRCode !=repeatKey) and (intIRCode !=0));
if (goodCode)
{
i=30; // Trick to bail out of loop because 'break'
// doesn't work on loops
}
irrecv.resume(); // Start listening again
}
goodCode = (intIRCode !=repeatKey and intIRCode !=0);
if (goodCode)
{
Serial.print("EEPROM Location = ");Serial.print(location);
Serial.print(" IR code = ");Serial.println(intIRCode,HEX);
EEPROMWritelong(location, intIRCode);
location = location + 4;
j++;
Confirm();
intIRCode = 0;
irrecv.resume(); // Start listening again
}
}
Serial.println();
Serial.println("Put Arduino back in run mode.");
for(i=0; i<=5; i++)
{
digitalWrite(statusPin, HIGH);
delay(1000);
digitalWrite(statusPin, LOW);
delay(1000);
digitalWrite(statusPin, HIGH); // Leave "Learn" LED high
}
Serial.println();
Serial.println("Returning to run mode.");
// Probably don't need to be so drastic here, but
// This is a "reset" to be sure we get out of learn
// mode and restart properly. It's *likely* OK to
// remove the following 4 lines.
delay(50);
Serial.flush();
delay(50);
asm volatile (" jmp 0");
}
void Process_Codes()
{
if (irrecv.decode(&results))
{
if (results.value!=0xFFFFFFFF) {
current_remote_code = results.value;
Serial.println(current_remote_code,HEX);
if (current_remote_code == remote_code_1)
{
togglestate1 = Flip_LED(PinOne,togglestate1);
}
else if (current_remote_code == remote_code_2) {
togglestate2 = Flip_LED(PinTwo,togglestate2);
}
else if (current_remote_code == remote_code_3) {
togglestate3 = Flip_LED(PinThree,togglestate3);
}
else if (current_remote_code == remote_code_4) {
togglestate4 = Flip_LED(PinFour,togglestate4);
}
else if (current_remote_code == remote_code_5) {
togglestate5 = Flip_LED(PinFive,togglestate5);
}
else if (current_remote_code == remote_code_6) {
togglestate6 = Flip_LED(PinSix,togglestate6);
}
else {
Reset();
}
}
delay(500); // Used to get around the rapid string of data
// if a button is held down. Slows down response time
// by introducing a lag in the loop.
irrecv.resume(); // Start listening again
}
}
void setup()
{
int i,j,k;
int location;
int dupeFlash[6] ={12,11,10,9,8,7}; // Pin numbers to flash
// if duplicates found
Serial.begin(9600);
irrecv.enableIRIn(); // Enable the IR Receiver
// Set LED pins as outputs
pinMode(PinOne, OUTPUT);
pinMode(PinTwo, OUTPUT);
pinMode(PinThree, OUTPUT);
pinMode(PinFour, OUTPUT);
pinMode(PinFive, OUTPUT);
pinMode(PinSix, OUTPUT);
Reset(); // Start out with them all off
pinMode(statusPin, OUTPUT);
pinMode(switchPin, INPUT);
//Get codes from last used remote
Serial.println("Reading stored IR remote codes . . . ");
location = 0;
for(j=0; j<=5; j++)
{
savedIRCodes[j] = EEPROMReadlong(location);
Serial.print("EEPROM Location = ");Serial.print(location);
Serial.print(" IR code = ");Serial.println(savedIRCodes[j],HEX);
location = location + 4;
dupeCheck[j]=savedIRCodes[j]; // Save a copy for duplicate checking
}
// Look for consecutive duplicate codes assigned to the
// outputs. We don't look for overall duplicates because
// they are unlikely to happen. Experience has shown that
// during programming, the most likely mistake is to press
// the same key twice on bacl-to-back LEDs. If duplicates
// are found, indicate this by flashing the suspect LEDs.
// There are only 6 LEDs, so it only takes 21 comparisons
// to find any duplicates (6 + 5 + 4 + 3 + 2 + 1 = 21). This
// section could be enhanced to look for any duplicates by
// sorting the array first, etc.
for (i = 0; i <5 - 1; i++)
{
for (j = i + 1; j <6; j++)
{
if (dupeCheck[i] == dupeCheck[j])
{
Serial.println("Duplicate codes found. Suggest re-running learn mode");
for(k=0; k<=5; k++)
{
digitalWrite(dupeFlash[i],HIGH);
digitalWrite(dupeFlash[j],HIGH);
delay(1000);
digitalWrite(dupeFlash[i],LOW);
digitalWrite(dupeFlash[j],LOW);
delay(1000);
}
}
}
}
remote_code_1 = savedIRCodes[0];
remote_code_2 = savedIRCodes[1];
remote_code_3 = savedIRCodes[2];
remote_code_4 = savedIRCodes[3];
remote_code_5 = savedIRCodes[4];
remote_code_6 = savedIRCodes[5];
delay(1000);
Serial.println("codes read.");
Serial.println();
}
void loop()
{
// Check if the toggle is on or off. If it is on (in learn mode)
// the switchPin is HIGH:
learnMode = (digitalRead(switchPin) == HIGH);
if (learnMode)
{
Reset();
digitalWrite(statusPin, HIGH); // Turn learn mode LED on:
Learn_Mode();
Reset();
}
else
{
digitalWrite(statusPin, LOW); // Turn learn mode LED off:
Process_Codes();
}
}
Thats a really neat design. Quite a satisfying project I should think. Β I copied the code and your fritz diagram for a very handy reference. Β I will get properly into my IR project once I've finished the task of completing my bathrooms that were part of a house renovation (which will be put on hold, but I do have to finish off the bits I started). Β Β But I'm starting off right now thinking about a design for my master IR remote that I can print out with my 3d printer. Β I'll be looking at what buttons I have in my parts drawer, I'm sure I have a bunch of momentary contact buttons somewhere. Β Are you thinking about putting your project into an enclosure?
Thanks for sharing, it's much appreciated. 👍Β
@byron I moved this project to a NANO from a UNO and posted all the detail here:
This is as good as it gets.Β I don't know if it of any use to anyone, but I started the discussion here and I guess we can check this one finished.
Paul VE1DX
@ve1dx - that appears to be a good detailed write up and example of its use, I've bookmarked the web page you link to and I will peruse it in detail in a few weeks time. Β Right now the sun is shinning and the grass needs its first cut of the year, but if we are all locked down this could prove to be a nice little project to get busy with. Thanks. 👍Β
I'll up your lockdown and raise you a "state of emergency" (which we are currently under.)Β Not a lot of fun.Β I don't think this project is going anywhere in terms of a PCB or the like.Β It's a beginner or intermediate level at the best.Β I simply wanted to close this thread and move on to something else.Β Working on these things helps me keep my sanity in these bothersome times.Β
I also am trying to keep a beginner level on the forum, because I, for one, can't follow much of the advanced AI topics and detailed hardware/software required to build robots to the detail the expert engineers here are doing.Β The best I could do was put together the Elegoo Robot Kit.Β In fact, after I got that working I stumbled across Bill's video describing exactly the same thing!Β That's how I ended up on the forum. Had I found his video first, the assembly of the kit would have gone easier, but I guess I learned a lot figuring it out on my own.
No sun here!Β A mixture of rain and snow leaving a coating of slush over everything.Β It'll be a month before I'll need to cut any grass.Β We seem to go from shovelling snow to mowing grass with little "spring" or "fall" in between.Β Such is life. 😀Β
@byron I moved this project to a NANO from a UNO and posted all the detail here:
This is as good as it gets.Β I don't know if it of any use to anyone, but I started the discussion here and I guess we can check this one finished.
Paul VE1DX
Great project.Β Thank you for sharing.Β I have bookmarked it for future reference.
Β
I checked out the arduino.cc link. Nice work. One question, the parts list does not show what IR receiver you used.
I ordered some solderable 400 point proto boards today to build this project on.
Β
Thanks for pointing out my oversight.Β I thought I had everything, but I missed the sensor.Β I updated that site so it's there now. I used a KY-022 37.9KHz Infrared IR Sensor Receiver.Β They are available from Amazon, but there's likely a delay because I believe they are shipped from overseas.Β My original one was in one of those 37-part Elegoo sensor kits (also available on Amazon.)Β I suspect these things are not that rare, and there seem to be a lot of similar ones around.Β I think any IR sensor with 3 pins (Signal, Vcc, and GND) would work.
Paul VE1DX