Mercurial > code > home > repos > homeauto
comparison service/busyboxArduino/IRremote/IRremote.cpp @ 155:e88308cea843
add IRremote
Ignore-this: c295c68f923af8d072841f1d7c1e5ab8
author | drewp@bigasterisk.com |
---|---|
date | Mon, 19 Jan 2015 17:55:26 -0800 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
154:58c47bfe0ce5 | 155:e88308cea843 |
---|---|
1 /* | |
2 * IRremote | |
3 * Version 0.11 August, 2009 | |
4 * Copyright 2009 Ken Shirriff | |
5 * For details, see http://arcfn.com/2009/08/multi-protocol-infrared-remote-library.html | |
6 * | |
7 * Modified by Paul Stoffregen <paul@pjrc.com> to support other boards and timers | |
8 * Modified by Mitra Ardron <mitra@mitra.biz> | |
9 * Added Sanyo and Mitsubishi controllers | |
10 * Modified Sony to spot the repeat codes that some Sony's send | |
11 * | |
12 * Interrupt code based on NECIRrcv by Joe Knapp | |
13 * http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1210243556 | |
14 * Also influenced by http://zovirl.com/2008/11/12/building-a-universal-remote-with-an-arduino/ | |
15 * | |
16 * JVC and Panasonic protocol added by Kristian Lauszus (Thanks to zenwheel and other people at the original blog post) | |
17 * LG added by Darryl Smith (based on the JVC protocol) | |
18 */ | |
19 | |
20 #include "IRremote.h" | |
21 #include "IRremoteInt.h" | |
22 | |
23 // Provides ISR | |
24 #include <avr/interrupt.h> | |
25 | |
26 volatile irparams_t irparams; | |
27 | |
28 // These versions of MATCH, MATCH_MARK, and MATCH_SPACE are only for debugging. | |
29 // To use them, set DEBUG in IRremoteInt.h | |
30 // Normally macros are used for efficiency | |
31 #ifdef DEBUG | |
32 int MATCH(int measured, int desired) { | |
33 Serial.print("Testing: "); | |
34 Serial.print(TICKS_LOW(desired), DEC); | |
35 Serial.print(" <= "); | |
36 Serial.print(measured, DEC); | |
37 Serial.print(" <= "); | |
38 Serial.println(TICKS_HIGH(desired), DEC); | |
39 return measured >= TICKS_LOW(desired) && measured <= TICKS_HIGH(desired); | |
40 } | |
41 | |
42 int MATCH_MARK(int measured_ticks, int desired_us) { | |
43 Serial.print("Testing mark "); | |
44 Serial.print(measured_ticks * USECPERTICK, DEC); | |
45 Serial.print(" vs "); | |
46 Serial.print(desired_us, DEC); | |
47 Serial.print(": "); | |
48 Serial.print(TICKS_LOW(desired_us + MARK_EXCESS), DEC); | |
49 Serial.print(" <= "); | |
50 Serial.print(measured_ticks, DEC); | |
51 Serial.print(" <= "); | |
52 Serial.println(TICKS_HIGH(desired_us + MARK_EXCESS), DEC); | |
53 return measured_ticks >= TICKS_LOW(desired_us + MARK_EXCESS) && measured_ticks <= TICKS_HIGH(desired_us + MARK_EXCESS); | |
54 } | |
55 | |
56 int MATCH_SPACE(int measured_ticks, int desired_us) { | |
57 Serial.print("Testing space "); | |
58 Serial.print(measured_ticks * USECPERTICK, DEC); | |
59 Serial.print(" vs "); | |
60 Serial.print(desired_us, DEC); | |
61 Serial.print(": "); | |
62 Serial.print(TICKS_LOW(desired_us - MARK_EXCESS), DEC); | |
63 Serial.print(" <= "); | |
64 Serial.print(measured_ticks, DEC); | |
65 Serial.print(" <= "); | |
66 Serial.println(TICKS_HIGH(desired_us - MARK_EXCESS), DEC); | |
67 return measured_ticks >= TICKS_LOW(desired_us - MARK_EXCESS) && measured_ticks <= TICKS_HIGH(desired_us - MARK_EXCESS); | |
68 } | |
69 #else | |
70 int MATCH(int measured, int desired) {return measured >= TICKS_LOW(desired) && measured <= TICKS_HIGH(desired);} | |
71 int MATCH_MARK(int measured_ticks, int desired_us) {return MATCH(measured_ticks, (desired_us + MARK_EXCESS));} | |
72 int MATCH_SPACE(int measured_ticks, int desired_us) {return MATCH(measured_ticks, (desired_us - MARK_EXCESS));} | |
73 // Debugging versions are in IRremote.cpp | |
74 #endif | |
75 | |
76 void IRsend::sendNEC(unsigned long data, int nbits) | |
77 { | |
78 enableIROut(38); | |
79 mark(NEC_HDR_MARK); | |
80 space(NEC_HDR_SPACE); | |
81 for (int i = 0; i < nbits; i++) { | |
82 if (data & TOPBIT) { | |
83 mark(NEC_BIT_MARK); | |
84 space(NEC_ONE_SPACE); | |
85 } | |
86 else { | |
87 mark(NEC_BIT_MARK); | |
88 space(NEC_ZERO_SPACE); | |
89 } | |
90 data <<= 1; | |
91 } | |
92 mark(NEC_BIT_MARK); | |
93 space(0); | |
94 } | |
95 | |
96 void IRsend::sendSony(unsigned long data, int nbits) { | |
97 enableIROut(40); | |
98 mark(SONY_HDR_MARK); | |
99 space(SONY_HDR_SPACE); | |
100 data = data << (32 - nbits); | |
101 for (int i = 0; i < nbits; i++) { | |
102 if (data & TOPBIT) { | |
103 mark(SONY_ONE_MARK); | |
104 space(SONY_HDR_SPACE); | |
105 } | |
106 else { | |
107 mark(SONY_ZERO_MARK); | |
108 space(SONY_HDR_SPACE); | |
109 } | |
110 data <<= 1; | |
111 } | |
112 } | |
113 | |
114 void IRsend::sendRaw(unsigned int buf[], int len, int hz) | |
115 { | |
116 enableIROut(hz); | |
117 for (int i = 0; i < len; i++) { | |
118 if (i & 1) { | |
119 space(buf[i]); | |
120 } | |
121 else { | |
122 mark(buf[i]); | |
123 } | |
124 } | |
125 space(0); // Just to be sure | |
126 } | |
127 | |
128 // Note: first bit must be a one (start bit) | |
129 void IRsend::sendRC5(unsigned long data, int nbits) | |
130 { | |
131 enableIROut(36); | |
132 data = data << (32 - nbits); | |
133 mark(RC5_T1); // First start bit | |
134 space(RC5_T1); // Second start bit | |
135 mark(RC5_T1); // Second start bit | |
136 for (int i = 0; i < nbits; i++) { | |
137 if (data & TOPBIT) { | |
138 space(RC5_T1); // 1 is space, then mark | |
139 mark(RC5_T1); | |
140 } | |
141 else { | |
142 mark(RC5_T1); | |
143 space(RC5_T1); | |
144 } | |
145 data <<= 1; | |
146 } | |
147 space(0); // Turn off at end | |
148 } | |
149 | |
150 // Caller needs to take care of flipping the toggle bit | |
151 void IRsend::sendRC6(unsigned long data, int nbits) | |
152 { | |
153 enableIROut(36); | |
154 data = data << (32 - nbits); | |
155 mark(RC6_HDR_MARK); | |
156 space(RC6_HDR_SPACE); | |
157 mark(RC6_T1); // start bit | |
158 space(RC6_T1); | |
159 int t; | |
160 for (int i = 0; i < nbits; i++) { | |
161 if (i == 3) { | |
162 // double-wide trailer bit | |
163 t = 2 * RC6_T1; | |
164 } | |
165 else { | |
166 t = RC6_T1; | |
167 } | |
168 if (data & TOPBIT) { | |
169 mark(t); | |
170 space(t); | |
171 } | |
172 else { | |
173 space(t); | |
174 mark(t); | |
175 } | |
176 | |
177 data <<= 1; | |
178 } | |
179 space(0); // Turn off at end | |
180 } | |
181 void IRsend::sendPanasonic(unsigned int address, unsigned long data) { | |
182 enableIROut(35); | |
183 mark(PANASONIC_HDR_MARK); | |
184 space(PANASONIC_HDR_SPACE); | |
185 | |
186 for(int i=0;i<16;i++) | |
187 { | |
188 mark(PANASONIC_BIT_MARK); | |
189 if (address & 0x8000) { | |
190 space(PANASONIC_ONE_SPACE); | |
191 } else { | |
192 space(PANASONIC_ZERO_SPACE); | |
193 } | |
194 address <<= 1; | |
195 } | |
196 for (int i=0; i < 32; i++) { | |
197 mark(PANASONIC_BIT_MARK); | |
198 if (data & TOPBIT) { | |
199 space(PANASONIC_ONE_SPACE); | |
200 } else { | |
201 space(PANASONIC_ZERO_SPACE); | |
202 } | |
203 data <<= 1; | |
204 } | |
205 mark(PANASONIC_BIT_MARK); | |
206 space(0); | |
207 } | |
208 void IRsend::sendJVC(unsigned long data, int nbits, int repeat) | |
209 { | |
210 enableIROut(38); | |
211 data = data << (32 - nbits); | |
212 if (!repeat){ | |
213 mark(JVC_HDR_MARK); | |
214 space(JVC_HDR_SPACE); | |
215 } | |
216 for (int i = 0; i < nbits; i++) { | |
217 if (data & TOPBIT) { | |
218 mark(JVC_BIT_MARK); | |
219 space(JVC_ONE_SPACE); | |
220 } | |
221 else { | |
222 mark(JVC_BIT_MARK); | |
223 space(JVC_ZERO_SPACE); | |
224 } | |
225 data <<= 1; | |
226 } | |
227 mark(JVC_BIT_MARK); | |
228 space(0); | |
229 } | |
230 | |
231 void IRsend::sendSAMSUNG(unsigned long data, int nbits) | |
232 { | |
233 enableIROut(38); | |
234 mark(SAMSUNG_HDR_MARK); | |
235 space(SAMSUNG_HDR_SPACE); | |
236 for (int i = 0; i < nbits; i++) { | |
237 if (data & TOPBIT) { | |
238 mark(SAMSUNG_BIT_MARK); | |
239 space(SAMSUNG_ONE_SPACE); | |
240 } | |
241 else { | |
242 mark(SAMSUNG_BIT_MARK); | |
243 space(SAMSUNG_ZERO_SPACE); | |
244 } | |
245 data <<= 1; | |
246 } | |
247 mark(SAMSUNG_BIT_MARK); | |
248 space(0); | |
249 } | |
250 | |
251 void IRsend::mark(int time) { | |
252 // Sends an IR mark for the specified number of microseconds. | |
253 // The mark output is modulated at the PWM frequency. | |
254 TIMER_ENABLE_PWM; // Enable pin 3 PWM output | |
255 if (time > 0) delayMicroseconds(time); | |
256 } | |
257 | |
258 /* Leave pin off for time (given in microseconds) */ | |
259 void IRsend::space(int time) { | |
260 // Sends an IR space for the specified number of microseconds. | |
261 // A space is no output, so the PWM output is disabled. | |
262 TIMER_DISABLE_PWM; // Disable pin 3 PWM output | |
263 if (time > 0) delayMicroseconds(time); | |
264 } | |
265 | |
266 void IRsend::enableIROut(int khz) { | |
267 // Enables IR output. The khz value controls the modulation frequency in kilohertz. | |
268 // The IR output will be on pin 3 (OC2B). | |
269 // This routine is designed for 36-40KHz; if you use it for other values, it's up to you | |
270 // to make sure it gives reasonable results. (Watch out for overflow / underflow / rounding.) | |
271 // TIMER2 is used in phase-correct PWM mode, with OCR2A controlling the frequency and OCR2B | |
272 // controlling the duty cycle. | |
273 // There is no prescaling, so the output frequency is 16MHz / (2 * OCR2A) | |
274 // To turn the output on and off, we leave the PWM running, but connect and disconnect the output pin. | |
275 // A few hours staring at the ATmega documentation and this will all make sense. | |
276 // See my Secrets of Arduino PWM at http://arcfn.com/2009/07/secrets-of-arduino-pwm.html for details. | |
277 | |
278 | |
279 // Disable the Timer2 Interrupt (which is used for receiving IR) | |
280 TIMER_DISABLE_INTR; //Timer2 Overflow Interrupt | |
281 | |
282 pinMode(TIMER_PWM_PIN, OUTPUT); | |
283 digitalWrite(TIMER_PWM_PIN, LOW); // When not sending PWM, we want it low | |
284 | |
285 // COM2A = 00: disconnect OC2A | |
286 // COM2B = 00: disconnect OC2B; to send signal set to 10: OC2B non-inverted | |
287 // WGM2 = 101: phase-correct PWM with OCRA as top | |
288 // CS2 = 000: no prescaling | |
289 // The top value for the timer. The modulation frequency will be SYSCLOCK / 2 / OCR2A. | |
290 TIMER_CONFIG_KHZ(khz); | |
291 } | |
292 | |
293 IRrecv::IRrecv(int recvpin) | |
294 { | |
295 irparams.recvpin = recvpin; | |
296 irparams.blinkflag = 0; | |
297 } | |
298 | |
299 // initialization | |
300 void IRrecv::enableIRIn() { | |
301 cli(); | |
302 // setup pulse clock timer interrupt | |
303 //Prescale /8 (16M/8 = 0.5 microseconds per tick) | |
304 // Therefore, the timer interval can range from 0.5 to 128 microseconds | |
305 // depending on the reset value (255 to 0) | |
306 TIMER_CONFIG_NORMAL(); | |
307 | |
308 //Timer2 Overflow Interrupt Enable | |
309 TIMER_ENABLE_INTR; | |
310 | |
311 TIMER_RESET; | |
312 | |
313 sei(); // enable interrupts | |
314 | |
315 // initialize state machine variables | |
316 irparams.rcvstate = STATE_IDLE; | |
317 irparams.rawlen = 0; | |
318 | |
319 // set pin modes | |
320 pinMode(irparams.recvpin, INPUT); | |
321 } | |
322 | |
323 // enable/disable blinking of pin 13 on IR processing | |
324 void IRrecv::blink13(int blinkflag) | |
325 { | |
326 irparams.blinkflag = blinkflag; | |
327 if (blinkflag) | |
328 pinMode(BLINKLED, OUTPUT); | |
329 } | |
330 | |
331 // TIMER2 interrupt code to collect raw data. | |
332 // Widths of alternating SPACE, MARK are recorded in rawbuf. | |
333 // Recorded in ticks of 50 microseconds. | |
334 // rawlen counts the number of entries recorded so far. | |
335 // First entry is the SPACE between transmissions. | |
336 // As soon as a SPACE gets long, ready is set, state switches to IDLE, timing of SPACE continues. | |
337 // As soon as first MARK arrives, gap width is recorded, ready is cleared, and new logging starts | |
338 ISR(TIMER_INTR_NAME) | |
339 { | |
340 TIMER_RESET; | |
341 | |
342 uint8_t irdata = (uint8_t)digitalRead(irparams.recvpin); | |
343 | |
344 irparams.timer++; // One more 50us tick | |
345 if (irparams.rawlen >= RAWBUF) { | |
346 // Buffer overflow | |
347 irparams.rcvstate = STATE_STOP; | |
348 } | |
349 switch(irparams.rcvstate) { | |
350 case STATE_IDLE: // In the middle of a gap | |
351 if (irdata == MARK) { | |
352 if (irparams.timer < GAP_TICKS) { | |
353 // Not big enough to be a gap. | |
354 irparams.timer = 0; | |
355 } | |
356 else { | |
357 // gap just ended, record duration and start recording transmission | |
358 irparams.rawlen = 0; | |
359 irparams.rawbuf[irparams.rawlen++] = irparams.timer; | |
360 irparams.timer = 0; | |
361 irparams.rcvstate = STATE_MARK; | |
362 } | |
363 } | |
364 break; | |
365 case STATE_MARK: // timing MARK | |
366 if (irdata == SPACE) { // MARK ended, record time | |
367 irparams.rawbuf[irparams.rawlen++] = irparams.timer; | |
368 irparams.timer = 0; | |
369 irparams.rcvstate = STATE_SPACE; | |
370 } | |
371 break; | |
372 case STATE_SPACE: // timing SPACE | |
373 if (irdata == MARK) { // SPACE just ended, record it | |
374 irparams.rawbuf[irparams.rawlen++] = irparams.timer; | |
375 irparams.timer = 0; | |
376 irparams.rcvstate = STATE_MARK; | |
377 } | |
378 else { // SPACE | |
379 if (irparams.timer > GAP_TICKS) { | |
380 // big SPACE, indicates gap between codes | |
381 // Mark current code as ready for processing | |
382 // Switch to STOP | |
383 // Don't reset timer; keep counting space width | |
384 irparams.rcvstate = STATE_STOP; | |
385 } | |
386 } | |
387 break; | |
388 case STATE_STOP: // waiting, measuring gap | |
389 if (irdata == MARK) { // reset gap timer | |
390 irparams.timer = 0; | |
391 } | |
392 break; | |
393 } | |
394 | |
395 if (irparams.blinkflag) { | |
396 if (irdata == MARK) { | |
397 BLINKLED_ON(); // turn pin 13 LED on | |
398 } | |
399 else { | |
400 BLINKLED_OFF(); // turn pin 13 LED off | |
401 } | |
402 } | |
403 } | |
404 | |
405 void IRrecv::resume() { | |
406 irparams.rcvstate = STATE_IDLE; | |
407 irparams.rawlen = 0; | |
408 } | |
409 | |
410 | |
411 | |
412 // Decodes the received IR message | |
413 // Returns 0 if no data ready, 1 if data ready. | |
414 // Results of decoding are stored in results | |
415 int IRrecv::decode(decode_results *results) { | |
416 results->rawbuf = irparams.rawbuf; | |
417 results->rawlen = irparams.rawlen; | |
418 if (irparams.rcvstate != STATE_STOP) { | |
419 return ERR; | |
420 } | |
421 #ifdef DEBUG | |
422 Serial.println("Attempting NEC decode"); | |
423 #endif | |
424 if (decodeNEC(results)) { | |
425 return DECODED; | |
426 } | |
427 #ifdef DEBUG | |
428 Serial.println("Attempting Sony decode"); | |
429 #endif | |
430 if (decodeSony(results)) { | |
431 return DECODED; | |
432 } | |
433 #ifdef DEBUG | |
434 Serial.println("Attempting Sanyo decode"); | |
435 #endif | |
436 if (decodeSanyo(results)) { | |
437 return DECODED; | |
438 } | |
439 #ifdef DEBUG | |
440 Serial.println("Attempting Mitsubishi decode"); | |
441 #endif | |
442 if (decodeMitsubishi(results)) { | |
443 return DECODED; | |
444 } | |
445 #ifdef DEBUG | |
446 Serial.println("Attempting RC5 decode"); | |
447 #endif | |
448 if (decodeRC5(results)) { | |
449 return DECODED; | |
450 } | |
451 #ifdef DEBUG | |
452 Serial.println("Attempting RC6 decode"); | |
453 #endif | |
454 if (decodeRC6(results)) { | |
455 return DECODED; | |
456 } | |
457 #ifdef DEBUG | |
458 Serial.println("Attempting Panasonic decode"); | |
459 #endif | |
460 if (decodePanasonic(results)) { | |
461 return DECODED; | |
462 } | |
463 #ifdef DEBUG | |
464 Serial.println("Attempting LG decode"); | |
465 #endif | |
466 if (decodeLG(results)) { | |
467 return DECODED; | |
468 } | |
469 #ifdef DEBUG | |
470 Serial.println("Attempting JVC decode"); | |
471 #endif | |
472 if (decodeJVC(results)) { | |
473 return DECODED; | |
474 } | |
475 #ifdef DEBUG | |
476 Serial.println("Attempting SAMSUNG decode"); | |
477 #endif | |
478 if (decodeSAMSUNG(results)) { | |
479 return DECODED; | |
480 } | |
481 // decodeHash returns a hash on any input. | |
482 // Thus, it needs to be last in the list. | |
483 // If you add any decodes, add them before this. | |
484 if (decodeHash(results)) { | |
485 return DECODED; | |
486 } | |
487 // Throw away and start over | |
488 resume(); | |
489 return ERR; | |
490 } | |
491 | |
492 // NECs have a repeat only 4 items long | |
493 long IRrecv::decodeNEC(decode_results *results) { | |
494 long data = 0; | |
495 int offset = 1; // Skip first space | |
496 // Initial mark | |
497 if (!MATCH_MARK(results->rawbuf[offset], NEC_HDR_MARK)) { | |
498 return ERR; | |
499 } | |
500 offset++; | |
501 // Check for repeat | |
502 if (irparams.rawlen == 4 && | |
503 MATCH_SPACE(results->rawbuf[offset], NEC_RPT_SPACE) && | |
504 MATCH_MARK(results->rawbuf[offset+1], NEC_BIT_MARK)) { | |
505 results->bits = 0; | |
506 results->value = REPEAT; | |
507 results->decode_type = NEC; | |
508 return DECODED; | |
509 } | |
510 if (irparams.rawlen < 2 * NEC_BITS + 4) { | |
511 return ERR; | |
512 } | |
513 // Initial space | |
514 if (!MATCH_SPACE(results->rawbuf[offset], NEC_HDR_SPACE)) { | |
515 return ERR; | |
516 } | |
517 offset++; | |
518 for (int i = 0; i < NEC_BITS; i++) { | |
519 if (!MATCH_MARK(results->rawbuf[offset], NEC_BIT_MARK)) { | |
520 return ERR; | |
521 } | |
522 offset++; | |
523 if (MATCH_SPACE(results->rawbuf[offset], NEC_ONE_SPACE)) { | |
524 data = (data << 1) | 1; | |
525 } | |
526 else if (MATCH_SPACE(results->rawbuf[offset], NEC_ZERO_SPACE)) { | |
527 data <<= 1; | |
528 } | |
529 else { | |
530 return ERR; | |
531 } | |
532 offset++; | |
533 } | |
534 // Success | |
535 results->bits = NEC_BITS; | |
536 results->value = data; | |
537 results->decode_type = NEC; | |
538 return DECODED; | |
539 } | |
540 | |
541 long IRrecv::decodeSony(decode_results *results) { | |
542 long data = 0; | |
543 if (irparams.rawlen < 2 * SONY_BITS + 2) { | |
544 return ERR; | |
545 } | |
546 int offset = 0; // Dont skip first space, check its size | |
547 | |
548 // Some Sony's deliver repeats fast after first | |
549 // unfortunately can't spot difference from of repeat from two fast clicks | |
550 if (results->rawbuf[offset] < SONY_DOUBLE_SPACE_USECS) { | |
551 // Serial.print("IR Gap found: "); | |
552 results->bits = 0; | |
553 results->value = REPEAT; | |
554 results->decode_type = SANYO; | |
555 return DECODED; | |
556 } | |
557 offset++; | |
558 | |
559 // Initial mark | |
560 if (!MATCH_MARK(results->rawbuf[offset], SONY_HDR_MARK)) { | |
561 return ERR; | |
562 } | |
563 offset++; | |
564 | |
565 while (offset + 1 < irparams.rawlen) { | |
566 if (!MATCH_SPACE(results->rawbuf[offset], SONY_HDR_SPACE)) { | |
567 break; | |
568 } | |
569 offset++; | |
570 if (MATCH_MARK(results->rawbuf[offset], SONY_ONE_MARK)) { | |
571 data = (data << 1) | 1; | |
572 } | |
573 else if (MATCH_MARK(results->rawbuf[offset], SONY_ZERO_MARK)) { | |
574 data <<= 1; | |
575 } | |
576 else { | |
577 return ERR; | |
578 } | |
579 offset++; | |
580 } | |
581 | |
582 // Success | |
583 results->bits = (offset - 1) / 2; | |
584 if (results->bits < 12) { | |
585 results->bits = 0; | |
586 return ERR; | |
587 } | |
588 results->value = data; | |
589 results->decode_type = SONY; | |
590 return DECODED; | |
591 } | |
592 | |
593 // I think this is a Sanyo decoder - serial = SA 8650B | |
594 // Looks like Sony except for timings, 48 chars of data and time/space different | |
595 long IRrecv::decodeSanyo(decode_results *results) { | |
596 long data = 0; | |
597 if (irparams.rawlen < 2 * SANYO_BITS + 2) { | |
598 return ERR; | |
599 } | |
600 int offset = 0; // Skip first space | |
601 // Initial space | |
602 /* Put this back in for debugging - note can't use #DEBUG as if Debug on we don't see the repeat cos of the delay | |
603 Serial.print("IR Gap: "); | |
604 Serial.println( results->rawbuf[offset]); | |
605 Serial.println( "test against:"); | |
606 Serial.println(results->rawbuf[offset]); | |
607 */ | |
608 if (results->rawbuf[offset] < SANYO_DOUBLE_SPACE_USECS) { | |
609 // Serial.print("IR Gap found: "); | |
610 results->bits = 0; | |
611 results->value = REPEAT; | |
612 results->decode_type = SANYO; | |
613 return DECODED; | |
614 } | |
615 offset++; | |
616 | |
617 // Initial mark | |
618 if (!MATCH_MARK(results->rawbuf[offset], SANYO_HDR_MARK)) { | |
619 return ERR; | |
620 } | |
621 offset++; | |
622 | |
623 // Skip Second Mark | |
624 if (!MATCH_MARK(results->rawbuf[offset], SANYO_HDR_MARK)) { | |
625 return ERR; | |
626 } | |
627 offset++; | |
628 | |
629 while (offset + 1 < irparams.rawlen) { | |
630 if (!MATCH_SPACE(results->rawbuf[offset], SANYO_HDR_SPACE)) { | |
631 break; | |
632 } | |
633 offset++; | |
634 if (MATCH_MARK(results->rawbuf[offset], SANYO_ONE_MARK)) { | |
635 data = (data << 1) | 1; | |
636 } | |
637 else if (MATCH_MARK(results->rawbuf[offset], SANYO_ZERO_MARK)) { | |
638 data <<= 1; | |
639 } | |
640 else { | |
641 return ERR; | |
642 } | |
643 offset++; | |
644 } | |
645 | |
646 // Success | |
647 results->bits = (offset - 1) / 2; | |
648 if (results->bits < 12) { | |
649 results->bits = 0; | |
650 return ERR; | |
651 } | |
652 results->value = data; | |
653 results->decode_type = SANYO; | |
654 return DECODED; | |
655 } | |
656 | |
657 // Looks like Sony except for timings, 48 chars of data and time/space different | |
658 long IRrecv::decodeMitsubishi(decode_results *results) { | |
659 // Serial.print("?!? decoding Mitsubishi:");Serial.print(irparams.rawlen); Serial.print(" want "); Serial.println( 2 * MITSUBISHI_BITS + 2); | |
660 long data = 0; | |
661 if (irparams.rawlen < 2 * MITSUBISHI_BITS + 2) { | |
662 return ERR; | |
663 } | |
664 int offset = 0; // Skip first space | |
665 // Initial space | |
666 /* Put this back in for debugging - note can't use #DEBUG as if Debug on we don't see the repeat cos of the delay | |
667 Serial.print("IR Gap: "); | |
668 Serial.println( results->rawbuf[offset]); | |
669 Serial.println( "test against:"); | |
670 Serial.println(results->rawbuf[offset]); | |
671 */ | |
672 /* Not seeing double keys from Mitsubishi | |
673 if (results->rawbuf[offset] < MITSUBISHI_DOUBLE_SPACE_USECS) { | |
674 // Serial.print("IR Gap found: "); | |
675 results->bits = 0; | |
676 results->value = REPEAT; | |
677 results->decode_type = MITSUBISHI; | |
678 return DECODED; | |
679 } | |
680 */ | |
681 offset++; | |
682 | |
683 // Typical | |
684 // 14200 7 41 7 42 7 42 7 17 7 17 7 18 7 41 7 18 7 17 7 17 7 18 7 41 8 17 7 17 7 18 7 17 7 | |
685 | |
686 // Initial Space | |
687 if (!MATCH_MARK(results->rawbuf[offset], MITSUBISHI_HDR_SPACE)) { | |
688 return ERR; | |
689 } | |
690 offset++; | |
691 while (offset + 1 < irparams.rawlen) { | |
692 if (MATCH_MARK(results->rawbuf[offset], MITSUBISHI_ONE_MARK)) { | |
693 data = (data << 1) | 1; | |
694 } | |
695 else if (MATCH_MARK(results->rawbuf[offset], MITSUBISHI_ZERO_MARK)) { | |
696 data <<= 1; | |
697 } | |
698 else { | |
699 // Serial.println("A"); Serial.println(offset); Serial.println(results->rawbuf[offset]); | |
700 return ERR; | |
701 } | |
702 offset++; | |
703 if (!MATCH_SPACE(results->rawbuf[offset], MITSUBISHI_HDR_SPACE)) { | |
704 // Serial.println("B"); Serial.println(offset); Serial.println(results->rawbuf[offset]); | |
705 break; | |
706 } | |
707 offset++; | |
708 } | |
709 | |
710 // Success | |
711 results->bits = (offset - 1) / 2; | |
712 if (results->bits < MITSUBISHI_BITS) { | |
713 results->bits = 0; | |
714 return ERR; | |
715 } | |
716 results->value = data; | |
717 results->decode_type = MITSUBISHI; | |
718 return DECODED; | |
719 } | |
720 | |
721 | |
722 // Gets one undecoded level at a time from the raw buffer. | |
723 // The RC5/6 decoding is easier if the data is broken into time intervals. | |
724 // E.g. if the buffer has MARK for 2 time intervals and SPACE for 1, | |
725 // successive calls to getRClevel will return MARK, MARK, SPACE. | |
726 // offset and used are updated to keep track of the current position. | |
727 // t1 is the time interval for a single bit in microseconds. | |
728 // Returns -1 for error (measured time interval is not a multiple of t1). | |
729 int IRrecv::getRClevel(decode_results *results, int *offset, int *used, int t1) { | |
730 if (*offset >= results->rawlen) { | |
731 // After end of recorded buffer, assume SPACE. | |
732 return SPACE; | |
733 } | |
734 int width = results->rawbuf[*offset]; | |
735 int val = ((*offset) % 2) ? MARK : SPACE; | |
736 int correction = (val == MARK) ? MARK_EXCESS : - MARK_EXCESS; | |
737 | |
738 int avail; | |
739 if (MATCH(width, t1 + correction)) { | |
740 avail = 1; | |
741 } | |
742 else if (MATCH(width, 2*t1 + correction)) { | |
743 avail = 2; | |
744 } | |
745 else if (MATCH(width, 3*t1 + correction)) { | |
746 avail = 3; | |
747 } | |
748 else { | |
749 return -1; | |
750 } | |
751 | |
752 (*used)++; | |
753 if (*used >= avail) { | |
754 *used = 0; | |
755 (*offset)++; | |
756 } | |
757 #ifdef DEBUG | |
758 if (val == MARK) { | |
759 Serial.println("MARK"); | |
760 } | |
761 else { | |
762 Serial.println("SPACE"); | |
763 } | |
764 #endif | |
765 return val; | |
766 } | |
767 | |
768 long IRrecv::decodeRC5(decode_results *results) { | |
769 if (irparams.rawlen < MIN_RC5_SAMPLES + 2) { | |
770 return ERR; | |
771 } | |
772 int offset = 1; // Skip gap space | |
773 long data = 0; | |
774 int used = 0; | |
775 // Get start bits | |
776 if (getRClevel(results, &offset, &used, RC5_T1) != MARK) return ERR; | |
777 if (getRClevel(results, &offset, &used, RC5_T1) != SPACE) return ERR; | |
778 if (getRClevel(results, &offset, &used, RC5_T1) != MARK) return ERR; | |
779 int nbits; | |
780 for (nbits = 0; offset < irparams.rawlen; nbits++) { | |
781 int levelA = getRClevel(results, &offset, &used, RC5_T1); | |
782 int levelB = getRClevel(results, &offset, &used, RC5_T1); | |
783 if (levelA == SPACE && levelB == MARK) { | |
784 // 1 bit | |
785 data = (data << 1) | 1; | |
786 } | |
787 else if (levelA == MARK && levelB == SPACE) { | |
788 // zero bit | |
789 data <<= 1; | |
790 } | |
791 else { | |
792 return ERR; | |
793 } | |
794 } | |
795 | |
796 // Success | |
797 results->bits = nbits; | |
798 results->value = data; | |
799 results->decode_type = RC5; | |
800 return DECODED; | |
801 } | |
802 | |
803 long IRrecv::decodeRC6(decode_results *results) { | |
804 if (results->rawlen < MIN_RC6_SAMPLES) { | |
805 return ERR; | |
806 } | |
807 int offset = 1; // Skip first space | |
808 // Initial mark | |
809 if (!MATCH_MARK(results->rawbuf[offset], RC6_HDR_MARK)) { | |
810 return ERR; | |
811 } | |
812 offset++; | |
813 if (!MATCH_SPACE(results->rawbuf[offset], RC6_HDR_SPACE)) { | |
814 return ERR; | |
815 } | |
816 offset++; | |
817 long data = 0; | |
818 int used = 0; | |
819 // Get start bit (1) | |
820 if (getRClevel(results, &offset, &used, RC6_T1) != MARK) return ERR; | |
821 if (getRClevel(results, &offset, &used, RC6_T1) != SPACE) return ERR; | |
822 int nbits; | |
823 for (nbits = 0; offset < results->rawlen; nbits++) { | |
824 int levelA, levelB; // Next two levels | |
825 levelA = getRClevel(results, &offset, &used, RC6_T1); | |
826 if (nbits == 3) { | |
827 // T bit is double wide; make sure second half matches | |
828 if (levelA != getRClevel(results, &offset, &used, RC6_T1)) return ERR; | |
829 } | |
830 levelB = getRClevel(results, &offset, &used, RC6_T1); | |
831 if (nbits == 3) { | |
832 // T bit is double wide; make sure second half matches | |
833 if (levelB != getRClevel(results, &offset, &used, RC6_T1)) return ERR; | |
834 } | |
835 if (levelA == MARK && levelB == SPACE) { // reversed compared to RC5 | |
836 // 1 bit | |
837 data = (data << 1) | 1; | |
838 } | |
839 else if (levelA == SPACE && levelB == MARK) { | |
840 // zero bit | |
841 data <<= 1; | |
842 } | |
843 else { | |
844 return ERR; // Error | |
845 } | |
846 } | |
847 // Success | |
848 results->bits = nbits; | |
849 results->value = data; | |
850 results->decode_type = RC6; | |
851 return DECODED; | |
852 } | |
853 long IRrecv::decodePanasonic(decode_results *results) { | |
854 unsigned long long data = 0; | |
855 int offset = 1; | |
856 | |
857 if (!MATCH_MARK(results->rawbuf[offset], PANASONIC_HDR_MARK)) { | |
858 return ERR; | |
859 } | |
860 offset++; | |
861 if (!MATCH_MARK(results->rawbuf[offset], PANASONIC_HDR_SPACE)) { | |
862 return ERR; | |
863 } | |
864 offset++; | |
865 | |
866 // decode address | |
867 for (int i = 0; i < PANASONIC_BITS; i++) { | |
868 if (!MATCH_MARK(results->rawbuf[offset++], PANASONIC_BIT_MARK)) { | |
869 return ERR; | |
870 } | |
871 if (MATCH_SPACE(results->rawbuf[offset],PANASONIC_ONE_SPACE)) { | |
872 data = (data << 1) | 1; | |
873 } else if (MATCH_SPACE(results->rawbuf[offset],PANASONIC_ZERO_SPACE)) { | |
874 data <<= 1; | |
875 } else { | |
876 return ERR; | |
877 } | |
878 offset++; | |
879 } | |
880 results->value = (unsigned long)data; | |
881 results->panasonicAddress = (unsigned int)(data >> 32); | |
882 results->decode_type = PANASONIC; | |
883 results->bits = PANASONIC_BITS; | |
884 return DECODED; | |
885 } | |
886 | |
887 long IRrecv::decodeLG(decode_results *results) { | |
888 long data = 0; | |
889 int offset = 1; // Skip first space | |
890 | |
891 // Initial mark | |
892 if (!MATCH_MARK(results->rawbuf[offset], LG_HDR_MARK)) { | |
893 return ERR; | |
894 } | |
895 offset++; | |
896 if (irparams.rawlen < 2 * LG_BITS + 1 ) { | |
897 return ERR; | |
898 } | |
899 // Initial space | |
900 if (!MATCH_SPACE(results->rawbuf[offset], LG_HDR_SPACE)) { | |
901 return ERR; | |
902 } | |
903 offset++; | |
904 for (int i = 0; i < LG_BITS; i++) { | |
905 if (!MATCH_MARK(results->rawbuf[offset], LG_BIT_MARK)) { | |
906 return ERR; | |
907 } | |
908 offset++; | |
909 if (MATCH_SPACE(results->rawbuf[offset], LG_ONE_SPACE)) { | |
910 data = (data << 1) | 1; | |
911 } | |
912 else if (MATCH_SPACE(results->rawbuf[offset], LG_ZERO_SPACE)) { | |
913 data <<= 1; | |
914 } | |
915 else { | |
916 return ERR; | |
917 } | |
918 offset++; | |
919 } | |
920 //Stop bit | |
921 if (!MATCH_MARK(results->rawbuf[offset], LG_BIT_MARK)){ | |
922 return ERR; | |
923 } | |
924 // Success | |
925 results->bits = LG_BITS; | |
926 results->value = data; | |
927 results->decode_type = LG; | |
928 return DECODED; | |
929 } | |
930 | |
931 | |
932 long IRrecv::decodeJVC(decode_results *results) { | |
933 long data = 0; | |
934 int offset = 1; // Skip first space | |
935 // Check for repeat | |
936 if (irparams.rawlen - 1 == 33 && | |
937 MATCH_MARK(results->rawbuf[offset], JVC_BIT_MARK) && | |
938 MATCH_MARK(results->rawbuf[irparams.rawlen-1], JVC_BIT_MARK)) { | |
939 results->bits = 0; | |
940 results->value = REPEAT; | |
941 results->decode_type = JVC; | |
942 return DECODED; | |
943 } | |
944 // Initial mark | |
945 if (!MATCH_MARK(results->rawbuf[offset], JVC_HDR_MARK)) { | |
946 return ERR; | |
947 } | |
948 offset++; | |
949 if (irparams.rawlen < 2 * JVC_BITS + 1 ) { | |
950 return ERR; | |
951 } | |
952 // Initial space | |
953 if (!MATCH_SPACE(results->rawbuf[offset], JVC_HDR_SPACE)) { | |
954 return ERR; | |
955 } | |
956 offset++; | |
957 for (int i = 0; i < JVC_BITS; i++) { | |
958 if (!MATCH_MARK(results->rawbuf[offset], JVC_BIT_MARK)) { | |
959 return ERR; | |
960 } | |
961 offset++; | |
962 if (MATCH_SPACE(results->rawbuf[offset], JVC_ONE_SPACE)) { | |
963 data = (data << 1) | 1; | |
964 } | |
965 else if (MATCH_SPACE(results->rawbuf[offset], JVC_ZERO_SPACE)) { | |
966 data <<= 1; | |
967 } | |
968 else { | |
969 return ERR; | |
970 } | |
971 offset++; | |
972 } | |
973 //Stop bit | |
974 if (!MATCH_MARK(results->rawbuf[offset], JVC_BIT_MARK)){ | |
975 return ERR; | |
976 } | |
977 // Success | |
978 results->bits = JVC_BITS; | |
979 results->value = data; | |
980 results->decode_type = JVC; | |
981 return DECODED; | |
982 } | |
983 | |
984 // SAMSUNGs have a repeat only 4 items long | |
985 long IRrecv::decodeSAMSUNG(decode_results *results) { | |
986 long data = 0; | |
987 int offset = 1; // Skip first space | |
988 // Initial mark | |
989 if (!MATCH_MARK(results->rawbuf[offset], SAMSUNG_HDR_MARK)) { | |
990 return ERR; | |
991 } | |
992 offset++; | |
993 // Check for repeat | |
994 if (irparams.rawlen == 4 && | |
995 MATCH_SPACE(results->rawbuf[offset], SAMSUNG_RPT_SPACE) && | |
996 MATCH_MARK(results->rawbuf[offset+1], SAMSUNG_BIT_MARK)) { | |
997 results->bits = 0; | |
998 results->value = REPEAT; | |
999 results->decode_type = SAMSUNG; | |
1000 return DECODED; | |
1001 } | |
1002 if (irparams.rawlen < 2 * SAMSUNG_BITS + 4) { | |
1003 return ERR; | |
1004 } | |
1005 // Initial space | |
1006 if (!MATCH_SPACE(results->rawbuf[offset], SAMSUNG_HDR_SPACE)) { | |
1007 return ERR; | |
1008 } | |
1009 offset++; | |
1010 for (int i = 0; i < SAMSUNG_BITS; i++) { | |
1011 if (!MATCH_MARK(results->rawbuf[offset], SAMSUNG_BIT_MARK)) { | |
1012 return ERR; | |
1013 } | |
1014 offset++; | |
1015 if (MATCH_SPACE(results->rawbuf[offset], SAMSUNG_ONE_SPACE)) { | |
1016 data = (data << 1) | 1; | |
1017 } | |
1018 else if (MATCH_SPACE(results->rawbuf[offset], SAMSUNG_ZERO_SPACE)) { | |
1019 data <<= 1; | |
1020 } | |
1021 else { | |
1022 return ERR; | |
1023 } | |
1024 offset++; | |
1025 } | |
1026 // Success | |
1027 results->bits = SAMSUNG_BITS; | |
1028 results->value = data; | |
1029 results->decode_type = SAMSUNG; | |
1030 return DECODED; | |
1031 } | |
1032 | |
1033 /* ----------------------------------------------------------------------- | |
1034 * hashdecode - decode an arbitrary IR code. | |
1035 * Instead of decoding using a standard encoding scheme | |
1036 * (e.g. Sony, NEC, RC5), the code is hashed to a 32-bit value. | |
1037 * | |
1038 * The algorithm: look at the sequence of MARK signals, and see if each one | |
1039 * is shorter (0), the same length (1), or longer (2) than the previous. | |
1040 * Do the same with the SPACE signals. Hszh the resulting sequence of 0's, | |
1041 * 1's, and 2's to a 32-bit value. This will give a unique value for each | |
1042 * different code (probably), for most code systems. | |
1043 * | |
1044 * http://arcfn.com/2010/01/using-arbitrary-remotes-with-arduino.html | |
1045 */ | |
1046 | |
1047 // Compare two tick values, returning 0 if newval is shorter, | |
1048 // 1 if newval is equal, and 2 if newval is longer | |
1049 // Use a tolerance of 20% | |
1050 int IRrecv::compare(unsigned int oldval, unsigned int newval) { | |
1051 if (newval < oldval * .8) { | |
1052 return 0; | |
1053 } | |
1054 else if (oldval < newval * .8) { | |
1055 return 2; | |
1056 } | |
1057 else { | |
1058 return 1; | |
1059 } | |
1060 } | |
1061 | |
1062 // Use FNV hash algorithm: http://isthe.com/chongo/tech/comp/fnv/#FNV-param | |
1063 #define FNV_PRIME_32 16777619 | |
1064 #define FNV_BASIS_32 2166136261 | |
1065 | |
1066 /* Converts the raw code values into a 32-bit hash code. | |
1067 * Hopefully this code is unique for each button. | |
1068 * This isn't a "real" decoding, just an arbitrary value. | |
1069 */ | |
1070 long IRrecv::decodeHash(decode_results *results) { | |
1071 // Require at least 6 samples to prevent triggering on noise | |
1072 if (results->rawlen < 6) { | |
1073 return ERR; | |
1074 } | |
1075 long hash = FNV_BASIS_32; | |
1076 for (int i = 1; i+2 < results->rawlen; i++) { | |
1077 int value = compare(results->rawbuf[i], results->rawbuf[i+2]); | |
1078 // Add value into the hash | |
1079 hash = (hash * FNV_PRIME_32) ^ value; | |
1080 } | |
1081 results->value = hash; | |
1082 results->bits = 32; | |
1083 results->decode_type = UNKNOWN; | |
1084 return DECODED; | |
1085 } | |
1086 | |
1087 /* Sharp and DISH support by Todd Treece ( http://unionbridge.org/design/ircommand ) | |
1088 | |
1089 The Dish send function needs to be repeated 4 times, and the Sharp function | |
1090 has the necessary repeat built in because of the need to invert the signal. | |
1091 | |
1092 Sharp protocol documentation: | |
1093 http://www.sbprojects.com/knowledge/ir/sharp.htm | |
1094 | |
1095 Here are the LIRC files that I found that seem to match the remote codes | |
1096 from the oscilloscope: | |
1097 | |
1098 Sharp LCD TV: | |
1099 http://lirc.sourceforge.net/remotes/sharp/GA538WJSA | |
1100 | |
1101 DISH NETWORK (echostar 301): | |
1102 http://lirc.sourceforge.net/remotes/echostar/301_501_3100_5100_58xx_59xx | |
1103 | |
1104 For the DISH codes, only send the last for characters of the hex. | |
1105 i.e. use 0x1C10 instead of 0x0000000000001C10 which is listed in the | |
1106 linked LIRC file. | |
1107 */ | |
1108 | |
1109 void IRsend::sendSharpRaw(unsigned long data, int nbits) { | |
1110 enableIROut(38); | |
1111 | |
1112 // Sending codes in bursts of 3 (normal, inverted, normal) makes transmission | |
1113 // much more reliable. That's the exact behaviour of CD-S6470 remote control. | |
1114 for (int n = 0; n < 3; n++) { | |
1115 for (int i = 1 << (nbits-1); i > 0; i>>=1) { | |
1116 if (data & i) { | |
1117 mark(SHARP_BIT_MARK); | |
1118 space(SHARP_ONE_SPACE); | |
1119 } | |
1120 else { | |
1121 mark(SHARP_BIT_MARK); | |
1122 space(SHARP_ZERO_SPACE); | |
1123 } | |
1124 } | |
1125 | |
1126 mark(SHARP_BIT_MARK); | |
1127 space(SHARP_ZERO_SPACE); | |
1128 delay(40); | |
1129 | |
1130 data = data ^ SHARP_TOGGLE_MASK; | |
1131 } | |
1132 } | |
1133 | |
1134 // Sharp send compatible with data obtained through decodeSharp | |
1135 void IRsend::sendSharp(unsigned int address, unsigned int command) { | |
1136 sendSharpRaw((address << 10) | (command << 2) | 2, 15); | |
1137 } | |
1138 | |
1139 void IRsend::sendDISH(unsigned long data, int nbits) { | |
1140 enableIROut(56); | |
1141 mark(DISH_HDR_MARK); | |
1142 space(DISH_HDR_SPACE); | |
1143 for (int i = 0; i < nbits; i++) { | |
1144 if (data & DISH_TOP_BIT) { | |
1145 mark(DISH_BIT_MARK); | |
1146 space(DISH_ONE_SPACE); | |
1147 } | |
1148 else { | |
1149 mark(DISH_BIT_MARK); | |
1150 space(DISH_ZERO_SPACE); | |
1151 } | |
1152 data <<= 1; | |
1153 } | |
1154 } |