- добавлена поддержка платы ELBEAR ACE-NANO;
- добавлена поддержка плат ELSOMIK OEM и SE;
- добавлена возможность работы в режиме отладки для всех плат, входящих в состав пакета. Доступно для версии ArduinoIDE 2 и выше;
- добавлена поддержка библиотеки FreeRTOS;
- добавлена поддержка библиотеки IRremote;
- добавлена поддержка библиотеки OneWire;
- добавлена поддержка аппаратного I2C0 для плат START-MIK32 и ELSOMIK. Для работы с ним доступен экземпляр класса Wire1;
- добавлена поддержка аппаратного SPI0 для всех плат, входящих в пакет. Для работы с ним доступен экземпляр класса SPI1;
- увеличено быстродействие функций digitalWrite, digitalRead;
- исправлены известные ошибки.
Co-authored-by: KlassenTS <klassen@elron.tech>
Co-committed-by: KlassenTS <klassen@elron.tech>
This commit was merged in pull request #19.
This commit is contained in:
klassents authored and klassents committed 2025-04-28 07:06:08 +03:00
1 parent de1f0d7fda
commit 1c8e06634c
211 files changed
+97525 -591

No files matched your search

+159
View File
@@ -0,0 +1,159 @@
/**
* @file IRFeedbackLED.hpp
*
* @brief All Feedback LED specific functions are contained in this file.
*
* This file is part of Arduino-IRremote https://github.com/Arduino-IRremote/Arduino-IRremote.
*
*************************************************************************************
* MIT License
*
* Copyright (c) 2021-2022 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_FEEDBACK_LED_HPP
#define _IR_FEEDBACK_LED_HPP
/** \addtogroup FeedbackLEDFunctions Feedback LED functions
* @{
*/
/**
* Contains pin number and enable status of the feedback LED
*/
struct FeedbackLEDControlStruct {
uint8_t FeedbackLEDPin; ///< if 0, then take board specific FEEDBACK_LED_ON() and FEEDBACK_LED_OFF() functions
uint8_t LedFeedbackEnabled; ///< LED_FEEDBACK_ENABLED_FOR_RECEIVE or LED_FEEDBACK_ENABLED_FOR_SEND -> enable blinking of pin on IR processing
};
struct FeedbackLEDControlStruct FeedbackLEDControl; ///< The feedback LED control instance
/**
* Enable blinking of feedback LED (LED_BUILTIN is taken as default) on IR sending and receiving
* Cannot disable it here!!! Use disableLEDFeedbackForReceive() or disableLEDFeedbackForSend()
* @param aFeedbackLEDPin If aFeedbackLEDPin == 0, then take board specific FEEDBACK_LED_ON() and FEEDBACK_LED_ON() and FEEDBACK_LED_OFF() functions
* If FeedbackLEDPin == 0 and no LED_BUILTIN defined, disable LED feedback
* @param aEnableLEDFeedback If LED_FEEDBACK_ENABLED_FOR_RECEIVE or LED_FEEDBACK_ENABLED_FOR_SEND -> enable blinking of Feedback LED
*/
void setLEDFeedback(uint8_t aFeedbackLEDPin, uint8_t aEnableLEDFeedback) {
FeedbackLEDControl.FeedbackLEDPin = aFeedbackLEDPin; // default is 0 -> use LED_BUILTIN if available, else disable feedback
if (aEnableLEDFeedback != DO_NOT_ENABLE_LED_FEEDBACK) {
FeedbackLEDControl.LedFeedbackEnabled |= aEnableLEDFeedback;
if (aFeedbackLEDPin != USE_DEFAULT_FEEDBACK_LED_PIN) {
pinModeFast(aFeedbackLEDPin, OUTPUT);
#if defined(LED_BUILTIN)
} else {
pinModeFast(LED_BUILTIN, OUTPUT);
#else
FeedbackLEDControl.LedFeedbackEnabled = LED_FEEDBACK_DISABLED_COMPLETELY; // we have no LED_BUILTIN available
#endif
}
}
}
/*
* Direct replacement for blink13()
*/
void setLEDFeedback(bool aEnableLEDFeedback) {
bool tEnableLEDFeedback = LED_FEEDBACK_DISABLED_COMPLETELY;
if (aEnableLEDFeedback) {
tEnableLEDFeedback = LED_FEEDBACK_ENABLED_FOR_SEND | LED_FEEDBACK_ENABLED_FOR_RECEIVE;
}
setLEDFeedback(FeedbackLEDControl.FeedbackLEDPin, tEnableLEDFeedback);
}
void enableLEDFeedback() {
FeedbackLEDControl.LedFeedbackEnabled |= LED_FEEDBACK_ENABLED_FOR_RECEIVE;
}
void disableLEDFeedback() {
FeedbackLEDControl.LedFeedbackEnabled &= ~(LED_FEEDBACK_ENABLED_FOR_RECEIVE);
}
void enableLEDFeedbackForSend() {
FeedbackLEDControl.LedFeedbackEnabled |= LED_FEEDBACK_ENABLED_FOR_SEND;
}
void disableLEDFeedbackForSend() {
FeedbackLEDControl.LedFeedbackEnabled &= ~(LED_FEEDBACK_ENABLED_FOR_SEND);
}
/**
* Flash LED while receiving or sending IR data. Does not check if enabled, this must be done by the caller.
* Handles the 0 value of FeedbackLEDPin and the macro FEEDBACK_LED_IS_ACTIVE_LOW.
*/
#if defined(ESP32) || defined(ESP8266)
IRAM_ATTR
#endif
void setFeedbackLED(bool aSwitchLedOn) {
if (aSwitchLedOn) {
if (FeedbackLEDControl.FeedbackLEDPin != USE_DEFAULT_FEEDBACK_LED_PIN) {
#if defined(FEEDBACK_LED_IS_ACTIVE_LOW)
digitalWriteFast(FeedbackLEDControl.FeedbackLEDPin, LOW); // Turn user defined pin LED on
#else
digitalWriteFast(FeedbackLEDControl.FeedbackLEDPin, HIGH); // Turn user defined pin LED on
#endif
#if defined(LED_BUILTIN) // use fast macros here
} else {
# if defined(FEEDBACK_LED_IS_ACTIVE_LOW)
digitalWriteFast(LED_BUILTIN, LOW); // For AVR, this generates a single cbi command
# else
digitalWriteFast(LED_BUILTIN, HIGH); // For AVR, this generates a single sbi command
# endif
#endif
}
} else {
if (FeedbackLEDControl.FeedbackLEDPin != USE_DEFAULT_FEEDBACK_LED_PIN) {
#if defined(FEEDBACK_LED_IS_ACTIVE_LOW)
digitalWriteFast(FeedbackLEDControl.FeedbackLEDPin, HIGH); // Turn user defined pin LED off
#else
digitalWriteFast(FeedbackLEDControl.FeedbackLEDPin, LOW); // Turn user defined pin LED off
#endif
#if defined(LED_BUILTIN)
} else {
# if defined(FEEDBACK_LED_IS_ACTIVE_LOW)
digitalWriteFast(LED_BUILTIN, HIGH); // For AVR, this generates a single sbi command
# else
digitalWriteFast(LED_BUILTIN, LOW); // For AVR, this generates a single cbi command
# endif
#endif
}
}
}
/**
* Old deprecated function name for setLEDFeedback() or enableLEDFeedback() / disableLEDFeedback()
*/
void IRrecv::blink13(uint8_t aEnableLEDFeedback) {
setLEDFeedback(FeedbackLEDControl.FeedbackLEDPin, aEnableLEDFeedback);
}
/**
* Old deprecated function name for setLEDFeedback()
*/
void setBlinkPin(uint8_t aBlinkPin) {
setLEDFeedback(aBlinkPin, FeedbackLEDControl.LedFeedbackEnabled);
}
/** @}*/
#endif // _IR_FEEDBACK_LED_HPP
+182
View File
@@ -0,0 +1,182 @@
/**
* @file IRProtocol.h
* @brief Common declarations for receiving and sending.
*
* This file is part of Arduino-IRremote https://github.com/Arduino-IRremote/Arduino-IRremote.
*
*
************************************************************************************
* MIT License
*
* Copyright (c) 2020-2024 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_PROTOCOL_H
#define _IR_PROTOCOL_H
/**
* An enum consisting of all supported formats.
* You do NOT need to remove entries from this list when disabling protocols!
* !!!Must be the same order as ProtocolNames in IRReceive.hpp!!!
*/
typedef enum {
UNKNOWN = 0,
PULSE_WIDTH,
PULSE_DISTANCE,
APPLE,
DENON,
JVC,
LG,
LG2,
NEC,
NEC2, /* 10 NEC with full frame as repeat */
ONKYO,
PANASONIC,
KASEIKYO,
KASEIKYO_DENON,
KASEIKYO_SHARP,
KASEIKYO_JVC,
KASEIKYO_MITSUBISHI,
RC5,
RC6,
RC6A, /*31 bit + 3 fixed 0b110 mode bits*/
SAMSUNG, /* 20*/
SAMSUNGLG,
SAMSUNG48,
SHARP,
SONY,
/* Now the exotic protocols */
BANG_OLUFSEN,
BOSEWAVE,
LEGO_PF,
MAGIQUEST,
WHYNTER, /* 30 */
FAST
} decode_type_t;
extern const char *const ProtocolNames[]; // The array of name strings for the decode_type_t enum
#define SIRCS_12_PROTOCOL 12
#define SIRCS_15_PROTOCOL 15
#define SIRCS_20_PROTOCOL 20
struct DistanceWidthTimingInfoStruct {
uint16_t HeaderMarkMicros;
uint16_t HeaderSpaceMicros;
uint16_t OneMarkMicros;
uint16_t OneSpaceMicros;
uint16_t ZeroMarkMicros;
uint16_t ZeroSpaceMicros;
};
/*
* Definitions for member IRData.flags
*/
#define IRDATA_FLAGS_EMPTY 0x00
#define IRDATA_FLAGS_IS_REPEAT 0x01 ///< The gap between the preceding frame is as smaller than the maximum gap expected for a repeat. !!!We do not check for changed command or address, because it is almost not possible to press 2 different buttons on the remote within around 100 ms!!!
#define IRDATA_FLAGS_IS_AUTO_REPEAT 0x02 ///< The current repeat frame is a repeat, that is always sent after a regular frame and cannot be avoided. Only specified for protocols DENON, and LEGO.
#define IRDATA_FLAGS_PARITY_FAILED 0x04 ///< The current (autorepeat) frame violated parity check.
#define IRDATA_FLAGS_TOGGLE_BIT 0x08 ///< Is set if RC5 or RC6 toggle bit is set.
#define IRDATA_TOGGLE_BIT_MASK 0x08 ///< deprecated -is set if RC5 or RC6 toggle bit is set.
#define IRDATA_FLAGS_EXTRA_INFO 0x10 ///< There is extra info not contained in address and data (e.g. Kaseikyo unknown vendor ID, or in decodedRawDataArray).
#define IRDATA_FLAGS_IS_PROTOCOL_WITH_DIFFERENT_REPEAT 0x20 ///< Here we have a repeat of type NEC2 or SamsungLG
#define IRDATA_FLAGS_WAS_OVERFLOW 0x40 ///< irparams.rawlen is set to 0 in this case to avoid endless OverflowFlag.
#define IRDATA_FLAGS_IS_MSB_FIRST 0x80 ///< Value is mainly determined by the (known) protocol.
#define IRDATA_FLAGS_IS_LSB_FIRST 0x00
#define RAW_DATA_ARRAY_SIZE ((((RAW_BUFFER_LENGTH - 2) - 1) / (2 * BITS_IN_RAW_DATA_TYPE)) + 1) // The -2 is for initial gap + stop bit mark, 128 mark + spaces for 64 bit.
/**
* Data structure for the user application, available as decodedIRData.
* Filled by decoders and read by print functions or user application.
*/
struct IRData {
decode_type_t protocol; ///< UNKNOWN, NEC, SONY, RC5, PULSE_DISTANCE, ...
uint16_t address; ///< Decoded address, Distance protocol (tMarkTicksLong (if tMarkTicksLong == 0, then tMarkTicksShort) << 8) | tSpaceTicksLong
uint16_t command; ///< Decoded command, Distance protocol (tMarkTicksShort << 8) | tSpaceTicksShort
uint16_t extra; ///< Contains upper 16 bit of Magiquest WandID, Kaseikyo unknown vendor ID and Distance protocol (HeaderMarkTicks << 8) | HeaderSpaceTicks.
IRRawDataType decodedRawData; ///< Up to 32/64 bit decoded raw data, to be used for send functions.
#if defined(DECODE_DISTANCE_WIDTH)
// This replaces the address, command, extra and decodedRawData in case of protocol == PULSE_DISTANCE or -rather seldom- protocol == PULSE_WIDTH.
DistanceWidthTimingInfoStruct DistanceWidthTimingInfo; // 12 bytes
IRRawDataType decodedRawDataArray[RAW_DATA_ARRAY_SIZE]; ///< 32/64 bit decoded raw data, to be used for send function.
#endif
uint16_t numberOfBits; ///< Number of bits received for data (address + command + parity) - to determine protocol length if different length are possible.
uint8_t flags; ///< IRDATA_FLAGS_IS_REPEAT, IRDATA_FLAGS_WAS_OVERFLOW etc. See IRDATA_FLAGS_* definitions above
/*
* These 2 variables allow to call resume() directly after decode.
* After resume(), decodedIRData.rawDataPtr->initialGapTicks and decodedIRData.rawDataPtr->rawlen are
* the first variables, which are overwritten by the next received frame.
* since 4.3.0.
*/
IRRawlenType rawlen; ///< counter of entries in rawbuf of last received frame.
uint16_t initialGapTicks; ///< contains the initial gap (pre 4.4: the value in rawbuf[0]) of the last received frame.
irparams_struct *rawDataPtr; ///< Pointer of the raw timing data to be decoded. Mainly the OverflowFlag and the data buffer filled by receiving ISR.
};
extern uint8_t sLastSendToggleValue; // Currently used by RC5 + RC6
struct PulseDistanceWidthProtocolConstants {
decode_type_t ProtocolIndex;
uint_fast8_t FrequencyKHz;
DistanceWidthTimingInfoStruct DistanceWidthTimingInfo;
uint8_t Flags;
unsigned int RepeatPeriodMillis;
void (*SpecialSendRepeatFunction)(); // using non member functions here saves up to 250 bytes for send demo
// void (IRsend::*SpecialSendRepeatFunction)();
};
/*
* Definitions for member PulseDistanceWidthProtocolConstants.Flags
*/
#define SUPPRESS_STOP_BIT 0x20 // Stop bit is otherwise sent for all pulse distance protocols, i.e. aOneSpaceMicros != aZeroSpaceMicros.
#define PROTOCOL_IS_MSB_FIRST IRDATA_FLAGS_IS_MSB_FIRST
#define PROTOCOL_IS_LSB_FIRST IRDATA_FLAGS_IS_LSB_FIRST
/*
* Carrier frequencies for various protocols
*/
#if !defined(BEO_KHZ) // guard used for unit test, which sends and receive Bang&Olufsen with 38 kHz.
#define BEO_KHZ 455
#endif
#define SONY_KHZ 40
#define BOSEWAVE_KHZ 38
#define DENON_KHZ 38
#define JVC_KHZ 38
#define LG_KHZ 38
#define NEC_KHZ 38
#define SAMSUNG_KHZ 38
#define KASEIKYO_KHZ 37
#define RC5_RC6_KHZ 36
#if defined(__AVR__)
const __FlashStringHelper* getProtocolString(decode_type_t aProtocol);
#else
const char* getProtocolString(decode_type_t aProtocol);
#endif
void printIRResultShort(Print *aSerial, IRData *aIRDataPtr, bool aPrintGap); // A static function to be able to print send or copied received data.
/*
* Convenience functions to convert MSB to LSB values
*/
uint8_t bitreverseOneByte(uint8_t aValue);
uint32_t bitreverse32Bit(uint32_t aInput);
#endif // _IR_PROTOCOL_H
+290
View File
@@ -0,0 +1,290 @@
/*
* IRReceive.hpp
* This file is exclusively included by IRremote.h to enable easy configuration of library switches
*
* Contains all protocol functions used by receiver and sender.
*
* This file is part of Arduino-IRremote https://github.com/Arduino-IRremote/Arduino-IRremote.
*
************************************************************************************
* MIT License
*
* Copyright (c) 2009-2023 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_PROTOCOL_HPP
#define _IR_PROTOCOL_HPP
#if defined(DEBUG) && !defined(LOCAL_DEBUG)
#define LOCAL_DEBUG
#else
//#define LOCAL_DEBUG // This enables debug output only for this file
#endif
/** \addtogroup Receiving Receiving IR data for multiple protocols
* @{
*/
const char string_Unknown[] PROGMEM = "UNKNOWN";
const char string_PulseWidth[] PROGMEM = "PulseWidth";
const char string_PulseDistance[] PROGMEM = "PulseDistance";
const char string_Apple[] PROGMEM = "Apple";
const char string_Denon[] PROGMEM = "Denon";
const char string_JVC[] PROGMEM = "JVC";
const char string_LG[] PROGMEM = "LG";
const char string_LG2[] PROGMEM = "LG2";
const char string_NEC[] PROGMEM = "NEC";
const char string_NEC2[] PROGMEM = "NEC2";
const char string_Onkyo[] PROGMEM = "Onkyo";
const char string_Panasonic[] PROGMEM = "Panasonic";
const char string_Kaseikyo[] PROGMEM = "Kaseikyo";
const char string_Kaseikyo_Denon[] PROGMEM = "Kaseikyo_Denon";
const char string_Kaseikyo_Sharp[] PROGMEM = "Kaseikyo_Sharp";
const char string_Kaseikyo_JVC[] PROGMEM = "Kaseikyo_JVC";
const char string_Kaseikyo_Mitsubishi[] PROGMEM = "Kaseikyo_Mitsubishi";
const char string_RC5[] PROGMEM = "RC5";
const char string_RC6[] PROGMEM = "RC6";
const char string_RC6A[] PROGMEM = "RC6A";
const char string_Samsung[] PROGMEM = "Samsung";
const char string_SamsungLG[] PROGMEM = "SamsungLG";
const char string_Samsung48[] PROGMEM = "Samsung48";
const char string_Sharp[] PROGMEM = "Sharp";
const char string_Sony[] PROGMEM = "Sony";
const char string_BangOlufsen[] PROGMEM = "Bang&Olufsen";
const char string_BoseWave[] PROGMEM = "BoseWave";
const char string_Lego[] PROGMEM = "Lego";
const char string_MagiQuest[] PROGMEM = "MagiQuest";
const char string_Whynter[] PROGMEM = "Whynter";
const char string_FAST[] PROGMEM = "FAST";
/*
* !!Must be the same order as in decode_type_t in IRProtocol.h!!!
*/
const char *const ProtocolNames[]
PROGMEM = { string_Unknown, string_PulseWidth, string_PulseDistance, string_Apple, string_Denon, string_JVC, string_LG, string_LG2,
string_NEC, string_NEC2, string_Onkyo, string_Panasonic, string_Kaseikyo, string_Kaseikyo_Denon, string_Kaseikyo_Sharp,
string_Kaseikyo_JVC, string_Kaseikyo_Mitsubishi, string_RC5, string_RC6, string_RC6A, string_Samsung, string_SamsungLG, string_Samsung48,
string_Sharp, string_Sony
#if !defined(EXCLUDE_EXOTIC_PROTOCOLS)
, string_BangOlufsen, string_BoseWave, string_Lego, string_MagiQuest, string_Whynter, string_FAST
#endif
};
#if defined(__AVR__)
const __FlashStringHelper* getProtocolString(decode_type_t aProtocol) {
const char *tProtocolStringPtr = (char*) pgm_read_word(&ProtocolNames[aProtocol]);
return ((__FlashStringHelper*) (tProtocolStringPtr));
}
#else
const char* getProtocolString(decode_type_t aProtocol) {
return ProtocolNames[aProtocol];
}
#endif
#if (__INT_WIDTH__ >= 32)
# if __has_include(<type_traits>)
/*
* This code to handle the missing print(unsigned long long...) function of seeduino core was contributed by sklott
* https://stackoverflow.com/questions/74622227/avoid-calling-of-function-size-t-printprintunsigned-long-long-n-int-base-if
*/
#include <type_traits>
// If you have C++17 you can just use std::void_t, or use this for all versions
#if __cpp_lib_void_t >= 201411L
template<typename T>
using void_t = std::void_t<T>;
#else
template<typename ... Ts> struct make_void {
typedef void type;
};
template<typename ... Ts> using void_t = typename make_void<Ts...>::type;
#endif
// Detecting if we have print(unsigned long long value, int base) / print(0ull, 0) overload
template<typename T, typename = void>
struct has_ull_print: std::false_type {
};
template<typename T>
struct has_ull_print<T, void_t<decltype(std::declval<T>().print(0ull, 0))>> : std::true_type {
};
// Must be namespace, to avoid public and static declarations for class
namespace PrintULL {
template<typename PrintImplType, typename std::enable_if<!has_ull_print<PrintImplType>::value, bool>::type = true>
size_t print(PrintImplType *p, unsigned long long value, int base) {
size_t tLength = p->print(static_cast<uint32_t>(value >> 32), base);
tLength += p->print(static_cast<uint32_t>(value), base);
return tLength;
}
template<typename PrintImplType, typename std::enable_if<has_ull_print<PrintImplType>::value, bool>::type = true>
size_t print(PrintImplType *p, unsigned long long value, int base) {
return p->print(value, base);
}
}
;
# else
namespace PrintULL {
size_t print(Print *aSerial, unsigned long long n, int base) {
return aSerial->print(n, base);
}
};
# endif
#endif
/**
* Function to print decoded result and flags in one line.
* A static function to be able to print data to send or copied received data.
* 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) {
if (aIRDataPtr->flags & IRDATA_FLAGS_WAS_OVERFLOW) {
aSerial->println(F("Overflow"));
return;
}
aSerial->print(F("Protocol="));
aSerial->print(getProtocolString(aIRDataPtr->protocol));
if (aIRDataPtr->protocol == UNKNOWN) {
#if defined(DECODE_HASH)
aSerial->print(F(" Hash=0x"));
#if (__INT_WIDTH__ < 32)
aSerial->print(aIRDataPtr->decodedRawData, HEX);
#else
PrintULL::print(aSerial,aIRDataPtr->decodedRawData, HEX);
#endif
#endif
#if !defined(DISABLE_CODE_FOR_RECEIVER)
aSerial->print(' ');
aSerial->print((aIRDataPtr->rawlen + 1) / 2, DEC);
aSerial->println(F(" bits (incl. gap and start) received"));
#endif
} else {
#if defined(DECODE_DISTANCE_WIDTH)
if (aIRDataPtr->protocol != PULSE_DISTANCE && aIRDataPtr->protocol != PULSE_WIDTH) {
#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_FLAGS_TOGGLE_BIT) {
aSerial->print(F(" Toggle=1"));
}
#if defined(DECODE_DISTANCE_WIDTH)
}
#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 !defined(DISABLE_CODE_FOR_RECEIVER)
if (aPrintRepeatGap) {
aSerial->print(F(" gap="));
aSerial->print((uint32_t) aIRDataPtr->initialGapTicks * MICROS_PER_TICK);
aSerial->print(F("us"));
}
#else
(void)aPrintRepeatGap;
#endif
}
/*
* Print raw data
*/
if (!(aIRDataPtr->flags & IRDATA_FLAGS_IS_REPEAT) || aIRDataPtr->decodedRawData != 0) {
aSerial->print(F(" Raw-Data=0x"));
#if (__INT_WIDTH__ < 32)
aSerial->print(aIRDataPtr->decodedRawData, HEX);
#else
PrintULL::print(aSerial, aIRDataPtr->decodedRawData, HEX);
#endif
/*
* 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();
}
}
}
/**********************************************************************************************************************
* Function to bit reverse OLD MSB values of e.g. NEC.
**********************************************************************************************************************/
uint8_t bitreverseOneByte(uint8_t aValue) {
// uint8_t tReversedValue;
// return __builtin_avr_insert_bits(0x01234567, aValue, tReversedValue);
// 76543210
aValue = (aValue >> 4) | (aValue << 4); // Swap in groups of 4
// 32107654
aValue = ((aValue & 0xcc) >> 2) | ((aValue & 0x33) << 2); // Swap in groups of 2
// 10325476
aValue = ((aValue & 0xaa) >> 1) | ((aValue & 0x55) << 1); // Swap bit pairs
// 01234567
return aValue;
}
uint32_t bitreverse32Bit(uint32_t aInput) {
// __builtin_avr_insert_bits();
LongUnion tValue;
tValue.UByte.HighByte = bitreverseOneByte(aInput);
tValue.UByte.MidHighByte = bitreverseOneByte(aInput >> 8);
tValue.UByte.MidLowByte = bitreverseOneByte(aInput >> 16);
tValue.UByte.LowByte = bitreverseOneByte(aInput >> 24);
return tValue.ULong;
}
/** @}*/
#if defined(LOCAL_DEBUG)
#undef LOCAL_DEBUG
#endif
#endif // _IR_PROTOCOL_HPP
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
+51
View File
@@ -0,0 +1,51 @@
/**
* @file IRversion.hpp
*
* This file is part of Arduino-IRremote https://github.com/Arduino-IRremote/Arduino-IRremote.
*
************************************************************************************
* MIT License
*
* Copyright (c) 2015-2024 Ken Shirriff http://www.righto.com, 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.
*
************************************************************************************
*
* For Ken Shiriffs original blog entry, see http://www.righto.com/2009/08/multi-protocol-infrared-remote-library.html
* Initially influenced by:
* http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1210243556
* and http://zovirl.com/2008/11/12/building-a-universal-remote-with-an-arduino/
*/
#ifndef _IR_VERSION_HPP
#define _IR_VERSION_HPP
#define VERSION_IRREMOTE "4.4.1"
#define VERSION_IRREMOTE_MAJOR 4
#define VERSION_IRREMOTE_MINOR 4
#define VERSION_IRREMOTE_PATCH 1
/*
* Macro to convert 3 version parts into an integer
* To be used in preprocessor comparisons, such as #if VERSION_IRREMOTE_HEX >= VERSION_HEX_VALUE(3, 7, 0)
*/
#define VERSION_HEX_VALUE(major, minor, patch) ((major << 16) | (minor << 8) | (patch))
#define VERSION_IRREMOTE_HEX VERSION_HEX_VALUE(VERSION_IRREMOTE_MAJOR, VERSION_IRREMOTE_MINOR, VERSION_IRREMOTE_PATCH)
#endif // _IR_VERSION_HPP
+58
View File
@@ -0,0 +1,58 @@
/**
* @file IRremote.h
*
* @brief Stub for backward compatibility
*/
#ifndef IRremote_h
#define IRremote_h
#include "IRremote.hpp"
#warning Thank you for using the IRremote library!
#warning It seems, that you are using a old version 2.0 code / example.
#warning This version is no longer supported!
#warning Please use one of the new code examples from the library available at "File > Examples > Examples from Custom Libraries / IRremote".
#warning Or downgrade your library to version 2.6.0.
#warning Start with the SimpleReceiver or SimpleSender example.
#warning The examples are documented here: https://github.com/Arduino-IRremote/Arduino-IRremote#examples-for-this-library
#warning A guide how to convert your 2.0 program is here: https://github.com/Arduino-IRremote/Arduino-IRremote#converting-your-2x-program-to-the-4x-version
/**********************************************************************************************************************
* The OLD and DEPRECATED decode function with parameter aResults, kept for backward compatibility to old 2.0 tutorials
* This function calls the old MSB first decoders and fills only the 3 variables:
* aResults->value
* aResults->bits
* aResults->decode_type
* It prints a message on the first call.
**********************************************************************************************************************/
bool IRrecv::decode(decode_results *aResults) {
static bool sMessageWasSent = false;
if (!sMessageWasSent) {
Serial.println(F("**************************************************************************************************"));
Serial.println(F("Thank you for using the IRremote library!"));
Serial.println(F("It seems, that you are using a old version 2.0 code / example."));
Serial.println(F("This version is no longer supported!"));
Serial.println(F("Please use one of the new code examples from the library,"));
Serial.println(F(" available at \"File > Examples > Examples from Custom Libraries / IRremote\"."));
Serial.println(F("Or downgrade your library to version 2.6.0."));
Serial.println();
Serial.println(F("Start with the SimpleReceiver or SimpleSender example."));
Serial.println();
Serial.println(F("The examples are documented here:"));
Serial.println(F(" https://github.com/Arduino-IRremote/Arduino-IRremote#examples-for-this-library"));
Serial.println(F("A guide how to convert your 2.0 program is here:"));
Serial.println(F(" https://github.com/Arduino-IRremote/Arduino-IRremote#converting-your-2x-program-to-the-4x-version"));
Serial.println();
Serial.println(F("Thanks"));
Serial.println(F("**************************************************************************************************"));
Serial.println();
Serial.println();
sMessageWasSent = true;
}
return decode_old(aResults);
}
#endif // IRremote_h
#pragma once
+321
View File
@@ -0,0 +1,321 @@
/**
* @file IRremote.hpp
*
* @brief Public API to the library.
*
* @code
* !!! All the macro values defined here can be overwritten with values, !!!
* !!! the user defines in its source code BEFORE the #include <IRremote.hpp> !!!
* @endcode
*
* This file is part of Arduino-IRremote https://github.com/Arduino-IRremote/Arduino-IRremote.
*
************************************************************************************
* MIT License
*
* Copyright (c) 2015-2023 Ken Shirriff http://www.righto.com, 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.
*
************************************************************************************
*
* For Ken Shiriffs original blog entry, see http://www.righto.com/2009/08/multi-protocol-infrared-remote-library.html
* Initially influenced by:
* http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1210243556
* and http://zovirl.com/2008/11/12/building-a-universal-remote-with-an-arduino/
*/
/*
* This library can be configured at compile time by the following options / macros:
* For more details see: https://github.com/Arduino-IRremote/Arduino-IRremote#compile-options--macros-for-this-library
*
* - RAW_BUFFER_LENGTH Buffer size of raw input buffer. Must be even! 100 is sufficient for *regular* protocols of up to 48 bits.
* - IR_SEND_PIN If specified (as constant), reduces program size and improves send timing for AVR.
* - SEND_PWM_BY_TIMER Disable carrier PWM generation in software and use (restricted) hardware PWM.
* - USE_NO_SEND_PWM Use no carrier PWM, just simulate an **active low** receiver signal. Overrides SEND_PWM_BY_TIMER definition.
* - USE_OPEN_DRAIN_OUTPUT_FOR_SEND_PIN Use or simulate open drain output mode at send pin. Attention, active state of open drain is LOW, so connect the send LED between positive supply and send pin!
* - EXCLUDE_EXOTIC_PROTOCOLS If activated, BANG_OLUFSEN, BOSEWAVE, WHYNTER, FAST and LEGO_PF are excluded in decode() and in sending with IrSender.write().
* - EXCLUDE_UNIVERSAL_PROTOCOLS If activated, the universal decoder for pulse distance protocols and decodeHash (special decoder for all protocols) are excluded in decode().
* - DECODE_* Selection of individual protocols to be decoded. See below.
* - MARK_EXCESS_MICROS Value is subtracted from all marks and added to all spaces before decoding, to compensate for the signal forming of different IR receiver modules.
* - RECORD_GAP_MICROS Minimum gap between IR transmissions, to detect the end of a protocol.
* - FEEDBACK_LED_IS_ACTIVE_LOW Required on some boards (like my BluePill and my ESP8266 board), where the feedback LED is active low.
* - NO_LED_FEEDBACK_CODE This completely disables the LED feedback code for send and receive.
* - IR_INPUT_IS_ACTIVE_HIGH Enable it if you use a RF receiver, which has an active HIGH output signal.
* - IR_SEND_DUTY_CYCLE_PERCENT Duty cycle of IR send signal.
* - MICROS_PER_TICK Resolution of the raw input buffer data. Corresponds to 2 pulses of each 26.3 us at 38 kHz.
* - IR_USE_AVR_TIMER* Selection of timer to be used for generating IR receiving sample interval.
*/
#ifndef _IR_REMOTE_HPP
#define _IR_REMOTE_HPP
#include "IRVersion.h"
// activate it for all cores that does not use the -flto flag, if you get false error messages regarding begin() during compilation.
//#define SUPPRESS_ERROR_MESSAGE_FOR_BEGIN
/*
* If activated, BANG_OLUFSEN, BOSEWAVE, MAGIQUEST, WHYNTER, FAST and LEGO_PF are excluded in decoding and in sending with IrSender.write
*/
//#define EXCLUDE_EXOTIC_PROTOCOLS
/****************************************************
* PROTOCOLS
****************************************************/
/*
* Supported IR protocols
* Each protocol you include costs memory and, during decode, costs time
* Copy the lines with the protocols you need in your program before the #include <IRremote.hpp> line
* See also SimpleReceiver example
*/
#if !defined(NO_DECODER) // for sending raw only
# if (!(defined(DECODE_DENON) || defined(DECODE_JVC) || defined(DECODE_KASEIKYO) \
|| defined(DECODE_PANASONIC) || defined(DECODE_LG) || defined(DECODE_NEC) || defined(DECODE_ONKYO) || defined(DECODE_SAMSUNG) \
|| defined(DECODE_SONY) || defined(DECODE_RC5) || defined(DECODE_RC6) \
|| defined(DECODE_DISTANCE_WIDTH) || defined(DECODE_HASH) || defined(DECODE_BOSEWAVE) \
|| defined(DECODE_LEGO_PF) || defined(DECODE_MAGIQUEST) || defined(DECODE_FAST) || defined(DECODE_WHYNTER)))
/*
* If no protocol is explicitly enabled, we enable all protocols
*/
#define DECODE_DENON // Includes Sharp
#define DECODE_JVC
#define DECODE_KASEIKYO
#define DECODE_PANASONIC // alias for DECODE_KASEIKYO
#define DECODE_LG
#define DECODE_NEC // Includes Apple and Onkyo
#define DECODE_SAMSUNG
#define DECODE_SONY
#define DECODE_RC5
#define DECODE_RC6
# if !defined(EXCLUDE_EXOTIC_PROTOCOLS) // saves around 2000 bytes program memory
#define DECODE_BOSEWAVE
#define DECODE_LEGO_PF
#define DECODE_MAGIQUEST
#define DECODE_WHYNTER
#define DECODE_FAST
# endif
# if !defined(EXCLUDE_UNIVERSAL_PROTOCOLS)
#define DECODE_DISTANCE_WIDTH // universal decoder for pulse distance width protocols - requires up to 750 bytes additional program memory
#define DECODE_HASH // special decoder for all protocols - requires up to 250 bytes additional program memory
# endif
# endif
#endif // !defined(NO_DECODER)
//#define DECODE_BEO // Bang & Olufsen protocol always must be enabled explicitly. It prevents decoding of SONY!
#if defined(DECODE_NEC) && !(~(~DECODE_NEC + 0) == 0 && ~(~DECODE_NEC + 1) == 1)
#warning "The macros DECODE_XXX no longer require a value. Decoding is now switched by defining / non defining the macro."
#endif
//#define DEBUG // Activate this for lots of lovely debug output from the IRremote core.
/****************************************************
* RECEIVING
****************************************************/
/**
* MARK_EXCESS_MICROS is subtracted from all marks and added to all spaces before decoding,
* to compensate for the signal forming of different IR receiver modules
* For Vishay TSOP*, marks tend to be too long and spaces tend to be too short.
* If you set MARK_EXCESS_MICROS to approx. 50us then the TSOP4838 works best.
* At 100us it also worked, but not as well.
* Set MARK_EXCESS to 100us and the VS1838 doesn't work at all.
*
* The right value is critical for IR codes using short pulses like Denon / Sharp / Lego
*
* Observed values:
* Delta of each signal type is around 50 up to 100 and at low signals up to 200. TSOP is better, especially at low IR signal level.
* VS1838 Mark Excess -50 at low intensity to +50 us at high intensity
* TSOP31238 Mark Excess 0 to +50
*/
#if !defined(MARK_EXCESS_MICROS)
// To change this value, you simply can add a line #define "MARK_EXCESS_MICROS <My_new_value>" in your ino file before the line "#include <IRremote.hpp>"
#define MARK_EXCESS_MICROS 20
#endif
/**
* Minimum gap between IR transmissions, to detect the end of a protocol.
* Must be greater than any space of a protocol e.g. the NEC header space of 4500 us.
* Must be smaller than any gap between a command and a repeat; e.g. the retransmission gap for Sony is around 15 ms for Sony20 protocol.
* Keep in mind, that this is the delay between the end of the received command and the start of decoding.
*/
#if !defined(RECORD_GAP_MICROS)
// To change this value, you simply can add a line #define "RECORD_GAP_MICROS <My_new_value>" in your *.ino file before the line "#include <IRremote.hpp>"
// Maximum value for RECORD_GAP_MICROS, which fit into 8 bit buffer, using 50 us as tick, is 12750
#define RECORD_GAP_MICROS 8000 // RECS80 (https://www.mikrocontroller.net/articles/IRMP#RECS80) 1 bit space is 7500µs , NEC header space is 4500
#endif
/**
* Threshold for warnings at printIRResult*() to report about changing the RECORD_GAP_MICROS value to a higher value.
*/
#if !defined(RECORD_GAP_MICROS_WARNING_THRESHOLD)
// To change this value, you simply can add a line #define "RECORD_GAP_MICROS_WARNING_THRESHOLD <My_new_value>" in your *.ino file before the line "#include <IRremote.hpp>"
#define RECORD_GAP_MICROS_WARNING_THRESHOLD 15000
#endif
/** Minimum gap between IR transmissions, in MICROS_PER_TICK */
#define RECORD_GAP_TICKS (RECORD_GAP_MICROS / MICROS_PER_TICK)
/*
* Activate this line if your receiver has an external output driver transistor / "inverted" output
*/
//#define IR_INPUT_IS_ACTIVE_HIGH
#if defined(IR_INPUT_IS_ACTIVE_HIGH)
// IR detector output is active high
#define INPUT_MARK 1 ///< Sensor output for a mark ("flash")
#else
// IR detector output is active low
#define INPUT_MARK 0 ///< Sensor output for a mark ("flash")
#endif
/****************************************************
* SENDING
****************************************************/
/**
* Define to disable carrier PWM generation in software and use (restricted) hardware PWM.
*/
//#define SEND_PWM_BY_TIMER // restricts send pin on many platforms to fixed pin numbers
#if (defined(ESP32) || defined(ARDUINO_ARCH_RP2040) || defined(PARTICLE)) || defined(ARDUINO_ARCH_MBED)
# if !defined(SEND_PWM_BY_TIMER)
#define SEND_PWM_BY_TIMER // the best and default method for ESP32 etc.
#warning INFO: For ESP32, RP2040, mbed and particle boards SEND_PWM_BY_TIMER is enabled by default, since we have the resources and timing is more exact than the software generated one. If this is not intended, deactivate the line in IRremote.hpp over this warning message in file IRremote.hpp.
# endif
#else
# if defined(SEND_PWM_BY_TIMER)
# if defined(IR_SEND_PIN)
#undef IR_SEND_PIN // to avoid warning 3 lines later
#warning Since SEND_PWM_BY_TIMER is defined, the existing value of IR_SEND_PIN is discarded and replaced by the value determined by timer used for PWM generation
# endif
#define IR_SEND_PIN DeterminedByTimer // must be set here, since it is evaluated at IRremoteInt.h, before the include of private/IRTimer.hpp
# endif
#endif
/**
* Define to use no carrier PWM, just simulate an active low receiver signal.
*/
//#define USE_NO_SEND_PWM
#if defined(SEND_PWM_BY_TIMER) && defined(USE_NO_SEND_PWM)
#warning "SEND_PWM_BY_TIMER and USE_NO_SEND_PWM are both defined -> undefine SEND_PWM_BY_TIMER now!"
#undef SEND_PWM_BY_TIMER // USE_NO_SEND_PWM overrides SEND_PWM_BY_TIMER
#endif
/**
* Define to use or simulate open drain output mode at send pin.
* Attention, active state of open drain is LOW, so connect the send LED between positive supply and send pin!
*/
//#define USE_OPEN_DRAIN_OUTPUT_FOR_SEND_PIN
#if defined(USE_OPEN_DRAIN_OUTPUT_FOR_SEND_PIN) && !defined(OUTPUT_OPEN_DRAIN)
#warning Pin mode OUTPUT_OPEN_DRAIN is not supported on this platform -> mimick open drain mode by switching between INPUT and OUTPUT mode.
#endif
/**
* This amount is subtracted from the on-time of the pulses generated for software PWM generation.
* It should be the time used for digitalWrite(sendPin, LOW) and the call to delayMicros()
* Measured value for Nano @16MHz is around 3000, for Bluepill @72MHz is around 700, for Zero 3600
*/
#if !defined(PULSE_CORRECTION_NANOS)
# if defined(F_CPU)
// To change this value, you simply can add a line #define "PULSE_CORRECTION_NANOS <My_new_value>" in your ino file before the line "#include <IRremote.hpp>"
#define PULSE_CORRECTION_NANOS (48000L / (F_CPU/MICROS_IN_ONE_SECOND)) // 3000 @16MHz, 666 @72MHz
# else
#define PULSE_CORRECTION_NANOS 600
# endif
#endif
/**
* Duty cycle in percent for sent signals.
*/
#if ! defined(IR_SEND_DUTY_CYCLE_PERCENT)
#define IR_SEND_DUTY_CYCLE_PERCENT 30 // 30 saves power and is compatible to the old existing code
#endif
/**
* microseconds per clock interrupt tick
*/
#if ! defined(MICROS_PER_TICK)
#define MICROS_PER_TICK 50L // must be with L to get 32 bit results if multiplied with rawbuf[] content.
#endif
#define MILLIS_IN_ONE_SECOND 1000L
#define MICROS_IN_ONE_SECOND 1000000L
#define MICROS_IN_ONE_MILLI 1000L
#include "IRremoteInt.h"
/*
* We always use digitalWriteFast() and digitalReadFast() functions to have a consistent mapping for pins.
* For most non AVR cpu's, it is just a mapping to digitalWrite() and digitalRead() functions.
*/
#include "digitalWriteFast.h"
#if !defined(USE_IRREMOTE_HPP_AS_PLAIN_INCLUDE)
#include "private/IRTimer.hpp" // defines IR_SEND_PIN for AVR and SEND_PWM_BY_TIMER
# if !defined(NO_LED_FEEDBACK_CODE)
# if !defined(LED_BUILTIN)
/*
* print a warning
*/
#warning INFO: No definition for LED_BUILTIN found -> default LED feedback is disabled.
# endif
#include "IRFeedbackLED.hpp"
# else
void disableLEDFeedback() {}; // dummy function for examples
# endif
#include "LongUnion.h" // used in most decoders
/*
* Include the sources here to enable compilation with macro values set by user program.
*/
#include "IRProtocol.hpp" // must be first, it includes definition for PrintULL (unsigned long long)
#if !defined(DISABLE_CODE_FOR_RECEIVER)
#include "IRReceive.hpp"
#endif
#include "IRSend.hpp"
/*
* Include the sources of all decoders here to enable compilation with macro values set by user program.
*/
#include "ir_BangOlufsen.hpp"
#include "ir_BoseWave.hpp"
#include "ir_Denon.hpp"
#include "ir_JVC.hpp"
#include "ir_Kaseikyo.hpp"
#include "ir_Lego.hpp"
#include "ir_LG.hpp"
#include "ir_MagiQuest.hpp"
#include "ir_NEC.hpp"
#include "ir_RC5_RC6.hpp"
#include "ir_Samsung.hpp"
#include "ir_Sony.hpp"
#include "ir_FAST.hpp"
#include "ir_Others.hpp"
#include "ir_Pronto.hpp" // pronto is an universal decoder and encoder
# if defined(DECODE_DISTANCE_WIDTH) // universal decoder for pulse distance width protocols - requires up to 750 bytes additional program memory
#include <ir_DistanceWidthProtocol.hpp>
# endif
#endif // #if !defined(USE_IRREMOTE_HPP_AS_PLAIN_INCLUDE)
/**
* Macros for legacy compatibility
*/
#define RAWBUF 101 // Maximum length of raw duration buffer
#define REPEAT 0xFFFFFFFF
#define USECPERTICK MICROS_PER_TICK
#define MARK_EXCESS MARK_EXCESS_MICROS
#endif // _IR_REMOTE_HPP
+665
View File
@@ -0,0 +1,665 @@
/**
* @file IRremoteInt.h
* @brief Contains all declarations required for the interface to IRremote.
* Could not be named IRremote.h, since this has another semantic (it must include all *.hpp files) for old example code found in the wild.
*
* This file is part of Arduino-IRremote https://github.com/Arduino-IRremote/Arduino-IRremote.
*
*
************************************************************************************
* MIT License
*
* Copyright (c) 2015-2023 Ken Shirriff http://www.righto.com, 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_REMOTE_INT_H
#define _IR_REMOTE_INT_H
#include <Arduino.h>
#define MARK 1
#define SPACE 0
#if defined(PARTICLE)
#define F_CPU 16000000 // definition for a board for which F_CPU is not defined
#endif
#if defined(F_CPU) // F_CPU is used to generate the receive send timings in some CPU's
#define CLOCKS_PER_MICRO (F_CPU / MICROS_IN_ONE_SECOND)
#endif
/*
* For backwards compatibility
*/
#if defined(SYSCLOCK) // allow for processor specific code to define F_CPU
#undef F_CPU
#define F_CPU SYSCLOCK // Clock frequency to be used for timing.
#endif
//#define DEBUG // Activate this for lots of lovely debug output from the IRremote core and all protocol decoders.
//#define TRACE // Activate this for more debug output.
/**
* For better readability of code
*/
#define DISABLE_LED_FEEDBACK false
#define ENABLE_LED_FEEDBACK true
#define USE_DEFAULT_FEEDBACK_LED_PIN 0
/**
* The RAW_BUFFER_LENGTH determines the length of the byte buffer where the received IR timing data is stored before decoding.
* 100 is sufficient for standard protocols up to 48 bits, with 1 bit consisting of one mark and space plus 1 byte for initial gap, 2 bytes for header and 1 byte for stop bit.
* 48 bit protocols are PANASONIC, KASEIKYO, SAMSUNG48, RC6.
* 32 bit protocols like NEC, SAMSUNG, WHYNTER, SONY(20), LG(28) requires a buffer length of 68.
* 16 bit protocols like BOSEWAVE, DENON, FAST, JVC, LEGO_PF, RC5, SONY(12 or 15) requires a buffer length of 36.
* MAGIQUEST requires a buffer length of 112.
* Air conditioners often send a longer protocol data stream up to 750 bits.
*/
#if !defined(RAW_BUFFER_LENGTH)
# if (defined(RAMEND) && RAMEND <= 0x8FF) || (defined(RAMSIZE) && RAMSIZE < 0x8FF)
// for RAMsize <= 2k
#define RAW_BUFFER_LENGTH 200 ///< Length of raw duration buffer. Must be even. 100 supports up to 48 bit codings inclusive 1 start and 1 stop bit.
# else
// For undefined or bigger RAMsize
#define RAW_BUFFER_LENGTH 750 // The value for air condition remotes.
# endif
#endif
#if RAW_BUFFER_LENGTH % 2 == 1
#error RAW_BUFFER_LENGTH must be even, since the array consists of space / mark pairs.
#endif
#if RAW_BUFFER_LENGTH <= 254 // saves around 75 bytes program memory and speeds up ISR
typedef uint_fast8_t IRRawlenType;
#else
typedef unsigned int IRRawlenType;
#endif
/*
* Use 8 bit buffer for IR timing in 50 ticks units.
* It is save to use 8 bit if RECORD_GAP_TICKS < 256, since any value greater 255 is interpreted as frame gap of 12750 us.
* The default for frame gap is currently 8000!
* But if we assume that for most protocols the frame gap is way greater than the biggest mark or space duration,
* we can choose to use a 8 bit buffer even for frame gaps up to 200000 us.
* This enables the use of 8 bit buffer even for more some protocols like B&O or LG air conditioner etc.
*/
#if RECORD_GAP_TICKS <= 400 // Corresponds to RECORD_GAP_MICROS of 200000. A value of 255 is foolproof, but we assume, that the frame gap is way greater than the biggest mark or space duration.
typedef uint8_t IRRawbufType; // all timings up to the gap fit into 8 bit.
#else
typedef uint16_t IRRawbufType; // The gap does not fit into 8 bit ticks value. This must not be a reason to use 16 bit for buffer, but it is at least save.
#endif
#if (__INT_WIDTH__ < 32)
typedef uint32_t IRRawDataType;
#define BITS_IN_RAW_DATA_TYPE 32
#else
typedef uint64_t IRRawDataType;
#define BITS_IN_RAW_DATA_TYPE 64
#endif
/**********************************************************
* Declarations for the receiver Interrupt Service Routine
**********************************************************/
// ISR State-Machine : Receiver States
#define IR_REC_STATE_IDLE 0 // Counting the gap time and waiting for the start bit to arrive
#define IR_REC_STATE_MARK 1 // A mark was received and we are counting the duration of it.
#define IR_REC_STATE_SPACE 2 // A space was received and we are counting the duration of it. If space is too long, we assume end of frame.
#define IR_REC_STATE_STOP 3 // Stopped until set to IR_REC_STATE_IDLE which can only be done by resume()
/**
* This struct contains the data and control used for receiver static functions and the ISR (interrupt service routine)
* Only StateForISR needs to be volatile. All the other fields are not written by ISR after data available and before start/resume.
*/
struct irparams_struct {
// The fields are ordered to reduce memory overflow caused by struct-padding
volatile uint8_t StateForISR; ///< State Machine state
uint_fast8_t IRReceivePin; ///< Pin connected to IR data from detector
#if defined(__AVR__)
volatile uint8_t *IRReceivePinPortInputRegister;
uint8_t IRReceivePinMask;
#endif
volatile uint_fast16_t TickCounterForISR; ///< Counts 50uS ticks. The value is copied into the rawbuf array on every transition. Counting is independent of state or resume().
#if !defined(IR_REMOTE_DISABLE_RECEIVE_COMPLETE_CALLBACK)
void (*ReceiveCompleteCallbackFunction)(void); ///< The function to call if a protocol message has arrived, i.e. StateForISR changed to IR_REC_STATE_STOP
#endif
bool OverflowFlag; ///< Raw buffer OverflowFlag occurred
IRRawlenType rawlen; ///< counter of entries in rawbuf
uint16_t initialGapTicks; ///< Tick counts of the length of the gap between previous and current IR frame. Pre 4.4: rawbuf[0].
IRRawbufType rawbuf[RAW_BUFFER_LENGTH]; ///< raw data / tick counts per mark/space. With 8 bit we can only store up to 12.7 ms. First entry is empty to be backwards compatible.
};
extern unsigned long sMicrosAtLastStopTimer; // Used to adjust TickCounterForISR with uncounted ticks between stopTimer() and restartTimer()
#include "IRProtocol.h"
/*
* Debug directives
* Outputs with IR_DEBUG_PRINT can only be activated by defining DEBUG!
* If LOCAL_DEBUG is defined in one file, all outputs with IR_DEBUG_PRINT are still suppressed.
*/
#if defined(DEBUG) || defined(TRACE)
# define IR_DEBUG_PRINT(...) Serial.print(__VA_ARGS__)
# define IR_DEBUG_PRINTLN(...) Serial.println(__VA_ARGS__)
#else
/**
* If DEBUG, print the arguments, otherwise do nothing.
*/
# define IR_DEBUG_PRINT(...) void()
/**
* If DEBUG, print the arguments as a line, otherwise do nothing.
*/
# define IR_DEBUG_PRINTLN(...) void()
#endif
#if defined(TRACE)
# define IR_TRACE_PRINT(...) Serial.print(__VA_ARGS__)
# define IR_TRACE_PRINTLN(...) Serial.println(__VA_ARGS__)
#else
# define IR_TRACE_PRINT(...) void()
# define IR_TRACE_PRINTLN(...) void()
#endif
/****************************************************
* RECEIVING
****************************************************/
/**
* Results returned from old decoders !!!deprecated!!!
*/
struct decode_results {
decode_type_t decode_type; // deprecated, moved to decodedIRData.protocol ///< UNKNOWN, NEC, SONY, RC5, ...
uint16_t address; // Used by Panasonic & Sharp [16-bits]
uint32_t value; // deprecated, moved to decodedIRData.decodedRawData ///< Decoded value / command [max 32-bits]
uint8_t bits; // deprecated, moved to decodedIRData.numberOfBits ///< Number of bits in decoded value
uint16_t magnitude; // deprecated, moved to decodedIRData.extra ///< Used by MagiQuest [16-bits]
bool isRepeat; // deprecated, moved to decodedIRData.flags ///< True if repeat of value is detected
// next 3 values are copies of irparams_struct values - see above
uint16_t *rawbuf; // deprecated, moved to decodedIRData.rawDataPtr->rawbuf ///< Raw intervals in 50uS ticks
uint_fast8_t rawlen; // deprecated, moved to decodedIRData.rawDataPtr->rawlen ///< Number of records in rawbuf
bool overflow; // deprecated, moved to decodedIRData.flags ///< true if IR raw code too long
};
/**
* Main class for receiving IR signals
*/
class IRrecv {
public:
IRrecv();
IRrecv(uint_fast8_t aReceivePin) __attribute__ ((deprecated ("Please use the default IRrecv instance \"IrReceiver\" and IrReceiver.begin(), and not your own IRrecv instance.")));
IRrecv(uint_fast8_t aReceivePin, uint_fast8_t aFeedbackLEDPin) __attribute__ ((deprecated ("Please use the default IRrecv instance \"IrReceiver\" and IrReceiver.begin(), and not your own IRrecv instance..")));
void setReceivePin(uint_fast8_t aReceivePinNumber);
#if !defined(IR_REMOTE_DISABLE_RECEIVE_COMPLETE_CALLBACK)
void registerReceiveCompleteCallback(void (*aReceiveCompleteCallbackFunction)(void));
#endif
/*
* Stream like API
*/
void begin(uint_fast8_t aReceivePin, bool aEnableLEDFeedback = false, uint_fast8_t aFeedbackLEDPin =
USE_DEFAULT_FEEDBACK_LED_PIN);
void start();
void enableIRIn(); // alias for start
void restartTimer();
void restartTimer(uint32_t aMicrosecondsToAddToGapCounter);
void restartTimerWithTicksToAdd(uint16_t aTicksToAddToGapCounter);
void restartAfterSend();
bool available();
IRData* read(); // returns decoded data
// write is a method of class IRsend below
// size_t write(IRData *aIRSendData, int_fast8_t aNumberOfRepeats = NO_REPEATS);
void stopTimer();
void stop();
void disableIRIn(); // alias for stop
void end(); // alias for stop
bool isIdle();
/*
* The main functions
*/
bool decode(); // Check if available and try to decode
void resume(); // Enable receiving of the next value
/*
* Useful info and print functions
*/
void printIRResultMinimal(Print *aSerial);
void printIRResultRawFormatted(Print *aSerial, bool aOutputMicrosecondsInsteadOfTicks = true);
void printIRResultAsCVariables(Print *aSerial);
uint8_t getMaximumMarkTicksFromRawData();
uint8_t getMaximumSpaceTicksFromRawData();
uint8_t getMaximumTicksFromRawData(bool aSearchSpaceInsteadOfMark);
uint32_t getTotalDurationOfRawData();
/*
* Next 4 functions are also available as non member functions
*/
bool printIRResultShort(Print *aSerial, bool aPrintRepeatGap = true, bool aCheckForRecordGapsMicros = true);
void printDistanceWidthTimingInfo(Print *aSerial, DistanceWidthTimingInfoStruct *aDistanceWidthTimingInfo);
void printIRSendUsage(Print *aSerial);
#if defined(__AVR__)
const __FlashStringHelper* getProtocolString();
#else
const char* getProtocolString();
#endif
static void printActiveIRProtocols(Print *aSerial);
void compensateAndPrintIRResultAsCArray(Print *aSerial, bool aOutputMicrosecondsInsteadOfTicks = true);
void compensateAndPrintIRResultAsPronto(Print *aSerial, uint16_t frequency = 38000U);
/*
* Store the data for further processing
*/
void compensateAndStoreIRResultInArray(uint8_t *aArrayPtr);
size_t compensateAndStorePronto(String *aString, uint16_t frequency = 38000U);
/*
* The main decoding functions used by the individual decoders
*/
bool decodePulseDistanceWidthData(PulseDistanceWidthProtocolConstants *aProtocolConstants, uint_fast8_t aNumberOfBits,
IRRawlenType aStartOffset = 3);
bool decodePulseDistanceWidthData(uint_fast8_t aNumberOfBits, IRRawlenType aStartOffset, uint16_t aOneMarkMicros,
uint16_t aOneSpaceMicros, uint16_t aZeroMarkMicros, bool aMSBfirst);
bool decodePulseDistanceWidthData(uint_fast8_t aNumberOfBits, IRRawlenType aStartOffset, uint16_t aOneMarkMicros,
uint16_t aZeroMarkMicros, uint16_t aOneSpaceMicros, uint16_t aZeroSpaceMicros, bool aMSBfirst)
__attribute__ ((deprecated ("Please use decodePulseDistanceWidthData() with 6 parameters.")));
bool decodePulseDistanceWidthDataStrict(uint_fast8_t aNumberOfBits, IRRawlenType aStartOffset, uint16_t aOneMarkMicros,
uint16_t aZeroMarkMicros, uint16_t aOneSpaceMicros, uint16_t aZeroSpaceMicros, bool aMSBfirst);
bool decodeBiPhaseData(uint_fast8_t aNumberOfBits, IRRawlenType aStartOffset, uint_fast8_t aStartClockCount,
uint_fast8_t aValueOfSpaceToMarkTransition, uint16_t aBiphaseTimeUnit);
void initBiphaselevel(uint_fast8_t aRCDecodeRawbuffOffset, uint16_t aBiphaseTimeUnit);
uint_fast8_t getBiphaselevel();
/*
* All standard (decode address + command) protocol decoders
*/
bool decodeBangOlufsen();
bool decodeBoseWave();
bool decodeDenon();
bool decodeFAST();
bool decodeJVC();
bool decodeKaseikyo();
bool decodeLegoPowerFunctions();
bool decodeLG();
bool decodeMagiQuest(); // not completely standard
bool decodeNEC();
bool decodeRC5();
bool decodeRC6();
bool decodeSamsung();
bool decodeSharp(); // redirected to decodeDenon()
bool decodeSony();
bool decodeWhynter();
bool decodeDistanceWidth();
bool decodeHash();
// Template function :-)
bool decodeShuzu();
/*
* Old functions
*/
bool decodeDenonOld(decode_results *aResults);
bool decodeJVCMSB(decode_results *aResults);
bool decodeLGMSB(decode_results *aResults);
bool decodeNECMSB(decode_results *aResults);
bool decodePanasonicMSB(decode_results *aResults);
bool decodeSonyMSB(decode_results *aResults);
bool decodeSAMSUNG(decode_results *aResults);
bool decodeHashOld(decode_results *aResults);
bool decode_old(decode_results *aResults);
bool decode(
decode_results *aResults)
__attribute__ ((deprecated ("Please use IrReceiver.decode() without a parameter and IrReceiver.decodedIRData.<fieldname> .")));
// for backward compatibility. Now in IRFeedbackLED.hpp
void blink13(uint8_t aEnableLEDFeedback)
__attribute__ ((deprecated ("Please use setLEDFeedback() or enableLEDFeedback() / disableLEDFeedback().")));
/*
* Internal functions
*/
void initDecodedIRData();
uint_fast8_t compare(uint16_t oldval, uint16_t newval);
bool checkHeader(PulseDistanceWidthProtocolConstants *aProtocolConstants);
void checkForRepeatSpaceTicksAndSetFlag(uint16_t aMaximumRepeatSpaceTicks);
bool checkForRecordGapsMicros(Print *aSerial);
IRData decodedIRData; // Decoded IR data for the application
// Last decoded IR data for repeat detection and parity for Denon autorepeat
decode_type_t lastDecodedProtocol;
uint32_t lastDecodedAddress;
uint32_t lastDecodedCommand;
uint8_t repeatCount; // Used e.g. for Denon decode for autorepeat decoding.
};
extern uint_fast8_t sBiphaseDecodeRawbuffOffset; //
/*
* Mark & Space matching functions
*/
bool matchTicks(uint16_t aMeasuredTicks, uint16_t aMatchValueMicros);
bool matchMark(uint16_t aMeasuredTicks, uint16_t aMatchValueMicros);
bool matchSpace(uint16_t aMeasuredTicks, uint16_t aMatchValueMicros);
/*
* Old function names
*/
bool MATCH(uint16_t measured, uint16_t desired);
bool MATCH_MARK(uint16_t measured_ticks, uint16_t desired_us);
bool MATCH_SPACE(uint16_t measured_ticks, uint16_t desired_us);
int getMarkExcessMicros();
void printActiveIRProtocols(Print *aSerial);
/****************************************************
* Feedback LED related functions
****************************************************/
#define DO_NOT_ENABLE_LED_FEEDBACK 0x00
#define LED_FEEDBACK_DISABLED_COMPLETELY 0x00
#define LED_FEEDBACK_ENABLED_FOR_RECEIVE 0x01
#define LED_FEEDBACK_ENABLED_FOR_SEND 0x02
void setFeedbackLED(bool aSwitchLedOn);
void setLEDFeedback(uint8_t aFeedbackLEDPin, uint8_t aEnableLEDFeedback); // if aFeedbackLEDPin == 0, then take board BLINKLED_ON() and BLINKLED_OFF() functions
void setLEDFeedback(bool aEnableLEDFeedback); // Direct replacement for blink13()
void enableLEDFeedback();
constexpr auto enableLEDFeedbackForReceive = enableLEDFeedback; // alias for enableLEDFeedback
void disableLEDFeedback();
constexpr auto disableLEDFeedbackForReceive = disableLEDFeedback; // alias for enableLEDFeedback
void enableLEDFeedbackForSend();
void disableLEDFeedbackForSend();
void setBlinkPin(uint8_t aFeedbackLEDPin) __attribute__ ((deprecated ("Please use setLEDFeedback()."))); // deprecated
/*
* Pulse parms are ((X*50)-MARK_EXCESS_MICROS) for the Mark and ((X*50)+MARK_EXCESS_MICROS) for the Space.
* First MARK is the one after the long gap
* Pulse parameters in microseconds
*/
#if !defined(TOLERANCE_FOR_DECODERS_MARK_OR_SPACE_MATCHING_PERCENT)
#define TOLERANCE_FOR_DECODERS_MARK_OR_SPACE_MATCHING_PERCENT 25 // Relative tolerance (in percent) for matchTicks(), matchMark() and matchSpace() functions used for protocol decoding.
#endif
#define TICKS(us) ((us)/MICROS_PER_TICK) // (us)/50
#if MICROS_PER_TICK == 50 && TOLERANCE_FOR_DECODERS_MARK_OR_SPACE_MATCHING_PERCENT == 25 // Defaults
#define TICKS_LOW(us) ((us)/67 ) // 67 = MICROS_PER_TICK / ((100-25)/100) = (MICROS_PER_TICK * 100) / (100-25)
#define TICKS_HIGH(us) (((us)/40) + 1) // 40 = MICROS_PER_TICK / ((100+25)/100) = (MICROS_PER_TICK * 100) / (100+25)
#else
/** Lower tolerance for comparison of measured data */
//#define LTOL (1.0 - (TOLERANCE/100.))
#define LTOL (100 - TOLERANCE_FOR_DECODERS_MARK_OR_SPACE_MATCHING_PERCENT)
/** Upper tolerance for comparison of measured data */
//#define UTOL (1.0 + (TOLERANCE/100.))
#define UTOL (100 + TOLERANCE_FOR_DECODERS_MARK_OR_SPACE_MATCHING_PERCENT)
#define TICKS_LOW(us) ((uint16_t ) ((long) (us) * LTOL / (MICROS_PER_TICK * 100) ))
#define TICKS_HIGH(us) ((uint16_t ) ((long) (us) * UTOL / (MICROS_PER_TICK * 100) + 1))
#endif
/*
* The receiver instance
*/
extern IRrecv IrReceiver;
/*
* The receiver interrupt handler for timer interrupt
*/
#if defined (MIK32V2)
__attribute__((noinline, section(".ram_text"), optimize("O3")))
# endif
void IRReceiveTimerInterruptHandler();
/****************************************************
* SENDING
****************************************************/
/**
* Just for better readability of code
*/
#define NO_REPEATS 0
#define SEND_REPEAT_COMMAND true ///< used for e.g. NEC, where a repeat is different from just repeating the data.
/**
* Main class for sending IR signals
*/
class IRsend {
public:
IRsend();
/*
* IR_SEND_PIN is defined or fixed by timer, value of IR_SEND_PIN is then "DeterminedByTimer"
*/
#if defined(IR_SEND_PIN)
void begin();
// The default parameter allowed to specify IrSender.begin(7); without errors, if IR_SEND_PIN was defined. But the semantics is not the one the user expect.
void begin(bool aEnableLEDFeedback, uint_fast8_t aFeedbackLEDPin); // 4.3.1 Removed default value USE_DEFAULT_FEEDBACK_LED_PIN for last parameter
// The next function is a dummy to avoid acceptance of pre 4.3 calls to begin(DISABLE_LED_FEEDBACK);
void begin(uint8_t aSendPin)
# if !defined (DOXYGEN)
__attribute__ ((deprecated ("Error: IR_SEND_PIN is still defined, therefore the function begin(aSendPin) is NOT available. You must disable '#define IR_SEND_PIN' to enable this function.")));
# endif
// The next function is a dummy to avoid acceptance of pre 4.0 calls to begin(IR_SEND_PIN, DISABLE_LED_FEEDBACK);
void begin(uint_fast8_t aSendPin, bool aEnableLEDFeedback)
# if !defined (DOXYGEN)
__attribute__ ((deprecated ("You must use begin() and enableLEDFeedback() or disableLEDFeedback() since version 4.3.")));
# endif
#else
IRsend(uint_fast8_t aSendPin);
void begin(uint_fast8_t aSendPin);
void setSendPin(uint_fast8_t aSendPin); // required if we use IRsend() as constructor
// Since 4.0 guarded and without default parameter
void begin(uint_fast8_t aSendPin, bool aEnableLEDFeedback, uint_fast8_t aFeedbackLEDPin); // aFeedbackLEDPin can be USE_DEFAULT_FEEDBACK_LED_PIN
#endif
size_t write(IRData *aIRSendData, int_fast8_t aNumberOfRepeats = NO_REPEATS);
size_t write(decode_type_t aProtocol, uint16_t aAddress, uint16_t aCommand, int_fast8_t aNumberOfRepeats = NO_REPEATS);
void enableIROut(uint_fast8_t aFrequencyKHz);
#if defined(SEND_PWM_BY_TIMER)
void enableHighFrequencyIROut(uint_fast16_t aFrequencyKHz); // Used for Bang&Olufsen
#endif
void sendPulseDistanceWidthFromArray(uint_fast8_t aFrequencyKHz, uint16_t aHeaderMarkMicros, uint16_t aHeaderSpaceMicros,
uint16_t aOneMarkMicros, uint16_t aOneSpaceMicros, uint16_t aZeroMarkMicros, uint16_t aZeroSpaceMicros,
IRRawDataType *aDecodedRawDataArray, uint16_t aNumberOfBits, uint8_t aFlags, uint16_t aRepeatPeriodMillis,
int_fast8_t aNumberOfRepeats);
void sendPulseDistanceWidthFromArray(PulseDistanceWidthProtocolConstants *aProtocolConstants,
IRRawDataType *aDecodedRawDataArray, uint16_t aNumberOfBits, int_fast8_t aNumberOfRepeats);
void sendPulseDistanceWidthFromArray(uint_fast8_t aFrequencyKHz, DistanceWidthTimingInfoStruct *aDistanceWidthTimingInfo,
IRRawDataType *aDecodedRawDataArray, uint16_t aNumberOfBits, uint8_t aFlags, uint16_t aRepeatPeriodMillis,
int_fast8_t aNumberOfRepeats);
void sendPulseDistanceWidth(PulseDistanceWidthProtocolConstants *aProtocolConstants, IRRawDataType aData,
uint_fast8_t aNumberOfBits, int_fast8_t aNumberOfRepeats);
void sendPulseDistanceWidthData(PulseDistanceWidthProtocolConstants *aProtocolConstants, IRRawDataType aData,
uint_fast8_t aNumberOfBits);
void sendPulseDistanceWidth(uint_fast8_t aFrequencyKHz, uint16_t aHeaderMarkMicros, uint16_t aHeaderSpaceMicros,
uint16_t aOneMarkMicros, uint16_t aOneSpaceMicros, uint16_t aZeroMarkMicros, uint16_t aZeroSpaceMicros,
IRRawDataType aData, uint_fast8_t aNumberOfBits, uint8_t aFlags, uint16_t aRepeatPeriodMillis,
int_fast8_t aNumberOfRepeats, void (*aSpecialSendRepeatFunction)() = NULL);
void sendPulseDistanceWidth(uint_fast8_t aFrequencyKHz, uint16_t aHeaderMarkMicros, uint16_t aHeaderSpaceMicros,
uint16_t aOneMarkMicros, uint16_t aOneSpaceMicros, uint16_t aZeroMarkMicros, uint16_t aZeroSpaceMicros,
IRRawDataType aData, uint_fast8_t aNumberOfBits, bool aMSBFirst, bool aSendStopBit, uint16_t aRepeatPeriodMillis,
int_fast8_t aNumberOfRepeats, void (*aSpecialSendRepeatFunction)() = NULL)
__attribute__ ((deprecated ("Since version 4.1.0 parameter aSendStopBit is not longer required.")));
void sendPulseDistanceWidthData(uint16_t aOneMarkMicros, uint16_t aOneSpaceMicros, uint16_t aZeroMarkMicros,
uint16_t aZeroSpaceMicros, IRRawDataType aData, uint_fast8_t aNumberOfBits, uint8_t aFlags);
void sendBiphaseData(uint16_t aBiphaseTimeUnit, uint32_t aData, uint_fast8_t aNumberOfBits);
void mark(uint16_t aMarkMicros);
static void space(uint16_t aSpaceMicros);
void IRLedOff();
// 8 Bit array
void sendRaw(const uint8_t aBufferWithTicks[], uint_fast16_t aLengthOfBuffer, uint_fast8_t aIRFrequencyKilohertz);
void sendRaw_P(const uint8_t aBufferWithTicks[], uint_fast16_t aLengthOfBuffer, uint_fast8_t aIRFrequencyKilohertz);
// 16 Bit array
void sendRaw(const uint16_t aBufferWithMicroseconds[], uint_fast16_t aLengthOfBuffer, uint_fast8_t aIRFrequencyKilohertz);
void sendRaw_P(const uint16_t aBufferWithMicroseconds[], uint_fast16_t aLengthOfBuffer, uint_fast8_t aIRFrequencyKilohertz);
/*
* New send functions
*/
void sendBangOlufsen(uint16_t aHeader, uint8_t aData, int_fast8_t aNumberOfRepeats = NO_REPEATS,
int8_t aNumberOfHeaderBits = 8);
void sendBangOlufsenDataLink(uint32_t aHeader, uint8_t aData, int_fast8_t aNumberOfRepeats = NO_REPEATS,
int8_t aNumberOfHeaderBits = 8);
void sendBangOlufsenRaw(uint32_t aRawData, int_fast8_t aBits, bool aBackToBack = false);
void sendBangOlufsenRawDataLink(uint64_t aRawData, int_fast8_t aBits, bool aBackToBack = false,
bool aUseDatalinkTiming = false);
void sendBoseWave(uint8_t aCommand, int_fast8_t aNumberOfRepeats = NO_REPEATS);
void sendDenon(uint8_t aAddress, uint8_t aCommand, int_fast8_t aNumberOfRepeats, bool aSendSharp = false);
void sendDenonRaw(uint16_t aRawData, int_fast8_t aNumberOfRepeats = NO_REPEATS)
#if !defined (DOXYGEN)
__attribute__ ((deprecated ("Please use sendDenon(aAddress, aCommand, aNumberOfRepeats).")));
#endif
void sendFAST(uint8_t aCommand, int_fast8_t aNumberOfRepeats);
void sendJVC(uint8_t aAddress, uint8_t aCommand, int_fast8_t aNumberOfRepeats);
void sendLG2Repeat();
uint32_t computeLGRawDataAndChecksum(uint8_t aAddress, uint16_t aCommand);
void sendLG(uint8_t aAddress, uint16_t aCommand, int_fast8_t aNumberOfRepeats);
void sendLG2(uint8_t aAddress, uint16_t aCommand, int_fast8_t aNumberOfRepeats);
void sendLGRaw(uint32_t aRawData, int_fast8_t aNumberOfRepeats = NO_REPEATS);
void sendNECRepeat();
uint32_t computeNECRawDataAndChecksum(uint16_t aAddress, uint16_t aCommand);
void sendNEC(uint16_t aAddress, uint16_t aCommand, int_fast8_t aNumberOfRepeats);
void sendNEC2(uint16_t aAddress, uint16_t aCommand, int_fast8_t aNumberOfRepeats);
void sendNECRaw(uint32_t aRawData, int_fast8_t aNumberOfRepeats = NO_REPEATS);
// NEC variants
void sendOnkyo(uint16_t aAddress, uint16_t aCommand, int_fast8_t aNumberOfRepeats);
void sendApple(uint8_t aAddress, uint8_t aCommand, int_fast8_t aNumberOfRepeats);
void sendKaseikyo(uint16_t aAddress, uint8_t aData, int_fast8_t aNumberOfRepeats, uint16_t aVendorCode); // LSB first
void sendPanasonic(uint16_t aAddress, uint8_t aData, int_fast8_t aNumberOfRepeats); // LSB first
void sendKaseikyo_Denon(uint16_t aAddress, uint8_t aData, int_fast8_t aNumberOfRepeats); // LSB first
void sendKaseikyo_Mitsubishi(uint16_t aAddress, uint8_t aData, int_fast8_t aNumberOfRepeats); // LSB first
void sendKaseikyo_Sharp(uint16_t aAddress, uint8_t aData, int_fast8_t aNumberOfRepeats); // LSB first
void sendKaseikyo_JVC(uint16_t aAddress, uint8_t aData, int_fast8_t aNumberOfRepeats); // LSB first
void sendRC5(uint8_t aAddress, uint8_t aCommand, int_fast8_t aNumberOfRepeats, bool aEnableAutomaticToggle = true);
void sendRC6(uint8_t aAddress, uint8_t aCommand, int_fast8_t aNumberOfRepeats, bool aEnableAutomaticToggle = true);
void sendRC6A(uint8_t aAddress, uint8_t aCommand, int_fast8_t aNumberOfRepeats, uint16_t aCustomer, bool aEnableAutomaticToggle = true);
void sendSamsungLGRepeat();
void sendSamsung(uint16_t aAddress, uint16_t aCommand, int_fast8_t aNumberOfRepeats);
void sendSamsung16BitAddressAnd8BitCommand(uint16_t aAddress, uint8_t aCommand, int_fast8_t aNumberOfRepeats);
void sendSamsung16BitAddressAndCommand(uint16_t aAddress, uint16_t aCommand, int_fast8_t aNumberOfRepeats);
void sendSamsung48(uint16_t aAddress, uint32_t aCommand, int_fast8_t aNumberOfRepeats);
void sendSamsungLG(uint16_t aAddress, uint16_t aCommand, int_fast8_t aNumberOfRepeats);
void sendSharp(uint8_t aAddress, uint8_t aCommand, int_fast8_t aNumberOfRepeats); // redirected to sendDenon
void sendSony(uint16_t aAddress, uint8_t aCommand, int_fast8_t aNumberOfRepeats, uint8_t numberOfBits = 12); // SIRCS_12_PROTOCOL
void sendLegoPowerFunctions(uint8_t aChannel, uint8_t tCommand, uint8_t aMode, bool aDoSend5Times = true);
void sendLegoPowerFunctions(uint16_t aRawData, bool aDoSend5Times = true);
void sendLegoPowerFunctions(uint16_t aRawData, uint8_t aChannel, bool aDoSend5Times = true);
void sendMagiQuest(uint32_t aWandId, uint16_t aMagnitude);
void sendPronto(const __FlashStringHelper *str, int_fast8_t aNumberOfRepeats = NO_REPEATS);
void sendPronto(const char *prontoHexString, int_fast8_t aNumberOfRepeats = NO_REPEATS);
void sendPronto(const uint16_t *data, uint16_t length, int_fast8_t aNumberOfRepeats = NO_REPEATS);
#if defined(__AVR__)
void sendPronto_PF(uint_farptr_t str, int_fast8_t aNumberOfRepeats = NO_REPEATS);
void sendPronto_P(const char *str, int_fast8_t aNumberOfRepeats);
#endif
// Template protocol :-)
void sendShuzu(uint16_t aAddress, uint8_t aCommand, int_fast8_t aNumberOfRepeats);
/*
* OLD send functions
*/
void sendDenon(unsigned long data,
int nbits)
__attribute__ ((deprecated ("The function sendDenon(data, nbits) is deprecated and may not work as expected! Use sendDenonRaw(data, NumberOfRepeats) or better sendDenon(Address, Command, NumberOfRepeats).")));
void sendDish(uint16_t aData);
void sendJVC(unsigned long data, int nbits,
bool repeat)
__attribute__ ((deprecated ("This old function sends MSB first! Please use sendJVC(aAddress, aCommand, aNumberOfRepeats)."))) {
sendJVCMSB(data, nbits, repeat);
}
void sendJVCMSB(unsigned long data, int nbits, bool repeat = false);
void sendLG(unsigned long data,
int nbits)
__attribute__ ((deprecated ("The function sendLG(data, nbits) is deprecated and may not work as expected! Use sendLGRaw(data, NumberOfRepeats) or better sendLG(Address, Command, NumberOfRepeats).")));
void sendNEC(uint32_t aRawData,
uint8_t nbits)
__attribute__ ((deprecated ("This old function sends MSB first! Please use sendNECMSB() or sendNEC(aAddress, aCommand, aNumberOfRepeats)."))) {
sendNECMSB(aRawData, nbits);
}
void sendNECMSB(uint32_t data, uint8_t nbits, bool repeat = false);
void sendRC5(uint32_t data, uint8_t nbits);
void sendRC5ext(uint8_t addr, uint8_t cmd, bool toggle);
void sendRC6Raw(uint32_t data, uint8_t nbits);
void sendRC6(uint32_t data, uint8_t nbits) __attribute__ ((deprecated ("Please use sendRC6Raw().")));
void sendRC6Raw(uint64_t data, uint8_t nbits);
void sendRC6(uint64_t data, uint8_t nbits) __attribute__ ((deprecated ("Please use sendRC6Raw().")));
;
void sendSharpRaw(unsigned long data, int nbits);
void sendSharp(uint16_t address, uint16_t command);
void sendSAMSUNG(unsigned long data, int nbits);
__attribute__ ((deprecated ("This old function sends MSB first! Please use sendSamsung().")));
void sendSamsungMSB(unsigned long data, int nbits);
void sendSonyMSB(unsigned long data, int nbits);
void sendSony(unsigned long data,
int nbits)
__attribute__ ((deprecated ("This old function sends MSB first! Please use sendSony(aAddress, aCommand, aNumberOfRepeats).")));
;
void sendWhynter(uint32_t aData, uint8_t aNumberOfBitsToSend);
#if !defined(IR_SEND_PIN)
uint8_t sendPin;
#endif
uint16_t periodTimeMicros;
uint16_t periodOnTimeMicros; // compensated with PULSE_CORRECTION_NANOS for duration of digitalWrite. Around 8 microseconds for 38 kHz.
uint16_t getPulseCorrectionNanos();
static void customDelayMicroseconds(unsigned long aMicroseconds);
};
/*
* The sender instance
*/
extern IRsend IrSender;
void sendNECSpecialRepeat();
void sendLG2SpecialRepeat();
void sendSamsungLGSpecialRepeat();
#endif // _IR_REMOTE_INT_H
+153
View File
@@ -0,0 +1,153 @@
/*
* LongUnion.h
*
* Copyright (C) 2020-2022 Armin Joachimsmeyer
* Email: armin.joachimsmeyer@gmail.com
*
* This file is part of Arduino-Utils https://github.com/ArminJo/Arduino-Utils.
*
* Arduino-Utils is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/gpl.html>.
*
*/
#if !defined(_WORD_UNION_H) || !defined(_LONG_UNION_H) || !defined(_LONG_LONG_UNION_H)
#include <stdint.h>
#ifndef _WORD_UNION_H
#define _WORD_UNION_H
/**
* Union to specify parts / manifestations of a 16 bit Word without casts and shifts.
* It also supports the compiler generating small code.
* Usage: WordUnion tWord;
* tWord.UByte.HighByte = 0x12;
*/
union WordUnion {
struct {
uint8_t LowByte;
uint8_t HighByte;
} UByte;
struct {
int8_t LowByte;
int8_t HighByte;
} Byte;
uint8_t UBytes[2]; // UBytes[0] is LowByte
int8_t Bytes[2];
uint16_t UWord;
int16_t Word;
uint8_t *BytePointer;
};
#endif // _WORD_UNION_H
#ifndef _LONG_UNION_H
#define _LONG_UNION_H
/**
* Union to specify parts / manifestations of a 32 bit Long without casts and shifts.
* It also supports the compiler generating small code.
*/
union LongUnion {
struct {
uint8_t LowByte;
uint8_t MidLowByte;
uint8_t MidHighByte;
uint8_t HighByte;
} UByte;
struct {
int8_t LowByte;
int8_t MidLowByte;
int8_t MidHighByte;
int8_t HighByte;
} Byte;
/* Does not work for STM32
struct {
uint8_t LowByte;
uint16_t MidWord;
uint8_t HighByte;
} UByteWord;
*/
struct {
uint16_t LowWord;
uint16_t HighWord;
} UWord;
struct {
int16_t LowWord;
int16_t HighWord;
} Word;
struct {
WordUnion LowWord;
WordUnion HighWord;
} TwoWordUnions;
uint8_t UBytes[4]; // seems to have the same code size as using struct UByte
int8_t Bytes[4]; // Bytes[0] is LowByte
uint16_t UWords[2];
int16_t Words[2];
uint32_t ULong;
int32_t Long;
float Float;
};
#endif // _LONG_UNION_H
#ifndef _LONG_LONG_UNION_H
#define _LONG_LONG_UNION_H
/**
* Union to specify parts / manifestations of a 64 bit LongLong without casts and shifts.
* It also supports the compiler generating small code.
*/
union LongLongUnion {
struct {
uint16_t LowWord;
uint16_t MidLowWord;
uint16_t MidHighWord;
uint16_t HighWord;
} UWord;
struct {
int16_t LowWord;
int16_t MidLowWord;
int16_t MidHighWord;
int16_t HighWord;
} Word;
struct {
WordUnion LowWord;
WordUnion MidLowWord;
WordUnion MidHighWord;
WordUnion HighWord;
} FourWordUnions;
struct {
uint32_t LowLong;
uint32_t HighLong;
} ULong;
struct {
int32_t LowLong;
int32_t HighLong;
} Long;
struct {
LongUnion LowLong;
LongUnion HighLong;
} TwoLongUnions;
uint8_t UBytes[8]; // seems to have the same code size as using struct UByte
int8_t Bytes[8];
uint16_t UWords[4];
int16_t Words[4];
uint64_t ULongLong;
int64_t LongLong;
#if __DBL_MANT_DIG__== 24
float Floats[2]; // 32 bit double, as for AVR
#else
// 64 bit double
double Double;
#endif
};
#endif // _LONG_LONG_UNION_H
#endif // !defined(_WORD_UNION_H) || !defined(_LONG_UNION_H) || !defined(_LONG_LONG_UNION_H)
+287
View File
@@ -0,0 +1,287 @@
/*
* TinyIR.h
*
*
* Copyright (C) 2021-2023 Armin Joachimsmeyer
* armin.joachimsmeyer@gmail.com
*
* This file is part of IRMP https://github.com/IRMP-org/IRMP.
* This file is part of Arduino-IRremote https://github.com/Arduino-IRremote/Arduino-IRremote.
*
* TinyIRReceiver is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/gpl.html>.
*
*/
#ifndef _TINY_IR_H
#define _TINY_IR_H
#include <Arduino.h>
#include "LongUnion.h"
/** \addtogroup TinyReceiver Minimal receiver for NEC and FAST protocol
* @{
*/
#define VERSION_TINYIR "2.2.0"
#define VERSION_TINYIR_MAJOR 2
#define VERSION_TINYIR_MINOR 2
#define VERSION_TINYIR_PATCH 0
// The change log is at the bottom of the file
/**
* Timing for NEC protocol
*
* see: https://www.sbprojects.net/knowledge/ir/nec.php
* LSB first, 1 start bit + 16 bit address + 8 bit data + 8 bit inverted data + 1 stop bit.
*/
#if !defined(NEC_ADDRESS_BITS)
#define NEC_ADDRESS_BITS 16 // 16 bit address or 8 bit address and 8 bit inverted address
#define NEC_COMMAND_BITS 16 // Command and inverted command
#define NEC_BITS (NEC_ADDRESS_BITS + NEC_COMMAND_BITS)
#define NEC_UNIT 560
#define NEC_HEADER_MARK (16 * NEC_UNIT) // 9000
#define NEC_HEADER_SPACE (8 * NEC_UNIT) // 4500
#define NEC_BIT_MARK NEC_UNIT
#define NEC_ONE_SPACE (3 * NEC_UNIT) // 1690
#define NEC_ZERO_SPACE NEC_UNIT
#define NEC_REPEAT_HEADER_SPACE (4 * NEC_UNIT) // 2250
#define NEC_REPEAT_PERIOD 110000 // Commands are repeated every 110 ms (measured from start to start) for as long as the key on the remote control is held down.
#define NEC_MINIMAL_DURATION 49900 // NEC_HEADER_MARK + NEC_HEADER_SPACE + 32 * 2 * NEC_UNIT + NEC_UNIT // 2.5 because we assume more zeros than ones
#define NEC_MAXIMUM_REPEAT_DISTANCE (NEC_REPEAT_PERIOD - NEC_MINIMAL_DURATION + 10000) // 70 ms
#endif
/**
* The FAST protocol is a proprietary modified JVC protocol without address, with parity and with a shorter header.
* FAST protocol characteristics:
* - Bit timing is like NEC or JVC
* - The header is shorter, 3156 vs. 12500
* - No address and 16 bit data, interpreted as 8 bit command and 8 bit inverted command,
* leading to a fixed protocol length of (6 + (16 * 3) + 1) * 526 = 55 * 526 = 28930 microseconds or 29 ms.
* - Repeats are sent as complete frames but in a 50 ms period / with a 21 ms distance.
*/
/*
Protocol=FAST Address=0x0 Command=0x76 Raw-Data=0x8976 16 bits LSB first
+2100,-1050
+ 550,- 500 + 550,-1550 + 550,-1550 + 550,- 500
+ 550,-1550 + 550,-1550 + 550,-1550 + 550,- 500
+ 550,-1550 + 550,- 500 + 550,- 500 + 550,-1550
+ 550,- 500 + 550,- 500 + 550,- 500 + 550,-1550
+ 550
Sum: 28900
*/
#define FAST_KHZ 38
#define FAST_ADDRESS_BITS 0 // No address
#define FAST_COMMAND_BITS 16 // Command and inverted command (parity)
#define FAST_BITS (FAST_ADDRESS_BITS + FAST_COMMAND_BITS)
#define FAST_UNIT 526 // 20 periods of 38 kHz (526.315789)
#define FAST_BIT_MARK FAST_UNIT
#define FAST_ONE_SPACE (3 * FAST_UNIT) // 1578 -> bit period = 2104
#define FAST_ZERO_SPACE FAST_UNIT // 526 -> bit period = 1052
#define FAST_HEADER_MARK (4 * FAST_UNIT) // 2104
#define FAST_HEADER_SPACE (2 * FAST_UNIT) // 1052
#define FAST_REPEAT_PERIOD 50000 // Commands are repeated every 50 ms (measured from start to start) for as long as the key on the remote control is held down.
#define FAST_REPEAT_DISTANCE (FAST_REPEAT_PERIOD - (55 * FAST_UNIT)) // 19 ms
#define FAST_MAXIMUM_REPEAT_DISTANCE (FAST_REPEAT_DISTANCE + 10000) // 29 ms
/*
* Definitions to switch between FAST and NEC/ONKYO timing with the same code.
*/
#if defined(USE_FAST_PROTOCOL)
#define ENABLE_NEC2_REPEATS // Disables detection of special short frame NEC repeats. Saves 40 bytes program memory.
#define TINY_RECEIVER_ADDRESS_BITS FAST_ADDRESS_BITS
#define TINY_RECEIVER_COMMAND_BITS FAST_COMMAND_BITS
#if !defined(TINY_RECEIVER_COMMAND_HAS_8_BIT_PARITY)
#define TINY_RECEIVER_COMMAND_HAS_8_BIT_PARITY true // 8 bit and 8 bit parity
//#define TINY_RECEIVER_COMMAND_HAS_8_BIT_PARITY false // 16 bit command without parity - not tested
#endif
#define TINY_RECEIVER_BITS FAST_BITS
#define TINY_RECEIVER_UNIT FAST_UNIT
#define TINY_RECEIVER_HEADER_MARK FAST_HEADER_MARK
#define TINY_RECEIVER_HEADER_SPACE FAST_HEADER_SPACE
#define TINY_RECEIVER_BIT_MARK FAST_BIT_MARK
#define TINY_RECEIVER_ONE_SPACE FAST_ONE_SPACE
#define TINY_RECEIVER_ZERO_SPACE FAST_ZERO_SPACE
#define TINY_RECEIVER_MAXIMUM_REPEAT_DISTANCE FAST_MAXIMUM_REPEAT_DISTANCE // for repeat detection
#else
#define TINY_RECEIVER_ADDRESS_BITS NEC_ADDRESS_BITS // the address bits + parity
# if defined(USE_ONKYO_PROTOCOL)
#define TINY_RECEIVER_ADDRESS_HAS_8_BIT_PARITY false // 16 bit address without parity
# elif defined(USE_EXTENDED_NEC_PROTOCOL)
#define TINY_RECEIVER_ADDRESS_HAS_8_BIT_PARITY false // 16 bit address without parity
# else
#define TINY_RECEIVER_ADDRESS_HAS_8_BIT_PARITY true // 8 bit and 8 bit parity
# endif
#define TINY_RECEIVER_COMMAND_BITS NEC_COMMAND_BITS // the command bits + parity
# if defined(USE_ONKYO_PROTOCOL)
#define TINY_RECEIVER_COMMAND_HAS_8_BIT_PARITY false // 16 bit command without parity
# else
#define TINY_RECEIVER_COMMAND_HAS_8_BIT_PARITY true // 8 bit and 8 bit parity
# endif
#define TINY_RECEIVER_BITS NEC_BITS
#define TINY_RECEIVER_UNIT NEC_UNIT
#define TINY_RECEIVER_HEADER_MARK NEC_HEADER_MARK
#define TINY_RECEIVER_HEADER_SPACE NEC_HEADER_SPACE
#define TINY_RECEIVER_BIT_MARK NEC_BIT_MARK
#define TINY_RECEIVER_ONE_SPACE NEC_ONE_SPACE
#define TINY_RECEIVER_ZERO_SPACE NEC_ZERO_SPACE
#define TINY_RECEIVER_MAXIMUM_REPEAT_DISTANCE NEC_MAXIMUM_REPEAT_DISTANCE
#endif
#if defined(USE_CALLBACK_FOR_TINY_RECEIVER)
/*
* This function is called, if a complete command was received and must be implemented in the file (user code)
* which includes this library if USE_CALLBACK_FOR_TINY_RECEIVER is activated.
*/
extern void handleReceivedTinyIRData();
#endif
#if !defined(MICROS_IN_ONE_SECOND)
#define MICROS_IN_ONE_SECOND 1000000L
#endif
#if !defined(MICROS_IN_ONE_MILLI)
#define MICROS_IN_ONE_MILLI 1000L
#endif
/*
* Macros for comparing timing values
*/
#define lowerValue25Percent(aDuration) (aDuration - (aDuration / 4))
#define upperValue25Percent(aDuration) (aDuration + (aDuration / 4))
#define lowerValue50Percent(aDuration) (aDuration / 2) // (aDuration - (aDuration / 2))
#define upperValue50Percent(aDuration) (aDuration + (aDuration / 2))
/*
* The states for the state machine
*/
#define IR_RECEIVER_STATE_WAITING_FOR_START_MARK 0
#define IR_RECEIVER_STATE_WAITING_FOR_START_SPACE 1
#define IR_RECEIVER_STATE_WAITING_FOR_FIRST_DATA_MARK 2
#define IR_RECEIVER_STATE_WAITING_FOR_DATA_SPACE 3
#define IR_RECEIVER_STATE_WAITING_FOR_DATA_MARK 4
#define IR_RECEIVER_STATE_WAITING_FOR_STOP_MARK 5
/**
* Control and data variables of the state machine for TinyReceiver
*/
struct TinyIRReceiverStruct {
/*
* State machine
*/
uint32_t LastChangeMicros; ///< Microseconds of last Pin Change Interrupt.
uint8_t IRReceiverState; ///< The state of the state machine.
uint8_t IRRawDataBitCounter; ///< How many bits are currently contained in raw data.
/*
* Data
*/
#if (TINY_RECEIVER_BITS > 16)
uint32_t IRRawDataMask; ///< The corresponding bit mask for IRRawDataBitCounter.
LongUnion IRRawData; ///< The current raw data. LongUnion helps with decoding of address and command.
#else
uint16_t IRRawDataMask; ///< The corresponding bit mask for IRRawDataBitCounter.
WordUnion IRRawData; ///< The current raw data. WordUnion helps with decoding of command.
#endif
uint8_t Flags; ///< One of IRDATA_FLAGS_EMPTY, IRDATA_FLAGS_IS_REPEAT, and IRDATA_FLAGS_PARITY_FAILED
};
/*
* Definitions for member TinyIRReceiverCallbackDataStruct.Flags
* From IRremoteInt.h
*/
#define IRDATA_FLAGS_EMPTY 0x00
#define IRDATA_FLAGS_IS_REPEAT 0x01
#define IRDATA_FLAGS_IS_AUTO_REPEAT 0x02 // not used here, overwritten with _IRDATA_FLAGS_IS_SHORT_REPEAT
#define IRDATA_FLAGS_PARITY_FAILED 0x04 ///< the current (autorepeat) frame violated parity check
/**
* Is filled before calling the user callback to transfer received data to main loop for further processing.
*/
struct TinyIRReceiverCallbackDataStruct {
#if (TINY_RECEIVER_ADDRESS_BITS > 0)
# if (TINY_RECEIVER_ADDRESS_BITS == 16) && !TINY_RECEIVER_ADDRESS_HAS_8_BIT_PARITY
uint16_t Address;
# else
uint8_t Address;
# endif
#endif
# if (TINY_RECEIVER_COMMAND_BITS == 16) && !TINY_RECEIVER_COMMAND_HAS_8_BIT_PARITY
uint16_t Command;
#else
uint8_t Command;
#endif
uint8_t Flags; // Bit coded flags. Can contain one of the bits: IRDATA_FLAGS_IS_REPEAT and IRDATA_FLAGS_PARITY_FAILED
bool justWritten; ///< Is set true if new data is available. Used by the main loop / TinyReceiverDecode(), to avoid multiple evaluations of the same IR frame.
};
extern volatile TinyIRReceiverCallbackDataStruct TinyIRReceiverData;
bool isIRReceiverAttachedForTinyReceiver();
bool initPCIInterruptForTinyReceiver();
bool enablePCIInterruptForTinyReceiver();
void disablePCIInterruptForTinyReceiver();
bool isTinyReceiverIdle();
bool TinyReceiverDecode();
void printTinyReceiverResultMinimal(Print *aSerial);
void sendFAST(uint8_t aSendPin, uint16_t aCommand, uint_fast8_t aNumberOfRepeats = 0);
void sendFast8BitAndParity(uint8_t aSendPin, uint8_t aCommand, uint_fast8_t aNumberOfRepeats = 0);
void sendONKYO(uint8_t aSendPin, uint16_t aAddress, uint16_t aCommand, uint_fast8_t aNumberOfRepeats = 0, bool aSendNEC2Repeats = false); // Send NEC with 16 bit command, even if aCommand < 0x100
void sendNECMinimal(uint8_t aSendPin, uint16_t aAddress, uint16_t aCommand, uint_fast8_t aNumberOfRepeats = 0)
__attribute__ ((deprecated ("Renamed to sendNEC().")));
void sendNEC(uint8_t aSendPin, uint16_t aAddress, uint16_t aCommand, uint_fast8_t aNumberOfRepeats = 0, bool aSendNEC2Repeats = false);
void sendExtendedNEC(uint8_t aSendPin, uint16_t aAddress, uint16_t aCommand, uint_fast8_t aNumberOfRepeats = 0, bool aSendNEC2Repeats = false);
/*
* Version 2.2.0 - 7/2024
* - New TinyReceiverDecode() function to be used as drop in for IrReceiver.decode().
*
* Version 2.1.0 - 2/2024
* - New sendExtendedNEC() function and new parameter aSendNEC2Repeats.
*
* Version 2.0.0 - 10/2023
* - New TinyIRReceiverData which is filled with address, command and flags.
* - Removed parameters address, command and flags from callback handleReceivedTinyIRData() and printTinyReceiverResultMinimal().
* - Callback function now only enabled if USE_CALLBACK_FOR_TINY_RECEIVER is activated.
*
* Version 1.2.0 - 01/2023
* - Added ONKYO protocol, NEC with 16 bit address and command, instead of 8 bit + 8 bit parity address and command.
* - Renamed functions and macros.
*
* Version 1.1.0 - 01/2023
* - FAST protocol added.
*/
/** @}*/
#endif // _TINY_IR_H
+713
View File
@@ -0,0 +1,713 @@
/*
* TinyIRReceiver.hpp
*
* Receives IR protocol data of NEC protocol using pin change interrupts.
* NEC is the protocol of most cheap remote controls for Arduino.
*
* Parity check is done for address and data.
* On a completely received IR command, the user function handleReceivedIRData(uint8_t aAddress, uint8_t aCommand, uint8_t aFlags)
* is called in interrupt context but with interrupts being enabled to enable use of delay() etc.
* !!!!!!!!!!!!!!!!!!!!!!
* Functions called in interrupt context should be running as short as possible,
* so if you require longer action, save the data (address + command) and handle them in the main loop.
* !!!!!!!!!!!!!!!!!!!!!
* aFlags can contain one of IRDATA_FLAGS_EMPTY, IRDATA_FLAGS_IS_REPEAT and IRDATA_FLAGS_PARITY_FAILED bits
*
* The FAST protocol is a proprietary modified JVC protocol without address, with parity and with a shorter header.
* FAST Protocol characteristics:
* - Bit timing is like NEC or JVC
* - The header is shorter, 3156 vs. 12500
* - No address and 16 bit data, interpreted as 8 bit command and 8 bit inverted command,
* leading to a fixed protocol length of (6 + (16 * 3) + 1) * 526 = 55 * 526 = 28930 microseconds or 29 ms.
* - Repeats are sent as complete frames but in a 50 ms period / with a 21 ms distance.
*
*
* This file is part of IRMP https://github.com/IRMP-org/IRMP.
* This file is part of Arduino-IRremote https://github.com/Arduino-IRremote/Arduino-IRremote.
*
************************************************************************************
* MIT License
*
* Copyright (c) 2022-2024 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.
*
************************************************************************************
*/
/*
* This library can be configured at compile time by the following options / macros:
* For more details see: https://github.com/Arduino-IRremote/Arduino-IRremote#compile-options--macros-for-this-library (scroll down)
*
* - IR_RECEIVE_PIN The pin number for TinyIRReceiver IR input.
* - IR_FEEDBACK_LED_PIN The pin number for TinyIRReceiver feedback LED.
* - NO_LED_FEEDBACK_CODE Disables the feedback LED function. Saves 14 bytes program memory.
* - DISABLE_PARITY_CHECKS Disable parity checks. Saves 48 bytes of program memory.
* - USE_EXTENDED_NEC_PROTOCOL Like NEC, but take the 16 bit address as one 16 bit value and not as 8 bit normal and 8 bit inverted value.
* - USE_ONKYO_PROTOCOL Like NEC, but take the 16 bit address and command each as one 16 bit value and not as 8 bit normal and 8 bit inverted value.
* - USE_FAST_PROTOCOL Use FAST protocol (no address and 16 bit data, interpreted as 8 bit command and 8 bit inverted command) instead of NEC.
* - ENABLE_NEC2_REPEATS Instead of sending / receiving the NEC special repeat code, send / receive the original frame for repeat.
* - USE_CALLBACK_FOR_TINY_RECEIVER Call the fixed function "void handleReceivedTinyIRData()" each time a frame or repeat is received.
*/
#ifndef _TINY_IR_RECEIVER_HPP
#define _TINY_IR_RECEIVER_HPP
#include <Arduino.h>
#if defined(DEBUG) && !defined(LOCAL_DEBUG)
#define LOCAL_DEBUG
#else
//#define LOCAL_DEBUG // This enables debug output only for this file
#endif
/*
* Protocol selection
*/
//#define DISABLE_PARITY_CHECKS // Disable parity checks. Saves 48 bytes of program memory.
//#define USE_EXTENDED_NEC_PROTOCOL // Like NEC, but take the 16 bit address as one 16 bit value and not as 8 bit normal and 8 bit inverted value.
//#define USE_ONKYO_PROTOCOL // Like NEC, but take the 16 bit address and command each as one 16 bit value and not as 8 bit normal and 8 bit inverted value.
//#define USE_FAST_PROTOCOL // Use FAST protocol instead of NEC / ONKYO.
//#define ENABLE_NEC2_REPEATS // Instead of sending / receiving the NEC special repeat code, send / receive the original frame for repeat.
#include "TinyIR.h" // If not defined, it defines IR_RECEIVE_PIN, IR_FEEDBACK_LED_PIN and TINY_RECEIVER_USE_ARDUINO_ATTACH_INTERRUPT
#include "digitalWriteFast.h"
/** \addtogroup TinyReceiver Minimal receiver for NEC and FAST protocol
* @{
*/
#if defined(DEBUG)
#define LOCAL_DEBUG_ATTACH_INTERRUPT
#else
//#define LOCAL_DEBUG_ATTACH_INTERRUPT // to see if attachInterrupt() or static interrupt (by register tweaking) is used
#endif
#if defined(TRACE)
#define LOCAL_TRACE_STATE_MACHINE
#else
//#define LOCAL_TRACE_STATE_MACHINE // to see the state of the ISR (Interrupt Service Routine) state machine
#endif
//#define _IR_MEASURE_TIMING // Activate this if you want to enable internal hardware timing measurement.
//#define _IR_TIMING_TEST_PIN 7
TinyIRReceiverStruct TinyIRReceiverControl;
volatile TinyIRReceiverCallbackDataStruct TinyIRReceiverData;
/*
* Set input pin and output pin definitions etc.
*/
#if defined(IR_INPUT_PIN)
#warning "IR_INPUT_PIN is deprecated, use IR_RECEIVE_PIN"
#define IR_RECEIVE_PIN IR_INPUT_PIN
#endif
#if !defined(IR_RECEIVE_PIN)
#if defined(__AVR_ATtiny1616__) || defined(__AVR_ATtiny3216__) || defined(__AVR_ATtiny3217__)
#warning "IR_RECEIVE_PIN is not defined, so it is set to 10"
#define IR_RECEIVE_PIN 10
#elif defined(__AVR_ATtiny816__)
#warning "IR_RECEIVE_PIN is not defined, so it is set to 14"
#define IR_RECEIVE_PIN 14
#else
#warning "IR_RECEIVE_PIN is not defined, so it is set to 2"
#define IR_RECEIVE_PIN 2
#endif
#endif
#if !defined(IR_FEEDBACK_LED_PIN) && defined(LED_BUILTIN)
#define IR_FEEDBACK_LED_PIN LED_BUILTIN
#endif
#if !( \
(defined(__AVR_ATtiny25__) || defined(__AVR_ATtiny45__) || defined(__AVR_ATtiny85__)) /* ATtinyX5 */ \
|| defined(__AVR_ATtiny88__) /* MH-ET LIVE Tiny88 */ \
|| defined(__AVR_ATmega1280__) || defined(__AVR_ATmega1281__) || defined(__AVR_ATmega2560__) || defined(__AVR_ATmega2561__) \
|| defined(__AVR_ATmega16U4__) || defined(__AVR_ATmega32U4__) \
|| defined(__AVR_ATmega8__) || defined(__AVR_ATmega48__) || defined(__AVR_ATmega48P__) || defined(__AVR_ATmega48PB__) || defined(__AVR_ATmega88P__) || defined(__AVR_ATmega88PB__) \
|| defined(__AVR_ATmega168__) || defined(__AVR_ATmega168PA__) || defined(__AVR_ATmega168PB__) || defined(__AVR_ATmega328__) || defined(__AVR_ATmega328P__) || defined(__AVR_ATmega328PB__) \
/* ATmegas with ports 0,1,2 above and ATtiny167 only 2 pins below */ \
|| ( (defined(__AVR_ATtiny87__) || defined(__AVR_ATtiny167__)) && ( (defined(ARDUINO_AVR_DIGISPARKPRO) && ((IR_RECEIVE_PIN == 3) || (IR_RECEIVE_PIN == 9))) /*ATtinyX7(digisparkpro) and pin 3 or 9 */\
|| (! defined(ARDUINO_AVR_DIGISPARKPRO) && ((IR_RECEIVE_PIN == 3) || (IR_RECEIVE_PIN == 14)))) ) /*ATtinyX7(ATTinyCore) and pin 3 or 14 */ \
)
#define TINY_RECEIVER_USE_ARDUINO_ATTACH_INTERRUPT // Cannot use any static ISR vector here. In other cases we have code provided for generating interrupt on pin change.
#endif
/**
* Declaration of the callback function provided by the user application.
* It is called every time a complete IR command or repeat was received.
*/
extern void handleTinyReceivedIRData();
#if defined(LOCAL_DEBUG)
uint32_t sMicrosOfGap; // The length of the gap before the start bit
#endif
/**
* The ISR (Interrupt Service Routine) of TinyIRRreceiver.
* It handles the NEC protocol decoding and calls the user callback function on complete.
* 5 us + 3 us for push + pop for a 16MHz ATmega
*/
#if defined(ESP8266) || defined(ESP32)
IRAM_ATTR
#endif
void IRPinChangeInterruptHandler(void) {
#if defined(_IR_MEASURE_TIMING) && defined(_IR_TIMING_TEST_PIN)
digitalWriteFast(_IR_TIMING_TEST_PIN, HIGH); // 2 clock cycles
#endif
/*
* Save IR input level
* Negative logic, true / HIGH means inactive / IR space, LOW / false means IR mark.
*/
uint_fast8_t tIRLevel = digitalReadFast(IR_RECEIVE_PIN);
#if !defined(NO_LED_FEEDBACK_CODE) && defined(IR_FEEDBACK_LED_PIN)
digitalWriteFast(IR_FEEDBACK_LED_PIN, !tIRLevel);
#endif
/*
* 1. compute microseconds after last change
*/
// Repeats can be sent after a pause, which is longer than 64000 microseconds, so we need a 32 bit value for check of repeats
uint32_t tCurrentMicros = micros();
uint32_t tMicrosOfMarkOrSpace32 = tCurrentMicros - TinyIRReceiverControl.LastChangeMicros;
uint16_t tMicrosOfMarkOrSpace = tMicrosOfMarkOrSpace32;
TinyIRReceiverControl.LastChangeMicros = tCurrentMicros;
uint8_t tState = TinyIRReceiverControl.IRReceiverState;
#if defined(LOCAL_TRACE_STATE_MACHINE)
Serial.print(tState);
Serial.print(F(" D="));
Serial.print(tMicrosOfMarkOrSpace);
// Serial.print(F(" I="));
// Serial.print(tIRLevel);
Serial.print('|');
#endif
if (tIRLevel == LOW) {
/*
* We have a mark here
*/
if (tMicrosOfMarkOrSpace > 2 * TINY_RECEIVER_HEADER_MARK) {
// timeout -> must reset state machine
tState = IR_RECEIVER_STATE_WAITING_FOR_START_MARK;
}
if (tState == IR_RECEIVER_STATE_WAITING_FOR_START_MARK) {
// We are at the beginning of the header mark, check timing at the next transition
tState = IR_RECEIVER_STATE_WAITING_FOR_START_SPACE;
TinyIRReceiverControl.Flags = IRDATA_FLAGS_EMPTY; // If we do it here, it saves 4 bytes
#if defined(LOCAL_TRACE)
sMicrosOfGap = tMicrosOfMarkOrSpace32;
#endif
#if defined(ENABLE_NEC2_REPEATS)
// Check for repeat, where full frame is sent again after TINY_RECEIVER_REPEAT_PERIOD ms
// Not required for NEC, where repeats are detected by a special header space duration
// Must use 32 bit arithmetic here!
if (tMicrosOfMarkOrSpace32 < TINY_RECEIVER_MAXIMUM_REPEAT_DISTANCE) {
TinyIRReceiverControl.Flags = IRDATA_FLAGS_IS_REPEAT;
}
#endif
}
else if (tState == IR_RECEIVER_STATE_WAITING_FOR_FIRST_DATA_MARK) {
if (tMicrosOfMarkOrSpace >= lowerValue25Percent(TINY_RECEIVER_HEADER_SPACE)
&& tMicrosOfMarkOrSpace <= upperValue25Percent(TINY_RECEIVER_HEADER_SPACE)) {
/*
* We have a valid data header space here -> initialize data
*/
TinyIRReceiverControl.IRRawDataBitCounter = 0;
#if (TINY_RECEIVER_BITS > 16)
TinyIRReceiverControl.IRRawData.ULong = 0;
#else
TinyIRReceiverControl.IRRawData.UWord = 0;
#endif
TinyIRReceiverControl.IRRawDataMask = 1;
tState = IR_RECEIVER_STATE_WAITING_FOR_DATA_SPACE;
#if !defined(ENABLE_NEC2_REPEATS)
// Check for NEC repeat header
} else if (tMicrosOfMarkOrSpace >= lowerValue25Percent(NEC_REPEAT_HEADER_SPACE)
&& tMicrosOfMarkOrSpace <= upperValue25Percent(NEC_REPEAT_HEADER_SPACE)
&& TinyIRReceiverControl.IRRawDataBitCounter >= TINY_RECEIVER_BITS) {
/*
* We have a repeat header here and no broken receive before -> set repeat flag
*/
TinyIRReceiverControl.Flags = IRDATA_FLAGS_IS_REPEAT;
tState = IR_RECEIVER_STATE_WAITING_FOR_DATA_SPACE;
#endif
} else {
// This parts are optimized by the compiler into jumps to one code :-)
// Wrong length -> reset state
tState = IR_RECEIVER_STATE_WAITING_FOR_START_MARK;
}
}
else if (tState == IR_RECEIVER_STATE_WAITING_FOR_DATA_MARK) {
// Check data space length
if (tMicrosOfMarkOrSpace >= lowerValue50Percent(TINY_RECEIVER_ZERO_SPACE)
&& tMicrosOfMarkOrSpace <= upperValue50Percent(TINY_RECEIVER_ONE_SPACE)) {
// We have a valid bit here
tState = IR_RECEIVER_STATE_WAITING_FOR_DATA_SPACE;
if (tMicrosOfMarkOrSpace >= 2 * TINY_RECEIVER_UNIT) {
// we received a 1
#if (TINY_RECEIVER_BITS > 16)
TinyIRReceiverControl.IRRawData.ULong |= TinyIRReceiverControl.IRRawDataMask;
#else
TinyIRReceiverControl.IRRawData.UWord |= TinyIRReceiverControl.IRRawDataMask;
#endif
} else {
// we received a 0 - empty code for documentation
}
// prepare for next bit
TinyIRReceiverControl.IRRawDataMask = TinyIRReceiverControl.IRRawDataMask << 1;
TinyIRReceiverControl.IRRawDataBitCounter++;
} else {
// Wrong length -> reset state
tState = IR_RECEIVER_STATE_WAITING_FOR_START_MARK;
}
} else {
// error wrong state for the received level, e.g. if we missed one change interrupt -> reset state
tState = IR_RECEIVER_STATE_WAITING_FOR_START_MARK;
}
}
else {
/*
* We have a space here
*/
if (tState == IR_RECEIVER_STATE_WAITING_FOR_START_SPACE) {
/*
* Check length of header mark here
*/
if (tMicrosOfMarkOrSpace >= lowerValue25Percent(TINY_RECEIVER_HEADER_MARK)
&& tMicrosOfMarkOrSpace <= upperValue25Percent(TINY_RECEIVER_HEADER_MARK)) {
tState = IR_RECEIVER_STATE_WAITING_FOR_FIRST_DATA_MARK;
} else {
// Wrong length of header mark -> reset state
tState = IR_RECEIVER_STATE_WAITING_FOR_START_MARK;
}
}
else if (tState == IR_RECEIVER_STATE_WAITING_FOR_DATA_SPACE) {
// Check data mark length
if (tMicrosOfMarkOrSpace >= lowerValue50Percent(TINY_RECEIVER_BIT_MARK)
&& tMicrosOfMarkOrSpace <= upperValue50Percent(TINY_RECEIVER_BIT_MARK)) {
/*
* We have a valid mark here, check for transmission complete, i.e. the mark of the stop bit
*/
if (TinyIRReceiverControl.IRRawDataBitCounter >= TINY_RECEIVER_BITS
#if !defined(ENABLE_NEC2_REPEATS)
|| (TinyIRReceiverControl.Flags & IRDATA_FLAGS_IS_REPEAT) // Do not check for full length received, if we have a short repeat frame
#endif
) {
/*
* Code complete -> optionally check parity
*/
// Reset state for new start
tState = IR_RECEIVER_STATE_WAITING_FOR_START_MARK;
#if !defined(DISABLE_PARITY_CHECKS) && (TINY_RECEIVER_ADDRESS_BITS == 16) && TINY_RECEIVER_ADDRESS_HAS_8_BIT_PARITY
/*
* Check address parity
* Address is sent first and contained in the lower word
*/
if (TinyIRReceiverControl.IRRawData.UBytes[0] != (uint8_t) (~TinyIRReceiverControl.IRRawData.UBytes[1])) {
#if defined(ENABLE_NEC2_REPEATS)
TinyIRReceiverControl.Flags |= IRDATA_FLAGS_PARITY_FAILED; // here we can have the repeat flag already set
#else
TinyIRReceiverControl.Flags = IRDATA_FLAGS_PARITY_FAILED; // here we do not check anything, if we have a repeat
#endif
}
#endif
#if !defined(DISABLE_PARITY_CHECKS) && (TINY_RECEIVER_COMMAND_BITS == 16) && TINY_RECEIVER_COMMAND_HAS_8_BIT_PARITY
/*
* Check command parity
*/
#if (TINY_RECEIVER_ADDRESS_BITS > 0)
if (TinyIRReceiverControl.IRRawData.UBytes[2] != (uint8_t) (~TinyIRReceiverControl.IRRawData.UBytes[3])) {
#if defined(ENABLE_NEC2_REPEATS)
TinyIRReceiverControl.Flags |= IRDATA_FLAGS_PARITY_FAILED;
#else
TinyIRReceiverControl.Flags = IRDATA_FLAGS_PARITY_FAILED;
#endif
# if defined(LOCAL_DEBUG)
Serial.print(F("Parity check for command failed. Command="));
Serial.print(TinyIRReceiverControl.IRRawData.UBytes[2], HEX);
Serial.print(F(" parity="));
Serial.println(TinyIRReceiverControl.IRRawData.UBytes[3], HEX);
# endif
#else
// No address, so command and parity are in the lowest bytes
if (TinyIRReceiverControl.IRRawData.UBytes[0] != (uint8_t) (~TinyIRReceiverControl.IRRawData.UBytes[1])) {
TinyIRReceiverControl.Flags |= IRDATA_FLAGS_PARITY_FAILED;
# if defined(LOCAL_DEBUG)
Serial.print(F("Parity check for command failed. Command="));
Serial.print(TinyIRReceiverControl.IRRawData.UBytes[0], HEX);
Serial.print(F(" parity="));
Serial.println(TinyIRReceiverControl.IRRawData.UBytes[1], HEX);
# endif
#endif
}
#endif
/*
* Call user provided callback here
* The parameter size is dependent of the code variant used in order to save program memory.
* We have 6 cases: 0, 8 bit or 16 bit address, each with 8 or 16 bit command
*/
#if !defined(ARDUINO_ARCH_MBED) && !defined(ESP32) // no Serial etc. in callback for ESP -> no interrupt required, WDT is running!
interrupts(); // enable interrupts, so delay() etc. works in callback
#endif
TinyIRReceiverData.justWritten = true;
TinyIRReceiverData.Flags = TinyIRReceiverControl.Flags;
#if (TINY_RECEIVER_ADDRESS_BITS > 0)
# if TINY_RECEIVER_ADDRESS_HAS_8_BIT_PARITY
// Here we have 8 bit address
TinyIRReceiverData.Address = TinyIRReceiverControl.IRRawData.UBytes[0];
# else
// Here we have 16 bit address
TinyIRReceiverData.Address = TinyIRReceiverControl.IRRawData.UWord.LowWord;
# endif
# if TINY_RECEIVER_COMMAND_HAS_8_BIT_PARITY
// Here we have 8 bit command
TinyIRReceiverData.Command = TinyIRReceiverControl.IRRawData.UBytes[2];
# else
// Here we have 16 bit command
TinyIRReceiverData.Command = TinyIRReceiverControl.IRRawData.UWord.HighWord;
# endif
#else
// Here we have NO address
# if TINY_RECEIVER_COMMAND_HAS_8_BIT_PARITY
// Here we have 8 bit command
TinyIRReceiverData.Command = TinyIRReceiverControl.IRRawData.UBytes[0];
# else
// Here we have 16 bit command
TinyIRReceiverData.Command = TinyIRReceiverControl.IRRawData.UWord;
# endif
#endif
#if defined(USE_CALLBACK_FOR_TINY_RECEIVER)
handleReceivedTinyIRData();
#endif
} else {
// not finished yet
tState = IR_RECEIVER_STATE_WAITING_FOR_DATA_MARK;
}
} else {
// Wrong length -> reset state
tState = IR_RECEIVER_STATE_WAITING_FOR_START_MARK;
}
} else {
// error wrong state for the received level, e.g. if we missed one change interrupt -> reset state
tState = IR_RECEIVER_STATE_WAITING_FOR_START_MARK;
}
}
TinyIRReceiverControl.IRReceiverState = tState;
#ifdef _IR_MEASURE_TIMING
digitalWriteFast(_IR_TIMING_TEST_PIN, LOW); // 2 clock cycles
#endif
}
bool isTinyReceiverIdle() {
return (TinyIRReceiverControl.IRReceiverState == IR_RECEIVER_STATE_WAITING_FOR_START_MARK);
}
/*
* Function to be used as drop in for IrReceiver.decode()
*/
bool TinyReceiverDecode() {
bool tJustWritten = TinyIRReceiverData.justWritten;
if (tJustWritten) {
TinyIRReceiverData.justWritten = false;
}
return tJustWritten;
}
/*
* Checks if IR_RECEIVE_PIN is connected and high
* @return true, if IR Receiver is attached
*/
bool isIRReceiverAttachedForTinyReceiver() {
pinModeFast(IR_RECEIVE_PIN, OUTPUT);
digitalWriteFast(IR_RECEIVE_PIN, LOW); // discharge pin capacity
pinModeFast(IR_RECEIVE_PIN, INPUT);
return digitalRead(IR_RECEIVE_PIN); // use slow digitalRead here, since the pin capacity is not fully charged again if we use digitalReadFast.
}
/**
* Sets IR_RECEIVE_PIN mode to INPUT, and if IR_FEEDBACK_LED_PIN is defined, sets feedback LED output mode.
* Then call enablePCIInterruptForTinyReceiver()
*/
bool initPCIInterruptForTinyReceiver() {
pinModeFast(IR_RECEIVE_PIN, INPUT);
#if !defined(NO_LED_FEEDBACK_CODE) && defined(IR_FEEDBACK_LED_PIN)
pinModeFast(IR_FEEDBACK_LED_PIN, OUTPUT);
#endif
return enablePCIInterruptForTinyReceiver();
}
void printTinyReceiverResultMinimal(Print *aSerial) {
// Print only very short output, since we are in an interrupt context and do not want to miss the next interrupts of the repeats coming soon
// Print only very short output, since we are in an interrupt context and do not want to miss the next interrupts of the repeats coming soon
#if defined(USE_FAST_PROTOCOL)
aSerial->print(F("C=0x"));
#else
aSerial->print(F("A=0x"));
aSerial->print(TinyIRReceiverData.Address, HEX);
aSerial->print(F(" C=0x"));
#endif
aSerial->print(TinyIRReceiverData.Command, HEX);
if (TinyIRReceiverData.Flags == IRDATA_FLAGS_IS_REPEAT) {
aSerial->print(F(" R"));
}
#if !defined(DISABLE_PARITY_CHECKS)
if (TinyIRReceiverData.Flags == IRDATA_FLAGS_PARITY_FAILED) {
aSerial->print(F(" P"));
}
#endif
aSerial->println();
}
#if defined (LOCAL_DEBUG_ATTACH_INTERRUPT) && !defined(STR)
// Helper macro for getting a macro definition as string
#define STR_HELPER(x) #x
#define STR(x) STR_HELPER(x)
#endif
/**************************************************
* Pin to interrupt mapping for different platforms
**************************************************/
#if defined(__AVR_ATtiny816__) || defined(__AVR_ATtiny1616__) || defined(__AVR_ATtiny3216__) || defined(__AVR_ATtiny3217__)
#define USE_ATTACH_INTERRUPT_DIRECT
#elif !defined(__AVR__) || defined(TINY_RECEIVER_USE_ARDUINO_ATTACH_INTERRUPT)
// Default for all NON AVR platforms
#define USE_ATTACH_INTERRUPT
#else
# if defined(__AVR_ATtiny25__) || defined(__AVR_ATtiny45__) || defined(__AVR_ATtiny85__)
#define USE_PCIE
# elif defined(__AVR_ATtiny87__) || defined(__AVR_ATtiny167__)
# if defined(ARDUINO_AVR_DIGISPARKPRO)
# if (IR_RECEIVE_PIN == 3)
#define USE_INT0
# elif (IR_RECEIVE_PIN == 9)
#define USE_INT1
# else
# error "IR_RECEIVE_PIN must be 9 or 3."
# endif // if (IR_RECEIVE_PIN == 9)
# else // defined(ARDUINO_AVR_DIGISPARKPRO)
# if (IR_RECEIVE_PIN == 14)
#define USE_INT0
# elif (IR_RECEIVE_PIN == 3)
#define USE_INT1
# else
# error "IR_RECEIVE_PIN must be 14 or 3."
# endif // if (IR_RECEIVE_PIN == 14)
# endif
# elif (defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__))
# if (IR_RECEIVE_PIN == 21)
#define USE_INT0
# elif (IR_RECEIVE_PIN == 20)
#define USE_INT1
# else
#warning "No pin mapping for IR_RECEIVE_PIN to interrupt found -> attachInterrupt() is used now."
#define USE_ATTACH_INTERRUPT
# endif
# else // defined(__AVR_ATtiny25__)
/*
* ATmegas + ATtiny88 here
*/
# if (IR_RECEIVE_PIN == 2)
#define USE_INT0
# elif (IR_RECEIVE_PIN == 3)
#define USE_INT1
# elif IR_RECEIVE_PIN == 4 || IR_RECEIVE_PIN == 5 || IR_RECEIVE_PIN == 6 || IR_RECEIVE_PIN == 7
//ATmega328 (Uno, Nano ) etc. Enable pin change interrupt 20 to 23 for port PD4 to PD7 (Arduino pin 4 to 7)
#define USE_PCINT2
# elif IR_RECEIVE_PIN == 8 || IR_RECEIVE_PIN == 9 || IR_RECEIVE_PIN == 10 || IR_RECEIVE_PIN == 11 || IR_RECEIVE_PIN == 12 || IR_RECEIVE_PIN == 13
//ATmega328 (Uno, Nano ) etc. Enable pin change interrupt 0 to 5 for port PB0 to PB5 (Arduino pin 8 to 13)
#define USE_PCINT0
# elif IR_RECEIVE_PIN == A0 || IR_RECEIVE_PIN == A1 || IR_RECEIVE_PIN == A2 || IR_RECEIVE_PIN == A3 || IR_RECEIVE_PIN == A4 || IR_RECEIVE_PIN == A5
//ATmega328 (Uno, Nano ) etc. Enable pin change interrupt 8 to 13 for port PC0 to PC5 (Arduino pin A0 to A5)
#define USE_PCINT1
# else
#warning "No pin mapping for IR_RECEIVE_PIN to interrupt found -> attachInterrupt() is used now."
#define USE_ATTACH_INTERRUPT
# endif // if (IR_RECEIVE_PIN == 2)
# endif // defined(__AVR_ATtiny25__)
#endif // ! defined(__AVR__) || defined(TINY_RECEIVER_USE_ARDUINO_ATTACH_INTERRUPT)
/**
* Initializes hardware interrupt generation according to IR_RECEIVE_PIN or use attachInterrupt() function.
* @return true if interrupt was successfully enabled
*/
bool enablePCIInterruptForTinyReceiver() {
#if defined(_IR_MEASURE_TIMING) && defined(_IR_TIMING_TEST_PIN)
pinModeFast(_IR_TIMING_TEST_PIN, OUTPUT);
#endif
#if defined(USE_ATTACH_INTERRUPT) || defined(USE_ATTACH_INTERRUPT_DIRECT)
# if defined(USE_ATTACH_INTERRUPT)
#if defined(NOT_AN_INTERRUPT)
if(digitalPinToInterrupt(IR_RECEIVE_PIN) == NOT_AN_INTERRUPT){
return false;
}
#endif
// costs 112 bytes program memory + 4 bytes RAM
attachInterrupt(digitalPinToInterrupt(IR_RECEIVE_PIN), IRPinChangeInterruptHandler, CHANGE);
# else
// 2.2 us more than version configured with macros and not compatible
attachInterrupt(IR_RECEIVE_PIN, IRPinChangeInterruptHandler, CHANGE); // no extra pin mapping here
# endif
# if defined(LOCAL_DEBUG_ATTACH_INTERRUPT)
Serial.println(F("Use attachInterrupt for pin=" STR(IR_RECEIVE_PIN)));
# endif
#else
# if defined(LOCAL_DEBUG_ATTACH_INTERRUPT)
Serial.println(F("Use static interrupt for pin=" STR(IR_RECEIVE_PIN)));
# endif
# if defined(USE_INT0)
// interrupt on any logical change
EICRA |= _BV(ISC00);
// clear interrupt bit
EIFR |= 1 << INTF0;
// enable interrupt on next change
EIMSK |= 1 << INT0;
# elif defined(USE_INT1)
EICRA |= _BV(ISC10);
// clear interrupt bit
EIFR |= 1 << INTF1;
// enable interrupt on next change
EIMSK |= 1 << INT1;
# elif defined(USE_PCIE) // For ATtiny85 etc.
// use PinChangeInterrupt no INT0 for pin PB2
PCMSK = _BV(IR_RECEIVE_PIN);
// clear interrupt bit
GIFR |= 1 << PCIF;
// enable interrupt on next change
GIMSK |= 1 << PCIE;
# elif defined(USE_PCINT0)
PCICR |= _BV(PCIE0);
PCMSK0 = digitalPinToBitMask(IR_RECEIVE_PIN);
# elif defined(USE_PCINT1)
PCICR |= _BV(PCIE1);
PCMSK1 = digitalPinToBitMask(IR_RECEIVE_PIN);
# elif defined(USE_PCINT2)
PCICR |= _BV(PCIE2);
PCMSK2 = digitalPinToBitMask(IR_RECEIVE_PIN);
# else
return false;
# endif
#endif // defined(USE_ATTACH_INTERRUPT)
return true;
}
void disablePCIInterruptForTinyReceiver() {
#if defined(_IR_MEASURE_TIMING) && defined(_IR_TIMING_TEST_PIN)
pinModeFast(_IR_TIMING_TEST_PIN, OUTPUT);
#endif
#if defined(USE_ATTACH_INTERRUPT) || defined(USE_ATTACH_INTERRUPT_DIRECT)
# if defined(USE_ATTACH_INTERRUPT)
detachInterrupt(digitalPinToInterrupt(IR_RECEIVE_PIN));
# else
detachInterrupt(IR_RECEIVE_PIN);
# endif
#else
# if defined(USE_INT0)
// clear interrupt bit
EIFR |= 1 << INTF0;
// disable interrupt on next change
EIMSK &= ~(1 << INT0);
# elif defined(USE_INT1)
// clear interrupt bit
EIFR |= 1 << INTF1;
// disable interrupt on next change
EIMSK &= ~(1 << INT1);
# elif defined(USE_PCIE) // For ATtiny85 etc.
// clear interrupt bit
GIFR |= 1 << PCIF;
// disable interrupt on next change
GIMSK &= ~(1 << PCIE);
# elif defined(USE_PCINT0)
PCICR &= ~(_BV(PCIE0));
# elif defined(USE_PCINT1)
PCICR &= ~(_BV(PCIE1));
# elif defined(USE_PCINT2)
PCICR &= ~(_BV(PCIE2));
# endif
#endif // defined(USE_ATTACH_INTERRUPT)
}
/*
* Specify the right INT0, INT1 or PCINT0 interrupt vector according to different pins and cores.
* The default value of TINY_RECEIVER_USE_ARDUINO_ATTACH_INTERRUPT is set in TinyIRReceiver.h
*/
#if !(defined(USE_ATTACH_INTERRUPT) || defined(USE_ATTACH_INTERRUPT_DIRECT))
# if defined(USE_INT0)
ISR(INT0_vect)
# elif defined(USE_INT1)
ISR(INT1_vect)
# elif defined(USE_PCIE) // For ATtiny85 etc.
// on ATtinyX5 we do not have a INT1_vect but we can use the PCINT0_vect
ISR(PCINT0_vect)
# elif defined(USE_PCINT0)
ISR(PCINT0_vect)
# elif defined(USE_PCINT1)
ISR(PCINT1_vect)
# elif defined(USE_PCINT2)
ISR(PCINT2_vect)
# else
void dummyFunctionToAvoidCompilerErrors()
# endif
{
IRPinChangeInterruptHandler();
}
#endif // !(defined(USE_ATTACH_INTERRUPT) || defined(USE_ATTACH_INTERRUPT_DIRECT))
/** @}*/
#if defined(LOCAL_DEBUG_ATTACH_INTERRUPT)
#undef LOCAL_DEBUG_ATTACH_INTERRUPT
#endif
#if defined(LOCAL_TRACE_STATE_MACHINE)
#undef LOCAL_TRACE_STATE_MACHINE
#endif
#if defined(LOCAL_DEBUG)
#undef LOCAL_DEBUG
#endif
#endif // _TINY_IR_RECEIVER_HPP
+347
View File
@@ -0,0 +1,347 @@
/*
* TinyIRSender.hpp
*
* Sends IR protocol data of NEC and FAST protocol using bit banging.
* NEC is the protocol of most cheap remote controls for Arduino.
*
* The FAST protocol is a proprietary modified JVC protocol without address, with parity and with a shorter header.
* FAST Protocol characteristics:
* - Bit timing is like NEC or JVC
* - The header is shorter, 3156 vs. 12500
* - No address and 16 bit data, interpreted as 8 bit command and 8 bit inverted command,
* leading to a fixed protocol length of (6 + (16 * 3) + 1) * 526 = 55 * 526 = 28930 microseconds or 29 ms.
* - Repeats are sent as complete frames but in a 50 ms period / with a 21 ms distance.
*
*
* This file is part of IRMP https://github.com/IRMP-org/IRMP.
* This file is part of Arduino-IRremote https://github.com/Arduino-IRremote/Arduino-IRremote.
*
************************************************************************************
* MIT License
*
* Copyright (c) 2022-2024 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 _TINY_IR_SENDER_HPP
#define _TINY_IR_SENDER_HPP
#include <Arduino.h>
//#define ENABLE_NEC2_REPEATS // Instead of sending / receiving the NEC special repeat code, send / receive the original frame for repeat.
#if defined(DEBUG) && !defined(LOCAL_DEBUG)
#define LOCAL_DEBUG
#else
//#define LOCAL_DEBUG // This enables debug output only for this file
#endif
#include "TinyIR.h" // Defines protocol timings
#include "digitalWriteFast.h"
/** \addtogroup TinySender Minimal sender for NEC and FAST protocol
* @{
*/
#if !defined(IR_SEND_PIN)
#warning "IR_SEND_PIN is not defined, so it is set to 3"
#define IR_SEND_PIN 3
#endif
/*
* Generate 38 kHz IR signal by bit banging
*/
void sendMark(uint8_t aSendPin, unsigned int aMarkMicros) {
unsigned long tStartMicros = micros();
unsigned long tNextPeriodEnding = tStartMicros;
unsigned long tMicros;
do {
/*
* Generate pulse
*/
noInterrupts(); // do not let interrupts extend the short on period
digitalWriteFast(aSendPin, HIGH);
delayMicroseconds(8); // 8 us for a 30 % duty cycle for 38 kHz
digitalWriteFast(aSendPin, LOW);
interrupts(); // Enable interrupts - to keep micros correct- for the longer off period 3.4 us until receive ISR is active (for 7 us + pop's)
/*
* PWM pause timing and end check
* Minimal pause duration is 4.3 us
*/
tNextPeriodEnding += 26; // for 38 kHz
do {
tMicros = micros(); // we have only 4 us resolution for AVR @16MHz
/*
* Exit the forever loop if aMarkMicros has reached
*/
unsigned int tDeltaMicros = tMicros - tStartMicros;
#if defined(__AVR__)
// Just getting variables and check for end condition takes minimal 3.8 us
if (tDeltaMicros >= aMarkMicros - (112 / (F_CPU / MICROS_IN_ONE_SECOND))) { // To compensate for call duration - 112 is an empirical value
#else
if (tDeltaMicros >= aMarkMicros) {
#endif
return;
}
} while (tMicros < tNextPeriodEnding);
} while (true);
}
/*
* Send NEC with 16 bit address and command, even if aCommand < 0x100 (I.E. ONKYO)
* @param aAddress - The 16 bit address to send.
* @param aCommand - The 16 bit command to send.
* @param aNumberOfRepeats - Number of repeats send at a period of 110 ms.
* @param aSendNEC2Repeats - Instead of sending the NEC special repeat code, send the original frame for repeat.
*/
void sendONKYO(uint8_t aSendPin, uint16_t aAddress, uint16_t aCommand, uint_fast8_t aNumberOfRepeats, bool aSendNEC2Repeats) {
pinModeFast(aSendPin, OUTPUT);
uint_fast8_t tNumberOfCommands = aNumberOfRepeats + 1;
while (tNumberOfCommands > 0) {
unsigned long tStartOfFrameMillis = millis();
sendMark(aSendPin, NEC_HEADER_MARK);
if ((!aSendNEC2Repeats) && (tNumberOfCommands < aNumberOfRepeats + 1)) {
// send the NEC special repeat
delayMicroseconds(NEC_REPEAT_HEADER_SPACE); // - 2250
} else {
// send header
delayMicroseconds(NEC_HEADER_SPACE);
LongUnion tData;
tData.UWord.LowWord = aAddress;
tData.UWord.HighWord = aCommand;
// Send data
for (uint_fast8_t i = 0; i < NEC_BITS; ++i) {
sendMark(aSendPin, NEC_BIT_MARK); // constant mark length
if (tData.ULong & 1) {
delayMicroseconds(NEC_ONE_SPACE);
} else {
delayMicroseconds(NEC_ZERO_SPACE);
}
tData.ULong >>= 1; // shift command for next bit
}
} // send stop bit
sendMark(aSendPin, NEC_BIT_MARK);
tNumberOfCommands--;
// skip last delay!
if (tNumberOfCommands > 0) {
/*
* Check and fallback for wrong RepeatPeriodMillis parameter. I.e the repeat period must be greater than each frame duration.
*/
auto tFrameDurationMillis = millis() - tStartOfFrameMillis;
if (NEC_REPEAT_PERIOD / 1000 > tFrameDurationMillis) {
delay(NEC_REPEAT_PERIOD / 1000 - tFrameDurationMillis);
}
}
}
}
/*
* Send NEC with 8 or 16 bit address or command depending on the values of aAddress and aCommand.
* @param aAddress - If aAddress < 0x100 send 8 bit address and 8 bit inverted address, else send 16 bit address.
* @param aCommand - If aCommand < 0x100 send 8 bit command and 8 bit inverted command, else send 16 bit command.
* @param aNumberOfRepeats - Number of repeats send at a period of 110 ms.
* @param aSendNEC2Repeats - Instead of sending the NEC special repeat code, send the original frame for repeat.
*/
void sendNECMinimal(uint8_t aSendPin, uint16_t aAddress, uint16_t aCommand, uint_fast8_t aNumberOfRepeats) {
sendNEC(aSendPin, aAddress, aCommand, aNumberOfRepeats); // sendNECMinimal() is deprecated
}
void sendNEC(uint8_t aSendPin, uint16_t aAddress, uint16_t aCommand, uint_fast8_t aNumberOfRepeats, bool aSendNEC2Repeats) {
pinModeFast(aSendPin, OUTPUT);
uint_fast8_t tNumberOfCommands = aNumberOfRepeats + 1;
while (tNumberOfCommands > 0) {
unsigned long tStartOfFrameMillis = millis();
sendMark(aSendPin, NEC_HEADER_MARK);
if ((!aSendNEC2Repeats) && (tNumberOfCommands < aNumberOfRepeats + 1)) {
// send the NEC special repeat
delayMicroseconds(NEC_REPEAT_HEADER_SPACE); // - 2250
} else {
// send header
delayMicroseconds(NEC_HEADER_SPACE);
LongUnion tData;
/*
* The compiler is intelligent and removes the code for "(aAddress > 0xFF)" if we are called with an uint8_t address :-).
* Using an uint16_t address requires additional 28 bytes program memory.
*/
if (aAddress > 0xFF) {
tData.UWord.LowWord = aAddress;
} else {
tData.UByte.LowByte = aAddress; // LSB first
tData.UByte.MidLowByte = ~aAddress;
}
if (aCommand > 0xFF) {
tData.UWord.HighWord = aCommand;
} else {
tData.UByte.MidHighByte = aCommand;
tData.UByte.HighByte = ~aCommand; // LSB first
}
// Send data
for (uint_fast8_t i = 0; i < NEC_BITS; ++i) {
sendMark(aSendPin, NEC_BIT_MARK); // constant mark length
if (tData.ULong & 1) {
delayMicroseconds(NEC_ONE_SPACE);
} else {
delayMicroseconds(NEC_ZERO_SPACE);
}
tData.ULong >>= 1; // shift command for next bit
}
} // send stop bit
sendMark(aSendPin, NEC_BIT_MARK);
tNumberOfCommands--;
// skip last delay!
if (tNumberOfCommands > 0) {
/*
* Check and fallback for wrong RepeatPeriodMillis parameter. I.e the repeat period must be greater than each frame duration.
*/
auto tFrameDurationMillis = millis() - tStartOfFrameMillis;
if (NEC_REPEAT_PERIOD / 1000 > tFrameDurationMillis) {
delay(NEC_REPEAT_PERIOD / 1000 - tFrameDurationMillis);
}
}
}
}
/*
* Send Extended NEC with a forced 16 bit address and 8 or 16 bit command depending on the value of aCommand.
* @param aAddress - Send 16 bit address.
* @param aCommand - If aCommand < 0x100 send 8 bit command and 8 bit inverted command, else send 16 bit command.
* @param aNumberOfRepeats - Number of repeats send at a period of 110 ms.
* @param aSendNEC2Repeats - Instead of sending the NEC special repeat code, send the original frame for repeat.
*/
void sendExtendedNEC(uint8_t aSendPin, uint16_t aAddress, uint16_t aCommand, uint_fast8_t aNumberOfRepeats, bool aSendNEC2Repeats) {
pinModeFast(aSendPin, OUTPUT);
uint_fast8_t tNumberOfCommands = aNumberOfRepeats + 1;
while (tNumberOfCommands > 0) {
unsigned long tStartOfFrameMillis = millis();
sendMark(aSendPin, NEC_HEADER_MARK);
if ((!aSendNEC2Repeats) && (tNumberOfCommands < aNumberOfRepeats + 1)) {
// send the NEC special repeat
delayMicroseconds(NEC_REPEAT_HEADER_SPACE); // - 2250
} else {
// send header
delayMicroseconds(NEC_HEADER_SPACE);
LongUnion tData;
tData.UWord.LowWord = aAddress;
if (aCommand > 0xFF) {
tData.UWord.HighWord = aCommand;
} else {
tData.UByte.MidHighByte = aCommand;
tData.UByte.HighByte = ~aCommand; // LSB first
}
// Send data
for (uint_fast8_t i = 0; i < NEC_BITS; ++i) {
sendMark(aSendPin, NEC_BIT_MARK); // constant mark length
if (tData.ULong & 1) {
delayMicroseconds(NEC_ONE_SPACE);
} else {
delayMicroseconds(NEC_ZERO_SPACE);
}
tData.ULong >>= 1; // shift command for next bit
}
} // send stop bit
sendMark(aSendPin, NEC_BIT_MARK);
tNumberOfCommands--;
// skip last delay!
if (tNumberOfCommands > 0) {
/*
* Check and fallback for wrong RepeatPeriodMillis parameter. I.e the repeat period must be greater than each frame duration.
*/
auto tFrameDurationMillis = millis() - tStartOfFrameMillis;
if (NEC_REPEAT_PERIOD / 1000 > tFrameDurationMillis) {
delay(NEC_REPEAT_PERIOD / 1000 - tFrameDurationMillis);
}
}
}
}
/*
* LSB first, send header, command, inverted command and stop bit
*/
void sendFast8BitAndParity(uint8_t aSendPin, uint8_t aCommand, uint_fast8_t aNumberOfRepeats) {
sendFAST(aSendPin, aCommand, aNumberOfRepeats);
}
/*
* LSB first, send header, 16 bit command or 8 bit command, inverted command and stop bit
*/
void sendFAST(uint8_t aSendPin, uint16_t aCommand, uint_fast8_t aNumberOfRepeats) {
pinModeFast(aSendPin, OUTPUT);
uint_fast8_t tNumberOfCommands = aNumberOfRepeats + 1;
while (tNumberOfCommands > 0) {
unsigned long tStartOfFrameMillis = millis();
// send header
sendMark(aSendPin, FAST_HEADER_MARK);
delayMicroseconds(FAST_HEADER_SPACE);
uint16_t tData;
/*
* The compiler is intelligent and removes the code for "(aCommand > 0xFF)" if we are called with an uint8_t command :-).
* Using an uint16_t command requires additional 56 bytes program memory.
*/
if (aCommand > 0xFF) {
tData = aCommand;
} else {
tData = aCommand | (((uint8_t) (~aCommand)) << 8); // LSB first
}
// Send data
for (uint_fast8_t i = 0; i < FAST_BITS; ++i) {
sendMark(aSendPin, FAST_BIT_MARK); // constant mark length
if (tData & 1) {
delayMicroseconds(FAST_ONE_SPACE);
} else {
delayMicroseconds(FAST_ZERO_SPACE);
}
tData >>= 1; // shift command for next bit
}
// send stop bit
sendMark(aSendPin, FAST_BIT_MARK);
tNumberOfCommands--;
// skip last delay!
if (tNumberOfCommands > 0) {
/*
* Check and fallback for wrong RepeatPeriodMillis parameter. I.e the repeat period must be greater than each frame duration.
*/
auto tFrameDurationMillis = millis() - tStartOfFrameMillis;
if (FAST_REPEAT_PERIOD / 1000 > tFrameDurationMillis) {
delay(FAST_REPEAT_PERIOD / 1000 - tFrameDurationMillis);
}
}
}
}
/** @}*/
#if defined(LOCAL_DEBUG)
#undef LOCAL_DEBUG
#endif
#endif // _TINY_IR_SENDER_HPP
+138
View File
@@ -0,0 +1,138 @@
/*
* ac_LG.h
*
* Contains definitions for receiving and sending LG air conditioner IR Protocol
*
* This file is part of Arduino-IRremote https://github.com/Arduino-IRremote/Arduino-IRremote.
*
************************************************************************************
* MIT License
*
* Copyright (c) 2021 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.
*
************************************************************************************
*/
// see also: https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_LG.h
#ifndef _AC_LG_H
#define _AC_LG_H
#include <Arduino.h>
/** \addtogroup Airconditoners Air conditioner special code
* @{
*/
#define LG_ADDRESS 0x88
/*
* The basic IR command codes
* Parts of the codes (especially the lower nibbles) may be modified to contain
* additional information like temperature, fan speed and minutes.
*/
#define LG_SWITCH_ON_MASK 0x0800 // This bit is masked if we switch Power on
#define LG_MODE_COOLING 0x0800 // Temperature and fan speed in lower nibbles
#define LG_MODE_DEHUMIDIFIYING 0x0990 // sets also temperature to 24 and fan speed to 0
#define LG_MODE_FAN 0x0A30 // sets also temperature to 18
#define LG_MODE_AUTO 0x0B00 // The remote initially sets also temperature to 22 and fan speed to 4
#define LG_MODE_HEATING 0x0C00 // Temperature and fan speed in lower nibbles
#define LG_ENERGY_SAVING_ON 0x1004
#define LG_ENERGY_SAVING_OFF 0x1005
#define LG_JET_ON 0x1008
#define LG_WALL_SWING_ON 0x1314
#define LG_WALL_SWING_OFF 0x1315
#define LG_SWING_ON 0x1316 // not verified, for AKB73757604
#define LG_SWING_OFF 0x1317 // not verified, for AKB73757604
#define LG_TIMER_ON 0x8000 // relative minutes in lower nibbles
#define LG_TIMER_OFF 0x9000 // relative minutes in lower nibbles
#define LG_SLEEP 0xA000 // relative minutes in lower nibbles
#define LG_CLEAR_ALL 0xB000 // Timers and sleep
#define LG_POWER_DOWN 0xC005
#define LG_LIGHT 0xC00A
#define LG_AUTO_CLEAN_ON 0xC00B
#define LG_AUTO_CLEAN_OFF 0xC00C
/*
* Commands as printed in menu and uses as first parameter for sendCommandAndParameter
*/
#define LG_COMMAND_OFF '0'
#define LG_COMMAND_ON '1'
#define LG_COMMAND_SWING 's'
#define LG_COMMAND_AUTO_CLEAN 'a'
#define LG_COMMAND_JET 'j'
#define LG_COMMAND_ENERGY 'e'
#define LG_COMMAND_LIGHT 'l'
#define LG_COMMAND_FAN_SPEED 'f'
#define LG_COMMAND_TEMPERATURE 't'
#define LG_COMMAND_TEMPERATURE_PLUS '+'
#define LG_COMMAND_TEMPERATURE_MINUS '-'
#define LG_COMMAND_MODE 'm'
#define LG_COMMAND_SLEEP 'S'
#define LG_COMMAND_TIMER_ON 'T'
#define LG_COMMAND_TIMER_OFF 'O'
#define LG_COMMAND_CLEAR_ALL 'C'
/*
* The modes are encoded as character values for easy printing :-)
*/
#define AC_MODE_COOLING 'c'
#define AC_MODE_DEHUMIDIFIYING 'd'
#define AC_MODE_FAN 'f'
#define AC_MODE_AUTO 'a'
#define AC_MODE_HEATING 'h'
// see https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_LG.h
union LGProtocol {
uint32_t raw; ///< The state of the IR remote in IR code form.
struct {
uint32_t Checksum :4;
uint32_t Fan :3;
uint32_t FanExt :1;
uint32_t Temp :4;
uint32_t Mode :4; // highest bit 1 => Set temperature and ventilation by mode
uint32_t Function :3;
uint32_t SwitchOnMask :1; /* Content is 0 when switching from off to on */
uint32_t Signature :8; /* Content is 0x88, LG_ADDRESS */
};
};
class Aircondition_LG {
public:
bool sendCommandAndParameter(char aCommand, int aParameter);
void setType(bool aIsWallType);
void printMenu(Print *aSerial);
void sendIRCommand(uint16_t aCommand);
void sendTemperatureFanSpeedAndMode();
/*
* Internal state of the air condition
*/
#define LG_IS_WALL_TYPE true
#define LG_IS_TOWER_TYPE false
bool ACIsWallType; // false : TOWER, true : WALL
bool PowerIsOn;
// These value are encoded and sent by AC_LG_SendCommandAndParameter()
uint8_t FanIntensity = 1; // 0 -> low, 4 high, 5 -> cycle
uint8_t Temperature = 22; // temperature : 18 ~ 30
uint8_t Mode = AC_MODE_COOLING;
bool useLG2Protocol = false;
};
/** @}*/
#endif // _AC_LG_H
+322
View File
@@ -0,0 +1,322 @@
/*
* ac_LG.hpp
*
* Contains functions for sending LG air conditioner IR Protocol
* There is no state plausibility check, e.g. you can send fan speed in Mode D and change temperature in mode F
*
* This file is part of Arduino-IRremote https://github.com/Arduino-IRremote/Arduino-IRremote.
*
************************************************************************************
* MIT License
*
* Copyright (c) 2021-2022 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 _AC_LG_HPP
#define _AC_LG_HPP
#include <Arduino.h>
#if defined(INFO) && !defined(LOCAL_INFO)
#define LOCAL_INFO
#else
//#define LOCAL_INFO // This enables info output only for this file
#endif
//#define DEBUG // for more output from the LG-AC driver.
#include "IRremoteInt.h"
#include "ac_LG.h" // useful constants
#include "LongUnion.h"
/** \addtogroup Airconditoners Air conditioner special code
* @{
*/
/*
* LG remote measurements: Type AKB73315611, Ver1.1 from 2011.03.01
* Internal crystal: 4 MHz
* Header: 8.9 ms mark 4.15 ms space
* Data: 500 / 540 and 500 / 1580;
* Clock is nor synchronized with gate so you have 19 and sometimes 19 and a spike pulses for mark
* Duty: 9 us on 17 us off => around 33 % duty
* NO REPEAT: If value like temperature has changed during long press, the last value is send at button release
* If you do a double press -tested with the fan button-, the next value can be sent after 118 ms
*/
#define SIZE_OF_FAN_SPEED_MAPPING_TABLE 4
const int AC_FAN_TOWER[SIZE_OF_FAN_SPEED_MAPPING_TABLE] = { 0, 4, 6, 6 }; // last dummy entry to avoid out of bounds access
const int AC_FAN_WALL[SIZE_OF_FAN_SPEED_MAPPING_TABLE] = { 0, 2, 4, 5 }; // 0 -> low, 4 high, 5 -> cycle
void Aircondition_LG::setType(bool aIsWallType) {
ACIsWallType = aIsWallType;
#if defined(LOCAL_INFO)
Serial.print(F("Set wall type to "));
Serial.println(aIsWallType);
#endif
}
void Aircondition_LG::printMenu(Print *aSerial) {
aSerial->println();
aSerial->println();
aSerial->println(F("Type command and optional parameter without a separator"));
aSerial->println(F("0 Off"));
aSerial->println(F("1 On"));
aSerial->println(F("s Swing <0 or 1>"));
aSerial->println(F("a Auto clean <0 or 1>"));
aSerial->println(F("j Jet on"));
aSerial->println(F("e Energy saving <0 or 1>"));
aSerial->println(F("l Lights toggle"));
aSerial->println(F("f Fan speed <0 to 2 or 3 for cycle>"));
aSerial->println(F("t Temperature <18 to 30> degree"));
aSerial->println(F("+ Temperature + 1"));
aSerial->println(F("- Temperature - 1"));
aSerial->println(F("m <c(ool) or a(uto) or d(ehumidifying) or h(eating) or f(an) mode>"));
aSerial->println(F("S Sleep after <0 to 420> minutes"));
aSerial->println(F("T Timer on after <0 to 1439> minutes"));
aSerial->println(F("O Timer off after <0 to 1439> minutes"));
aSerial->println(F("C Clear all timer and sleep"));
aSerial->println(F("e.g. \"s1\" or \"t23\" or \"mc\" or \"O60\" or \"+\""));
aSerial->println(F("No plausibility check is made!"));
aSerial->println();
}
/*
* Send repeat
* Repeat commands should be sent in a 110 ms raster.
* @param aCommand one of LG_COMMAND_OFF, LG_COMMAND_ON etc.
*/
bool Aircondition_LG::sendCommandAndParameter(char aCommand, int aParameter) {
// Commands without parameter
switch (aCommand) {
case LG_COMMAND_OFF: // off
sendIRCommand(LG_POWER_DOWN);
PowerIsOn = false;
return true;
case LG_COMMAND_ON: // on
PowerIsOn = false; // set to false in order to suppress on bit
sendTemperatureFanSpeedAndMode();
return true;
case LG_COMMAND_JET:
IR_DEBUG_PRINTLN(F("Send jet on"));
sendIRCommand(LG_JET_ON);
return true;
case LG_COMMAND_LIGHT:
sendIRCommand(LG_LIGHT);
return true;
case LG_COMMAND_CLEAR_ALL:
sendIRCommand(LG_CLEAR_ALL);
return true;
case LG_COMMAND_TEMPERATURE_PLUS:
if (18 <= Temperature && Temperature <= 29) {
Temperature++;
sendTemperatureFanSpeedAndMode();
} else {
return false;
}
return true;
case LG_COMMAND_TEMPERATURE_MINUS:
if (19 <= Temperature && Temperature <= 30) {
Temperature--;
sendTemperatureFanSpeedAndMode();
} else {
return false;
}
return true;
}
PowerIsOn = true;
/*
* Now the commands which require a parameter
*/
if (aParameter < 0) {
IR_DEBUG_PRINT(F("Error: Parameter is less than 0"));
return false;
}
switch (aCommand) {
case LG_COMMAND_MODE:
Mode = aParameter + '0';
sendTemperatureFanSpeedAndMode();
break;
case LG_COMMAND_SWING:
IR_DEBUG_PRINT(F("Send air swing="));
IR_DEBUG_PRINTLN(aParameter);
if (ACIsWallType) {
if (aParameter) {
sendIRCommand(LG_WALL_SWING_ON);
} else {
sendIRCommand(LG_WALL_SWING_OFF);
}
} else {
if (aParameter) {
sendIRCommand(LG_SWING_ON);
} else {
sendIRCommand(LG_SWING_OFF);
}
}
break;
case LG_COMMAND_AUTO_CLEAN:
IR_DEBUG_PRINT(F("Send auto clean="));
IR_DEBUG_PRINTLN(aParameter);
if (aParameter) {
sendIRCommand(LG_AUTO_CLEAN_ON);
} else {
sendIRCommand(LG_AUTO_CLEAN_OFF);
}
break;
case LG_COMMAND_ENERGY:
IR_DEBUG_PRINT(F("Send energy saving on="));
IR_DEBUG_PRINTLN(aParameter);
if (aParameter) {
sendIRCommand(LG_ENERGY_SAVING_ON);
} else {
sendIRCommand(LG_ENERGY_SAVING_OFF);
}
break;
case LG_COMMAND_FAN_SPEED:
if (aParameter < SIZE_OF_FAN_SPEED_MAPPING_TABLE) {
FanIntensity = aParameter;
sendTemperatureFanSpeedAndMode();
} else {
return false;
}
break;
case LG_COMMAND_TEMPERATURE:
if (18 <= aParameter && aParameter <= 30) {
Temperature = aParameter;
sendTemperatureFanSpeedAndMode();
} else {
return false;
}
break;
case LG_COMMAND_SLEEP:
// 420 = maximum I have recorded
if (aParameter <= 420) {
sendIRCommand(LG_SLEEP + aParameter);
} else {
return false;
}
break;
case LG_COMMAND_TIMER_ON:
// 1440 = minutes of a day
if (aParameter <= 1439) {
sendIRCommand(LG_TIMER_ON + aParameter);
} else {
return false;
}
break;
case LG_COMMAND_TIMER_OFF:
if (aParameter <= 1439) {
sendIRCommand(LG_TIMER_OFF + aParameter);
} else {
return false;
}
break;
default:
return false;
}
return true;
}
void Aircondition_LG::sendIRCommand(uint16_t aCommand) {
#if defined(LOCAL_INFO)
Serial.print(F("Send code=0x"));
Serial.print(aCommand, HEX);
Serial.print(F(" | 0b"));
Serial.println(aCommand, BIN);
#endif
IrSender.sendLG2((uint8_t) LG_ADDRESS, aCommand, 0);
}
/*
* Takes values from static variables
*/
void Aircondition_LG::sendTemperatureFanSpeedAndMode() {
uint8_t tTemperature = Temperature;
#if defined(LOCAL_INFO)
Serial.print(F("Send temperature="));
Serial.print(tTemperature);
Serial.print(F(" fan intensity="));
Serial.print(FanIntensity);
Serial.print(F(" mode="));
Serial.println((char )Mode);
#endif
WordUnion tIRCommand;
tIRCommand.UWord = 0;
// Temperature is coded in the upper nibble of the LowByte
tIRCommand.UByte.LowByte = ((tTemperature - 15) << 4); // 16 -> 0x00, 18 -> 0x30, 30 -> 0xF0
// Fan intensity is coded in the lower nibble of the LowByte
if (ACIsWallType) {
tIRCommand.UByte.LowByte |= AC_FAN_WALL[FanIntensity];
} else {
tIRCommand.UByte.LowByte |= AC_FAN_TOWER[FanIntensity];
}
switch (Mode) {
case AC_MODE_COOLING:
tIRCommand.UByte.HighByte = LG_MODE_COOLING >> 8;
break;
case AC_MODE_HEATING:
tIRCommand.UByte.HighByte = LG_MODE_HEATING >> 8;
break;
case AC_MODE_AUTO:
tIRCommand.UByte.HighByte = LG_MODE_AUTO >> 8;
break;
case AC_MODE_FAN:
tTemperature = 18;
tIRCommand.UByte.HighByte = LG_MODE_FAN >> 8;
break;
case AC_MODE_DEHUMIDIFIYING:
tIRCommand.UWord = LG_MODE_DEHUMIDIFIYING;
break;
default:
break;
}
if (!PowerIsOn) {
// switch on requires masked bit
tIRCommand.UByte.HighByte &= ~(LG_SWITCH_ON_MASK >> 8);
}
PowerIsOn = true;
sendIRCommand(tIRCommand.UWord);
}
/** @}*/
#endif // _AC_LG_HPP
+431
View File
@@ -0,0 +1,431 @@
/*
* digitalWriteFast.h
*
* Optimized digital functions for AVR microcontrollers
* by Watterott electronic (www.watterott.com)
* based on https://code.google.com/p/digitalwritefast
*
* License: BSD 3-Clause License (https://opensource.org/licenses/BSD-3-Clause)
*/
#ifndef __digitalWriteFast_h_
#define __digitalWriteFast_h_ 1
//#define SANGUINO_PINOUT // define for Sanguino pinout
// general macros/defines
#if !defined(BIT_READ)
# define BIT_READ(value, bit) ((value) & (1UL << (bit)))
#endif
#if !defined(BIT_SET)
# define BIT_SET(value, bit) ((value) |= (1UL << (bit)))
#endif
#if !defined(BIT_CLEAR)
# define BIT_CLEAR(value, bit) ((value) &= ~(1UL << (bit)))
#endif
#if !defined(BIT_WRITE)
# define BIT_WRITE(value, bit, bitvalue) (bitvalue ? BIT_SET(value, bit) : BIT_CLEAR(value, bit))
#endif
#include <Arduino.h> // declarations for the fallback to digitalWrite(), digitalRead() etc.
#if defined(MEGATINYCORE)
#error Do not use "#include digitalWriteFast.h" because megaTinyCore has it own digitalWriteFast function set, except digitalToggleFast().
#endif
// --- Arduino Mega and ATmega128x/256x based boards ---
#if (defined(ARDUINO_AVR_MEGA) || \
defined(ARDUINO_AVR_MEGA1280) || \
defined(ARDUINO_AVR_MEGA2560) || \
defined(__AVR_ATmega1280__) || \
defined(__AVR_ATmega1281__) || \
defined(__AVR_ATmega2560__) || \
defined(__AVR_ATmega2561__))
#define __digitalPinToPortReg(P) \
(((P) >= 22 && (P) <= 29) ? &PORTA : \
((((P) >= 10 && (P) <= 13) || ((P) >= 50 && (P) <= 53)) ? &PORTB : \
(((P) >= 30 && (P) <= 37) ? &PORTC : \
((((P) >= 18 && (P) <= 21) || (P) == 38) ? &PORTD : \
((((P) <= 3) || (P) == 5) ? &PORTE : \
(((P) >= 54 && (P) <= 61) ? &PORTF : \
((((P) >= 39 && (P) <= 41) || (P) == 4) ? &PORTG : \
((((P) >= 6 && (P) <= 9) || (P) == 16 || (P) == 17) ? &PORTH : \
(((P) == 14 || (P) == 15) ? &PORTJ : \
(((P) >= 62 && (P) <= 69) ? &PORTK : &PORTL))))))))))
#define __digitalPinToDDRReg(P) \
(((P) >= 22 && (P) <= 29) ? &DDRA : \
((((P) >= 10 && (P) <= 13) || ((P) >= 50 && (P) <= 53)) ? &DDRB : \
(((P) >= 30 && (P) <= 37) ? &DDRC : \
((((P) >= 18 && (P) <= 21) || (P) == 38) ? &DDRD : \
((((P) <= 3) || (P) == 5) ? &DDRE : \
(((P) >= 54 && (P) <= 61) ? &DDRF : \
((((P) >= 39 && (P) <= 41) || (P) == 4) ? &DDRG : \
((((P) >= 6 && (P) <= 9) || (P) == 16 || (P) == 17) ? &DDRH : \
(((P) == 14 || (P) == 15) ? &DDRJ : \
(((P) >= 62 && (P) <= 69) ? &DDRK : &DDRL))))))))))
#define __digitalPinToPINReg(P) \
(((P) >= 22 && (P) <= 29) ? &PINA : \
((((P) >= 10 && (P) <= 13) || ((P) >= 50 && (P) <= 53)) ? &PINB : \
(((P) >= 30 && (P) <= 37) ? &PINC : \
((((P) >= 18 && (P) <= 21) || (P) == 38) ? &PIND : \
((((P) <= 3) || (P) == 5) ? &PINE : \
(((P) >= 54 && (P) <= 61) ? &PINF : \
((((P) >= 39 && (P) <= 41) || (P) == 4) ? &PING : \
((((P) >= 6 && (P) <= 9) || (P) == 16 || (P) == 17) ? &PINH : \
(((P) == 14 || (P) == 15) ? &PINJ : \
(((P) >= 62 && (P) <= 69) ? &PINK : &PINL))))))))))
#define __digitalPinToBit(P) \
(((P) >= 7 && (P) <= 9) ? (P) - 3 : \
(((P) >= 10 && (P) <= 13) ? (P) - 6 : \
(((P) >= 22 && (P) <= 29) ? (P) - 22 : \
(((P) >= 30 && (P) <= 37) ? 37 - (P) : \
(((P) >= 39 && (P) <= 41) ? 41 - (P) : \
(((P) >= 42 && (P) <= 49) ? 49 - (P) : \
(((P) >= 50 && (P) <= 53) ? 53 - (P) : \
(((P) >= 54 && (P) <= 61) ? (P) - 54 : \
(((P) >= 62 && (P) <= 69) ? (P) - 62 : \
(((P) == 0 || (P) == 15 || (P) == 17 || (P) == 21) ? 0 : \
(((P) == 1 || (P) == 14 || (P) == 16 || (P) == 20) ? 1 : \
(((P) == 19) ? 2 : \
(((P) == 5 || (P) == 6 || (P) == 18) ? 3 : \
(((P) == 2) ? 4 : \
(((P) == 3 || (P) == 4) ? 5 : 7)))))))))))))))
// --- Arduino MightyCore standard pinout ---
#elif (defined(__AVR_ATmega1284P__) || \
defined(__AVR_ATmega1284__) || \
defined(__AVR_ATmega644P__) || \
defined(__AVR_ATmega644A__) || \
defined(__AVR_ATmega644__) || \
defined(__AVR_ATmega324PB__) || \
defined(__AVR_ATmega324PA__) || \
defined(__AVR_ATmega324P__) || \
defined(__AVR_ATmega324A__) || \
defined(__AVR_ATmega164P__) || \
defined(__AVR_ATmega164A__) || \
defined(__AVR_ATmega32__) || \
defined(__AVR_ATmega16__) || \
defined(__AVR_ATmega8535__)) && \
!defined(BOBUINO_PINOUT)
#if defined(__AVR_ATmega324PB__)
#define __digitalPinToPortReg(P) \
(((P) <= 7) ? &PORTB : (((P) >= 8 && (P) <= 15) ? &PORTD : (((P) >= 16 && (P) <= 23) ? &PORTC : (((P) >= 24 && (P) <= 31) ? &PORTA : &PORTE))))
#define __digitalPinToDDRReg(P) \
(((P) <= 7) ? &DDRB : (((P) >= 8 && (P) <= 15) ? &DDRD : (((P) >= 16 && (P) <= 23) ? &DDRC : (((P) >= 24 && (P) <= 31) ? &DDRA : &DDRE))))
#define __digitalPinToPINReg(P) \
(((P) <= 7) ? &PINB : (((P) >= 8 && (P) <= 15) ? &PIND : (((P) >= 16 && (P) <= 23) ? &PINC : (((P) >= 24 && (P) <= 31) ? &PINA : &PINE))))
# if defined(SANGUINO_PINOUT)
#define __digitalPinToBit(P) \
(((P) <= 7) ? (P) : (((P) >= 8 && (P) <= 15) ? (P) - 8 : (((P) >= 16 && (P) <= 23) ? (P) - 16 : (((P) >= 16 && (P) <= 23) ? (7 - ((P) - 24)) : (P) - 32))))
# else //MightyCore Pinout
#define __digitalPinToBit(P) \
(((P) <= 7) ? (P) : (((P) >= 8 && (P) <= 15) ? (P) - 8 : (((P) >= 16 && (P) <= 23) ? (P) - 16 : (((P) >= 16 && (P) <= 23) ? (P) - 24 : (P) - 32))))
# endif
#elif defined(PORTA)
#define __digitalPinToPortReg(P) \
(((P) <= 7) ? &PORTB : (((P) >= 8 && (P) <= 15) ? &PORTD : (((P) >= 16 && (P) <= 23) ? &PORTC : &PORTA)))
#define __digitalPinToDDRReg(P) \
(((P) <= 7) ? &DDRB : (((P) >= 8 && (P) <= 15) ? &DDRD : (((P) >= 16 && (P) <= 23) ? &DDRC : &DDRA)))
#define __digitalPinToPINReg(P) \
(((P) <= 7) ? &PINB : (((P) >= 8 && (P) <= 15) ? &PIND : (((P) >= 16 && (P) <= 23) ? &PINC : &PINA)))
# if defined(SANGUINO_PINOUT)
#define __digitalPinToBit(P) \
(((P) <= 7) ? (P) : (((P) >= 8 && (P) <= 15) ? (P) - 8 : (((P) >= 16 && (P) <= 23) ? (P) - 16 : (7 - ((P) - 24)))))
# else //MightyCore Pinout
#define __digitalPinToBit(P) \
(((P) <= 7) ? (P) : (((P) >= 8 && (P) <= 15) ? (P) - 8 : (((P) >= 16 && (P) <= 23) ? (P) - 16 : (P) - 24)))
# endif
#else
#define __digitalPinToPortReg(P) \
(((P) <= 7) ? &PORTB : (((P) >= 8 && (P) <= 15) ? &PORTD : &PORTC))
#define __digitalPinToDDRReg(P) \
(((P) <= 7) ? &DDRB : (((P) >= 8 && (P) <= 15) ? &DDRD : &DDRC))
#define __digitalPinToPINReg(P) \
(((P) <= 7) ? &PINB : (((P) >= 8 && (P) <= 15) ? &PIND : &PINC))
# if defined(SANGUINO_PINOUT)
#define __digitalPinToBit(P) \
(((P) <= 7) ? (P) : (((P) >= 8 && (P) <= 15) ? (P) - 8 : (((P) >= 16 && (P) <= 23) ? (P) - 16 : (7 - ((P) - 24)))))
# else //MightyCore Pinout
#define __digitalPinToBit(P) \
(((P) <= 7) ? (P) : (((P) >= 8 && (P) <= 15) ? (P) - 8 : (((P) >= 16 && (P) <= 23) ? (P) - 16 : (P) - 24)))
# endif
#endif
// --- Arduino Leonardo and ATmega16U4/32U4 based boards ---
#elif (defined(ARDUINO_AVR_LEONARDO) || \
defined(__AVR_ATmega16U4__) || \
defined(__AVR_ATmega32U4__))
# if defined(TEENSYDUINO)
#define __digitalPinToPortReg(P) \
((((P) <= 4) || ((P) >= 13 && (P) <= 15)) ? &PORTB : (((P) == 9 || (P) == 10) ? &PORTC : (((P) >= 16 && (P) <= 21)) ? &PORTF : &PORTD))
#define __digitalPinToDDRReg(P) \
((((P) <= 4) || ((P) >= 13 && (P) <= 15)) ? &DDRB : (((P) == 9 || (P) == 10) ? &DDRC : (((P) >= 16 && (P) <= 21)) ? &DDRF : &DDRD))
#define __digitalPinToPINReg(P) \
((((P) <= 4) || ((P) >= 13 && (P) <= 15)) ? &PINB : (((P) == 9 || (P) == 10) ? &PINC : (((P) >= 16 && (P) <= 21)) ? &PINF : &PIND))
#define __digitalPinToBit(P) \
(((P) <= 3) ? (P) : \
(((P) == 4 || (P) == 12) ? 7 : \
(((P) <= 8) ? (P) - 5 : \
(((P) <= 10) ? (P) - 3 : \
(((P) == 11) ? 6 : \
(((P) <= 15) ? (P) - 9 : \
(((P) <= 19) ? 23 - (P) : \
(((P) <= 21) ? 21 - (P) : (P) - 18))))))))
# else
#define __digitalPinToPortReg(P) \
((((P) <= 4) || (P) == 6 || (P) == 12 || (P) == 24 || (P) == 25 || (P) == 29) ? &PORTD : (((P) == 5 || (P) == 13) ? &PORTC : (((P) >= 18 && (P) <= 23)) ? &PORTF : (((P) == 7) ? &PORTE : &PORTB)))
#define __digitalPinToDDRReg(P) \
((((P) <= 4) || (P) == 6 || (P) == 12 || (P) == 24 || (P) == 25 || (P) == 29) ? &DDRD : (((P) == 5 || (P) == 13) ? &DDRC : (((P) >= 18 && (P) <= 23)) ? &DDRF : (((P) == 7) ? &DDRE : &DDRB)))
#define __digitalPinToPINReg(P) \
((((P) <= 4) || (P) == 6 || (P) == 12 || (P) == 24 || (P) == 25 || (P) == 29) ? &PIND : (((P) == 5 || (P) == 13) ? &PINC : (((P) >= 18 && (P) <= 23)) ? &PINF : (((P) == 7) ? &PINE : &PINB)))
#define __digitalPinToBit(P) \
(((P) >= 8 && (P) <= 11) ? (P) - 4 : \
(((P) >= 18 && (P) <= 21) ? 25 - (P) : \
(((P) == 0) ? 2 : (((P) == 1) ? 3 : (((P) == 2) ? 1 : (((P) == 3) ? 0 : (((P) == 4) ? 4 : (((P) == 6) ? 7 : (((P) == 13) ? 7 : \
(((P) == 14) ? 3 : (((P) == 15) ? 1 : (((P) == 16) ? 2 : (((P) == 17) ? 0 : (((P) == 22) ? 1 : (((P) == 23) ? 0 : \
(((P) == 24) ? 4 : (((P) == 25) ? 7 : (((P) == 26) ? 4 : (((P) == 27) ? 5 : 6 )))))))))))))))))))
# endif
// --- Arduino Uno and ATmega168/328 based boards ---
#elif (defined(ARDUINO_AVR_UNO) || \
defined(ARDUINO_AVR_DUEMILANOVE) || \
defined(__AVR_ATmega8__) || \
defined(__AVR_ATmega48__) || \
defined(__AVR_ATmega48P__) || \
defined(__AVR_ATmega48PB__) || \
defined(__AVR_ATmega88P__) || \
defined(__AVR_ATmega88PB__) || \
defined(__AVR_ATmega168__) || \
defined(__AVR_ATmega168PA__) || \
defined(__AVR_ATmega168PB__) || \
defined(__AVR_ATmega328__) || \
defined(__AVR_ATmega328P__) || \
defined(__AVR_ATmega328PB__))
#if defined(__AVR_ATmega48PB__) || defined(__AVR_ATmega88PB__) || defined(__AVR_ATmega168PB__) || defined(__AVR_ATmega328PB__)
#define __digitalPinToPortReg(P) \
(((P) <= 7) ? &PORTD : (((P) >= 8 && (P) <= 13) ? &PORTB : (((P) >= 14 && (P) <= 19) ? &PORTC : &PORTE)))
#define __digitalPinToDDRReg(P) \
(((P) <= 7) ? &DDRD : (((P) >= 8 && (P) <= 13) ? &DDRB : (((P) >= 14 && (P) <= 19) ? &DDRC : &DDRE)))
#define __digitalPinToPINReg(P) \
(((P) <= 7) ? &PIND : (((P) >= 8 && (P) <= 13) ? &PINB : (((P) >= 14 && (P) <= 19) ? &PINC : &PINE)))
#define __digitalPinToBit(P) \
(((P) <= 7) ? (P) : (((P) >= 8 && (P) <= 13) ? (P) - 8 : (((P) >= 14 && (P) <= 19) ? (P) - 14 : (((P) >= 20 && (P) <= 21) ? (P) - 18 : (P) - 22))))
#else
#define __digitalPinToPortReg(P) \
(((P) <= 7) ? &PORTD : (((P) >= 8 && (P) <= 13) ? &PORTB : &PORTC))
#define __digitalPinToDDRReg(P) \
(((P) <= 7) ? &DDRD : (((P) >= 8 && (P) <= 13) ? &DDRB : &DDRC))
#define __digitalPinToPINReg(P) \
(((P) <= 7) ? &PIND : (((P) >= 8 && (P) <= 13) ? &PINB : &PINC))
#define __digitalPinToBit(P) \
(((P) <= 7) ? (P) : (((P) >= 8 && (P) <= 13) ? (P) - 8 : (P) - 14))
#endif
// --- Arduino Uno WiFi Rev 2, Nano Every ---
#elif defined(__AVR_ATmega4809__)
#define __digitalPinToPortReg(P) \
(((P) == 2 || (P) == 7 ) ? &VPORTA.OUT : ((P) == 5 || (P) == 9 || (P) == 10) ? &VPORTB.OUT : ((P) == 4) ? &VPORTC.OUT : (((P) >= 14 && (P) <= 17) || (P) == 20 || (P) == 21) ? &VPORTD.OUT : ((P) == 8 || (P) == 11 || (P) == 12 || (P) == 13) ? &VPORTE.OUT : &VPORTF.OUT)
#define __digitalPinToDDRReg(P) \
(((P) == 2 || (P) == 7 ) ? &VPORTA.DIR : ((P) == 5 || (P) == 9 || (P) == 10) ? &VPORTB.DIR : ((P) == 4) ? &VPORTC.DIR : (((P) >= 14 && (P) <= 17) || (P) == 20 || (P) == 21) ? &VPORTD.DIR : ((P) == 8 || (P) == 11 || (P) == 12 || (P) == 13) ? &VPORTE.DIR : &VPORTF.DIR)
#define __digitalPinToPINReg(P) \
(((P) == 2 || (P) == 7 ) ? &VPORTA.IN : ((P) == 5 || (P) == 9 || (P) == 10) ? &VPORTB.IN : ((P) == 4) ? &VPORTC.IN : (((P) >= 14 && (P) <= 17) || (P) == 20 || (P) == 21) ? &VPORTD.IN : ((P) == 8 || (P) == 11 || (P) == 12 || (P) == 13) ? &VPORTE.IN : &VPORTF.IN)
#define __digitalPinToBit(P) \
(((P) == 2 || (P) == 9 || (P) == 11 || (P) == 17) ? 0 : ((P) == 7 || (P) == 10 || (P) == 12 || (P) == 16) ? 1 : ((P) == 5 || (P) == 13 || (P) == 15 || (P) == 18) ? 2 : ((P) == 9 || (P) == 14 || (P) == 19) ? 3 : ((P) == 6 || (P) == 20) ? 4 : ((P) == 3 || (P) == 21) ? 5 : 6 )
// TinyCore
// https://raw.githubusercontent.com/xukangmin/TinyCore/master/avr/package/package_tinycore_index.json
// https://docs.tinycore.dev/en/latest/
#elif defined(__AVR_ATtiny1616__) || defined(__AVR_ATtiny3216__) || defined(__AVR_ATtiny3217__)
#define __digitalPinToPortReg(P) ((P) <= 5 ? &VPORTB.OUT : ((P) <= 9 ? &VPORTC.OUT : ((P) <= 16 ? &VPORTA.OUT : ((P) <= 18 ? &VPORTB.OUT : &VPORTC.OUT))))
#define __digitalPinToDDRReg(P) ((P) <= 5 ? &VPORTB.DIR : ((P) <= 9 ? &VPORTC.DIR : ((P) <= 16 ? &VPORTA.DIR : ((P) <= 18 ? &VPORTB.DIR : &VPORTC.DIR))))
#define __digitalPinToPINReg(P) ((P) <= 5 ? &VPORTB.IN : ((P) <= 9 ? &VPORTC.IN : ((P) <= 16 ? &VPORTA.IN : ((P) <= 18 ? &VPORTB.IN : &VPORTC.IN))))
#define __digitalPinToBit(P) ( (P) <= 3 ? (3 - P) : ((P) <= 5 ? (P) : ((P) <= 9 ? (P - 6) : ((P) <= 16 ? ((P) - 9) : ((P) <= 18 ? ((P) - 11) : ((P) - 15))))) )
#elif defined(__AVR_ATtiny1614__)
#define __digitalPinToPortReg(P) ((P) <= 3 ? &VPORTA.OUT : ((P) <= 7 ? &VPORTB.OUT : &VPORTA.OUT))
#define __digitalPinToDDRReg(P) ((P) <= 3 ? &VPORTA.DIR : ((P) <= 7 ? &VPORTB.DIR : &VPORTA.DIR))
#define __digitalPinToPINReg(P) ((P) <= 3 ? &VPORTA.IN : ((P) <= 7 ? &VPORTB.IN : &VPORTA.IN))
#define __digitalPinToBit(P) ( (P) <= 3 ? (P + 4) : ((P) <= 7 ? (7 - P) : ((P) <= 10 ? (P - 7) : (P) - 11)) )
#elif defined(__AVR_ATtiny816__)
// https://github.com/Arduino-IRremote/Arduino-IRremote/discussions/1029
#define __digitalPinToPortReg(P) ((P) <= 3 ? &VPORTA.OUT : ((P) <= 9 ? &VPORTB.OUT : ((P) <= 13 ? &VPORTC.OUT : ((P) <= 17 ? &VPORTA.OUT : &VPORTC.OUT))))
#define __digitalPinToDDRReg(P) ((P) <= 3 ? &VPORTA.DIR : ((P) <= 9 ? &VPORTB.DIR : ((P) <= 13 ? &VPORTC.DIR : ((P) <= 17 ? &VPORTA.DIR : &VPORTC.DIR))))
#define __digitalPinToPINReg(P) ((P) <= 3 ? &VPORTA.IN : ((P) <= 9 ? &VPORTB.IN : ((P) <= 13 ? &VPORTC.IN : ((P) <= 17 ? &VPORTA.IN : &VPORTC.IN))))
#define __digitalPinToBit(P) ( (P) <= 3 ? (P + 4) : ((P) <= 9 ? (9 - P) : ((P) <= 13 ? (P - 10) : ((P) <= 16 ? (P - 13) : ((P) - 17)))) )
// --- ATtinyX5 ---
#elif defined(__AVR_ATtiny25__) || defined(__AVR_ATtiny45__) || defined(__AVR_ATtiny85__)
// we have only PORTB
#define __digitalPinToPortReg(P) (&PORTB)
#define __digitalPinToDDRReg(P) (&DDRB)
#define __digitalPinToPINReg(P) (&PINB)
#define __digitalPinToBit(P) (((P) <= 7) ? (P) : (((P) >= 8 && (P) <= 13) ? (P) - 8 : (P) - 14))
// --- ATtiny88 ---
#elif defined(__AVR_ATtiny88__)
# if defined(ARDUINO_AVR_DIGISPARKPRO)
#define __digitalPinToPortReg(P) ((P) <= 7 ? &PORTD : ((P) <= 14 ? &PORTB : ((P) <= 18 ? &PORTA : &PORTC)))
#define __digitalPinToDDRReg(P) ((P) <= 7 ? &DDRD : ((P) <= 14 ? &DDRB : ((P) <= 18 ? &DDRA : &DDRC)))
#define __digitalPinToPINReg(P) ((P) <= 7 ? &PIND : ((P) <= 14 ? &PINB : ((P) <= 18 ? &PINA : &PINC)))
#define __digitalPinToBit(P) ( (P) <= 7 ? (P) : ((P) <= 13 ? ((P) - 8) : ((P) == 14 ? 7 : ((P) <= 16 ? ((P) - 14) : ((P) <= 18 ? ((P) - 17) : ((P) == 25 ? 7 : ((P) - 19)))))) )
# else
#define __digitalPinToPortReg(P) ((P) <= 7 ? &PORTD : ((P) <= 15 ? &PORTB : ((P) <= 22 ? &PORTC : ((P) <= 26 ? &PORTA : &PORTC))))
#define __digitalPinToDDRReg(P) ((P) <= 7 ? &DDRD : ((P) <= 15 ? &DDRB : ((P) <= 22 ? &DDRC : ((P) <= 26 ? &DDRA : &DDRC))))
#define __digitalPinToPINReg(P) ((P) <= 7 ? &PIND : ((P) <= 15 ? &PINB : ((P) <= 22 ? &PINC : ((P) <= 26 ? &PINA : &PINC))))
#define __digitalPinToBit(P) ((P) <= 15 ? ((P) & 0x7) : ((P) == 16 ? (7) : ((P) <= 22 ? ((P) - 17) : ((P) == 27 ? (6) : ((P) - 23)))))
# endif
// --- ATtinyX4 + ATtinyX7 ---
#elif defined(__AVR_ATtiny24__) || defined(__AVR_ATtiny44__) || defined(__AVR_ATtiny84__) \
|| defined(__AVR_ATtiny441__) || defined(__AVR_ATtiny841__) || defined(__AVR_ATtiny87__) || defined(__AVR_ATtiny167__)
# if defined(ARDUINO_AVR_DIGISPARKPRO) || PIN_PA7 == 5
// Strange enumeration of pins on Digispark board and core library
#define __digitalPinToPortReg(P) (((P) <= 4) ? &PORTB : &PORTA)
#define __digitalPinToDDRReg(P) (((P) <= 4) ? &DDRB : &DDRA)
#define __digitalPinToPINReg(P) (((P) <= 4) ? &PINB : &PINA)
#define __digitalPinToBit(P) (((P) <= 2) ? (P) : (((P) == 3) ? 6 : (((P) == 4) ? 3 : (((P) == 5) ? 7 : (P) - 6 ))))
# else
// ATtinyX4: PORTA for 0 to 7, PORTB for 8 to 11
// ATtinyX7: PORTA for 0 to 7, PORTB for 8 to 15
#define __digitalPinToPortReg(P) (((P) <= 7) ? &PORTA : &PORTB)
#define __digitalPinToDDRReg(P) (((P) <= 7) ? &DDRA : &DDRB)
#define __digitalPinToPINReg(P) (((P) <= 7) ? &PINA : &PINB)
# endif
# if defined(__AVR_ATtiny24__) || defined(__AVR_ATtiny44__) || defined(__AVR_ATtiny84__) || defined(__AVR_ATtiny441__) || defined(__AVR_ATtiny841__)
// https://github.com/SpenceKonde/ATTinyCore/blob/v2.0.0-devThis-is-the-head-submit-PRs-against-this/avr/variants/tinyx41_cw/pins_arduino.h#L334
// Clockwise layout
#define __digitalPinToBit(P) (((P) <= 7) ? (P) : ((P) == 11 ? (3) : 10 - (P)))
# else
#define __digitalPinToBit(P) (((P) <= 7) ? (P) : (P) - 8 )
# endif
#elif defined(MIK32V2)
#include "wiring_LL.h"
#define pinModeFast fastPinMode
#define digitalWriteFast(P, V) (((V)==1) ? GPIO_SET_PIN(digitalPinToPort(P), digitalPinToBitMask(P)) : GPIO_CLEAR_PIN(digitalPinToPort(P), digitalPinToBitMask(P)))
#define digitalReadFast(P) GPIO_READ_PIN(digitalPinToPort(P), digitalPinToBitMask(P))
#define digitalToggleFast(P) GPIO_TOGGLE_PIN(digitalPinToPort(P), digitalPinToBitMask(P))
#endif
void NonConstantsUsedForPinModeFast( void ) __attribute__ (( error("Parameter for pinModeFast() function is not constant") ));
void NonConstantsUsedForDigitalWriteFast( void ) __attribute__ (( error("Parameter for digitalWriteFast() function is not constant") ));
void NonConstantsUsedForDigitalToggleFast( void ) __attribute__ (( error("Parameter for digitalToggleFast() function is not constant") ));
int NonConstantsUsedForDigitalReadFast( void ) __attribute__ (( error("Parameter for digitalReadFast() function is not constant") ));
#if !defined(digitalWriteFast)
# if (defined(__AVR__) || defined(ARDUINO_ARCH_AVR)) && defined(__digitalPinToPortReg)
# if defined(THROW_ERROR_IF_NOT_FAST)
#define digitalWriteFast(P, V) \
if (__builtin_constant_p(P)) { \
BIT_WRITE(*__digitalPinToPortReg(P), __digitalPinToBit(P), (V)); \
} else { \
NonConstantsUsedForDigitalWriteFast(); \
}
# else
#define digitalWriteFast(P, V) \
if (__builtin_constant_p(P)) { \
BIT_WRITE(*__digitalPinToPortReg(P), __digitalPinToBit(P), (V)); \
} else { \
digitalWrite((P), (V)); \
}
# endif // defined(THROW_ERROR_IF_NOT_FAST)
# else
#define digitalWriteFast digitalWrite
# endif
#endif
#if !defined(pinModeFast)
# if (defined(__AVR__) || defined(ARDUINO_ARCH_AVR)) && defined(__digitalPinToPortReg)
# if defined(THROW_ERROR_IF_NOT_FAST)
#define pinModeFast(P, V) \
if (__builtin_constant_p(P) && __builtin_constant_p(V)) { \
if (V == INPUT_PULLUP) {\
BIT_CLEAR(*__digitalPinToDDRReg(P), __digitalPinToBit(P)); \
BIT_SET(*__digitalPinToPortReg(P), __digitalPinToBit(P)); \
} else { \
BIT_WRITE(*__digitalPinToDDRReg(P), __digitalPinToBit(P), (V)); \
} \
} else { \
NonConstantsUsedForPinModeFast(); \
}
# else
#define pinModeFast(P, V) \
if (__builtin_constant_p(P) && __builtin_constant_p(V)) { \
if (V == INPUT_PULLUP) {\
BIT_CLEAR(*__digitalPinToDDRReg(P), __digitalPinToBit(P)); \
BIT_SET(*__digitalPinToPortReg(P), __digitalPinToBit(P)); \
} else { \
BIT_WRITE(*__digitalPinToDDRReg(P), __digitalPinToBit(P), (V)); \
} \
} else { \
pinMode((P), (V)); \
}
# endif // defined(THROW_ERROR_IF_NOT_FAST)
# else
#define pinModeFast pinMode
# endif
#endif // !defined(pinModeFast)
#if !defined(digitalReadFast)
# if (defined(__AVR__) || defined(ARDUINO_ARCH_AVR)) && defined(__digitalPinToPINReg)
# if defined(THROW_ERROR_IF_NOT_FAST)
#define digitalReadFast(P) ( (int) __digitalReadFast((P)) )
// since we have return values, it is easier to implement it by ?:
#define __digitalReadFast(P ) \
(__builtin_constant_p(P) ) ? \
(( BIT_READ(*__digitalPinToPINReg(P), __digitalPinToBit(P))) ? HIGH:LOW ) : \
NonConstantsUsedForDigitalReadFast()
# else
#define digitalReadFast(P) ( (int) __digitalReadFast((P)) )
// since we have return values, it is easier to implement it by ?:
#define __digitalReadFast(P ) \
(__builtin_constant_p(P) ) ? \
(( BIT_READ(*__digitalPinToPINReg(P), __digitalPinToBit(P))) ? HIGH:LOW ) : \
digitalRead((P))
# endif // defined(THROW_ERROR_IF_NOT_FAST)
# else
#define digitalReadFast digitalRead
# endif
#endif // !defined(digitalReadFast)
#if !defined(digitalToggleFast)
# if (defined(__AVR__) || defined(ARDUINO_ARCH_AVR)) && defined(__digitalPinToPINReg)
# if defined(THROW_ERROR_IF_NOT_FAST)
#define digitalToggleFast(P) \
if (__builtin_constant_p(P)) { \
BIT_SET(*__digitalPinToPINReg(P), __digitalPinToBit(P)); \
} else { \
NonConstantsUsedForDigitalToggleFast(); \
}
# else
#define digitalToggleFast(P) \
if (__builtin_constant_p(P)) { \
BIT_SET(*__digitalPinToPINReg(P), __digitalPinToBit(P)); \
} else { \
digitalWrite(P, ! digitalRead(P)); \
}
# endif // defined(THROW_ERROR_IF_NOT_FAST)
# else
#define digitalToggleFast(P) digitalWrite(P, ! digitalRead(P))
# endif
#endif // !defined(digitalToggleFast)
#endif //__digitalWriteFast_h_
+496
View File
@@ -0,0 +1,496 @@
/*
* ir_BangOlufsen.hpp
*
* Contains functions for receiving and sending Bang & Olufsen IR and Datalink '86 protocols
* To receive B&O and ENABLE_BEO_WITHOUT_FRAME_GAP is NOT defined, you must set RECORD_GAP_MICROS to
* at least 16000 to accommodate the unusually long 3. start space.
*
* This file is part of Arduino-IRremote https://github.com/Arduino-IRremote/Arduino-IRremote.
*
************************************************************************************
* MIT License
*
* Copyright (c) 2022-2023 Daniel Wallner and 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_BANG_OLUFSEN_HPP
#define _IR_BANG_OLUFSEN_HPP
//==============================================================================
//
//
// Bang & Olufsen
//
//
//==============================================================================
// https://www.mikrocontroller.net/attachment/33137/datalink.pdf
// https://www.mikrocontroller.net/articles/IRMP_-_english#B&O
// This protocol is unusual in two ways:
// 1. The carrier frequency is 455 kHz
// You can build your own receiver as Bang & Olufsen did (check old schematics) or use a TSOP7000
// Vishay stopped producing TSOP7000 since 2009 so you will probably only find counterfeits:
// https://www.vishay.com/files/whatsnew/doc/ff_FastFacts_CounterfeitTSOP7000_Dec72018.pdf
// It is also likely that you will need an oscilloscope to debug a counterfeit TSOP7000
// The specimen used to test this code was very noisy and had a very low output current
// A somewhat working fix was to put a 4n7 capacitor across the output and ground followed by a pnp emitter follower
// Other examples may require a different treatment
// This particular receiver also did receive lower frequencies but rather poorly and with a lower delay than usual
// If you need to parallel a receiver with another one you may need to delay the signal to get in phase with the other receiver
// 2. A stream of messages can be sent back to back with a new message immediately following the previous stop space
// It might be that this only happens over IR and not on the datalink protocol
// You can choose to support this or not:
// Mode 1: Mode with gaps between frames
// Set RECORD_GAP_MICROS to at least 16000 to accept the unusually long 3. start space
// Can only receive single messages. Back to back repeats will result in overflow
// Mode 2: Break at start mode
// Define ENABLE_BEO_WITHOUT_FRAME_GAP and set RECORD_GAP_MICROS to less than 15000
// to treat the 3. start space of 15.5 ms as a gap between messages, which makes decoding easier :-).
// The receiving of a transmission will then result in a dummy decode of the first 2 start bits with 0 bits data
// followed by a 15.5 ms gap and a data frame with one start bit (originally sent as 4. start bit).
// If the receiver is not resumed within a ms or so, partial messages will be decoded
// Debug printing in the wrong place is very likely to break reception
// Make sure to check the number of bits to filter dummy and incomplete messages
// !!! We assume that the real implementations never set the official first header bit to anything other than 0 !!!
// !!! We therefore use 4 start bits instead of the specified 3 and in turn ignore the first header bit of the specification !!!
// IR messages are 16 bits long. Datalink messages have different lengths.
// This implementation supports up to 40 bits total length split into 8 bit data/command and a header/address of variable length
// Header data with more than 16 bits is stored in decodedIRData.extra
// B&O is a pulse distance protocol, but it has 3 bit values 0, 1 and (equal/repeat) as well as a special start and trailing bit.
//
// MSB first, 4 start bits + 8 to 16? bit address + 8 bit command + 1 special trailing bit + 1 stop bit.
// Address can be longer than 8 bit.
/*
* Options for this decoder
*
*/
#define ENABLE_BEO_WITHOUT_FRAME_GAP // Requires additional 30 bytes program memory. Enabled by default, see https://github.com/Arduino-IRremote/Arduino-IRremote/discussions/1181
//#define SUPPORT_BEO_DATALINK_TIMING_FOR_DECODE // This also supports headers up to 32 bit. Requires additional 150 bytes program memory.
#if defined(DECODE_BEO)
# if defined(ENABLE_BEO_WITHOUT_FRAME_GAP)
# if RECORD_GAP_MICROS > 15000
#warning If defined ENABLE_BEO_WITHOUT_FRAME_GAP, RECORD_GAP_MICROS must be set to <= 15000 by "#define RECORD_GAP_MICROS 12750"
# endif
# else
# if RECORD_GAP_MICROS < 16000
#error If not defined ENABLE_BEO_WITHOUT_FRAME_GAP, RECORD_GAP_MICROS must be set to a value >= 16000 by "#define RECORD_GAP_MICROS 16000"
# endif
# endif
#endif
#define BEO_DATA_BITS 8 // Command or character
#define BEO_UNIT 3125 // All timings are in microseconds
#define BEO_IR_MARK 200 // The length of a mark in the IR protocol
#define BEO_DATALINK_MARK (BEO_UNIT / 2) // The length of a mark in the Datalink protocol
#define BEO_PULSE_LENGTH_ZERO BEO_UNIT // The length of a one to zero transition
#define BEO_PULSE_LENGTH_EQUAL (2 * BEO_UNIT) // 6250 The length of an equal bit
#define BEO_PULSE_LENGTH_ONE (3 * BEO_UNIT) // 9375 The length of a zero to one transition
#define BEO_PULSE_LENGTH_TRAILING_BIT (4 * BEO_UNIT) // 12500 The length of the stop bit
#define BEO_PULSE_LENGTH_START_BIT (5 * BEO_UNIT) // 15625 The length of the start bit
// It is not allowed to send two ones or zeros, you must send a one or zero and a equal instead.
//#define BEO_LOCAL_DEBUG
//#define BEO_LOCAL_TRACE
#ifdef BEO_LOCAL_DEBUG
# define BEO_DEBUG_PRINT(...) Serial.print(__VA_ARGS__)
# define BEO_DEBUG_PRINTLN(...) Serial.println(__VA_ARGS__)
#else
# define BEO_DEBUG_PRINT(...) void()
# define BEO_DEBUG_PRINTLN(...) void()
#endif
#ifdef BEO_LOCAL_TRACE
# undef BEO_TRACE_PRINT
# undef BEO_TRACE_PRINTLN
# define BEO_TRACE_PRINT(...) Serial.print(__VA_ARGS__)
# define BEO_TRACE_PRINTLN(...) Serial.println(__VA_ARGS__)
#else
# define BEO_TRACE_PRINT(...) void()
# define BEO_TRACE_PRINTLN(...) void()
#endif
/************************************
* Start of send and decode functions
************************************/
/*
* TODO aNumberOfRepeats are handled not correctly if ENABLE_BEO_WITHOUT_FRAME_GAP is defined
*/
void IRsend::sendBangOlufsen(uint16_t aHeader, uint8_t aData, int_fast8_t aNumberOfRepeats, int8_t aNumberOfHeaderBits) {
for (int_fast8_t i = 0; i < aNumberOfRepeats + 1; ++i) {
sendBangOlufsenRaw((uint32_t(aHeader) << 8) | aData, aNumberOfHeaderBits + 8, i != 0);
}
}
void IRsend::sendBangOlufsenDataLink(uint32_t aHeader, uint8_t aData, int_fast8_t aNumberOfRepeats, int8_t aNumberOfHeaderBits) {
for (int_fast8_t i = 0; i < aNumberOfRepeats + 1; ++i) {
sendBangOlufsenRawDataLink((uint64_t(aHeader) << 8) | aData, aNumberOfHeaderBits + 8, i != 0, true);
}
}
/*
* @param aBackToBack If true send data back to back, which cannot be decoded if ENABLE_BEO_WITHOUT_FRAME_GAP is NOT defined
*/
void IRsend::sendBangOlufsenRaw(uint32_t aRawData, int_fast8_t aBits, bool aBackToBack) {
#if defined(USE_NO_SEND_PWM) || defined(SEND_PWM_BY_TIMER) || BEO_KHZ == 38 // BEO_KHZ == 38 is for unit test which runs the B&O protocol with 38 kHz
/*
* 455 kHz PWM is currently only supported with SEND_PWM_BY_TIMER defined, otherwise maximum is 180 kHz
*/
# if !defined(USE_NO_SEND_PWM)
# if defined(SEND_PWM_BY_TIMER)
enableHighFrequencyIROut (BEO_KHZ);
# elif (BEO_KHZ == 38)
enableIROut (BEO_KHZ); // currently only for unit test
# endif
# endif
// AGC / Start - 3 bits + first constant 0 header bit described in the official documentation
if (!aBackToBack) {
mark(BEO_IR_MARK);
}
space(BEO_PULSE_LENGTH_ZERO - BEO_IR_MARK);
mark(BEO_IR_MARK);
space(BEO_PULSE_LENGTH_ZERO - BEO_IR_MARK);
mark(BEO_IR_MARK);
space(BEO_PULSE_LENGTH_START_BIT - BEO_IR_MARK);
// First bit of header is assumed to be a constant 0 to have a fixed state to begin with the equal decisions.
// So this first 0 is treated as the last bit of AGC
mark(BEO_IR_MARK);
space(BEO_PULSE_LENGTH_ZERO - BEO_IR_MARK);
bool tLastBitValueWasOne = false;
// Header / Data
uint32_t mask = 1UL << (aBits - 1);
for (; mask; mask >>= 1) {
if (tLastBitValueWasOne && !(aRawData & mask)) {
mark(BEO_IR_MARK);
space(BEO_PULSE_LENGTH_ZERO - BEO_IR_MARK);
tLastBitValueWasOne = false;
} else if (!tLastBitValueWasOne && (aRawData & mask)) {
mark(BEO_IR_MARK);
space(BEO_PULSE_LENGTH_ONE - BEO_IR_MARK);
tLastBitValueWasOne = true;
} else {
mark(BEO_IR_MARK);
space(BEO_PULSE_LENGTH_EQUAL - BEO_IR_MARK);
}
}
// Stop
mark(BEO_IR_MARK);
space(BEO_PULSE_LENGTH_TRAILING_BIT - BEO_IR_MARK);
mark(BEO_IR_MARK);
#else
(void) aRawData;
(void) aBits;
(void) aBackToBack;
#endif
}
/*
* Version with 64 bit aRawData, which can send both timings, but costs more program memory
* @param aBackToBack If true send data back to back, which cannot be decoded if ENABLE_BEO_WITHOUT_FRAME_GAP is NOT defined
* @param aUseDatalinkTiming if false it does the same as sendBangOlufsenRaw()
*/
void IRsend::sendBangOlufsenRawDataLink(uint64_t aRawData, int_fast8_t aBits, bool aBackToBack, bool aUseDatalinkTiming) {
#if defined(USE_NO_SEND_PWM) || BEO_KHZ == 38 // BEO_KHZ == 38 is for unit test which runs the B&O protocol with 38 kHz instead 0f 455 kHz
uint16_t tSendBEOMarkLength = aUseDatalinkTiming ? BEO_DATALINK_MARK : BEO_IR_MARK;
/*
* 455 kHz PWM is currently not supported, maximum is 180 kHz
*/
#if !defined(USE_NO_SEND_PWM)
enableIROut (BEO_KHZ);
#endif
// AGC / Start - 3 bits + first constant 0 header bit described in the official documentation
if (!aBackToBack) {
mark(tSendBEOMarkLength);
}
space(BEO_PULSE_LENGTH_ZERO - tSendBEOMarkLength);
mark(tSendBEOMarkLength);
space(BEO_PULSE_LENGTH_ZERO - tSendBEOMarkLength);
mark(tSendBEOMarkLength);
space(BEO_PULSE_LENGTH_START_BIT - tSendBEOMarkLength);
// First bit of header is assumed to be a constant 0 to have a fixed state to begin with the equal decisions.
// So this first 0 is treated as the last bit of AGC
mark(tSendBEOMarkLength);
space(BEO_PULSE_LENGTH_ZERO - tSendBEOMarkLength);
bool tLastBitValueWasOne = false;
// Header / Data
uint32_t mask = 1UL << (aBits - 1);
for (; mask; mask >>= 1) {
if (tLastBitValueWasOne && !(aRawData & mask)) {
mark(tSendBEOMarkLength);
space(BEO_PULSE_LENGTH_ZERO - tSendBEOMarkLength);
tLastBitValueWasOne = false;
} else if (!tLastBitValueWasOne && (aRawData & mask)) {
mark(tSendBEOMarkLength);
space(BEO_PULSE_LENGTH_ONE - tSendBEOMarkLength);
tLastBitValueWasOne = true;
} else {
mark(tSendBEOMarkLength);
space(BEO_PULSE_LENGTH_EQUAL - tSendBEOMarkLength);
}
}
// Stop
mark(tSendBEOMarkLength);
space(BEO_PULSE_LENGTH_TRAILING_BIT - tSendBEOMarkLength);
mark(tSendBEOMarkLength);
#else
(void) aRawData;
(void) aBits;
(void) aUseDatalinkTiming;
(void) aBackToBack;
#endif
}
#define BEO_MATCH_DELTA (BEO_UNIT / 2 - MICROS_PER_TICK)
static bool matchBeoLength(uint16_t aMeasuredTicks, uint16_t aMatchValueMicros) {
const uint16_t tMeasuredMicros = aMeasuredTicks * MICROS_PER_TICK;
return aMatchValueMicros - BEO_MATCH_DELTA < tMeasuredMicros && tMeasuredMicros < aMatchValueMicros + BEO_MATCH_DELTA;
}
bool IRrecv::decodeBangOlufsen() {
#if defined(ENABLE_BEO_WITHOUT_FRAME_GAP)
if (decodedIRData.rawlen != 6 && decodedIRData.rawlen < 36) { // 16 bits minimum
#else
if (decodedIRData.rawlen < 44) { // 16 bits minimum
#endif
return false;
}
#if defined(SUPPORT_BEO_DATALINK_TIMING_FOR_DECODE)
uint16_t protocolMarkLength = 0; // contains BEO_IR_MARK or BEO_DATALINK_MARK depending of 4. mark received
uint64_t tDecodedRawData = 0;
#else
uint32_t tDecodedRawData = 0;
#endif
uint8_t tLastDecodedBitValue = 0; // the last start bit is assumed to be zero
uint8_t tPulseNumber = 0;
uint8_t tBitNumber = 0;
BEO_TRACE_PRINT(F("Pre gap: "));
BEO_TRACE_PRINT(decodedIRData.initialGapTicks * 50);
BEO_TRACE_PRINT(F(" raw len: "));
BEO_TRACE_PRINTLN(decodedIRData.rawlen);
#if defined(ENABLE_BEO_WITHOUT_FRAME_GAP)
/*
* Check if we have the AGC part of the first frame, i.e. start bit 1 and 2.
*/
if (decodedIRData.rawlen == 6) {
if ((matchMark(decodedIRData.rawDataPtr->rawbuf[3], BEO_IR_MARK)
|| matchMark(decodedIRData.rawDataPtr->rawbuf[3], BEO_DATALINK_MARK))
&& (matchSpace(decodedIRData.rawDataPtr->rawbuf[4], BEO_PULSE_LENGTH_ZERO - BEO_IR_MARK)
|| matchSpace(decodedIRData.rawDataPtr->rawbuf[4], BEO_PULSE_LENGTH_ZERO - BEO_DATALINK_MARK))) {
BEO_TRACE_PRINT(::getProtocolString(BANG_OLUFSEN));
BEO_TRACE_PRINTLN(F("B&O: AGC only part (start bits 1 + 2 of 4) detected"));
} else {
return false; // no B&O protocol
}
} else {
/*
* Check if leading gap is trailing bit of first frame
*/
if (!matchSpace(decodedIRData.initialGapTicks, BEO_PULSE_LENGTH_START_BIT)) {
BEO_TRACE_PRINT(::getProtocolString(BANG_OLUFSEN));
BEO_TRACE_PRINTLN(F(": Leading gap is wrong")); // Leading gap is trailing bit of first frame
return false; // no B&O protocol
}
if (matchMark(decodedIRData.rawDataPtr->rawbuf[1], BEO_IR_MARK)) {
# if defined(SUPPORT_BEO_DATALINK_TIMING_FOR_DECODE)
protocolMarkLength = BEO_IR_MARK;
} else if (matchMark(decodedIRData.rawDataPtr->rawbuf[1], BEO_DATALINK_MARK)) {
protocolMarkLength = BEO_DATALINK_MARK;
# endif
} else {
BEO_TRACE_PRINT(::getProtocolString(BANG_OLUFSEN));
BEO_TRACE_PRINTLN(F(": mark length is wrong"));
return false;
}
// skip first zero header bit
for (uint8_t tRawBufferMarkIndex = 3; tRawBufferMarkIndex < decodedIRData.rawlen; tRawBufferMarkIndex += 2) {
#else
for (uint8_t tRawBufferMarkIndex = 1; tRawBufferMarkIndex < decodedIRData.rawlen; tRawBufferMarkIndex += 2) {
#endif // defined(ENABLE_BEO_WITHOUT_FRAME_GAP)
uint16_t markLength = decodedIRData.rawDataPtr->rawbuf[tRawBufferMarkIndex];
uint16_t spaceLength = decodedIRData.rawDataPtr->rawbuf[tRawBufferMarkIndex + 1];
BEO_TRACE_PRINT(tPulseNumber);
BEO_TRACE_PRINT(' ');
BEO_TRACE_PRINT(markLength * MICROS_PER_TICK);
BEO_TRACE_PRINT(' ');
BEO_TRACE_PRINT(spaceLength * MICROS_PER_TICK);
BEO_TRACE_PRINT(F(" ("));
BEO_TRACE_PRINT((markLength + spaceLength) * MICROS_PER_TICK);
BEO_TRACE_PRINTLN(F(") "));
#if !defined(ENABLE_BEO_WITHOUT_FRAME_GAP)
/*
* Handle the first 4 start bits
* Check if the 3. bit is the long start bit. If we see the long start bit earlier, synchronize bit counter.
*/
if (tPulseNumber < 4) {
if (tPulseNumber < 2) {
// bit 0 and 1
if (matchSpace(spaceLength, BEO_PULSE_LENGTH_START_BIT - BEO_IR_MARK)) {
BEO_TRACE_PRINTLN(F(": detected long start bit -> synchronize state now"));
tPulseNumber = 2;
}
} else {
if (tPulseNumber == 3) {
if (matchMark(markLength, BEO_IR_MARK)) {
# if defined(SUPPORT_BEO_DATALINK_TIMING_FOR_DECODE)
protocolMarkLength = BEO_IR_MARK;
} else if (matchMark(markLength, BEO_DATALINK_MARK)) {
protocolMarkLength = BEO_DATALINK_MARK;
# endif
} else {
BEO_DEBUG_PRINT(::getProtocolString(BANG_OLUFSEN));
BEO_DEBUG_PRINTLN(F(": 4. (start) mark length is wrong"));
return false;
}
}
// bit 2 and 3
if (!matchBeoLength(markLength + spaceLength,
(tPulseNumber == 2) ? BEO_PULSE_LENGTH_START_BIT : BEO_PULSE_LENGTH_ZERO)) {
BEO_DEBUG_PRINT(::getProtocolString(BANG_OLUFSEN));
BEO_DEBUG_PRINTLN(F(": Start length is wrong"));
return false;
}
}
} else {
#endif // !defined(ENABLE_BEO_WITHOUT_FRAME_GAP)
/*
* Decode header / data
* First check for length of mark
*/
#if defined(SUPPORT_BEO_DATALINK_TIMING_FOR_DECODE)
if (!matchMark(markLength, protocolMarkLength)) {
#else
if (!matchMark(markLength, BEO_IR_MARK)) {
#endif
BEO_DEBUG_PRINT(::getProtocolString(BANG_OLUFSEN));
BEO_DEBUG_PRINTLN(F(": Mark length is wrong"));
return false;
}
/*
* Check for stop after receiving at least 8 bits for data and 4 bits for header
*/
if (tBitNumber > BEO_DATA_BITS + 4) {
if (matchBeoLength(markLength + spaceLength, BEO_PULSE_LENGTH_TRAILING_BIT)) {
BEO_DEBUG_PRINT(::getProtocolString(BANG_OLUFSEN));
BEO_DEBUG_PRINTLN(F(": Trailing bit detected"));
break;
}
#if !defined(ENABLE_BEO_WITHOUT_FRAME_GAP)
if (tRawBufferMarkIndex >= decodedIRData.rawlen - 3) { // (rawlen - 3) is index of trailing bit mark
BEO_DEBUG_PRINT(::getProtocolString(BANG_OLUFSEN));
BEO_DEBUG_PRINTLN(F(": End of buffer, but no trailing bit detected"));
return false;
}
#endif
}
/*
* Decode bit
*/
if (tLastDecodedBitValue == 0 && matchBeoLength(markLength + spaceLength, BEO_PULSE_LENGTH_ONE)) {
tLastDecodedBitValue = 1;
} else if (tLastDecodedBitValue == 1 && matchBeoLength(markLength + spaceLength, BEO_PULSE_LENGTH_ZERO)) {
tLastDecodedBitValue = 0;
} else if (!matchBeoLength(markLength + spaceLength, BEO_PULSE_LENGTH_EQUAL)) {
BEO_DEBUG_PRINT(::getProtocolString(BANG_OLUFSEN));
BEO_DEBUG_PRINT(F(": Index="));
BEO_DEBUG_PRINT(tRawBufferMarkIndex);
BEO_DEBUG_PRINT(F(" Length "));
BEO_DEBUG_PRINT((markLength + spaceLength) * MICROS_PER_TICK);
BEO_DEBUG_PRINTLN(F(" is wrong"));
return false;
}
tDecodedRawData <<= 1;
tDecodedRawData |= tLastDecodedBitValue;
++tBitNumber;
BEO_TRACE_PRINT(F("Bits "));
BEO_TRACE_PRINT(tBitNumber);
BEO_TRACE_PRINT(F(" "));
BEO_TRACE_PRINT(uint32_t(tDecodedRawData >> BEO_DATA_BITS), HEX);
BEO_TRACE_PRINT(F(" "));
BEO_TRACE_PRINTLN(uint8_t(tDecodedRawData & ((1 << BEO_DATA_BITS) - 1)), HEX);
// End of bit decode
#if !defined(ENABLE_BEO_WITHOUT_FRAME_GAP)
}
#else
/*
* Check for last bit after decoding it
*/
if (tRawBufferMarkIndex >= decodedIRData.rawlen - 3) { // (rawlen - 3) is index of last bit mark
BEO_TRACE_PRINT(::getProtocolString(BANG_OLUFSEN));
BEO_TRACE_PRINTLN(F(": Last bit reached"));
break;
}
#endif
++tPulseNumber;
}
#if defined(ENABLE_BEO_WITHOUT_FRAME_GAP)
}
#endif
decodedIRData.protocol = BANG_OLUFSEN;
decodedIRData.address = tDecodedRawData >> BEO_DATA_BITS; // lower header tBitNumber
decodedIRData.command = tDecodedRawData & ((1 << BEO_DATA_BITS) - 1); // lower 8 tBitNumber
decodedIRData.extra = tDecodedRawData >> (BEO_DATA_BITS + 16); // upper header tBitNumber
decodedIRData.numberOfBits = tBitNumber;
decodedIRData.flags = IRDATA_FLAGS_IS_MSB_FIRST;
decodedIRData.decodedRawData = tDecodedRawData;
return true;
}
#endif // _IR_BANG_OLUFSEN_HPP
+123
View File
@@ -0,0 +1,123 @@
/*
* ir_BoseWave.cpp
*
* Contains functions for receiving and sending Bose IR Protocol
*
* This file is part of Arduino-IRremote https://github.com/Arduino-IRremote/Arduino-IRremote.
*
*/
#ifndef _IR_BOSEWAVE_HPP
#define _IR_BOSEWAVE_HPP
#if defined(DEBUG) && !defined(LOCAL_DEBUG)
#define LOCAL_DEBUG
#else
//#define LOCAL_DEBUG // This enables debug output only for this file
#endif
/** \addtogroup Decoder Decoders and encoders for different protocols
* @{
*/
//==============================================================================
// BBBB OOO SSSS EEEEE
// B B O O S E
// BB B O O SSS EEEE
// B B O O S E
// BBBB OOO SSSS EEEEE
//==============================================================================
// see http://lirc.sourceforge.net/remotes/bose/WAVERADIO
// see: https://www.mikrocontroller.net/articles/IRMP_-_english#BOSE
//
// Support for Bose Wave Radio CD initially provided by https://github.com/uvotguy.
//
// As seen on my oscilloscope, there is no repeat code. Instead, when I
// press and hold a button on my remote, it sends a command, makes a 51.2ms space,
// and resends the command again, and so on.
// 38 kHz, LSB first, 1 start bit + 8 bit data + 8 bit inverted data + 1 stop bit.
#define BOSEWAVE_BITS 16 // Command and inverted command
#define BOSEWAVE_HEADER_MARK 1014 // 1014 are 39 clock periods (I counted 3 times!)
#define BOSEWAVE_HEADER_SPACE 1468 // 1468(measured), 1456 are 56 clock periods
#define BOSEWAVE_BIT_MARK 520 // 520 are 20 clock periods
#define BOSEWAVE_ZERO_SPACE 468 // 468 are 18 clock periods
#define BOSEWAVE_ONE_SPACE 1468 // 1468(measured), 1456 are 56 clock periods
#define BOSEWAVE_REPEAT_PERIOD 75000
#define BOSEWAVE_REPEAT_DISTANCE 50000
#define BOSEWAVE_MAXIMUM_REPEAT_DISTANCE 62000
struct PulseDistanceWidthProtocolConstants BoseWaveProtocolConstants = { BOSEWAVE, BOSEWAVE_KHZ, BOSEWAVE_HEADER_MARK,
BOSEWAVE_HEADER_SPACE, BOSEWAVE_BIT_MARK, BOSEWAVE_ONE_SPACE, BOSEWAVE_BIT_MARK, BOSEWAVE_ZERO_SPACE, PROTOCOL_IS_LSB_FIRST
, (BOSEWAVE_REPEAT_PERIOD / MICROS_IN_ONE_MILLI), NULL };
/************************************
* Start of send and decode functions
************************************/
void IRsend::sendBoseWave(uint8_t aCommand, int_fast8_t aNumberOfRepeats) {
// send 8 command bits and then 8 inverted command bits LSB first
uint16_t tData = ((~aCommand) << 8) | aCommand;
sendPulseDistanceWidth(&BoseWaveProtocolConstants, tData, BOSEWAVE_BITS, aNumberOfRepeats);
}
bool IRrecv::decodeBoseWave() {
if (!checkHeader(&BoseWaveProtocolConstants)) {
return false;
}
// Check we have enough data +4 for initial gap, start bit mark and space + stop bit mark
if (decodedIRData.rawlen != (2 * BOSEWAVE_BITS) + 4) {
IR_DEBUG_PRINT(F("Bose: "));
IR_DEBUG_PRINT(F("Data length="));
IR_DEBUG_PRINT(decodedIRData.rawlen);
IR_DEBUG_PRINTLN(F(" is not 36"));
return false;
}
if (!decodePulseDistanceWidthData(&BoseWaveProtocolConstants, BOSEWAVE_BITS)) {
#if defined(LOCAL_DEBUG)
Serial.print(F("Bose: "));
Serial.println(F("Decode failed"));
#endif
return false;
}
// Stop bit
if (!matchMark(decodedIRData.rawDataPtr->rawbuf[3 + (2 * BOSEWAVE_BITS)], BOSEWAVE_BIT_MARK)) {
#if defined(LOCAL_DEBUG)
Serial.print(F("Bose: "));
Serial.println(F("Stop bit mark length is wrong"));
#endif
return false;
}
// Success
// decodedIRData.flags = IRDATA_FLAGS_IS_LSB_FIRST; // Not required, since this is the start value
uint16_t tDecodedValue = decodedIRData.decodedRawData;
uint8_t tCommandNotInverted = tDecodedValue & 0xFF; // comes first and is in the lower bits (LSB first :-))
uint8_t tCommandInverted = tDecodedValue >> 8;
// parity check for command. Use this variant to avoid compiler warning "comparison of promoted ~unsigned with unsigned [-Wsign-compare]"
if ((tCommandNotInverted ^ tCommandInverted) != 0xFF) {
#if defined(LOCAL_DEBUG)
Serial.print(F("Bose: "));
Serial.println(F("Command and inverted command check failed"));
#endif
return false;
}
decodedIRData.command = tCommandNotInverted;
decodedIRData.numberOfBits = BOSEWAVE_BITS;
decodedIRData.protocol = BOSEWAVE;
// check for repeat
checkForRepeatSpaceTicksAndSetFlag(BOSEWAVE_MAXIMUM_REPEAT_DISTANCE / MICROS_PER_TICK);
return true;
}
/** @}*/
#if defined(LOCAL_DEBUG)
#undef LOCAL_DEBUG
#endif
#endif // _IR_BOSEWAVE_HPP
+313
View File
@@ -0,0 +1,313 @@
/*
* ir_Denon.cpp
*
* Contains functions for receiving and sending Denon/Sharp IR Protocol
*
* This file is part of Arduino-IRremote https://github.com/Arduino-IRremote/Arduino-IRremote.
*
************************************************************************************
* MIT License
*
* Copyright (c) 2020-2023 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_DENON_HPP
#define _IR_DENON_HPP
#if defined(DEBUG) && !defined(LOCAL_DEBUG)
#define LOCAL_DEBUG
#else
//#define LOCAL_DEBUG // This enables debug output only for this file
#endif
/** \addtogroup Decoder Decoders and encoders for different protocols
* @{
*/
//==============================================================================
// DDDD EEEEE N N OOO N N
// D D E NN N O O NN N
// D D EEE N N N O O N N N
// D D E N NN O O N NN
// DDDD EEEEE N N OOO N N
//==============================================================================
// SSSS H H AAA RRRR PPPP
// S H H A A R R P P
// SSS HHHHH AAAAA RRRR PPPP
// S H H A A R R P
// SSSS H H A A R R P
//==============================================================================
/*
Protocol=Denon Address=0x11 Command=0x76 Raw-Data=0xED1 15 bits LSB first
+ 200,-1800 + 300,- 750 + 300,- 800 + 200,- 800
+ 250,-1800 + 250,- 800 + 250,-1800 + 300,-1750
+ 300,- 750 + 300,-1800 + 250,-1800 + 250,-1850
+ 250,- 750 + 300,- 800 + 250,- 800 + 250
Sum: 23050
Denon/Sharp variant
Protocol=Denon Address=0x11 Command=0x76 Raw-Data=0x4ED1 15 bits LSB first
+ 200,-1800 + 300,- 750 + 250,- 800 + 250,- 750
+ 300,-1800 + 250,- 800 + 250,-1800 + 300,-1750
+ 300,- 750 + 300,-1800 + 250,-1800 + 250,-1800
+ 300,- 750 + 300,- 750 + 300,-1800 + 250
Sum: 23050
*/
/*
* https://www.mikrocontroller.net/articles/IRMP_-_english#DENON
* Denon published all their IR codes:
* http://assets.denon.com/documentmaster/us/denon%20master%20ir%20hex.xls
* Example:
* 0000 006D 0000 0020 000A 001E 000A 0046 000A 001E 000A 001E 000A 001E // 5 address bits
* 000A 001E 000A 001E 000A 0046 000A 0046 000A 0046 000A 001E 000A 0046 000A 0046 // 8 command bits
* 000A 001E 000A 001E 000A 0679 // 2 frame bits 0,0 + stop bit + space for AutoRepeat
* 000A 001E 000A 0046 000A 001E 000A 001E 000A 001E // 5 address bits
* 000A 0046 000A 0046 000A 001E 000A 001E 000A 001E 000A 0046 000A 001E 000A 001E // 8 inverted command bits
* 000A 0046 000A 0046 000A 0679 // 2 frame bits 1,1 + stop bit + space for Repeat
* From analyzing the codes for Tuner preset 1 to 8 in tab Main Zone ID#1 it is obvious, that the protocol is LSB first at least for command.
* All Denon codes with 32 as 3. value use the Kaseyikyo Denon variant.
*/
// LSB first, no start bit, 5 address + 8 command + 2 frame (0,0) + 1 stop bit - each frame 2 times
// Every frame is auto repeated with a space period of 45 ms and the command and frame inverted to (1,1) or (0,1) for SHARP.
//
#define DENON_ADDRESS_BITS 5
#define DENON_COMMAND_BITS 8
#define DENON_FRAME_BITS 2 // 00/10 for 1. frame Denon/Sharp, inverted for autorepeat frame
#define DENON_BITS (DENON_ADDRESS_BITS + DENON_COMMAND_BITS + DENON_FRAME_BITS) // 15 - The number of bits in the command
#define DENON_UNIT 260
#define DENON_BIT_MARK DENON_UNIT // The length of a Bit:Mark
#define DENON_ONE_SPACE (7 * DENON_UNIT) // 1820 // The length of a Bit:Space for 1's
#define DENON_ZERO_SPACE (3 * DENON_UNIT) // 780 // The length of a Bit:Space for 0's
#define DENON_AUTO_REPEAT_DISTANCE 45000 // Every frame is auto repeated with a space period of 45 ms and the command and frame inverted.
#define DENON_REPEAT_PERIOD 110000 // Commands are repeated every 110 ms (measured from start to start) for as long as the key on the remote control is held down.
// for old decoder
#define DENON_HEADER_MARK DENON_UNIT // The length of the Header:Mark
#define DENON_HEADER_SPACE (3 * DENON_UNIT) // 780 // The length of the Header:Space
struct PulseDistanceWidthProtocolConstants DenonProtocolConstants = { DENON, DENON_KHZ, DENON_HEADER_MARK, DENON_HEADER_SPACE,
DENON_BIT_MARK, DENON_ONE_SPACE, DENON_BIT_MARK, DENON_ZERO_SPACE, PROTOCOL_IS_LSB_FIRST,
(DENON_REPEAT_PERIOD / MICROS_IN_ONE_MILLI), NULL };
/************************************
* Start of send and decode functions
************************************/
void IRsend::sendSharp(uint8_t aAddress, uint8_t aCommand, int_fast8_t aNumberOfRepeats) {
sendDenon(aAddress, aCommand, aNumberOfRepeats, true);
}
void IRsend::sendDenon(uint8_t aAddress, uint8_t aCommand, int_fast8_t aNumberOfRepeats, bool aSendSharp) {
// Set IR carrier frequency
enableIROut (DENON_KHZ); // 38 kHz
// Add frame marker for sharp
uint16_t tCommand = aCommand;
if (aSendSharp) {
tCommand |= 0x0200; // the 2 upper bits are 00 for Denon and 10 for Sharp
}
uint16_t tData = aAddress | ((uint16_t) tCommand << DENON_ADDRESS_BITS);
uint16_t tInvertedData = (tData ^ 0x7FE0); // Command and frame (upper 10 bits) are inverted
uint_fast8_t tNumberOfCommands = aNumberOfRepeats + 1;
while (tNumberOfCommands > 0) {
// Data
sendPulseDistanceWidthData(&DenonProtocolConstants, tData, DENON_BITS);
// Inverted autorepeat frame
delay(DENON_AUTO_REPEAT_DISTANCE / MICROS_IN_ONE_MILLI);
sendPulseDistanceWidthData(&DenonProtocolConstants, tInvertedData, DENON_BITS);
tNumberOfCommands--;
// skip last delay!
if (tNumberOfCommands > 0) {
// send repeated command with a fixed space gap
delay( DENON_AUTO_REPEAT_DISTANCE / MICROS_IN_ONE_MILLI);
}
}
}
bool IRrecv::decodeSharp() {
return decodeDenon();
}
bool IRrecv::decodeDenon() {
// we have no start bit, so check for the exact amount of data bits
// Check we have the right amount of data (32). The + 2 is for initial gap + stop bit mark
if (decodedIRData.rawlen != (2 * DENON_BITS) + 2) {
IR_DEBUG_PRINT(F("Denon: "));
IR_DEBUG_PRINT(F("Data length="));
IR_DEBUG_PRINT(decodedIRData.rawlen);
IR_DEBUG_PRINTLN(F(" is not 32"));
return false;
}
// Try to decode as Denon protocol
if (!decodePulseDistanceWidthData(&DenonProtocolConstants, DENON_BITS, 1)) {
#if defined(LOCAL_DEBUG)
Serial.print(F("Denon: "));
Serial.println(F("Decode failed"));
#endif
return false;
}
// Check for stop mark
if (!matchMark(decodedIRData.rawDataPtr->rawbuf[(2 * DENON_BITS) + 1], DENON_HEADER_MARK)) {
#if defined(LOCAL_DEBUG)
Serial.print(F("Denon: "));
Serial.println(F("Stop bit mark length is wrong"));
#endif
return false;
}
// Success
decodedIRData.address = decodedIRData.decodedRawData & 0x1F;
decodedIRData.command = decodedIRData.decodedRawData >> DENON_ADDRESS_BITS;
uint8_t tFrameBits = (decodedIRData.command >> 8) & 0x03;
decodedIRData.command &= 0xFF;
// Check for (auto) repeat
if (decodedIRData.initialGapTicks < ((DENON_AUTO_REPEAT_DISTANCE + (DENON_AUTO_REPEAT_DISTANCE / 4)) / MICROS_PER_TICK)) {
repeatCount++;
if (repeatCount > 1) { // skip first auto repeat
decodedIRData.flags = IRDATA_FLAGS_IS_REPEAT;
}
if (tFrameBits & 0x01) {
/*
* Here we are in the auto repeated frame with the inverted command
*/
#if defined(LOCAL_DEBUG)
Serial.print(F("Denon: "));
Serial.println(F("Autorepeat received="));
#endif
decodedIRData.flags |= IRDATA_FLAGS_IS_AUTO_REPEAT;
// Check parity of consecutive received commands. There is no parity in one data set.
if ((uint8_t) lastDecodedCommand != (uint8_t)(~decodedIRData.command)) {
decodedIRData.flags |= IRDATA_FLAGS_PARITY_FAILED;
#if defined(LOCAL_DEBUG)
Serial.print(F("Denon: "));
Serial.print(F("Parity check for repeat failed. Last command="));
Serial.print(lastDecodedCommand, HEX);
Serial.print(F(" current="));
Serial.println(~decodedIRData.command, HEX);
#endif
}
// always take non inverted command
decodedIRData.command = lastDecodedCommand;
}
// repeated command here
if (tFrameBits == 1 || tFrameBits == 2) {
decodedIRData.protocol = SHARP;
} else {
decodedIRData.protocol = DENON;
}
} else {
repeatCount = 0;
// first command here
if (tFrameBits == 2) {
decodedIRData.protocol = SHARP;
} else {
decodedIRData.protocol = DENON;
}
}
decodedIRData.numberOfBits = DENON_BITS;
return true;
}
/*********************************************************************************
* Old deprecated functions, kept for backward compatibility to old 2.0 tutorials
*********************************************************************************/
/*
* Only for backwards compatibility
*/
void IRsend::sendDenonRaw(uint16_t aRawData, int_fast8_t aNumberOfRepeats) {
sendDenon(aRawData >> (DENON_COMMAND_BITS + DENON_FRAME_BITS), (aRawData >> DENON_FRAME_BITS) & 0xFF, aNumberOfRepeats);
}
/*
* Old function with parameter data
*/
void IRsend::sendDenon(unsigned long data, int nbits) {
// Set IR carrier frequency
enableIROut (DENON_KHZ);
#if !(defined(__AVR_ATtiny25__) || defined(__AVR_ATtiny45__) || defined(__AVR_ATtiny85__) || defined(__AVR_ATtiny87__) || defined(__AVR_ATtiny167__))
// Serial.println(
// "The function sendDenon(data, nbits) is deprecated and may not work as expected! Use sendDenonRaw(data, NumberOfRepeats) or better sendDenon(Address, Command, NumberOfRepeats).");
#endif
// Header
mark(DENON_HEADER_MARK);
space(DENON_HEADER_SPACE);
// Data
sendPulseDistanceWidthData(DENON_BIT_MARK, DENON_ONE_SPACE, DENON_BIT_MARK, DENON_ZERO_SPACE, data, nbits,
PROTOCOL_IS_MSB_FIRST);
}
/*
* Old function without parameter aNumberOfRepeats
*/
void IRsend::sendSharp(uint16_t aAddress, uint16_t aCommand) {
sendDenon(aAddress, aCommand, true, 0);
}
bool IRrecv::decodeDenonOld(decode_results *aResults) {
// Check we have the right amount of data
if (decodedIRData.rawlen != 1 + 2 + (2 * DENON_BITS) + 1) {
return false;
}
// Check initial Mark+Space match
if (!matchMark(aResults->rawbuf[1], DENON_HEADER_MARK)) {
return false;
}
if (!matchSpace(aResults->rawbuf[2], DENON_HEADER_SPACE)) {
return false;
}
// Try to decode as Denon protocol.
if (!decodePulseDistanceWidthData(DENON_BITS, 3, DENON_BIT_MARK, DENON_ONE_SPACE, 0, PROTOCOL_IS_MSB_FIRST)) {
return false;
}
// Success
aResults->value = decodedIRData.decodedRawData;
aResults->bits = DENON_BITS;
aResults->decode_type = DENON;
decodedIRData.protocol = DENON;
return true;
}
/** @}*/
#if defined(LOCAL_DEBUG)
#undef LOCAL_DEBUG
#endif
#endif // _IR_DENON_HPP
@@ -0,0 +1,482 @@
/*
* ir_DistanceWidthProtocol.hpp
*
* Contains only the decoder functions for universal pulse width or pulse distance protocols!
* The send functions are used by almost all protocols and are therefore located in IRSend.hpp.
*
* If RAM is not more than 2k, the decoder only accepts mark or space durations up to 50 * 50 (MICROS_PER_TICK) = 2500 microseconds
* to save RAM space, otherwise it accepts durations up to 10 ms.
*
* This decoder tries to decode a pulse distance or pulse distance width with constant period (or pulse width - not enabled yet) protocol.
* 1. Analyze all space and mark length
* 2. Decide which protocol we have
* 3. Try to decode with the mark and space data found in step 1
* 4. Assume one start bit / header and one stop bit, since pulse distance data must have a stop bit!
* No data and address decoding, only raw data as result.
*
* Pulse distance data can be sent with the generic function as in SendDemo example line 155:
* https://github.com/Arduino-IRremote/Arduino-IRremote/blob/d51b540cb2ddf1424888d2d9e6b62fe1ef46859d/examples/SendDemo/SendDemo.ino#L155
* void sendPulseDistanceWidthData(unsigned int aOneMarkMicros, unsigned int aOneSpaceMicros, unsigned int aZeroMarkMicros,
* unsigned int aZeroSpaceMicros, uint32_t aData, uint8_t aNumberOfBits, bool aMSBfirst, bool aSendStopBit = false)
* The header must be sent manually with:
* IrSender.mark(MarkMicros)
* IrSender.space(SpaceMicros);
*
* Or send it by filling a DecodedRawDataArray and with the sendPulseDistanceWidthFromArray() function as in SendDemo example line 175:
* https://github.com/Arduino-IRremote/Arduino-IRremote/blob/d51b540cb2ddf1424888d2d9e6b62fe1ef46859d/examples/SendDemo/SendDemo.ino#L175
* sendPulseDistanceWidthFromArray(uint_fast8_t aFrequencyKHz, unsigned int aHeaderMarkMicros,
* unsigned int aHeaderSpaceMicros, unsigned int aOneMarkMicros, unsigned int aOneSpaceMicros, unsigned int aZeroMarkMicros,
* unsigned int aZeroSpaceMicros, uint32_t *aDecodedRawDataArray, unsigned int aNumberOfBits, uint8_t aFlags,
* unsigned int aRepeatPeriodMillis, int_fast8_t aNumberOfRepeats)
*
* This file is part of Arduino-IRremote https://github.com/Arduino-IRremote/Arduino-IRremote.
*
************************************************************************************
* MIT License
*
* Copyright (c) 2022-2024 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_DISTANCE_WIDTH_HPP
#define _IR_DISTANCE_WIDTH_HPP
#if !defined(DISTANCE_WIDTH_MAXIMUM_REPEAT_DISTANCE_MICROS)
#define DISTANCE_WIDTH_MAXIMUM_REPEAT_DISTANCE_MICROS 100000 // 100 ms, bit it is just a guess
#endif
#if defined(DEBUG) && !defined(LOCAL_DEBUG)
#define LOCAL_DEBUG
#else
//#define LOCAL_DEBUG // This enables debug output only for this file
#endif
#if !defined(DISTANCE_WIDTH_DECODER_DURATION_ARRAY_SIZE)
# if (defined(RAMEND) && RAMEND <= 0x8FF) || (defined(RAMSIZE) && RAMSIZE < 0x8FF)
#define DISTANCE_WIDTH_DECODER_DURATION_ARRAY_SIZE 50 // To save program space, the decoder only accepts mark or space durations up to 50 * 50 (MICROS_PER_TICK) = 2500 microseconds
# else
#define DISTANCE_WIDTH_DECODER_DURATION_ARRAY_SIZE 200 // The decoder accepts mark or space durations up to 200 * 50 (MICROS_PER_TICK) = 10 milliseconds
# endif
#endif
// Switch the decoding according to your needs
//#define USE_MSB_DECODING_FOR_DISTANCE_DECODER // If active, it resembles LG, otherwise LSB first as most other protocols e.g. NEC and Kaseikyo/Panasonic
/** \addtogroup Decoder Decoders and encoders for different protocols
* @{
*/
//=====================================================================================
// DDD III SSS TTTTTT AA N N CCC EEEE W W III DDD TTTTTT H H
// D D I S TT A A NN N C E W W I D D TT H H
// D D I SSS TT AAAA N N N C EEE W W W I D D TT HHHH
// D D I S TT A A N NN C E W W W I D D TT H H
// DDD III SSSS TT A A N N CCC EEEE W W III DDD TT H H
//=====================================================================================
// see: https://www.mikrocontroller.net/articles/IRMP_-_english#Codings
/*
Example output of UnitTest.ino for PulseWidth protocol:
Protocol=PulseWidth Raw-Data=0x87654321 32 bits LSB first
Send on a 8 bit platform with: IrSender.sendPulseDistanceWidth(38, 950, 550, 600, 300, 300, 300, 0x87654321, 32, PROTOCOL_IS_LSB_FIRST, <RepeatPeriodMillis>, <numberOfRepeats>);
rawData[66]:
-1088600
+ 950,- 550
+ 600,- 300 + 300,- 300 + 350,- 250 + 350,- 250
+ 350,- 300 + 600,- 300 + 300,- 300 + 300,- 300
+ 650,- 250 + 650,- 250 + 300,- 300 + 350,- 250
+ 350,- 300 + 300,- 300 + 600,- 300 + 300,- 300
+ 600,- 300 + 350,- 250 + 600,- 300 + 350,- 250
+ 350,- 300 + 600,- 300 + 600,- 300 + 300,- 300
+ 600,- 300 + 600,- 300 + 600,- 300 + 300,- 300
+ 300,- 300 + 300,- 300 + 350,- 250 + 650
Sum: 24500
Example output of UnitTest.ino for PulseDistanceWidth protocol:
Protocol=PulseDistance Raw-Data=0x76 7 bits LSB first
Send on a 8 bit platform with: IrSender.sendPulseDistanceWidth(38, 5950, 500, 550, 1450, 1550, 500, 0x76, 7, PROTOCOL_IS_LSB_FIRST, <RepeatPeriodMillis>, <numberOfRepeats>);
rawData[18]:
-1092450
+5950,- 500
+1500,- 500 + 500,-1450 + 550,-1450 +1550,- 450
+ 550,-1450 + 550,-1450 + 550,-1450 + 550
Sum: 20950
*/
#if defined(LOCAL_DEBUG)
void printDurations(uint8_t aArray[], uint8_t aMaxIndex) {
for (uint_fast8_t i = 0; i <= aMaxIndex; i++) {
//Print index at the beginning of a new line
if (i % 10 == 0) {
if (i == 0) {
Serial.print(' '); // indentation for the first index 0
} else {
Serial.println(); // new line for next indexes 10, 20 etc.
}
Serial.print(i);
Serial.print(F(": "));
}
// Print number of values in array and duration if != 0
Serial.print(aArray[i]);
if (aArray[i] != 0) {
Serial.print('x');
Serial.print(i * (uint16_t) MICROS_PER_TICK);
}
Serial.print(F(" | "));
}
Serial.println();
}
#endif
/*
* We count all consecutive (allow only one gap between) durations and compute the average.
* @return false if more than 2 distinct duration values found
*/
bool aggregateArrayCounts(uint8_t aArray[], uint8_t aMaxIndex, uint8_t *aShortIndex, uint8_t *aLongIndex) {
uint8_t tSum = 0;
uint16_t tWeightedSum = 0;
uint8_t tGapCount = 0;
for (uint_fast8_t i = 0; i <= aMaxIndex; i++) {
uint8_t tCurrentDurations = aArray[i];
if (tCurrentDurations != 0) {
// Add it to sum and remove array content
tSum += tCurrentDurations;
tWeightedSum += (tCurrentDurations * i);
aArray[i] = 0;
tGapCount = 0;
} else {
tGapCount++;
}
if (tSum != 0 && (i == aMaxIndex || tGapCount > 1)) {
/*
* Here we have a sum AND last element OR more than 1 consecutive gap
*/
uint8_t tAggregateIndex = (tWeightedSum + (tSum / 2)) / tSum; // with rounding
aArray[tAggregateIndex] = tSum; // disabling this line increases code size by 2 - unbelievable!
// store aggregate for later decoding
if (*aShortIndex == 0) {
*aShortIndex = tAggregateIndex;
} else if (*aLongIndex == 0) {
*aLongIndex = tAggregateIndex;
} else {
// we have 3 bins => this is likely no pulse width or distance protocol. e.g. it can be RC5.
return false;
}
// initialize for next aggregation
tSum = 0;
tWeightedSum = 0;
}
}
return true;
}
/*
* Try to decode a pulse distance or pulse width protocol.
* 1. Analyze all space and mark length
* 2. Decide if we have an pulse width or distance protocol
* 3. Try to decode with the mark and space data found in step 1
* No data and address decoding, only raw data as result.
*
* calloc() version is 700 bytes larger :-(
*/
bool IRrecv::decodeDistanceWidth() {
/*
* Array for up to 49 ticks / 2500 us (or 199 ticks / 10 ms us if RAM > 2k)
* 0 tick covers mark or space durations from 0 to 49 us, and 49 ticks from 2450 to 2499 us
*/
uint8_t tDurationArray[DISTANCE_WIDTH_DECODER_DURATION_ARRAY_SIZE];
/*
* Accept only protocols with at least 7 bits
*/
if (decodedIRData.rawlen < (2 * 7) + 4) {
IR_DEBUG_PRINT(F("PULSE_DISTANCE_WIDTH: "));
IR_DEBUG_PRINT(F("Data length="));
IR_DEBUG_PRINT(decodedIRData.rawlen);
IR_DEBUG_PRINTLN(F(" is less than 18"));
return false;
}
// Reset duration array
memset(tDurationArray, 0, DISTANCE_WIDTH_DECODER_DURATION_ARRAY_SIZE);
uint8_t tIndexOfMaxDuration = 0;
/*
* Count number of mark durations. Skip leading start and trailing stop bit.
*/
for (IRRawlenType i = 3; i < decodedIRData.rawlen - 2; i += 2) {
auto tDurationTicks = decodedIRData.rawDataPtr->rawbuf[i];
if (tDurationTicks < DISTANCE_WIDTH_DECODER_DURATION_ARRAY_SIZE) {
tDurationArray[tDurationTicks]++; // count duration if less than DISTANCE_WIDTH_DECODER_DURATION_ARRAY_SIZE
if (tIndexOfMaxDuration < tDurationTicks) {
tIndexOfMaxDuration = tDurationTicks;
}
} else {
#if defined(LOCAL_DEBUG)
Serial.print(F("PULSE_DISTANCE_WIDTH: "));
Serial.print(F("Mark "));
Serial.print(tDurationTicks * MICROS_PER_TICK);
Serial.print(F(" is longer than maximum "));
Serial.print(DISTANCE_WIDTH_DECODER_DURATION_ARRAY_SIZE * MICROS_PER_TICK);
Serial.print(F(" us. Index="));
Serial.println(i);
#endif
return false;
}
}
/*
* Aggregate mark counts to one duration bin
*/
uint8_t tMarkTicksShort = 0;
uint8_t tMarkTicksLong = 0;
bool tSuccess = aggregateArrayCounts(tDurationArray, tIndexOfMaxDuration, &tMarkTicksShort, &tMarkTicksLong);
#if defined(LOCAL_DEBUG)
Serial.println(F("Mark:"));
printDurations(tDurationArray, tIndexOfMaxDuration);
#endif
if (!tSuccess) {
#if defined(LOCAL_DEBUG)
Serial.print(F("PULSE_DISTANCE_WIDTH: "));
Serial.println(F("Mark aggregation failed, more than 2 distinct mark duration values found"));
#endif
return false;
}
// Reset duration array
memset(tDurationArray, 0, DISTANCE_WIDTH_DECODER_DURATION_ARRAY_SIZE);
/*
* Count number of space durations. Skip leading start and trailing stop bit.
*/
tIndexOfMaxDuration = 0;
for (IRRawlenType i = 4; i < decodedIRData.rawlen - 2; i += 2) {
auto tDurationTicks = decodedIRData.rawDataPtr->rawbuf[i];
if (tDurationTicks < DISTANCE_WIDTH_DECODER_DURATION_ARRAY_SIZE) {
tDurationArray[tDurationTicks]++;
if (tIndexOfMaxDuration < tDurationTicks) {
tIndexOfMaxDuration = tDurationTicks;
}
} else {
#if defined(LOCAL_DEBUG)
Serial.print(F("PULSE_DISTANCE_WIDTH: "));
Serial.print(F("Space "));
Serial.print(tDurationTicks * MICROS_PER_TICK);
Serial.print(F(" is longer than maximum "));
Serial.print(DISTANCE_WIDTH_DECODER_DURATION_ARRAY_SIZE * MICROS_PER_TICK);
Serial.print(F(" us. Index="));
Serial.println(i);
#endif
return false;
}
}
/*
* Aggregate space counts to one duration bin
*/
uint8_t tSpaceTicksShort = 0;
uint8_t tSpaceTicksLong = 0;
tSuccess = aggregateArrayCounts(tDurationArray, tIndexOfMaxDuration, &tSpaceTicksShort, &tSpaceTicksLong);
#if defined(LOCAL_DEBUG)
Serial.println(F("Space:"));
printDurations(tDurationArray, tIndexOfMaxDuration);
#endif
if (!tSuccess) {
#if defined(LOCAL_DEBUG)
Serial.print(F("PULSE_DISTANCE_WIDTH: "));
Serial.println(F("Space aggregation failed, more than 2 distinct space duration values found"));
#endif
return false;
}
/*
* Print characteristics of this protocol. Durations are in (50 us) ticks.
* Number of bits, start bit, start pause, long mark, long space, short mark, short space
*
* NEC: 32, 180, 90, 0, 34, 11, 11
* Samsung32: 32, 90, 90, 0, 34, 11, 11
* LG: 28, 180, 84, 0, 32, 10, 11
* JVC: 16, 168, 84, 0, 32, 10, 10
* Kaseikyo: 48. 69, 35, 0, 26, 9, 9
* Sony: 12|15|20, 48, 12, 24, 0, 12, 12 // the only known pulse width protocol
* Disney monorail
* model: 7, 120, 10, 30, 30, 10, 10 // PulseDistanceWidth. Can be seen as direct conversion of a 7 bit serial timing at 250 baud with a 6 ms start bit.
*/
#if defined(LOCAL_DEBUG)
Serial.print(F("DistanceWidthTimingInfoStruct: "));
Serial.print(decodedIRData.rawDataPtr->rawbuf[1] * MICROS_PER_TICK);
Serial.print(F(", "));
Serial.print(decodedIRData.rawDataPtr->rawbuf[2] * MICROS_PER_TICK);
Serial.print(F(", "));
Serial.print(tMarkTicksLong * MICROS_PER_TICK);
Serial.print(F(", "));
Serial.print(tSpaceTicksLong * MICROS_PER_TICK);
Serial.print(F(", "));
Serial.print(tMarkTicksShort * MICROS_PER_TICK);
Serial.print(F(", "));
Serial.println(tSpaceTicksShort * MICROS_PER_TICK);
#endif
#if RAW_BUFFER_LENGTH <= (512 -4)
uint_fast8_t tNumberOfBits;
#else
uint16_t tNumberOfBits;
#endif
tNumberOfBits = (decodedIRData.rawlen / 2) - 1;
if (tSpaceTicksLong > 0) {
// For PULSE_DISTANCE -including PULSE_DISTANCE_WIDTH- a stop bit is mandatory, for PULSE_WIDTH it is not required!
tNumberOfBits--; // Correct for stop bit
}
decodedIRData.numberOfBits = tNumberOfBits;
uint8_t tNumberOfAdditionalArrayValues = (tNumberOfBits - 1) / BITS_IN_RAW_DATA_TYPE;
/*
* We can have the following protocol timings
* PULSE_DISTANCE: Pause/spaces have different length and determine the bit value, longer space is 1. Pulses/marks can be constant, like NEC.
* PULSE_WIDTH: Pulses/marks have different length and determine the bit value, longer mark is 1. Pause/spaces can be constant, like Sony.
* PULSE_DISTANCE_WIDTH: Pulses/marks and pause/spaces have different length, often the bit length is constant, like MagiQuest. Can be decoded by PULSE_DISTANCE decoder.
*/
if (tMarkTicksLong == 0 && tSpaceTicksLong == 0) {
#if defined(LOCAL_DEBUG)
Serial.print(F("PULSE_DISTANCE: "));
Serial.println(F("Only 1 distinct duration value for each space and mark found"));
#endif
return false;
}
unsigned int tSpaceMicrosShort;
#if defined DECODE_STRICT_CHECKS
if(tMarkTicksLong > 0 && tSpaceTicksLong > 0) {
// We have different mark and space length here, so signal decodePulseDistanceWidthData() not to check against constant length decodePulseDistanceWidthData
tSpaceMicrosShort = 0;
}
#endif
tSpaceMicrosShort = tSpaceTicksShort * MICROS_PER_TICK;
unsigned int tMarkMicrosShort = tMarkTicksShort * MICROS_PER_TICK;
unsigned int tMarkMicrosLong = tMarkTicksLong * MICROS_PER_TICK;
unsigned int tSpaceMicrosLong = tSpaceTicksLong * MICROS_PER_TICK;
IRRawlenType tStartIndex = 3; // skip leading start bit for decoding.
for (uint_fast8_t i = 0; i <= tNumberOfAdditionalArrayValues; ++i) {
uint8_t tNumberOfBitsForOneDecode = tNumberOfBits;
/*
* Decode in 32/64 bit chunks. Only the last chunk can contain less than 32/64 bits
*/
if (tNumberOfBitsForOneDecode > BITS_IN_RAW_DATA_TYPE) {
tNumberOfBitsForOneDecode = BITS_IN_RAW_DATA_TYPE;
}
bool tResult;
if (tSpaceTicksLong > 0) {
/*
* Here short and long space durations found.
* Since parameters aOneMarkMicros and aOneSpaceMicros are equal, we only check tSpaceMicrosLong here.
*/
decodedIRData.protocol = PULSE_DISTANCE; // Sony + PULSE_DISTANCE_WIDTH
tResult = decodePulseDistanceWidthData(tNumberOfBitsForOneDecode, tStartIndex, tMarkMicrosShort, tSpaceMicrosLong,
tMarkMicrosShort,
#if defined(USE_MSB_DECODING_FOR_DISTANCE_DECODER)
true
#else
false
#endif
);
} else {
/*
* Here no long space duration found. => short and long mark durations found, check tMarkMicrosLong here
* This else case will most likely never be used, but it only requires 12 bytes additional programming space :-)
*/
decodedIRData.protocol = PULSE_WIDTH; // NEC etc.
tResult = decodePulseDistanceWidthData(tNumberOfBitsForOneDecode, tStartIndex, tMarkMicrosLong, tSpaceMicrosShort,
tMarkMicrosShort,
#if defined(USE_MSB_DECODING_FOR_DISTANCE_DECODER)
true
#else
false
#endif
);
}
if (!tResult) {
#if defined(LOCAL_DEBUG)
Serial.print(F("PULSE_WIDTH: "));
Serial.println(F("Decode failed"));
#endif
return false;
}
#if defined(LOCAL_DEBUG)
Serial.print(F("PULSE_WIDTH: "));
Serial.print(F("decodedRawData=0x"));
Serial.println(decodedIRData.decodedRawData, HEX);
#endif
// fill array with decoded data
decodedIRData.decodedRawDataArray[i] = decodedIRData.decodedRawData;
tStartIndex += (2 * BITS_IN_RAW_DATA_TYPE);
tNumberOfBits -= BITS_IN_RAW_DATA_TYPE;
}
#if defined(USE_MSB_DECODING_FOR_DISTANCE_DECODER)
decodedIRData.flags = IRDATA_FLAGS_IS_MSB_FIRST;
#endif
// Check for repeat
checkForRepeatSpaceTicksAndSetFlag(DISTANCE_WIDTH_MAXIMUM_REPEAT_DISTANCE_MICROS / MICROS_PER_TICK);
/*
* Store timing data to reproduce frame for sending
*/
decodedIRData.DistanceWidthTimingInfo.HeaderMarkMicros = (decodedIRData.rawDataPtr->rawbuf[1] * MICROS_PER_TICK);
decodedIRData.DistanceWidthTimingInfo.HeaderSpaceMicros = (decodedIRData.rawDataPtr->rawbuf[2] * MICROS_PER_TICK);
decodedIRData.DistanceWidthTimingInfo.ZeroMarkMicros = tMarkMicrosShort;
decodedIRData.DistanceWidthTimingInfo.ZeroSpaceMicros = tSpaceMicrosShort;
if (tMarkMicrosLong != 0) {
if (tSpaceMicrosLong == 0) {
// PULSE_DISTANCE, Sony
decodedIRData.DistanceWidthTimingInfo.OneMarkMicros = tMarkMicrosLong;
decodedIRData.DistanceWidthTimingInfo.OneSpaceMicros = tSpaceMicrosShort;
} else {
// PULSE_DISTANCE_WIDTH, we have 4 distinct values here
// Assume long space for a one when we have PulseDistanceWidth like for RS232, where a long inactive period (high) is a 1
decodedIRData.DistanceWidthTimingInfo.OneSpaceMicros = tSpaceMicrosLong;
decodedIRData.DistanceWidthTimingInfo.OneMarkMicros = tMarkMicrosShort;
decodedIRData.DistanceWidthTimingInfo.ZeroMarkMicros = tMarkMicrosLong;
// // Assume long mark for a one when we have PulseDistanceWidth
// decodedIRData.DistanceWidthTimingInfo.OneSpaceMicros = tSpaceMicrosShort;
// decodedIRData.DistanceWidthTimingInfo.ZeroSpaceMicros = tSpaceMicrosLong;
// decodedIRData.DistanceWidthTimingInfo.OneMarkMicros = tMarkMicrosLong;
}
} else {
// PULSE_WIDTH, NEC etc.
// Here tMarkMicrosLong is 0 => tSpaceMicrosLong != 0
decodedIRData.DistanceWidthTimingInfo.OneMarkMicros = tMarkMicrosShort;
decodedIRData.DistanceWidthTimingInfo.OneSpaceMicros = tSpaceMicrosLong;
}
#if defined(LOCAL_DEBUG)
Serial.print(F("DistanceWidthTimingInfo="));
IrReceiver.printDistanceWidthTimingInfo(&Serial, &decodedIRData.DistanceWidthTimingInfo);
Serial.println();
#endif
return true;
}
/** @}*/
#if defined(LOCAL_DEBUG)
#undef LOCAL_DEBUG
#endif
#endif // _IR_DISTANCE_WIDTH_HPP
+155
View File
@@ -0,0 +1,155 @@
/*
* ir_FAST.hpp
*
* Contains functions for receiving and sending FAST IR protocol
* with no address and 16 bit data, interpreted as 8 bit command and 8 bit inverted command
*
* This file is part of Arduino-IRremote https://github.com/Arduino-IRremote/Arduino-IRremote.
*
************************************************************************************
* MIT License
*
* Copyright (c) 2023 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_FAST_HPP
#define _IR_FAST_HPP
#include "TinyIR.h"
#if defined(DEBUG) && !defined(LOCAL_DEBUG)
#define LOCAL_DEBUG
#else
//#define LOCAL_DEBUG // This enables debug output only for this file
#endif
/** \addtogroup Decoder Decoders and encoders for different protocols
* @{
*/
// generated with https://patorjk.com/software/taag/#p=display&f=Alphabet&t=FAST
//==============================================================================
// FFFF AA SSS TTTTTT
// F A A S TT
// FFF AAAA SSS TT
// F A A S TT
// F A A SSSS TT
//==============================================================================
#include "TinyIR.h"
/*
Protocol=FAST Address=0x0 Command=0x76 Raw-Data=0x8976 16 bits LSB first
+2100,-1050
+ 550,- 500 + 550,-1550 + 550,-1550 + 550,- 500
+ 550,-1550 + 550,-1550 + 550,-1550 + 550,- 500
+ 550,-1550 + 550,- 500 + 550,- 500 + 550,-1550
+ 550,- 500 + 550,- 500 + 550,- 500 + 550,-1550
+ 550
Sum: 28900
*/
struct PulseDistanceWidthProtocolConstants FASTProtocolConstants = { FAST, FAST_KHZ, FAST_HEADER_MARK, FAST_HEADER_SPACE,
FAST_BIT_MARK, FAST_ONE_SPACE, FAST_BIT_MARK, FAST_ZERO_SPACE, PROTOCOL_IS_LSB_FIRST, (FAST_REPEAT_PERIOD / MICROS_IN_ONE_MILLI),
NULL };
/************************************
* Start of send and decode functions
************************************/
/**
* The FAST protocol repeats by skipping the header mark and space -> this leads to a poor repeat detection for JVC protocol.
*/
void IRsend::sendFAST(uint8_t aCommand, int_fast8_t aNumberOfRepeats) {
// Set IR carrier frequency
enableIROut(FAST_KHZ); // 38 kHz
uint_fast8_t tNumberOfCommands = aNumberOfRepeats + 1;
while (tNumberOfCommands > 0) {
mark(FAST_HEADER_MARK);
space(FAST_HEADER_SPACE);
sendPulseDistanceWidthData(&FASTProtocolConstants, aCommand | (((uint8_t)(~aCommand)) << 8), FAST_BITS);
tNumberOfCommands--;
// skip last delay!
if (tNumberOfCommands > 0) {
// send repeated command in a fixed raster
delay(FAST_REPEAT_DISTANCE / MICROS_IN_ONE_MILLI);
}
}
}
bool IRrecv::decodeFAST() {
// uint_fast8_t tRawlen = decodedIRData.rawlen; // Using a local variable does not improve code size
// Check we have the right amount of data (36). The +4 is for initial gap, start bit mark and space + stop bit mark.
if (decodedIRData.rawlen != ((2 * FAST_BITS) + 4)) {
IR_DEBUG_PRINT(F("FAST: "));
IR_DEBUG_PRINT(F("Data length="));
IR_DEBUG_PRINT(decodedIRData.rawlen);
IR_DEBUG_PRINTLN(F(" is not 36"));
return false;
}
if (!checkHeader(&FASTProtocolConstants)) {
return false;
}
if (!decodePulseDistanceWidthData(&FASTProtocolConstants, FAST_BITS)) {
#if defined(LOCAL_DEBUG)
Serial.print(F("FAST: "));
Serial.println(F("Decode failed"));
#endif
return false;
}
WordUnion tValue;
tValue.UWord = decodedIRData.decodedRawData;
if (tValue.UByte.LowByte != (uint8_t)~(tValue.UByte.HighByte)) {
#if defined(LOCAL_DEBUG)
Serial.print(F("FAST: "));
Serial.print(F("8 bit parity is not correct. Expected=0x"));
Serial.print((uint8_t)~(tValue.UByte.LowByte), HEX);
Serial.print(F(" received=0x"));
Serial.print(tValue.UByte.HighByte, HEX);
Serial.print(F(" data=0x"));
Serial.println(tValue.UWord, HEX);
#endif
decodedIRData.flags = IRDATA_FLAGS_PARITY_FAILED;
}
checkForRepeatSpaceTicksAndSetFlag(FAST_MAXIMUM_REPEAT_DISTANCE / MICROS_PER_TICK);
// Success
// decodedIRData.flags = IRDATA_FLAGS_IS_LSB_FIRST; // Not required, since this is the start value
decodedIRData.command = tValue.UByte.LowByte;
decodedIRData.address = 0; // No address for this protocol
decodedIRData.numberOfBits = FAST_BITS;
decodedIRData.protocol = FAST;
return true;
}
/** @}*/
#if defined(LOCAL_DEBUG)
#undef LOCAL_DEBUG
#endif
#endif // _IR_FAST_HPP
+260
View File
@@ -0,0 +1,260 @@
/*
* ir_JVC.hpp
*
* Contains functions for receiving and sending JVC IR Protocol in "raw" and standard format with 8 bit address and 8 bit command
*
* This file is part of Arduino-IRremote https://github.com/Arduino-IRremote/Arduino-IRremote.
*
************************************************************************************
* MIT License
*
* Copyright (c) 2017-2023 Kristian Lauszus, 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_JVC_HPP
#define _IR_JVC_HPP
#if defined(DEBUG) && !defined(LOCAL_DEBUG)
#define LOCAL_DEBUG
#else
//#define LOCAL_DEBUG // This enables debug output only for this file
#endif
/** \addtogroup Decoder Decoders and encoders for different protocols
* @{
*/
//==============================================================================
// JJJJJ V V CCCC
// J V V C
// J V V C
// J J V V C
// J V CCCC
//==============================================================================
/*
+8400,-4150
+ 550,-1550 + 550,- 500 + 550,- 500 + 550,- 500
+ 550,-1500 + 600,-1500 + 600,-1500 + 550,-1550
+ 550,- 500 + 550,-1550 + 550,-1550 + 550,- 500
+ 500,-1600 + 550,-1550 + 550,-1500 + 600,- 500
+ 550
Sum: 40350
*/
// https://www.sbprojects.net/knowledge/ir/jvc.php
// http://www.hifi-remote.com/johnsfine/DecodeIR.html#JVC
// IRP: {38k,525}<1,-1|1,-3>(16,-8,(D:8,F:8,1,-45)+)
// LSB first, 1 start bit + 8 bit address + 8 bit command + 1 stop bit.
// The JVC protocol repeats by skipping the header mark and space -> this leads to a poor repeat detection for JVC protocol.
// Some JVC devices require to send 3 repeats. https://github.com/Arduino-IRremote/Arduino-IRremote/issues/21
#define JVC_ADDRESS_BITS 8 // 8 bit address
#define JVC_COMMAND_BITS 8 // Command
#define JVC_BITS (JVC_ADDRESS_BITS + JVC_COMMAND_BITS) // 16 - The number of bits in the protocol
#define JVC_UNIT 526 // 20 periods of 38 kHz (526.315789)
#define JVC_HEADER_MARK (16 * JVC_UNIT) // 8400
#define JVC_HEADER_SPACE (8 * JVC_UNIT) // 4200
#define JVC_BIT_MARK JVC_UNIT // The length of a Bit:Mark
#define JVC_ONE_SPACE (3 * JVC_UNIT) // 1578 - The length of a Bit:Space for 1's
#define JVC_ZERO_SPACE JVC_UNIT // The length of a Bit:Space for 0's
#define JVC_REPEAT_DISTANCE (uint16_t)(45 * JVC_UNIT) // 23625 - Commands are repeated with a distance of 23 ms for as long as the key on the remote control is held down.
#define JVC_REPEAT_PERIOD 65000 // assume around 40 ms for a JVC frame. JVC IR Remotes: RM-SA911U, RM-SX463U have 45 ms period
struct PulseDistanceWidthProtocolConstants JVCProtocolConstants = { JVC, JVC_KHZ, JVC_HEADER_MARK, JVC_HEADER_SPACE, JVC_BIT_MARK,
JVC_ONE_SPACE, JVC_BIT_MARK, JVC_ZERO_SPACE, PROTOCOL_IS_LSB_FIRST, (JVC_REPEAT_PERIOD / MICROS_IN_ONE_MILLI), NULL };
/************************************
* Start of send and decode functions
************************************/
/**
* The JVC protocol repeats by skipping the header mark and space -> this leads to a poor repeat detection for JVC protocol.
*/
void IRsend::sendJVC(uint8_t aAddress, uint8_t aCommand, int_fast8_t aNumberOfRepeats) {
// Set IR carrier frequency
enableIROut (JVC_KHZ); // 38 kHz
if (aNumberOfRepeats < 0) {
// The JVC protocol repeats by skipping the header.
aNumberOfRepeats = 0;
} else {
mark(JVC_HEADER_MARK);
space(JVC_HEADER_SPACE);
}
uint_fast8_t tNumberOfCommands = aNumberOfRepeats + 1;
while (tNumberOfCommands > 0) {
// Address + command
sendPulseDistanceWidthData(&JVCProtocolConstants, aAddress | (aCommand << JVC_ADDRESS_BITS), JVC_BITS);
tNumberOfCommands--;
// skip last delay!
if (tNumberOfCommands > 0) {
// send repeated command in a fixed raster
delay(JVC_REPEAT_DISTANCE / MICROS_IN_ONE_MILLI);
}
}
}
bool IRrecv::decodeJVC() {
// uint_fast8_t tRawlen = decodedIRData.rawlen; // Using a local variable does not improve code size
// Check we have the right amount of data (36 or 34). The +4 is for initial gap, start bit mark and space + stop bit mark.
// +4 is for first frame, +2 is for repeats
if (decodedIRData.rawlen != ((2 * JVC_BITS) + 2) && decodedIRData.rawlen != ((2 * JVC_BITS) + 4)) {
IR_DEBUG_PRINT(F("JVC: "));
IR_DEBUG_PRINT(F("Data length="));
IR_DEBUG_PRINT(decodedIRData.rawlen);
IR_DEBUG_PRINTLN(F(" is not 34 or 36"));
return false;
}
if (decodedIRData.rawlen == ((2 * JVC_BITS) + 2)) {
/*
* Check for repeat
* Check leading space and first and last mark length
*/
if (decodedIRData.initialGapTicks < ((JVC_REPEAT_DISTANCE + (JVC_REPEAT_DISTANCE / 4) / MICROS_PER_TICK))
&& matchMark(decodedIRData.rawDataPtr->rawbuf[1], JVC_BIT_MARK)
&& matchMark(decodedIRData.rawDataPtr->rawbuf[decodedIRData.rawlen - 1], JVC_BIT_MARK)) {
/*
* We have a repeat here, so do not check for start bit
*/
decodedIRData.flags = IRDATA_FLAGS_IS_REPEAT | IRDATA_FLAGS_IS_LSB_FIRST;
decodedIRData.address = lastDecodedAddress;
decodedIRData.command = lastDecodedCommand;
decodedIRData.protocol = JVC;
}
} else {
if (!checkHeader(&JVCProtocolConstants)) {
return false;
}
if (!decodePulseDistanceWidthData(&JVCProtocolConstants, JVC_BITS)) {
#if defined(LOCAL_DEBUG)
Serial.print(F("JVC: "));
Serial.println(F("Decode failed"));
#endif
return false;
}
// Success
// decodedIRData.flags = IRDATA_FLAGS_IS_LSB_FIRST; // Not required, since this is the start value
decodedIRData.command = decodedIRData.decodedRawData >> JVC_ADDRESS_BITS; // upper 8 bits of LSB first value
decodedIRData.address = decodedIRData.decodedRawData & 0xFF; // lowest 8 bit of LSB first value
decodedIRData.numberOfBits = JVC_BITS;
decodedIRData.protocol = JVC;
}
return true;
}
/*********************************************************************************
* Old deprecated functions, kept for backward compatibility to old 2.0 tutorials
*********************************************************************************/
bool IRrecv::decodeJVCMSB(decode_results *aResults) {
unsigned int offset = 1; // Skip first space
// Check for repeat
if ((aResults->rawlen - 1 == 33) && matchMark(aResults->rawbuf[offset], JVC_BIT_MARK)
&& matchMark(aResults->rawbuf[aResults->rawlen - 1], JVC_BIT_MARK)) {
aResults->bits = 0;
aResults->value = 0xFFFFFFFF;
decodedIRData.flags = IRDATA_FLAGS_IS_REPEAT;
decodedIRData.protocol = JVC;
return true;
}
// Initial mark
if (!matchMark(aResults->rawbuf[offset], JVC_HEADER_MARK)) {
return false;
}
offset++;
// Check we have enough data - +3 for start bit mark and space + stop bit mark
if (aResults->rawlen <= (2 * JVC_BITS) + 3) {
IR_DEBUG_PRINT(F("Data length="));
IR_DEBUG_PRINT(aResults->rawlen);
IR_DEBUG_PRINTLN(F(" is too small. >= 36 is required."));
return false;
}
// Initial space
if (!matchSpace(aResults->rawbuf[offset], JVC_HEADER_SPACE)) {
return false;
}
offset++;
if (!decodePulseDistanceWidthData(JVC_BITS, offset, JVC_BIT_MARK, JVC_ONE_SPACE, 0, PROTOCOL_IS_MSB_FIRST)) {
return false;
}
// Stop bit
if (!matchMark(aResults->rawbuf[offset + (2 * JVC_BITS)], JVC_BIT_MARK)) {
#if defined(LOCAL_DEBUG)
Serial.println(F("Stop bit mark length is wrong"));
#endif
return false;
}
// Success
aResults->value = decodedIRData.decodedRawData;
aResults->bits = JVC_BITS;
aResults->decode_type = JVC;
decodedIRData.protocol = JVC;
return true;
}
/**
* With Send sendJVCMSB() you can send your old 32 bit codes.
* To convert one into the other, you must reverse the byte positions and then reverse all bit positions of each byte.
* Use bitreverse32Bit().
* Or write it as one binary string and reverse/mirror it.
* Example:
* 0xCB340102 byte reverse -> 02 01 34 CB bit reverse-> 40 80 2C D3.
* 0xCB340102 is binary 11001011001101000000000100000010.
* 0x40802CD3 is binary 01000000100000000010110011010011.
* If you read the first binary sequence backwards (right to left), you get the second sequence.
*/
void IRsend::sendJVCMSB(unsigned long data, int nbits, bool repeat) {
// Set IR carrier frequency
enableIROut (JVC_KHZ);
// Only send the Header if this is NOT a repeat command
if (!repeat) {
mark(JVC_HEADER_MARK);
space(JVC_HEADER_SPACE);
}
// Old version with MSB first Data
sendPulseDistanceWidthData(JVC_BIT_MARK, JVC_ONE_SPACE, JVC_BIT_MARK, JVC_ZERO_SPACE, data, nbits, PROTOCOL_IS_MSB_FIRST);
}
/** @}*/
#if defined(LOCAL_DEBUG)
#undef LOCAL_DEBUG
#endif
#endif // _IR_JVC_HPP
+322
View File
@@ -0,0 +1,322 @@
/*
* ir_Kaseikyo.hpp
*
* Contains functions for receiving and sending Kaseikyo/Panasonic IR Protocol in "raw" and standard format with 16 bit address + 8 bit command
*
* This file is part of Arduino-IRremote https://github.com/Arduino-IRremote/Arduino-IRremote.
*
************************************************************************************
* MIT License
*
* Copyright (c) 2020-2023 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_KASEIKYO_HPP
#define _IR_KASEIKYO_HPP
#if defined(DEBUG) && !defined(LOCAL_DEBUG)
#define LOCAL_DEBUG
#else
//#define LOCAL_DEBUG // This enables debug output only for this file
#endif
/** \addtogroup Decoder Decoders and encoders for different protocols
* @{
*/
//==============================================================================
// K K AA SSS EEEE III K K Y Y OOO
// K K A A S E I K K Y Y O O
// KK AAAA SSS EEE I KK Y O O
// K K A A S E I K K Y O O
// K K A A SSSS EEEE III K K Y OOO
//==============================================================================
//==============================================================================
// PPPP AAA N N AAA SSSS OOO N N IIIII CCCC
// P P A A NN N A A S O O NN N I C
// PPPP AAAAA N N N AAAAA SSS O O N N N I C
// P A A N NN A A S O O N NN I C
// P A A N N A A SSSS OOO N N IIIII CCCC
//==============================================================================
/*
Protocol=Panasonic Address=0xFF1 Command=0x76 Raw-Data=0x9976FF10 48 bits LSB first
+3450,-1700
+ 450,- 400 + 500,-1250 + 450,- 400 + 500,- 400
+ 450,- 400 + 400,- 450 + 500,- 350 + 450,- 450
+ 450,- 400 + 450,- 400 + 500,- 400 + 450,- 400
+ 450,- 400 + 500,-1250 + 450,- 400 + 500,- 350
+ 500,- 400 + 450,- 400 + 450,- 450 + 450,- 400
+ 450,-1250 + 500,- 400 + 450,- 400 + 450,- 400
+ 450,-1300 + 450,-1250 + 450,-1300 + 400,-1300
+ 450,-1300 + 450,-1250 + 450,-1250 + 500,-1250
+ 450,- 450 + 450,-1250 + 450,-1250 + 500,- 400
+ 450,-1250 + 450,-1300 + 450,-1250 + 450,- 450
+ 450,-1250 + 450,- 400 + 450,- 400 + 500,-1250
+ 450,-1250 + 450,- 400 + 500,- 400 + 450,-1250
+ 450
Sum: 64300
*/
// http://www.hifi-remote.com/johnsfine/DecodeIR.html#Panasonic
// http://www.hifi-remote.com/johnsfine/DecodeIR.html#Kaseikyo
// LSB first
// The first two (8-bit) bytes contains the vendor code.
// The next 4 bit is VendorID parity.
// The last byte is parity (XOR) of the 3 bytes before.
// There are multiple interpretations of the next fields:
// IRP: {37k,432}<1,-1|1,-3>(8,-4,M:8,N:8,X:4,D:4,S:8,F:8,G:8,1,-173)+ {X=M:4:0^M:4:4^N:4:0^N:4:4}
// 1. interpretation: 4 bit Device, 8 bitSubdevice and 8 bit function.
// 0_______ 1_______ 2______ 3_______ 4_______ 5_______
// 01234567 89ABCDEF 01234567 01234567 01234567 01234567
// 01000000 00100100 0110Dev_ Sub_Dev_ Fun____ XOR( B2, B3, B4) - Byte 0,1 and vendor parity showing Panasonic vendor code 0x2002.
// 1. interpretation: <start bit><VendorID:16><VendorID parity:4><Device:4><Subdevice:8><Function:8><Parity:8><stop bit>
// see: http://www.remotecentral.com/cgi-bin/mboard/rc-pronto/thread.cgi?26152
// 2. interpretation (Flipper Zero style): <start bit><VendorID:16><VendorID parity:4><Genre1:4><Genre2:4><Command:10><ID:2><Parity:8><stop bit>
// see: https://www.mikrocontroller.net/articles/IRMP_-_english#KASEIKYO
// Implemented is Samsung style: <start bit><VendorID:16><VendorID parity:4><Address:12><Command:8><Parity of VendorID parity, Address and Command:8><stop bit>
// which is derived from Samsung remotes and may not be optimal for Denon kind of Kaseikyo protokol usage.
//
#define KASEIKYO_VENDOR_ID_BITS 16
#define KASEIKYO_VENDOR_ID_PARITY_BITS 4
#define KASEIKYO_ADDRESS_BITS 12
#define KASEIKYO_COMMAND_BITS 8
#define KASEIKYO_PARITY_BITS 8
#define KASEIKYO_BITS (KASEIKYO_VENDOR_ID_BITS + KASEIKYO_VENDOR_ID_PARITY_BITS + KASEIKYO_ADDRESS_BITS + KASEIKYO_COMMAND_BITS + KASEIKYO_PARITY_BITS) // 48
#define KASEIKYO_UNIT 432 // 16 pulses of 37 kHz (432,432432) - Pronto 0x70 | 0x10
#define KASEIKYO_HEADER_MARK (8 * KASEIKYO_UNIT) // 3456
#define KASEIKYO_HEADER_SPACE (4 * KASEIKYO_UNIT) // 1728
#define KASEIKYO_BIT_MARK KASEIKYO_UNIT
#define KASEIKYO_ONE_SPACE (3 * KASEIKYO_UNIT) // 1296
#define KASEIKYO_ZERO_SPACE KASEIKYO_UNIT
#define KASEIKYO_AVERAGE_DURATION 56000
#define KASEIKYO_REPEAT_PERIOD 130000
#define KASEIKYO_REPEAT_DISTANCE (KASEIKYO_REPEAT_PERIOD - KASEIKYO_AVERAGE_DURATION) // 74 ms
#define KASEIKYO_MAXIMUM_REPEAT_DISTANCE (KASEIKYO_REPEAT_DISTANCE + (KASEIKYO_REPEAT_DISTANCE / 4)) // Just a guess
#define PANASONIC_VENDOR_ID_CODE 0x2002
#define DENON_VENDOR_ID_CODE 0x3254
#define MITSUBISHI_VENDOR_ID_CODE 0xCB23
#define SHARP_VENDOR_ID_CODE 0x5AAA
#define JVC_VENDOR_ID_CODE 0x0103
struct PulseDistanceWidthProtocolConstants KaseikyoProtocolConstants = { KASEIKYO, KASEIKYO_KHZ, KASEIKYO_HEADER_MARK,
KASEIKYO_HEADER_SPACE, KASEIKYO_BIT_MARK, KASEIKYO_ONE_SPACE, KASEIKYO_BIT_MARK, KASEIKYO_ZERO_SPACE, PROTOCOL_IS_LSB_FIRST
, (KASEIKYO_REPEAT_PERIOD / MICROS_IN_ONE_MILLI), NULL };
/************************************
* Start of send and decode functions
************************************/
/**
* Address can be interpreted as sub-device << 4 + 4 bit device
*/
void IRsend::sendKaseikyo(uint16_t aAddress, uint8_t aCommand, int_fast8_t aNumberOfRepeats, uint16_t aVendorCode) {
// Set IR carrier frequency
enableIROut (KASEIKYO_KHZ); // 37 kHz
// Vendor Parity
uint8_t tVendorParity = aVendorCode ^ (aVendorCode >> 8);
tVendorParity = (tVendorParity ^ (tVendorParity >> 4)) & 0xF;
#if __INT_WIDTH__ < 32
LongUnion tSendValue;
// Compute parity
tSendValue.UWord.LowWord = (aAddress << KASEIKYO_VENDOR_ID_PARITY_BITS) | tVendorParity; // set low nibble with vendor parity
tSendValue.UBytes[2] = aCommand;
tSendValue.UBytes[3] = aCommand ^ tSendValue.UBytes[0] ^ tSendValue.UBytes[1]; // 8 bit parity of 3 bytes command, address and vendor parity
IRRawDataType tRawKaseikyoData[2];
tRawKaseikyoData[0] = (uint32_t) tSendValue.UWord.LowWord << 16 | aVendorCode; // LSB of tRawKaseikyoData[0] is sent first
tRawKaseikyoData[1] = tSendValue.UWord.HighWord;
sendPulseDistanceWidthFromArray(&KaseikyoProtocolConstants, &tRawKaseikyoData[0], KASEIKYO_BITS, aNumberOfRepeats);
#else
LongLongUnion tSendValue;
tSendValue.UWords[0] = aVendorCode;
// Compute parity
tSendValue.UWords[1] = (aAddress << KASEIKYO_VENDOR_ID_PARITY_BITS) | tVendorParity; // set low nibble to parity
tSendValue.UBytes[4] = aCommand;
tSendValue.UBytes[5] = aCommand ^ tSendValue.UBytes[2] ^ tSendValue.UBytes[3]; // Parity
sendPulseDistanceWidth(&KaseikyoProtocolConstants, tSendValue.ULongLong, KASEIKYO_BITS, aNumberOfRepeats);
#endif
}
/**
* Stub using Kaseikyo with PANASONIC_VENDOR_ID_CODE
*/
void IRsend::sendPanasonic(uint16_t aAddress, uint8_t aCommand, int_fast8_t aNumberOfRepeats) {
sendKaseikyo(aAddress, aCommand, aNumberOfRepeats, PANASONIC_VENDOR_ID_CODE);
}
/**
* Stub using Kaseikyo with DENON_VENDOR_ID_CODE
*/
void IRsend::sendKaseikyo_Denon(uint16_t aAddress, uint8_t aCommand, int_fast8_t aNumberOfRepeats) {
sendKaseikyo(aAddress, aCommand, aNumberOfRepeats, DENON_VENDOR_ID_CODE);
}
/**
* Stub using Kaseikyo with MITSUBISHI_VENDOR_ID_CODE
*/
void IRsend::sendKaseikyo_Mitsubishi(uint16_t aAddress, uint8_t aCommand, int_fast8_t aNumberOfRepeats) {
sendKaseikyo(aAddress, aCommand, aNumberOfRepeats, MITSUBISHI_VENDOR_ID_CODE);
}
/**
* Stub using Kaseikyo with SHARP_VENDOR_ID_CODE
*/
void IRsend::sendKaseikyo_Sharp(uint16_t aAddress, uint8_t aCommand, int_fast8_t aNumberOfRepeats) {
sendKaseikyo(aAddress, aCommand, aNumberOfRepeats, SHARP_VENDOR_ID_CODE);
}
/**
* Stub using Kaseikyo with JVC_VENDOR_ID_CODE
*/
void IRsend::sendKaseikyo_JVC(uint16_t aAddress, uint8_t aCommand, int_fast8_t aNumberOfRepeats) {
sendKaseikyo(aAddress, aCommand, aNumberOfRepeats, JVC_VENDOR_ID_CODE);
}
/*
* Tested with my Panasonic DVD/TV remote
*/
bool IRrecv::decodeKaseikyo() {
decode_type_t tProtocol;
// Check we have enough data (96 + 4) 4 for initial gap, start bit mark and space + stop bit mark
if (decodedIRData.rawlen != ((2 * KASEIKYO_BITS) + 4)) {
IR_DEBUG_PRINT(F("Kaseikyo: "));
IR_DEBUG_PRINT(F("Data length="));
IR_DEBUG_PRINT(decodedIRData.rawlen);
IR_DEBUG_PRINTLN(F(" is not 100"));
return false;
}
if (!checkHeader(&KaseikyoProtocolConstants)) {
return false;
}
// decode first 16 Vendor ID bits
if (!decodePulseDistanceWidthData(&KaseikyoProtocolConstants, KASEIKYO_VENDOR_ID_BITS)) {
#if defined(LOCAL_DEBUG)
Serial.print(F("Kaseikyo: "));
Serial.println(F("Vendor ID decode failed"));
#endif
return false;
}
uint16_t tVendorId = decodedIRData.decodedRawData;
if (tVendorId == PANASONIC_VENDOR_ID_CODE) {
tProtocol = PANASONIC;
} else if (tVendorId == SHARP_VENDOR_ID_CODE) {
tProtocol = KASEIKYO_SHARP;
} else if (tVendorId == DENON_VENDOR_ID_CODE) {
tProtocol = KASEIKYO_DENON;
} else if (tVendorId == JVC_VENDOR_ID_CODE) {
tProtocol = KASEIKYO_JVC;
} else if (tVendorId == MITSUBISHI_VENDOR_ID_CODE) {
tProtocol = KASEIKYO_MITSUBISHI;
} else {
tProtocol = KASEIKYO;
}
// Vendor Parity
uint8_t tVendorParity = tVendorId ^ (tVendorId >> 8);
tVendorParity = (tVendorParity ^ (tVendorParity >> 4)) & 0xF;
/*
* Decode next 32 bits, 8 VendorID parity parity + 12 address (device and subdevice) + 8 command + 8 parity
*/
if (!decodePulseDistanceWidthData(&KaseikyoProtocolConstants,
KASEIKYO_VENDOR_ID_PARITY_BITS + KASEIKYO_ADDRESS_BITS + KASEIKYO_COMMAND_BITS + KASEIKYO_PARITY_BITS,
3 + (2 * KASEIKYO_VENDOR_ID_BITS))) {
#if defined(LOCAL_DEBUG)
Serial.print(F("Kaseikyo: "));
Serial.println(F("VendorID parity, address, command + parity decode failed"));
#endif
return false;
}
// Success
// decodedIRData.flags = IRDATA_FLAGS_IS_LSB_FIRST; // Not required, since this is the start value
LongUnion tValue;
tValue.ULong = decodedIRData.decodedRawData;
#if __INT_WIDTH__ >= 32
// workaround until complete refactoring for 64 bit
decodedIRData.decodedRawData = (decodedIRData.decodedRawData << 16) | tVendorId; // store all 48 bits in decodedRawData
#endif
decodedIRData.address = (tValue.UWord.LowWord >> KASEIKYO_VENDOR_ID_PARITY_BITS); // remove 4 bit vendor parity
decodedIRData.command = tValue.UByte.MidHighByte;
uint8_t tParity = tValue.UByte.LowByte ^ tValue.UByte.MidLowByte ^ tValue.UByte.MidHighByte;
if (tVendorParity != (tValue.UByte.LowByte & 0xF)) {
decodedIRData.flags = IRDATA_FLAGS_PARITY_FAILED | IRDATA_FLAGS_IS_LSB_FIRST;
#if defined(LOCAL_DEBUG)
Serial.print(F("Kaseikyo: "));
Serial.print(F("4 bit VendorID parity is not correct. Expected=0x"));
Serial.print(tVendorParity, HEX);
Serial.print(F(" received=0x"));
Serial.print(decodedIRData.decodedRawData, HEX);
Serial.print(F(" VendorID=0x"));
Serial.println(tVendorId, HEX);
#endif
}
if (tProtocol == KASEIKYO) {
decodedIRData.flags |= IRDATA_FLAGS_EXTRA_INFO;
decodedIRData.extra = tVendorId; // Store (unknown) vendor ID
}
if (tValue.UByte.HighByte != tParity) {
decodedIRData.flags |= IRDATA_FLAGS_PARITY_FAILED;
#if defined(LOCAL_DEBUG)
Serial.print(F("Kaseikyo: "));
Serial.print(F("8 bit parity is not correct. Expected=0x"));
Serial.print(tParity, HEX);
Serial.print(F(" received=0x"));
Serial.print(decodedIRData.decodedRawData >> KASEIKYO_COMMAND_BITS, HEX);
Serial.print(F(" address=0x"));
Serial.print(decodedIRData.address, HEX);
Serial.print(F(" command=0x"));
Serial.println(decodedIRData.command, HEX);
#endif
}
decodedIRData.numberOfBits = KASEIKYO_BITS;
decodedIRData.protocol = tProtocol;
// check for repeat
checkForRepeatSpaceTicksAndSetFlag(KASEIKYO_MAXIMUM_REPEAT_DISTANCE / MICROS_PER_TICK);
return true;
}
/*
* Removed void IRsend::sendPanasonic(uint16_t aAddress, uint32_t aData)
* and bool IRrecv::decodePanasonicMSB(decode_results *aResults)
* since their implementations were wrong (wrong length), and nobody recognized it
*/
/** @}*/
#if defined(LOCAL_DEBUG)
#undef LOCAL_DEBUG
#endif
#endif // _IR_KASEIKYO_HPP
+349
View File
@@ -0,0 +1,349 @@
/*
* ir_LG.hpp
*
* Contains functions for receiving and sending LG IR Protocol for air conditioner
*
* This file is part of Arduino-IRremote https://github.com/Arduino-IRremote/Arduino-IRremote.
*
************************************************************************************
* MIT License
*
* Copyright (c) 2017-2024 Darryl Smith, 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_LG_HPP
#define _IR_LG_HPP
#if defined(DEBUG) && !defined(LOCAL_DEBUG)
#define LOCAL_DEBUG
#else
//#define LOCAL_DEBUG // This enables debug output only for this file
#endif
/** \addtogroup Decoder Decoders and encoders for different protocols
* @{
*/
//==============================================================================
// L GGGG
// L G
// L G GG
// L G G
// LLLLL GGG
//==============================================================================
/*
* Protocol=LG Address=0xF1 Command=0x7776 Raw-Data=0xF17776B 28 bits MSB first
+8950,-4150
+ 500,-1550 + 550,-1550 + 500,-1550 + 500,-1600
+ 500,- 700 + 350,- 600 + 450,- 600 + 450,-1550
+ 500,- 550 + 500,-1550 + 500,-1600 + 500,-1550
+ 550,- 550 + 500,-1550 + 500,-1550 + 550,-1550
+ 500,- 550 + 500,-1550 + 500,-1600 + 500,-1550
+ 500,- 550 + 500,-1550 + 500,-1600 + 500,- 550
+ 500,-1550 + 500,- 600 + 450,-1600 + 500,-1550
+ 500
Sum: 62400
*/
// LG originally added by Darryl Smith (based on the JVC protocol)
// see: https://github.com/Arduino-IRremote/Arduino-IRremote/tree/master/examples/LGAirConditionerSendDemo
// see: https://www.mikrocontroller.net/articles/IRMP_-_english#LGAIR
// MSB first, 1 start bit + 8 bit address + 16 bit command + 4 bit checksum + 1 stop bit (28 data bits).
// Bit and repeat timing is like NEC
// LG2 has different header timing and a shorter bit time
/*
* LG remote IR-LED measurements: Type AKB 73315611 for air conditioner, Ver1.1 from 2011.03.01
* Protocol: LG2
* Internal crystal: 4 MHz
* Header: 8.9 ms mark 4.15 ms space
* Data: 500 / 540 and 500 / 1580;
* Clock is not synchronized with gate so you have 19 and sometimes 19 and a spike pulses for mark
* Duty: 9 us on 17 us off => around 33 % duty
* NO REPEAT: If value like temperature has changed during long press, the last value is send at button release.
* If you do a double press, the next value can be sent after around 118 ms. Tested with the fan button.
* LG remote IR-LED measurements: Type AKB 75095308 for LG TV
* Protocol: NEC!!!
* Frequency 37.88 kHz
* Header: 9.0 ms mark 4.5 ms space
* Data: 560 / 560 and 560 / 1680;
* Clock is synchronized with gate, mark always starts with a full period
* Duty: 13 us on 13 us off => 50 % duty
* Repeat: 110 ms 9.0 ms mark, 2250 us space, 560 stop
* LSB first!
*
* The codes of the LG air conditioner are documented in https://github.com/Arduino-IRremote/Arduino-IRremote/blob/master/ac_LG.cpp
*/
#define LG_ADDRESS_BITS 8
#define LG_COMMAND_BITS 16
#define LG_CHECKSUM_BITS 4
#define LG_BITS (LG_ADDRESS_BITS + LG_COMMAND_BITS + LG_CHECKSUM_BITS) // 28
#define LG_UNIT 500 // 19 periods of 38 kHz
#define LG_HEADER_MARK (18 * LG_UNIT) // 9000
#define LG_HEADER_SPACE 4200 // 4200 | 84
#define LG2_HEADER_MARK (19 * LG_UNIT) // 9500
#define LG2_HEADER_SPACE (6 * LG_UNIT) // 3000
#define LG_BIT_MARK LG_UNIT
#define LG_ONE_SPACE 1580 // 60 periods of 38 kHz
#define LG_ZERO_SPACE 550
#define LG_REPEAT_HEADER_SPACE (4 * LG_UNIT) // 2250
#define LG_REPEAT_PERIOD 110000 // Commands are repeated every 110 ms (measured from start to start) for as long as the key on the remote control is held down.
//#define LG_AVERAGE_DURATION 58000 // LG_HEADER_MARK + LG_HEADER_SPACE + 32 * 2,5 * LG_UNIT) + LG_UNIT // 2.5 because we assume more zeros than ones
//#define LG_REPEAT_DURATION (LG_HEADER_MARK + LG_REPEAT_HEADER_SPACE + LG_BIT_MARK)
//#define LG_REPEAT_DISTANCE (LG_REPEAT_PERIOD - LG_AVERAGE_DURATION) // 52 ms
struct PulseDistanceWidthProtocolConstants LGProtocolConstants = { LG, LG_KHZ, LG_HEADER_MARK, LG_HEADER_SPACE, LG_BIT_MARK,
LG_ONE_SPACE, LG_BIT_MARK, LG_ZERO_SPACE, PROTOCOL_IS_MSB_FIRST, (LG_REPEAT_PERIOD / MICROS_IN_ONE_MILLI), &sendNECSpecialRepeat };
struct PulseDistanceWidthProtocolConstants LG2ProtocolConstants = { LG2, LG_KHZ, LG2_HEADER_MARK, LG2_HEADER_SPACE, LG_BIT_MARK,
LG_ONE_SPACE, LG_BIT_MARK, LG_ZERO_SPACE, PROTOCOL_IS_MSB_FIRST, (LG_REPEAT_PERIOD / MICROS_IN_ONE_MILLI), &sendLG2SpecialRepeat };
/************************************
* Start of send and decode functions
************************************/
/*
* Send special LG2 repeat - not used internally
*/
void IRsend::sendLG2Repeat() {
enableIROut (LG_KHZ); // 38 kHz
mark(LG2_HEADER_MARK); // + 3000
space(LG_REPEAT_HEADER_SPACE); // - 2250
mark(LG_BIT_MARK); // + 500
}
/**
* Static function for sending special repeat frame.
* For use in ProtocolConstants. Saves up to 250 bytes compared to a member function.
*/
void sendLG2SpecialRepeat() {
IrSender.enableIROut(LG_KHZ); // 38 kHz
IrSender.mark(LG2_HEADER_MARK); // + 3000
IrSender.space(LG_REPEAT_HEADER_SPACE); // - 2250
IrSender.mark(LG_BIT_MARK); // + 500
}
uint32_t IRsend::computeLGRawDataAndChecksum(uint8_t aAddress, uint16_t aCommand) {
uint32_t tRawData = ((uint32_t) aAddress << (LG_COMMAND_BITS + LG_CHECKSUM_BITS)) | ((uint32_t) aCommand << LG_CHECKSUM_BITS);
/*
* My guess of the 4 bit checksum
* Addition of all 4 nibbles of the 16 bit command
*/
uint8_t tChecksum = 0;
uint16_t tTempForChecksum = aCommand;
for (int i = 0; i < 4; ++i) {
tChecksum += tTempForChecksum & 0xF; // add low nibble
tTempForChecksum >>= 4; // shift by a nibble
}
return (tRawData | (tChecksum & 0xF));
}
/**
* LG uses the NEC repeat.
*/
void IRsend::sendLG(uint8_t aAddress, uint16_t aCommand, int_fast8_t aNumberOfRepeats) {
sendPulseDistanceWidth(&LGProtocolConstants, computeLGRawDataAndChecksum(aAddress, aCommand), LG_BITS, aNumberOfRepeats);
}
/**
* LG2 uses a special repeat.
*/
void IRsend::sendLG2(uint8_t aAddress, uint16_t aCommand, int_fast8_t aNumberOfRepeats) {
sendPulseDistanceWidth(&LG2ProtocolConstants, computeLGRawDataAndChecksum(aAddress, aCommand), LG_BITS, aNumberOfRepeats);
}
bool IRrecv::decodeLG() {
decode_type_t tProtocol = LG;
uint16_t tHeaderSpace = LG_HEADER_SPACE;
/*
* First check for right data length
* Next check start bit
* Next try the decode
*/
// Check we have the right amount of data (60). The +4 is for initial gap, start bit mark and space + stop bit mark.
if (decodedIRData.rawlen != ((2 * LG_BITS) + 4) && (decodedIRData.rawlen != 4)) {
IR_DEBUG_PRINT(F("LG: "));
IR_DEBUG_PRINT(F("Data length="));
IR_DEBUG_PRINT(decodedIRData.rawlen);
IR_DEBUG_PRINTLN(F(" is not 60 or 4"));
return false;
}
// Check header "mark" this must be done for repeat and data
if (!matchMark(decodedIRData.rawDataPtr->rawbuf[1], LG_HEADER_MARK)) {
if (matchMark(decodedIRData.rawDataPtr->rawbuf[1], LG2_HEADER_MARK)) {
tProtocol = LG2;
tHeaderSpace = LG2_HEADER_SPACE;
} else {
#if defined(LOCAL_DEBUG)
Serial.print(F("LG: "));
Serial.println(F("Header mark is wrong"));
#endif
return false; // neither LG nor LG2 header
}
}
// Check for repeat - here we have another header space length
if (decodedIRData.rawlen == 4) {
if (matchSpace(decodedIRData.rawDataPtr->rawbuf[2], LG_REPEAT_HEADER_SPACE)
&& matchMark(decodedIRData.rawDataPtr->rawbuf[3], LG_BIT_MARK)) {
decodedIRData.flags = IRDATA_FLAGS_IS_REPEAT | IRDATA_FLAGS_IS_MSB_FIRST;
decodedIRData.address = lastDecodedAddress;
decodedIRData.command = lastDecodedCommand;
decodedIRData.protocol = lastDecodedProtocol;
return true;
}
#if defined(LOCAL_DEBUG)
Serial.print(F("LG: "));
Serial.print(F("Repeat header space is wrong"));
#endif
return false;
}
// Check command header space
if (!matchSpace(decodedIRData.rawDataPtr->rawbuf[2], tHeaderSpace)) {
#if defined(LOCAL_DEBUG)
Serial.print(F("LG: "));
Serial.println(F("Header space length is wrong"));
#endif
return false;
}
if (!decodePulseDistanceWidthData(&LGProtocolConstants, LG_BITS)) {
#if defined(LOCAL_DEBUG)
Serial.print(F("LG: "));
Serial.println(F("Decode failed"));
#endif
return false;
}
// Success
decodedIRData.flags = IRDATA_FLAGS_IS_MSB_FIRST;
decodedIRData.command = (decodedIRData.decodedRawData >> LG_CHECKSUM_BITS) & 0xFFFF;
decodedIRData.address = decodedIRData.decodedRawData >> (LG_COMMAND_BITS + LG_CHECKSUM_BITS); // first 8 bit
/*
* My guess of the checksum
*/
uint8_t tChecksum = 0;
uint16_t tTempForChecksum = decodedIRData.command;
for (int i = 0; i < 4; ++i) {
tChecksum += tTempForChecksum & 0xF; // add low nibble
tTempForChecksum >>= 4; // shift by a nibble
}
// Checksum check
if ((tChecksum & 0xF) != (decodedIRData.decodedRawData & 0xF)) {
#if defined(LOCAL_DEBUG)
Serial.print(F("LG: "));
Serial.print(F("4 bit checksum is not correct. expected=0x"));
Serial.print(tChecksum, HEX);
Serial.print(F(" received=0x"));
Serial.print((decodedIRData.decodedRawData & 0xF), HEX);
Serial.print(F(" data=0x"));
Serial.println(decodedIRData.command, HEX);
#endif
decodedIRData.flags |= IRDATA_FLAGS_PARITY_FAILED;
}
decodedIRData.protocol = tProtocol; // LG or LG2
decodedIRData.numberOfBits = LG_BITS;
return true;
}
/*********************************************************************************
* Old deprecated functions, kept for backward compatibility to old 2.0 tutorials
*********************************************************************************/
/**
* Here you can put your raw data, even one with "wrong" checksum.
* @param aRawData The lowest 28 (LG_BITS) bit of this value are sent MSB first.
* @param aNumberOfRepeats If < 0 then only a special repeat frame will be sent.
*/
void IRsend::sendLGRaw(uint32_t aRawData, int_fast8_t aNumberOfRepeats) {
sendPulseDistanceWidth(&LGProtocolConstants, aRawData, LG_BITS, aNumberOfRepeats);
}
bool IRrecv::decodeLGMSB(decode_results *aResults) {
unsigned int offset = 1; // Skip first space
// Check we have enough data (60) - +4 for initial gap, start bit mark and space + stop bit mark
if (aResults->rawlen != (2 * LG_BITS) + 4) {
return false;
}
// Initial mark/space
if (!matchMark(aResults->rawbuf[offset], LG_HEADER_MARK)) {
return false;
}
offset++;
if (!matchSpace(aResults->rawbuf[offset], LG_HEADER_SPACE)) {
return false;
}
offset++;
if (!decodePulseDistanceWidthData(LG_BITS, offset, LG_BIT_MARK, LG_ONE_SPACE, 0, PROTOCOL_IS_MSB_FIRST)) {
return false;
}
// Stop bit
if (!matchMark(aResults->rawbuf[offset + (2 * LG_BITS)], LG_BIT_MARK)) {
#if defined(LOCAL_DEBUG)
Serial.println(F("Stop bit mark length is wrong"));
#endif
return false;
}
// Success
aResults->value = decodedIRData.decodedRawData;
aResults->bits = LG_BITS;
aResults->decode_type = LG;
decodedIRData.protocol = LG;
return true;
}
//+=============================================================================
void IRsend::sendLG(unsigned long data, int nbits) {
// Set IR carrier frequency
enableIROut (LG_KHZ);
#if !(defined(__AVR_ATtiny25__) || defined(__AVR_ATtiny45__) || defined(__AVR_ATtiny85__) || defined(__AVR_ATtiny87__) || defined(__AVR_ATtiny167__))
// Serial.println(F(
// "The function sendLG(data, nbits) is deprecated and may not work as expected! Use sendLGRaw(data, NumberOfRepeats) or better sendLG(Address, Command, NumberOfRepeats)."));
#endif
// Header
mark(LG_HEADER_MARK);
space(LG_HEADER_SPACE);
// mark(LG_BIT_MARK);
// Data + stop bit
sendPulseDistanceWidthData(LG_BIT_MARK, LG_ONE_SPACE, LG_BIT_MARK, LG_ZERO_SPACE, data, nbits, PROTOCOL_IS_MSB_FIRST);
}
/** @}*/
#if defined(LOCAL_DEBUG)
#undef LOCAL_DEBUG
#endif
#endif // _IR_LG_HPP
+210
View File
@@ -0,0 +1,210 @@
/*
* ir_Lego.hpp
*
* Contains functions for receiving and sending Lego Power Functions IR Protocol
*
* This file is part of Arduino-IRremote https://github.com/Arduino-IRremote/Arduino-IRremote.
*
************************************************************************************
* MIT License
*
* Copyright (c) 2020-2023 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_LEGO_HPP
#define _IR_LEGO_HPP
/** \addtogroup Decoder Decoders and encoders for different protocols
* @{
*/
//==============================================================================
// L EEEEEE EEEE OOOO
// L E E O O
// L EEEE E EEE O O
// L E E E O O
// LLLLLL EEEEEE EEEE OOOO
//==============================================================================
// from LEGO Power Functions RC Manual 26.02.2010 Version 1.20
// https://github.com/jurriaan/Arduino-PowerFunctions/raw/master/LEGO_Power_Functions_RC_v120.pdf
// https://oberguru.net/elektronik/ir/codes/lego_power_functions_train.lircd.conf
// For original LEGO receiver see: https://www.philohome.com/pfrec/pfrec.htm and https://www.youtube.com/watch?v=KCM4Ug1bPrM
//
// To ensure correct detection of IR messages six 38 kHz cycles are transmitted as mark.
// Low bit consists of 6 cycles of IR and 10 �cycles� of pause,
// high bit of 6 cycles IR and 21 �cycles� of pause and start bit of 6 cycles IR and 39 �cycles� of pause.
// Low bit range 316 - 526 us
// High bit range 526 � 947 us
// Start/stop bit range 947 � 1579 us
// If tm is the maximum message length (16ms) and Ch is the channel number, then
// The delay before transmitting the first message is: (4 � Ch)*tm
// The time from start to start for the next 2 messages is: 5*tm
// The time from start to start for the following messages is: (6 + 2*Ch)*tm
// Supported Devices
// LEGO Power Functions IR Receiver 8884
// MSB first, 1 start bit + 4 bit channel, 4 bit mode + 4 bit command + 4 bit parity + 1 stop bit.
#define LEGO_CHANNEL_BITS 4
#define LEGO_MODE_BITS 4
#define LEGO_COMMAND_BITS 4
#define LEGO_PARITY_BITS 4
#define LEGO_BITS (LEGO_CHANNEL_BITS + LEGO_MODE_BITS + LEGO_COMMAND_BITS + LEGO_PARITY_BITS)
#define LEGO_HEADER_MARK 158 // 6 cycles
#define LEGO_HEADER_SPACE 1026 // 39 cycles
#define LEGO_BIT_MARK 158 // 6 cycles
#define LEGO_ONE_SPACE 553 // 21 cycles
#define LEGO_ZERO_SPACE 263 // 10 cycles
#define LEGO_AVERAGE_DURATION 11000 // LEGO_HEADER_MARK + LEGO_HEADER_SPACE + 16 * 600 + 158
#define LEGO_AUTO_REPEAT_PERIOD_MIN 110000 // Every frame is auto repeated 5 times.
#define LEGO_AUTO_REPEAT_PERIOD_MAX 230000 // space for channel 3
#define LEGO_MODE_EXTENDED 0
#define LEGO_MODE_COMBO 1
#define LEGO_MODE_SINGLE 0x4 // here the 2 LSB have meanings like Output A / Output B
struct PulseDistanceWidthProtocolConstants LegoProtocolConstants = { LEGO_PF, 38, LEGO_HEADER_MARK, LEGO_HEADER_SPACE, LEGO_BIT_MARK,
LEGO_ONE_SPACE, LEGO_BIT_MARK, LEGO_ZERO_SPACE, PROTOCOL_IS_LSB_FIRST, (LEGO_AUTO_REPEAT_PERIOD_MIN
/ MICROS_IN_ONE_MILLI), NULL };
/************************************
* Start of send and decode functions
************************************/
/*
* Here we process the structured data, and call the send raw data function
* @param aMode one of LEGO_MODE_EXTENDED, LEGO_MODE_COMBO, LEGO_MODE_SINGLE
*/
void IRsend::sendLegoPowerFunctions(uint8_t aChannel, uint8_t aCommand, uint8_t aMode, bool aDoSend5Times) {
aChannel &= 0x0F; // allow toggle and escape bits too
aCommand &= 0x0F;
aMode &= 0x0F;
uint8_t tParity = 0xF ^ aChannel ^ aMode ^ aCommand;
// send 4 bit channel, 4 bit mode, 4 bit command, 4 bit parity
uint16_t tRawData = (((aChannel << LEGO_MODE_BITS) | aMode) << (LEGO_COMMAND_BITS + LEGO_PARITY_BITS))
| (aCommand << LEGO_PARITY_BITS) | tParity;
sendLegoPowerFunctions(tRawData, aChannel, aDoSend5Times);
}
void IRsend::sendLegoPowerFunctions(uint16_t aRawData, uint8_t aChannel, bool aDoSend5Times) {
IR_DEBUG_PRINT(F("sendLego aRawData=0x"));
IR_DEBUG_PRINTLN(aRawData, HEX);
aChannel &= 0x03; // we have 4 channels
uint_fast8_t tNumberOfRepeats = 0;
if (aDoSend5Times) {
tNumberOfRepeats = 4;
}
// required for repeat timing, see http://www.hackvandedam.nl/blog/?page_id=559
uint8_t tRepeatPeriod = (LEGO_AUTO_REPEAT_PERIOD_MIN / MICROS_IN_ONE_MILLI) + (aChannel * 40); // from 110 to 230
LegoProtocolConstants.RepeatPeriodMillis = tRepeatPeriod;
sendPulseDistanceWidth(&LegoProtocolConstants, aRawData, LEGO_BITS, tNumberOfRepeats);
}
/*
* Mode is stored in the upper nibble of command
*/
bool IRrecv::decodeLegoPowerFunctions() {
if (!checkHeader(&LegoProtocolConstants)) {
return false;
}
// Check we have enough data - +4 for initial gap, start bit mark and space + stop bit mark
if (decodedIRData.rawlen != (2 * LEGO_BITS) + 4) {
IR_DEBUG_PRINT(F("LEGO: "));
IR_DEBUG_PRINT(F("Data length="));
IR_DEBUG_PRINT(decodedIRData.rawDataPtr->rawlen);
IR_DEBUG_PRINTLN(F(" is not 36"));
return false;
}
if (!decodePulseDistanceWidthData(&LegoProtocolConstants, LEGO_BITS)) {
IR_DEBUG_PRINT(F("LEGO: "));
IR_DEBUG_PRINTLN(F("Decode failed"));
return false;
}
// Stop bit
if (!matchMark(decodedIRData.rawDataPtr->rawbuf[3 + (2 * LEGO_BITS)], LEGO_BIT_MARK)) {
IR_DEBUG_PRINT(F("LEGO: "));
IR_DEBUG_PRINTLN(F("Stop bit mark length is wrong"));
return false;
}
// Success
decodedIRData.flags = IRDATA_FLAGS_IS_MSB_FIRST;
uint16_t tDecodedValue = decodedIRData.decodedRawData;
uint8_t tToggleEscapeChannel = tDecodedValue >> (LEGO_MODE_BITS + LEGO_COMMAND_BITS + LEGO_PARITY_BITS);
uint8_t tMode = (tDecodedValue >> (LEGO_COMMAND_BITS + LEGO_PARITY_BITS)) & 0xF;
uint8_t tData = (tDecodedValue >> LEGO_PARITY_BITS) & 0xF; // lego calls this field "data"
uint8_t tParityReceived = tDecodedValue & 0xF;
// This is parity as defined in the specifications
// But in some scans I saw 0x9 ^ .. as parity formula
uint8_t tParityComputed = 0xF ^ tToggleEscapeChannel ^ tMode ^ tData;
// parity check
if (tParityReceived != tParityComputed) {
IR_DEBUG_PRINT(F("LEGO: "));
IR_DEBUG_PRINT(F("Parity is not correct. expected=0x"));
IR_DEBUG_PRINT(tParityComputed, HEX);
IR_DEBUG_PRINT(F(" received=0x"));
IR_DEBUG_PRINT(tParityReceived, HEX);
IR_DEBUG_PRINT(F(", raw=0x"));
IR_DEBUG_PRINT(tDecodedValue, HEX);
IR_DEBUG_PRINT(F(", 3 nibbles are 0x"));
IR_DEBUG_PRINT(tToggleEscapeChannel, HEX);
IR_DEBUG_PRINT(F(", 0x"));
IR_DEBUG_PRINT(tMode, HEX);
IR_DEBUG_PRINT(F(", 0x"));
IR_DEBUG_PRINTLN(tData, HEX);
// might not be an error, so just continue
decodedIRData.flags = IRDATA_FLAGS_PARITY_FAILED | IRDATA_FLAGS_IS_MSB_FIRST;
}
/*
* Check for autorepeat (should happen 4 times for one press)
*/
if (decodedIRData.initialGapTicks < (LEGO_AUTO_REPEAT_PERIOD_MAX / MICROS_PER_TICK)) {
decodedIRData.flags |= IRDATA_FLAGS_IS_AUTO_REPEAT;
}
decodedIRData.address = tToggleEscapeChannel;
decodedIRData.command = tData | tMode << LEGO_COMMAND_BITS;
decodedIRData.numberOfBits = LEGO_BITS;
decodedIRData.protocol = LEGO_PF;
return true;
}
/*********************************************************************************
* Old deprecated functions, kept for backward compatibility to old 2.0 tutorials
*********************************************************************************/
void IRsend::sendLegoPowerFunctions(uint16_t aRawData, bool aDoSend5Times) {
sendLegoPowerFunctions(aRawData, (aRawData >> (LEGO_MODE_BITS + LEGO_COMMAND_BITS + LEGO_PARITY_BITS)) & 0x3, aDoSend5Times);
}
/** @}*/
#endif // _IR_LEGO_HPP
+251
View File
@@ -0,0 +1,251 @@
/*
* ir_MagiQuest.hpp
*
* Contains functions for receiving and sending MagiQuest Protocol
* Based off the Magiquest fork of Arduino-IRremote by mpflaga https://github.com/mpflaga/Arduino-IRremote/
*
* RESULT:
* The 31 bit wand ID is available in decodedRawData.
* The lower 16 bit of the ID is available in address.
* The magnitude is available in command.
*
* This file is part of Arduino-IRremote https://github.com/Arduino-IRremote/Arduino-IRremote.
*
************************************************************************************
* MIT License
*
* Copyright (c) 2017-2024 E. Stuart Hicks <ehicks@binarymagi.com>, 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_MAGIQUEST_HPP
#define _IR_MAGIQUEST_HPP
#if defined(DEBUG) && !defined(LOCAL_DEBUG)
#define LOCAL_DEBUG
#else
//#define LOCAL_DEBUG // This enables debug output only for this file
#endif
//
//==============================================================================
//
// M M AA GGG III QQQ U U EEEE SSS TTTTTT
// MM MM A A G I Q Q U U E S TT
// M M M AAAA G GG I Q Q U U EEE SSS TT
// M M A A G G I Q QQ U U E S TT
// M M A A GGG III QQQQ UUU EEEE SSSS TT
// Q
//==============================================================================
/*
* https://github.com/kitlaan/Arduino-IRremote/blob/master/ir_Magiquest.cpp
* https://github.com/Arduino-IRremote/Arduino-IRremote/discussions/1027#discussioncomment-3636857
* https://github.com/Arduino-IRremote/Arduino-IRremote/issues/1015#issuecomment-1222247231
Protocol=MagiQuest Address=0xFF00 Command=0x176 Raw-Data=0x6BCDFF00 56 bits MSB first
+ 250,- 800 + 250,- 850 + 250,- 850 + 250,- 850 // 8 zero start bits
+ 250,- 850 + 300,- 800 + 250,- 850 + 250,- 850
// 31 ID bits
+ 550,- 600 + 550,- 550 + 350,- 800 + 600,- 600 // 110 1 6
+ 200,- 950 + 550,- 600 + 550,- 600 + 550,- 600 // 011 1 B - 1(from above)011 => B
+ 550,- 600 + 250,- 900 + 300,- 850 + 550,- 600 // 100 1 C
+ 550,- 600 + 300,- 850 + 550,- 600 + 550,- 600
+ 550,- 600 + 550,- 600 + 550,- 600 + 550,- 600
+ 550,- 600 + 550,- 600 + 550,- 600 + 300,- 800
+ 350,- 850 + 300,- 850 + 300,- 850 + 300,- 850
+ 300,- 850 + 300,- 850 + 300,- 850 + 550,- 600 // 000 1 - 3 LSB ID bits 000 + 1 MSB magnitude bit 1
// 8 bit magnitude
+ 300,- 850 + 550,- 600 + 550,- 600 + 550,- 600
+ 300,- 850 + 550,- 600 + 550,- 600 + 250,- 900
// Checksum (+ sum of the 5 bytes before == 0)
+ 250,- 900 + 300,- 900 + 250,- 850 + 550,- 600
+ 600,- 550 + 300,- 900 + 250,- 850 + 550
// No stop bit!
*/
// MSB first, 8 start bits (zero), 31 wand id bits, 9 magnitude bits 8 checksum bits and no stop bit => 56 bits
#define MAGIQUEST_CHECKSUM_BITS 8 // magiquest_t.cmd.checksum
#define MAGIQUEST_MAGNITUDE_BITS 9 // magiquest_t.cmd.magnitude
#define MAGIQUEST_WAND_ID_BITS 31 // magiquest_t.cmd.wand_id -> wand-id is handled as 32 bit and always even
#define MAGIQUEST_START_BITS 8 // magiquest_t.cmd.StartBits
#define MAGIQUEST_PERIOD 1150 // Time for a full MagiQuest "bit" (1100 - 1200 usec)
#define MAGIQUEST_DATA_BITS (MAGIQUEST_CHECKSUM_BITS + MAGIQUEST_MAGNITUDE_BITS + MAGIQUEST_WAND_ID_BITS) // 48 Size of the command without the start bits
#define MAGIQUEST_BITS (MAGIQUEST_CHECKSUM_BITS + MAGIQUEST_MAGNITUDE_BITS + MAGIQUEST_WAND_ID_BITS + MAGIQUEST_START_BITS) // 56 Size of the command with the start bits
/*
* 0 = 25% mark & 75% space across 1 period
* 1150 * 0.25 = 288 usec mark
* 1150 - 288 = 862 usec space
* 1 = 50% mark & 50% space across 1 period
* 1150 * 0.5 = 575 usec mark
* 1150 - 575 = 575 usec space
*/
#define MAGIQUEST_UNIT (MAGIQUEST_PERIOD / 4) // 287.5
#define MAGIQUEST_ONE_MARK (2 * MAGIQUEST_UNIT) // 576
#define MAGIQUEST_ONE_SPACE (2 * MAGIQUEST_UNIT) // 576
#define MAGIQUEST_ZERO_MARK MAGIQUEST_UNIT // 287.5
#define MAGIQUEST_ZERO_SPACE (3 * MAGIQUEST_UNIT) // 864
// assume 110 as repeat period
struct PulseDistanceWidthProtocolConstants MagiQuestProtocolConstants = { MAGIQUEST, 38, MAGIQUEST_ZERO_MARK, MAGIQUEST_ZERO_SPACE,
MAGIQUEST_ONE_MARK, MAGIQUEST_ONE_SPACE, MAGIQUEST_ZERO_MARK, MAGIQUEST_ZERO_SPACE, PROTOCOL_IS_MSB_FIRST | SUPPRESS_STOP_BIT, 110,
NULL };
//+=============================================================================
//
/**
* @param aWandId 31 bit ID
* @param aMagnitude 9 bit Magnitude
*/
void IRsend::sendMagiQuest(uint32_t aWandId, uint16_t aMagnitude) {
// Set IR carrier frequency
enableIROut(38);
aMagnitude &= 0x1FF; // we have 9 bit
LongUnion tWandId;
tWandId.ULong = aWandId << 1;
uint8_t tChecksum = (tWandId.Bytes[0]) + tWandId.Bytes[1] + tWandId.Bytes[2] + tWandId.Bytes[3];
tChecksum += aMagnitude + (aMagnitude >> 8);
tChecksum = ~tChecksum + 1;
// 8 start bits
sendPulseDistanceWidthData(&MagiQuestProtocolConstants, 0, 8);
// 48 bit data
sendPulseDistanceWidthData(&MagiQuestProtocolConstants, aWandId, MAGIQUEST_WAND_ID_BITS); // send only 31 bit, do not send MSB here
sendPulseDistanceWidthData(&MagiQuestProtocolConstants, aMagnitude, MAGIQUEST_MAGNITUDE_BITS);
sendPulseDistanceWidthData(&MagiQuestProtocolConstants, tChecksum, MAGIQUEST_CHECKSUM_BITS);
#if defined(LOCAL_DEBUG)
// must be after sending, in order not to destroy the send timing
Serial.print(F("MagiQuest checksum=0x"));
Serial.println(tChecksum, HEX);
#endif
}
//+=============================================================================
//
/*
* decodes a 56 bit result, which is not really compatible with standard decoder layout
* magnitude is stored in command
* 31 bit wand_id is stored in decodedRawData
* lower 16 bit of wand_id is stored in address
*/
bool IRrecv::decodeMagiQuest() {
// Check we have the right amount of data, magnitude and ID bits and 8 start bits + 0 stop bit
if (decodedIRData.rawlen != (2 * MAGIQUEST_BITS)) {
IR_DEBUG_PRINT(F("MagiQuest: "));
IR_DEBUG_PRINT(F("Data length="));
IR_DEBUG_PRINT(decodedIRData.rawlen);
IR_DEBUG_PRINTLN(F(" is not 112"));
return false;
}
/*
* Check for 8 zero header bits
*/
if (!decodePulseDistanceWidthData(&MagiQuestProtocolConstants, MAGIQUEST_START_BITS, 1)) {
#if defined(LOCAL_DEBUG)
Serial.print(F("MagiQuest: "));
Serial.println(F("Start bit decode failed"));
#endif
return false;
}
if (decodedIRData.decodedRawData != 0) {
#if defined(LOCAL_DEBUG)
Serial.print(F("MagiQuest: "));
Serial.print(F("Not 8 leading zero start bits received, RawData=0x"));
Serial.println(decodedIRData.decodedRawData, HEX);
#endif
return false;
}
/*
* Decode the 31 bit ID
*/
if (!decodePulseDistanceWidthData(&MagiQuestProtocolConstants, MAGIQUEST_WAND_ID_BITS, (MAGIQUEST_START_BITS * 2) + 1)) {
#if defined(LOCAL_DEBUG)
Serial.print(F("MagiQuest: "));
Serial.println(F("ID decode failed"));
#endif
return false;
}
LongUnion tDecodedRawData;
#if defined(LOCAL_DEBUG)
Serial.print(F("31 bit WandId=0x"));
Serial.println(decodedIRData.decodedRawData, HEX);
#endif
uint32_t tWandId = decodedIRData.decodedRawData; // save tWandId for later use
tDecodedRawData.ULong = decodedIRData.decodedRawData << 1; // shift for checksum computation
uint8_t tChecksum = tDecodedRawData.Bytes[0] + tDecodedRawData.Bytes[1] + tDecodedRawData.Bytes[2] + tDecodedRawData.Bytes[3];
#if defined(LOCAL_DEBUG)
Serial.print(F("31 bit WandId=0x"));
Serial.print(decodedIRData.decodedRawData, HEX);
Serial.print(F(" shifted=0x"));
Serial.println(tDecodedRawData.ULong, HEX);
#endif
/*
* Decode the 9 bit Magnitude + 8 bit checksum
*/
if (!decodePulseDistanceWidthData(&MagiQuestProtocolConstants, MAGIQUEST_MAGNITUDE_BITS + MAGIQUEST_CHECKSUM_BITS,
((MAGIQUEST_WAND_ID_BITS + MAGIQUEST_START_BITS) * 2) + 1)) {
#if defined(LOCAL_DEBUG)
Serial.print(F("MagiQuest: "));
Serial.println(F("Magnitude + checksum decode failed"));
#endif
return false;
}
#if defined(LOCAL_DEBUG)
Serial.print(F("Magnitude + checksum=0x"));
Serial.println(decodedIRData.decodedRawData, HEX);
#endif
tDecodedRawData.ULong = decodedIRData.decodedRawData;
decodedIRData.command = tDecodedRawData.UBytes[1] | tDecodedRawData.UBytes[2] << 8; // Values observed are 0x102,01,04,37,05,38,2D| 02,06,04|03,103,12,18,0E|09
tChecksum += tDecodedRawData.UBytes[2] /* only one bit */+ tDecodedRawData.UBytes[1] + tDecodedRawData.UBytes[0];
if (tChecksum != 0) {
decodedIRData.flags |= IRDATA_FLAGS_PARITY_FAILED;
#if defined(LOCAL_DEBUG)
Serial.print(F("Checksum 0x"));
Serial.print(tChecksum, HEX);
Serial.println(F(" is not 0"));
#endif
}
// Success
decodedIRData.decodedRawData = tWandId; // 31 bit wand_id
decodedIRData.address = tWandId; // lower 16 bit of wand_id
decodedIRData.extra = tWandId >> 16; // upper 15 bit of wand_id
decodedIRData.protocol = MAGIQUEST;
decodedIRData.numberOfBits = MAGIQUEST_BITS;
decodedIRData.flags = IRDATA_FLAGS_IS_MSB_FIRST;
return true;
}
#if defined(LOCAL_DEBUG)
#undef LOCAL_DEBUG
#endif
#endif // _IR_MAGIQUEST_HPP
+441
View File
@@ -0,0 +1,441 @@
/*
* ir_NEC.hpp
*
* Contains functions for receiving and sending NEC IR Protocol in "raw" and standard format with 16 or 8 bit address and 8 bit command
*
* This file is part of Arduino-IRremote https://github.com/Arduino-IRremote/Arduino-IRremote.
*
************************************************************************************
* MIT License
*
* Copyright (c) 2020-2023 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_NEC_HPP
#define _IR_NEC_HPP
#if defined(DEBUG) && !defined(LOCAL_DEBUG)
#define LOCAL_DEBUG // IR_DEBUG_PRINT is a real print function here. Add local debug output.
#else
//#define LOCAL_DEBUG // This enables debug output only for this file. IR_DEBUG_PRINT is still a void function here.
#endif
/** \addtogroup Decoder Decoders and encoders for different protocols
* @{
*/
//==============================================================================
// N N EEEEE CCCC
// NN N E C
// N N N EEE C
// N NN E C
// N N EEEEE CCCC
//==============================================================================
/*
Protocol=NEC Address=0x4 Command=0x8 Raw-Data=0xF708FB04 32 bits LSB first
+8950,-4450
+ 600,- 500 + 650,- 500 + 600,-1650 + 600,- 550
+ 600,- 500 + 600,- 500 + 650,- 500 + 600,- 500
+ 650,-1650 + 600,-1600 + 650,- 500 + 600,-1650
+ 600,-1650 + 600,-1650 + 600,-1600 + 650,-1600
+ 650,- 500 + 600,- 550 + 600,- 500 + 600,-1650
+ 600,- 550 + 600,- 500 + 600,- 550 + 600,- 500
+ 600,-1650 + 600,-1650 + 600,-1650 + 600,- 550
+ 600,-1650 + 600,-1650 + 600,-1650 + 600,-1600
+ 650
Sum: 68000
Protocol=NEC Address=0x8 Command=0x7 Repeat gap=40900us
rawData[4]:
-40900
+10450,-2250
+ 700
Sum: 13400
*/
// http://www.hifi-remote.com/wiki/index.php/NEC
// https://www.sbprojects.net/knowledge/ir/nec.php
// NEC: LSB first, <start bit><address:16> (or <address:8><inverted address:8>) <command:16><command:8><inverted command:8><stop bit>.
// ONKYO: like NEC but force to 16 independent address and 16 bit command bits: <start bit><address:16><command:16><stop bit>
// Standard NEC sends a special fixed repeat frame.
// NEC2: like NEC, but for repeat, the same full frame is sent after the 110 ms. I have a DVD remote with NEC2.
// NEC and NEC 2 only differ in the repeat frames, so the protocol can only be detected correctly after the first repeat.
// PIONEER (not implemented) is NEC2 with 40 kHz
//
// For Apple see https://en.wikipedia.org/wiki/Apple_Remote - <start bit><0x87EE:16><device ID:8><command:7><parity><stop bit> - not implemented!
// The parity is not implemented, so we get: <start bit><0x87EE:16><device ID:8><command:8><stop bit>
//
// IRP notation:
// IRP: NEC {38.0k,564}<1,-1|1,-3>(16,-8,D:8,S:8,F:8,~F:8,1,^108m,(16,-4,1,^108m)*) ==> "*" means send special repeat frames o ore more times
// IRP: NEC2 {38.0k,564}<1,-1|1,-3>(16,-8,D:8,S:8,F:8,~F:8,1,^108m)+ ==> "+" means send frame 1 or more times (special repeat is missing here!)
// Interpretation of IRP notation:
// {38.0k,564} ==> 38.0k -> Frequency , 564 -> unit in microseconds (we use 560), no "msb", so "lsb" is assumed
// <1,-1|1,-3> ==> Zero is 1 unit mark and space | One is 1 unit mark and 3 units space
// 16,-8 ==> Start bit durations
// D:8,S:8,F:8,~F:8 ==> D:8 -> 8 bit bitfield for Device, S:8 -> 8 bit bitfield for Subdevice, F:8 -> 8 bit bitfield for Function, ~F:8 -> 8 bit inverted bitfield for Function
// 1,^108m ==> 1 -> unit mark Stop bit, ^108m -> wait until 108 milliseconds after start of protocol (we use 110)
//
#define NEC_ADDRESS_BITS 16 // 16 bit address or 8 bit address and 8 bit inverted address
#define NEC_COMMAND_BITS 16 // Command and inverted command
#define NEC_BITS (NEC_ADDRESS_BITS + NEC_COMMAND_BITS)
#define NEC_UNIT 560 // 21.28 periods of 38 kHz, 11.2 ticks TICKS_LOW = 8.358 TICKS_HIGH = 15.0
#define NEC_HEADER_MARK (16 * NEC_UNIT) // 9000 | 180
#define NEC_HEADER_SPACE (8 * NEC_UNIT) // 4500 | 90
#define NEC_BIT_MARK NEC_UNIT
#define NEC_ONE_SPACE (3 * NEC_UNIT) // 1690 | 33.8 TICKS_LOW = 25.07 TICKS_HIGH = 45.0
#define NEC_ZERO_SPACE NEC_UNIT
#define NEC_REPEAT_HEADER_SPACE (4 * NEC_UNIT) // 2250
#define NEC_AVERAGE_DURATION 62000 // NEC_HEADER_MARK + NEC_HEADER_SPACE + 32 * 2,5 * NEC_UNIT + NEC_UNIT // 2.5 because we assume more zeros than ones
#define NEC_MINIMAL_DURATION 49900 // NEC_HEADER_MARK + NEC_HEADER_SPACE + 32 * 2 * NEC_UNIT + NEC_UNIT // 2.5 because we assume more zeros than ones
#define NEC_REPEAT_DURATION (NEC_HEADER_MARK + NEC_REPEAT_HEADER_SPACE + NEC_BIT_MARK) // 12 ms
#define NEC_REPEAT_PERIOD 110000 // Commands are repeated every 110 ms (measured from start to start) for as long as the key on the remote control is held down.
#define NEC_REPEAT_DISTANCE (NEC_REPEAT_PERIOD - NEC_AVERAGE_DURATION) // 48 ms
#define NEC_MAXIMUM_REPEAT_DISTANCE (NEC_REPEAT_PERIOD - NEC_MINIMAL_DURATION + 10000) // 70 ms
#define APPLE_ADDRESS 0x87EE
struct PulseDistanceWidthProtocolConstants NECProtocolConstants =
{ NEC, NEC_KHZ, NEC_HEADER_MARK, NEC_HEADER_SPACE, NEC_BIT_MARK,
NEC_ONE_SPACE, NEC_BIT_MARK, NEC_ZERO_SPACE, PROTOCOL_IS_LSB_FIRST, (NEC_REPEAT_PERIOD / MICROS_IN_ONE_MILLI),
&sendNECSpecialRepeat };
// Like NEC but repeats are full frames instead of special NEC repeats
struct PulseDistanceWidthProtocolConstants NEC2ProtocolConstants = { NEC2, NEC_KHZ, NEC_HEADER_MARK, NEC_HEADER_SPACE, NEC_BIT_MARK,
NEC_ONE_SPACE, NEC_BIT_MARK, NEC_ZERO_SPACE, PROTOCOL_IS_LSB_FIRST, (NEC_REPEAT_PERIOD / MICROS_IN_ONE_MILLI), NULL };
/************************************
* Start of send and decode functions
************************************/
/**
* Send special NEC repeat frame
* Repeat commands should be sent in a 110 ms raster.
*/
void IRsend::sendNECRepeat() {
enableIROut (NEC_KHZ); // 38 kHz
mark(NEC_HEADER_MARK); // + 9000
space(NEC_REPEAT_HEADER_SPACE); // - 2250
mark(NEC_BIT_MARK); // + 560
}
/**
* Static function variant of IRsend::sendNECRepeat
* For use in ProtocolConstants. Saves up to 250 bytes compared to a member function.
*/
void sendNECSpecialRepeat() {
IrSender.enableIROut(NEC_KHZ); // 38 kHz
IrSender.mark(NEC_HEADER_MARK); // + 9000
IrSender.space(NEC_REPEAT_HEADER_SPACE); // - 2250
IrSender.mark(NEC_BIT_MARK); // + 560
}
/**
* Convert 16 bit address and 16 bit command to 32 bit NECRaw data
* If we get a command < 0x100, we send command and then ~command
* If we get an address < 0x100, we send 8 bit address and then ~address
* !!! Be aware, that this is flexible, but makes it impossible to send e.g. 0x0042 as 16 bit value!!!
* To force send 16 bit address, use: sendOnkyo().
*/
uint32_t IRsend::computeNECRawDataAndChecksum(uint16_t aAddress, uint16_t aCommand) {
LongUnion tRawData;
// Address 16 bit LSB first
if ((aAddress & 0xFF00) == 0) {
// assume 8 bit address -> send 8 address bits and then 8 inverted address bits LSB first
tRawData.UByte.LowByte = aAddress;
tRawData.UByte.MidLowByte = ~tRawData.UByte.LowByte;
} else {
tRawData.UWord.LowWord = aAddress;
}
// send 8 command bits and then 8 inverted command bits LSB first
tRawData.UByte.MidHighByte = aCommand;
tRawData.UByte.HighByte = ~aCommand;
return tRawData.ULong;
}
/**
* NEC Send frame and special repeats
* There is NO delay after the last sent repeat!
* @param aNumberOfRepeats If < 0 then only a special NEC repeat frame will be sent by calling NECProtocolConstants.SpecialSendRepeatFunction().
*/
void IRsend::sendNEC(uint16_t aAddress, uint16_t aCommand, int_fast8_t aNumberOfRepeats) {
sendPulseDistanceWidth(&NECProtocolConstants, computeNECRawDataAndChecksum(aAddress, aCommand), NEC_BITS, aNumberOfRepeats);
}
/**
* There is NO delay after the last sent repeat!
* @param aNumberOfRepeats If < 0 then only a special repeat frame without leading and trailing space
* will be sent by calling NECProtocolConstants.SpecialSendRepeatFunction().
*/
void IRsend::sendOnkyo(uint16_t aAddress, uint16_t aCommand, int_fast8_t aNumberOfRepeats) {
sendPulseDistanceWidth(&NECProtocolConstants, (uint32_t) aCommand << 16 | aAddress, NEC_BITS, aNumberOfRepeats);
}
/**
* NEC2 Send frame !!! and repeat the frame for each requested repeat !!!
* There is NO delay after the last sent repeat!
* @param aNumberOfRepeats If < 0 then nothing is sent.
*/
void IRsend::sendNEC2(uint16_t aAddress, uint16_t aCommand, int_fast8_t aNumberOfRepeats) {
sendPulseDistanceWidth(&NEC2ProtocolConstants, computeNECRawDataAndChecksum(aAddress, aCommand), NEC_BITS, aNumberOfRepeats);
}
/**
* Apple: Send NEC with fixed 16 bit Apple address 0x87EE.
* There is NO delay after the last sent repeat!
* https://en.wikipedia.org/wiki/Apple_Remote
* https://gist.github.com/darconeous/4437f79a34e3b6441628
* @param aNumberOfRepeats If < 0 then only a special repeat frame without leading and trailing space
* will be sent by calling NECProtocolConstants.SpecialSendRepeatFunction().
*/
void IRsend::sendApple(uint8_t aDeviceId, uint8_t aCommand, int_fast8_t aNumberOfRepeats) {
LongUnion tRawData;
// Address 16 bit LSB first fixed value of 0x87EE
tRawData.UWord.LowWord = APPLE_ADDRESS;
// send Apple code and then 8 command bits LSB first
tRawData.UByte.MidHighByte = aCommand;
tRawData.UByte.HighByte = aDeviceId; // e.g. 0xD7
sendPulseDistanceWidth(&NECProtocolConstants, tRawData.ULong, NEC_BITS, aNumberOfRepeats);
}
/**
* Sends NEC protocol
* @param aNumberOfRepeats If < 0 then only a special repeat frame without leading and trailing space
* will be sent by calling NECProtocolConstants.SpecialSendRepeatFunction().
*/
void IRsend::sendNECRaw(uint32_t aRawData, int_fast8_t aNumberOfRepeats) {
sendPulseDistanceWidth(&NECProtocolConstants, aRawData, NEC_BITS, aNumberOfRepeats);
}
/**
* Decodes also Onkyo and Apple
*/
bool IRrecv::decodeNEC() {
/*
* First check for right data length
* Next check start bit
* Next try the decode
*/
// Check we have the right amount of data (68). The +4 is for initial gap, start bit mark and space + stop bit mark.
if (decodedIRData.rawlen != ((2 * NEC_BITS) + 4) && (decodedIRData.rawlen != 4)) {
IR_DEBUG_PRINT(F("NEC: "));
IR_DEBUG_PRINT(F("Data length="));
IR_DEBUG_PRINT(decodedIRData.rawlen);
IR_DEBUG_PRINTLN(F(" is not 68 or 4"));
return false;
}
// Check header "mark" this must be done for repeat and data
if (!matchMark(decodedIRData.rawDataPtr->rawbuf[1], NEC_HEADER_MARK)) {
return false;
}
// Check for repeat - here we have another header space length
if (decodedIRData.rawlen == 4) {
if (matchSpace(decodedIRData.rawDataPtr->rawbuf[2], NEC_REPEAT_HEADER_SPACE)
&& matchMark(decodedIRData.rawDataPtr->rawbuf[3], NEC_BIT_MARK)) {
decodedIRData.flags = IRDATA_FLAGS_IS_REPEAT | IRDATA_FLAGS_IS_LSB_FIRST;
decodedIRData.address = lastDecodedAddress;
decodedIRData.command = lastDecodedCommand;
decodedIRData.protocol = lastDecodedProtocol;
return true;
}
return false;
}
// Check command header space
if (!matchSpace(decodedIRData.rawDataPtr->rawbuf[2], NEC_HEADER_SPACE)) {
#if defined(LOCAL_DEBUG)
Serial.print(F("NEC: "));
Serial.println(F("Header space length is wrong"));
#endif
return false;
}
// Try to decode as NEC protocol
if (!decodePulseDistanceWidthData(&NECProtocolConstants, NEC_BITS)) {
#if defined(LOCAL_DEBUG)
Serial.print(F("NEC: "));
Serial.println(F("Decode failed"));
#endif
return false;
}
// Success
// decodedIRData.flags = IRDATA_FLAGS_IS_LSB_FIRST; // Not required, since this is the start value
LongUnion tValue;
tValue.ULong = decodedIRData.decodedRawData;
decodedIRData.command = tValue.UByte.MidHighByte; // 8 bit
#if defined(DECODE_ONKYO)
// Here only Onkyo protocol is supported -> force 16 bit address and command decoding
decodedIRData.address = tValue.UWord.LowWord; // first 16 bit
decodedIRData.protocol = ONKYO;
decodedIRData.command = tValue.UWord.HighWord; // 16 bit command
#else
// Address
if (tValue.UWord.LowWord == APPLE_ADDRESS) {
/*
* Apple
*/
decodedIRData.protocol = APPLE;
decodedIRData.address = tValue.UByte.HighByte;
} else {
/*
* NEC LSB first, so first sent bit is also LSB of decodedIRData.decodedRawData
*/
if (tValue.UByte.LowByte == (uint8_t)(~tValue.UByte.MidLowByte)) {
// standard 8 bit address NEC protocol
decodedIRData.address = tValue.UByte.LowByte; // first 8 bit
} else {
// extended NEC protocol
decodedIRData.address = tValue.UWord.LowWord; // first 16 bit
}
// Check for command if it is 8 bit NEC or 16 bit ONKYO
if (tValue.UByte.MidHighByte == (uint8_t)(~tValue.UByte.HighByte)) {
decodedIRData.protocol = NEC;
} else {
decodedIRData.protocol = ONKYO;
decodedIRData.command = tValue.UWord.HighWord; // 16 bit command
}
}
#endif
decodedIRData.numberOfBits = NEC_BITS;
// check for NEC2 repeat, do not check for same content ;-)
checkForRepeatSpaceTicksAndSetFlag(NEC_MAXIMUM_REPEAT_DISTANCE / MICROS_PER_TICK);
if (decodedIRData.flags & IRDATA_FLAGS_IS_REPEAT) {
decodedIRData.protocol = NEC2;
decodedIRData.flags |= IRDATA_FLAGS_IS_PROTOCOL_WITH_DIFFERENT_REPEAT;
}
return true;
}
/*********************************************************************************
* Old deprecated functions, kept for backward compatibility to old 2.0 tutorials
*********************************************************************************/
bool IRrecv::decodeNECMSB(decode_results *aResults) {
unsigned int offset = 1; // Index in to results; Skip first space.
// Check header "mark"
if (!matchMark(aResults->rawbuf[offset], NEC_HEADER_MARK)) {
return false;
}
offset++;
// Check for repeat
if ((aResults->rawlen == 4) && matchSpace(aResults->rawbuf[offset], NEC_REPEAT_HEADER_SPACE)
&& matchMark(aResults->rawbuf[offset + 1], NEC_BIT_MARK)) {
aResults->bits = 0;
aResults->value = 0xFFFFFFFF;
decodedIRData.flags |= IRDATA_FLAGS_IS_REPEAT;
decodedIRData.protocol = NEC;
return true;
}
// Check we have the right amount of data (32). +4 for initial gap, start bit mark and space + stop bit mark
if (aResults->rawlen != (2 * NEC_BITS) + 4) {
IR_DEBUG_PRINT(F("NEC MSB: "));
IR_DEBUG_PRINT(F("Data length="));
IR_DEBUG_PRINT(aResults->rawlen);
IR_DEBUG_PRINTLN(F(" is not 68"));
return false;
}
// Check header "space"
if (!matchSpace(aResults->rawbuf[offset], NEC_HEADER_SPACE)) {
#if defined(LOCAL_DEBUG)
Serial.print(F("NEC MSB: "));
Serial.println(F("Header space length is wrong"));
#endif
return false;
}
offset++;
if (!decodePulseDistanceWidthData(NEC_BITS, offset, NEC_BIT_MARK, NEC_ONE_SPACE, 0, PROTOCOL_IS_MSB_FIRST)) {
#if defined(LOCAL_DEBUG)
Serial.print(F("NEC MSB: "));
Serial.println(F("Decode failed"));
#endif
return false;
}
// Stop bit
if (!matchMark(aResults->rawbuf[offset + (2 * NEC_BITS)], NEC_BIT_MARK)) {
#if defined(LOCAL_DEBUG)
Serial.print(F("NEC MSB: "));
Serial.println(F("Stop bit mark length is wrong"));
#endif
return false;
}
// Success
aResults->value = decodedIRData.decodedRawData;
aResults->bits = NEC_BITS;
aResults->decode_type = NEC;
decodedIRData.protocol = NEC;
return true;
}
/**
* With Send sendNECMSB() you can send your old 32 bit codes.
* To convert one into the other, you must reverse the byte positions and then reverse all bit positions of each byte.
* Use bitreverse32Bit().
* Or write it as one binary string and reverse/mirror it.
* Example:
* 0xCB340102 byte reverse -> 02 01 34 CB bit reverse-> 40 80 2C D3.
* 0xCB340102 is binary 11001011001101000000000100000010.
* 0x40802CD3 is binary 01000000100000000010110011010011.
* If you read the first binary sequence backwards (right to left), you get the second sequence.
*/
void IRsend::sendNECMSB(uint32_t data, uint8_t nbits, bool repeat) {
// Set IR carrier frequency
enableIROut (NEC_KHZ);
if (data == 0xFFFFFFFF || repeat) {
sendNECRepeat();
return;
}
// Header
mark(NEC_HEADER_MARK);
space(NEC_HEADER_SPACE);
// Old version with MSB first Data + stop bit
sendPulseDistanceWidthData(NEC_BIT_MARK, NEC_ONE_SPACE, NEC_BIT_MARK, NEC_ZERO_SPACE, data, nbits, PROTOCOL_IS_MSB_FIRST);
}
/** @}*/
#if defined(LOCAL_DEBUG)
#undef LOCAL_DEBUG
#endif
#endif // _IR_NEC_HPP
+113
View File
@@ -0,0 +1,113 @@
/*
* ir_Others.hpp
*
* Contains functions for miscellaneous protocols
*
* This file is part of Arduino-IRremote https://github.com/Arduino-IRremote/Arduino-IRremote.
*
************************************************************************************
* MIT License
*
* 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.
*
************************************************************************************
*/
/** \addtogroup Decoder Decoders and encoders for different protocols
* @{
*/
#ifndef _IR_OTHERS_HPP
#define _IR_OTHERS_HPP
//==============================================================================
// DDDD IIIII SSSS H H
// D D I S H H
// D D I SSS HHHHH
// D D I S H H
// DDDD IIIII SSSS H H
//==============================================================================
// DISH support by Todd Treece
//
// The send function needs to be repeated 4 times
// Only send the last for characters of the hex.
// I.E. Use 0x1C10 instead of 0x0000000000001C10 as listed in the LIRC file.
// Here is the LIRC file I found that seems to match the remote codes from the
// oscilloscope: DISH NETWORK (echostar 301):
// http://lirc.sourceforge.net/remotes/echostar/301_501_3100_5100_58xx_59xx
#define DISH_BITS 16
#define DISH_HEADER_MARK 400
#define DISH_HEADER_SPACE 6100
#define DISH_BIT_MARK 400
#define DISH_ONE_SPACE 1700
#define DISH_ZERO_SPACE 2800
#define DISH_REPEAT_SPACE 6200 // really?
struct PulseDistanceWidthProtocolConstants DishProtocolConstants = { UNKNOWN, 56, DISH_HEADER_MARK, DISH_HEADER_SPACE,
DISH_BIT_MARK, DISH_ONE_SPACE, DISH_BIT_MARK, DISH_ZERO_SPACE, PROTOCOL_IS_MSB_FIRST, 40, NULL };
void IRsend::sendDish(uint16_t aData) {
sendPulseDistanceWidth(&DishProtocolConstants, aData, DISH_BITS, 4);
}
//==============================================================================
// W W H H Y Y N N TTTTT EEEEE RRRRR
// W W H H Y Y NN N T E R R
// W W W HHHHH Y N N N T EEE RRRR
// W W W H H Y N NN T E R R
// WWW H H Y N N T EEEEE R R
//==============================================================================
// Whynter A/C ARC-110WD added by Francesco Meschia
// see https://docs.google.com/spreadsheets/d/1dsr4Jh-nzC6xvSKGpLlPBF0NRwvlpyw-ozg8eZU813w/edit#gid=0
// Looking at the code table the protocol is LSB first with start and stop bit.
// 4 bit checksum, constant address 0xAA00, 8 bit Command and 4 bit Command group
// but we use MSB first to be backwards compatible
#define WHYNTER_BITS 32
#define WHYNTER_HEADER_MARK 2850
#define WHYNTER_HEADER_SPACE 2850
#define WHYNTER_BIT_MARK 750
#define WHYNTER_ONE_SPACE 2150
#define WHYNTER_ZERO_SPACE 750
struct PulseDistanceWidthProtocolConstants WhynterProtocolConstants = { WHYNTER, 38, WHYNTER_HEADER_MARK, WHYNTER_HEADER_SPACE,
WHYNTER_BIT_MARK, WHYNTER_ONE_SPACE, WHYNTER_BIT_MARK, WHYNTER_ZERO_SPACE, PROTOCOL_IS_MSB_FIRST, 110, NULL };
void IRsend::sendWhynter(uint32_t aData, uint8_t aNumberOfBitsToSend) {
sendPulseDistanceWidth(&WhynterProtocolConstants, aData, NEC_BITS, aNumberOfBitsToSend);
}
bool IRrecv::decodeWhynter() {
// Check we have the right amount of data (68). The +4 is for initial gap, start bit mark and space + stop bit mark.
if (decodedIRData.rawlen != (2 * WHYNTER_BITS) + 4) {
return false;
}
if (!checkHeader(&WhynterProtocolConstants)) {
return false;
}
if (!decodePulseDistanceWidthData(&WhynterProtocolConstants, WHYNTER_BITS)) {
return false;
}
// Success
decodedIRData.flags = IRDATA_FLAGS_IS_MSB_FIRST;
decodedIRData.numberOfBits = WHYNTER_BITS;
decodedIRData.protocol = WHYNTER;
return true;
}
/** @}*/
#endif // _IR_OTHERS_HPP
+344
View File
@@ -0,0 +1,344 @@
/*
* @file ir_Pronto.hpp
* @brief In this file, the functions IRrecv::compensateAndPrintPronto and IRsend::sendPronto are defined.
*
* See http://www.harctoolbox.org/Glossary.html#ProntoSemantics
* Pronto database http://www.remotecentral.com/search.htm
*
* This file is part of Arduino-IRremote https://github.com/Arduino-IRremote/Arduino-IRremote.
*
************************************************************************************
* MIT License
*
* Copyright (c) 2020 Bengt Martensson
*
* 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_PRONTO_HPP
#define _IR_PRONTO_HPP
#if defined(DEBUG) && !defined(LOCAL_DEBUG)
#define LOCAL_DEBUG
#else
//#define LOCAL_DEBUG // This enables debug output only for this file
#endif
/** \addtogroup Decoder Decoders and encoders for different protocols
* @{
*/
//! @cond
// DO NOT EXPORT from this file
static const uint16_t learnedToken = 0x0000U;
static const uint16_t learnedNonModulatedToken = 0x0100U;
static const uint16_t bitsInHexadecimal = 4U;
static const uint16_t digitsInProntoNumber = 4U;
static const uint16_t numbersInPreamble = 4U;
static const uint16_t hexMask = 0xFU;
static const uint32_t referenceFrequency = 4145146UL;
static const uint16_t fallbackFrequency = 64767U; // To use with frequency = 0;
static const uint32_t microsecondsInSeconds = 1000000UL;
static const uint16_t PRONTO_DEFAULT_GAP = 45000;
//! @endcond
static uint16_t toFrequencyKHz(uint16_t code) {
return ((referenceFrequency / code) + 500) / 1000;
}
/*
* Parse the string given as Pronto Hex, and send it a number of times given as argument.
* The first number denotes the type of the signal. 0000 denotes a raw IR signal with modulation,
// The second number denotes a frequency code
*/
void IRsend::sendPronto(const uint16_t *data, uint16_t length, int_fast8_t aNumberOfRepeats) {
uint16_t timebase = (microsecondsInSeconds * data[1] + referenceFrequency / 2) / referenceFrequency;
uint16_t khz;
switch (data[0]) {
case learnedToken: // normal, "learned"
khz = toFrequencyKHz(data[1]);
break;
case learnedNonModulatedToken: // non-demodulated, "learned"
khz = 0U;
break;
default:
return; // There are other types, but they are not handled yet.
}
uint16_t intros = 2 * data[2];
uint16_t repeats = 2 * data[3];
#if defined(LOCAL_DEBUG)
Serial.print(F("sendPronto intros="));
Serial.print(intros);
Serial.print(F(" repeats="));
Serial.println(repeats);
#endif
if (numbersInPreamble + intros + repeats != length) { // inconsistent sizes
return;
}
/*
* Generate a new microseconds timing array for sendRaw.
* If recorded by IRremote, intro contains the whole IR data and repeat is empty
*/
uint16_t durations[intros + repeats];
for (uint16_t i = 0; i < intros + repeats; i++) {
uint32_t duration = ((uint32_t) data[i + numbersInPreamble]) * timebase;
durations[i] = (uint16_t) ((duration <= UINT16_MAX) ? duration : UINT16_MAX);
}
/*
* Send the intro. intros is even.
* Do not send the trailing space here, send it if repeats are requested
*/
if (intros >= 2) {
sendRaw(durations, intros - 1, khz);
}
if (repeats == 0 || aNumberOfRepeats == 0) {
// only send intro once
return;
}
/*
* Now send the trailing space/gap of the intro and all the repeats
*/
if (intros >= 2) {
delay(durations[intros - 1] / MICROS_IN_ONE_MILLI); // equivalent to space(durations[intros - 1]); but allow bigger values for the gap
}
for (int i = 0; i < aNumberOfRepeats; i++) {
sendRaw(durations + intros, repeats - 1, khz);
if ((i + 1) < aNumberOfRepeats) { // skip last trailing space/gap, see above
delay(durations[intros + repeats - 1] / MICROS_IN_ONE_MILLI);
}
}
}
/**
* Parse the string given as Pronto Hex, and send it a number of times given
* as the second argument. Thereby the division of the Pronto Hex into
* an intro-sequence and a repeat sequence is taken into account:
* First the intro sequence is sent, then the repeat sequence is sent times-1 times.
* However, if the intro sequence is empty, the repeat sequence is sent times times.
* <a href="http://www.harctoolbox.org/Glossary.html#ProntoSemantics">Reference</a>.
*
* Note: Using this function is very wasteful for the memory consumption on
* a small board.
* Normally it is a much better idea to use a tool like e.g. IrScrutinizer
* to transform Pronto type signals offline
* to a more memory efficient format.
*
* @param str C type string (null terminated) containing a Pronto Hex representation.
* @param aNumberOfRepeats Number of times to send the signal.
*/
void IRsend::sendPronto(const char *str, int_fast8_t aNumberOfRepeats) {
size_t len = strlen(str) / (digitsInProntoNumber + 1) + 1;
uint16_t data[len];
const char *p = str;
char *endptr[1];
for (uint16_t i = 0; i < len; i++) {
long x = strtol(p, endptr, 16);
if (x == 0 && i >= numbersInPreamble) {
// Alignment error?, bail immediately (often right result).
len = i;
break;
}
data[i] = static_cast<uint16_t>(x); // If input is conforming, there can be no overflow!
p = *endptr;
}
sendPronto(data, len, aNumberOfRepeats);
}
#if defined(__AVR__)
/**
* Version of sendPronto that reads from PROGMEM, saving RAM memory.
* @param str pronto C type string (null terminated) containing a Pronto Hex representation.
* @param aNumberOfRepeats Number of times to send the signal.
*/
//far pointer (? for ATMega2560 etc.)
void IRsend::sendPronto_PF(uint_farptr_t str, int_fast8_t aNumberOfRepeats) {
size_t len = strlen_PF(str);
char work[len + 1];
strcpy_PF(work, str); // We know that string including terminating character fits in work
sendPronto(work, aNumberOfRepeats);
}
//standard pointer
void IRsend::sendPronto_P(const char *str, int_fast8_t aNumberOfRepeats) {
size_t len = strlen_P(str);
char work[len + 1];
strcpy_P(work, str);
sendPronto(work, aNumberOfRepeats);
}
#endif
/*
* Copy flash data to ram buffer in stack
*/
void IRsend::sendPronto(const __FlashStringHelper *str, int_fast8_t aNumberOfRepeats) {
size_t len = strlen_P(reinterpret_cast<const char*>(str));
char work[len + 1];
strcpy_P(work, reinterpret_cast<const char*>(str));
return sendPronto(work, aNumberOfRepeats);
}
static uint16_t effectiveFrequency(uint16_t frequency) {
return frequency > 0 ? frequency : fallbackFrequency;
}
static uint16_t toTimebase(uint16_t frequency) {
return microsecondsInSeconds / effectiveFrequency(frequency);
}
static uint16_t toFrequencyCode(uint16_t frequency) {
return referenceFrequency / effectiveFrequency(frequency);
}
static char hexDigit(uint16_t x) {
return (char) (x <= 9 ? ('0' + x) : ('A' + (x - 10)));
}
static void dumpDigit(Print *aSerial, uint16_t number) {
aSerial->print(hexDigit(number));
}
static void dumpNumber(Print *aSerial, uint16_t number) {
for (uint16_t i = 0; i < digitsInProntoNumber; i++) {
uint16_t shifts = bitsInHexadecimal * (digitsInProntoNumber - 1 - i);
dumpDigit(aSerial, (number >> shifts) & hexMask);
}
aSerial->print(' ');
}
static void dumpDuration(Print *aSerial, uint32_t duration, uint16_t timebase) {
dumpNumber(aSerial, (duration + timebase / 2) / timebase);
}
/*
* Compensate received values by MARK_EXCESS_MICROS, like it is done for decoding!
*/
static void compensateAndDumpSequence(Print *aSerial, const volatile IRRawbufType *data, size_t length, uint16_t timebase) {
for (size_t i = 0; i < length; i++) {
uint32_t tDuration = data[i] * MICROS_PER_TICK;
if (i & 1) {
// Mark
tDuration -= getMarkExcessMicros();
} else {
tDuration += getMarkExcessMicros();
}
dumpDuration(aSerial, tDuration, timebase);
}
// append a gap
dumpDuration(aSerial, PRONTO_DEFAULT_GAP, timebase);
}
/**
* Print the result (second argument) as Pronto Hex on the Print supplied as argument.
* Used in the ReceiveDump example.
* @param aSerial The Print object on which to write, for Arduino you can use &Serial.
* @param aFrequencyHertz Modulation frequency in Hz. Often 38000Hz.
*/
void IRrecv::compensateAndPrintIRResultAsPronto(Print *aSerial, uint16_t aFrequencyHertz) {
aSerial->println(F("Pronto Hex as string"));
aSerial->print(F("char prontoData[] = \""));
dumpNumber(aSerial, aFrequencyHertz > 0 ? learnedToken : learnedNonModulatedToken);
dumpNumber(aSerial, toFrequencyCode(aFrequencyHertz));
dumpNumber(aSerial, (decodedIRData.rawlen + 1) / 2);
dumpNumber(aSerial, 0);
uint16_t timebase = toTimebase(aFrequencyHertz);
compensateAndDumpSequence(aSerial, &decodedIRData.rawDataPtr->rawbuf[1], decodedIRData.rawlen - 1, timebase); // skip leading space
aSerial->println(F("\";"));
}
/*
* Functions for dumping Pronto to a String. This is not very time and space efficient
* and can lead to resource problems especially on small processors like AVR's
*/
static bool dumpDigit(String *aString, uint16_t number) {
aString->concat(hexDigit(number));
return number;
}
static size_t dumpNumber(String *aString, uint16_t number) {
size_t size = 0;
for (uint16_t i = 0; i < digitsInProntoNumber; i++) {
uint16_t shifts = bitsInHexadecimal * (digitsInProntoNumber - 1 - i);
size += dumpDigit(aString, (number >> shifts) & hexMask);
}
aString->concat(' ');
size++;
return size;
}
/*
* Compensate received values by MARK_EXCESS_MICROS, like it is done for decoding!
*/
static size_t dumpDuration(String *aString, uint32_t duration, uint16_t timebase) {
return dumpNumber(aString, (duration + timebase / 2) / timebase);
}
static size_t compensateAndDumpSequence(String *aString, const volatile IRRawbufType *data, size_t length, uint16_t timebase) {
size_t size = 0;
for (size_t i = 0; i < length; i++) {
uint32_t tDuration = data[i] * MICROS_PER_TICK;
if (i & 1) {
// Mark
tDuration -= getMarkExcessMicros();
} else {
tDuration += getMarkExcessMicros();
}
size += dumpDuration(aString, tDuration, timebase);
}
// append minimum gap
size += dumpDuration(aString, PRONTO_DEFAULT_GAP, timebase);
return size;
}
/*
* Writes Pronto HEX to a String object.
* Returns the amount of characters added to the string.(360 characters for a NEC code!)
*/
size_t IRrecv::compensateAndStorePronto(String *aString, uint16_t frequency) {
size_t size = 0;
uint16_t timebase = toTimebase(frequency);
size += dumpNumber(aString, frequency > 0 ? learnedToken : learnedNonModulatedToken);
size += dumpNumber(aString, toFrequencyCode(frequency));
size += dumpNumber(aString, (decodedIRData.rawlen + 1) / 2);
size += dumpNumber(aString, 0);
size += compensateAndDumpSequence(aString, &decodedIRData.rawDataPtr->rawbuf[1], decodedIRData.rawlen - 1,
timebase); // skip leading space
return size;
}
/** @}*/
#if defined(LOCAL_DEBUG)
#undef LOCAL_DEBUG
#endif
#endif // _IR_PRONTO_HPP
+670
View File
@@ -0,0 +1,670 @@
/*
* ir_RC5_RC6.hpp
*
* Contains functions for receiving and sending RC5, RC5X, RC6 protocols
*
* This file is part of Arduino-IRremote https://github.com/Arduino-IRremote/Arduino-IRremote.
*
************************************************************************************
* MIT License
*
* Copyright (c) 2020-2024 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_RC5_RC6_HPP
#define _IR_RC5_RC6_HPP
#if defined(DEBUG) && !defined(LOCAL_DEBUG)
#define LOCAL_DEBUG
#else
//#define LOCAL_DEBUG // This enables debug output only for this file
#endif
/** \addtogroup Decoder Decoders and encoders for different protocols
* @{
*/
uint8_t sLastSendToggleValue = 1; // To start first command with toggle 0
//uint8_t sLastReceiveToggleValue = 3; // 3 -> start value
//==============================================================================
// RRRR CCCC 55555
// R R C 5
// RRRR C 5555
// R R C 5
// R R CCCC 5555
//==============================================================================
/*
Protocol=RC5 Address=0x11 Command=0x36 Raw-Data=0x1476 13 bits MSB first
+ 900,- 900
+1800,-1750 +1800,- 850 + 900,- 850 + 900,-1750
+ 950,- 850 + 900,- 850 +1800,-1750 + 950,- 850
+1800
Sum: 23100
RC5X with 7.th MSB of command set
Protocol=RC5 Address=0x11 Command=0x76 Toggle=1 Raw-Data=0xC76 13 bits MSB first
+1800,-1750
+ 850,- 900 +1800,- 850 + 950,- 850 + 900,-1750
+ 900,- 850 + 950,- 850 +1800,-1750 + 900,- 850
+1800
Sum: 23050
*/
//
// see: https://www.sbprojects.net/knowledge/ir/rc5.php
// https://en.wikipedia.org/wiki/Manchester_code
// https://forum.arduino.cc/t/sending-rc-5-extended-code-using-irsender/1045841/10 - Protocol Maranz Extended
// mark->space => 0
// space->mark => 1
// MSB first 1 start bit, 1 field bit, 1 toggle bit + 5 bit address + 6 bit command, no stop bit
// Field bit is 1 for RC5 and inverted 7. command bit for RC5X. That way the first 64 commands of RC5X remain compatible with the original RC5.
// SF TAAA AACC CCCC
// IR duty factor is 25%,
//
#define RC5_ADDRESS_BITS 5
#define RC5_COMMAND_BITS 6
#define RC5_COMMAND_FIELD_BIT 1
#define RC5_TOGGLE_BIT 1
#define RC5_BITS (RC5_COMMAND_FIELD_BIT + RC5_TOGGLE_BIT + RC5_ADDRESS_BITS + RC5_COMMAND_BITS) // 13
#define RC5_UNIT 889 // 32 periods of 36 kHz (888.8888)
#define MIN_RC5_MARKS ((RC5_BITS + 1) / 2) // 7 - Divided by 2 to handle the bit sequence of 01010101 which gives one mark and space for each 2 bits
#define RC5_DURATION (15L * RC5_UNIT) // 13335
#define RC5_REPEAT_PERIOD (128L * RC5_UNIT) // 113792
#define RC5_REPEAT_DISTANCE (RC5_REPEAT_PERIOD - RC5_DURATION) // 100 ms
#define RC5_MAXIMUM_REPEAT_DISTANCE (RC5_REPEAT_DISTANCE + (RC5_REPEAT_DISTANCE / 4)) // Just a guess
/************************************
* Start of send and decode functions
************************************/
/**
* @param aCommand If aCommand is >=0x40 then we switch automatically to RC5X.
* @param aEnableAutomaticToggle Send toggle bit according to the state of the static sLastSendToggleValue variable.
*/
void IRsend::sendRC5(uint8_t aAddress, uint8_t aCommand, int_fast8_t aNumberOfRepeats, bool aEnableAutomaticToggle) {
// Set IR carrier frequency
enableIROut (RC5_RC6_KHZ);
uint16_t tIRData = ((aAddress & 0x1F) << RC5_COMMAND_BITS);
if (aCommand < 0x40) {
// Auto discovery of RC5X, set field bit to 1
tIRData |= 1 << (RC5_TOGGLE_BIT + RC5_ADDRESS_BITS + RC5_COMMAND_BITS);
} else {
// Mask bit 7 of command and let field bit 0
aCommand &= 0x3F;
}
tIRData |= aCommand;
if (aEnableAutomaticToggle) {
if (sLastSendToggleValue == 0) {
sLastSendToggleValue = 1;
// set toggled bit
tIRData |= 1 << (RC5_ADDRESS_BITS + RC5_COMMAND_BITS);
} else {
sLastSendToggleValue = 0;
}
}
uint_fast8_t tNumberOfCommands = aNumberOfRepeats + 1;
while (tNumberOfCommands > 0) {
// start bit is sent by sendBiphaseData
sendBiphaseData(RC5_UNIT, tIRData, RC5_BITS);
tNumberOfCommands--;
// skip last delay!
if (tNumberOfCommands > 0) {
// send repeated command in a fixed raster
delay(RC5_REPEAT_DISTANCE / MICROS_IN_ONE_MILLI);
}
}
}
/**
* Try to decode data as RC5 protocol
* _ _ _ _ _ _ _ _ _ _ _ _ _
* Clock _____| |_| |_| |_| |_| |_| |_| |_| |_| |_| |_| |_| |_| |
* ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ End of each data bit period
* _ _ - Mark
* 2 Start bits for RC5 _____| |_| ... - Data starts with a space->mark bit
* - Space
* _
* 1 Start bit for RC5X _____| ...
*
*/
bool IRrecv::decodeRC5() {
uint8_t tBitIndex;
uint32_t tDecodedRawData = 0;
// Set Biphase decoding start values
initBiphaselevel(1, RC5_UNIT); // Skip gap space
// Check we have the right amount of data (11 to 26). The +2 is for initial gap and start bit mark.
if (decodedIRData.rawlen < ((RC5_BITS + 1) / 2) + 2 && (RC5_BITS + 2) < decodedIRData.rawlen) {
// no debug output, since this check is mainly to determine the received protocol
IR_DEBUG_PRINT(F("RC5: "));
IR_DEBUG_PRINT(F("Data length="));
IR_DEBUG_PRINT(decodedIRData.rawlen);
IR_DEBUG_PRINTLN(F(" is not between 9 and 15"));
return false;
}
// Check start bit, the first space is included in the gap
if (getBiphaselevel() != MARK) {
IR_DEBUG_PRINT(F("RC5: "));
IR_DEBUG_PRINTLN(F("first getBiphaselevel() is not MARK"));
return false;
}
/*
* Get data bits - MSB first
*/
for (tBitIndex = 0; sBiphaseDecodeRawbuffOffset < decodedIRData.rawlen; tBitIndex++) {
// get next 2 levels and check for transition
uint8_t tStartLevel = getBiphaselevel();
uint8_t tEndLevel = getBiphaselevel();
if ((tStartLevel == SPACE) && (tEndLevel == MARK)) {
// we have a space to mark transition here
tDecodedRawData = (tDecodedRawData << 1) | 1;
} else if ((tStartLevel == MARK) && (tEndLevel == SPACE)) {
// we have a mark to space transition here
tDecodedRawData = (tDecodedRawData << 1) | 0;
} else {
#if defined(LOCAL_DEBUG)
Serial.print(F("RC5: "));
Serial.println(F("no transition found, decode failed"));
#endif
return false;
}
}
// Success
decodedIRData.numberOfBits = tBitIndex; // must be RC5_BITS
LongUnion tValue;
tValue.ULong = tDecodedRawData;
decodedIRData.decodedRawData = tDecodedRawData;
decodedIRData.command = tValue.UByte.LowByte & 0x3F;
decodedIRData.address = (tValue.UWord.LowWord >> RC5_COMMAND_BITS) & 0x1F;
// Get the inverted 7. command bit for RC5X, the inverted value is always 1 for RC5 and serves as a second start bit.
if ((tValue.UWord.LowWord & (1 << (RC5_TOGGLE_BIT + RC5_ADDRESS_BITS + RC5_COMMAND_BITS))) == 0) {
decodedIRData.command += 0x40;
}
decodedIRData.flags = IRDATA_FLAGS_IS_MSB_FIRST;
if (tValue.UByte.MidLowByte & 0x8) {
decodedIRData.flags = IRDATA_FLAGS_TOGGLE_BIT | IRDATA_FLAGS_IS_MSB_FIRST;
}
decodedIRData.protocol = RC5;
// check for repeat
checkForRepeatSpaceTicksAndSetFlag(RC5_MAXIMUM_REPEAT_DISTANCE / MICROS_PER_TICK);
return true;
}
//+=============================================================================
// RRRR CCCC 6666
// R R C 6
// RRRR C 6666
// R R C 6 6
// R R CCCC 666
//+=============================================================================
//
/*
Protocol=RC6 Address=0xF1 Command=0x76 Raw-Data=0xF176 20 bits MSB first
+2650,- 850
+ 500,- 850 + 500,- 400 + 450,- 450 + 450,- 850
+1400,- 400 + 450,- 450 + 450,- 450 + 450,- 900
+ 450,- 450 + 450,- 400 + 950,- 850 + 900,- 450
+ 450,- 450 + 450,- 850 + 950,- 400 + 450,- 900
+ 450
Sum: 23150
*/
// Frame RC6: 1 start bit + 1 Bit "1" + 3 mode bits (000) + 1 toggle bit + 8 address + 8 command bits + 2666us pause - 22 bits incl. start bit
// Frame RC6A: 1 start bit + 1 Bit "1" + 3 mode bits (110) + 1 toggle bit + "1" + 14 customer bits + 8 system bits + 8 command bits + 2666us pause - 37 bits incl. start bit
// !!! toggle bit has another timing :-( !!!
// mark->space => 1
// space->mark => 0
// https://www.sbprojects.net/knowledge/ir/rc6.php
// https://www.mikrocontroller.net/articles/IRMP_-_english#RC6_.2B_RC6A
// https://en.wikipedia.org/wiki/Manchester_code
#define MIN_RC6_SAMPLES 1
#define RC6_RPT_LENGTH 46000
#define RC6_LEADING_BIT 1
#define RC6_MODE_BITS 3 // never seen others than all 0 for Philips TV
#define RC6_TOGGLE_BIT 1 // toggles at every key press. Can be used to distinguish repeats from 2 key presses and has another timing :-(.
#define RC6_TOGGLE_BIT_INDEX RC6_MODE_BITS // fourth position, index = 3
#define RC6_ADDRESS_BITS 8
#define RC6_COMMAND_BITS 8
#define RC6_CUSTOMER_BITS 14
#define RC6_BITS (RC6_LEADING_BIT + RC6_MODE_BITS + RC6_TOGGLE_BIT + RC6_ADDRESS_BITS + RC6_COMMAND_BITS) // 21
#define RC6A_BITS (RC6_LEADING_BIT + RC6_MODE_BITS + RC6_TOGGLE_BIT + 1 + RC6_CUSTOMER_BITS + RC6_ADDRESS_BITS + RC6_COMMAND_BITS) // 36
#define RC6_UNIT 444 // 16 periods of 36 kHz (444.4444)
#define RC6_HEADER_MARK (6 * RC6_UNIT) // 2666
#define RC6_HEADER_SPACE (2 * RC6_UNIT) // 889
#define RC6_TRAILING_SPACE (6 * RC6_UNIT) // 2666
#define MIN_RC6_MARKS 4 + ((RC6_ADDRESS_BITS + RC6_COMMAND_BITS) / 2) // 12, 4 are for preamble
#define RC6_REPEAT_DISTANCE 107000 // just a guess but > 2.666ms
#define RC6_MAXIMUM_REPEAT_DISTANCE (RC6_REPEAT_DISTANCE + (RC6_REPEAT_DISTANCE / 4)) // Just a guess
/**
* Main RC6 send function
*/
void IRsend::sendRC6(uint32_t aRawData, uint8_t aNumberOfBitsToSend) {
sendRC6Raw(aRawData, aNumberOfBitsToSend);
}
void IRsend::sendRC6Raw(uint32_t aRawData, uint8_t aNumberOfBitsToSend) {
// Set IR carrier frequency
enableIROut (RC5_RC6_KHZ);
// Header
mark(RC6_HEADER_MARK);
space(RC6_HEADER_SPACE);
// Start bit
mark(RC6_UNIT);
space(RC6_UNIT);
// Data MSB first
uint32_t mask = 1UL << (aNumberOfBitsToSend - 1);
for (uint_fast8_t i = 1; mask; i++, mask >>= 1) {
// The fourth bit we send is the "double width toggle bit"
unsigned int t = (i == (RC6_TOGGLE_BIT_INDEX + 1)) ? (RC6_UNIT * 2) : (RC6_UNIT);
if (aRawData & mask) {
mark(t);
space(t);
} else {
space(t);
mark(t);
}
}
}
/**
* Send RC6 64 bit raw data
* Can be used to send RC6A with ?31? data bits
*/
void IRsend::sendRC6(uint64_t aRawData, uint8_t aNumberOfBitsToSend) {
sendRC6Raw(aRawData, aNumberOfBitsToSend);
}
void IRsend::sendRC6Raw(uint64_t aRawData, uint8_t aNumberOfBitsToSend) {
// Set IR carrier frequency
enableIROut (RC5_RC6_KHZ);
// Header
mark(RC6_HEADER_MARK);
space(RC6_HEADER_SPACE);
// Start bit
mark(RC6_UNIT);
space(RC6_UNIT);
// Data MSB first
uint64_t mask = 1ULL << (aNumberOfBitsToSend - 1);
for (uint_fast8_t i = 1; mask; i++, mask >>= 1) {
// The fourth bit we send is the "double width toggle bit"
unsigned int t = (i == (RC6_TOGGLE_BIT_INDEX + 1)) ? (RC6_UNIT * 2) : (RC6_UNIT);
if (aRawData & mask) {
mark(t);
space(t);
} else {
space(t);
mark(t);
}
}
}
/**
* Assemble raw data for RC6 from parameters and toggle state and send
* We do not wait for the minimal trailing space of 2666 us
* @param aEnableAutomaticToggle Send toggle bit according to the state of the static sLastSendToggleValue variable.
*/
void IRsend::sendRC6(uint8_t aAddress, uint8_t aCommand, int_fast8_t aNumberOfRepeats, bool aEnableAutomaticToggle) {
LongUnion tIRRawData;
tIRRawData.UByte.LowByte = aCommand;
tIRRawData.UByte.MidLowByte = aAddress;
tIRRawData.UWord.HighWord = 0; // must clear high word
if (aEnableAutomaticToggle) {
if (sLastSendToggleValue == 0) {
sLastSendToggleValue = 1;
// set toggled bit
IR_DEBUG_PRINT(F("Set Toggle "));
tIRRawData.UByte.MidHighByte = 1; // 3 Mode bits are 0
} else {
sLastSendToggleValue = 0;
}
}
#if defined(LOCAL_DEBUG)
Serial.print(F("RC6: "));
Serial.print(F("sLastSendToggleValue="));
Serial.print (sLastSendToggleValue);
Serial.print(F(" RawData="));
Serial.println(tIRRawData.ULong, HEX);
#endif
uint_fast8_t tNumberOfCommands = aNumberOfRepeats + 1;
while (tNumberOfCommands > 0) {
// start and leading bits are sent by sendRC6
sendRC6Raw(tIRRawData.ULong, RC6_BITS - 1); // -1 since the leading bit is additionally sent by sendRC6
tNumberOfCommands--;
// skip last delay!
if (tNumberOfCommands > 0) {
// send repeated command in a fixed raster
delay(RC6_REPEAT_DISTANCE / MICROS_IN_ONE_MILLI);
}
}
}
/**
* Assemble raw data for RC6 from parameters and toggle state and send
* We do not wait for the minimal trailing space of 2666 us
* @param aEnableAutomaticToggle Send toggle bit according to the state of the static sLastSendToggleValue variable.
*/
void IRsend::sendRC6A(uint8_t aAddress, uint8_t aCommand, int_fast8_t aNumberOfRepeats, uint16_t aCustomer,
bool aEnableAutomaticToggle) {
LongUnion tIRRawData;
tIRRawData.UByte.LowByte = aCommand;
tIRRawData.UByte.MidLowByte = aAddress;
tIRRawData.UWord.HighWord = aCustomer | 0x400; // bit 31 is always 1
if (aEnableAutomaticToggle) {
if (sLastSendToggleValue == 0) {
sLastSendToggleValue = 1;
// set toggled bit
IR_DEBUG_PRINT(F("Set Toggle "));
tIRRawData.UByte.HighByte |= 0x80; // toggle bit is bit 32
} else {
sLastSendToggleValue = 0;
}
}
// Set mode bits
uint64_t tRawData = tIRRawData.ULong + 0x0600000000;
#if defined(LOCAL_DEBUG)
Serial.print(F("RC6A: "));
Serial.print(F("sLastSendToggleValue="));
Serial.print (sLastSendToggleValue);
Serial.print(F(" RawData="));
Serial.println(tIRRawData.ULong, HEX);
#endif
uint_fast8_t tNumberOfCommands = aNumberOfRepeats + 1;
while (tNumberOfCommands > 0) {
// start and leading bits are sent by sendRC6
sendRC6Raw(tRawData, RC6A_BITS - 1); // -1 since the leading bit is additionally sent by sendRC6
tNumberOfCommands--;
// skip last delay!
if (tNumberOfCommands > 0) {
// send repeated command in a fixed raster
delay(RC6_REPEAT_DISTANCE / MICROS_IN_ONE_MILLI);
}
}
}
/**
* Try to decode data as RC6 protocol
*/
bool IRrecv::decodeRC6() {
uint8_t tBitIndex;
uint32_t tDecodedRawData = 0;
// Check we have the right amount of data (). The +3 for initial gap, start bit mark and space
if (decodedIRData.rawlen < MIN_RC6_MARKS + 3 && (RC6_BITS + 3) < decodedIRData.rawlen) {
IR_DEBUG_PRINT(F("RC6: "));
IR_DEBUG_PRINT(F("Data length="));
IR_DEBUG_PRINT(decodedIRData.rawlen);
IR_DEBUG_PRINTLN(F(" is not between 15 and 25"));
return false;
}
// Check header "mark" and "space", this must be done for repeat and data
if (!matchMark(decodedIRData.rawDataPtr->rawbuf[1], RC6_HEADER_MARK)
|| !matchSpace(decodedIRData.rawDataPtr->rawbuf[2], RC6_HEADER_SPACE)) {
// no debug output, since this check is mainly to determine the received protocol
IR_DEBUG_PRINT(F("RC6: "));
IR_DEBUG_PRINTLN(F("Header mark or space length is wrong"));
return false;
}
// Set Biphase decoding start values
initBiphaselevel(3, RC6_UNIT); // Skip gap-space and start-bit mark + space
// Process first bit, which is known to be a 1 (mark->space)
if (getBiphaselevel() != MARK) {
IR_DEBUG_PRINT(F("RC6: "));
IR_DEBUG_PRINTLN(F("first getBiphaselevel() is not MARK"));
return false;
}
if (getBiphaselevel() != SPACE) {
IR_DEBUG_PRINT(F("RC6: "));
IR_DEBUG_PRINTLN(F("second getBiphaselevel() is not SPACE"));
return false;
}
for (tBitIndex = 0; sBiphaseDecodeRawbuffOffset < decodedIRData.rawlen; tBitIndex++) {
uint8_t tStartLevel; // start level of coded bit
uint8_t tEndLevel; // end level of coded bit
tStartLevel = getBiphaselevel();
if (tBitIndex == RC6_TOGGLE_BIT_INDEX) {
// Toggle bit is double wide; make sure second half is equal first half
if (tStartLevel != getBiphaselevel()) {
#if defined(LOCAL_DEBUG)
Serial.print(F("RC6: "));
Serial.println(F("Toggle mark or space length is wrong"));
#endif
return false;
}
}
tEndLevel = getBiphaselevel();
if (tBitIndex == RC6_TOGGLE_BIT_INDEX) {
// Toggle bit is double wide; make sure second half matches
if (tEndLevel != getBiphaselevel()) {
#if defined(LOCAL_DEBUG)
Serial.print(F("RC6: "));
Serial.println(F("Toggle mark or space length is wrong"));
#endif
return false;
}
}
/*
* Determine tDecodedRawData bit value by checking the transition type
*/
if ((tStartLevel == MARK) && (tEndLevel == SPACE)) {
// we have a mark to space transition here
tDecodedRawData = (tDecodedRawData << 1) | 1; // inverted compared to RC5
} else if ((tStartLevel == SPACE) && (tEndLevel == MARK)) {
// we have a space to mark transition here
tDecodedRawData = (tDecodedRawData << 1) | 0;
} else {
#if defined(LOCAL_DEBUG)
Serial.print(F("RC6: "));
Serial.println(F("Decode failed"));
#endif
// we have no transition here or one level is -1 -> error
return false; // Error
}
}
// Success
decodedIRData.numberOfBits = tBitIndex;
LongUnion tValue;
tValue.ULong = tDecodedRawData;
decodedIRData.decodedRawData = tDecodedRawData;
if (tBitIndex < 35) {
// RC6 8 address bits, 8 command bits
decodedIRData.flags = IRDATA_FLAGS_IS_MSB_FIRST;
decodedIRData.command = tValue.UByte.LowByte;
decodedIRData.address = tValue.UByte.MidLowByte;
// Check for toggle flag
if ((tValue.UByte.MidHighByte & 1) != 0) {
decodedIRData.flags = IRDATA_FLAGS_TOGGLE_BIT | IRDATA_FLAGS_IS_MSB_FIRST;
}
if (tBitIndex > 20) {
decodedIRData.flags |= IRDATA_FLAGS_EXTRA_INFO;
}
decodedIRData.protocol = RC6;
} else {
// RC6A
decodedIRData.flags = IRDATA_FLAGS_IS_MSB_FIRST | IRDATA_FLAGS_EXTRA_INFO;
decodedIRData.command = tValue.UByte.LowByte;
decodedIRData.address = tValue.UByte.MidLowByte;
decodedIRData.extra = tValue.UWord.HighWord & 0x3FFF; // Mask to 14 bits, remove toggle and constant 1
if ((tValue.UByte.HighByte & 0x80) != 0) {
decodedIRData.flags |= IRDATA_FLAGS_TOGGLE_BIT;
}
decodedIRData.protocol = RC6A;
}
// check for repeat, do not check toggle bit yet
checkForRepeatSpaceTicksAndSetFlag(RC6_MAXIMUM_REPEAT_DISTANCE / MICROS_PER_TICK);
return true;
}
/*********************************************************************************
* Old deprecated functions, kept for backward compatibility to old 2.0 tutorials
*********************************************************************************/
/**
* Old version with 32 bit data
*/
void IRsend::sendRC5(uint32_t data, uint8_t nbits) {
// Set IR carrier frequency
enableIROut (RC5_RC6_KHZ);
// Start
mark(RC5_UNIT);
space(RC5_UNIT);
mark(RC5_UNIT);
// Data - Biphase code MSB first
for (uint32_t mask = 1UL << (nbits - 1); mask; mask >>= 1) {
if (data & mask) {
space(RC5_UNIT); // 1 is space, then mark
mark(RC5_UNIT);
} else {
mark(RC5_UNIT);
space(RC5_UNIT);
}
}
}
/*
* Not longer required, use sendRC5(uint8_t aAddress, uint8_t aCommand, int_fast8_t aNumberOfRepeats, bool aEnableAutomaticToggle) instead
*/
void IRsend::sendRC5ext(uint8_t addr, uint8_t cmd, bool toggle) {
// Set IR carrier frequency
enableIROut (RC5_RC6_KHZ);
uint8_t addressBits = 5;
uint8_t commandBits = 7;
// unsigned long nbits = addressBits + commandBits;
// Start
mark(RC5_UNIT);
// Bit #6 of the command part, but inverted!
uint8_t cmdBit6 = (1UL << (commandBits - 1)) & cmd;
if (cmdBit6) {
// Inverted (1 -> 0 = mark-to-space)
mark(RC5_UNIT);
space(RC5_UNIT);
} else {
space(RC5_UNIT);
mark(RC5_UNIT);
}
commandBits--;
// Toggle bit
static int toggleBit = 1;
if (toggle) {
if (toggleBit == 0) {
toggleBit = 1;
} else {
toggleBit = 0;
}
}
if (toggleBit) {
space(RC5_UNIT);
mark(RC5_UNIT);
} else {
mark(RC5_UNIT);
space(RC5_UNIT);
}
// Address
for (uint_fast8_t mask = 1UL << (addressBits - 1); mask; mask >>= 1) {
if (addr & mask) {
space(RC5_UNIT); // 1 is space, then mark
mark(RC5_UNIT);
} else {
mark(RC5_UNIT);
space(RC5_UNIT);
}
}
// Command
for (uint_fast8_t mask = 1UL << (commandBits - 1); mask; mask >>= 1) {
if (cmd & mask) {
space(RC5_UNIT); // 1 is space, then mark
mark(RC5_UNIT);
} else {
mark(RC5_UNIT);
space(RC5_UNIT);
}
}
}
/** @}*/
#if defined(LOCAL_DEBUG)
#undef LOCAL_DEBUG
#endif
#endif // _IR_RC5_RC6_HPP
+429
View File
@@ -0,0 +1,429 @@
/*
* ir_Samsung.hpp
*
* Contains functions for receiving and sending Samsung IR Protocol in "raw" and standard format with 16 bit address and 16 or 32 bit command
*
* This file is part of Arduino-IRremote https://github.com/Arduino-IRremote/Arduino-IRremote.
*
************************************************************************************
* MIT License
*
* Copyright (c) 2017-2024 Darryl Smith, 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 CONSAMSUNGTION WITH THE SOFTWARE
* OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
************************************************************************************
*/
#ifndef _IR_SAMSUNG_HPP
#define _IR_SAMSUNG_HPP
#if defined(DEBUG) && !defined(LOCAL_DEBUG)
#define LOCAL_DEBUG
#else
//#define LOCAL_DEBUG // This enables debug output only for this file
#endif
/** \addtogroup Decoder Decoders and encoders for different protocols
* @{
*/
//==============================================================================
// SSSS AAA MMM SSSS U U N N GGGG
// S A A M M M S U U NN N G
// SSS AAAAA M M M SSS U U N N N G GG
// S A A M M S U U N NN G G
// SSSS A A M M SSSS UUU N N GGG
//==============================================================================
/*
* Address=0xFFF1 Command=0x76 Raw-Data=0x8976FFF1
+4500,-4400
+ 600,-1600 + 650,- 500 + 600,- 500 + 650,- 500
+ 600,-1650 + 600,-1600 + 650,-1600 + 600,-1650
+ 600,-1600 + 600,-1650 + 600,-1650 + 600,-1600
+ 600,-1650 + 600,-1650 + 600,-1600 + 600,-1650
+ 600,- 500 + 650,-1600 + 600,-1650 + 600,- 500
+ 650,-1600 + 600,-1650 + 600,-1650 + 600,- 500
+ 600,-1650 + 600,- 500 + 600,- 550 + 600,-1600
+ 600,- 550 + 600,- 550 + 550,- 550 + 600,-1650
+ 550
Sum: 68750
*/
/*
* Samsung repeat frame can be the original frame again or a special short repeat frame,
* then we call the protocol SamsungLG. They differ only in the handling of repeat,
* so we can not decide for the first frame which protocol is used.
*/
// see http://www.hifi-remote.com/wiki/index.php?title=DecodeIR#Samsung
// https://www.mikrocontroller.net/articles/IRMP_-_english#SAMSUNG32
// https://www.mikrocontroller.net/articles/IRMP_-_english#SAMSUNG48
// LSB first, 1 start bit + 16 bit address + 16 or 32 bit data + 1 stop bit.
// Here https://forum.arduino.cc/t/klimaanlage-per-ir-steuern/1051381/10 the address (0xB24D) is also 8 bits and then 8 inverted bits
//
// Here https://github.com/flipperdevices/flipperzero-firmware/blob/master/lib/infrared/encoder_decoder/samsung/infrared_decoder_samsung.c#L18
// Address is 8 bit + same 8 bit if command is 8 bit and ~8 bit.
//
// So we assume 8 bit address, if command is 8 bit and ~8 bit!
//
// IRP notation: {38k,5553}<1,-1|1,-3>(8,-8,D:8,S:8,F:8,~F:8,1,^110)+ ==> 8 bit + 8 bit inverted data - Samsung32
// IRP notation: {38k,5553}<1,-1|1,-3>(8,-8,D:8,S:8,F:16,1,^110)+ ==> 16 bit data - still Samsung32
// IRP notation: {38k,5553}<1,-1|1,-3>(8,-8,D:8,S:8,F:8,~F:8,G:8,~G:8,1,^110)+ ==> 2 x (8 bit + 8 bit inverted data) - Samsung48
//
#define SAMSUNG_ADDRESS_BITS 16
#define SAMSUNG_COMMAND16_BITS 16
#define SAMSUNG_COMMAND32_BITS 32
#define SAMSUNG_BITS (SAMSUNG_ADDRESS_BITS + SAMSUNG_COMMAND16_BITS)
#define SAMSUNG48_BITS (SAMSUNG_ADDRESS_BITS + SAMSUNG_COMMAND32_BITS)
// except SAMSUNG_HEADER_MARK, values are like NEC
#define SAMSUNG_UNIT 560 // 21.28 periods of 38 kHz, 11.2 ticks TICKS_LOW = 8.358 TICKS_HIGH = 15.0
#define SAMSUNG_HEADER_MARK (8 * SAMSUNG_UNIT) // 4500 | 180 periods
#define SAMSUNG_HEADER_SPACE (8 * SAMSUNG_UNIT) // 4500
#define SAMSUNG_BIT_MARK SAMSUNG_UNIT
#define SAMSUNG_ONE_SPACE (3 * SAMSUNG_UNIT) // 1690 | 33.8 TICKS_LOW = 25.07 TICKS_HIGH = 45.0
#define SAMSUNG_ZERO_SPACE SAMSUNG_UNIT
#define SAMSUNG_AVERAGE_DURATION 55000 // SAMSUNG_HEADER_MARK + SAMSUNG_HEADER_SPACE + 32 * 2,5 * SAMSUNG_UNIT + SAMSUNG_UNIT // 2.5 because we assume more zeros than ones
#define SAMSUNG_REPEAT_DURATION (SAMSUNG_HEADER_MARK + SAMSUNG_HEADER_SPACE + SAMSUNG_BIT_MARK + SAMSUNG_ZERO_SPACE + SAMSUNG_BIT_MARK)
#define SAMSUNG_REPEAT_PERIOD 110000 // Commands are repeated every 110 ms (measured from start to start) for as long as the key on the remote control is held down.
#define SAMSUNG_MAXIMUM_REPEAT_DISTANCE (SAMSUNG_REPEAT_PERIOD + (SAMSUNG_REPEAT_PERIOD / 4)) // 137000 - Just a guess
// 19 byte RAM
struct PulseDistanceWidthProtocolConstants SamsungProtocolConstants = { SAMSUNG, SAMSUNG_KHZ, SAMSUNG_HEADER_MARK,
SAMSUNG_HEADER_SPACE, SAMSUNG_BIT_MARK, SAMSUNG_ONE_SPACE, SAMSUNG_BIT_MARK, SAMSUNG_ZERO_SPACE, PROTOCOL_IS_LSB_FIRST,
(SAMSUNG_REPEAT_PERIOD / MICROS_IN_ONE_MILLI), NULL };
struct PulseDistanceWidthProtocolConstants SamsungLGProtocolConstants = { SAMSUNGLG, SAMSUNG_KHZ, SAMSUNG_HEADER_MARK,
SAMSUNG_HEADER_SPACE, SAMSUNG_BIT_MARK, SAMSUNG_ONE_SPACE, SAMSUNG_BIT_MARK, SAMSUNG_ZERO_SPACE, PROTOCOL_IS_LSB_FIRST,
(SAMSUNG_REPEAT_PERIOD / MICROS_IN_ONE_MILLI), &sendSamsungLGSpecialRepeat };
/************************************
* Start of send and decode functions
************************************/
/**
* Send repeat
* Repeat commands should be sent in a 110 ms raster.
* This repeat was sent by an LG 6711R1P071A remote
*/
void IRsend::sendSamsungLGRepeat() {
enableIROut (SAMSUNG_KHZ); // 38 kHz
mark(SAMSUNG_HEADER_MARK); // + 4500
space(SAMSUNG_HEADER_SPACE); // - 4500
mark(SAMSUNG_BIT_MARK); // + 560
space(SAMSUNG_ZERO_SPACE); // - 560
mark(SAMSUNG_BIT_MARK); // + 560
}
/**
* Like above, but implemented as a static function
* Used for sending special repeat frame.
* For use in ProtocolConstants. Saves up to 250 bytes compared to a member function.
*/
void sendSamsungLGSpecialRepeat() {
IrSender.enableIROut(SAMSUNG_KHZ); // 38 kHz
IrSender.mark(SAMSUNG_HEADER_MARK); // + 4500
IrSender.space(SAMSUNG_HEADER_SPACE); // - 4500
IrSender.mark(SAMSUNG_BIT_MARK); // + 560
IrSender.space(SAMSUNG_ZERO_SPACE); // - 560
IrSender.mark(SAMSUNG_BIT_MARK); // + 560
}
/*
* Sent e.g. by an LG 6711R1P071A remote
* @param aNumberOfRepeats If < 0 then only a special repeat frame will be sent
*/
void IRsend::sendSamsungLG(uint16_t aAddress, uint16_t aCommand, int_fast8_t aNumberOfRepeats) {
if (aNumberOfRepeats < 0) {
sendSamsungLGRepeat();
return;
}
// send 16 bit address and 8 command bits and then 8 inverted command bits LSB first
LongUnion tRawData;
tRawData.UWord.LowWord = aAddress;
tRawData.UByte.MidHighByte = aCommand;
tRawData.UByte.HighByte = ~aCommand;
sendPulseDistanceWidth(&SamsungLGProtocolConstants, tRawData.ULong, SAMSUNG_BITS, aNumberOfRepeats);
}
/**
* Here we send Samsung32
* If we get a command < 0x100, we send command and then ~command
* If we get an address < 0x100, we send 8 bit address and then the same 8 bit address again, this makes it flipper IRDB compatible
* !!! Be aware, that this is flexible, but makes it impossible to send e.g. 0x0042 as 16 bit value!!!
* To force send 16 bit address, use: sendSamsung16BitAddressAndCommand().
* @param aNumberOfRepeats If < 0 then only a special repeat frame will be sent
*/
void IRsend::sendSamsung(uint16_t aAddress, uint16_t aCommand, int_fast8_t aNumberOfRepeats) {
LongUnion tSendValue;
if (aAddress < 0x100) {
// This makes it flipper IRDB compatible:
// https://github.com/flipperdevices/flipperzero-firmware/blob/master/lib/infrared/encoder_decoder/samsung/infrared_decoder_samsung.c#L18
// Duplicate address byte, if address is only 8 bit
tSendValue.UBytes[1] = aAddress;
tSendValue.UBytes[0] = aAddress;
} else {
tSendValue.UWords[0] = aAddress;
}
if (aCommand < 0x100) {
// Send 8 command bits and then 8 inverted command bits LSB first
tSendValue.UBytes[2] = aCommand;
tSendValue.UBytes[3] = ~aCommand;
} else {
// Send 16 command bits
tSendValue.UWords[1] = aCommand;
}
sendPulseDistanceWidth(&SamsungProtocolConstants, tSendValue.ULong, SAMSUNG_BITS, aNumberOfRepeats);
}
/**
* Maybe no one needs it in the wild...
* As above, but we are able to send e.g. 0x0042 as 16 bit address
* @param aNumberOfRepeats If < 0 then only a special repeat frame will be sent
*/
void IRsend::sendSamsung16BitAddressAnd8BitCommand(uint16_t aAddress, uint8_t aCommand, int_fast8_t aNumberOfRepeats) {
LongUnion tSendValue;
// send 16 bit address
tSendValue.UWords[0] = aAddress;
// Send 8 command bits and then 8 inverted command bits LSB first
tSendValue.UBytes[2] = aCommand;
tSendValue.UBytes[3] = ~aCommand;
sendPulseDistanceWidth(&SamsungProtocolConstants, tSendValue.ULong, SAMSUNG_BITS, aNumberOfRepeats);
}
/**
* Maybe no one needs it in the wild...
* As above, but we are able to send e.g. 0x0042 as 16 bit address
* @param aNumberOfRepeats If < 0 then only a special repeat frame will be sent
*/
void IRsend::sendSamsung16BitAddressAndCommand(uint16_t aAddress, uint16_t aCommand, int_fast8_t aNumberOfRepeats) {
LongUnion tSendValue;
// send 16 bit address
tSendValue.UWords[0] = aAddress;
// Send 16 command bits
tSendValue.UWords[1] = aCommand;
sendPulseDistanceWidth(&SamsungProtocolConstants, tSendValue.ULong, SAMSUNG_BITS, aNumberOfRepeats);
}
/**
* Here we send Samsung48
* We send 2 x (8 bit command and then ~command)
*/
void IRsend::sendSamsung48(uint16_t aAddress, uint32_t aCommand, int_fast8_t aNumberOfRepeats) {
// send 16 bit address and 2 x ( 8 command bits and then 8 inverted command bits) LSB first
#if __INT_WIDTH__ < 32
uint32_t tRawSamsungData[2]; // prepare 2 long for Samsung48
LongUnion tSendValue;
tSendValue.UWords[0] = aAddress;
tSendValue.UBytes[2] = aCommand;
tSendValue.UBytes[3] = ~aCommand;
uint8_t tUpper8BitsOfCommand = aCommand >> 8;
tRawSamsungData[1] = tUpper8BitsOfCommand | (~tUpper8BitsOfCommand) << 8;
tRawSamsungData[0] = tSendValue.ULong;
sendPulseDistanceWidthFromArray(&SamsungProtocolConstants, &tRawSamsungData[0], SAMSUNG48_BITS, aNumberOfRepeats);
#else
LongLongUnion tSendValue;
tSendValue.UWords[0] = aAddress;
if (aCommand < 0x10000) {
tSendValue.UBytes[2] = aCommand;
tSendValue.UBytes[3] = ~aCommand;
uint8_t tUpper8BitsOfCommand = aCommand >> 8;
tSendValue.UBytes[4] = tUpper8BitsOfCommand;
tSendValue.UBytes[5] = ~tUpper8BitsOfCommand;
} else {
tSendValue.ULongLong = aAddress | aCommand << 16;
}
sendPulseDistanceWidth(&SamsungProtocolConstants, tSendValue.ULongLong, SAMSUNG48_BITS, aNumberOfRepeats);
#endif
}
/*
* We cannot decode frames with 8 command bits and then 8 inverted command bits LSB and 16 bit address,
* because in this case we always assume 8 bit address.
* This is, because we did not see 8 bit command and real 16 bit address in the wild.
*/
bool IRrecv::decodeSamsung() {
// Check we have enough data (68). The +4 is for initial gap, start bit mark and space + stop bit mark
if (decodedIRData.rawlen != ((2 * SAMSUNG_BITS) + 4) && decodedIRData.rawlen != ((2 * SAMSUNG48_BITS) + 4)
&& (decodedIRData.rawlen != 6)) {
IR_DEBUG_PRINT(F("Samsung: "));
IR_DEBUG_PRINT(F("Data length="));
IR_DEBUG_PRINT(decodedIRData.rawlen);
IR_DEBUG_PRINTLN(F(" is not 6 or 68 or 100"));
return false;
}
if (!checkHeader(&SamsungProtocolConstants)) {
return false;
}
// Check for SansungLG style repeat
if (decodedIRData.rawlen == 6) {
decodedIRData.flags = IRDATA_FLAGS_IS_REPEAT | IRDATA_FLAGS_IS_PROTOCOL_WITH_DIFFERENT_REPEAT | IRDATA_FLAGS_IS_LSB_FIRST;
decodedIRData.address = lastDecodedAddress;
decodedIRData.command = lastDecodedCommand;
decodedIRData.protocol = SAMSUNGLG;
return true;
}
/*
* Decode first 32 bits
*/
if (!decodePulseDistanceWidthData(&SamsungProtocolConstants, SAMSUNG_BITS, 3)) {
#if defined(LOCAL_DEBUG)
Serial.print(F("Samsung: "));
Serial.println(F("Decode failed"));
#endif
return false;
}
LongUnion tValue;
tValue.ULong = decodedIRData.decodedRawData;
decodedIRData.address = tValue.UWord.LowWord;
if (decodedIRData.rawlen == (2 * SAMSUNG48_BITS) + 4) {
/*
* Samsung48
*/
// decode additional 16 bit
if (!decodePulseDistanceWidthData(&SamsungProtocolConstants, (SAMSUNG_COMMAND32_BITS - SAMSUNG_COMMAND16_BITS),
3 + (2 * SAMSUNG_BITS))) {
#if defined(LOCAL_DEBUG)
Serial.print(F("Samsung: "));
Serial.println(F("Decode failed"));
#endif
return false;
}
/*
* LSB data is already in tValue.UWord.HighWord!
* Put latest (MSB) bits in LowWord, LSB first would have them expect in HighWord so keep this in mind for decoding below
*/
tValue.UWord.LowWord = decodedIRData.decodedRawData; // We have only 16 bit in decodedRawData here
#if __INT_WIDTH__ >= 32
// workaround until complete refactoring for 64 bit
decodedIRData.decodedRawData = (decodedIRData.decodedRawData << 32)| tValue.UWord.HighWord << 16 | decodedIRData.address; // store all 48 bits in decodedRawData
#endif
/*
* Check parity of 32 bit command
*/
// receive 2 * (8 bits then 8 inverted bits) LSB first
if (tValue.UByte.MidHighByte != (uint8_t)(~tValue.UByte.HighByte)
&& tValue.UByte.LowByte != (uint8_t)(~tValue.UByte.MidLowByte)) {
decodedIRData.flags = IRDATA_FLAGS_PARITY_FAILED | IRDATA_FLAGS_IS_LSB_FIRST;
}
decodedIRData.command = tValue.UByte.LowByte << 8 | tValue.UByte.MidHighByte; // low and high word are swapped here, so fetch it this way
decodedIRData.numberOfBits = SAMSUNG48_BITS;
decodedIRData.protocol = SAMSUNG48;
} else {
/*
* Samsung32
*/
if (tValue.UByte.MidHighByte == (uint8_t)(~tValue.UByte.HighByte)) {
// 8 bit command protocol -> assume 8 bit address
decodedIRData.command = tValue.UByte.MidHighByte; // first 8 bit
decodedIRData.address = tValue.UByte.LowByte; // assume LowByte == MidLowByte
} else {
// 16 bit command protocol, address is filled above with the 16 bit value
decodedIRData.command = tValue.UWord.HighWord; // first 16 bit
}
decodedIRData.numberOfBits = SAMSUNG_BITS;
decodedIRData.protocol = SAMSUNG;
}
// check for repeat
checkForRepeatSpaceTicksAndSetFlag(SAMSUNG_MAXIMUM_REPEAT_DISTANCE / MICROS_PER_TICK);
return true;
}
// Old version with MSB first
bool IRrecv::decodeSAMSUNG(decode_results *aResults) {
unsigned int offset = 1; // Skip first space
// Initial mark
if (!matchMark(aResults->rawbuf[offset], SAMSUNG_HEADER_MARK)) {
return false;
}
offset++;
// Check for repeat -- like a NEC repeat
if ((aResults->rawlen == 4) && matchSpace(aResults->rawbuf[offset], 2250)
&& matchMark(aResults->rawbuf[offset + 1], SAMSUNG_BIT_MARK)) {
aResults->bits = 0;
aResults->value = 0xFFFFFFFF;
decodedIRData.flags = IRDATA_FLAGS_IS_REPEAT;
decodedIRData.protocol = SAMSUNG;
return true;
}
if (aResults->rawlen < (2 * SAMSUNG_BITS) + 4) {
return false;
}
// Initial space
if (!matchSpace(aResults->rawbuf[offset], SAMSUNG_HEADER_SPACE)) {
return false;
}
offset++;
if (!decodePulseDistanceWidthData(SAMSUNG_BITS, offset, SAMSUNG_BIT_MARK, SAMSUNG_ONE_SPACE, 0, PROTOCOL_IS_MSB_FIRST)) {
return false;
}
// Success
aResults->value = decodedIRData.decodedRawData;
aResults->bits = SAMSUNG_BITS;
aResults->decode_type = SAMSUNG;
decodedIRData.protocol = SAMSUNG;
return true;
}
// Old version with MSB first
void IRsend::sendSamsungMSB(unsigned long data, int nbits) {
// Set IR carrier frequency
enableIROut (SAMSUNG_KHZ);
// Header
mark(SAMSUNG_HEADER_MARK);
space(SAMSUNG_HEADER_SPACE);
// Old version with MSB first Data + stop bit
sendPulseDistanceWidthData(SAMSUNG_BIT_MARK, SAMSUNG_ONE_SPACE, SAMSUNG_BIT_MARK, SAMSUNG_ZERO_SPACE, data, nbits,
PROTOCOL_IS_MSB_FIRST);
}
void IRsend::sendSAMSUNG(unsigned long data, int nbits) {
sendSamsungMSB(data, nbits);
}
/** @}*/
#if defined(LOCAL_DEBUG)
#undef LOCAL_DEBUG
#endif
#endif // _IR_SAMSUNG_HPP
+235
View File
@@ -0,0 +1,235 @@
/*
* ir_Sony.hpp
*
* Contains functions for receiving and sending SIRCS/Sony IR Protocol in "raw" and standard format with 5 bit address 7 bit command
*
* This file is part of Arduino-IRremote https://github.com/Arduino-IRremote/Arduino-IRremote.
*
************************************************************************************
* MIT License
*
* 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_SONY_HPP
#define _IR_SONY_HPP
#if defined(DEBUG) && !defined(LOCAL_DEBUG)
#define LOCAL_DEBUG
#else
//#define LOCAL_DEBUG // This enables debug output only for this file
#endif
/** \addtogroup Decoder Decoders and encoders for different protocols
* @{
*/
//==============================================================================
// SSSS OOO N N Y Y
// S O O NN N Y Y
// SSS O O N N N Y
// S O O N NN Y
// SSSS OOO N N Y
//==============================================================================
/*
* Protocol=Sony Address=0x4B9 Command=0x7 Raw-Data=0x25C87 20 bits LSB first
+2550,- 400
// 7 command bits
+1300,- 450 +1350,- 450 +1300,- 450 + 700,- 450
+ 700,- 450 + 750,- 450 + 700,- 400
// (5,8,) 13 address bits and NO stop bit!
+1300,- 500
+ 700,- 450 + 700,- 450 +1300,- 500 +1300,- 450
+1300,- 450 + 700,- 450 +1350,- 400 + 750,- 450
+ 700,- 450 +1300,- 450 + 700,- 450 + 700
Sum: 31100
*/
/*
* Sony is the only protocol using the pulse width encoding, which requires no stop bit
*/
// see https://www.sbprojects.net/knowledge/ir/sirc.php
// https://www.mikrocontroller.net/articles/IRMP_-_english#SIRCS
// Frames are repeated every 45ms (measured from start to start) for as long as the key on the remote control is held down.
// This leads to a 15 ms gap for a Sony20 protocol!
// Here http://picprojects.org.uk/projects/sirc/ it is claimed, that many Sony remotes send each frame a minimum of 3 times. But 1 repeat (2 sends) has also been seen in real life.
// LSB first, start bit + 7 command + 5 to 13 address, no stop bit
// IRP: Sony12 {40k,600}<1,-1|2,-1>(4,-1,F:7,D:5,^45m)+ ==> 40 kHz, Unit is 600, LSB, One mark is 2 units, Start bit is 4 units, 7 bit Function, 5 bit Device, no Stop bit, every 45 milliseconds
// IRP: Sony15 {40k,600}<1,-1|2,-1>(4,-1,F:7,D:8,^45m)+ ==> 8 bit Device
// IRP: Sony20 {40k,600}<1,-1|2,-1>(4,-1,F:7,D:5,S:8,^45m)+ ==> 5 bit Device, 8 bit Subdevice
//
#define SONY_ADDRESS_BITS 5
#define SONY_COMMAND_BITS 7
#define SONY_EXTRA_BITS 8
#define SONY_BITS_MIN (SONY_COMMAND_BITS + SONY_ADDRESS_BITS) // 12 bits
#define SONY_BITS_15 (SONY_COMMAND_BITS + SONY_ADDRESS_BITS + 3) // 15 bits
#define SONY_BITS_MAX (SONY_COMMAND_BITS + SONY_ADDRESS_BITS + SONY_EXTRA_BITS) // 20 bits == SIRCS_20_PROTOCOL
#define SONY_UNIT 600 // 24 periods of 40kHz
#define SONY_HEADER_MARK (4 * SONY_UNIT) // 2400
#define SONY_ONE_MARK (2 * SONY_UNIT) // 1200
#define SONY_ZERO_MARK SONY_UNIT
#define SONY_SPACE SONY_UNIT
#define SONY_AVERAGE_DURATION_MIN 21000 // SONY_HEADER_MARK + SONY_SPACE + 12 * 2,5 * SONY_UNIT // 2.5 because we assume more zeros than ones
#define SONY_AVERAGE_DURATION_MAX 33000 // SONY_HEADER_MARK + SONY_SPACE + 20 * 2,5 * SONY_UNIT // 2.5 because we assume more zeros than ones
#define SONY_REPEAT_PERIOD 45000 // Commands are repeated every 45 ms (measured from start to start) for as long as the key on the remote control is held down.
#define SONY_MAXIMUM_REPEAT_DISTANCE (SONY_REPEAT_PERIOD - SONY_AVERAGE_DURATION_MIN) // 24 ms
struct PulseDistanceWidthProtocolConstants SonyProtocolConstants = { SONY, SONY_KHZ, SONY_HEADER_MARK, SONY_SPACE, SONY_ONE_MARK,
SONY_SPACE, SONY_ZERO_MARK, SONY_SPACE, PROTOCOL_IS_LSB_FIRST, (SONY_REPEAT_PERIOD / MICROS_IN_ONE_MILLI), NULL };
/************************************
* Start of send and decode functions
************************************/
/**
* @param numberOfBits should be one of SIRCS_12_PROTOCOL, SIRCS_15_PROTOCOL, SIRCS_20_PROTOCOL. Not checked! 20 -> send 13 address bits
*/
void IRsend::sendSony(uint16_t aAddress, uint8_t aCommand, int_fast8_t aNumberOfRepeats, uint8_t numberOfBits) {
uint32_t tData = (uint32_t) aAddress << 7 | (aCommand & 0x7F);
// send 5, 8, 13 address bits LSB first
sendPulseDistanceWidth(&SonyProtocolConstants, tData, numberOfBits, aNumberOfRepeats);
}
bool IRrecv::decodeSony() {
if (!checkHeader(&SonyProtocolConstants)) {
return false;
}
// Check we have enough data. +2 for initial gap and start bit mark and space minus the last/MSB space. NO stop bit! 26, 32, 42
if (decodedIRData.rawlen != (2 * SONY_BITS_MIN) + 2 && decodedIRData.rawlen != (2 * SONY_BITS_MAX) + 2
&& decodedIRData.rawlen != (2 * SONY_BITS_15) + 2) {
IR_DEBUG_PRINT(F("Sony: "));
IR_DEBUG_PRINT(F("Data length="));
IR_DEBUG_PRINT(decodedIRData.rawlen);
IR_DEBUG_PRINTLN(F(" is not 12, 15 or 20"));
return false;
}
if (!decodePulseDistanceWidthData(&SonyProtocolConstants, (decodedIRData.rawlen - 1) / 2, 3)) {
#if defined(LOCAL_DEBUG)
Serial.print(F("Sony: "));
Serial.println(F("Decode failed"));
#endif
return false;
}
// Success
// decodedIRData.flags = IRDATA_FLAGS_IS_LSB_FIRST; // Not required, since this is the start value
decodedIRData.command = decodedIRData.decodedRawData & 0x7F; // first 7 bits
decodedIRData.address = decodedIRData.decodedRawData >> 7; // next 5 or 8 or 13 bits
decodedIRData.numberOfBits = (decodedIRData.rawlen - 1) / 2;
decodedIRData.protocol = SONY;
//Check for repeat
checkForRepeatSpaceTicksAndSetFlag(SONY_MAXIMUM_REPEAT_DISTANCE / MICROS_PER_TICK);
return true;
}
/*********************************************************************************
* Old deprecated functions, kept for backward compatibility to old 2.0 tutorials
*********************************************************************************/
/*
* Old version with MSB first data
*/
#define SONY_DOUBLE_SPACE_USECS 500 // usually see 713 - not using ticks as get number wrap around
bool IRrecv::decodeSonyMSB(decode_results *aResults) {
long data = 0;
uint8_t bits = 0;
unsigned int offset = 0; // Dont skip first space, check its size
if (aResults->rawlen < (2 * SONY_BITS_MIN) + 2) {
return false;
}
// Some Sony's deliver repeats fast after first
// unfortunately can't spot difference from of repeat from two fast clicks
if (aResults->rawbuf[0] < (SONY_DOUBLE_SPACE_USECS / MICROS_PER_TICK)) {
#if defined(LOCAL_DEBUG)
Serial.println(F("IR Gap found"));
#endif
aResults->bits = 0;
aResults->value = 0xFFFFFFFF;
decodedIRData.flags = IRDATA_FLAGS_IS_REPEAT;
decodedIRData.protocol = SONY;
return true;
}
offset++;
// Check header "mark"
if (!matchMark(aResults->rawbuf[offset], SONY_HEADER_MARK)) {
return false;
}
offset++;
// MSB first - Not compatible to standard, which says LSB first :-(
while (offset + 1 < aResults->rawlen) {
// First check for the constant space length, we do not have a space at the end of raw data
// we are lucky, since the start space is equal the data space.
if (!matchSpace(aResults->rawbuf[offset], SONY_SPACE)) {
return false;
}
offset++;
// bit value is determined by length of the mark
if (matchMark(aResults->rawbuf[offset], SONY_ONE_MARK)) {
data = (data << 1) | 1;
} else if (matchMark(aResults->rawbuf[offset], SONY_ZERO_MARK)) {
data = (data << 1) | 0;
} else {
return false;
}
offset++;
bits++;
}
aResults->bits = bits;
aResults->value = data;
aResults->decode_type = SONY;
decodedIRData.protocol = SONY;
return true;
}
/**
* Old version with MSB first data
*/
void IRsend::sendSonyMSB(unsigned long data, int nbits) {
// Set IR carrier frequency
enableIROut (SONY_KHZ);
// Header
mark(SONY_HEADER_MARK);
space(SONY_SPACE);
// Old version with MSB first Data
sendPulseDistanceWidthData(SONY_ONE_MARK, SONY_SPACE, SONY_ZERO_MARK, SONY_SPACE, data, nbits, PROTOCOL_IS_MSB_FIRST);
}
void IRsend::sendSony(unsigned long data, int nbits) {
sendSonyMSB(data, nbits);
}
/** @}*/
#if defined(LOCAL_DEBUG)
#undef LOCAL_DEBUG
#endif
#endif // _IR_SONY_HPP
+175
View File
@@ -0,0 +1,175 @@
/*
Assuming the protocol we are adding is for the (imaginary) manufacturer: Shuzu
Our fantasy protocol is a standard protocol, so we can use this standard
template without too much work. Some protocols are quite unique and will require
considerably more work in this file! It is way beyond the scope of this text to
explain how to reverse engineer "unusual" IR protocols. But, unless you own an
oscilloscope, the starting point is probably to use the ReceiveDump.ino sketch and
try to spot the pattern!
Before you start, make sure the IR library is working OK:
# Open up the Arduino IDE
# Load up the ReceiveDump.ino example sketch
# Run it
# Analyze your data to have an idea, what is the header timing, the bit timing, the address, the command and the checksum of your protocol.
Now we can start to add our new protocol...
1. Copy this file to : ir_<YourProtocolName>.hpp
2. Replace all occurrences of "SHUZU" with the name of your protocol.
3. Tweak the #defines to suit your protocol.
4. If you're lucky, tweaking the #defines will make the decode and send() function
work.
You have now written the code to support your new protocol!
To integrate it into the IRremote library, you must search for "BOSEWAVE"
and add your protocol in the same way as it is already done for BOSEWAVE.
You have to change the following files:
IRSend.hpp IRsend::write(IRData *aIRSendData + int_fast8_t aNumberOfRepeats)
IRProtocol.h Add it to decode_type_t
IRReceive.hpp IRrecv::decode() + printActiveIRProtocols(Print *aSerial) + getProtocolString(decode_type_t aProtocol)
IRremote.hpp At 3 occurrences of DECODE_XXX
IRremoteInt.h Add the declaration of the decode and send function
Now open the Arduino IDE, load up the ReceiveDump.ino sketch, and run it.
Hopefully it will compile and upload.
If it doesn't, you've done something wrong. Check your work and look carefully at the error messages.
If you get this far, I will assume you have successfully added your new protocol
At last, delete this giant instructional comment.
If you want us to include your work in the library so others may benefit
from your hard work, you have to extend the examples
IRremoteInfo, SmallReceiver, simple Receiver, SendDemo and UnitTest too
as well as the Readme.md
It is not an act, but required for completeness.
Thanks
The maintainer
*/
/*
* ir_Shuzu.hpp
*
* Contains functions for receiving and sending Shuzu IR Protocol ...
*
* Copyright (C) 2022 Shuzu Guru
* shuzu.guru@gmail.com
*
* This file is part of Arduino-IRremote https://github.com/Arduino-IRremote/Arduino-IRremote.
*
************************************************************************************
* MIT License
*
* Copyright (c) 2022 Unknown Contributor :-)
*
* 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_SHUZU_HPP
#define _IR_SHUZU_HPP
//==============================================================================
//
//
// S H U Z U
//
//
//==============================================================================
// see: https://www....
// LSB first, 1 start bit + 16 bit address + 8 bit command + 1 stop bit.
#define SHUZU_ADDRESS_BITS 16 // 16 bit address
#define SHUZU_COMMAND_BITS 8 // Command
#define SHUZU_BITS (SHUZU_ADDRESS_BITS + SHUZU_COMMAND_BITS) // The number of bits in the protocol
#define SHUZU_UNIT 560 // All timings are in microseconds
#define SHUZU_HEADER_MARK (16 * SHUZU_UNIT) // The length of the Header:Mark
#define SHUZU_HEADER_SPACE (8 * SHUZU_UNIT) // The length of the Header:Space
#define SHUZU_BIT_MARK SHUZU_UNIT // The length of a Bit:Mark
#define SHUZU_ONE_SPACE (3 * SHUZU_UNIT) // The length of a Bit:Space for 1's
#define SHUZU_ZERO_SPACE SHUZU_UNIT // The length of a Bit:Space for 0's
#define SHUZU_REPEAT_HEADER_SPACE (4 * SHUZU_UNIT) // 2250
#define SHUZU_REPEAT_PERIOD 110000 // From start to start
#define SHUZU_REPEAT_SPACE 45000 // SHUZU_REPEAT_PERIOD - default frame duration. Used for repeat detection.
#define SHUZU_OTHER 1234 // Other things you may need to define
// use BOSEWAVE, we have no SHUZU code
struct PulseDistanceWidthProtocolConstants ShuzuProtocolConstants = { BOSEWAVE, 38, SHUZU_HEADER_MARK, SHUZU_HEADER_SPACE,
SHUZU_BIT_MARK, SHUZU_ONE_SPACE, SHUZU_BIT_MARK, SHUZU_ZERO_SPACE, PROTOCOL_IS_LSB_FIRST, (SHUZU_REPEAT_PERIOD
/ MICROS_IN_ONE_MILLI), NULL };
/************************************
* Start of send and decode functions
************************************/
void IRsend::sendShuzu(uint16_t aAddress, uint8_t aCommand, int_fast8_t aNumberOfRepeats) {
sendPulseDistanceWidth(&ShuzuProtocolConstants, (uint32_t) aCommand << 8 | aCommand, SHUZU_BITS, aNumberOfRepeats);
}
bool IRrecv::decodeShuzu() {
/*
* First check for right data length
* Next check start bit / header
* Next try the decode
*/
// Check we have the right amount of data (28). The +4 is for initial gap, start bit mark and space + stop bit mark
if (decodedIRData.rawDataPtr->rawlen != (2 * SHUZU_BITS) + 4) {
// no debug output, since this check is mainly to determine the received protocol
return false;
}
// Check header
if (!checkHeader(&ShuzuProtocolConstants)) {
return false;
}
// Decode
if (!decodePulseDistanceData(&ShuzuProtocolConstants, SHUZU_BITS)) {
IR_DEBUG_PRINT(F("Shuzu: "));
IR_DEBUG_PRINTLN(F("Decode failed"));
return false;
}
// Success, interpret raw data
// decodedIRData.flags = IRDATA_FLAGS_IS_LSB_FIRST; // Not required, since this is the start value
decodedIRData.command = decodedIRData.decodedRawData >> SHUZU_ADDRESS_BITS; // upper 8 bits of LSB first value
decodedIRData.address = decodedIRData.decodedRawData & 0xFFFF; // lowest 16 bit of LSB first value
decodedIRData.numberOfBits = SHUZU_BITS;
decodedIRData.protocol = BOSEWAVE; // we have no SHUZU code
//Check for repeat
checkForRepeatSpaceAndSetFlag(SHUZU_REPEAT_SPACE / MICROS_IN_ONE_MILLI);
return true;
}
#endif // _IR_SHUZU_HPP
File diff suppressed because it is too large. Load diff