IRLib.cpp 42.4 KB
Newer Older
Chris Young's avatar
Chris Young committed
1
/* IRLib.cpp from IRLib - an Arduino library for infrared encoding and decoding
Chris Young's avatar
Chris Young committed
2
 * Version 1.5   June 2014
Chris Young's avatar
Chris Young committed
3
 * Copyright 2014 by Chris Young http://cyborg5.com
Chris Young's avatar
Chris Young committed
4 5 6 7 8 9 10 11
 *
 * This library is a major rewrite of IRemote by Ken Shirriff which was covered by
 * GNU LESSER GENERAL PUBLIC LICENSE which as I read it allows me to make modified versions.
 * That same license applies to this modified version. See his original copyright below.
 * The latest Ken Shirriff code can be found at https://github.com/shirriff/Arduino-IRremote
 * My purpose was to reorganize the code to make it easier to add or remove protocols.
 * As a result I have separated the act of receiving a set of raw timing codes from the act of decoding them
 * by making them separate classes. That way the receiving aspect can be more black box and implementers
Chris Young's avatar
Chris Young committed
12 13
 * of decoders and senders can just deal with the decoding of protocols. It also allows for alternative
 * types of receivers independent of the decoding. This makes porting to different hardware platforms easier.
Chris Young's avatar
Chris Young committed
14 15 16 17 18 19 20 21 22
 * Also added provisions to make the classes base classes that could be extended with new protocols
 * which would not require recompiling of the original library nor understanding of its detailed contents.
 * Some of the changes were made to reduce code size such as unnecessary use of long versus bool.
 * Some changes were just my weird programming style. Also extended debugging information added.
 */
/*
 * IRremote
 * Version 0.1 July, 2009
 * Copyright 2009 Ken Shirriff
Chris Young's avatar
Chris Young committed
23
 * For details, see http://www.righto.com/2009/08/multi-protocol-infrared-remote-library.html http://www.righto.com/
Chris Young's avatar
Chris Young committed
24 25 26 27 28 29 30 31
 *
 * 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 "IRLib.h"
#include "IRLibMatch.h"
Chris Young's avatar
Chris Young committed
32
#include "IRLibRData.h"
Chris Young's avatar
Chris Young committed
33 34
#include <Arduino.h>

Chris Young's avatar
Chris Young committed
35
volatile irparams_t irparams;
Chris Young's avatar
Chris Young committed
36 37 38 39 40
/*
 * Returns a pointer to a flash stored string that is the name of the protocol received. 
 */
const __FlashStringHelper *Pnames(IRTYPES Type) {
  if(Type>LAST_PROTOCOL) Type=UNKNOWN;
Chris Young's avatar
Chris Young committed
41
  // You can add additional strings before the entry for hash code.
Chris Young's avatar
Chris Young committed
42 43 44 45 46 47 48 49 50
  const __FlashStringHelper *Names[LAST_PROTOCOL+1]={F("Unknown"),F("NEC"),F("Sony"),F("RC5"),F("RC6"),F("Panasonic Old"),F("JVC"),F("NECx"),F("Hash Code")};
  return Names[Type];
};


#define TOPBIT 0x80000000

/*
 * The IRsend classes contain a series of methods for sending various protocols.
Chris Young's avatar
Chris Young committed
51
 * Each of these begin by calling enableIROut(unsigned char kHz) to set the carrier frequency.
Chris Young's avatar
Chris Young committed
52 53
 * It then calls mark(int usec) and space(inc usec) to transmit marks and
 * spaces of varying length of microseconds however the protocol defines.
Chris Young's avatar
Chris Young committed
54 55 56
 * Because we want to separate the hardware specific portions of the code from the general programming
 * portions of the code, the code for IRsendBase::IRsendBase, IRsendBase::enableIROut, 
 * IRsendBase::mark and IRsendBase::space can be found in the lower section of this file.
Chris Young's avatar
Chris Young committed
57 58 59 60
 */

/*
 * Most of the protocols have a header consisting of a mark/space of a particular length followed by 
Chris Young's avatar
Chris Young committed
61
 * a series of variable length mark/space signals.  Depending on the protocol they very the lengths of the 
Chris Young's avatar
Chris Young committed
62 63 64 65 66 67 68
 * mark or the space to indicate a data bit of "0" or "1". Most also end with a stop bit of "1".
 * The basic structure of the sending and decoding these protocols led to lots of redundant code. 
 * Therefore I have implemented generic sending and decoding routines. You just need to pass a bunch of customized 
 * parameters and it does the work. This reduces compiled code size with only minor speed degradation. 
 * You may be able to implement additional protocols by simply passing the proper values to these generic routines.
 * The decoding routines do not encode stop bits. So you have to tell this routine whether or not to send one.
 */
Chris Young's avatar
Chris Young committed
69 70 71 72
void IRsendBase::sendGeneric(unsigned long data, unsigned char Num_Bits, unsigned int Head_Mark, unsigned int Head_Space, 
                             unsigned int Mark_One, unsigned int Mark_Zero, unsigned int Space_One, unsigned int Space_Zero, 
							 unsigned char kHz, bool Use_Stop, unsigned long Max_Extent) {
  Extent=0;
Chris Young's avatar
Chris Young committed
73
  data = data << (32 - Num_Bits);
Chris Young's avatar
Chris Young committed
74
  enableIROut(kHz);
Chris Young's avatar
Chris Young committed
75 76 77 78 79 80 81 82 83 84 85 86
//Some protocols do not send a header when sending repeat codes. So we pass a zero value to indicate skipping this.
  if(Head_Mark) mark(Head_Mark); 
  if(Head_Space) space(Head_Space);
  for (int i = 0; i <Num_Bits; i++) {
    if (data & TOPBIT) {
      mark(Mark_One);  space(Space_One);
    } 
    else {
      mark(Mark_Zero);  space(Space_Zero);
    }
    data <<= 1;
  }
Chris Young's avatar
Chris Young committed
87 88
  if(Use_Stop) mark(Mark_One);   //stop bit of "1"
  if(Max_Extent) {
Chris Young's avatar
Chris Young committed
89
#ifdef IRLIB_TRACE
Chris Young's avatar
Chris Young committed
90 91 92
    Serial.print("Max_Extent="); Serial.println(Max_Extent);
	Serial.print("Extent="); Serial.println(Extent);
	Serial.print("Difference="); Serial.println(Max_Extent-Extent);
Chris Young's avatar
Chris Young committed
93
#endif
Chris Young's avatar
Chris Young committed
94 95 96
	space(Max_Extent-Extent); 
	}
	else space(Space_One);
Chris Young's avatar
Chris Young committed
97 98 99 100 101
};

void IRsendNEC::send(unsigned long data)
{
  if (data==REPEAT) {
Chris Young's avatar
Chris Young committed
102 103
    enableIROut(38);
    mark (564* 16); space(564*4); mark(564);space(56*173);
Chris Young's avatar
Chris Young committed
104 105 106 107 108
  }
  else {
    sendGeneric(data,32, 564*16, 564*8, 564, 564, 564*3, 564, 38, true);
  }
};
Chris Young's avatar
Chris Young committed
109

Chris Young's avatar
Chris Young committed
110 111 112 113 114 115
/*
 * Sony is backwards from most protocols. It uses a variable length mark and a fixed length space rather than
 * a fixed mark and a variable space. Our generic send will still work. According to the protocol you must send
 * Sony commands at least three times so we automatically do it here.
 */
void IRsendSony::send(unsigned long data, int nbits) {
Chris Young's avatar
Chris Young committed
116 117 118
  for(int i=0; i<3;i++){
     sendGeneric(data,nbits, 600*4, 600, 600*2, 600, 600, 600, 40, false,((nbits==8)? 22000:45000)); 
  }
Chris Young's avatar
Chris Young committed
119 120 121 122 123 124 125
};

