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 Arduino-IRremote https://github.com/Arduino-IRremote/Arduino-IRremote.
26  * This file is also part of IRMP https://github.com/IRMP-org/IRMP.
27  *
28  ************************************************************************************
29  * MIT License
30  *
31  * Copyright (c) 2022-2026 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 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.
79 //#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.
80 //#define USE_FAST_PROTOCOL // Use FAST protocol instead of NEC / ONKYO.
81 //#define ENABLE_NEC2_REPEATS // Instead of sending / receiving the NEC special repeat code, send / receive the original frame for repeat.
82 //#define DISABLE_PARITY_CHECKS // Disable parity checks. Saves 48 bytes of program memory.
83 //#define IR_RECEIVE_PIN 2
84 //#define IR_FEEDBACK_LED_PIN 12 // Use this, to disable use of LED_BUILTIN definition for IR_FEEDBACK_LED_PIN
85 #include "TinyIR.h"
86 
87 #include "digitalWriteFast.h"
92 // This block must be located after the includes of other *.hpp files
93 //#define LOCAL_DEBUG // This enables debug output only for this file - only for development
94 //#define LOCAL_TRACE // This enables trace output only for this file - only for development
95 #include "LocalDebugLevelStart.h"
96 #if defined(LOCAL_DEBUG)
97 #define LOCAL_DEBUG_ATTACH_INTERRUPT
98 #else
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 //#define _IR_MEASURE_TIMING // Activate this if you want to enable internal hardware timing measurement.
103 //#define _IR_TIMING_TEST_PIN 7
105 volatile TinyIRReceiverCallbackDataStruct TinyIRReceiverData; // The persistent copy of all IR data after receiving a complete frame. To be used by main program.
106 
107 /*
108  * Set input pin and output pin definitions etc.
109  */
110 #if defined(IR_INPUT_PIN)
111 #warning "IR_INPUT_PIN is deprecated, use IR_RECEIVE_PIN"
112 #define IR_RECEIVE_PIN IR_INPUT_PIN
113 #endif
114 
115 #if !defined(IR_RECEIVE_PIN)
116 # if defined(__AVR_ATtiny1616__) || defined(__AVR_ATtiny3216__) || defined(__AVR_ATtiny3217__)
117 #warning "IR_RECEIVE_PIN is not defined, so it is set to 10"
118 #define IR_RECEIVE_PIN 10
119 # elif defined(__AVR_ATtiny816__)
120 #warning "IR_RECEIVE_PIN is not defined, so it is set to 14"
121 #define IR_RECEIVE_PIN 14
122 # else
123 #warning "IR_RECEIVE_PIN is not defined, so it is set to 2"
124 #define IR_RECEIVE_PIN 2
125 # endif
126 #endif
127 
128 #if !defined(NO_LED_RECEIVE_FEEDBACK_CODE)
129 #define LED_RECEIVE_FEEDBACK_CODE // Resolve the double negative
130 #endif
131 
132 #if !( \
133  (defined(__AVR_ATtiny25__) || defined(__AVR_ATtiny45__) || defined(__AVR_ATtiny85__)) /* ATtinyX5 */ \
134 || defined(__AVR_ATtiny88__) /* MH-ET LIVE Tiny88 */ \
135 || defined(__AVR_ATmega1280__) || defined(__AVR_ATmega1281__) || defined(__AVR_ATmega2560__) || defined(__AVR_ATmega2561__) \
136 || defined(__AVR_ATmega16U4__) || defined(__AVR_ATmega32U4__) \
137 || defined(__AVR_ATmega8__) || defined(__AVR_ATmega48__) || defined(__AVR_ATmega48P__) || defined(__AVR_ATmega48PB__) || defined(__AVR_ATmega88P__) || defined(__AVR_ATmega88PB__) \
138 || defined(__AVR_ATmega168__) || defined(__AVR_ATmega168PA__) || defined(__AVR_ATmega168PB__) || defined(__AVR_ATmega328__) || defined(__AVR_ATmega328P__) || defined(__AVR_ATmega328PB__) \
139  /* ATmegas with ports 0,1,2 above and ATtiny167 only 2 pins below */ \
140 || ( (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 */\
141  || (! defined(ARDUINO_AVR_DIGISPARKPRO) && ((IR_RECEIVE_PIN == 3) || (IR_RECEIVE_PIN == 14)))) ) /*ATtinyX7(ATTinyCore) and pin 3 or 14 */ \
142 )
143 /*
144  * Cannot use any static ISR vector here. In other cases we have code provided for generating interrupt on pin change.
145  * Requires additional 112 bytes program memory + 4 bytes RAM
146  */
147 #define TINY_RECEIVER_USE_ARDUINO_ATTACH_INTERRUPT
148 #endif
149 
154 extern void handleReceivedTinyIRData();
155 
156 uint32_t sMicrosOfGap; // The length of the gap before the start bit, used for trace
162 #if defined(ESP8266) || defined(ESP32)
163 IRAM_ATTR
164 #endif
166 #if defined(_IR_MEASURE_TIMING) && defined(_IR_TIMING_TEST_PIN)
167  digitalWriteFast(_IR_TIMING_TEST_PIN, HIGH); // 2 clock cycles
168 #endif
169  /*
170  * Save IR input level
171  * Negative logic, true / HIGH means inactive / IR space, LOW / false means IR mark.
172  */
173  uint_fast8_t tIRLevel = digitalReadFast(IR_RECEIVE_PIN);
174 
175 #if defined(LED_RECEIVE_FEEDBACK_CODE) && defined(IR_FEEDBACK_LED_PIN)
176 # if defined(FEEDBACK_LED_IS_ACTIVE_LOW)
177  digitalWriteFast(IR_FEEDBACK_LED_PIN, tIRLevel);
178 # else
179  digitalWriteFast(IR_FEEDBACK_LED_PIN, !tIRLevel);
180 # endif
181 #endif
182 
183  /*
184  * 1. compute microseconds after last change
185  */
186  // Repeats can be sent after a pause, which is longer than 64000 microseconds, so we need a 32 bit value for check of repeats
187  uint32_t tCurrentMicros = micros();
188  uint32_t tMicrosOfMarkOrSpace32 = tCurrentMicros - TinyIRReceiverControl.LastChangeMicros; // statement is required to force 32 bit arithmetic
189  uint16_t tMicrosOfMarkOrSpace = tMicrosOfMarkOrSpace32;
190 
191  TinyIRReceiverControl.LastChangeMicros = tCurrentMicros;
192 
193  uint8_t tState = TinyIRReceiverControl.IRReceiverState;
194 
195  TRACE_PRINT(tState);
196  TRACE_PRINT(F(" D="));
197  TRACE_PRINT(tMicrosOfMarkOrSpace);
198 // TRACE_PRINT(F(" I="));
199 // TRACE_PRINT(tIRLevel);
200  TRACE_PRINT('|');
201 
202  if (tIRLevel == LOW) {
203  /*
204  * We are at the start of a mark here and tMicrosOfMarkOrSpace is the time of the previous space
205  */
206  if (tMicrosOfMarkOrSpace > TINY_RECEIVER_MARK_TIMEOUT) {
207  // timeout -> must reset state machine
209  }
211  // We are at the beginning of the header mark, check timing at the next transition
213  TinyIRReceiverControl.Flags = IRDATA_FLAGS_EMPTY; // If we do it here, it saves 4 bytes
214 #if defined(TRACE) // Do not use LOCAL_TRACE here since sMicrosOfGap is read in a cpp file at TRACE
215  sMicrosOfGap = tMicrosOfMarkOrSpace32;
216 #endif
217 #if defined(ENABLE_NEC2_REPEATS)
218  // Check for repeat, where full frame is sent again after TINY_RECEIVER_REPEAT_PERIOD ms
219  // Not required for NEC, where repeats are detected by a special header space duration
220  // Must use 32 bit arithmetic here!
221  if (tMicrosOfMarkOrSpace32 < TINY_RECEIVER_MAXIMUM_REPEAT_DISTANCE) {
223  }
224 #endif
225  }
226 
228  if (tMicrosOfMarkOrSpace >= lowerValue25Percent(TINY_RECEIVER_HEADER_SPACE)
229  && tMicrosOfMarkOrSpace <= upperValue25Percent(TINY_RECEIVER_HEADER_SPACE)) {
230  /*
231  * We had a valid data header space before -> initialize data
232  */
234 #if (TINY_RECEIVER_BITS > 16)
236 #else
238 #endif
241 
242 #if !defined(ENABLE_NEC2_REPEATS)
243  // Alternatively check for NEC repeat header space length
244  } else if (tMicrosOfMarkOrSpace >= lowerValue25Percent(NEC_REPEAT_HEADER_SPACE)
245  && tMicrosOfMarkOrSpace <= upperValue25Percent(NEC_REPEAT_HEADER_SPACE)
247  /*
248  * We have a repeat header here and no broken receive before -> set repeat flag
249  */
252 #endif
253  } else {
254  // This parts are optimized by the compiler into jumps to one code :-)
255  // Wrong length -> reset state
257  }
258  }
259 
260  else if (tState == IR_RECEIVER_STATE_WAITING_FOR_DATA_MARK) {
261  /*
262  * Start of data mark here, check data space length
263  * Maybe the minimum length check could be removed here.
264  */
265  if (tMicrosOfMarkOrSpace >= lowerValue50Percent(TINY_RECEIVER_ZERO_SPACE)
266  && tMicrosOfMarkOrSpace <= upperValue50Percent(TINY_RECEIVER_ONE_SPACE)) {
267  // We have a valid bit here
269  if (tMicrosOfMarkOrSpace >= TINY_RECEIVER_ONE_THRESHOLD) {
270  // we received a 1
271 #if (TINY_RECEIVER_BITS > 16)
273 #else
275 #endif
276  } else {
277  // we received a 0 - empty code for documentation
278  }
279  // prepare for next bit
282  } else {
283  // Wrong length -> reset state
285  }
286  } else {
287  // error wrong state for the received level, e.g. if we missed one change interrupt -> reset state
289  }
290  }
291 
292  else {
293  /*
294  * We are at the start of a space here and tMicrosOfMarkOrSpace is the time of the previous mark
295  *
296  */
298  /*
299  * Check length of header mark here
300  */
301  if (tMicrosOfMarkOrSpace >= lowerValue25Percent(TINY_RECEIVER_HEADER_MARK)
302  && tMicrosOfMarkOrSpace <= upperValue25Percent(TINY_RECEIVER_HEADER_MARK)) {
304  } else {
305  // Wrong length of header mark -> reset state
307  }
308  }
309 
310  else if (tState == IR_RECEIVER_STATE_WAITING_FOR_DATA_SPACE) {
311  // Check data mark length
312  if (tMicrosOfMarkOrSpace >= lowerValue50Percent(TINY_RECEIVER_BIT_MARK)
313  && tMicrosOfMarkOrSpace <= upperValue50Percent(TINY_RECEIVER_BIT_MARK)) {
314  /*
315  * We have a valid mark here, check for transmission complete, i.e. the mark of the stop bit
316  */
318 #if !defined(ENABLE_NEC2_REPEATS)
319  || (TinyIRReceiverControl.Flags & IRDATA_FLAGS_IS_REPEAT) // Do not check for full length received, if we have a short repeat frame
320 #endif
321  ) {
322  /*
323  * Code complete -> optionally check parity
324  */
325  // Reset state for new start
327 
328 #if !defined(DISABLE_PARITY_CHECKS) && (TINY_RECEIVER_ADDRESS_BITS == 16) && TINY_RECEIVER_ADDRESS_HAS_8_BIT_PARITY
329  /*
330  * Check address parity
331  * Address is sent first and contained in the lower word
332  */
334 #if defined(ENABLE_NEC2_REPEATS)
335  TinyIRReceiverControl.Flags |= IRDATA_FLAGS_PARITY_FAILED; // here we can have the repeat flag already set
336 #else
337  TinyIRReceiverControl.Flags = IRDATA_FLAGS_PARITY_FAILED; // here we do not check anything, if we have a repeat
338 #endif
339  }
340 #endif
341 #if !defined(DISABLE_PARITY_CHECKS) && (TINY_RECEIVER_COMMAND_BITS == 16) && TINY_RECEIVER_COMMAND_HAS_8_BIT_PARITY
342  /*
343  * Check command parity
344  */
345 #if (TINY_RECEIVER_ADDRESS_BITS > 0)
347 #if defined(ENABLE_NEC2_REPEATS)
349 #else
351 #endif
352  DEBUG_PRINT(F("Parity check for command failed. Command="));
354  DEBUG_PRINT(F(" parity="));
356 #else
357  // No address, so command and parity are in the lowest bytes
360  DEBUG_PRINT(F("Parity check for command failed. Command="));
362  DEBUG_PRINT(F(" parity="));
364 #endif
365  }
366 #endif
367  /*
368  * Call user provided callback here
369  * The parameter size is dependent of the code variant used in order to save program memory.
370  * We have 6 cases: 0, 8 bit or 16 bit address, each with 8 or 16 bit command
371  */
372 #if !defined(ARDUINO_ARCH_MBED) && !defined(ESP32) // no Serial etc. possible in callback for RTOS based cores like ESP, even when interrupts are enabled
373  interrupts(); // enable interrupts, so delay() etc. works in callback
374 #endif
377 #if (TINY_RECEIVER_ADDRESS_BITS > 0)
378 # if TINY_RECEIVER_ADDRESS_HAS_8_BIT_PARITY
379  // Here we have 8 bit address
381 # else
382  // Here we have 16 bit address
384 # endif
385 # if TINY_RECEIVER_COMMAND_HAS_8_BIT_PARITY
386  // Here we have 8 bit command
388 # else
389  // Here we have 16 bit command
391 # endif
392 
393 #else
394  // Here we have NO address
395 # if TINY_RECEIVER_COMMAND_HAS_8_BIT_PARITY
396  // Here we have 8 bit command
398 # else
399  // Here we have 16 bit command
401 # endif
402 #endif
403 #if defined(USE_CALLBACK_FOR_TINY_RECEIVER)
405 #endif
406 
407  } else {
408  // not finished yet
410  }
411  } else {
412  // Wrong length -> reset state
414  }
415  } else {
416  // error wrong state for the received level, e.g. if we missed one change interrupt -> reset state
418  }
419  }
420 
422 #ifdef _IR_MEASURE_TIMING
423  digitalWriteFast(_IR_TIMING_TEST_PIN, LOW); // 2 clock cycles
424 #endif
425 }
426 
429 }
430 
431 /*
432  * Function to be used as drop in for IrReceiver.decode()
433  */
435  bool tJustWritten = TinyIRReceiverData.justWritten;
436  if (tJustWritten) {
438  }
439  return tJustWritten;
440 }
441 
442 /*
443  * Checks if IR_RECEIVE_PIN is connected and high
444  * @return true, if IR Receiver is attached
445  */
447  pinModeFast(IR_RECEIVE_PIN, OUTPUT);
448  digitalWriteFast(IR_RECEIVE_PIN, LOW); // discharge pin capacity
449  pinModeFast(IR_RECEIVE_PIN, INPUT);
450  return digitalRead(IR_RECEIVE_PIN); // use slow digitalRead here, since the pin capacity is not fully charged again if we use digitalReadFast.
451 }
452 
458  pinModeFast(IR_RECEIVE_PIN, INPUT);
459 
460 #if defined(LED_RECEIVE_FEEDBACK_CODE) && defined(IR_FEEDBACK_LED_PIN)
461  pinModeFast(IR_FEEDBACK_LED_PIN, OUTPUT);
462 # if defined(FEEDBACK_LED_IS_ACTIVE_LOW)
463  digitalWriteFast(IR_FEEDBACK_LED_PIN, HIGH);
464 # endif
465 #endif
467 }
468 
469 void printTinyReceiverResultMinimal(Print *aSerial) {
470 // 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
471 #if defined(USE_FAST_PROTOCOL)
472  aSerial->print(F("C=0x"));
473 #else
474  aSerial->print(F("A=0x"));
475  aSerial->print(TinyIRReceiverData.Address, HEX);
476  aSerial->print(F(" C=0x"));
477 #endif
478  aSerial->print(TinyIRReceiverData.Command, HEX);
480  aSerial->print(F(" R"));
481  }
482 #if !defined(DISABLE_PARITY_CHECKS)
484  aSerial->print(F(" P"));
485  }
486 #endif
487  aSerial->println();
488 }
489 
490 #if !defined(STR_HELPER) && !defined(STR)
491 // Helper macro for getting a macro definition as string
492 #define STR_HELPER(x) #x
493 #define STR(x) STR_HELPER(x)
494 #endif
495 
496 /**************************************************
497  * Pin to interrupt mapping for different platforms
498  **************************************************/
499 #if defined(__AVR_ATtiny816__) || defined(__AVR_ATtiny1616__) || defined(__AVR_ATtiny3216__) || defined(__AVR_ATtiny3217__)
500 #define USE_ATTACH_INTERRUPT_DIRECT
501 
502 #elif !defined(__AVR__) || defined(TINY_RECEIVER_USE_ARDUINO_ATTACH_INTERRUPT)
503 // Default for all NON AVR platforms
504 #define USE_ATTACH_INTERRUPT
505 
506 #else
507 # if defined(__AVR_ATtiny25__) || defined(__AVR_ATtiny45__) || defined(__AVR_ATtiny85__)
508 #define USE_PCIE
509 
510 # elif defined(__AVR_ATtiny87__) || defined(__AVR_ATtiny167__)
511 # if defined(ARDUINO_AVR_DIGISPARKPRO)
512 # if (IR_RECEIVE_PIN == 3)
513 #define USE_INT0
514 # elif (IR_RECEIVE_PIN == 9)
515 #define USE_INT1
516 # else
517 # error "IR_RECEIVE_PIN must be 9 or 3."
518 # endif // if (IR_RECEIVE_PIN == 9)
519 # else // defined(ARDUINO_AVR_DIGISPARKPRO)
520 # if (IR_RECEIVE_PIN == 14)
521 #define USE_INT0
522 # elif (IR_RECEIVE_PIN == 3)
523 #define USE_INT1
524 # else
525 # error "IR_RECEIVE_PIN must be 14 or 3."
526 # endif // if (IR_RECEIVE_PIN == 14)
527 # endif
528 
529 # elif (defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__))
530 # if (IR_RECEIVE_PIN == 21)
531 #define USE_INT0
532 # elif (IR_RECEIVE_PIN == 20)
533 #define USE_INT1
534 # else
535 #warning "No pin mapping for IR_RECEIVE_PIN to interrupt found -> attachInterrupt() is used now."
536 #define USE_ATTACH_INTERRUPT
537 # endif
538 
539 # else // defined(__AVR_ATtiny25__)
540 /*
541  * ATmegas + ATtiny88 here
542  */
543 # if (IR_RECEIVE_PIN == 2)
544 #define USE_INT0
545 # elif (IR_RECEIVE_PIN == 3)
546 #define USE_INT1
547 
548 # elif IR_RECEIVE_PIN == 4 || IR_RECEIVE_PIN == 5 || IR_RECEIVE_PIN == 6 || IR_RECEIVE_PIN == 7
549  //ATmega328 (Uno, Nano ) etc. Enable pin change interrupt 20 to 23 for port PD4 to PD7 (Arduino pin 4 to 7)
550 #define USE_PCINT2
551 # 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
552  //ATmega328 (Uno, Nano ) etc. Enable pin change interrupt 0 to 5 for port PB0 to PB5 (Arduino pin 8 to 13)
553 #define USE_PCINT0
554 # 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
555  //ATmega328 (Uno, Nano ) etc. Enable pin change interrupt 8 to 13 for port PC0 to PC5 (Arduino pin A0 to A5)
556 #define USE_PCINT1
557 
558 # else
559 #warning "No pin mapping for IR_RECEIVE_PIN to interrupt found -> attachInterrupt() is used now."
560 #define USE_ATTACH_INTERRUPT
561 # endif // if (IR_RECEIVE_PIN == 2)
562 # endif // defined(__AVR_ATtiny25__)
563 #endif // ! defined(__AVR__) || defined(TINY_RECEIVER_USE_ARDUINO_ATTACH_INTERRUPT)
564 
570 #if defined(_IR_MEASURE_TIMING) && defined(_IR_TIMING_TEST_PIN)
571  pinModeFast(_IR_TIMING_TEST_PIN, OUTPUT);
572 #endif
573 
574 #if defined(USE_ATTACH_INTERRUPT) || defined(USE_ATTACH_INTERRUPT_DIRECT)
575 # if defined(USE_ATTACH_INTERRUPT)
576 # if defined(NOT_AN_INTERRUPT) // check if IDE has defined the check of digitalPinToInterrupt
577  if(digitalPinToInterrupt(IR_RECEIVE_PIN) == NOT_AN_INTERRUPT){
578  return false;
579  }
580 # endif
581  // costs 112 bytes program memory + 4 bytes RAM
582 # if defined(ARDUINO_ARCH_SAMD) // see https://www.arduino.cc/reference/tr/language/functions/external-interrupts/attachinterrupt/ paragraph: Syntax
583  attachInterrupt(IR_RECEIVE_PIN, IRPinChangeInterruptHandler, CHANGE); // no extra pin mapping here :-(
584 # else
585  attachInterrupt(digitalPinToInterrupt(IR_RECEIVE_PIN), IRPinChangeInterruptHandler, CHANGE); // CHANGE can be an enum :-(
586 # endif
587 # else
588  // USE_ATTACH_INTERRUPT_DIRECT here, only defined for ATtinies *16, see above
589  // 2.2 us more than version configured with macros and not compatible
590  attachInterrupt(IR_RECEIVE_PIN, IRPinChangeInterruptHandler, CHANGE); // no extra pin mapping here
591 # endif
592 
593 # if defined(LOCAL_DEBUG_ATTACH_INTERRUPT)
594  Serial.println(F("Use attachInterrupt for pin=" STR(IR_RECEIVE_PIN)));
595 # endif
596 #else
597 # if defined(LOCAL_DEBUG_ATTACH_INTERRUPT)
598  Serial.println(F("Use hardware/static interrupt for pin=" STR(IR_RECEIVE_PIN)));
599 # endif
600 
601 # if defined(USE_INT0)
602  // interrupt on any logical change
603  EICRA |= _BV(ISC00);
604  // clear interrupt bit
605  EIFR |= 1 << INTF0;
606  // enable interrupt on next change
607  EIMSK |= 1 << INT0;
608 
609 # elif defined(USE_INT1)
610  EICRA |= _BV(ISC10);
611 // clear interrupt bit
612  EIFR |= 1 << INTF1;
613 // enable interrupt on next change
614  EIMSK |= 1 << INT1;
615 
616 # elif defined(USE_PCIE) // For ATtiny85 etc.
617  // use PinChangeInterrupt no INT0 for pin PB2
618  PCMSK = _BV(IR_RECEIVE_PIN);
619  // clear interrupt bit
620  GIFR |= 1 << PCIF;
621  // enable interrupt on next change
622  GIMSK |= 1 << PCIE;
623 
624 # elif defined(USE_PCINT0)
625  PCICR |= _BV(PCIE0);
626  PCMSK0 = digitalPinToBitMask(IR_RECEIVE_PIN);
627 # elif defined(USE_PCINT1)
628  PCICR |= _BV(PCIE1);
629  PCMSK1 = digitalPinToBitMask(IR_RECEIVE_PIN);
630 # elif defined(USE_PCINT2)
631  PCICR |= _BV(PCIE2);
632  PCMSK2 = digitalPinToBitMask(IR_RECEIVE_PIN);
633 # else
634  return false;
635 # endif
636 #endif // defined(USE_ATTACH_INTERRUPT)
637  return true;
638 }
639 
641 #if defined(_IR_MEASURE_TIMING) && defined(_IR_TIMING_TEST_PIN)
642  pinModeFast(_IR_TIMING_TEST_PIN, OUTPUT);
643 #endif
644 
645 #if defined(USE_ATTACH_INTERRUPT) || defined(USE_ATTACH_INTERRUPT_DIRECT)
646 # if defined(USE_ATTACH_INTERRUPT)
647  detachInterrupt(digitalPinToInterrupt(IR_RECEIVE_PIN));
648 # else
649  detachInterrupt(IR_RECEIVE_PIN);
650 # endif
651 
652 #else
653 # if defined(USE_INT0)
654  // clear interrupt bit
655  EIFR |= 1 << INTF0;
656  // disable interrupt on next change
657  EIMSK &= ~(1 << INT0);
658 
659 # elif defined(USE_INT1)
660  // clear interrupt bit
661  EIFR |= 1 << INTF1;
662  // disable interrupt on next change
663  EIMSK &= ~(1 << INT1);
664 
665 # elif defined(USE_PCIE) // For ATtiny85 etc.
666  // clear interrupt bit
667  GIFR |= 1 << PCIF;
668  // disable interrupt on next change
669  GIMSK &= ~(1 << PCIE);
670 
671 # elif defined(USE_PCINT0)
672  PCICR &= ~(_BV(PCIE0));
673 # elif defined(USE_PCINT1)
674  PCICR &= ~(_BV(PCIE1));
675 # elif defined(USE_PCINT2)
676  PCICR &= ~(_BV(PCIE2));
677 
678 # endif
679 #endif // defined(USE_ATTACH_INTERRUPT)
680 }
681 
682 /*
683  * Specify the right INT0, INT1 or PCINT0 interrupt vector according to different pins and cores.
684  * The default value of TINY_RECEIVER_USE_ARDUINO_ATTACH_INTERRUPT is set in TinyIRReceiver.h
685  */
686 #if !(defined(USE_ATTACH_INTERRUPT) || defined(USE_ATTACH_INTERRUPT_DIRECT))
687 # if defined(USE_INT0)
688 ISR(INT0_vect)
689 
690 # elif defined(USE_INT1)
691 ISR(INT1_vect)
692 
693 # elif defined(USE_PCIE) // For ATtiny85 etc.
694 // on ATtinyX5 we do not have a INT1_vect but we can use the PCINT0_vect
695 ISR(PCINT0_vect)
696 
697 # elif defined(USE_PCINT0)
698 ISR(PCINT0_vect)
699 # elif defined(USE_PCINT1)
700 ISR(PCINT1_vect)
701 # elif defined(USE_PCINT2)
702 ISR(PCINT2_vect)
703 # else
704 void dummyFunctionToAvoidCompilerErrors()
705 # endif
706 {
708 }
709 #endif // !(defined(USE_ATTACH_INTERRUPT) || defined(USE_ATTACH_INTERRUPT_DIRECT))
710 
713 #if defined(LOCAL_DEBUG_ATTACH_INTERRUPT)
714 #undef LOCAL_DEBUG_ATTACH_INTERRUPT
715 #endif
716 #include "LocalDebugLevelEnd.h"
717 
718 #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
DEBUG_PRINT
#define DEBUG_PRINT(...)
Definition: LocalDebugLevelStart.h:79
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
TRACE_PRINT
#define TRACE_PRINT(...)
Definition: LocalDebugLevelStart.h:69
TinyIRReceiverControl
TinyIRReceiverStruct TinyIRReceiverControl
Definition: TinyIRReceiver.hpp:104
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:125
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:569
TinyIR.h
TinyIRReceiverData
volatile TinyIRReceiverCallbackDataStruct TinyIRReceiverData
Definition: TinyIRReceiver.hpp:105
STR
#define STR(x)
Definition: TinyIRReceiver.hpp:493
LongUnion::LowWord
uint16_t LowWord
Definition: LongUnion.h:80
IR_RECEIVE_PIN
#define IR_RECEIVE_PIN
Definition: TinyIRReceiver.hpp:124
isTinyReceiverIdle
bool isTinyReceiverIdle()
Definition: TinyIRReceiver.hpp:427
TINY_RECEIVER_ONE_SPACE
#define TINY_RECEIVER_ONE_SPACE
Definition: TinyIR.h:160
LocalDebugLevelStart.h
handleReceivedTinyIRData
void handleReceivedTinyIRData()
Declaration of the callback function provided by the user application.
Definition: IRCommandDispatcher.hpp:89
IRDATA_FLAGS_PARITY_FAILED
#define IRDATA_FLAGS_PARITY_FAILED
The current (autorepeat) frame violated parity check.
Definition: IRProtocol.h:127
disablePCIInterruptForTinyReceiver
void disablePCIInterruptForTinyReceiver()
Definition: TinyIRReceiver.hpp:640
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:434
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:165
TinyIRReceiverCallbackDataStruct::Command
uint16_t Command
Definition: TinyIR.h:245
sMicrosOfGap
uint32_t sMicrosOfGap
Definition: TinyIRReceiver.hpp:156
printTinyReceiverResultMinimal
void printTinyReceiverResultMinimal(Print *aSerial)
Definition: TinyIRReceiver.hpp:469
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
DEBUG_PRINTLN
#define DEBUG_PRINTLN(...)
Definition: LocalDebugLevelStart.h:80
IRDATA_FLAGS_EMPTY
#define IRDATA_FLAGS_EMPTY
Definition: IRProtocol.h:124
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:105
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:446
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:457
TINY_RECEIVER_MARK_TIMEOUT
#define TINY_RECEIVER_MARK_TIMEOUT
Definition: TinyIR.h:156
LocalDebugLevelEnd.h