+++ /dev/null
-/*\r
- * IRremote\r
- * Version 0.11 August, 2009\r
- * Copyright 2009 Ken Shirriff\r
- * For details, see http://arcfn.com/2009/08/multi-protocol-infrared-remote-library.html\r
- *\r
- * Interrupt code based on NECIRrcv by Joe Knapp\r
- * http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1210243556\r
- * Also influenced by http://zovirl.com/2008/11/12/building-a-universal-remote-with-an-arduino/\r
- */\r
-\r
-#include "IRremote.h"\r
-#include "IRremoteInt.h"\r
-\r
-// Provides ISR\r
-#include <avr/interrupt.h>\r
-\r
-volatile irparams_t irparams;\r
-\r
-// These versions of MATCH, MATCH_MARK, and MATCH_SPACE are only for debugging.\r
-// To use them, set DEBUG in IRremoteInt.h\r
-// Normally macros are used for efficiency\r
-#ifdef DEBUG\r
-int MATCH(int measured, int desired) {\r
- Serial.print("Testing: ");\r
- Serial.print(TICKS_LOW(desired), DEC);\r
- Serial.print(" <= ");\r
- Serial.print(measured, DEC);\r
- Serial.print(" <= ");\r
- Serial.println(TICKS_HIGH(desired), DEC);\r
- return measured >= TICKS_LOW(desired) && measured <= TICKS_HIGH(desired);\r
-}\r
-\r
-int MATCH_MARK(int measured_ticks, int desired_us) {\r
- Serial.print("Testing mark ");\r
- Serial.print(measured_ticks * USECPERTICK, DEC);\r
- Serial.print(" vs ");\r
- Serial.print(desired_us, DEC);\r
- Serial.print(": ");\r
- Serial.print(TICKS_LOW(desired_us + MARK_EXCESS), DEC);\r
- Serial.print(" <= ");\r
- Serial.print(measured_ticks, DEC);\r
- Serial.print(" <= ");\r
- Serial.println(TICKS_HIGH(desired_us + MARK_EXCESS), DEC);\r
- return measured_ticks >= TICKS_LOW(desired_us + MARK_EXCESS) && measured_ticks <= TICKS_HIGH(desired_us + MARK_EXCESS);\r
-}\r
-\r
-int MATCH_SPACE(int measured_ticks, int desired_us) {\r
- Serial.print("Testing space ");\r
- Serial.print(measured_ticks * USECPERTICK, DEC);\r
- Serial.print(" vs ");\r
- Serial.print(desired_us, DEC);\r
- Serial.print(": ");\r
- Serial.print(TICKS_LOW(desired_us - MARK_EXCESS), DEC);\r
- Serial.print(" <= ");\r
- Serial.print(measured_ticks, DEC);\r
- Serial.print(" <= ");\r
- Serial.println(TICKS_HIGH(desired_us - MARK_EXCESS), DEC);\r
- return measured_ticks >= TICKS_LOW(desired_us - MARK_EXCESS) && measured_ticks <= TICKS_HIGH(desired_us - MARK_EXCESS);\r
-}\r
-#endif\r
-\r
-void IRsend::sendNEC(unsigned long data, int nbits)\r
-{\r
- enableIROut(38);\r
- mark(NEC_HDR_MARK);\r
- space(NEC_HDR_SPACE);\r
- for (int i = 0; i < nbits; i++) {\r
- if (data & TOPBIT) {\r
- mark(NEC_BIT_MARK);\r
- space(NEC_ONE_SPACE);\r
- } \r
- else {\r
- mark(NEC_BIT_MARK);\r
- space(NEC_ZERO_SPACE);\r
- }\r
- data <<= 1;\r
- }\r
- mark(NEC_BIT_MARK);\r
- space(0);\r
-}\r
-\r
-void IRsend::sendSony(unsigned long data, int nbits) {\r
- enableIROut(40);\r
- mark(SONY_HDR_MARK);\r
- space(SONY_HDR_SPACE);\r
- data = data << (32 - nbits);\r
- for (int i = 0; i < nbits; i++) {\r
- if (data & TOPBIT) {\r
- mark(SONY_ONE_MARK);\r
- space(SONY_HDR_SPACE);\r
- } \r
- else {\r
- mark(SONY_ZERO_MARK);\r
- space(SONY_HDR_SPACE);\r
- }\r
- data <<= 1;\r
- }\r
-}\r
-\r
-void IRsend::sendRaw(unsigned int buf[], int len, int hz)\r
-{\r
- enableIROut(hz);\r
- for (int i = 0; i < len; i++) {\r
- if (i & 1) {\r
- space(buf[i]);\r
- } \r
- else {\r
- mark(buf[i]);\r
- }\r
- }\r
- space(0); // Just to be sure\r
-}\r
-\r
-// Note: first bit must be a one (start bit)\r
-void IRsend::sendRC5(unsigned long data, int nbits)\r
-{\r
- enableIROut(36);\r
- data = data << (32 - nbits);\r
- mark(RC5_T1); // First start bit\r
- space(RC5_T1); // Second start bit\r
- mark(RC5_T1); // Second start bit\r
- for (int i = 0; i < nbits; i++) {\r
- if (data & TOPBIT) {\r
- space(RC5_T1); // 1 is space, then mark\r
- mark(RC5_T1);\r
- } \r
- else {\r
- mark(RC5_T1);\r
- space(RC5_T1);\r
- }\r
- data <<= 1;\r
- }\r
- space(0); // Turn off at end\r
-}\r
-\r
-// Caller needs to take care of flipping the toggle bit\r
-void IRsend::sendRC6(unsigned long data, int nbits)\r
-{\r
- enableIROut(36);\r
- data = data << (32 - nbits);\r
- mark(RC6_HDR_MARK);\r
- space(RC6_HDR_SPACE);\r
- mark(RC6_T1); // start bit\r
- space(RC6_T1);\r
- int t;\r
- for (int i = 0; i < nbits; i++) {\r
- if (i == 3) {\r
- // double-wide trailer bit\r
- t = 2 * RC6_T1;\r
- } \r
- else {\r
- t = RC6_T1;\r
- }\r
- if (data & TOPBIT) {\r
- mark(t);\r
- space(t);\r
- } \r
- else {\r
- space(t);\r
- mark(t);\r
- }\r
-\r
- data <<= 1;\r
- }\r
- space(0); // Turn off at end\r
-}\r
-\r
-void IRsend::mark(int time) {\r
- // Sends an IR mark for the specified number of microseconds.\r
- // The mark output is modulated at the PWM frequency.\r
- TCCR2A |= _BV(COM2B1); // Enable pin 3 PWM output\r
- delayMicroseconds(time);\r
-}\r
-\r
-/* Leave pin off for time (given in microseconds) */\r
-void IRsend::space(int time) {\r
- // Sends an IR space for the specified number of microseconds.\r
- // A space is no output, so the PWM output is disabled.\r
- TCCR2A &= ~(_BV(COM2B1)); // Disable pin 3 PWM output\r
- delayMicroseconds(time);\r
-}\r
-\r
-void IRsend::enableIROut(int khz) {\r
- // Enables IR output. The khz value controls the modulation frequency in kilohertz.\r
- // The IR output will be on pin 3 (OC2B).\r
- // This routine is designed for 36-40KHz; if you use it for other values, it's up to you\r
- // to make sure it gives reasonable results. (Watch out for overflow / underflow / rounding.)\r
- // TIMER2 is used in phase-correct PWM mode, with OCR2A controlling the frequency and OCR2B\r
- // controlling the duty cycle.\r
- // There is no prescaling, so the output frequency is 16MHz / (2 * OCR2A)\r
- // To turn the output on and off, we leave the PWM running, but connect and disconnect the output pin.\r
- // A few hours staring at the ATmega documentation and this will all make sense.\r
- // See my Secrets of Arduino PWM at http://arcfn.com/2009/07/secrets-of-arduino-pwm.html for details.\r
-\r
- \r
- // Disable the Timer2 Interrupt (which is used for receiving IR)\r
- TIMSK2 &= ~_BV(TOIE2); //Timer2 Overflow Interrupt\r
- \r
- pinMode(3, OUTPUT);\r
- digitalWrite(3, HIGH); // When not sending PWM, we want it low (invertiert angeschlossen)\r
- \r
- // COM2A = 00: disconnect OC2A\r
- // COM2B = 00: disconnect OC2B; to send signal set to 10: OC2B non-inverted\r
- // WGM2 = 101: phase-correct PWM with OCRA as top\r
- // CS2 = 000: no prescaling\r
- TCCR2A = _BV(WGM20);\r
- TCCR2B = _BV(WGM22) | _BV(CS20);\r
-\r
- // The top value for the timer. The modulation frequency will be SYSCLOCK / 2 / OCR2A.\r
- OCR2A = SYSCLOCK / 2 / khz / 1000;\r
- OCR2B = OCR2A / 4; // 33% duty cycle\r
-}\r
-\r
-IRrecv::IRrecv(int recvpin)\r
-{\r
- irparams.recvpin = recvpin;\r
- irparams.blinkflag = 0;\r
-}\r
-\r
-// initialization\r
-void IRrecv::enableIRIn() {\r
- // setup pulse clock timer interrupt\r
- TCCR2A = 0; // normal mode\r
-\r
- //Prescale /8 (16M/8 = 0.5 microseconds per tick)\r
- // Therefore, the timer interval can range from 0.5 to 128 microseconds\r
- // depending on the reset value (255 to 0)\r
- cbi(TCCR2B,CS22);\r
- sbi(TCCR2B,CS21);\r
- cbi(TCCR2B,CS20);\r
-\r
- //Timer2 Overflow Interrupt Enable\r
- sbi(TIMSK2,TOIE2);\r
-\r
- RESET_TIMER2;\r
-\r
- sei(); // enable interrupts\r
-\r
- // initialize state machine variables\r
- irparams.rcvstate = STATE_IDLE;\r
- irparams.rawlen = 0;\r
-\r
-\r
- // set pin modes\r
- pinMode(irparams.recvpin, INPUT);\r
-}\r
-\r
-// enable/disable blinking of pin 13 on IR processing\r
-void IRrecv::blink13(int blinkflag)\r
-{\r
- irparams.blinkflag = blinkflag;\r
- if (blinkflag)\r
- pinMode(BLINKLED, OUTPUT);\r
-}\r
-\r
-// TIMER2 interrupt code to collect raw data.\r
-// Widths of alternating SPACE, MARK are recorded in rawbuf.\r
-// Recorded in ticks of 50 microseconds.\r
-// rawlen counts the number of entries recorded so far.\r
-// First entry is the SPACE between transmissions.\r
-// As soon as a SPACE gets long, ready is set, state switches to IDLE, timing of SPACE continues.\r
-// As soon as first MARK arrives, gap width is recorded, ready is cleared, and new logging starts\r
-ISR(TIMER2_OVF_vect)\r
-{\r
- RESET_TIMER2;\r
-\r
- uint8_t irdata = (uint8_t)digitalRead(irparams.recvpin);\r
-\r
- irparams.timer++; // One more 50us tick\r
- if (irparams.rawlen >= RAWBUF) {\r
- // Buffer overflow\r
- irparams.rcvstate = STATE_STOP;\r
- }\r
- switch(irparams.rcvstate) {\r
- case STATE_IDLE: // In the middle of a gap\r
- if (irdata == MARK) {\r
- if (irparams.timer < GAP_TICKS) {\r
- // Not big enough to be a gap.\r
- irparams.timer = 0;\r
- } \r
- else {\r
- // gap just ended, record duration and start recording transmission\r
- irparams.rawlen = 0;\r
- irparams.rawbuf[irparams.rawlen++] = irparams.timer;\r
- irparams.timer = 0;\r
- irparams.rcvstate = STATE_MARK;\r
- }\r
- }\r
- break;\r
- case STATE_MARK: // timing MARK\r
- if (irdata == SPACE) { // MARK ended, record time\r
- irparams.rawbuf[irparams.rawlen++] = irparams.timer;\r
- irparams.timer = 0;\r
- irparams.rcvstate = STATE_SPACE;\r
- }\r
- break;\r
- case STATE_SPACE: // timing SPACE\r
- if (irdata == MARK) { // SPACE just ended, record it\r
- irparams.rawbuf[irparams.rawlen++] = irparams.timer;\r
- irparams.timer = 0;\r
- irparams.rcvstate = STATE_MARK;\r
- } \r
- else { // SPACE\r
- if (irparams.timer > GAP_TICKS) {\r
- // big SPACE, indicates gap between codes\r
- // Mark current code as ready for processing\r
- // Switch to STOP\r
- // Don't reset timer; keep counting space width\r
- irparams.rcvstate = STATE_STOP;\r
- } \r
- }\r
- break;\r
- case STATE_STOP: // waiting, measuring gap\r
- if (irdata == MARK) { // reset gap timer\r
- irparams.timer = 0;\r
- }\r
- break;\r
- }\r
-\r
- if (irparams.blinkflag) {\r
- if (irdata == MARK) {\r
- PORTB |= B00100000; // turn pin 13 LED on\r
- } \r
- else {\r
- PORTB &= B11011111; // turn pin 13 LED off\r
- }\r
- }\r
-}\r
-\r
-void IRrecv::resume() {\r
- irparams.rcvstate = STATE_IDLE;\r
- irparams.rawlen = 0;\r
-}\r
-\r
-\r
-\r
-// Decodes the received IR message\r
-// Returns 0 if no data ready, 1 if data ready.\r
-// Results of decoding are stored in results\r
-int IRrecv::decode(decode_results *results) {\r
- results->rawbuf = irparams.rawbuf;\r
- results->rawlen = irparams.rawlen;\r
- if (irparams.rcvstate != STATE_STOP) {\r
- return ERR;\r
- }\r
-#ifdef DEBUG\r
- Serial.println("Attempting NEC decode");\r
-#endif\r
- if (decodeNEC(results)) {\r
- return DECODED;\r
- }\r
-#ifdef DEBUG\r
- Serial.println("Attempting Sony decode");\r
-#endif\r
- if (decodeSony(results)) {\r
- return DECODED;\r
- }\r
-#ifdef DEBUG\r
- Serial.println("Attempting RC5 decode");\r
-#endif \r
- if (decodeRC5(results)) {\r
- return DECODED;\r
- }\r
-#ifdef DEBUG\r
- Serial.println("Attempting RC6 decode");\r
-#endif \r
- if (decodeRC6(results)) {\r
- return DECODED;\r
- }\r
- if (results->rawlen >= 6) {\r
- // Only return raw buffer if at least 6 bits\r
- results->decode_type = UNKNOWN;\r
- results->bits = 0;\r
- results->value = 0;\r
- return DECODED;\r
- }\r
- // Throw away and start over\r
- resume();\r
- return ERR;\r
-}\r
-\r
-long IRrecv::decodeNEC(decode_results *results) {\r
- long data = 0;\r
- int offset = 1; // Skip first space\r
- // Initial mark\r
- if (!MATCH_MARK(results->rawbuf[offset], NEC_HDR_MARK)) {\r
- return ERR;\r
- }\r
- offset++;\r
- // Check for repeat\r
- if (irparams.rawlen == 4 &&\r
- MATCH_SPACE(results->rawbuf[offset], NEC_RPT_SPACE) &&\r
- MATCH_MARK(results->rawbuf[offset+1], NEC_BIT_MARK)) {\r
- results->bits = 0;\r
- results->value = REPEAT;\r
- results->decode_type = NEC;\r
- return DECODED;\r
- }\r
- if (irparams.rawlen < 2 * NEC_BITS + 4) {\r
- return ERR;\r
- }\r
- // Initial space \r
- if (!MATCH_SPACE(results->rawbuf[offset], NEC_HDR_SPACE)) {\r
- return ERR;\r
- }\r
- offset++;\r
- for (int i = 0; i < NEC_BITS; i++) {\r
- if (!MATCH_MARK(results->rawbuf[offset], NEC_BIT_MARK)) {\r
- return ERR;\r
- }\r
- offset++;\r
- if (MATCH_SPACE(results->rawbuf[offset], NEC_ONE_SPACE)) {\r
- data = (data << 1) | 1;\r
- } \r
- else if (MATCH_SPACE(results->rawbuf[offset], NEC_ZERO_SPACE)) {\r
- data <<= 1;\r
- } \r
- else {\r
- return ERR;\r
- }\r
- offset++;\r
- }\r
- // Success\r
- results->bits = NEC_BITS;\r
- results->value = data;\r
- results->decode_type = NEC;\r
- return DECODED;\r
-}\r
-\r
-long IRrecv::decodeSony(decode_results *results) {\r
- long data = 0;\r
- if (irparams.rawlen < 2 * SONY_BITS + 2) {\r
- return ERR;\r
- }\r
- int offset = 1; // Skip first space\r
- // Initial mark\r
- if (!MATCH_MARK(results->rawbuf[offset], SONY_HDR_MARK)) {\r
- return ERR;\r
- }\r
- offset++;\r
-\r
- while (offset + 1 < irparams.rawlen) {\r
- if (!MATCH_SPACE(results->rawbuf[offset], SONY_HDR_SPACE)) {\r
- break;\r
- }\r
- offset++;\r
- if (MATCH_MARK(results->rawbuf[offset], SONY_ONE_MARK)) {\r
- data = (data << 1) | 1;\r
- } \r
- else if (MATCH_MARK(results->rawbuf[offset], SONY_ZERO_MARK)) {\r
- data <<= 1;\r
- } \r
- else {\r
- return ERR;\r
- }\r
- offset++;\r
- }\r
-\r
- // Success\r
- results->bits = (offset - 1) / 2;\r
- if (results->bits < 12) {\r
- results->bits = 0;\r
- return ERR;\r
- }\r
- results->value = data;\r
- results->decode_type = SONY;\r
- return DECODED;\r
-}\r
-\r
-// Gets one undecoded level at a time from the raw buffer.\r
-// The RC5/6 decoding is easier if the data is broken into time intervals.\r
-// E.g. if the buffer has MARK for 2 time intervals and SPACE for 1,\r
-// successive calls to getRClevel will return MARK, MARK, SPACE.\r
-// offset and used are updated to keep track of the current position.\r
-// t1 is the time interval for a single bit in microseconds.\r
-// Returns -1 for error (measured time interval is not a multiple of t1).\r
-int IRrecv::getRClevel(decode_results *results, int *offset, int *used, int t1) {\r
- if (*offset >= results->rawlen) {\r
- // After end of recorded buffer, assume SPACE.\r
- return SPACE;\r
- }\r
- int width = results->rawbuf[*offset];\r
- int val = ((*offset) % 2) ? MARK : SPACE;\r
- int correction = (val == MARK) ? MARK_EXCESS : - MARK_EXCESS;\r
-\r
- int avail;\r
- if (MATCH(width, t1 + correction)) {\r
- avail = 1;\r
- } \r
- else if (MATCH(width, 2*t1 + correction)) {\r
- avail = 2;\r
- } \r
- else if (MATCH(width, 3*t1 + correction)) {\r
- avail = 3;\r
- } \r
- else {\r
- return -1;\r
- }\r
-\r
- (*used)++;\r
- if (*used >= avail) {\r
- *used = 0;\r
- (*offset)++;\r
- }\r
-#ifdef DEBUG\r
- if (val == MARK) {\r
- Serial.println("MARK");\r
- } \r
- else {\r
- Serial.println("SPACE");\r
- }\r
-#endif\r
- return val; \r
-}\r
-\r
-long IRrecv::decodeRC5(decode_results *results) {\r
- if (irparams.rawlen < MIN_RC5_SAMPLES + 2) {\r
- return ERR;\r
- }\r
- int offset = 1; // Skip gap space\r
- long data = 0;\r
- int used = 0;\r
- // Get start bits\r
- if (getRClevel(results, &offset, &used, RC5_T1) != MARK) return ERR;\r
- if (getRClevel(results, &offset, &used, RC5_T1) != SPACE) return ERR;\r
- if (getRClevel(results, &offset, &used, RC5_T1) != MARK) return ERR;\r
- int nbits;\r
- for (nbits = 0; offset < irparams.rawlen; nbits++) {\r
- int levelA = getRClevel(results, &offset, &used, RC5_T1); \r
- int levelB = getRClevel(results, &offset, &used, RC5_T1);\r
- if (levelA == SPACE && levelB == MARK) {\r
- // 1 bit\r
- data = (data << 1) | 1;\r
- } \r
- else if (levelA == MARK && levelB == SPACE) {\r
- // zero bit\r
- data <<= 1;\r
- } \r
- else {\r
- return ERR;\r
- } \r
- }\r
-\r
- // Success\r
- results->bits = nbits;\r
- results->value = data;\r
- results->decode_type = RC5;\r
- return DECODED;\r
-}\r
-\r
-long IRrecv::decodeRC6(decode_results *results) {\r
- if (results->rawlen < MIN_RC6_SAMPLES) {\r
- return ERR;\r
- }\r
- int offset = 1; // Skip first space\r
- // Initial mark\r
- if (!MATCH_MARK(results->rawbuf[offset], RC6_HDR_MARK)) {\r
- return ERR;\r
- }\r
- offset++;\r
- if (!MATCH_SPACE(results->rawbuf[offset], RC6_HDR_SPACE)) {\r
- return ERR;\r
- }\r
- offset++;\r
- long data = 0;\r
- int used = 0;\r
- // Get start bit (1)\r
- if (getRClevel(results, &offset, &used, RC6_T1) != MARK) return ERR;\r
- if (getRClevel(results, &offset, &used, RC6_T1) != SPACE) return ERR;\r
- int nbits;\r
- for (nbits = 0; offset < results->rawlen; nbits++) {\r
- int levelA, levelB; // Next two levels\r
- levelA = getRClevel(results, &offset, &used, RC6_T1); \r
- if (nbits == 3) {\r
- // T bit is double wide; make sure second half matches\r
- if (levelA != getRClevel(results, &offset, &used, RC6_T1)) return ERR;\r
- } \r
- levelB = getRClevel(results, &offset, &used, RC6_T1);\r
- if (nbits == 3) {\r
- // T bit is double wide; make sure second half matches\r
- if (levelB != getRClevel(results, &offset, &used, RC6_T1)) return ERR;\r
- } \r
- if (levelA == MARK && levelB == SPACE) { // reversed compared to RC5\r
- // 1 bit\r
- data = (data << 1) | 1;\r
- } \r
- else if (levelA == SPACE && levelB == MARK) {\r
- // zero bit\r
- data <<= 1;\r
- } \r
- else {\r
- return ERR; // Error\r
- } \r
- }\r
- // Success\r
- results->bits = nbits;\r
- results->value = data;\r
- results->decode_type = RC6;\r
- return DECODED;\r
-}\r