Arduino-IRremote/src/IRReceive.hpp

1735 lines
60 KiB
C++

/*
* 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_<PROTOCOL> 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(", <numberOfRepeats>"));
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(", <millisofRepeatPeriod>, <numberOfRepeats>"));
}
#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.<fieldname> .");
#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