Skip to content

Instantly share code, notes, and snippets.

@houseofjeff
Last active August 29, 2015 14:03
Show Gist options
  • Save houseofjeff/e5ae839b0b23c02194e8 to your computer and use it in GitHub Desktop.
Save houseofjeff/e5ae839b0b23c02194e8 to your computer and use it in GitHub Desktop.
Identify a specific IR message with an Arduino
//------------------------------------------------------------------------------
// Interrupt 0 fires on pin D2
#define IR_PIN 2
// the maximum pulse we'll listen for
#define MAX_PULSE 30000
// the largest message we expect to receive (in bits)
#define MAX_MSG_SIZE 50
// These variables hold the info for the incoming IR message. If we have received
// a message, then these will hold the message length and the pulse width values.
// We'll store up to 50 pulse pairs (high and low), which is more than enough.
volatile uint8_t ir_msg_len = 0;
volatile uint16_t ir_msg[MAX_MSG_SIZE*2]; // pair is high and low pulse
//------------------------------------------------------------------------------
// Initialize the arduino, attach the interrupt that will fire whenever pin 2
// goes HIGH. (On the Uno, interrupt 0 goes to pin D2, but it depends on the
// model
void setup() {
Serial.begin(9600);
attachInterrupt( 0, captureIR, RISING );
Serial.println("Ready to capture message");
}
//------------------------------------------------------------------------------
// The loop function now does whatever you would normally want it to do, but
// each time through the loop, it'll look to see if a message is available, and
// then test to see if it's the target sequence (in my case, the power button
// on an old CD player remote, but use whatever you have around and paste in
// the result from ir_record.
void loop() {
// ... do your own thing ...
if (ir_msg_len > 0) {
if (is_match()) {
Serial.println("You pressed the right button!");
}
else {
Serial.println("You pressed some other button :(");
}
ir_msg_len = 0;
}
}
//------------------------------------------------------------------------------
// captureIR() captures the pulse-width data that is delivered to pin 2 when
// an infrared message triggers the IR sensor.
unsigned long last_time = 0;
const float cycle_time = (1.0/36000.0)*1000000; // 27.7778 uS per cycle
void captureIR() {
// If the previous call came in less than 0.25 seconds ago, just ignore it.
unsigned long now = micros();
if (now - last_time < 250000)
return;
last_time = now;
// Track the pin as it rises and falls and record the time it spends in HIGH
// then LOW state. If we pass MAX_PULSE uS, the message is over.
unsigned long hightime, lowtime;
ir_msg_len = 0;
while (ir_msg_len < MAX_MSG_SIZE*2) {
hightime = pulseIn(IR_PIN, HIGH, MAX_PULSE);
lowtime = pulseIn(IR_PIN, LOW, MAX_PULSE);
if ( (hightime == 0) || (lowtime == 0) ) {
// Finished with message
return;
}
ir_msg[ir_msg_len++] = round(hightime/cycle_time);
ir_msg[ir_msg_len++] = round(lowtime/cycle_time);
}
}
//------------------------------------------------------------------------------
// The messages are often not exactly the same (it's all timing measurements,
// and things vary). Measure the variance (the sum of the errors squared)
// between the expected message and the one we have here.
//
// Allow for up to 3 cycles difference per measurement, that means we allow for
// a delta up to 18 per pulse pair. If the variance is less than 18*len, then
// we call it 'close enough', and if it's more than it is not.
// Replace vv-- this line here --vv to change what signal to listen for.
uint16_t target_seq[16*2] = { 14,17, 15,17, 15,17, 15,17, 15,17, 15,16, 16,16, 48,16, 15,16, 16,16, 47,16, 48,16, 47,15, 47,16, 47,16, 16,16 };
boolean is_match() {
uint32_t sum_of_squares = 0;
// start at two because the first pair can vary wildly based on initial
// timing, but it's possible I should be starting with 1.
for (uint8_t i = 2; i < ir_msg_len; i++) {
int16_t delta = ir_msg[i] - target_seq[i];
sum_of_squares += (delta*delta);
}
// the variance is the sum of the squares of the error, divided by the
// size of the input.
uint16_t variance = sum_of_squares/(sizeof(target_seq)/sizeof(target_seq[0]));
Serial.print("variance: ");
Serial.println(variance);
// Call it a match if the average difference per reading is 3uS or less (squared).
return variance < 3*3;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment