Skip to content

Instantly share code, notes, and snippets.

@dquadros
Forked from SeeJayDee/tiny_IRremote.cpp
Last active July 28, 2019 18:39
Show Gist options
  • Select an option

  • Save dquadros/2df488663eb1f59994d6d142f61ed3af to your computer and use it in GitHub Desktop.

Select an option

Save dquadros/2df488663eb1f59994d6d142f61ed3af to your computer and use it in GitHub Desktop.

Revisions

  1. dquadros revised this gist Jun 30, 2018. 3 changed files with 99 additions and 10 deletions.
    73 changes: 69 additions & 4 deletions tiny_IRremote.cpp
    Original file line number Diff line number Diff line change
    @@ -1,5 +1,11 @@
    /*
    * tiny_IRremote
    *
    * Version 0.3 June, 2018
    * Daniel Quadros
    * Added LG support from https://github.com/z3t0/Arduino-IRremote
    * Fixed NEC_ONE_SPACE
    *
    * Version 0.2 July, 2016
    * Christian D'Abrera
    * Fixed what was originally rather broken code from http://www.gammon.com.au/Arduino/
    @@ -176,6 +182,29 @@ void IRsend::sendRC6(unsigned long data, int nbits)
    space(0); // Turn off at end
    }

    void IRsend::sendLG(unsigned long data, int nbits)
    {
    // Set IR carrier frequency
    enableIROut(38);

    // Header
    mark(LG_HDR_MARK);
    space(LG_HDR_SPACE);
    mark(LG_BIT_MARK);

    // Data
    for (unsigned long mask = 1UL << (nbits - 1); mask; mask >>= 1) {
    if (data & mask) {
    space(LG_ONE_SPACE);
    mark(LG_BIT_MARK);
    } else {
    space(LG_ZERO_SPACE);
    mark(LG_BIT_MARK);
    }
    }
    space(0); // Always end with the LED off
    }

    void IRsend::mark(int time) {
    // Sends an IR mark for the specified number of microseconds.
    // The mark output is modulated at the PWM frequency.
    @@ -367,6 +396,12 @@ int IRrecv::decode(decode_results *results) {
    if (decodeRC6(results)) {
    return DECODED;
    }
    #ifdef DEBUG
    Serial.println("Attempting LG decode");
    #endif
    if (decodeLG(results)) {
    return DECODED;
    }
    if (results->rawlen >= 6) {
    // Only return raw buffer if at least 6 bits
    results->decode_type = UNKNOWN;
    @@ -379,7 +414,7 @@ int IRrecv::decode(decode_results *results) {
    return ERR;
    }

    long IRrecv::decodeNEC(decode_results *results) {
    bool IRrecv::decodeNEC(decode_results *results) {
    long data = 0;
    int offset = 1; // Skip first space
    // Initial mark
    @@ -427,7 +462,7 @@ long IRrecv::decodeNEC(decode_results *results) {
    return DECODED;
    }

    long IRrecv::decodeSony(decode_results *results) {
    bool IRrecv::decodeSony(decode_results *results) {
    long data = 0;
    if (irparams.rawlen < 2 * SONY_BITS + 2) {
    return ERR;
    @@ -513,7 +548,7 @@ int IRrecv::getRClevel(decode_results *results, int *offset, int *used, int t1)
    return val;
    }

    long IRrecv::decodeRC5(decode_results *results) {
    bool IRrecv::decodeRC5(decode_results *results) {
    if (irparams.rawlen < MIN_RC5_SAMPLES + 2) {
    return ERR;
    }
    @@ -548,7 +583,7 @@ long IRrecv::decodeRC5(decode_results *results) {
    return DECODED;
    }

    long IRrecv::decodeRC6(decode_results *results) {
    bool IRrecv::decodeRC6(decode_results *results) {
    if (results->rawlen < MIN_RC6_SAMPLES) {
    return ERR;
    }
    @@ -598,3 +633,33 @@ long IRrecv::decodeRC6(decode_results *results) {
    results->decode_type = RC6;
    return DECODED;
    }

    bool IRrecv::decodeLG(decode_results *results) {
    long data = 0;
    int offset = 1; // Skip first space

    // Check we have the right amount of data
    if (irparams.rawlen < (2 * LG_BITS) + 1 ) return false ;

    // Initial mark/space
    if (!MATCH_MARK(results->rawbuf[offset++], LG_HDR_MARK)) return false ;
    if (!MATCH_SPACE(results->rawbuf[offset++], LG_HDR_SPACE)) return false ;

    for (int i = 0; i < LG_BITS; i++) {
    if (!MATCH_MARK(results->rawbuf[offset++], LG_BIT_MARK)) return false ;

    if (MATCH_SPACE(results->rawbuf[offset], LG_ONE_SPACE)) data = (data << 1) | 1 ;
    else if (MATCH_SPACE(results->rawbuf[offset], LG_ZERO_SPACE)) data = (data << 1) | 0 ;
    else return false ;
    offset++;
    }

    // Stop bit
    if (!MATCH_MARK(results->rawbuf[offset], LG_BIT_MARK)) return false ;

    // Success
    results->bits = LG_BITS;
    results->value = data;
    results->decode_type = LG;
    return true;
    }
    19 changes: 14 additions & 5 deletions tiny_IRremote.h
    Original file line number Diff line number Diff line change
    @@ -1,5 +1,11 @@
    /*
    * tiny_IRremote
    *
    * Version 0.3 June, 2018
    * Daniel Quadros
    * Added LG support from https://github.com/z3t0/Arduino-IRremote
    * Fixed NEC_ONE_SPACE
    *
    * Version 0.2 July, 2016
    * Christian D'Abrera
    * Fixed what was originally rather broken code from http://www.gammon.com.au/Arduino/
    @@ -32,7 +38,7 @@
    // Results returned from the decoder
    class decode_results {
    public:
    int decode_type; // NEC, SONY, RC5, UNKNOWN
    int decode_type; // NEC, SONY, RC5, LG, UNKNOWN
    unsigned long value; // Decoded value
    int bits; // Number of bits in decoded value
    volatile unsigned int *rawbuf; // Raw intervals in .5 us ticks
    @@ -44,6 +50,7 @@ class decode_results {
    #define SONY 2
    #define RC5 3
    #define RC6 4
    #define LG 5
    #define UNKNOWN -1

    // Decoded value for NEC when a repeat code is received
    @@ -60,10 +67,11 @@ class IRrecv
    private:
    // These are called by decode
    int getRClevel(decode_results *results, int *offset, int *used, int t1);
    long decodeNEC(decode_results *results);
    long decodeSony(decode_results *results);
    long decodeRC5(decode_results *results);
    long decodeRC6(decode_results *results);
    bool decodeNEC(decode_results *results);
    bool decodeSony(decode_results *results);
    bool decodeRC5(decode_results *results);
    bool decodeRC6(decode_results *results);
    bool decodeLG(decode_results *results);
    }
    ;

    @@ -83,6 +91,7 @@ class IRsend
    void sendRaw(unsigned int buf[], int len, int hz);
    void sendRC5(unsigned long data, int nbits);
    void sendRC6(unsigned long data, int nbits);
    void sendLG(unsigned long data, int nbits);
    // private:
    void enableIROut(int khz);
    VIRTUAL void mark(int usec);
    17 changes: 16 additions & 1 deletion tiny_IRremoteInt.h
    Original file line number Diff line number Diff line change
    @@ -1,5 +1,11 @@
    /*
    * tiny_IRremote
    *
    * Version 0.3 June, 2018
    * Daniel Quadros
    * Added LG support from https://github.com/z3t0/Arduino-IRremote
    * Fixed NEC_ONE_SPACE
    *
    * Version 0.2 July, 2016
    * Christian D'Abrera
    * Fixed what was originally rather broken code from http://www.gammon.com.au/Arduino/
    @@ -57,7 +63,7 @@
    #define NEC_HDR_MARK 9000
    #define NEC_HDR_SPACE 4500
    #define NEC_BIT_MARK 560
    #define NEC_ONE_SPACE 1600
    #define NEC_ONE_SPACE 1690
    #define NEC_ZERO_SPACE 560
    #define NEC_RPT_SPACE 2250

    @@ -75,6 +81,15 @@
    #define RC6_T1 444
    #define RC6_RPT_LENGTH 46000

    #define LG_BITS 28

    #define LG_HDR_MARK 8000
    #define LG_HDR_SPACE 4000
    #define LG_BIT_MARK 600
    #define LG_ONE_SPACE 1600
    #define LG_ZERO_SPACE 550
    #define LG_RPT_LENGTH 60000

    #define TOLERANCE 25 // percent tolerance in measurements
    #define LTOL (1.0 - TOLERANCE/100.)
    #define UTOL (1.0 + TOLERANCE/100.)
  2. @SeeJayDee SeeJayDee revised this gist Jul 26, 2016. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion tiny_IRremote.cpp
    Original file line number Diff line number Diff line change
    @@ -219,7 +219,7 @@ void IRsend::enableIROut(int khz) {
    // PWM1B = 1: Enable PWM for OCR1B
    GTCCR = _BV(PWM1B);

    // The top value for the timer. The modulation frequency will be SYSCLOCK / 2 / OCR2A.
    // The top value for the timer. The modulation frequency will be SYSCLOCK / OCR1C.
    OCR1C = SYSCLOCK / khz / 1000;
    OCR1B = OCR1C / 3; // 33% duty cycle

  3. @SeeJayDee SeeJayDee revised this gist Jul 26, 2016. 2 changed files with 7 additions and 7 deletions.
    8 changes: 4 additions & 4 deletions tiny_IRremote.cpp
    Original file line number Diff line number Diff line change
    @@ -240,13 +240,13 @@ void IRrecv::enableIRIn() {
    // depending on the reset value (255 to 0)
    TCCR1 = _BV(CS11) | _BV(CS10);

    //Timer2 Overflow Interrupt Enable
    //TIMER1 Overflow Interrupt Enable
    TIMSK |= _BV(TOIE1);




    RESET_TIMER2;
    RESET_TIMER1;

    sei(); // enable interrupts

    @@ -261,7 +261,7 @@ void IRrecv::enableIRIn() {



    // TIMER2 interrupt code to collect raw data.
    // TIMER1 interrupt code to collect raw data.
    // Widths of alternating SPACE, MARK are recorded in rawbuf.
    // Recorded in ticks of 50 microseconds.
    // rawlen counts the number of entries recorded so far.
    @@ -270,7 +270,7 @@ void IRrecv::enableIRIn() {
    // As soon as first MARK arrives, gap width is recorded, ready is cleared, and new logging starts
    ISR(TIM1_OVF_vect)
    {
    RESET_TIMER2;
    RESET_TIMER1;

    uint8_t irdata = (uint8_t)digitalRead(irparams.recvpin);

    6 changes: 3 additions & 3 deletions tiny_IRremoteInt.h
    Original file line number Diff line number Diff line change
    @@ -25,7 +25,7 @@

    #define CLKFUDGE 5 // fudge factor for clock interrupt overhead
    #define CLK 256 // max value for clock (timer 2)
    #define PRESCALE 4 // timer2 clock prescale
    #define PRESCALE 4 // TIMER1 clock prescale
    #if defined (F_CPU)
    #define SYSCLOCK F_CPU // main Arduino clock
    #else
    @@ -46,10 +46,10 @@
    #endif

    // clock timer reset value
    #define INIT_TIMER_COUNT2 (CLK - USECPERTICK*CLKSPERUSEC + CLKFUDGE)
    #define INIT_TIMER_COUNT1 (CLK - USECPERTICK*CLKSPERUSEC + CLKFUDGE)


    #define RESET_TIMER2 TCNT1 = INIT_TIMER_COUNT2
    #define RESET_TIMER1 TCNT1 = INIT_TIMER_COUNT1



  4. @SeeJayDee SeeJayDee created this gist Jul 26, 2016.
    600 changes: 600 additions & 0 deletions tiny_IRremote.cpp
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,600 @@
    /*
    * tiny_IRremote
    * Version 0.2 July, 2016
    * Christian D'Abrera
    * Fixed what was originally rather broken code from http://www.gammon.com.au/Arduino/
    * ...itself based on work by Ken Shirriff.
    *
    * This code was tested for both sending and receiving IR on an ATtiny85 DIP-8 chip.
    * IMPORTANT: IRsend only works from PB4 ("pin 4" according to Arduino). You will need to
    * determine which physical pin this corresponds to for your chip, and connect your transmitter
    * LED there.
    *
    * Copyright 2009 Ken Shirriff
    * For details, see http://arcfn.com/2009/08/multi-protocol-infrared-remote-library.html
    *
    * Interrupt code based on NECIRrcv by Joe Knapp
    * http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1210243556
    * Also influenced by http://zovirl.com/2008/11/12/building-a-universal-remote-with-an-arduino/
    */

    #include "tiny_IRremote.h"
    #include "tiny_IRremoteInt.h"

    // Provides ISR
    #include <avr/interrupt.h>

    volatile irparams_t irparams;

    // These versions of MATCH, MATCH_MARK, and MATCH_SPACE are only for debugging.
    // To use them, set DEBUG in tiny_IRremoteInt.h
    // Normally macros are used for efficiency
    #ifdef DEBUG
    #error debug enabled
    int MATCH(int measured, int desired) {
    Serial.print("Testing: ");
    Serial.print(TICKS_LOW(desired), DEC);
    Serial.print(" <= ");
    Serial.print(measured, DEC);
    Serial.print(" <= ");
    Serial.println(TICKS_HIGH(desired), DEC);
    return measured >= TICKS_LOW(desired) && measured <= TICKS_HIGH(desired);
    }

    int MATCH_MARK(int measured_ticks, int desired_us) {
    Serial.print("Testing mark ");
    Serial.print(measured_ticks * USECPERTICK, DEC);
    Serial.print(" vs ");
    Serial.print(desired_us, DEC);
    Serial.print(": ");
    Serial.print(TICKS_LOW(desired_us + MARK_EXCESS), DEC);
    Serial.print(" <= ");
    Serial.print(measured_ticks, DEC);
    Serial.print(" <= ");
    Serial.println(TICKS_HIGH(desired_us + MARK_EXCESS), DEC);
    return measured_ticks >= TICKS_LOW(desired_us + MARK_EXCESS) && measured_ticks <= TICKS_HIGH(desired_us + MARK_EXCESS);
    }

    int MATCH_SPACE(int measured_ticks, int desired_us) {
    Serial.print("Testing space ");
    Serial.print(measured_ticks * USECPERTICK, DEC);
    Serial.print(" vs ");
    Serial.print(desired_us, DEC);
    Serial.print(": ");
    Serial.print(TICKS_LOW(desired_us - MARK_EXCESS), DEC);
    Serial.print(" <= ");
    Serial.print(measured_ticks, DEC);
    Serial.print(" <= ");
    Serial.println(TICKS_HIGH(desired_us - MARK_EXCESS), DEC);
    return measured_ticks >= TICKS_LOW(desired_us - MARK_EXCESS) && measured_ticks <= TICKS_HIGH(desired_us - MARK_EXCESS);
    }
    #endif

    void IRsend::sendNEC(unsigned long data, int nbits)
    {
    enableIROut(38);
    mark(NEC_HDR_MARK);
    space(NEC_HDR_SPACE);
    for (int i = 0; i < nbits; i++) {
    if (data & TOPBIT) {
    mark(NEC_BIT_MARK);
    space(NEC_ONE_SPACE);
    }
    else {
    mark(NEC_BIT_MARK);
    space(NEC_ZERO_SPACE);
    }
    data <<= 1;
    }
    mark(NEC_BIT_MARK);
    space(0);
    }

    void IRsend::sendSony(unsigned long data, int nbits) {
    enableIROut(40);
    mark(SONY_HDR_MARK);
    space(SONY_HDR_SPACE);
    data = data << (32 - nbits);
    for (int i = 0; i < nbits; i++) {
    if (data & TOPBIT) {
    mark(SONY_ONE_MARK);
    space(SONY_HDR_SPACE);
    }
    else {
    mark(SONY_ZERO_MARK);
    space(SONY_HDR_SPACE);
    }
    data <<= 1;
    }
    }

    void IRsend::sendRaw(unsigned int buf[], int len, int hz)
    {
    enableIROut(hz);
    for (int i = 0; i < len; i++) {
    if (i & 1) {
    space(buf[i]);
    }
    else {
    mark(buf[i]);
    }
    }
    space(0); // Just to be sure
    }

    // Note: first bit must be a one (start bit)
    void IRsend::sendRC5(unsigned long data, int nbits)
    {
    enableIROut(36);
    data = data << (32 - nbits);
    mark(RC5_T1); // First start bit
    space(RC5_T1); // Second start bit
    mark(RC5_T1); // Second start bit
    for (int i = 0; i < nbits; i++) {
    if (data & TOPBIT) {
    space(RC5_T1); // 1 is space, then mark
    mark(RC5_T1);
    }
    else {
    mark(RC5_T1);
    space(RC5_T1);
    }
    data <<= 1;
    }
    space(0); // Turn off at end
    }

    // Caller needs to take care of flipping the toggle bit
    void IRsend::sendRC6(unsigned long data, int nbits)
    {
    enableIROut(36);
    data = data << (32 - nbits);
    mark(RC6_HDR_MARK);
    space(RC6_HDR_SPACE);
    mark(RC6_T1); // start bit
    space(RC6_T1);
    int t;
    for (int i = 0; i < nbits; i++) {
    if (i == 3) {
    // double-wide trailer bit
    t = 2 * RC6_T1;
    }
    else {
    t = RC6_T1;
    }
    if (data & TOPBIT) {
    mark(t);
    space(t);
    }
    else {
    space(t);
    mark(t);
    }

    data <<= 1;
    }
    space(0); // Turn off at end
    }

    void IRsend::mark(int time) {
    // Sends an IR mark for the specified number of microseconds.
    // The mark output is modulated at the PWM frequency.
    GTCCR |= _BV(COM1B1); // Enable pin 3 PWM output (PB4 - Arduino D4)
    delayMicroseconds(time);
    }

    /* Leave pin off for time (given in microseconds) */
    void IRsend::space(int time) {
    // Sends an IR space for the specified number of microseconds.
    // A space is no output, so the PWM output is disabled.
    GTCCR &= ~(_BV(COM1B1)); // Disable pin 3 PWM output (PB4 - Arduino D4)
    delayMicroseconds(time);
    }

    void IRsend::enableIROut(int khz) {
    // Enables IR output. The khz value controls the modulation frequency in kilohertz.
    // The IR output will be on pin 3 (PB4 - Arduino D4) (OC1B).
    // This routine is designed for 36-40KHz; if you use it for other values, it's up to you
    // to make sure it gives reasonable results. (Watch out for overflow / underflow / rounding.)
    // TIMER1 is used in fast PWM mode, with OCR1Ccontrolling the frequency and OCR1B
    // controlling the duty cycle.
    // There is no prescaling, so the output frequency is 8MHz / (2 * OCR1C)
    // To turn the output on and off, we leave the PWM running, but connect and disconnect the output pin.
    // A few hours staring at the ATmega documentation and this will all make sense.
    // See my Secrets of Arduino PWM at http://arcfn.com/2009/07/secrets-of-arduino-pwm.html for details.


    // Disable the Timer1 Interrupt (which is used for receiving IR)
    TIMSK &= ~_BV(TOIE1); //Timer1 Overflow Interrupt


    pinMode(4, OUTPUT); // (PB4 - Arduino D4 - physical pin 3)
    digitalWrite(4, LOW); // When not sending PWM, we want it low


    // CTC1 = 1: TOP value set to OCR1C
    // CS = 0001: No Prescaling
    TCCR1 = _BV(CTC1) | _BV(CS10);

    // PWM1B = 1: Enable PWM for OCR1B
    GTCCR = _BV(PWM1B);

    // The top value for the timer. The modulation frequency will be SYSCLOCK / 2 / OCR2A.
    OCR1C = SYSCLOCK / khz / 1000;
    OCR1B = OCR1C / 3; // 33% duty cycle

    }

    IRrecv::IRrecv(int recvpin)
    {
    irparams.recvpin = recvpin;
    }

    // initialization
    void IRrecv::enableIRIn() {
    // setup pulse clock timer interrupt
    GTCCR = 0; // normal, non-PWM mode

    //Prescale /4 (8M/4 = 0.5 microseconds per tick)
    // Therefore, the timer interval can range from 0.5 to 128 microseconds
    // depending on the reset value (255 to 0)
    TCCR1 = _BV(CS11) | _BV(CS10);

    //Timer2 Overflow Interrupt Enable
    TIMSK |= _BV(TOIE1);




    RESET_TIMER2;

    sei(); // enable interrupts

    // initialize state machine variables
    irparams.rcvstate = STATE_IDLE;
    irparams.rawlen = 0;


    // set pin modes
    pinMode(irparams.recvpin, INPUT);
    }



    // TIMER2 interrupt code to collect raw data.
    // Widths of alternating SPACE, MARK are recorded in rawbuf.
    // Recorded in ticks of 50 microseconds.
    // rawlen counts the number of entries recorded so far.
    // First entry is the SPACE between transmissions.
    // As soon as a SPACE gets long, ready is set, state switches to IDLE, timing of SPACE continues.
    // As soon as first MARK arrives, gap width is recorded, ready is cleared, and new logging starts
    ISR(TIM1_OVF_vect)
    {
    RESET_TIMER2;

    uint8_t irdata = (uint8_t)digitalRead(irparams.recvpin);

    irparams.timer++; // One more 50us tick
    if (irparams.rawlen >= RAWBUF) {
    // Buffer overflow
    irparams.rcvstate = STATE_STOP;
    }
    switch(irparams.rcvstate) {
    case STATE_IDLE: // In the middle of a gap
    if (irdata == MARK) {
    if (irparams.timer < GAP_TICKS) {
    // Not big enough to be a gap.
    irparams.timer = 0;
    }
    else {
    // gap just ended, record duration and start recording transmission
    irparams.rawlen = 0;
    irparams.rawbuf[irparams.rawlen++] = irparams.timer;
    irparams.timer = 0;
    irparams.rcvstate = STATE_MARK;
    }
    }
    break;
    case STATE_MARK: // timing MARK
    if (irdata == SPACE) { // MARK ended, record time
    irparams.rawbuf[irparams.rawlen++] = irparams.timer;
    irparams.timer = 0;
    irparams.rcvstate = STATE_SPACE;
    }
    break;
    case STATE_SPACE: // timing SPACE
    if (irdata == MARK) { // SPACE just ended, record it
    irparams.rawbuf[irparams.rawlen++] = irparams.timer;
    irparams.timer = 0;
    irparams.rcvstate = STATE_MARK;
    }
    else { // SPACE
    if (irparams.timer > GAP_TICKS) {
    // big SPACE, indicates gap between codes
    // Mark current code as ready for processing
    // Switch to STOP
    // Don't reset timer; keep counting space width
    irparams.rcvstate = STATE_STOP;
    }
    }
    break;
    case STATE_STOP: // waiting, measuring gap
    if (irdata == MARK) { // reset gap timer
    irparams.timer = 0;
    }
    break;
    }

    }

    void IRrecv::resume() {
    irparams.rcvstate = STATE_IDLE;
    irparams.rawlen = 0;
    }



    // Decodes the received IR message
    // Returns 0 if no data ready, 1 if data ready.
    // Results of decoding are stored in results
    int IRrecv::decode(decode_results *results) {
    results->rawbuf = irparams.rawbuf;
    results->rawlen = irparams.rawlen;
    if (irparams.rcvstate != STATE_STOP) {
    return ERR;
    }
    #ifdef DEBUG
    Serial.println("Attempting NEC decode");
    #endif
    if (decodeNEC(results)) {
    return DECODED;
    }
    #ifdef DEBUG
    Serial.println("Attempting Sony decode");
    #endif
    if (decodeSony(results)) {
    return DECODED;
    }
    #ifdef DEBUG
    Serial.println("Attempting RC5 decode");
    #endif
    if (decodeRC5(results)) {
    return DECODED;
    }
    #ifdef DEBUG
    Serial.println("Attempting RC6 decode");
    #endif
    if (decodeRC6(results)) {
    return DECODED;
    }
    if (results->rawlen >= 6) {
    // Only return raw buffer if at least 6 bits
    results->decode_type = UNKNOWN;
    results->bits = 0;
    results->value = 0;
    return DECODED;
    }
    // Throw away and start over
    resume();
    return ERR;
    }

    long IRrecv::decodeNEC(decode_results *results) {
    long data = 0;
    int offset = 1; // Skip first space
    // Initial mark
    if (!MATCH_MARK(results->rawbuf[offset], NEC_HDR_MARK)) {
    return ERR;
    }
    offset++;
    // Check for repeat
    if (irparams.rawlen == 4 &&
    MATCH_SPACE(results->rawbuf[offset], NEC_RPT_SPACE) &&
    MATCH_MARK(results->rawbuf[offset+1], NEC_BIT_MARK)) {
    results->bits = 0;
    results->value = REPEAT;
    results->decode_type = NEC;
    return DECODED;
    }
    if (irparams.rawlen < 2 * NEC_BITS + 4) {
    return ERR;
    }
    // Initial space
    if (!MATCH_SPACE(results->rawbuf[offset], NEC_HDR_SPACE)) {
    return ERR;
    }
    offset++;
    for (int i = 0; i < NEC_BITS; i++) {
    if (!MATCH_MARK(results->rawbuf[offset], NEC_BIT_MARK)) {
    return ERR;
    }
    offset++;
    if (MATCH_SPACE(results->rawbuf[offset], NEC_ONE_SPACE)) {
    data = (data << 1) | 1;
    }
    else if (MATCH_SPACE(results->rawbuf[offset], NEC_ZERO_SPACE)) {
    data <<= 1;
    }
    else {
    return ERR;
    }
    offset++;
    }
    // Success
    results->bits = NEC_BITS;
    results->value = data;
    results->decode_type = NEC;
    return DECODED;
    }

    long IRrecv::decodeSony(decode_results *results) {
    long data = 0;
    if (irparams.rawlen < 2 * SONY_BITS + 2) {
    return ERR;
    }
    int offset = 1; // Skip first space
    // Initial mark
    if (!MATCH_MARK(results->rawbuf[offset], SONY_HDR_MARK)) {
    return ERR;
    }
    offset++;

    while (offset + 1 < irparams.rawlen) {
    if (!MATCH_SPACE(results->rawbuf[offset], SONY_HDR_SPACE)) {
    break;
    }
    offset++;
    if (MATCH_MARK(results->rawbuf[offset], SONY_ONE_MARK)) {
    data = (data << 1) | 1;
    }
    else if (MATCH_MARK(results->rawbuf[offset], SONY_ZERO_MARK)) {
    data <<= 1;
    }
    else {
    return ERR;
    }
    offset++;
    }

    // Success
    results->bits = (offset - 1) / 2;
    if (results->bits < 12) {
    results->bits = 0;
    return ERR;
    }
    results->value = data;
    results->decode_type = SONY;
    return DECODED;
    }

    // Gets one undecoded level at a time from the raw buffer.
    // The RC5/6 decoding is easier if the data is broken into time intervals.
    // E.g. if the buffer has MARK for 2 time intervals and SPACE for 1,
    // successive calls to getRClevel will return MARK, MARK, SPACE.
    // offset and used are updated to keep track of the current position.
    // t1 is the time interval for a single bit in microseconds.
    // Returns -1 for error (measured time interval is not a multiple of t1).
    int IRrecv::getRClevel(decode_results *results, int *offset, int *used, int t1) {
    if (*offset >= results->rawlen) {
    // After end of recorded buffer, assume SPACE.
    return SPACE;
    }
    int width = results->rawbuf[*offset];
    int val = ((*offset) % 2) ? MARK : SPACE;
    int correction = (val == MARK) ? MARK_EXCESS : - MARK_EXCESS;

    int avail;
    if (MATCH(width, t1 + correction)) {
    avail = 1;
    }
    else if (MATCH(width, 2*t1 + correction)) {
    avail = 2;
    }
    else if (MATCH(width, 3*t1 + correction)) {
    avail = 3;
    }
    else {
    return -1;
    }

    (*used)++;
    if (*used >= avail) {
    *used = 0;
    (*offset)++;
    }
    #ifdef DEBUG
    if (val == MARK) {
    Serial.println("MARK");
    }
    else {
    Serial.println("SPACE");
    }
    #endif
    return val;
    }

    long IRrecv::decodeRC5(decode_results *results) {
    if (irparams.rawlen < MIN_RC5_SAMPLES + 2) {
    return ERR;
    }
    int offset = 1; // Skip gap space
    long data = 0;
    int used = 0;
    // Get start bits
    if (getRClevel(results, &offset, &used, RC5_T1) != MARK) return ERR;
    if (getRClevel(results, &offset, &used, RC5_T1) != SPACE) return ERR;
    if (getRClevel(results, &offset, &used, RC5_T1) != MARK) return ERR;
    int nbits;
    for (nbits = 0; offset < irparams.rawlen; nbits++) {
    int levelA = getRClevel(results, &offset, &used, RC5_T1);
    int levelB = getRClevel(results, &offset, &used, RC5_T1);
    if (levelA == SPACE && levelB == MARK) {
    // 1 bit
    data = (data << 1) | 1;
    }
    else if (levelA == MARK && levelB == SPACE) {
    // zero bit
    data <<= 1;
    }
    else {
    return ERR;
    }
    }

    // Success
    results->bits = nbits;
    results->value = data;
    results->decode_type = RC5;
    return DECODED;
    }

    long IRrecv::decodeRC6(decode_results *results) {
    if (results->rawlen < MIN_RC6_SAMPLES) {
    return ERR;
    }
    int offset = 1; // Skip first space
    // Initial mark
    if (!MATCH_MARK(results->rawbuf[offset], RC6_HDR_MARK)) {
    return ERR;
    }
    offset++;
    if (!MATCH_SPACE(results->rawbuf[offset], RC6_HDR_SPACE)) {
    return ERR;
    }
    offset++;
    long data = 0;
    int used = 0;
    // Get start bit (1)
    if (getRClevel(results, &offset, &used, RC6_T1) != MARK) return ERR;
    if (getRClevel(results, &offset, &used, RC6_T1) != SPACE) return ERR;
    int nbits;
    for (nbits = 0; offset < results->rawlen; nbits++) {
    int levelA, levelB; // Next two levels
    levelA = getRClevel(results, &offset, &used, RC6_T1);
    if (nbits == 3) {
    // T bit is double wide; make sure second half matches
    if (levelA != getRClevel(results, &offset, &used, RC6_T1)) return ERR;
    }
    levelB = getRClevel(results, &offset, &used, RC6_T1);
    if (nbits == 3) {
    // T bit is double wide; make sure second half matches
    if (levelB != getRClevel(results, &offset, &used, RC6_T1)) return ERR;
    }
    if (levelA == MARK && levelB == SPACE) { // reversed compared to RC5
    // 1 bit
    data = (data << 1) | 1;
    }
    else if (levelA == SPACE && levelB == MARK) {
    // zero bit
    data <<= 1;
    }
    else {
    return ERR; // Error
    }
    }
    // Success
    results->bits = nbits;
    results->value = data;
    results->decode_type = RC6;
    return DECODED;
    }
    102 changes: 102 additions & 0 deletions tiny_IRremote.h
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,102 @@
    /*
    * tiny_IRremote
    * Version 0.2 July, 2016
    * Christian D'Abrera
    * Fixed what was originally rather broken code from http://www.gammon.com.au/Arduino/
    * ...itself based on work by Ken Shirriff.
    *
    * This code was tested for both sending and receiving IR on an ATtiny85 DIP-8 chip.
    * IMPORTANT: IRsend only works from PB4 ("pin 4" according to Arduino). You will need to
    * determine which physical pin this corresponds to for your chip, and connect your transmitter
    * LED there.
    *
    * Copyright 2009 Ken Shirriff
    * For details, see http://arcfn.com/2009/08/multi-protocol-infrared-remote-library.htm http://arcfn.com
    *
    * Interrupt code based on NECIRrcv by Joe Knapp
    * http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1210243556
    * Also influenced by http://zovirl.com/2008/11/12/building-a-universal-remote-with-an-arduino/
    */

    #ifndef tiny_IRremote_h
    #define tiny_IRremote_h

    // The following are compile-time library options.
    // If you change them, recompile the library.
    // If DEBUG is defined, a lot of debugging output will be printed during decoding.
    // TEST must be defined for the IRtest unittests to work. It will make some
    // methods virtual, which will be slightly slower, which is why it is optional.
    // #define DEBUG
    // #define TEST

    // Results returned from the decoder
    class decode_results {
    public:
    int decode_type; // NEC, SONY, RC5, UNKNOWN
    unsigned long value; // Decoded value
    int bits; // Number of bits in decoded value
    volatile unsigned int *rawbuf; // Raw intervals in .5 us ticks
    int rawlen; // Number of records in rawbuf.
    };

    // Values for decode_type
    #define NEC 1
    #define SONY 2
    #define RC5 3
    #define RC6 4
    #define UNKNOWN -1

    // Decoded value for NEC when a repeat code is received
    #define REPEAT 0xffffffff

    // main class for receiving IR
    class IRrecv
    {
    public:
    IRrecv(int recvpin);
    int decode(decode_results *results);
    void enableIRIn();
    void resume();
    private:
    // These are called by decode
    int getRClevel(decode_results *results, int *offset, int *used, int t1);
    long decodeNEC(decode_results *results);
    long decodeSony(decode_results *results);
    long decodeRC5(decode_results *results);
    long decodeRC6(decode_results *results);
    }
    ;

    // Only used for testing; can remove virtual for shorter code
    #ifdef TEST
    #define VIRTUAL virtual
    #else
    #define VIRTUAL
    #endif

    class IRsend
    {
    public:
    IRsend() {}
    void sendNEC(unsigned long data, int nbits);
    void sendSony(unsigned long data, int nbits);
    void sendRaw(unsigned int buf[], int len, int hz);
    void sendRC5(unsigned long data, int nbits);
    void sendRC6(unsigned long data, int nbits);
    // private:
    void enableIROut(int khz);
    VIRTUAL void mark(int usec);
    VIRTUAL void space(int usec);
    }
    ;

    // Some useful constants

    #define USECPERTICK 50 // microseconds per clock interrupt tick
    #define RAWBUF 76 // Length of raw duration buffer

    // Marks tend to be 100us too long, and spaces 100us too short
    // when received due to sensor lag.
    #define MARK_EXCESS 100

    #endif
    126 changes: 126 additions & 0 deletions tiny_IRremoteInt.h
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,126 @@
    /*
    * tiny_IRremote
    * Version 0.2 July, 2016
    * Christian D'Abrera
    * Fixed what was originally rather broken code from http://www.gammon.com.au/Arduino/
    * ...itself based on work by Ken Shirriff.
    *
    * This code was tested for both sending and receiving IR on an ATtiny85 DIP-8 chip.
    * IMPORTANT: IRsend only works from PB4 ("pin 4" according to Arduino). You will need to
    * determine which physical pin this corresponds to for your chip, and connect your transmitter
    * LED there.
    *
    * Copyright 2009 Ken Shirriff
    * For details, see http://arcfn.com/2009/08/multi-protocol-infrared-remote-library.html
    *
    * Interrupt code based on NECIRrcv by Joe Knapp
    * http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1210243556
    * Also influenced by http://zovirl.com/2008/11/12/building-a-universal-remote-with-an-arduino/
    */

    #ifndef tiny_IRremoteint_h
    #define tiny_IRremoteint_h

    #include <Arduino.h>

    #define CLKFUDGE 5 // fudge factor for clock interrupt overhead
    #define CLK 256 // max value for clock (timer 2)
    #define PRESCALE 4 // timer2 clock prescale
    #if defined (F_CPU)
    #define SYSCLOCK F_CPU // main Arduino clock
    #else
    #define SYSCLOCK 8000000 // default ATtiny clock
    #endif
    #define CLKSPERUSEC (SYSCLOCK/PRESCALE/1000000) // timer clocks per microsecond

    #define ERR 0
    #define DECODED 1


    // defines for setting and clearing register bits
    #ifndef cbi
    #define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit))
    #endif
    #ifndef sbi
    #define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit))
    #endif

    // clock timer reset value
    #define INIT_TIMER_COUNT2 (CLK - USECPERTICK*CLKSPERUSEC + CLKFUDGE)


    #define RESET_TIMER2 TCNT1 = INIT_TIMER_COUNT2



    // pulse parameters in usec
    #define NEC_HDR_MARK 9000
    #define NEC_HDR_SPACE 4500
    #define NEC_BIT_MARK 560
    #define NEC_ONE_SPACE 1600
    #define NEC_ZERO_SPACE 560
    #define NEC_RPT_SPACE 2250

    #define SONY_HDR_MARK 2400
    #define SONY_HDR_SPACE 600
    #define SONY_ONE_MARK 1200
    #define SONY_ZERO_MARK 600
    #define SONY_RPT_LENGTH 45000

    #define RC5_T1 889
    #define RC5_RPT_LENGTH 46000

    #define RC6_HDR_MARK 2666
    #define RC6_HDR_SPACE 889
    #define RC6_T1 444
    #define RC6_RPT_LENGTH 46000

    #define TOLERANCE 25 // percent tolerance in measurements
    #define LTOL (1.0 - TOLERANCE/100.)
    #define UTOL (1.0 + TOLERANCE/100.)

    #define _GAP 5000 // Minimum map between transmissions
    #define GAP_TICKS (_GAP/USECPERTICK)

    #define TICKS_LOW(us) (int) (((us)*LTOL/USECPERTICK))
    #define TICKS_HIGH(us) (int) (((us)*UTOL/USECPERTICK + 1))

    #ifndef DEBUG
    #define MATCH(measured_ticks, desired_us) ((measured_ticks) >= TICKS_LOW(desired_us) && (measured_ticks) <= TICKS_HIGH(desired_us))
    #define MATCH_MARK(measured_ticks, desired_us) MATCH(measured_ticks, (desired_us) + MARK_EXCESS)
    #define MATCH_SPACE(measured_ticks, desired_us) MATCH((measured_ticks), (desired_us) - MARK_EXCESS)
    // Debugging versions are in tiny_IRremote.cpp
    #endif

    // receiver states
    #define STATE_IDLE 2
    #define STATE_MARK 3
    #define STATE_SPACE 4
    #define STATE_STOP 5

    // information for the interrupt handler
    typedef struct {
    uint8_t recvpin; // pin for IR data from detector
    uint8_t rcvstate; // state machine
    unsigned int timer; // state timer, counts 50uS ticks.
    unsigned int rawbuf[RAWBUF]; // raw data
    uint8_t rawlen; // counter of entries in rawbuf
    }
    irparams_t;

    // Defined in tiny_IRremote.cpp
    extern volatile irparams_t irparams;

    // IR detector output is active low
    #define MARK 0
    #define SPACE 1

    #define TOPBIT 0x80000000

    #define NEC_BITS 32
    #define SONY_BITS 12
    #define MIN_RC5_SAMPLES 11
    #define MIN_RC6_SAMPLES 1

    #endif