Skip to content

Instantly share code, notes, and snippets.

@happytm
Forked from dquadros/tiny_IRremote.cpp
Created July 28, 2019 18:39
Show Gist options
  • Save happytm/8c9bfd539e810ecfa68997439d54093f to your computer and use it in GitHub Desktop.
Save happytm/8c9bfd539e810ecfa68997439d54093f to your computer and use it in GitHub Desktop.
tiny_IRremote - Arduino IRremote ported to the ATtiny
/*
* 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/
* ...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::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.
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 / OCR1C.
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);
//TIMER1 Overflow Interrupt Enable
TIMSK |= _BV(TOIE1);
RESET_TIMER1;
sei(); // enable interrupts
// initialize state machine variables
irparams.rcvstate = STATE_IDLE;
irparams.rawlen = 0;
// set pin modes
pinMode(irparams.recvpin, INPUT);
}
// 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.
// 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_TIMER1;
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;
}
#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;
results->bits = 0;
results->value = 0;
return DECODED;
}
// Throw away and start over
resume();
return ERR;
}
bool 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;
}
bool 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;
}
bool 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;
}
bool 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;
}
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;
}
/*
* 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/
* ...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, 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
int rawlen; // Number of records in rawbuf.
};
// Values for decode_type
#define NEC 1
#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
#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);
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);
}
;
// 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);
void sendLG(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
/*
* 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/
* ...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 // TIMER1 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_COUNT1 (CLK - USECPERTICK*CLKSPERUSEC + CLKFUDGE)
#define RESET_TIMER1 TCNT1 = INIT_TIMER_COUNT1
// pulse parameters in usec
#define NEC_HDR_MARK 9000
#define NEC_HDR_SPACE 4500
#define NEC_BIT_MARK 560
#define NEC_ONE_SPACE 1690
#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 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.)
#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
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment