/* * IRReceive.hpp * This file is exclusively included by IRremote.h to enable easy configuration of library switches * * Contains all IRrecv class functions as well as other receiver related functions. * * This file is part of Arduino-IRremote https://github.com/Arduino-IRremote/Arduino-IRremote. * ************************************************************************************ * MIT License * * Copyright (c) 2009-2021 Ken Shirriff, Rafi Khan, Armin Joachimsmeyer * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is furnished * to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * ************************************************************************************ */ #ifndef _IR_RECEIVE_HPP #define _IR_RECEIVE_HPP /** \addtogroup Receiving Receiving IR data for multiple protocols * @{ */ /** * The receiver instance */ IRrecv IrReceiver; /* * The control structure instance */ struct irparams_struct irparams; // the irparams instance /** * Instantiate the IRrecv class. Multiple instantiation is not supported. * @param IRReceivePin Arduino pin to use. No sanity check is made. */ IRrecv::IRrecv() { decodedIRData.rawDataPtr = &irparams; // for decodePulseDistanceData() etc. setReceivePin(0); #if !defined(NO_LED_FEEDBACK_CODE) setLEDFeedback(0, DO_NOT_ENABLE_LED_FEEDBACK); #endif } IRrecv::IRrecv(uint_fast8_t aReceivePin) { decodedIRData.rawDataPtr = &irparams; // for decodePulseDistanceData() etc. setReceivePin(aReceivePin); #if !defined(NO_LED_FEEDBACK_CODE) setLEDFeedback(0, DO_NOT_ENABLE_LED_FEEDBACK); #endif } /** * Instantiate the IRrecv class. Multiple instantiation is not supported. * @param aReceivePin Arduino pin to use, where a demodulating IR receiver is connected. * @param aFeedbackLEDPin if 0, then take board specific FEEDBACK_LED_ON() and FEEDBACK_LED_OFF() functions */ IRrecv::IRrecv(uint_fast8_t aReceivePin, uint_fast8_t aFeedbackLEDPin) { decodedIRData.rawDataPtr = &irparams; // for decodePulseDistanceData() etc. setReceivePin(aReceivePin); #if !defined(NO_LED_FEEDBACK_CODE) setLEDFeedback(aFeedbackLEDPin, DO_NOT_ENABLE_LED_FEEDBACK); #else (void) aFeedbackLEDPin; #endif } /********************************************************************************************************************** * Stream like API **********************************************************************************************************************/ /** * Initializes the receive and feedback pin * @param aReceivePin The Arduino pin number, where a demodulating IR receiver is connected. * @param aEnableLEDFeedback if true / ENABLE_LED_FEEDBACK, then let the feedback led blink on receiving IR signal * @param aFeedbackLEDPin if 0 / USE_DEFAULT_FEEDBACK_LED_PIN, then take board specific FEEDBACK_LED_ON() and FEEDBACK_LED_OFF() functions */ void IRrecv::begin(uint_fast8_t aReceivePin, bool aEnableLEDFeedback, uint_fast8_t aFeedbackLEDPin) { setReceivePin(aReceivePin); #if !defined(NO_LED_FEEDBACK_CODE) bool tEnableLEDFeedback = DO_NOT_ENABLE_LED_FEEDBACK; if (aEnableLEDFeedback) { tEnableLEDFeedback = LED_FEEDBACK_ENABLED_FOR_RECEIVE; } setLEDFeedback(aFeedbackLEDPin, tEnableLEDFeedback); #else (void) aEnableLEDFeedback; (void) aFeedbackLEDPin; #endif // Set pin mode once pinModeFast(irparams.IRReceivePin, INPUT); #if defined(_IR_MEASURE_TIMING) && defined(_IR_TIMING_TEST_PIN) pinModeFast(_IR_TIMING_TEST_PIN, OUTPUT); #endif start(); } /** * Sets / changes the receiver pin number */ void IRrecv::setReceivePin(uint_fast8_t aReceivePinNumber) { irparams.IRReceivePin = aReceivePinNumber; #if defined(__AVR__) irparams.IRReceivePinMask = digitalPinToBitMask(aReceivePinNumber); irparams.IRReceivePinPortInputRegister = portInputRegister(digitalPinToPort(aReceivePinNumber)); #endif } /** * Configures the timer and the state machine for IR reception. */ void IRrecv::start() { // Setup for cyclic 50 us interrupt timerConfigForReceive(); // no interrupts enabled here! // Initialize state machine state resume(); // Timer interrupt is enabled after state machine reset TIMER_ENABLE_RECEIVE_INTR; } /** * Alias for start(). */ void IRrecv::enableIRIn() { start(); } /** * Configures the timer and the state machine for IR reception. * @param aMicrosecondsToAddToGapCounter To compensate for the amount of microseconds the timer was stopped / disabled. */ void IRrecv::start(uint32_t aMicrosecondsToAddToGapCounter) { start(); noInterrupts(); irparams.TickCounterForISR += aMicrosecondsToAddToGapCounter / MICROS_PER_TICK; interrupts(); } /** * Restarts receiver after send. Is a NOP if sending does not require a timer */ void IRrecv::restartAfterSend() { #if defined(SEND_PWM_BY_TIMER) && !defined(SEND_PWM_DOES_NOT_USE_RECEIVE_TIMER) start(); #endif } /** * Disables the timer for IR reception. */ void IRrecv::stop() { TIMER_DISABLE_RECEIVE_INTR; } /** * Alias for stop(). */ void IRrecv::disableIRIn() { stop(); } /** * Alias for stop(). */ void IRrecv::end() { stop(); } /** * Returns status of reception * @return true if no reception is on-going. */ bool IRrecv::isIdle() { return (irparams.StateForISR == IR_REC_STATE_IDLE || irparams.StateForISR == IR_REC_STATE_STOP) ? true : false; } /** * Restart the ISR state machine * Enable receiving of the next value */ void IRrecv::resume() { // check allows to call resume at arbitrary places or more than once if (irparams.StateForISR == IR_REC_STATE_STOP) { irparams.StateForISR = IR_REC_STATE_IDLE; } #if defined(SEND_PWM_BY_TIMER) // TIMER_ENABLE_RECEIVE_INTR; // normally it is stopped by send() #endif } /** * Is internally called by decode before calling decoders. * Must be used to setup data, if you call decoders manually. */ void IRrecv::initDecodedIRData() { if (irparams.OverflowFlag) { // Copy overflow flag to decodedIRData.flags and reset it irparams.OverflowFlag = false; irparams.rawlen = 0; // otherwise we have OverflowFlag again at next ISR call decodedIRData.flags = IRDATA_FLAGS_WAS_OVERFLOW; IR_DEBUG_PRINTLN(F("Overflow happened")); } else { decodedIRData.flags = IRDATA_FLAGS_EMPTY; // save last protocol, command and address for repeat handling (where the are copied back :-)) lastDecodedProtocol = decodedIRData.protocol; // repeat patterns can be equal between protocols (e.g. NEC and LG), so we must keep the original one lastDecodedCommand = decodedIRData.command; lastDecodedAddress = decodedIRData.address; } decodedIRData.protocol = UNKNOWN; decodedIRData.command = 0; decodedIRData.address = 0; decodedIRData.decodedRawData = 0; decodedIRData.numberOfBits = 0; } /** * Returns true if IR receiver data is available. */ bool IRrecv::available() { return (irparams.StateForISR == IR_REC_STATE_STOP); } /** * If IR receiver data is available, returns pointer to IrReceiver.decodedIRData, else NULL. */ IRData* IRrecv::read() { if (irparams.StateForISR != IR_REC_STATE_STOP) { return NULL; } if (decode()) { return &decodedIRData; } else { return NULL; } } /** * The main decode function, attempts to decode the recently receive IR signal. * The set of decoders used is determined by active definitions of the DECODE_ macros. * @return false if no IR receiver data available, true if data available. Results of decoding are stored in IrReceiver.decodedIRData. */ bool IRrecv::decode() { if (irparams.StateForISR != IR_REC_STATE_STOP) { return false; } initDecodedIRData(); // sets IRDATA_FLAGS_WAS_OVERFLOW if (decodedIRData.flags & IRDATA_FLAGS_WAS_OVERFLOW) { /* * Set OverflowFlag flag and return true here, to let the loop call resume or print raw data. */ decodedIRData.protocol = UNKNOWN; return true; } #if defined(DECODE_NEC) IR_TRACE_PRINTLN(F("Attempting NEC decode")); if (decodeNEC()) { return true; } #endif #if defined(DECODE_PANASONIC) || defined(DECODE_KASEIKYO) IR_TRACE_PRINTLN(F("Attempting Panasonic/Kaseikyo decode")); if (decodeKaseikyo()) { return true; } #endif #if defined(DECODE_DENON) IR_TRACE_PRINTLN(F("Attempting Denon/Sharp decode")); if (decodeDenon()) { return true; } #endif #if defined(DECODE_SONY) IR_TRACE_PRINTLN(F("Attempting Sony decode")); if (decodeSony()) { return true; } #endif #if defined(DECODE_RC5) IR_TRACE_PRINTLN(F("Attempting RC5 decode")); if (decodeRC5()) { return true; } #endif #if defined(DECODE_RC6) IR_TRACE_PRINTLN(F("Attempting RC6 decode")); if (decodeRC6()) { return true; } #endif #if defined(DECODE_LG) IR_TRACE_PRINTLN(F("Attempting LG decode")); if (decodeLG()) { return true; } #endif #if defined(DECODE_JVC) IR_TRACE_PRINTLN(F("Attempting JVC decode")); if (decodeJVC()) { return true; } #endif #if defined(DECODE_SAMSUNG) IR_TRACE_PRINTLN(F("Attempting Samsung decode")); if (decodeSamsung()) { return true; } #endif /* * Start of the exotic protocols */ #if defined(DECODE_WHYNTER) IR_TRACE_PRINTLN(F("Attempting Whynter decode")); if (decodeWhynter()) { return true; } #endif #if defined(DECODE_LEGO_PF) IR_TRACE_PRINTLN(F("Attempting Lego Power Functions")); if (decodeLegoPowerFunctions()) { return true; } #endif #if defined(DECODE_BOSEWAVE) IR_TRACE_PRINTLN(F("Attempting Bosewave decode")); if (decodeBoseWave()) { return true; } #endif #if defined(DECODE_MAGIQUEST) IR_TRACE_PRINTLN(F("Attempting MagiQuest decode")); if (decodeMagiQuest()) { return true; } #endif /* * Try the universal decoder for pulse distance protocols */ #if defined(DECODE_DISTANCE) IR_TRACE_PRINTLN(F("Attempting universal Distance decode")); if (decodeDistance()) { return true; } #endif /* * Last resort is the universal hash decode which always return true */ #if defined(DECODE_HASH) IR_TRACE_PRINTLN(F("Hash decode")); // decodeHash returns a hash on any input. // Thus, it needs to be last in the list. // If you add any decodes, add them before this. if (decodeHash()) { return true; } #endif /* * Return true here, to let the loop decide to call resume or to print raw data. */ return true; } /********************************************************************************************************************** * Common decode functions **********************************************************************************************************************/ /** * Decode pulse width protocols. Currently only used for sony protocol, which is LSB first. * The space (pause) has constant length, the length of the mark determines the bit value. * Each bit looks like: MARK_1 + SPACE -> 1 or : MARK_0 + SPACE -> 0 * * Input is IrReceiver.decodedIRData.rawDataPtr->rawbuf[] * Output is IrReceiver.decodedIRData.decodedRawData * * @param aStartOffset must point to a mark * @return true if decoding was successful */ bool IRrecv::decodePulseWidthData(uint_fast8_t aNumberOfBits, uint_fast8_t aStartOffset, unsigned int aOneMarkMicros, unsigned int aZeroMarkMicros, unsigned int aBitSpaceMicros, bool aMSBfirst) { unsigned int *tRawBufPointer = &decodedIRData.rawDataPtr->rawbuf[aStartOffset]; uint32_t tDecodedData = 0; if (aMSBfirst) { /* * MSB first is currently optimized out by the compiler, since it is never used. */ for (uint_fast8_t i = 0; i < aNumberOfBits; i++) { // Check for variable length mark indicating a 0 or 1 if (matchMark(*tRawBufPointer, aOneMarkMicros)) { tDecodedData = (tDecodedData << 1) | 1; IR_TRACE_PRINT('1'); } else if (matchMark(*tRawBufPointer, aZeroMarkMicros)) { tDecodedData = (tDecodedData << 1) | 0; IR_TRACE_PRINT('0'); } else { IR_DEBUG_PRINT(F("Mark=")); IR_DEBUG_PRINT(*tRawBufPointer * MICROS_PER_TICK); IR_DEBUG_PRINT(F(" is not ")); IR_DEBUG_PRINT(aOneMarkMicros); IR_DEBUG_PRINT(F(" or ")); IR_DEBUG_PRINT(aZeroMarkMicros); IR_DEBUG_PRINT(' '); return false; } tRawBufPointer++; // If we have no stop bit, assume that last space, which is not recorded, is correct, since we can not check it if (tRawBufPointer < &decodedIRData.rawDataPtr->rawbuf[decodedIRData.rawDataPtr->rawlen]) { // Check for constant length space if (!matchSpace(*tRawBufPointer, aBitSpaceMicros)) { IR_DEBUG_PRINT(F("Space=")); IR_DEBUG_PRINT(*tRawBufPointer * MICROS_PER_TICK); IR_DEBUG_PRINT(F(" is not ")); IR_DEBUG_PRINT(aBitSpaceMicros); IR_DEBUG_PRINT(' '); return false; } tRawBufPointer++; } } IR_TRACE_PRINTLN(F("")); } else { // LSB first for (uint32_t tMask = 1UL; aNumberOfBits > 0; tMask <<= 1, aNumberOfBits--) { // Check for variable length mark indicating a 0 or 1 if (matchMark(*tRawBufPointer, aOneMarkMicros)) { tDecodedData |= tMask; // set the bit IR_TRACE_PRINT('1'); } else if (matchMark(*tRawBufPointer, aZeroMarkMicros)) { // do not set the bit IR_TRACE_PRINT('0'); } else { IR_DEBUG_PRINT(F("Mark=")); IR_DEBUG_PRINT(*tRawBufPointer * MICROS_PER_TICK); IR_DEBUG_PRINT(F(" is not ")); IR_DEBUG_PRINT(aOneMarkMicros); IR_DEBUG_PRINT(F(" or ")); IR_DEBUG_PRINT(aZeroMarkMicros); IR_DEBUG_PRINT(' '); return false; } tRawBufPointer++; // If we have no stop bit, assume that last space, which is not recorded, is correct, since we can not check it if (tRawBufPointer < &decodedIRData.rawDataPtr->rawbuf[decodedIRData.rawDataPtr->rawlen]) { // Check for constant length space here if (!matchSpace(*tRawBufPointer, aBitSpaceMicros)) { IR_DEBUG_PRINT(F("Space=")); IR_DEBUG_PRINT(*tRawBufPointer * MICROS_PER_TICK); IR_DEBUG_PRINT(F(" is not ")); IR_DEBUG_PRINT(aBitSpaceMicros); IR_DEBUG_PRINT(' '); return false; } tRawBufPointer++; } } IR_TRACE_PRINTLN(F("")); } decodedIRData.decodedRawData = tDecodedData; return true; } /** * Decode pulse distance protocols. * The mark (pulse) has constant length, the length of the space determines the bit value. * Each bit looks like: MARK + SPACE_1 -> 1 * or : MARK + SPACE_0 -> 0 * * Input is IrReceiver.decodedIRData.rawDataPtr->rawbuf[] * Output is IrReceiver.decodedIRData.decodedRawData * * @param aStartOffset must point to a mark * @return true if decoding was successful */ bool IRrecv::decodePulseDistanceData(uint_fast8_t aNumberOfBits, uint_fast8_t aStartOffset, unsigned int aBitMarkMicros, unsigned int aOneSpaceMicros, unsigned int aZeroSpaceMicros, bool aMSBfirst) { unsigned int *tRawBufPointer = &decodedIRData.rawDataPtr->rawbuf[aStartOffset]; uint32_t tDecodedData = 0; if (aMSBfirst) { for (uint_fast8_t i = 0; i < aNumberOfBits; i++) { // Check for constant length mark if (!matchMark(*tRawBufPointer, aBitMarkMicros)) { IR_DEBUG_PRINT(F("Mark=")); IR_DEBUG_PRINT(*tRawBufPointer * MICROS_PER_TICK); IR_DEBUG_PRINT(F(" is not ")); IR_DEBUG_PRINT(aBitMarkMicros); IR_DEBUG_PRINT(' '); return false; } tRawBufPointer++; // Check for variable length space indicating a 0 or 1 if (matchSpace(*tRawBufPointer, aOneSpaceMicros)) { tDecodedData = (tDecodedData << 1) | 1; IR_TRACE_PRINT('1'); } else if (matchSpace(*tRawBufPointer, aZeroSpaceMicros)) { tDecodedData = (tDecodedData << 1) | 0; IR_TRACE_PRINT('0'); } else { IR_DEBUG_PRINT(F("Space=")); IR_DEBUG_PRINT(*tRawBufPointer * MICROS_PER_TICK); IR_DEBUG_PRINT(F(" is not ")); IR_DEBUG_PRINT(aOneSpaceMicros); IR_DEBUG_PRINT(F(" or ")); IR_DEBUG_PRINT(aZeroSpaceMicros); IR_DEBUG_PRINT(' '); return false; } tRawBufPointer++; } IR_TRACE_PRINTLN(F("")); } else { for (uint32_t tMask = 1UL; aNumberOfBits > 0; tMask <<= 1, aNumberOfBits--) { // Check for constant length mark if (!matchMark(*tRawBufPointer, aBitMarkMicros)) { IR_DEBUG_PRINT(F("Mark=")); IR_DEBUG_PRINT(*tRawBufPointer * MICROS_PER_TICK); IR_DEBUG_PRINT(F(" is not ")); IR_DEBUG_PRINT(aBitMarkMicros); IR_DEBUG_PRINT(' '); return false; } tRawBufPointer++; // Check for variable length space indicating a 0 or 1 if (matchSpace(*tRawBufPointer, aOneSpaceMicros)) { tDecodedData |= tMask; // set the bit IR_TRACE_PRINT('1'); } else if (matchSpace(*tRawBufPointer, aZeroSpaceMicros)) { // do not set the bit IR_TRACE_PRINT('0'); } else { IR_DEBUG_PRINT(F("Space=")); IR_DEBUG_PRINT(*tRawBufPointer * MICROS_PER_TICK); IR_DEBUG_PRINT(F(" is not ")); IR_DEBUG_PRINT(aOneSpaceMicros); IR_DEBUG_PRINT(F(" or ")); IR_DEBUG_PRINT(aZeroSpaceMicros); IR_DEBUG_PRINT(' '); return false; } tRawBufPointer++; } IR_TRACE_PRINTLN(F("")); } decodedIRData.decodedRawData = tDecodedData; return true; } /* * Static variables for the getBiphaselevel function */ uint_fast8_t sBiphaseDecodeRawbuffOffset; // Index into raw timing array unsigned int sCurrentTimingIntervals; // Number of aBiphaseTimeUnit intervals of the current rawbuf[sBiphaseDecodeRawbuffOffset] timing. uint_fast8_t sUsedTimingIntervals; // Number of already used intervals of sCurrentTimingIntervals. unsigned int sBiphaseTimeUnit; void IRrecv::initBiphaselevel(uint_fast8_t aRCDecodeRawbuffOffset, unsigned int aBiphaseTimeUnit) { sBiphaseDecodeRawbuffOffset = aRCDecodeRawbuffOffset; sBiphaseTimeUnit = aBiphaseTimeUnit; sUsedTimingIntervals = 0; } /** * Gets the level of one time interval (aBiphaseTimeUnit) 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 getBiphaselevel will return 1, 1, 0. * * _ _ _ _ _ _ _ _ _ _ _ _ _ * _____| |_| |_| |_| |_| |_| |_| |_| |_| |_| |_| |_| |_| | * ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ Significant clock edge * _ _ _ ___ _ ___ ___ _ - Mark * Data _____| |___| |_| |_| |_| |___| |___| |_| | - Data starts with a mark->space bit * 1 0 0 0 1 1 0 1 0 1 1 - Space * A mark to space at a significant clock edge results in a 1 * A space to mark at a significant clock edge results in a 0 (for RC6) * Returns current level [MARK or SPACE] or -1 for error (measured time interval is not a multiple of sBiphaseTimeUnit). */ uint_fast8_t IRrecv::getBiphaselevel() { uint_fast8_t tLevelOfCurrentInterval; // 0 (SPACE) or 1 (MARK) if (sBiphaseDecodeRawbuffOffset >= decodedIRData.rawDataPtr->rawlen) { return SPACE; // After end of recorded buffer, assume space. } tLevelOfCurrentInterval = (sBiphaseDecodeRawbuffOffset) & 1; // on odd rawbuf offsets we have mark timings /* * Setup data if sUsedTimingIntervals is 0 */ if (sUsedTimingIntervals == 0) { unsigned int tCurrentTimingWith = decodedIRData.rawDataPtr->rawbuf[sBiphaseDecodeRawbuffOffset]; unsigned int tMarkExcessCorrection = (tLevelOfCurrentInterval == MARK) ? MARK_EXCESS_MICROS : -MARK_EXCESS_MICROS; if (matchTicks(tCurrentTimingWith, (sBiphaseTimeUnit) + tMarkExcessCorrection)) { sCurrentTimingIntervals = 1; } else if (matchTicks(tCurrentTimingWith, (2 * sBiphaseTimeUnit) + tMarkExcessCorrection)) { sCurrentTimingIntervals = 2; } else if (matchTicks(tCurrentTimingWith, (3 * sBiphaseTimeUnit) + tMarkExcessCorrection)) { sCurrentTimingIntervals = 3; } else { return -1; } } // We use another interval from tCurrentTimingIntervals sUsedTimingIntervals++; // keep track of current timing offset if (sUsedTimingIntervals >= sCurrentTimingIntervals) { // we have used all intervals of current timing, switch to next timing value sUsedTimingIntervals = 0; sBiphaseDecodeRawbuffOffset++; } IR_TRACE_PRINTLN(tLevelOfCurrentInterval); return tLevelOfCurrentInterval; } #if defined(DECODE_HASH) /********************************************************************************************************************** * Internal Hash decode function **********************************************************************************************************************/ /** * Compare two (tick) values for Hash decoder * Use a tolerance of 20% to enable e.g. 500 and 600 (NEC timing) to be equal * @return 0 if newval is shorter, 1 if newval is equal, and 2 if newval is longer */ uint_fast8_t IRrecv::compare(unsigned int oldval, unsigned int newval) { if (newval * 10 < oldval * 8) { return 0; } if (oldval * 10 < newval * 8) { return 2; } return 1; } #define FNV_PRIME_32 16777619 ///< used for decodeHash() #define FNV_BASIS_32 2166136261 ///< used for decodeHash() /** * decodeHash - decode an arbitrary IR code. * Instead of decoding using a standard encoding scheme * (e.g. Sony, NEC, RC5), the code is hashed to a 32-bit value. * * The algorithm: look at the sequence of MARK signals, and see if each one * is shorter (0), the same length (1), or longer (2) than the previous. * Do the same with the SPACE signals. Hash the resulting sequence of 0's, * 1's, and 2's to a 32-bit value. This will give a unique value for each * different code (probably), for most code systems. * * 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. * This isn't a "real" decoding, just an arbitrary value. * * see: http://www.righto.com/2010/01/using-arbitrary-remotes-with-arduino.html */ bool IRrecv::decodeHash() { unsigned long hash = FNV_BASIS_32; // the result is the same no matter if we use a long or unsigned long variable // Require at least 6 samples to prevent triggering on noise if (decodedIRData.rawDataPtr->rawlen < 6) { return false; } #if RAW_BUFFER_LENGTH <= 254 // saves around 75 bytes program memory and speeds up ISR uint_fast8_t i; #else unsigned int i; #endif for (i = 1; (i + 2) < decodedIRData.rawDataPtr->rawlen; i++) { uint_fast8_t value = compare(decodedIRData.rawDataPtr->rawbuf[i], decodedIRData.rawDataPtr->rawbuf[i + 2]); // Add value into the hash hash = (hash * FNV_PRIME_32) ^ value; } decodedIRData.decodedRawData = hash; decodedIRData.numberOfBits = 32; decodedIRData.protocol = UNKNOWN; return true; } bool IRrecv::decodeHashOld(decode_results *aResults) { unsigned long hash = FNV_BASIS_32; // Require at least 6 samples to prevent triggering on noise if (aResults->rawlen < 6) { return false; } for (uint8_t i = 3; i < aResults->rawlen; i++) { uint_fast8_t value = compare(aResults->rawbuf[i - 2], aResults->rawbuf[i]); // Add value into the hash hash = (hash * FNV_PRIME_32) ^ value; } aResults->value = hash; aResults->bits = 32; aResults->decode_type = UNKNOWN; decodedIRData.protocol = UNKNOWN; return true; } #endif // DECODE_HASH /********************************************************************************************************************** * Match functions **********************************************************************************************************************/ /** * Match function without compensating for marks exceeded or spaces shortened by demodulator hardware * Currently not used */ bool matchTicks(unsigned int aMeasuredTicks, unsigned int aMatchValueMicros) { #if defined(TRACE) Serial.print(F("Testing: ")); Serial.print(TICKS_LOW(aMatchValueMicros), DEC); Serial.print(F(" <= ")); Serial.print(aMeasuredTicks, DEC); Serial.print(F(" <= ")); Serial.print(TICKS_HIGH(aMatchValueMicros), DEC); #endif bool passed = ((aMeasuredTicks >= TICKS_LOW(aMatchValueMicros)) && (aMeasuredTicks <= TICKS_HIGH(aMatchValueMicros))); #if defined(TRACE) if (passed) { Serial.println(F("?; passed")); } else { Serial.println(F("?; FAILED")); } #endif return passed; } bool MATCH(unsigned int measured_ticks, unsigned int desired_us) { return matchTicks(measured_ticks, desired_us); } /** * Compensate for marks exceeded by demodulator hardware */ bool matchMark(unsigned int aMeasuredTicks, unsigned int aMatchValueMicros) { #if defined(TRACE) Serial.print(F("Testing mark (actual vs desired): ")); Serial.print(aMeasuredTicks * MICROS_PER_TICK, DEC); Serial.print(F("us vs ")); Serial.print(aMatchValueMicros, DEC); Serial.print(F("us: ")); Serial.print(TICKS_LOW(aMatchValueMicros + MARK_EXCESS_MICROS) * MICROS_PER_TICK, DEC); Serial.print(F(" <= ")); Serial.print(aMeasuredTicks * MICROS_PER_TICK, DEC); Serial.print(F(" <= ")); Serial.print(TICKS_HIGH(aMatchValueMicros + MARK_EXCESS_MICROS) * MICROS_PER_TICK, DEC); #endif // compensate for marks exceeded by demodulator hardware bool passed = ((aMeasuredTicks >= TICKS_LOW(aMatchValueMicros + MARK_EXCESS_MICROS)) && (aMeasuredTicks <= TICKS_HIGH(aMatchValueMicros + MARK_EXCESS_MICROS))); #if defined(TRACE) if (passed) { Serial.println(F("?; passed")); } else { Serial.println(F("?; FAILED")); } #endif return passed; } bool MATCH_MARK(unsigned int measured_ticks, unsigned int desired_us) { return matchMark(measured_ticks, desired_us); } /** * Compensate for spaces shortened by demodulator hardware */ bool matchSpace(unsigned int aMeasuredTicks, unsigned int aMatchValueMicros) { #if defined(TRACE) Serial.print(F("Testing space (actual vs desired): ")); Serial.print(aMeasuredTicks * MICROS_PER_TICK, DEC); Serial.print(F("us vs ")); Serial.print(aMatchValueMicros, DEC); Serial.print(F("us: ")); Serial.print(TICKS_LOW(aMatchValueMicros - MARK_EXCESS_MICROS) * MICROS_PER_TICK, DEC); Serial.print(F(" <= ")); Serial.print(aMeasuredTicks * MICROS_PER_TICK, DEC); Serial.print(F(" <= ")); Serial.print(TICKS_HIGH(aMatchValueMicros - MARK_EXCESS_MICROS) * MICROS_PER_TICK, DEC); #endif // compensate for spaces shortened by demodulator hardware bool passed = ((aMeasuredTicks >= TICKS_LOW(aMatchValueMicros - MARK_EXCESS_MICROS)) && (aMeasuredTicks <= TICKS_HIGH(aMatchValueMicros - MARK_EXCESS_MICROS))); #if defined(TRACE) if (passed) { Serial.println(F("?; passed")); } else { Serial.println(F("?; FAILED")); } #endif return passed; } bool MATCH_SPACE(unsigned int measured_ticks, unsigned int desired_us) { return matchSpace(measured_ticks, desired_us); } /** * Getter function for MARK_EXCESS_MICROS */ int getMarkExcessMicros() { return MARK_EXCESS_MICROS; } /* * Check if protocol is not detected and detected space between two transmissions * is smaller than known value for protocols (Sony with around 24 ms) */ void CheckForRecordGapsMicros(Print *aSerial, IRData *aIRDataPtr) { /* * Check if protocol is not detected and detected space between two transmissions * is smaller than known value for protocols (Sony with around 24 ms) */ if (aIRDataPtr->protocol <= PULSE_DISTANCE && aIRDataPtr->rawDataPtr->rawbuf[0] < (RECORD_GAP_MICROS_WARNING_THRESHOLD / MICROS_PER_TICK)) { aSerial->println(); aSerial->print(F("Space of ")); aSerial->print(aIRDataPtr->rawDataPtr->rawbuf[0] * MICROS_PER_TICK); aSerial->print(F(" us between two detected transmission is smaller than the minimal gap of ")); aSerial->print(RECORD_GAP_MICROS_WARNING_THRESHOLD); aSerial->println(F(" us known for a protocol.")); aSerial->println(F("If you get unexpected results, try to increase the RECORD_GAP_MICROS in IRremote.h.")); aSerial->println(); } } /********************************************************************************************************************** * Print functions * Since a library should not allocate the "Serial" object, all functions require a pointer to a Print object. **********************************************************************************************************************/ void IRrecv::printActiveIRProtocols(Print *aSerial) { // call no class function with same name ::printActiveIRProtocols(aSerial); } void printActiveIRProtocols(Print *aSerial) { #if defined(DECODE_NEC) aSerial->print(F("NEC/NEC2/Onkyo/Apple, ")); #endif #if defined(DECODE_PANASONIC) || defined(DECODE_KASEIKYO) aSerial->print(F("Panasonic/Kaseikyo, ")); #endif #if defined(DECODE_DENON) aSerial->print(F("Denon/Sharp, ")); #endif #if defined(DECODE_SONY) aSerial->print(F("Sony, ")); #endif #if defined(DECODE_RC5) aSerial->print(F("RC5, ")); #endif #if defined(DECODE_RC6) aSerial->print(F("RC6, ")); #endif #if defined(DECODE_LG) aSerial->print(F("LG, ")); #endif #if defined(DECODE_JVC) aSerial->print(F("JVC, ")); #endif #if defined(DECODE_SAMSUNG) aSerial->print(F("Samsung, ")); #endif /* * Start of the exotic protocols */ #if defined(DECODE_WHYNTER) aSerial->print(F("Whynter, ")); #endif #if defined(DECODE_LEGO_PF) aSerial->print(F("Lego Power Functions, ")); #endif #if defined(DECODE_BOSEWAVE) aSerial->print(F("Bosewave , ")); #endif #if defined(DECODE_MAGIQUEST) aSerial->print(F("MagiQuest, ")); #endif #if defined(DECODE_DISTANCE) # if defined(SUPPORT_PULSE_WIDTH_DECODING) // The only known pulse width protocol is Sony aSerial->print(F("Universal Distance, ")); # else aSerial->print(F("Pulse Distance, ")); # endif #endif #if defined(DECODE_HASH) aSerial->print(F("Hash ")); #endif #if defined(NO_DECODER) // for sending raw only (void)aSerial; // to avoid compiler warnings #endif } /** * Function to print values and flags of IrReceiver.decodedIRData in one line. * Ends with println(). * * @param aSerial The Print object on which to write, for Arduino you can use &Serial. */ void IRrecv::printIRResultShort(Print *aSerial) { // call no class function with same name ::printIRResultShort(aSerial, &decodedIRData, true); } /** * Internal function to print decoded result and flags in one line. * Ends with println(). * * @param aSerial The Print object on which to write, for Arduino you can use &Serial. * @param aIRDataPtr Pointer to the data to be printed. * @param aPrintRepeatGap If true also print the gap before repeats. */ void printIRResultShort(Print *aSerial, IRData *aIRDataPtr, bool aPrintRepeatGap) { aSerial->print(F("Protocol=")); aSerial->print(getProtocolString(aIRDataPtr->protocol)); if (aIRDataPtr->protocol == UNKNOWN) { #if defined(DECODE_HASH) aSerial->print(F(" Hash=0x")); aSerial->print(aIRDataPtr->decodedRawData, HEX); #endif aSerial->print(' '); aSerial->print((aIRDataPtr->rawDataPtr->rawlen + 1) / 2, DEC); aSerial->println(F(" bits (incl. gap and start) received")); } else { #if defined(DECODE_DISTANCE) if(aIRDataPtr->protocol != PULSE_DISTANCE) { #endif /* * New decoders have address and command */ aSerial->print(F(" Address=0x")); aSerial->print(aIRDataPtr->address, HEX); aSerial->print(F(" Command=0x")); aSerial->print(aIRDataPtr->command, HEX); if (aIRDataPtr->flags & IRDATA_FLAGS_EXTRA_INFO) { aSerial->print(F(" Extra=0x")); aSerial->print(aIRDataPtr->extra, HEX); } if (aIRDataPtr->flags & IRDATA_FLAGS_PARITY_FAILED) { aSerial->print(F(" Parity fail")); } if (aIRDataPtr->flags & IRDATA_TOGGLE_BIT_MASK) { if (aIRDataPtr->protocol == NEC) { aSerial->print(F(" Special repeat")); } else { aSerial->print(F(" Toggle=1")); } } #if defined(DECODE_DISTANCE) } #endif if (aIRDataPtr->flags & (IRDATA_FLAGS_IS_AUTO_REPEAT | IRDATA_FLAGS_IS_REPEAT)) { aSerial->print(' '); if (aIRDataPtr->flags & IRDATA_FLAGS_IS_AUTO_REPEAT) { aSerial->print(F("Auto-")); } aSerial->print(F("Repeat")); if (aPrintRepeatGap) { aSerial->print(F(" gap=")); aSerial->print((uint32_t) aIRDataPtr->rawDataPtr->rawbuf[0] * MICROS_PER_TICK); aSerial->print(F("us")); } } /* * Print raw data */ if (!(aIRDataPtr->flags & IRDATA_FLAGS_IS_REPEAT) || aIRDataPtr->decodedRawData != 0) { aSerial->print(F(" Raw-Data=0x")); aSerial->print(aIRDataPtr->decodedRawData, HEX); /* * Print number of bits processed */ aSerial->print(' '); aSerial->print(aIRDataPtr->numberOfBits, DEC); aSerial->print(F(" bits")); if (aIRDataPtr->flags & IRDATA_FLAGS_IS_MSB_FIRST) { aSerial->println(F(" MSB first")); } else { aSerial->println(F(" LSB first")); } } else { aSerial->println(); } CheckForRecordGapsMicros(aSerial, aIRDataPtr); } } /** * Function to print values and flags of IrReceiver.decodedIRData in one line. * Ends with println(). * * @param aSerial The Print object on which to write, for Arduino you can use &Serial. */ void IRrecv::printIRSendUsage(Print *aSerial) { // call no class function with same name ::printIRSendUsage(aSerial, &decodedIRData); } void printIRSendUsage(Print *aSerial, IRData *aIRDataPtr) { if (aIRDataPtr->protocol != UNKNOWN && (aIRDataPtr->flags & (IRDATA_FLAGS_IS_AUTO_REPEAT | IRDATA_FLAGS_IS_REPEAT)) == 0x00) { #if defined(DECODE_DISTANCE) aSerial->print(F("Send with:")); if (aIRDataPtr->protocol == PULSE_DISTANCE) { aSerial->println(); aSerial->print(F(" uint32_t tRawData[]={0x")); uint_fast8_t tNumberOf32BitChunks = ((aIRDataPtr->numberOfBits - 1) / 32) + 1; for (uint_fast8_t i = 0; i < tNumberOf32BitChunks; ++i) { aSerial->print(aIRDataPtr->decodedRawDataArray[i], HEX); if (i != tNumberOf32BitChunks - 1) { aSerial->print(F(", 0x")); } } aSerial->println(F("};")); aSerial->print(F(" ")); } aSerial->print(F(" IrSender.send")); #else aSerial->print(F("Send with: IrSender.send")); #endif #if defined(DECODE_DISTANCE) if (aIRDataPtr->protocol != PULSE_DISTANCE) { #endif aSerial->print(getProtocolString(aIRDataPtr->protocol)); aSerial->print(F("(0x")); /* * New decoders have address and command */ aSerial->print(aIRDataPtr->address, HEX); aSerial->print(F(", 0x")); aSerial->print(aIRDataPtr->command, HEX); aSerial->print(F(", ")); if (aIRDataPtr->flags & IRDATA_FLAGS_EXTRA_INFO) { aSerial->print(F(", 0x")); aSerial->print(aIRDataPtr->extra, HEX); } #if defined(DECODE_DISTANCE) } else { aSerial->print("PulseDistanceWidthFromArray(38, "); aSerial->print((aIRDataPtr->extra >> 8) * MICROS_PER_TICK); // aHeaderMarkMicros aSerial->print(F(", ")); aSerial->print((aIRDataPtr->extra & 0xFF) * MICROS_PER_TICK); // aHeaderSpaceMicros aSerial->print(F(", ")); aSerial->print((aIRDataPtr->address >> 8) * MICROS_PER_TICK); // aOneMarkMicros aSerial->print(F(", ")); aSerial->print((aIRDataPtr->address & 0xFF) * MICROS_PER_TICK); // aOneSpaceMicros aSerial->print(F(", ")); aSerial->print((aIRDataPtr->command >> 8) * MICROS_PER_TICK); // aZeroMarkMicros aSerial->print(F(", ")); aSerial->print((aIRDataPtr->command & 0xFF) * MICROS_PER_TICK); // aZeroSpaceMicros aSerial->print(F(", &tRawData[0], ")); aSerial->print(aIRDataPtr->numberOfBits); // aNumberOfBits #if defined(DISTANCE_DO_MSB_DECODING) aSerial->print(F(", PROTOCOL_IS_MSB_FIRST")); #else aSerial->print(F(", PROTOCOL_IS_LSB_FIRST")); #endif aSerial->print(F(", , ")); } #endif aSerial->println(F(");")); } } /** * Function to print protocol number, address, command, raw data and repeat flag of IrReceiver.decodedIRData in one short line. * Does not print a Newline / does not end with println(). * * @param aSerial The Print object on which to write, for Arduino you can use &Serial. */ void IRrecv::printIRResultMinimal(Print *aSerial) { aSerial->print(F("P=")); aSerial->print(decodedIRData.protocol); if (decodedIRData.protocol == UNKNOWN) { #if defined(DECODE_HASH) aSerial->print(F(" #=0x")); aSerial->print(decodedIRData.decodedRawData, HEX); #endif aSerial->print(' '); aSerial->print((decodedIRData.rawDataPtr->rawlen + 1) / 2, DEC); aSerial->println(F(" bits received")); } else { /* * New decoders have address and command */ aSerial->print(F(" A=0x")); aSerial->print(decodedIRData.address, HEX); aSerial->print(F(" C=0x")); aSerial->print(decodedIRData.command, HEX); aSerial->print(F(" Raw=0x")); aSerial->print(decodedIRData.decodedRawData, HEX); if (decodedIRData.flags & (IRDATA_FLAGS_IS_AUTO_REPEAT | IRDATA_FLAGS_IS_REPEAT)) { aSerial->print(F(" R")); } } } /** * Dump out the timings in IrReceiver.decodedIRData.rawDataPtr->rawbuf[] array 8 values per line. * * @param aSerial The Print object on which to write, for Arduino you can use &Serial. * @param aOutputMicrosecondsInsteadOfTicks Output the (rawbuf_values * MICROS_PER_TICK) for better readability. */ void IRrecv::printIRResultRawFormatted(Print *aSerial, bool aOutputMicrosecondsInsteadOfTicks) { // Print Raw data aSerial->print(F("rawData[")); aSerial->print(decodedIRData.rawDataPtr->rawlen, DEC); aSerial->println(F("]: ")); /* * Print initial gap */ aSerial->print(F(" -")); if (aOutputMicrosecondsInsteadOfTicks) { aSerial->println((uint32_t) decodedIRData.rawDataPtr->rawbuf[0] * MICROS_PER_TICK, DEC); } else { aSerial->println(decodedIRData.rawDataPtr->rawbuf[0], DEC); } #if RAW_BUFFER_LENGTH <= 254 // saves around 75 bytes program memory and speeds up ISR uint_fast8_t i; #else unsigned int i; #endif // Newline is printed every 8. value, if tCounterForNewline % 8 == 0 uint_fast8_t tCounterForNewline = 6; // first newline is after the 2 values of the start bit // check if we have a protocol with no or 8 start bits #if defined(DECODE_DENON) || defined(DECODE_MAGIQUEST) if ( #if defined(DECODE_DENON) decodedIRData.protocol == DENON || decodedIRData.protocol == SHARP || #endif #if defined(DECODE_MAGIQUEST) decodedIRData.protocol == MAGIQUEST || #endif false) { tCounterForNewline = 0; // no or 8 start bits } #endif uint32_t tDuration; uint16_t tSumOfDurationTicks = 0; for (i = 1; i < decodedIRData.rawDataPtr->rawlen; i++) { auto tCurrentTicks = decodedIRData.rawDataPtr->rawbuf[i]; if (aOutputMicrosecondsInsteadOfTicks) { tDuration = tCurrentTicks * MICROS_PER_TICK; } else { tDuration = tCurrentTicks; } tSumOfDurationTicks += tCurrentTicks; // compute length of protocol frame if (!(i & 1)) { // even aSerial->print('-'); } else { // odd aSerial->print(F(" +")); } // padding only for big values if (aOutputMicrosecondsInsteadOfTicks && tDuration < 1000) { aSerial->print(' '); } if (aOutputMicrosecondsInsteadOfTicks && tDuration < 100) { aSerial->print(' '); } if (tDuration < 10) { aSerial->print(' '); } aSerial->print(tDuration, DEC); if ((i & 1) && (i + 1) < decodedIRData.rawDataPtr->rawlen) { aSerial->print(','); //',' not required for last one } tCounterForNewline++; if ((tCounterForNewline % 8) == 0) { aSerial->println(); } } aSerial->println(); aSerial->print("Sum: "); if (aOutputMicrosecondsInsteadOfTicks) { aSerial->println((uint32_t) tSumOfDurationTicks * MICROS_PER_TICK, DEC); } else { aSerial->println(tSumOfDurationTicks, DEC); } } /** * Dump out the IrReceiver.decodedIRData.rawDataPtr->rawbuf[] to be used as C definition for sendRaw(). * * Compensate received values by MARK_EXCESS_MICROS, like it is done for decoding! * Print ticks in 8 bit format to save space. * Maximum is 255*50 microseconds = 12750 microseconds = 12.75 ms, which hardly ever occurs inside an IR sequence. * Recording of IRremote anyway stops at a gap of RECORD_GAP_MICROS (5 ms). * * @param aSerial The Print object on which to write, for Arduino you can use &Serial. * @param aOutputMicrosecondsInsteadOfTicks Output the (rawbuf_values * MICROS_PER_TICK) for better readability. */ void IRrecv::compensateAndPrintIRResultAsCArray(Print *aSerial, bool aOutputMicrosecondsInsteadOfTicks) { // Start declaration if (aOutputMicrosecondsInsteadOfTicks) { aSerial->print(F("uint16_t rawData[")); // variable type, array name } else { aSerial->print(F("uint8_t rawTicks[")); // variable type, array name } aSerial->print(decodedIRData.rawDataPtr->rawlen - 1, DEC); // array size aSerial->print(F("] = {")); // Start declaration // Dump data #if RAW_BUFFER_LENGTH <= 254 // saves around 75 bytes program memory and speeds up ISR uint_fast8_t i; #else unsigned int i; #endif for (i = 1; i < decodedIRData.rawDataPtr->rawlen; i++) { uint32_t tDuration = decodedIRData.rawDataPtr->rawbuf[i] * MICROS_PER_TICK; if (i & 1) { // Mark tDuration -= MARK_EXCESS_MICROS; } else { tDuration += MARK_EXCESS_MICROS; } if (aOutputMicrosecondsInsteadOfTicks) { aSerial->print(tDuration); } else { unsigned int tTicks = (tDuration + (MICROS_PER_TICK / 2)) / MICROS_PER_TICK; tTicks = (tTicks > UINT8_MAX) ? UINT8_MAX : tTicks; // uint8_t rawTicks above are 8 bit aSerial->print(tTicks); } if (i + 1 < decodedIRData.rawDataPtr->rawlen) aSerial->print(','); // ',' not required on last one if (!(i & 1)) aSerial->print(' '); } // End declaration aSerial->print(F("};")); // // Comment aSerial->print(F(" // ")); printIRResultShort(aSerial); // Newline aSerial->println(""); } /** * Store the decodedIRData to be used for sendRaw(). * * Compensate received values by MARK_EXCESS_MICROS, like it is done for decoding and store it in an array provided. * * Maximum for uint8_t is 255*50 microseconds = 12750 microseconds = 12.75 ms, which hardly ever occurs inside an IR sequence. * Recording of IRremote anyway stops at a gap of RECORD_GAP_MICROS (5 ms). * @param aArrayPtr Address of an array provided by the caller. */ void IRrecv::compensateAndStoreIRResultInArray(uint8_t *aArrayPtr) { // Store data, skip leading space# #if RAW_BUFFER_LENGTH <= 254 // saves around 75 bytes program memory and speeds up ISR uint_fast8_t i; #else unsigned int i; #endif for (i = 1; i < decodedIRData.rawDataPtr->rawlen; i++) { uint32_t tDuration = decodedIRData.rawDataPtr->rawbuf[i] * MICROS_PER_TICK; if (i & 1) { // Mark tDuration -= MARK_EXCESS_MICROS; } else { tDuration += MARK_EXCESS_MICROS; } unsigned int tTicks = (tDuration + (MICROS_PER_TICK / 2)) / MICROS_PER_TICK; *aArrayPtr = (tTicks > UINT8_MAX) ? UINT8_MAX : tTicks; // we store it in an 8 bit array aArrayPtr++; } } /** * Print results as C variables to be used for sendXXX() * @param aSerial The Print object on which to write, for Arduino you can use &Serial. */ void IRrecv::printIRResultAsCVariables(Print *aSerial) { // Now dump "known" codes if (decodedIRData.protocol != UNKNOWN) { /* * New decoders have address and command */ aSerial->print(F("uint16_t")); aSerial->print(F(" address = 0x")); aSerial->print(decodedIRData.address, HEX); aSerial->println(';'); aSerial->print(F("uint16_t")); aSerial->print(F(" command = 0x")); aSerial->print(decodedIRData.command, HEX); aSerial->println(';'); // All protocols have data aSerial->print(F("uint32_t data = 0x")); aSerial->print(decodedIRData.decodedRawData, HEX); aSerial->println(';'); aSerial->println(); } } const __FlashStringHelper* IRrecv::getProtocolString() { // call no class function with same name return ::getProtocolString(decodedIRData.protocol); } const __FlashStringHelper* getProtocolString(decode_type_t aProtocol) { switch (aProtocol) { default: case UNKNOWN: return (F("UNKNOWN")); break; #if defined(SUPPORT_PULSE_WIDTH_DECODING) // The only known pulse width protocol is Sony case PULSE_WIDTH: return (F("PulseWidth")); break; #endif case PULSE_DISTANCE: return (F("PulseDistance")); break; case DENON: return (F("Denon")); break; case SHARP: return (F("Sharp")); break; case JVC: return (F("JVC")); break; case LG: return (F("LG")); break; case LG2: return (F("LG2")); break; case NEC: return (F("NEC")); break; case PANASONIC: return (F("Panasonic")); break; case KASEIKYO: return (F("Kaseikyo")); break; case KASEIKYO_DENON: return (F("Kaseikyo_Denon")); break; case KASEIKYO_SHARP: return (F("Kaseikyo_Sharp")); break; case KASEIKYO_JVC: return (F("Kaseikyo_JVC")); break; case KASEIKYO_MITSUBISHI: return (F("Kaseikyo_Mitsubishi")); break; case RC5: return (F("RC5")); break; case RC6: return (F("RC6")); break; case SAMSUNG: return (F("Samsung")); break; case SAMSUNG_LG: return (F("SamsungLG")); break; case SONY: return (F("Sony")); break; case NEC2: return (F("NEC2")); break; case ONKYO: return (F("Onkyo")); break; case APPLE: return (F("Apple")); break; #if !defined(EXCLUDE_EXOTIC_PROTOCOLS) case BOSEWAVE: return (F("BoseWave")); break; case LEGO_PF: return (F("Lego")); break; case MAGIQUEST: return (F("MagiQuest")); break; case WHYNTER: return (F("Whynter")); break; #endif } } /********************************************************************************************************************** * Interrupt Service Routine - Called every 50 us * * Duration in ticks of 50 us of alternating SPACE, MARK are recorded in irparams.rawbuf array. * 'rawlen' counts the number of entries recorded so far. * First entry is the SPACE between transmissions. * * As soon as one SPACE entry gets longer than RECORD_GAP_TICKS, state switches to STOP (frame received). Timing of SPACE continues. * A call of resume() switches from STOP to IDLE. * As soon as first MARK arrives in IDLE, gap width is recorded and new logging starts. * * With digitalRead and Feedback LED * 15 pushs, 1 in, 1 eor before start of code = 2 us @16MHz + * 7.2 us computation time (6us idle time) + * pop + reti = 2.25 us @16MHz => 10.3 to 11.5 us @16MHz * With portInputRegister and mask and Feedback LED code commented * 9 pushs, 1 in, 1 eor before start of code = 1.25 us @16MHz + * 2.25 us computation time + * pop + reti = 1.5 us @16MHz => 5 us @16MHz * => Minimal CPU frequency is 4 MHz * **********************************************************************************************************************/ //#define _IR_MEASURE_TIMING //#define _IR_TIMING_TEST_PIN 7 // do not forget to execute: "pinModeFast(_IR_TIMING_TEST_PIN, OUTPUT);" if activated by line above #if defined(TIMER_INTR_NAME) ISR (TIMER_INTR_NAME) // for ISR definitions #else ISR () // for functions definitions which are called by separate (board specific) ISR #endif { #if defined(_IR_MEASURE_TIMING) && defined(_IR_TIMING_TEST_PIN) digitalWriteFast(_IR_TIMING_TEST_PIN, HIGH); // 2 clock cycles #endif // 7 - 8.5 us for ISR body (without pushes and pops) for ATmega328 @16MHz TIMER_RESET_INTR_PENDING;// reset TickCounterForISR interrupt flag if required (currently only for Teensy and ATmega4809) // Read if IR Receiver -> SPACE [xmt LED off] or a MARK [xmt LED on] #if defined(__AVR__) uint8_t tIRInputLevel = *irparams.IRReceivePinPortInputRegister & irparams.IRReceivePinMask; #else uint_fast8_t tIRInputLevel = (uint_fast8_t) digitalReadFast(irparams.IRReceivePin); #endif /* * Increase TickCounter and clip it at maximum 0xFFFF / 3.2 seconds at 50 us ticks */ if (irparams.TickCounterForISR < UINT16_MAX) { irparams.TickCounterForISR++; // One more 50uS tick } /* * Due to a ESP32 compiler bug https://github.com/espressif/esp-idf/issues/1552 no switch statements are possible for ESP32 * So we change the code to if / else if */ // switch (irparams.StateForISR) { //...................................................................... if (irparams.StateForISR == IR_REC_STATE_IDLE) { // In the middle of a gap or just resumed (and maybe in the middle of a transmission if (tIRInputLevel == INPUT_MARK) { // check if we did not start in the middle of a transmission by checking the minimum length of leading space if (irparams.TickCounterForISR > RECORD_GAP_TICKS) { // Gap just ended; Record gap duration + start recording transmission // Initialize all state machine variables #if defined(_IR_MEASURE_TIMING) && defined(_IR_TIMING_TEST_PIN) // digitalWriteFast(_IR_TIMING_TEST_PIN, HIGH); // 2 clock cycles #endif irparams.OverflowFlag = false; irparams.rawbuf[0] = irparams.TickCounterForISR; irparams.rawlen = 1; irparams.StateForISR = IR_REC_STATE_MARK; } // otherwise stay in idle state irparams.TickCounterForISR = 0;// reset counter in both cases } } else if (irparams.StateForISR == IR_REC_STATE_MARK) { // Timing mark if (tIRInputLevel != INPUT_MARK) { // Mark ended; Record time #if defined(_IR_MEASURE_TIMING) && defined(_IR_TIMING_TEST_PIN) // digitalWriteFast(_IR_TIMING_TEST_PIN, HIGH); // 2 clock cycles #endif irparams.rawbuf[irparams.rawlen++] = irparams.TickCounterForISR; irparams.StateForISR = IR_REC_STATE_SPACE; irparams.TickCounterForISR = 0; } } else if (irparams.StateForISR == IR_REC_STATE_SPACE) { // Timing space if (tIRInputLevel == INPUT_MARK) { // Space just ended; Record time if (irparams.rawlen >= RAW_BUFFER_LENGTH) { // Flag up a read OverflowFlag; Stop the state machine irparams.OverflowFlag = true; irparams.StateForISR = IR_REC_STATE_STOP; } else { #if defined(_IR_MEASURE_TIMING) && defined(_IR_TIMING_TEST_PIN) // digitalWriteFast(_IR_TIMING_TEST_PIN, HIGH); // 2 clock cycles #endif irparams.rawbuf[irparams.rawlen++] = irparams.TickCounterForISR; irparams.StateForISR = IR_REC_STATE_MARK; } irparams.TickCounterForISR = 0; } else if (irparams.TickCounterForISR > RECORD_GAP_TICKS) { /* * Current code is ready for processing! * We received a long space, which indicates gap between codes. * Switch to IR_REC_STATE_STOP * Don't reset TickCounterForISR; keep counting width of next leading space */ irparams.StateForISR = IR_REC_STATE_STOP; } } else if (irparams.StateForISR == IR_REC_STATE_STOP) { /* * Complete command received * stay here until resume() is called, which switches state to IR_REC_STATE_IDLE */ #if defined(_IR_MEASURE_TIMING) && defined(_IR_TIMING_TEST_PIN) // digitalWriteFast(_IR_TIMING_TEST_PIN, HIGH); // 2 clock cycles #endif if (tIRInputLevel == INPUT_MARK) { // Reset gap TickCounterForISR, to prepare for detection if we are in the middle of a transmission after call of resume() irparams.TickCounterForISR = 0; } } #if !defined(NO_LED_FEEDBACK_CODE) if (FeedbackLEDControl.LedFeedbackEnabled == LED_FEEDBACK_ENABLED_FOR_RECEIVE) { setFeedbackLED(tIRInputLevel == INPUT_MARK); } #endif #ifdef _IR_MEASURE_TIMING digitalWriteFast(_IR_TIMING_TEST_PIN, LOW); // 2 clock cycles #endif } /********************************************************************************************************************** * The DEPRECATED decode function with parameter aResults ONLY for backwards compatibility! * This function calls the old MSB first decoders and fills only the 3 variables: * aResults->value * aResults->bits * aResults->decode_type **********************************************************************************************************************/ bool IRrecv::decode(decode_results *aResults) { static bool sDeprecationMessageSent = false; if (irparams.StateForISR != IR_REC_STATE_STOP) { return false; } if (!sDeprecationMessageSent) { #if !(defined(__AVR_ATtiny25__) || defined(__AVR_ATtiny45__) || defined(__AVR_ATtiny85__) || defined(__AVR_ATtiny87__) || defined(__AVR_ATtiny167__)) Serial.println( "The function decode(&results)) is deprecated and may not work as expected! Just use decode() without a parameter and IrReceiver.decodedIRData. ."); #endif sDeprecationMessageSent = true; } // copy for usage by legacy programs aResults->rawbuf = irparams.rawbuf; aResults->rawlen = irparams.rawlen; if (irparams.OverflowFlag) { // Copy overflow flag to decodedIRData.flags irparams.OverflowFlag = false; irparams.rawlen = 0; // otherwise we have OverflowFlag again at next ISR call IR_DEBUG_PRINTLN(F("Overflow happened")); } aResults->overflow = irparams.OverflowFlag; aResults->value = 0; decodedIRData.flags = IRDATA_FLAGS_IS_MSB_FIRST; // for print #if defined(DECODE_NEC) IR_DEBUG_PRINTLN(F("Attempting old NEC decode")); if (decodeNECMSB(aResults)) { return true ; } #endif #if defined(DECODE_SONY) IR_DEBUG_PRINTLN(F("Attempting old Sony decode")); if (decodeSonyMSB(aResults)) { return true ; } #endif //#if defined(DECODE_MITSUBISHI) // IR_DEBUG_PRINTLN(F("Attempting Mitsubishi decode")); // if (decodeMitsubishi(results)) return true ; //#endif #if defined(DECODE_RC5) IR_DEBUG_PRINTLN(F("Attempting RC5 decode")); if (decodeRC5()) { aResults->bits = decodedIRData.numberOfBits; aResults->value = decodedIRData.decodedRawData; aResults->decode_type = RC5; return true ; } #endif #if defined(DECODE_RC6) IR_DEBUG_PRINTLN(F("Attempting RC6 decode")); if (decodeRC6()) { aResults->bits = decodedIRData.numberOfBits; aResults->value = decodedIRData.decodedRawData; aResults->decode_type = RC6; return true ; } #endif #if defined( DECODE_PANASONIC) IR_DEBUG_PRINTLN(F("Attempting old Panasonic decode")); if (decodePanasonicMSB(aResults)) { return true ; } #endif #if defined(DECODE_LG) IR_DEBUG_PRINTLN(F("Attempting old LG decode")); if (decodeLGMSB(aResults)) { return true ;} #endif #if defined(DECODE_JVC) IR_DEBUG_PRINTLN(F("Attempting old JVC decode")); if (decodeJVCMSB(aResults)) { return true ; } #endif #if defined(DECODE_SAMSUNG) IR_DEBUG_PRINTLN(F("Attempting old SAMSUNG decode")); if (decodeSAMSUNG(aResults)) { return true ; } #endif //#if defined(DECODE_WHYNTER) // IR_DEBUG_PRINTLN(F("Attempting Whynter decode")); // if (decodeWhynter(results)) return true ; //#endif #if defined(DECODE_DENON) IR_DEBUG_PRINTLN(F("Attempting old Denon decode")); if (decodeDenonOld(aResults)) { return true ; } #endif //#if defined(DECODE_LEGO_PF) // IR_DEBUG_PRINTLN(F("Attempting Lego Power Functions")); // if (decodeLegoPowerFunctions(results)) return true ; //#endif // decodeHash returns a hash on any input. // Thus, it needs to be last in the list. // If you add any decodes, add them before this. if (decodeHashOld(aResults)) { return true; } // Throw away and start over resume(); return false; } /** @}*/ #endif // _IR_RECEIVE_HPP