Last active
August 29, 2015 14:03
-
-
Save houseofjeff/e5ae839b0b23c02194e8 to your computer and use it in GitHub Desktop.
Identify a specific IR message with an Arduino
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//------------------------------------------------------------------------------ | |
// 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