/*
 * This next section of send routines were added by Chris Young. They all use the generic send.
 */
void IRsendNECx::send(unsigned long data)
{
Chris Young's avatar
Chris Young committed
126
  sendGeneric(data,32, 564*8, 564*8, 564, 564, 564*3, 564, 38, true, 108000);
Chris Young's avatar
Chris Young committed
127 128 129 130 131 132 133 134
};

void IRsendPanasonic_Old::send(unsigned long data)
{
  sendGeneric(data,22, 833*4, 833*4, 833, 833, 833*3, 833,57, true);
};

/*
Chris Young's avatar
Chris Young committed
135
 * JVC omits the mark/space header on repeat sending. Therefore we multiply it by 0 if it's a repeat.
Chris Young's avatar
Chris Young committed
136 137
 * The only device I had to test this protocol was an old JVC VCR. It would only work if at least
 * 2 frames are sent separated by 45us of "space". Therefore you should call this routine once with
Chris Young's avatar
Chris Young committed
138
 * "First=true" and it will send a first frame followed by one repeat frame. If First== false,
Chris Young's avatar
Chris Young committed
139 140 141 142 143
 * it will only send a single repeat frame.
 */
void IRsendJVC::send(unsigned long data, bool First)
{
  sendGeneric(data, 16,525*16*First, 525*8*First, 525, 525,525*3, 525, 38, true);
144
  space(525*45);
Chris Young's avatar
Chris Young committed
145 146
  if(First) sendGeneric(data, 16,0,0, 525, 525,525*3, 525, 38, true);
}
Chris Young's avatar
Chris Young committed
147

Chris Young's avatar
Chris Young committed
148 149 150
/*
 * The remaining protocols require special treatment. They were in the original IRremote library.
 */
Chris Young's avatar
Chris Young committed
151
void IRsendRaw::send(unsigned int buf[], unsigned char len, unsigned char hz)
Chris Young's avatar
Chris Young committed
152 153
{
  enableIROut(hz);
Chris Young's avatar
Chris Young committed
154
  for (unsigned char i = 0; i < len; i++) {
Chris Young's avatar
Chris Young committed
155 156 157 158 159 160 161 162 163
    if (i & 1) {
      space(buf[i]);
    } 
    else {
      mark(buf[i]);
    }
  }
  space(0); // Just to be sure
}
Chris Young's avatar
Chris Young committed
164

Chris Young's avatar
Chris Young committed
165 166 167 168 169 170 171 172 173 174 175 176 177
/*
 * The RC5 protocol uses a phase encoding of data bits. A space/mark pair indicates "1"
 * and a mark/space indicates a "0". It begins with a single "1" bit which is not encoded
 * in the data. The high order data bit is a toggle bit that indicates individual
 * keypresses. You must toggle this bit yourself when sending data.
 */

#define RC5_T1		889
#define RC5_RPT_LENGTH	46000
void IRsendRC5::send(unsigned long data)
{
  enableIROut(36);
  data = data << (32 - 13);
Chris Young's avatar
Chris Young committed
178
  Extent=0;
Chris Young's avatar
Chris Young committed
179 180 181
  mark(RC5_T1); // First start bit
//Note: Original IRremote library incorrectly assumed second bit was always a "1"
//bit patterns from this decoder are not backward compatible with patterns produced
Chris Young's avatar
Chris Young committed
182
//by original library. Uncomment the following two lines to maintain backward compatibility.
Chris Young's avatar
Chris Young committed
183 184
  //space(RC5_T1); // Second start bit
  //mark(RC5_T1); // Second start bit
Chris Young's avatar
Chris Young committed
185
  for (unsigned char i = 0; i < 13; i++) {
Chris Young's avatar
Chris Young committed
186 187 188 189 190 191 192 193
    if (data & TOPBIT) {
      space(RC5_T1); mark(RC5_T1);// 1 is space, then mark
    } 
    else {
      mark(RC5_T1);  space(RC5_T1);// 0 is mark, then space
    }
    data <<= 1;
  }
Chris Young's avatar
Chris Young committed
194
  space(114000-Extent); // Turn off at end
Chris Young's avatar
Chris Young committed
195 196 197 198 199 200 201 202
}

/*
 * The RC6 protocol also phase encodes databits although the phasing is opposite of RC5.
 */
#define RC6_HDR_MARK	2666
#define RC6_HDR_SPACE	889
#define RC6_T1		444
Chris Young's avatar
Chris Young committed
203
void IRsendRC6::send(unsigned long data, unsigned char nbits)
Chris Young's avatar
Chris Young committed
204 205 206
{
  enableIROut(36);
  data = data << (32 - nbits);
Chris Young's avatar
Chris Young committed
207
  Extent=0;
Chris Young's avatar
Chris Young committed
208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225
  mark(RC6_HDR_MARK); space(RC6_HDR_SPACE);
  mark(RC6_T1);  space(RC6_T1);// start bit "1"
  int t;
  for (int i = 0; i < nbits; i++) {
    if (i == 3) {
      t = 2 * RC6_T1;       // double-wide trailer bit
    } 
    else {
      t = RC6_T1;
    }
    if (data & TOPBIT) {
      mark(t); space(t);//"1" is a Mark/space
    } 
    else {
      space(t); mark(t);//"0" is a space/Mark
    }
    data <<= 1;
  }
Chris Young's avatar
Chris Young committed
226
  space(107000-Extent); // Turn off at end
Chris Young's avatar
Chris Young committed
227
}
Chris Young's avatar
Chris Young committed
228

Chris Young's avatar
Chris Young committed
229 230 231
/*
 * This method can be used to send any of the supported types except for raw and hash code.
 * There is no hash code send possible. You can call sendRaw directly if necessary.
Chris Young's avatar
Chris Young committed
232
 * Typically "data2" is the number of bits.
Chris Young's avatar
Chris Young committed
233
 */
Chris Young's avatar
Chris Young committed
234
void IRsend::send(IRTYPES Type, unsigned long data, unsigned int data2) {
Chris Young's avatar
Chris Young committed
235 236
  switch(Type) {
    case NEC:           IRsendNEC::send(data); break;
Chris Young's avatar
Chris Young committed
237
    case SONY:          IRsendSony::send(data,data2); break;
Chris Young's avatar
Chris Young committed
238
    case RC5:           IRsendRC5::send(data); break;
Chris Young's avatar
Chris Young committed
239
    case RC6:           IRsendRC6::send(data,data2); break;
Chris Young's avatar
Chris Young committed
240 241
    case PANASONIC_OLD: IRsendPanasonic_Old::send(data); break;
    case NECX:          IRsendNECx::send(data); break;    
Chris Young's avatar
Chris Young committed
242 243 244
    case JVC:           IRsendJVC::send(data,(bool)data2); break;
  //case ADDITIONAL:    IRsendADDITIONAL::send(data); break;//add additional protocols here
	//You should comment out protocols you will likely never use and/or add extra protocols here
Chris Young's avatar
Chris Young committed
245 246 247 248
  }
}

/*
Chris Young's avatar
Chris Young committed
249 250
 * The irparams definitions which were located here have been moved to IRLibRData.h
 */
Chris Young's avatar
Chris Young committed
251

Chris Young's avatar
Chris Young committed
252
 /*
Chris Young's avatar
Chris Young committed
253 254
 * We've chosen to separate the decoding routines from the receiving routines to isolate
 * the technical hardware and interrupt portion of the code which should never need modification
Chris Young's avatar
Chris Young committed
255 256
 * from the protocol decoding portion that will likely be extended and modified. It also allows for
 * creation of alternative receiver classes separate from the decoder classes.
Chris Young's avatar
Chris Young committed
257 258 259
 */
