This post describes a simple routine that will generate a unique code for each key on an arbitrary remote. The demo application flashes the LED the appropriate number of times when I push the 0-9 button on my remote.
How it works
The IRremote library records the duration of each (modulated) pulse sent by the remote control. Each key on the remote corresponds to a particular code value, which is converted to a particular sequence of pulses. If you know the encoding algorithm, you can determine the code value, and thus the key pressed. However, for many applications it doesn't really matter what the original code value is, as long as you can uniquely distinguish each key. Thus, if you can turn each unique sequence of pulses into a unique value, then this value will indicate the desired key.To do this, I look at the duration of successive pulses. If the pulse is shorter than the previous, I assign 0. If the pulse is the same length, I assign 1. If the pulse is longer, I assign 2. (I compare on-durations with on-durations and off-durations with off-duations.) The result is a sequence of 0's, 1's, and 2's. I hash these values into a 32-bit hash value.
With luck, the 32-bit hash value will be unique for each key. Note that these hash values are arbitrary, and don't have any obvious connection with the underlying code value. Most encoding protocols use two different durations, so comparing shorter vs longer will work, but you can imagine a code with multiple duration values that wouldn't work here. In addition, hash collisions could occur, but are pretty unlikely with a 32-bit hash.
Code
The code can be downloaded from IRhashcode.pde. The sample application prints the "real" decoded value and the hash value. The "real" value will only work for supported protocols (Sony, RC5, RC6, NEC), but the hash value should work for almost any remote control.
The code is pretty straightforward. compare()
compares the two measured durations within the 20% tolerance.
int compare(unsigned int oldval, unsigned int newval) { if (newval < oldval * .8) { return 0; } else if (oldval < newval * .8) { return 2; } else { return 1; } }The actual decoding is fairly simple.
results->rawbuf
holds the measured durations as space, mark, space, mark, ... The loop compares each duration with the next of the same type, and adds that value to the hash result.
#define FNV_PRIME_32 16777619 #define FNV_BASIS_32 2166136261 unsigned long decodeHash(decode_results *results) { unsigned long hash = FNV_BASIS_32; for (int i = 1; i+2 < results->rawlen; i++) { int value = compare(results->rawbuf[i], results->rawbuf[i+2]); // Add value into the hash hash = (hash * FNV_PRIME_32) ^ value; } return hash; }The mystery FNV numbers come from the FNV 32-bit hash function, which combines all the values into a single 32-bit value.
The following example code prints out the "real" decoded value and the hash decoded value to the serial port. You will want to use this code to figure out what hash value is associated with each key on the remote.
void loop() { if (irrecv.decode(&results)) { Serial.print("'real' decode: "); Serial.print(results.value, HEX); Serial.print(", hash decode: "); Serial.println(decodeHash(&results), HEX); irrecv.resume(); // Resume decoding (necessary!) } }Here's a slightly more realistic example. It receives a code from a Philips remote and flashes the LED appropriately. If you press "1", it flashes once, if you press "8" it flashes 8 times, etc. The code simply uses a case statement to match against the pressed key, and blinks that many times.
You will need to modify this to work with your remote. If it doesn't recognize the code, it will print it to the serial output. Put these unrecognized values into the code to make it work with your remote.
#define LEDPIN 13 void blink() { digitalWrite(LEDPIN, HIGH); delay(200); digitalWrite(LEDPIN, LOW); delay(200); } void loop() { if (irrecv.decode(&results)) { unsigned long hash = decodeHash(&results); switch (hash) { case 0x322ddc47: // 0 (10) blink(); // fallthrough case 0xdb78c103: // 9 blink(); case 0xab57dd3b: // 8 blink(); case 0x715cc13f: // 7 blink(); case 0xdc685a5f: // 6 blink(); case 0x85b33f1b: // 5 blink(); case 0x4ff51b3f: // 4 blink(); case 0x15f9ff43: // 3 blink(); case 0x2e81ea9b: // 2 blink(); case 0x260a8662: // 1 blink(); break; default: Serial.print("Unknown "); Serial.println(hash, HEX); } irrecv.resume(); // Resume decoding (necessary!) } }
Debugging
The most likely problem is a key will occasionally have different code values. This is likely due to random errors in measuring the pulses. The code considers any durations within +/- 20% to be equal; you can try increasing this value.If you're using a RC5/RC6 remote, each key will alternate between two different values. In that case, you probably want to use the "real" decoded value, rather than the hashed value, since you can mask out the toggle bit.
If your remote uses a protocol with multiple duration values, you probably won't get unique values from this algorithm.
For other problems, see the IRremote page, which also has a schematic for wiring up the IR detector.