IRremote
TinyIRReceiver.hpp
Go to the documentation of this file.
1 /*
2  * TinyIRReceiver.hpp
3  *
4  * Receives IR data of NEC protocol using pin change interrupts.
5  * NEC is the protocol of most cheap remote controls for Arduino.
6  *
7  * Parity check is done for address and data.
8  * On a completely received IR command, the user function handleReceivedIRData(uint8_t aAddress, uint8_t aCommand, uint8_t aFlags)
9  * is called in interrupt context but with interrupts being enabled to enable use of delay() etc.
10  * !!!!!!!!!!!!!!!!!!!!!!
11  * Functions called in interrupt context should be running as short as possible,
12  * so if you require longer action, save the data (address + command) and handle them in the main loop.
13  * !!!!!!!!!!!!!!!!!!!!!
14  * aFlags can contain one of IRDATA_FLAGS_EMPTY, IRDATA_FLAGS_IS_REPEAT and IRDATA_FLAGS_PARITY_FAILED bits
15  *
16  * The FAST protocol is a proprietary modified JVC protocol without address, with parity and with a shorter header.
17  * FAST Protocol characteristics:
18  * - Bit timing is like NEC or JVC
19  * - The header is shorter, 3156 vs. 12500
20  * - No address and 16 bit data, interpreted as 8 bit command and 8 bit inverted command,
21  * leading to a fixed protocol length of (6 + (16 * 3) + 1) * 526 = 55 * 526 = 28930 microseconds or 29 ms.
22  * - Repeats are sent as complete frames but in a 50 ms period / with a 21 ms distance.
23  *
24  *
25  * This file is part of IRMP https://github.com/IRMP-org/IRMP.
26  * This file is part of Arduino-IRremote https://github.com/Arduino-IRremote/Arduino-IRremote.
27  *
28  ************************************************************************************
29  * MIT License
30  *
31  * Copyright (c) 2022-2025 Armin Joachimsmeyer
32  *
33  * Permission is hereby granted, free of charge, to any person obtaining a copy
34  * of this software and associated documentation files (the "Software"), to deal
35  * in the Software without restriction, including without limitation the rights
36  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
37  * copies of the Software, and to permit persons to whom the Software is furnished
38  * to do so, subject to the following conditions:
39  *
40  * The above copyright notice and this permission notice shall be included in all
41  * copies or substantial portions of the Software.
42  *
43  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
44  * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
45  * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
46  * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
47  * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
48  * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
49  *
50  ************************************************************************************
51  */
52 
53 /*
54  * This library can be configured at compile time by the following options / macros:
55  * For more details see: https://github.com/Arduino-IRremote/Arduino-IRremote#compile-options--macros-for-this-library (scroll down)
56  *
57  * - IR_RECEIVE_PIN The pin number for TinyIRReceiver IR input.
58  * - IR_FEEDBACK_LED_PIN The pin number for TinyIRReceiver feedback LED.
59  * - NO_LED_FEEDBACK_CODE Disables the feedback LED function for send and receive. Saves 14 bytes program memory.
60  * - NO_LED_RECEIVE_FEEDBACK_CODE Disables the LED feedback code for receive.
61  * - NO_LED_SEND_FEEDBACK_CODE Disables the LED feedback code for send.
62  * - DISABLE_PARITY_CHECKS Disable parity checks. Saves 48 bytes of program memory.
63  * - USE_EXTENDED_NEC_PROTOCOL Like NEC, but take the 16 bit address as one 16 bit value and not as 8 bit normal and 8 bit inverted value.
64  * - USE_ONKYO_PROTOCOL Like NEC, but take the 16 bit address and command each as one 16 bit value and not as 8 bit normal and 8 bit inverted value.
65  * - USE_FAST_PROTOCOL Use FAST protocol (no address and 16 bit data, interpreted as 8 bit command and 8 bit inverted command) instead of NEC.
66  * - ENABLE_NEC2_REPEATS Instead of sending / receiving the NEC special repeat code, send / receive the original frame for repeat.
67  * - USE_CALLBACK_FOR_TINY_RECEIVER Call the user provided function "void handleReceivedTinyIRData()" each time a frame or repeat is received.
68  */
69 
70 #ifndef _TINY_IR_RECEIVER_HPP
71 #define _TINY_IR_RECEIVER_HPP
72 
73 #include <Arduino.h>
74 
75 /*
76  * Protocol selection
77  */
78 //#define DISABLE_PARITY_CHECKS // Disable parity checks. Saves 48 bytes of program memory.
79 //#define USE_EXTENDED_NEC_PROTOCOL // Like NEC, but take the 16 bit address as one 16 bit value and not as 8 bit normal and 8 bit inverted value.
80 //#define USE_ONKYO_PROTOCOL // Like NEC, but take the 16 bit address and command each as one 16 bit value and not as 8 bit normal and 8 bit inverted value.
81 //#define USE_FAST_PROTOCOL // Use FAST protocol instead of NEC / ONKYO.
82 //#define ENABLE_NEC2_REPEATS // Instead of sending / receiving the NEC special repeat code, send / receive the original frame for repeat.
83 
84 //#define IR_RECEIVE_PIN 2
85 //#define NO_LED_RECEIVE_FEEDBACK_CODE // Disables the LED feedback code for receive.
86 //#define IR_FEEDBACK_LED_PIN 12 // Use this, to disable use of LED_BUILTIN definition for IR_FEEDBACK_LED_PIN
87 #include "TinyIR.h"
88 
89 #include "digitalWriteFast.h"
94 #if defined(DEBUG)
95 #define LOCAL_DEBUG
96 #define LOCAL_DEBUG_ATTACH_INTERRUPT
97 #else
98 //#define LOCAL_DEBUG // This enables debug output only for this file
99 //#define LOCAL_DEBUG_ATTACH_INTERRUPT // To see if attachInterrupt() or static interrupt (by register tweaking) is used and no other debug output
100 #endif
101 
102 #if defined(TRACE)
103 #define LOCAL_TRACE_STATE_MACHINE
104 #else
105 //#define LOCAL_TRACE_STATE_MACHINE // to see the state of the ISR (Interrupt Service Routine) state machine
106 #endif
107 
108 //#define _IR_MEASURE_TIMING // Activate this if you want to enable internal hardware timing measurement.
109 //#define _IR_TIMING_TEST_PIN 7
112 
113 /*
114  * Set input pin and output pin definitions etc.
115  */
116 #if defined(IR_INPUT_PIN)
117 #warning "IR_INPUT_PIN is deprecated, use IR_RECEIVE_PIN"
118 #define IR_RECEIVE_PIN IR_INPUT_PIN
119 #endif
120 
121 #if !defined(IR_RECEIVE_PIN)
122 # if defined(__AVR_ATtiny1616__) || defined(__AVR_ATtiny3216__) || defined(__AVR_ATtiny3217__)
123 #warning "IR_RECEIVE_PIN is not defined, so it is set to 10"
124 #define IR_RECEIVE_PIN 10
125 # elif defined(__AVR_ATtiny816__)
126 #warning "IR_RECEIVE_PIN is not defined, so it is set to 14"
127 #define IR_RECEIVE_PIN 14
128 # else
129 #warning "IR_RECEIVE_PIN is not defined, so it is set to 2"
130 #define IR_RECEIVE_PIN 2
131 # endif
132 #endif
133 
134 #if !defined(NO_LED_RECEIVE_FEEDBACK_CODE)
135 #define LED_RECEIVE_FEEDBACK_CODE // Resolve the double negative
136 #endif
137 
138 #if !( \
139  (defined(__AVR_ATtiny25__) || defined(__AVR_ATtiny45__) || defined(__AVR_ATtiny85__)) /* ATtinyX5 */ \
140 || defined(__AVR_ATtiny88__) /* MH-ET LIVE Tiny88 */ \
141 || defined(__AVR_ATmega1280__) || defined(__AVR_ATmega1281__) || defined(__AVR_ATmega2560__) || defined(__AVR_ATmega2561__) \
142 || defined(__AVR_ATmega16U4__) || defined(__AVR_ATmega32U4__) \
143 || defined(__AVR_ATmega8__) || defined(__AVR_ATmega48__) || defined(__AVR_ATmega48P__) || defined(__AVR_ATmega48PB__) || defined(__AVR_ATmega88P__) || defined(__AVR_ATmega88PB__) \
144 || defined(__AVR_ATmega168__) || defined(__AVR_ATmega168PA__) || defined(__AVR_ATmega168PB__) || defined(__AVR_ATmega328__) || defined(__AVR_ATmega328P__) || defined(__AVR_ATmega328PB__) \
145  /* ATmegas with ports 0,1,2 above and ATtiny167 only 2 pins below */ \
146 || ( (defined(__AVR_ATtiny87__) || defined(__AVR_ATtiny167__)) && ( (defined(ARDUINO_AVR_DIGISPARKPRO) && ((IR_RECEIVE_PIN == 3) || (IR_RECEIVE_PIN == 9))) /*ATtinyX7(digisparkpro) and pin 3 or 9 */\
147  || (! defined(ARDUINO_AVR_DIGISPARKPRO) && ((IR_RECEIVE_PIN == 3) || (IR_RECEIVE_PIN == 14)))) ) /*ATtinyX7(ATTinyCore) and pin 3 or 14 */ \
148 )
149 #define TINY_RECEIVER_USE_ARDUINO_ATTACH_INTERRUPT // Cannot use any static ISR vector here. In other cases we have code provided for generating interrupt on pin change.
150 #endif
151 
157 
158 uint32_t sMicrosOfGap; // The length of the gap before the start bit, used for trace
164 #if defined(ESP8266) || defined(ESP32)
165 IRAM_ATTR
166 #endif
168 #if defined(_IR_MEASURE_TIMING) && defined(_IR_TIMING_TEST_PIN)
169  digitalWriteFast(_IR_TIMING_TEST_PIN, HIGH); // 2 clock cycles
170 #endif
171  /*
172  * Save IR input level
173  * Negative logic, true / HIGH means inactive / IR space, LOW / false means IR mark.
174  */
175  uint_fast8_t tIRLevel = digitalReadFast(IR_RECEIVE_PIN);
176 
177 #if defined(LED_RECEIVE_FEEDBACK_CODE) && defined(IR_FEEDBACK_LED_PIN)
178 # if defined(FEEDBACK_LED_IS_ACTIVE_LOW)
179  digitalWriteFast(IR_FEEDBACK_LED_PIN, tIRLevel);
180 # else
181  digitalWriteFast(IR_FEEDBACK_LED_PIN, !tIRLevel);
182 # endif
183 #endif
184 
185  /*
186  * 1. compute microseconds after last change
187  */
188  // Repeats can be sent after a pause, which is longer than 64000 microseconds, so we need a 32 bit value for check of repeats
189  uint32_t tCurrentMicros = micros();
190  uint32_t tMicrosOfMarkOrSpace32 = tCurrentMicros - TinyIRReceiverControl.LastChangeMicros; // statement is required to force 32 bit arithmetic
191  uint16_t tMicrosOfMarkOrSpace = tMicrosOfMarkOrSpace32;
192 
193  TinyIRReceiverControl.LastChangeMicros = tCurrentMicros;
194 
195  uint8_t tState = TinyIRReceiverControl.IRReceiverState;
196 
197 #if defined(LOCAL_TRACE_STATE_MACHINE)
198  Serial.print(tState);
199  Serial.print(F(" D="));
200  Serial.print(tMicrosOfMarkOrSpace);
201 // Serial.print(F(" I="));
202 // Serial.print(tIRLevel);
203  Serial.print('|');
204 #endif
205 
206  if (tIRLevel == LOW) {
207  /*
208  * We are at the start of a mark here and tMicrosOfMarkOrSpace is the time of the previous space
209  */
210  if (tMicrosOfMarkOrSpace > TINY_RECEIVER_MARK_TIMEOUT) {
211  // timeout -> must reset state machine
213  }
215  // We are at the beginning of the header mark, check timing at the next transition
217  TinyIRReceiverControl.Flags = IRDATA_FLAGS_EMPTY; // If we do it here, it saves 4 bytes
218 #if defined(TRACE) // Do not use LOCAL_TRACE here since sMicrosOfGap is read in a cpp file at TRACE
219  sMicrosOfGap = tMicrosOfMarkOrSpace32;
220 #endif
221 #if defined(ENABLE_NEC2_REPEATS)
222  // Check for repeat, where full frame is sent again after TINY_RECEIVER_REPEAT_PERIOD ms
223  // Not required for NEC, where repeats are detected by a special header space duration
224  // Must use 32 bit arithmetic here!
225  if (tMicrosOfMarkOrSpace32 < TINY_RECEIVER_MAXIMUM_REPEAT_DISTANCE) {
227  }
228 #endif
229  }
230 
232  if (tMicrosOfMarkOrSpace >= lowerValue25Percent(TINY_RECEIVER_HEADER_SPACE)
233  && tMicrosOfMarkOrSpace <= upperValue25Percent(TINY_RECEIVER_HEADER_SPACE)) {
234  /*
235  * We had a valid data header space before -> initialize data
236  */
238 #if (TINY_RECEIVER_BITS > 16)
240 #else
242 #endif
245 
246 #if !defined(ENABLE_NEC2_REPEATS)
247  // Alternatively check for NEC repeat header space length
248  } else if (tMicrosOfMarkOrSpace >= lowerValue25Percent(NEC_REPEAT_HEADER_SPACE)
249  && tMicrosOfMarkOrSpace <= upperValue25Percent(NEC_REPEAT_HEADER_SPACE)
251  /*
252  * We have a repeat header here and no broken receive before -> set repeat flag
253  */
256 #endif
257  } else {
258  // This parts are optimized by the compiler into jumps to one code :-)
259  // Wrong length -> reset state
261  }
262  }
263 
264 
265  else if (tState == IR_RECEIVER_STATE_WAITING_FOR_DATA_MARK) {
266  /*
267  * Start of data mark here, check data space length
268  * Maybe the minimum length check could be removed here.
269  */
270  if (tMicrosOfMarkOrSpace >= lowerValue50Percent(TINY_RECEIVER_ZERO_SPACE)
271  && tMicrosOfMarkOrSpace <= upperValue50Percent(TINY_RECEIVER_ONE_SPACE)) {
272  // We have a valid bit here
274  if (tMicrosOfMarkOrSpace >= TINY_RECEIVER_ONE_THRESHOLD) {
275  // we received a 1
276 #if (TINY_RECEIVER_BITS > 16)
278 #else
280 #endif
281  } else {
282  // we received a 0 - empty code for documentation
283  }
284  // prepare for next bit
287  } else {
288  // Wrong length -> reset state
290  }
291  } else {
292  // error wrong state for the received level, e.g. if we missed one change interrupt -> reset state
294  }
295  }
296 
297  else {
298  /*
299  * We are at the start of a space here and tMicrosOfMarkOrSpace is the time of the previous mark
300  *
301  */
303  /*
304  * Check length of header mark here
305  */
306  if (tMicrosOfMarkOrSpace >= lowerValue25Percent(TINY_RECEIVER_HEADER_MARK)
307  && tMicrosOfMarkOrSpace <= upperValue25Percent(TINY_RECEIVER_HEADER_MARK)) {
309  } else {
310  // Wrong length of header mark -> reset state
312  }
313  }
314 
315  else if (tState == IR_RECEIVER_STATE_WAITING_FOR_DATA_SPACE) {
316  // Check data mark length
317  if (tMicrosOfMarkOrSpace >= lowerValue50Percent(TINY_RECEIVER_BIT_MARK)
318  && tMicrosOfMarkOrSpace <= upperValue50Percent(TINY_RECEIVER_BIT_MARK)) {
319  /*
320  * We have a valid mark here, check for transmission complete, i.e. the mark of the stop bit
321  */
323 #if !defined(ENABLE_NEC2_REPEATS)
324  || (TinyIRReceiverControl.Flags & IRDATA_FLAGS_IS_REPEAT) // Do not check for full length received, if we have a short repeat frame
325 #endif
326  ) {
327  /*
328  * Code complete -> optionally check parity
329  */
330  // Reset state for new start
332 
333 #if !defined(DISABLE_PARITY_CHECKS) && (TINY_RECEIVER_ADDRESS_BITS == 16) && TINY_RECEIVER_ADDRESS_HAS_8_BIT_PARITY
334  /*
335  * Check address parity
336  * Address is sent first and contained in the lower word
337  */
339 #if defined(ENABLE_NEC2_REPEATS)
340  TinyIRReceiverControl.Flags |= IRDATA_FLAGS_PARITY_FAILED; // here we can have the repeat flag already set
341 #else
342  TinyIRReceiverControl.Flags = IRDATA_FLAGS_PARITY_FAILED; // here we do not check anything, if we have a repeat
343 #endif
344  }
345 #endif
346 #if !defined(DISABLE_PARITY_CHECKS) && (TINY_RECEIVER_COMMAND_BITS == 16) && TINY_RECEIVER_COMMAND_HAS_8_BIT_PARITY
347  /*
348  * Check command parity
349  */
350 #if (TINY_RECEIVER_ADDRESS_BITS > 0)
352 #if defined(ENABLE_NEC2_REPEATS)
354 #else
356 #endif
357 # if defined(LOCAL_DEBUG)
358  Serial.print(F("Parity check for command failed. Command="));
359  Serial.print(TinyIRReceiverControl.IRRawData.UBytes[2], HEX);
360  Serial.print(F(" parity="));
361  Serial.println(TinyIRReceiverControl.IRRawData.UBytes[3], HEX);
362 # endif
363 #else
364  // No address, so command and parity are in the lowest bytes
367 # if defined(LOCAL_DEBUG)
368  Serial.print(F("Parity check for command failed. Command="));
369  Serial.print(TinyIRReceiverControl.IRRawData.UBytes[0], HEX);
370  Serial.print(F(" parity="));
371  Serial.println(TinyIRReceiverControl.IRRawData.UBytes[1], HEX);
372 # endif
373 #endif
374  }
375 #endif
376  /*
377  * Call user provided callback here
378  * The parameter size is dependent of the code variant used in order to save program memory.
379  * We have 6 cases: 0, 8 bit or 16 bit address, each with 8 or 16 bit command
380  */
381 #if !defined(ARDUINO_ARCH_MBED) && !defined(ESP32) // no Serial etc. in callback for ESP -> no interrupt required, WDT is running!
382  interrupts(); // enable interrupts, so delay() etc. works in callback
383 #endif
386 #if (TINY_RECEIVER_ADDRESS_BITS > 0)
387 # if TINY_RECEIVER_ADDRESS_HAS_8_BIT_PARITY
388  // Here we have 8 bit address
390 # else
391  // Here we have 16 bit address
393 # endif
394 # if TINY_RECEIVER_COMMAND_HAS_8_BIT_PARITY
395  // Here we have 8 bit command
397 # else
398  // Here we have 16 bit command
400 # endif
401 
402 #else
403  // Here we have NO address
404 # if TINY_RECEIVER_COMMAND_HAS_8_BIT_PARITY
405  // Here we have 8 bit command
407 # else
408  // Here we have 16 bit command
410 # endif
411 #endif
412 #if defined(USE_CALLBACK_FOR_TINY_RECEIVER)
414 #endif
415 
416  } else {
417  // not finished yet
419  }
420  } else {
421  // Wrong length -> reset state
423  }
424  } else {
425  // error wrong state for the received level, e.g. if we missed one change interrupt -> reset state
427  }
428  }
429 
431 #ifdef _IR_MEASURE_TIMING
432  digitalWriteFast(_IR_TIMING_TEST_PIN, LOW); // 2 clock cycles
433 #endif
434 }
435 
438 }
439 
440 /*
441  * Function to be used as drop in for IrReceiver.decode()
442  */
444  bool tJustWritten = TinyIRReceiverData.justWritten;
445  if (tJustWritten) {
447  }
448  return tJustWritten;
449 }
450 
451 /*
452  * Checks if IR_RECEIVE_PIN is connected and high
453  * @return true, if IR Receiver is attached
454  */
456  pinModeFast(IR_RECEIVE_PIN, OUTPUT);
457  digitalWriteFast(IR_RECEIVE_PIN, LOW); // discharge pin capacity
458  pinModeFast(IR_RECEIVE_PIN, INPUT);
459  return digitalRead(IR_RECEIVE_PIN); // use slow digitalRead here, since the pin capacity is not fully charged again if we use digitalReadFast.
460 }
461 
467  pinModeFast(IR_RECEIVE_PIN, INPUT);
468 
469 #if defined(LED_RECEIVE_FEEDBACK_CODE) && defined(IR_FEEDBACK_LED_PIN)
470  pinModeFast(IR_FEEDBACK_LED_PIN, OUTPUT);
471 # if defined(FEEDBACK_LED_IS_ACTIVE_LOW)
472  digitalWriteFast(IR_FEEDBACK_LED_PIN, HIGH);
473 # endif
474 #endif
476 }
477 
478 void printTinyReceiverResultMinimal(Print *aSerial) {
479 // Print only very short output, since we are in an interrupt context and do not want to miss the next interrupts of the repeats coming soon
480 #if defined(USE_FAST_PROTOCOL)
481  aSerial->print(F("C=0x"));
482 #else
483  aSerial->print(F("A=0x"));
484  aSerial->print(TinyIRReceiverData.Address, HEX);
485  aSerial->print(F(" C=0x"));
486 #endif
487  aSerial->print(TinyIRReceiverData.Command, HEX);
489  aSerial->print(F(" R"));
490  }
491 #if !defined(DISABLE_PARITY_CHECKS)
493  aSerial->print(F(" P"));
494  }
495 #endif
496  aSerial->println();
497 }
498 
499 #if defined (LOCAL_DEBUG_ATTACH_INTERRUPT) && !defined(STR)
500 // Helper macro for getting a macro definition as string
501 #define STR_HELPER(x) #x
502 #define STR(x) STR_HELPER(x)
503 #endif
504 
505 /**************************************************
506  * Pin to interrupt mapping for different platforms
507  **************************************************/
508 #if defined(__AVR_ATtiny816__) || defined(__AVR_ATtiny1616__) || defined(__AVR_ATtiny3216__) || defined(__AVR_ATtiny3217__)
509 #define USE_ATTACH_INTERRUPT_DIRECT
510 
511 #elif !defined(__AVR__) || defined(TINY_RECEIVER_USE_ARDUINO_ATTACH_INTERRUPT)
512 // Default for all NON AVR platforms
513 #define USE_ATTACH_INTERRUPT
514 
515 #else
516 # if defined(__AVR_ATtiny25__) || defined(__AVR_ATtiny45__) || defined(__AVR_ATtiny85__)
517 #define USE_PCIE
518 
519 # elif defined(__AVR_ATtiny87__) || defined(__AVR_ATtiny167__)
520 # if defined(ARDUINO_AVR_DIGISPARKPRO)
521 # if (IR_RECEIVE_PIN == 3)
522 #define USE_INT0
523 # elif (IR_RECEIVE_PIN == 9)
524 #define USE_INT1
525 # else
526 # error "IR_RECEIVE_PIN must be 9 or 3."
527 # endif // if (IR_RECEIVE_PIN == 9)
528 # else // defined(ARDUINO_AVR_DIGISPARKPRO)
529 # if (IR_RECEIVE_PIN == 14)
530 #define USE_INT0
531 # elif (IR_RECEIVE_PIN == 3)
532 #define USE_INT1
533 # else
534 # error "IR_RECEIVE_PIN must be 14 or 3."
535 # endif // if (IR_RECEIVE_PIN == 14)
536 # endif
537 
538 # elif (defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__))
539 # if (IR_RECEIVE_PIN == 21)
540 #define USE_INT0
541 # elif (IR_RECEIVE_PIN == 20)
542 #define USE_INT1
543 # else
544 #warning "No pin mapping for IR_RECEIVE_PIN to interrupt found -> attachInterrupt() is used now."
545 #define USE_ATTACH_INTERRUPT
546 # endif
547 
548 # else // defined(__AVR_ATtiny25__)
549 /*
550  * ATmegas + ATtiny88 here
551  */
552 # if (IR_RECEIVE_PIN == 2)
553 #define USE_INT0
554 # elif (IR_RECEIVE_PIN == 3)
555 #define USE_INT1
556 
557 # elif IR_RECEIVE_PIN == 4 || IR_RECEIVE_PIN == 5 || IR_RECEIVE_PIN == 6 || IR_RECEIVE_PIN == 7
558  //ATmega328 (Uno, Nano ) etc. Enable pin change interrupt 20 to 23 for port PD4 to PD7 (Arduino pin 4 to 7)
559 #define USE_PCINT2
560 # elif IR_RECEIVE_PIN == 8 || IR_RECEIVE_PIN == 9 || IR_RECEIVE_PIN == 10 || IR_RECEIVE_PIN == 11 || IR_RECEIVE_PIN == 12 || IR_RECEIVE_PIN == 13
561  //ATmega328 (Uno, Nano ) etc. Enable pin change interrupt 0 to 5 for port PB0 to PB5 (Arduino pin 8 to 13)
562 #define USE_PCINT0
563 # elif IR_RECEIVE_PIN == A0 || IR_RECEIVE_PIN == A1 || IR_RECEIVE_PIN == A2 || IR_RECEIVE_PIN == A3 || IR_RECEIVE_PIN == A4 || IR_RECEIVE_PIN == A5
564  //ATmega328 (Uno, Nano ) etc. Enable pin change interrupt 8 to 13 for port PC0 to PC5 (Arduino pin A0 to A5)
565 #define USE_PCINT1
566 
567 # else
568 #warning "No pin mapping for IR_RECEIVE_PIN to interrupt found -> attachInterrupt() is used now."
569 #define USE_ATTACH_INTERRUPT
570 # endif // if (IR_RECEIVE_PIN == 2)
571 # endif // defined(__AVR_ATtiny25__)
572 #endif // ! defined(__AVR__) || defined(TINY_RECEIVER_USE_ARDUINO_ATTACH_INTERRUPT)
573 
579 #if defined(_IR_MEASURE_TIMING) && defined(_IR_TIMING_TEST_PIN)
580  pinModeFast(_IR_TIMING_TEST_PIN, OUTPUT);
581 #endif
582 
583 #if defined(USE_ATTACH_INTERRUPT) || defined(USE_ATTACH_INTERRUPT_DIRECT)
584 # if defined(USE_ATTACH_INTERRUPT)
585 # if defined(NOT_AN_INTERRUPT) // check if IDE has defined the check of digitalPinToInterrupt
586  if(digitalPinToInterrupt(IR_RECEIVE_PIN) == NOT_AN_INTERRUPT){
587  return false;
588  }
589 # endif
590  // costs 112 bytes program memory + 4 bytes RAM
591 # if defined(ARDUINO_ARCH_SAMD) // see https://www.arduino.cc/reference/tr/language/functions/external-interrupts/attachinterrupt/ paragraph: Syntax
592  attachInterrupt(IR_RECEIVE_PIN, IRPinChangeInterruptHandler, CHANGE); // no extra pin mapping here :-(
593 # else
594  attachInterrupt(digitalPinToInterrupt(IR_RECEIVE_PIN), IRPinChangeInterruptHandler, CHANGE);
595 # endif
596 # else
597  // USE_ATTACH_INTERRUPT_DIRECT here, only defined for ATtinies *16, see above
598  // 2.2 us more than version configured with macros and not compatible
599  attachInterrupt(IR_RECEIVE_PIN, IRPinChangeInterruptHandler, CHANGE); // no extra pin mapping here
600 # endif
601 
602 # if defined(LOCAL_DEBUG_ATTACH_INTERRUPT)
603  Serial.println(F("Use attachInterrupt for pin=" STR(IR_RECEIVE_PIN)));
604 # endif
605 #else
606 # if defined(LOCAL_DEBUG_ATTACH_INTERRUPT)
607  Serial.println(F("Use static interrupt for pin=" STR(IR_RECEIVE_PIN)));
608 # endif
609 
610 # if defined(USE_INT0)
611  // interrupt on any logical change
612  EICRA |= _BV(ISC00);
613  // clear interrupt bit
614  EIFR |= 1 << INTF0;
615  // enable interrupt on next change
616  EIMSK |= 1 << INT0;
617 
618 # elif defined(USE_INT1)
619  EICRA |= _BV(ISC10);
620 // clear interrupt bit
621  EIFR |= 1 << INTF1;
622 // enable interrupt on next change
623  EIMSK |= 1 << INT1;
624 
625 # elif defined(USE_PCIE) // For ATtiny85 etc.
626  // use PinChangeInterrupt no INT0 for pin PB2
627  PCMSK = _BV(IR_RECEIVE_PIN);
628  // clear interrupt bit
629  GIFR |= 1 << PCIF;
630  // enable interrupt on next change
631  GIMSK |= 1 << PCIE;
632 
633 # elif defined(USE_PCINT0)
634  PCICR |= _BV(PCIE0);
635  PCMSK0 = digitalPinToBitMask(IR_RECEIVE_PIN);
636 # elif defined(USE_PCINT1)
637  PCICR |= _BV(PCIE1);
638  PCMSK1 = digitalPinToBitMask(IR_RECEIVE_PIN);
639 # elif defined(USE_PCINT2)
640  PCICR |= _BV(PCIE2);
641  PCMSK2 = digitalPinToBitMask(IR_RECEIVE_PIN);
642 # else
643  return false;
644 # endif
645 #endif // defined(USE_ATTACH_INTERRUPT)
646  return true;
647 }
648 
650 #if defined(_IR_MEASURE_TIMING) && defined(_IR_TIMING_TEST_PIN)
651  pinModeFast(_IR_TIMING_TEST_PIN, OUTPUT);
652 #endif
653 
654 #if defined(USE_ATTACH_INTERRUPT) || defined(USE_ATTACH_INTERRUPT_DIRECT)
655 # if defined(USE_ATTACH_INTERRUPT)
656  detachInterrupt(digitalPinToInterrupt(IR_RECEIVE_PIN));
657 # else
658  detachInterrupt(IR_RECEIVE_PIN);
659 # endif
660 
661 #else
662 # if defined(USE_INT0)
663  // clear interrupt bit
664  EIFR |= 1 << INTF0;
665  // disable interrupt on next change
666  EIMSK &= ~(1 << INT0);
667 
668 # elif defined(USE_INT1)
669  // clear interrupt bit
670  EIFR |= 1 << INTF1;
671  // disable interrupt on next change
672  EIMSK &= ~(1 << INT1);
673 
674 # elif defined(USE_PCIE) // For ATtiny85 etc.
675  // clear interrupt bit
676  GIFR |= 1 << PCIF;
677  // disable interrupt on next change
678  GIMSK &= ~(1 << PCIE);
679 
680 # elif defined(USE_PCINT0)
681  PCICR &= ~(_BV(PCIE0));
682 # elif defined(USE_PCINT1)
683  PCICR &= ~(_BV(PCIE1));
684 # elif defined(USE_PCINT2)
685  PCICR &= ~(_BV(PCIE2));
686 
687 # endif
688 #endif // defined(USE_ATTACH_INTERRUPT)
689 }
690 
691 /*
692  * Specify the right INT0, INT1 or PCINT0 interrupt vector according to different pins and cores.
693  * The default value of TINY_RECEIVER_USE_ARDUINO_ATTACH_INTERRUPT is set in TinyIRReceiver.h
694  */
695 #if !(defined(USE_ATTACH_INTERRUPT) || defined(USE_ATTACH_INTERRUPT_DIRECT))
696 # if defined(USE_INT0)
697 ISR(INT0_vect)
698 
699 # elif defined(USE_INT1)
700 ISR(INT1_vect)
701 
702 # elif defined(USE_PCIE) // For ATtiny85 etc.
703 // on ATtinyX5 we do not have a INT1_vect but we can use the PCINT0_vect
704 ISR(PCINT0_vect)
705 
706 # elif defined(USE_PCINT0)
707 ISR(PCINT0_vect)
708 # elif defined(USE_PCINT1)
709 ISR(PCINT1_vect)
710 # elif defined(USE_PCINT2)
711 ISR(PCINT2_vect)
712 # else
713 void dummyFunctionToAvoidCompilerErrors()
714 # endif
715 {
717 }
718 #endif // !(defined(USE_ATTACH_INTERRUPT) || defined(USE_ATTACH_INTERRUPT_DIRECT))
719 
722 #if defined(LOCAL_DEBUG_ATTACH_INTERRUPT)
723 #undef LOCAL_DEBUG_ATTACH_INTERRUPT
724 #endif
725 #if defined(LOCAL_TRACE_STATE_MACHINE)
726 #undef LOCAL_TRACE_STATE_MACHINE
727 #endif
728 
729 #if defined(LOCAL_DEBUG)
730 #undef LOCAL_DEBUG
731 #endif
732 #endif // _TINY_IR_RECEIVER_HPP
TinyIRReceiverStruct::IRReceiverState
uint8_t IRReceiverState
The state of the state machine.
Definition: TinyIR.h:208
lowerValue25Percent
#define lowerValue25Percent(aDuration)
Definition: TinyIR.h:186
digitalReadFast
#define digitalReadFast
Definition: digitalWriteFast.h:399
IR_RECEIVER_STATE_WAITING_FOR_START_MARK
#define IR_RECEIVER_STATE_WAITING_FOR_START_MARK
Definition: TinyIR.h:194
TinyIRReceiverCallbackDataStruct::Flags
uint8_t Flags
Definition: TinyIR.h:249
TinyIRReceiverStruct
Control and data variables of the state machine for TinyReceiver.
Definition: TinyIR.h:203
IR_RECEIVER_STATE_WAITING_FOR_DATA_MARK
#define IR_RECEIVER_STATE_WAITING_FOR_DATA_MARK
Definition: TinyIR.h:198
TinyIRReceiverStruct::Flags
uint8_t Flags
One of IRDATA_FLAGS_EMPTY, IRDATA_FLAGS_IS_REPEAT, and IRDATA_FLAGS_PARITY_FAILED.
Definition: TinyIR.h:220
pinModeFast
#define pinModeFast
Definition: digitalWriteFast.h:383
digitalWriteFast
#define digitalWriteFast
Definition: digitalWriteFast.h:347
TinyIRReceiverControl
TinyIRReceiverStruct TinyIRReceiverControl
Definition: TinyIRReceiver.hpp:110
IRDATA_FLAGS_IS_REPEAT
#define IRDATA_FLAGS_IS_REPEAT
The gap between the preceding frame is as smaller than the maximum gap expected for a repeat....
Definition: IRProtocol.h:147
LongUnion::UBytes
uint8_t UBytes[4]
Definition: LongUnion.h:91
enablePCIInterruptForTinyReceiver
bool enablePCIInterruptForTinyReceiver()
Initializes hardware interrupt generation according to IR_RECEIVE_PIN or use attachInterrupt() functi...
Definition: TinyIRReceiver.hpp:578
TinyIR.h
TinyIRReceiverData
volatile TinyIRReceiverCallbackDataStruct TinyIRReceiverData
Definition: TinyIRReceiver.hpp:111
LongUnion::LowWord
uint16_t LowWord
Definition: LongUnion.h:80
IR_RECEIVE_PIN
#define IR_RECEIVE_PIN
Definition: TinyIRReceiver.hpp:130
isTinyReceiverIdle
bool isTinyReceiverIdle()
Definition: TinyIRReceiver.hpp:436
TINY_RECEIVER_ONE_SPACE
#define TINY_RECEIVER_ONE_SPACE
Definition: TinyIR.h:160
handleReceivedTinyIRData
void handleReceivedTinyIRData()
Declaration of the callback function provided by the user application.
IRDATA_FLAGS_PARITY_FAILED
#define IRDATA_FLAGS_PARITY_FAILED
The current (autorepeat) frame violated parity check.
Definition: IRProtocol.h:149
disablePCIInterruptForTinyReceiver
void disablePCIInterruptForTinyReceiver()
Definition: TinyIRReceiver.hpp:649
digitalWriteFast.h
TinyIRReceiverCallbackDataStruct::Address
uint16_t Address
Definition: TinyIR.h:238
TINY_RECEIVER_BITS
#define TINY_RECEIVER_BITS
Definition: TinyIR.h:152
TinyReceiverDecode
bool TinyReceiverDecode()
Definition: TinyIRReceiver.hpp:443
LongUnion::HighWord
uint16_t HighWord
Definition: LongUnion.h:81
IR_RECEIVER_STATE_WAITING_FOR_DATA_SPACE
#define IR_RECEIVER_STATE_WAITING_FOR_DATA_SPACE
Definition: TinyIR.h:197
TinyIRReceiverCallbackDataStruct::justWritten
bool justWritten
Is set true if new data is available. Used by the main loop / TinyReceiverDecode(),...
Definition: TinyIR.h:250
upperValue50Percent
#define upperValue50Percent(aDuration)
Definition: TinyIR.h:189
TinyIRReceiverStruct::LastChangeMicros
uint32_t LastChangeMicros
Microseconds of last Pin Change Interrupt.
Definition: TinyIR.h:207
TINY_RECEIVER_HEADER_MARK
#define TINY_RECEIVER_HEADER_MARK
Definition: TinyIR.h:155
TinyIRReceiverStruct::IRRawDataMask
uint32_t IRRawDataMask
The corresponding bit mask for IRRawDataBitCounter.
Definition: TinyIR.h:214
TinyIRReceiverCallbackDataStruct
Is filled before calling the user callback to transfer received data to main loop for further process...
Definition: TinyIR.h:235
IRPinChangeInterruptHandler
void IRPinChangeInterruptHandler(void)
The ISR (Interrupt Service Routine) of TinyIRRreceiver.
Definition: TinyIRReceiver.hpp:167
TinyIRReceiverCallbackDataStruct::Command
uint16_t Command
Definition: TinyIR.h:245
sMicrosOfGap
uint32_t sMicrosOfGap
Definition: TinyIRReceiver.hpp:158
printTinyReceiverResultMinimal
void printTinyReceiverResultMinimal(Print *aSerial)
Definition: TinyIRReceiver.hpp:478
TinyIRReceiverStruct::IRRawDataBitCounter
uint8_t IRRawDataBitCounter
How many bits are currently contained in raw data.
Definition: TinyIR.h:209
TINY_RECEIVER_HEADER_SPACE
#define TINY_RECEIVER_HEADER_SPACE
Definition: TinyIR.h:157
LongUnion::ULong
uint32_t ULong
Definition: LongUnion.h:95
TINY_RECEIVER_ONE_THRESHOLD
#define TINY_RECEIVER_ONE_THRESHOLD
Definition: TinyIR.h:162
IRDATA_FLAGS_EMPTY
#define IRDATA_FLAGS_EMPTY
Definition: IRProtocol.h:146
TINY_RECEIVER_BIT_MARK
#define TINY_RECEIVER_BIT_MARK
Definition: TinyIR.h:159
upperValue25Percent
#define upperValue25Percent(aDuration)
Definition: TinyIR.h:187
NEC_REPEAT_HEADER_SPACE
#define NEC_REPEAT_HEADER_SPACE
Definition: ir_NEC.hpp:107
TINY_RECEIVER_ZERO_SPACE
#define TINY_RECEIVER_ZERO_SPACE
Definition: TinyIR.h:161
TINY_RECEIVER_MAXIMUM_REPEAT_DISTANCE
#define TINY_RECEIVER_MAXIMUM_REPEAT_DISTANCE
Definition: TinyIR.h:164
isIRReceiverAttachedForTinyReceiver
bool isIRReceiverAttachedForTinyReceiver()
Definition: TinyIRReceiver.hpp:455
lowerValue50Percent
#define lowerValue50Percent(aDuration)
Definition: TinyIR.h:188
IR_RECEIVER_STATE_WAITING_FOR_FIRST_DATA_MARK
#define IR_RECEIVER_STATE_WAITING_FOR_FIRST_DATA_MARK
Definition: TinyIR.h:196
LongUnion::UWord
struct LongUnion::@6 UWord
TinyIRReceiverStruct::IRRawData
LongUnion IRRawData
The current raw data. LongUnion helps with decoding of address and command.
Definition: TinyIR.h:215
IR_RECEIVER_STATE_WAITING_FOR_START_SPACE
#define IR_RECEIVER_STATE_WAITING_FOR_START_SPACE
Definition: TinyIR.h:195
initPCIInterruptForTinyReceiver
bool initPCIInterruptForTinyReceiver()
Sets IR_RECEIVE_PIN mode to INPUT, and if IR_FEEDBACK_LED_PIN is defined, sets feedback LED output mo...
Definition: TinyIRReceiver.hpp:466
TINY_RECEIVER_MARK_TIMEOUT
#define TINY_RECEIVER_MARK_TIMEOUT
Definition: TinyIR.h:156