IRdecodeBase::IRdecodeBase(void) {
  rawbuf=(volatile unsigned int*)irparams.rawbuf;
Chris Young's avatar
Chris Young committed
260
  IgnoreHeader=false;
Chris Young's avatar
Chris Young committed
261 262
  Reset();
};
Chris Young's avatar
Chris Young committed
263

Chris Young's avatar
Chris Young committed
264 265 266
/*
 * Normally the decoder uses irparams.rawbuf but if you want to resume receiving while
 * still decoding you can define a separate buffer and pass the address here. 
Chris Young's avatar
Chris Young committed
267 268
 * Then IRrecvBase::GetResults will copy the raw values from its buffer to yours allowing you to
 * call IRrecvBase::resume immediately before you call decode.
Chris Young's avatar
Chris Young committed
269 270 271 272
 */
void IRdecodeBase::UseExtnBuf(void *P){
  rawbuf=(volatile unsigned int*)P;
};
Chris Young's avatar
Chris Young committed
273

Chris Young's avatar
Chris Young committed
274 275 276 277 278 279 280 281 282 283
/*
 * Copies rawbuf and rawlen from one decoder to another. See IRhashdecode example
 * for usage.
 */
void IRdecodeBase::copyBuf (IRdecodeBase *source){
   memcpy((void *)rawbuf,(const void *)source->rawbuf,sizeof(irparams.rawbuf));
   rawlen=source->rawlen;
};

/*
Chris Young's avatar
Chris Young committed
284 285
 * This routine is actually quite useful. Allows extended classes to call their parent
 * if they fail to decode themselves.
Chris Young's avatar
Chris Young committed
286 287 288 289 290 291 292 293 294 295 296
 */
bool IRdecodeBase::decode(void) {
  return false;
};

void IRdecodeBase::Reset(void) {
  decode_type= UNKNOWN;
  value=0;
  bits=0;
  rawlen=0;
};
Chris Young's avatar
Chris Young committed
297 298 299
#ifndef USE_DUMP
void DumpUnavailable(void) {Serial.println(F("DumpResults unavailable"));}
#endif
Chris Young's avatar
Chris Young committed
300 301 302 303
/*
 * This method dumps useful information about the decoded values.
 */
void IRdecodeBase::DumpResults(void) {
Chris Young's avatar
Chris Young committed
304
#ifdef USE_DUMP
Chris Young's avatar
Chris Young committed
305
  int i;unsigned long Extent;int interval;
Chris Young's avatar
Chris Young committed
306 307
  if(decode_type<=LAST_PROTOCOL){
    Serial.print(F("Decoded ")); Serial.print(Pnames(decode_type));
Chris Young's avatar
Chris Young committed
308 309
	Serial.print(F("(")); Serial.print(decode_type,DEC);
    Serial.print(F("): Value:")); Serial.print(value, HEX);
Chris Young's avatar
Chris Young committed
310 311 312
  };
  Serial.print(F(" ("));  Serial.print(bits, DEC); Serial.println(F(" bits)"));
  Serial.print(F("Raw samples(")); Serial.print(rawlen, DEC);
Chris Young's avatar
Chris Young committed
313 314 315
  Serial.print(F("): Gap:")); Serial.println(rawbuf[0], DEC);
  Serial.print(F("  Head: m")); Serial.print(rawbuf[1], DEC);
  Serial.print(F("  s")); Serial.println(rawbuf[2], DEC);
Chris Young's avatar
Chris Young committed
316 317
  int LowSpace= 32767; int LowMark=  32767;
  int HiSpace=0; int HiMark=  0;
Chris Young's avatar
Chris Young committed
318
  Extent=rawbuf[1]+rawbuf[2];
Chris Young's avatar
Chris Young committed
319
  for (i = 3; i < rawlen; i++) {
Chris Young's avatar
Chris Young committed
320
    Extent+=(interval= rawbuf[i]);
Chris Young's avatar
Chris Young committed
321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336
    if (i % 2) {
      LowMark=min(LowMark, interval);  HiMark=max(HiMark, interval);
      Serial.print(i/2-1,DEC);  Serial.print(F(":m"));
    } 
    else {
       if(interval>0)LowSpace=min(LowSpace, interval);  HiSpace=max (HiSpace, interval);
       Serial.print(F(" s"));
    }
    Serial.print(interval, DEC);
    int j=i-1;
    if ((j % 2)==1)Serial.print(F("\t"));
    if ((j % 4)==1)Serial.print(F("\t "));
    if ((j % 8)==1)Serial.println();
    if ((j % 32)==1)Serial.println();
  }
  Serial.println();
Chris Young's avatar
Chris Young committed
337
  Serial.print(F("Extent="));  Serial.println(Extent,DEC);
Chris Young's avatar
Chris Young committed
338 339 340
  Serial.print(F("Mark  min:")); Serial.print(LowMark,DEC);Serial.print(F("\t max:")); Serial.println(HiMark,DEC);
  Serial.print(F("Space min:")); Serial.print(LowSpace,DEC);Serial.print(F("\t max:")); Serial.println(HiSpace,DEC);
  Serial.println();
Chris Young's avatar
Chris Young committed
341 342 343
#else
  DumpUnavailable();
#endif
Chris Young's avatar
Chris Young committed
344 345 346 347 348 349 350 351 352
}

/*
 * Again we use a generic routine because most protocols have the same basic structure. However we need to
 * indicate whether or not the protocol varies the length of the mark or the space to indicate a "0" or "1".
 * If "Mark_One" is zero. We assume that the length of the space varies. If "Mark_One" is not zero then
 * we assume that the length of Mark varies and the value passed as "Space_Zero" is ignored.
 * When using variable length Mark, assumes Head_Space==Space_One. If it doesn't, you need a specialized decoder.
 */
Chris Young's avatar
Chris Young committed
353 354
bool IRdecodeBase::decodeGeneric(unsigned char Raw_Count, unsigned int Head_Mark, unsigned int Head_Space, 
                                 unsigned int Mark_One, unsigned int Mark_Zero, unsigned int Space_One, unsigned int Space_Zero) {
Chris Young's avatar
Chris Young committed
355 356
// If raw samples count or head mark are zero then don't perform these tests.
// Some protocols need to do custom header work.
Chris Young's avatar
Chris Young committed
357
  unsigned long data = 0;  unsigned char Max; offset=1;
Chris Young's avatar
Chris Young committed
358
  if (Raw_Count) {if (rawlen != Raw_Count) return RAW_COUNT_ERROR;}
Chris Young's avatar
Chris Young committed
359 360 361 362 363
  if(!IgnoreHeader) {
    if (Head_Mark) {
	  if (!MATCH(rawbuf[offset],Head_Mark)) return HEADER_MARK_ERROR(Head_Mark);
	}
  }
Chris Young's avatar
Chris Young committed
364 365
  offset++;
  if (Head_Space) {if (!MATCH(rawbuf[offset],Head_Space)) return HEADER_SPACE_ERROR(Head_Space);}
Chris Young's avatar
Chris Young committed
366 367 368 369 370

  if (Mark_One) {//Length of a mark indicates data "0" or "1". Space_Zero is ignored.
    offset=2;//skip initial gap plus header Mark.
    Max=rawlen;
    while (offset < Max) {
Chris Young's avatar
Chris Young committed
371
      if (!MATCH(rawbuf[offset], Space_One)) return DATA_SPACE_ERROR(Space_One);
Chris Young's avatar
Chris Young committed
372
      offset++;
Chris Young's avatar
Chris Young committed
373
      if (MATCH(rawbuf[offset], Mark_One)) {
Chris Young's avatar
Chris Young committed
374 375
        data = (data << 1) | 1;
      } 
Chris Young's avatar
Chris Young committed
376
      else if (MATCH(rawbuf[offset], Mark_Zero)) {
Chris Young's avatar
Chris Young committed
377 378
        data <<= 1;
      } 
Chris Young's avatar
Chris Young committed
379
      else return DATA_MARK_ERROR(Mark_Zero);
Chris Young's avatar
Chris Young committed
380 381 382 383 384 385 386 387
      offset++;
    }
    bits = (offset - 1) / 2;
  }
  else {//Mark_One was 0 therefore length of a space indicates data "0" or "1".
    Max=rawlen-1; //ignore stop bit
    offset=3;//skip initial gap plus two header items
    while (offset < Max) {
Chris Young's avatar
Chris Young committed
388
      if (!MATCH (rawbuf[offset],Mark_Zero)) return DATA_MARK_ERROR(Mark_Zero);
Chris Young's avatar
Chris Young committed
389
      offset++;
Chris Young's avatar
Chris Young committed
390
      if (MATCH(rawbuf[offset],Space_One)) {
Chris Young's avatar
Chris Young committed
391 392
        data = (data << 1) | 1;
      } 
Chris Young's avatar
Chris Young committed
393
      else if (MATCH (rawbuf[offset],Space_Zero)) {
Chris Young's avatar
Chris Young committed
394 395
        data <<= 1;
      } 
Chris Young's avatar
Chris Young committed
396
      else return DATA_SPACE_ERROR(Space_Zero);
Chris Young's avatar
Chris Young committed
397 398 399 400 401 402 403 404 405 406 407 408 409
      offset++;
    }
    bits = (offset - 1) / 2 -1;//didn't encode stop bit
  }
  // Success
  value = data;
  return true;
}

/*
 * This routine has been modified significantly from the original IRremote.
 * It assumes you've already called IRrecvBase::GetResults and it was true.
 * The purpose of GetResults is to determine if a complete set of signals
Chris Young's avatar
Chris Young committed
410 411
 * has been received. It then copies the raw data into your decoder's rawbuf
 * By moving the test for completion and the copying of the buffer
Chris Young's avatar
Chris Young committed
412 413 414 415 416 417 418 419 420 421 422 423 424
 * outside of this "decode" method you can use the individual decode
 * methods or make your own custom "decode" without checking for
 * protocols you don't use.
 * Note: Don't forget to call IRrecvBase::resume(); after decoding is complete.
 */
bool IRdecode::decode(void) {
  if (IRdecodeNEC::decode()) return true;
  if (IRdecodeSony::decode()) return true;
  if (IRdecodeRC5::decode()) return true;
  if (IRdecodeRC6::decode()) return true;
  if (IRdecodePanasonic_Old::decode()) return true;
  if (IRdecodeNECx::decode()) return true;
  if (IRdecodeJVC::decode()) return true;
Chris Young's avatar
Chris Young committed
425
//if (IRdecodeADDITIONAL::decode()) return true;//add additional protocols here
Chris Young's avatar
Chris Young committed
426 427 428 429 430 431 432 433
//Deliberately did not add hash code decoding. If you get decode_type==UNKNOWN and
// you want to know a hash code you can call IRhash::decode() yourself.
// BTW This is another reason we separated IRrecv from IRdecode.
  return false;
}

#define NEC_RPT_SPACE	2250
bool IRdecodeNEC::decode(void) {
Chris Young's avatar
Chris Young committed
434
  IRLIB_ATTEMPT_MESSAGE(F("NEC"));
Chris Young's avatar
Chris Young committed
435
  // Check for repeat
Chris Young's avatar
Chris Young committed
436 437
  if (rawlen == 4 && MATCH(rawbuf[2], NEC_RPT_SPACE) &&
    MATCH(rawbuf[3],564)) {
Chris Young's avatar
Chris Young committed
438 439 440 441 442 443 444 445 446 447 448 449 450
    bits = 0;
    value = REPEAT;
    decode_type = NEC;
    return true;
  }
  if(!decodeGeneric(68, 564*16, 564*8, 0, 564, 564*3, 564)) return false;
  decode_type = NEC;
  return true;
}

// According to http://www.hifi-remote.com/johnsfine/DecodeIR.html#Sony8 
// Sony protocol can only be 8, 12, 15, or 20 bits in length.
bool IRdecodeSony::decode(void) {
Chris Young's avatar
Chris Young committed
451
  IRLIB_ATTEMPT_MESSAGE(F("Sony"));
Chris Young's avatar
Chris Young committed
452 453 454 455 456 457 458 459 460 461 462 463
  if(rawlen!=2*8+2 && rawlen!=2*12+2 && rawlen!=2*15+2 && rawlen!=2*20+2) return RAW_COUNT_ERROR;
  if(!decodeGeneric(0, 600*4, 600, 600*2, 600, 600,0)) return false;
  decode_type = SONY;
  return true;
}

/*
 * The next several decoders were added by Chris Young. They illustrate some of the special cases
 * that can come up when decoding using the generic decoder.
 */

/*
Chris Young's avatar
Chris Young committed
464
 * A very good source for protocol information is... http://www.hifi-remote.com/johnsfine/DecodeIR.html
Chris Young's avatar
Chris Young committed
465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480
 * I used that information to understand what they call the "Panasonic old" protocol which is used by
 * Scientific Atlanta cable boxes. That website uses a very strange notation called IRP notation.
 * For this protocol, the notation was:
 * {57.6k,833}<1,-1|1,-3>(4,-4,D:5,F:6,~D:5,~F:6,1,-???)+ 
 * This indicates that the frequency is 57.6, the base length for the pulse is 833
 * The first part of the <x,-x|x,-x> section tells you what a "0" is and the second part
 * tells you what a "1" is. That means "0" is 833 on, 833 off while an "1" is 833 on
 * followed by 833*3=2499 off. The section in parentheses tells you what data gets sent.
 * The protocol begins with header consisting of 4*833 on and 4*833 off. The other items 
 * describe what the remaining data bits are.
 * It reads as 5 device bits followed by 6 function bits. You then repeat those bits complemented.
 * It concludes with a single "1" bit followed by and an undetermined amount of blank space.
 * This makes the entire protocol 5+6+5+6= 22 bits long since we don't encode the stop bit.
 * The "+" at the end means you only need to send it once and it can repeat as many times as you want.
 */
bool IRdecodePanasonic_Old::decode(void) {
Chris Young's avatar
Chris Young committed
481
  IRLIB_ATTEMPT_MESSAGE(F("Panasonic_Old"));
Chris Young's avatar
Chris Young committed
482 483 484 485 486 487 488
  if(!decodeGeneric(48,833*4,833*4,0,833,833*3,833)) return false;
  /*
   * The protocol spec says that the first 11 bits described the device and function.
   * The next 11 bits are the same thing only it is the logical Bitwise complement.
   * Many protocols have such check features in their definition but our code typically doesn't
   * perform these checks. For example NEC's least significant 8 bits are the complement of 
   * of the next more significant 8 bits. While it's probably not necessary to error check this, 
Chris Young's avatar
Chris Young committed
489
   * you can un-comment the next 4 lines of code to do this extra checking.
Chris Young's avatar
Chris Young committed
490
   */
Chris Young's avatar
Chris Young committed
491 492 493 494
//  long S1= (value & 0x0007ff);       // 00 0000 0000 0111 1111 1111 //00000 000000 11111 111111
//  long S2= (value & 0x3ff800)>> 11;  // 11 1111 1111 1000 0000 0000 //11111 111111 00000 000000
//  S2= (~S2) & 0x0007ff;
//  if (S1!=S2) return IRLIB_REJECTION_MESSAGE(F("inverted bit redundancy"));
Chris Young's avatar
Chris Young committed
495 496 497 498 499 500
  // Success
  decode_type = PANASONIC_OLD;
  return true;
}

bool IRdecodeNECx::decode(void) {
Chris Young's avatar
Chris Young committed
501
  IRLIB_ATTEMPT_MESSAGE(F("NECx"));  
Chris Young's avatar
Chris Young committed
502 503 504 505 506 507 508
  if(!decodeGeneric(68,564*8,564*8,0,564,564*3,564)) return false;
  decode_type = NECX;
  return true;
}

// JVC does not send any header if there is a repeat.
bool IRdecodeJVC::decode(void) {
Chris Young's avatar
Chris Young committed
509
  IRLIB_ATTEMPT_MESSAGE(F("JVC"));
Chris Young's avatar
Chris Young committed
510 511
  if(!decodeGeneric(36,525*16,525*8,0,525,525*3,525)) 
  {
Chris Young's avatar
Chris Young committed
512
     IRLIB_ATTEMPT_MESSAGE(F("JVC Repeat"));
Chris Young's avatar
Chris Young committed
513 514 515
     if (rawlen==34) 
     {
        if(!decodeGeneric(0,525,0,0,525,525*3,525))
Chris Young's avatar
Chris Young committed
516
           {return IRLIB_REJECTION_MESSAGE(F("JVC repeat failed generic"));}
Chris Young's avatar
Chris Young committed
517 518
        else {
 //If this is a repeat code then IRdecodeBase::decode fails to add the most significant bit
Chris Young's avatar
Chris Young committed
519
           if (MATCH(rawbuf[4],(525*3))) 
Chris Young's avatar
Chris Young committed
520 521 522 523 524
           {
              value |= 0x8000;
           } 
           else
           {
Chris Young's avatar
Chris Young committed
525
             if (!MATCH(rawbuf[4],525)) return DATA_SPACE_ERROR(525);
Chris Young's avatar
Chris Young committed
526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545
           }
        }
        bits++;
     }
     else return RAW_COUNT_ERROR;
  } 
  decode_type =JVC;
  return true;
}

/*
 * The remaining protocols from the original IRremote library require special handling
 * This routine 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 ERROR if the measured time interval is not a multiple of t1.
 */
Chris Young's avatar
Chris Young committed
546 547
IRdecodeRC::RCLevel IRdecodeRC::getRClevel(unsigned char *used, const unsigned int t1) {
  if (offset >= rawlen) {
Chris Young's avatar
Chris Young committed
548 549 550
    // After end of recorded buffer, assume SPACE.
    return SPACE;
  }
Chris Young's avatar
Chris Young committed
551
  unsigned int width = rawbuf[offset];
Chris Young's avatar
Chris Young committed
552
  IRdecodeRC::RCLevel val;
Chris Young's avatar
Chris Young committed
553
  if ((offset) % 2) val=MARK; else val=SPACE;
Chris Young's avatar
Chris Young committed
554 555 556
  
  unsigned char avail;
  if (MATCH(width, t1)) {
Chris Young's avatar
Chris Young committed
557 558
    avail = 1;
  } 
Chris Young's avatar
Chris Young committed
559
  else if (MATCH(width, 2*t1)) {
Chris Young's avatar
Chris Young committed
560 561
    avail = 2;
  } 
Chris Young's avatar
Chris Young committed
562
  else if (MATCH(width, 3*t1)) {
Chris Young's avatar
Chris Young committed
563 564 565
    avail = 3;
  } 
  else {
Chris Young's avatar
Chris Young committed
566 567 568 569
    if((IgnoreHeader) && (offset==1) && (width<t1))
	  avail =1;
	else{
      return ERROR;}
Chris Young's avatar
Chris Young committed
570 571 572 573
  }
  (*used)++;
  if (*used >= avail) {
    *used = 0;
Chris Young's avatar
Chris Young committed
574
    (offset)++;
Chris Young's avatar
Chris Young committed
575 576 577 578 579 580 581 582
  }
  return val;   
}

#define MIN_RC5_SAMPLES 11
#define MIN_RC6_SAMPLES 1

bool IRdecodeRC5::decode(void) {
Chris Young's avatar
Chris Young committed
583
  IRLIB_ATTEMPT_MESSAGE(F("RC5"));
Chris Young's avatar
Chris Young committed
584
  if (rawlen < MIN_RC5_SAMPLES + 2) return RAW_COUNT_ERROR;
Chris Young's avatar
Chris Young committed
585 586 587
  offset = 1; // Skip gap space
  data = 0;
  used = 0;
Chris Young's avatar
Chris Young committed
588
  // Get start bits
Chris Young's avatar
Chris Young committed
589
  if (getRClevel(&used, RC5_T1) != MARK) return HEADER_MARK_ERROR(RC5_T1);
Chris Young's avatar
Chris Young committed
590 591
//Note: Original IRremote library incorrectly assumed second bit was always a "1"
//bit patterns from this decoder are not backward compatible with patterns produced
Chris Young's avatar
Chris Young committed
592
//by original library. Uncomment the following two lines to maintain backward compatibility.
Chris Young's avatar
Chris Young committed
593 594
  //if (getRClevel(&used, RC5_T1) != SPACE) return HEADER_SPACE_ERROR(RC5_T1);
  //if (getRClevel(&used, RC5_T1) != MARK) return HEADER_MARK_ERROR(RC5_T1);
Chris Young's avatar
Chris Young committed
595
  for (nbits = 0; offset < rawlen; nbits++) {
Chris Young's avatar
Chris Young committed
596 597
    RCLevel levelA = getRClevel(&used, RC5_T1); 
    RCLevel levelB = getRClevel(&used, RC5_T1);
Chris Young's avatar
Chris Young committed
598 599 600 601 602 603 604 605
    if (levelA == SPACE && levelB == MARK) {
      // 1 bit
      data = (data << 1) | 1;
    } 
    else if (levelA == MARK && levelB == SPACE) {
      // zero bit
      data <<= 1;
    } 
Chris Young's avatar
Chris Young committed
606
    else return DATA_MARK_ERROR(RC5_T1);
Chris Young's avatar
Chris Young committed
607 608 609 610 611 612 613 614 615
  }
  // Success
  bits = 13;
  value = data;
  decode_type = RC5;
  return true;
}

bool IRdecodeRC6::decode(void) {
Chris Young's avatar
Chris Young committed
616
  IRLIB_ATTEMPT_MESSAGE(F("RC6"));
Chris Young's avatar
Chris Young committed
617 618
  if (rawlen < MIN_RC6_SAMPLES) return RAW_COUNT_ERROR;
  // Initial mark
Chris Young's avatar
Chris Young committed
619 620 621
  if (!IgnoreHeader) {
    if (!MATCH(rawbuf[1], RC6_HDR_MARK)) return HEADER_MARK_ERROR(RC6_HDR_MARK);
  }
Chris Young's avatar
Chris Young committed
622
  if (!MATCH(rawbuf[2], RC6_HDR_SPACE)) return HEADER_SPACE_ERROR(RC6_HDR_SPACE);
Chris Young's avatar
Chris Young committed
623 624 625
  offset=3;//Skip gap and header
  data = 0;
  used = 0;
Chris Young's avatar
Chris Young committed
626
  // Get start bit (1)
Chris Young's avatar
Chris Young committed
627 628
  if (getRClevel(&used, RC6_T1) != MARK) return DATA_MARK_ERROR(RC6_T1);
  if (getRClevel(&used, RC6_T1) != SPACE) return DATA_SPACE_ERROR(RC6_T1);
Chris Young's avatar
Chris Young committed
629 630
  for (nbits = 0; offset < rawlen; nbits++) {
    RCLevel levelA, levelB; // Next two levels
Chris Young's avatar
Chris Young committed
631
    levelA = getRClevel(&used, RC6_T1); 
Chris Young's avatar
Chris Young committed
632 633
    if (nbits == 3) {
      // T bit is double wide; make sure second half matches
Chris Young's avatar
Chris Young committed
634
      if (levelA != getRClevel(&used, RC6_T1)) return TRAILER_BIT_ERROR(RC6_T1);
Chris Young's avatar
Chris Young committed
635
    } 
Chris Young's avatar
Chris Young committed
636
    levelB = getRClevel(&used, RC6_T1);
Chris Young's avatar
Chris Young committed
637 638
    if (nbits == 3) {
      // T bit is double wide; make sure second half matches
Chris Young's avatar
Chris Young committed
639
      if (levelB != getRClevel(&used, RC6_T1)) return TRAILER_BIT_ERROR(RC6_T1);
Chris Young's avatar
Chris Young committed
640 641 642 643 644 645 646 647 648 649
    } 
    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 {
Chris Young's avatar
Chris Young committed
650
      return DATA_MARK_ERROR(RC6_T1); 
Chris Young's avatar
Chris Young committed
651 652 653 654 655 656 657 658 659 660 661 662
    } 
  }
  // Success
  bits = nbits;
  value = data;
  decode_type = RC6;
  return true;
}

/*
 * This Hash decoder is based on IRhashcode
 * Copyright 2010 Ken Shirriff
Chris Young's avatar
Chris Young committed
663
 * For details see http://www.righto.com/2010/01/using-arbitrary-remotes-with-arduino.html
Chris Young's avatar
Chris Young committed
664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686
 * Use FNV hash algorithm: http://isthe.com/chongo/tech/comp/fnv/#FNV-param
 * Converts the raw code values into a 32-bit hash code.
 * Hopefully this code is unique for each button.
 */
#define FNV_PRIME_32 16777619
#define FNV_BASIS_32 2166136261
// Compare two tick values, returning 0 if newval is shorter,
// 1 if newval is equal, and 2 if newval is longer
int IRdecodeHash::compare(unsigned int oldval, unsigned int newval) {
  if (newval < oldval * .8) return 0;
  if (oldval < newval * .8) return 2;
  return 1;
}

bool IRdecodeHash::decode(void) {
  hash = FNV_BASIS_32;
  for (int i = 1; i+2 < rawlen; i++) {
    hash = (hash * FNV_PRIME_32) ^ compare(rawbuf[i], rawbuf[i+2]);
  }
//note: does not set decode_type=HASH_CODE nor "value" because you might not want to.
  return true;
}

Chris Young's avatar
Chris Young committed
687 688
/* We have created a new receiver base class so that we can use its code to implement
 * additional receiver classes in addition to the original IRremote code which used
Chris Young's avatar
Chris Young committed
689 690
 * 50us interrupt sampling of the input pin. See IRrecvLoop and IRrecvPCI classes
 * below. IRrecv is the original receiver class with the 50us sampling.
Chris Young's avatar
Chris Young committed
691
 */
Chris Young's avatar
Chris Young committed
692 693 694 695 696 697 698 699 700 701 702 703 704
IRrecvBase::IRrecvBase(unsigned char recvpin)
{
  irparams.recvpin = recvpin;
  Init();
}
void IRrecvBase::Init(void) {
  irparams.blinkflag = 0;
  Mark_Excess=100;
}

unsigned char IRrecvBase::getPinNum(void){
  return irparams.recvpin;
}
Chris Young's avatar
Chris Young committed
705

Chris Young's avatar
Chris Young committed
706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779
/* Any receiver class must implement a GetResults method that will return true when a complete code
 * has been received. At a successful end of your GetResults code you should then call IRrecvBase::GetResults
 * and it will copy the data from the receiver structures into your decoder. Some receivers
 * provide results in rawbuf measured in ticks on some number of microseconds while others
 * return results in actual microseconds. If you use ticks then you should pass a multiplier
 * value in Time_per_Ticks.
 */
bool IRrecvBase::GetResults(IRdecodeBase *decoder, const unsigned int Time_per_Tick) {
  decoder->Reset();//clear out any old values.
  decoder->rawlen = irparams.rawlen;
/* Typically IR receivers over-report the length of a mark and under-report the length of a space.
 * This routine adjusts for that by subtracting Mark_Excess from recorded marks and
 * deleting it from a recorded spaces. The amount of adjustment used to be defined in IRLibMatch.h.
 * It is now user adjustable with the old default of 100;
 * By copying the the values from irparams to decoder we can call IRrecvBase::resume 
 * immediately while decoding is still in progress.
 */
  for(unsigned char i=0; i<irparams.rawlen; i++) {
    decoder->rawbuf[i]=irparams.rawbuf[i]*Time_per_Tick + ( (i % 2)? -Mark_Excess:Mark_Excess);
  }
  return true;
}

void IRrecvBase::enableIRIn(void) { 
  pinMode(irparams.recvpin, INPUT);
  resume();
}

void IRrecvBase::resume() {
  irparams.rawlen = 0;
}

/* This receiver uses no interrupts or timers. Other interrupt driven receivers
 * allow you to do other things and call GetResults at your leisure to see if perhaps
 * a sequence has been received. Typically you would put GetResults in your loop
 * and it would return false until the sequence had been received. However because this
 * receiver uses no interrupts, it takes control of your program when you call GetResults
 * and doesn't let go until it's got something to show you. The advantage is you don't need
 * interrupts which would make it easier to use and nonstandard hardware and will allow you to
 * use any digital input pin. Timing of this routine is only as accurate as your "micros();"
 */
bool IRrecvLoop::GetResults(IRdecodeBase *decoder) {
  bool Finished=false;
  byte OldState=HIGH;byte NewState;
  unsigned long StartTime, DeltaTime, EndTime;
  StartTime=micros();
  while(irparams.rawlen<RAWBUF) {  //While the buffer not overflowing
    while(OldState==(NewState=digitalRead(irparams.recvpin))) { //While the pin hasn't changed
      if( (DeltaTime = (EndTime=micros()) - StartTime) > 10000) { //If it's a very long wait
        if(Finished=irparams.rawlen) break; //finished unless it's the opening gap
      }
    }
    if(Finished) break;
	do_Blink();
    irparams.rawbuf[irparams.rawlen++]=DeltaTime;
    OldState=NewState;StartTime=EndTime;
  };
  IRrecvBase::GetResults(decoder);
  return true;
}

/* This receiver uses the pin change hardware interrupt to detect when your input pin
 * changes state. It gives more detailed results than the 50µs interrupts of IRrecv
 * and theoretically is more accurate than IRrecvLoop. However because it only detects
 * pin changes, it doesn't always know when it's finished. GetResults attempts to detect
 * a long gap of space but sometimes the next signal gets there before GetResults notices.
 * This means the second set of signals can get messed up unless there is a long gap.
 * This receiver is based in part on Arduino firmware for use with AnalysIR IR signal analysis
 * software for Windows PCs. Many thanks to the people at http://analysir.com for their 
 * assistance in developing this section of code.
 */

IRrecvPCI::IRrecvPCI(unsigned char inum) {
  Init();
Chris Young's avatar
Chris Young committed
780 781 782
  intrnum=inum;
  irparams.recvpin=Pin_from_Intr(inum);

Chris Young's avatar
Chris Young committed
783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832
  }
void IRrecvPCI_Handler(){ 
  unsigned long volatile ChangeTime=micros();
  unsigned long DeltaTime=ChangeTime-irparams.timer;
  switch(irparams.rcvstate) {
    case STATE_STOP: return;
    case STATE_RUNNING:
	  do_Blink();
      if (DeltaTime>10000) {
        irparams.rcvstate=STATE_STOP; 
        //Setting gap to 0 is a flag to let you know why we stopped For debugging purposes
        //irparams.rawbuf[0]=0;
        return;
      };
      break;
    case STATE_IDLE:
       if(digitalRead(irparams.recvpin)) return; else irparams.rcvstate=STATE_RUNNING;
       break;
  };
  irparams.rawbuf[irparams.rawlen]=DeltaTime;
  irparams.timer=ChangeTime;
  if(++irparams.rawlen>=RAWBUF) {
    irparams.rcvstate=STATE_STOP;
    //Setting gap to 1 is a flag to let you know why we stopped For debugging purposes
    //irparams.rawbuf[0]=1;
  }
}

void IRrecvPCI::resume(void) {
  irparams.rcvstate = STATE_IDLE;
  IRrecvBase::resume();
  irparams.timer=micros();
  attachInterrupt(intrnum, IRrecvPCI_Handler, CHANGE);
};

bool IRrecvPCI::GetResults(IRdecodeBase *decoder) {
  if(irparams.rcvstate==STATE_RUNNING) {
    unsigned long ChangeTime=irparams.timer;
    if( (micros()-ChangeTime) > 10000) {
      irparams.rcvstate=STATE_STOP;
      //Setting gap to 2 is a flag to let you know why we stopped For debugging purposes
      //irparams.rawbuf[0]=2;
    }
  }
  if (irparams.rcvstate != STATE_STOP) return false;
  detachInterrupt(intrnum);
  IRrecvBase::GetResults(decoder);
  return true;
};

Chris Young's avatar
Chris Young committed
833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887
 /* This class facilitates detection of frequency of an IR signal. Requires a TSMP58000
 * or equivalent device connected to the hardware interrupt pin.
 * Create an instance of the object passing the interrupt number.
 */
volatile unsigned FREQUENCY_BUFFER_TYPE *IRfreqTimes;
volatile unsigned char IRfreqCount;
IRfrequency::IRfrequency(unsigned char inum) {  //Note this is interrupt number, not pin number
  intrnum=inum;
  pin= Pin_from_Intr(inum);
  //ISR cannot be passed parameters. If I declare the buffer global it would
  //always eat RAN even if this object was not declared. So we make global pointer
  //and copy the address to it. ISR still puts data in the object.
  IRfreqTimes= & (Time_Stamp[0]);
};

// Note ISR handler cannot be part of a class/object
void IRfreqISR(void) {
   IRfreqTimes[IRfreqCount++]=micros();
}

void IRfrequency::enableFreqDetect(void){
  attachInterrupt(intrnum,IRfreqISR, FALLING);
  for(i=0; i<256; i++) Time_Stamp[i]=0;
  IRfreqCount=0;
  Results= 0.0;
  Samples=0;
};
/* Test to see if we have collected at least one full buffer of data.
 * Note values are always zeroed before beginning so any non-zero data
 * in the final elements means we have collected at least a buffer full.
 * By chance the final might be zero so we test two of them. Would be
 * nearly impossible for two consecutive elements to be zero unless
 * we had not yet collected data.
 */
bool IRfrequency::HaveData(void) {
  return (Time_Stamp[255] || Time_Stamp[254]);
};

void IRfrequency::disableFreqDetect(void){
  detachInterrupt(intrnum);
 };

void IRfrequency::ComputeFreq(void){
   Samples=0; Sum=0;
   for(i=1; i<256; i++) {
     unsigned char Interval=Time_Stamp[i]-Time_Stamp[i-1];
	 if(Interval>50 || Interval<10) continue;//ignore extraneous results
	 Sum+=Interval;//accumulate usable intervals
	 Samples++;    //account usable intervals
   };
   if(Sum)
     Results=(double) Samples/(double)Sum*1000;
   else
     Results= 0.0;
 };
Chris Young's avatar
Chris Young committed
888
 
Chris Young's avatar
Chris Young committed
889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916
//Didn't need to be a method that we made one following example of IRrecvBase
unsigned char IRfrequency::getPinNum(void) {
  return pin;
}

void IRfrequency::DumpResults(bool Detail) {
  ComputeFreq();
#ifdef USE_DUMP
  Serial.print(F("Number of samples:")); Serial.print(Samples,DEC);
  Serial.print(F("\t  Total interval (us):")); Serial.println(Sum,DEC); 
  Serial.print(F("Avg. interval(us):")); Serial.print(1.0*Sum/Samples,2);
  Serial.print(F("\t Aprx. Frequency(kHz):")); Serial.print(Results,2);
  Serial.print(F(" (")); Serial.print(int(Results+0.5),DEC);
  Serial.println(F(")"));
  if(Detail) {
    for(i=1; i<256; i++) {
      unsigned int Interval=Time_Stamp[i]-Time_Stamp[i-1];
      Serial.print(Interval,DEC); Serial.print("\t");
      if ((i % 4)==0)Serial.print(F("\t "));
      if ((i % 8)==0)Serial.println();
      if ((i % 32)==0)Serial.println();
    }
    Serial.println();
  }
#else
  DumpUnavailable(); 
#endif
};
Chris Young's avatar
Chris Young committed
917 918 919 920 921 922 923
 
 
/*
 * The remainder of this file is all related to interrupt handling and hardware issues. It has 
 * nothing to do with IR protocols. You need not understand this is all you're doing is adding 
 * new protocols or improving the receiving, decoding and sending of protocols.
 */
Chris Young's avatar
Chris Young committed
924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947

//See IRLib.h comment explaining this function
 unsigned char Pin_from_Intr(unsigned char inum) {
  const unsigned char PROGMEM attach_to_pin[]= {
#if defined(__AVR_ATmega256RFR2__)//Assume Pinoccio Scout
	4,5,SCL,SDA,RX1,TX1,7
#elif defined(__AVR_ATmega32U4__) //Assume Arduino Leonardo
	3,2,0,1,7
#elif defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__)//Assume Arduino Mega 
	2,3, 21, 20, 1, 18
#else	//Assume Arduino Uno or other ATmega328
	2, 3
#endif
  };
#if defined(ARDUINO_SAM_DUE)
  return inum;
#endif
  if (inum<sizeof attach_to_pin) {//note this works because we know it's one byte per entry
	return attach_to_pin[inum];
  } else {
    return 255;
  }
}

Chris Young's avatar
Chris Young committed
948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967
// Provides ISR
#include <avr/interrupt.h>
// 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
#define CLKFUDGE 5      // fudge factor for clock interrupt overhead
#ifdef F_CPU
#define SYSCLOCK F_CPU     // main Arduino clock
#else
#define SYSCLOCK 16000000  // main Arduino clock
#endif
#define PRESCALE 8      // timer clock prescale
#define CLKSPERUSEC (SYSCLOCK/PRESCALE/1000000)   // timer clocks per microsecond

#include <IRLibTimer.h>

Chris Young's avatar
Chris Young committed
968 969 970
/* 
 * This section contains the hardware specific portions of IRrecvBase
 */
Chris Young's avatar
Chris Young committed
971 972 973 974
/* If your hardware is set up to do both output and input but your particular sketch
 * doesn't do any output, this method will ensure that your output pin is low
 * and doesn't turn on your IR LED or any output circuit.
 */
Chris Young's avatar
Chris Young committed
975
void IRrecvBase::No_Output (void) {
Chris Young's avatar
Chris Young committed
976 977 978 979
#if defined(IR_SEND_PWM_PIN)
 pinMode(IR_SEND_PWM_PIN, OUTPUT);  
 digitalWrite(IR_SEND_PWM_PIN, LOW); // When not sending PWM, we want it low    
#endif
Chris Young's avatar
Chris Young committed
980 981
}

Chris Young's avatar
Chris Young committed
982
// enable/disable blinking of pin 13 on IR processing
Chris Young's avatar
Chris Young committed
983
void IRrecvBase::blink13(bool blinkflag)
Chris Young's avatar
Chris Young committed
984 985 986 987 988
{
  irparams.blinkflag = blinkflag;
  if (blinkflag)
     pinMode(BLINKLED, OUTPUT);
}
Chris Young's avatar
Chris Young committed
989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002

//Do the actual blinking off and on
//This is not part of IRrecvBase because it may need to be inside an ISR
//and we cannot pass parameters to them.
void do_Blink(void) {
  if (irparams.blinkflag) {
    if(irparams.rawlen % 2) {
      BLINKLED_ON();  // turn pin 13 LED on
    } 
    else {
      BLINKLED_OFF();  // turn pin 13 LED off
    }
  }
}
Chris Young's avatar
Chris Young committed
1003
#ifdef USE_IRRECV
Chris Young's avatar
Chris Young committed
1004 1005 1006
/*
 * The original IRrecv which uses 50µs timer driven interrupts to sample input pin.
 */
Chris Young's avatar
Chris Young committed
1007
void IRrecv::resume() {
Chris Young's avatar
Chris Young committed
1008
  // initialize state machine variables
Chris Young's avatar
Chris Young committed
1009
  irparams.rcvstate = STATE_IDLE;
Chris Young's avatar
Chris Young committed
1010 1011 1012 1013 1014 1015 1016
  IRrecvBase::resume();
}

void IRrecv::enableIRIn(void) {
  IRrecvBase::enableIRIn();
  // setup pulse clock timer interrupt
  cli();
Chris Young's avatar
Chris Young committed
1017 1018
  IR_RECV_CONFIG_TICKS();
  IR_RECV_ENABLE_INTR;
Chris Young's avatar
Chris Young committed
1019
  sei();
Chris Young's avatar
Chris Young committed
1020
}
Chris Young's avatar
Chris Young committed
1021

Chris Young's avatar
Chris Young committed
1022 1023
bool IRrecv::GetResults(IRdecodeBase *decoder) {
  if (irparams.rcvstate != STATE_STOP) return false;
Chris Young's avatar
Chris Young committed
1024
  IRrecvBase::GetResults(decoder,USECPERTICK);
Chris Young's avatar
Chris Young committed
1025 1026
  return true;
}
Chris Young's avatar
Chris Young committed
1027

Chris Young's avatar
Chris Young committed
1028 1029
#define _GAP 5000 // Minimum map between transmissions
#define GAP_TICKS (_GAP/USECPERTICK)
Chris Young's avatar
Chris Young committed
1030 1031 1032 1033 1034 1035 1036 1037
/*
 * This interrupt service routine is only used by IRrecv and may or may not be used by other
 * extensions of the IRrecBase. It is timer driven 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.
 */
Chris Young's avatar
Chris Young committed
1038
ISR(IR_RECV_INTR_NAME)
Chris Young's avatar
Chris Young committed
1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091
{
  enum irdata_t {IR_MARK=0, IR_SPACE=1};
  irdata_t irdata = (irdata_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 == IR_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 == IR_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 == IR_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 == IR_MARK) { // reset gap timer
      irparams.timer = 0;
    }
    break;
  }
Chris Young's avatar
Chris Young committed
1092
  do_Blink();
Chris Young's avatar
Chris Young committed
1093
}
Chris Young's avatar
Chris Young committed
1094
#endif //end of ifdef USE_IRRECV
Chris Young's avatar
Chris Young committed
1095 1096 1097 1098 1099 1100
/*
 * The hardware specific portions of IRsendBase
 */
void IRsendBase::enableIROut(unsigned char khz) {
//NOTE: the comments on this routine accompanied the original early version of IRremote library
//which only used TIMER2. The parameters defined in IRLibTimer.h may or may not work this way.
Chris Young's avatar
Chris Young committed
1101 1102 1103 1104 1105 1106 1107 1108 1109
  // Enables IR output.  The khz value controls the modulation frequency in kilohertz.
  // The IR output will be on pin 3 (OC2B).
  // 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.)
  // TIMER2 is used in phase-correct PWM mode, with OCR2A controlling the frequency and OCR2B
  // controlling the duty cycle.
  // There is no prescaling, so the output frequency is 16MHz / (2 * OCR2A)
  // 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.
Chris Young's avatar
Chris Young committed
1110
  // See my Secrets of Arduino PWM at http://www.righto.com/2009/07/secrets-of-arduino-pwm.html for details.
Chris Young's avatar
Chris Young committed
1111 1112
  
  // Disable the Timer2 Interrupt (which is used for receiving IR)
Chris Young's avatar
Chris Young committed
1113 1114 1115 1116
 IR_RECV_DISABLE_INTR; //Timer2 Overflow Interrupt    
 pinMode(IR_SEND_PWM_PIN, OUTPUT);  
 digitalWrite(IR_SEND_PWM_PIN, LOW); // When not sending PWM, we want it low    
 IR_SEND_CONFIG_KHZ(khz);
Chris Young's avatar
Chris Young committed
1117
 }
Chris Young's avatar
Chris Young committed
1118 1119

IRsendBase::IRsendBase () {
Chris Young's avatar
Chris Young committed
1120 1121
 pinMode(IR_SEND_PWM_PIN, OUTPUT);  
 digitalWrite(IR_SEND_PWM_PIN, LOW); // When not sending PWM, we want it low    
Chris Young's avatar
Chris Young committed
1122 1123 1124 1125 1126 1127 1128 1129 1130
}

//The Arduino built in function delayMicroseconds has limits we wish to exceed
//Therefore we have created this alternative
void  My_delay_uSecs(unsigned int T) {
  if(T){if(T>16000) {delayMicroseconds(T % 1000); delay(T/1000); } else delayMicroseconds(T);};
}

void IRsendBase::mark(unsigned int time) {
Chris Young's avatar
Chris Young committed
1131 1132
 IR_SEND_PWM_START;
 IR_SEND_MARK_TIME(time);
Chris Young's avatar
Chris Young committed
1133
 Extent+=time;
Chris Young's avatar
Chris Young committed
1134 1135
}

Chris Young's avatar
Chris Young committed
1136
void IRsendBase::space(unsigned int time) {
Chris Young's avatar
Chris Young committed
1137
 IR_SEND_PWM_STOP;
Chris Young's avatar
Chris Young committed
1138 1139
 My_delay_uSecs(time);
 Extent+=time;
Chris Young's avatar
Chris Young committed
1140 1141 1142 1143 1144 1145 1146
}

/*
 * Various debugging routines
 */


Chris Young's avatar
Chris Young committed
1147 1148 1149 1150 1151 1152 1153 1154
#ifdef IRLIB_TRACE
void IRLIB_ATTEMPT_MESSAGE(const __FlashStringHelper * s) {Serial.print(F("Attempting ")); Serial.print(s); Serial.println(F(" decode:"));};
void IRLIB_TRACE_MESSAGE(const __FlashStringHelper * s) {Serial.print(F("Executing ")); Serial.println(s);};
byte IRLIB_REJECTION_MESSAGE(const __FlashStringHelper * s) { Serial.print(F(" Protocol failed because ")); Serial.print(s); Serial.println(F(" wrong.")); return false;};
byte IRLIB_DATA_ERROR_MESSAGE(const __FlashStringHelper * s, unsigned char index, unsigned int value, unsigned int expected) {  
 IRLIB_REJECTION_MESSAGE(s); Serial.print(F("Error occurred with rawbuf[")); Serial.print(index,DEC); Serial.print(F("]=")); Serial.print(value,DEC);
 Serial.print(F(" expected:")); Serial.println(expected,DEC); return false;
};
Chris Young's avatar
Chris Young committed
1155
#endif