feat: 全量同步 254 个常用的 Arduino 扩展库文件

This commit is contained in:
yczpf2019
2026-01-24 16:05:38 +08:00
parent c665ba662b
commit 397b9a23a3
6878 changed files with 2732224 additions and 1 deletions

View File

@@ -0,0 +1,161 @@
/**
* @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
* @{
*/
#include "digitalWriteFast.h"
/**
* 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) {
pinMode(aFeedbackLEDPin, OUTPUT);
#if defined(LED_BUILTIN)
} else {
pinMode(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)
digitalWrite(FeedbackLEDControl.FeedbackLEDPin, LOW); // Turn user defined pin LED on
#else
digitalWrite(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)
digitalWrite(FeedbackLEDControl.FeedbackLEDPin, HIGH); // Turn user defined pin LED off
#else
digitalWrite(FeedbackLEDControl.FeedbackLEDPin, LOW); // Turn user defined pin LED off
#endif
#if defined(LED_BUILTIN) // use fast macros here
} 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

View File

@@ -0,0 +1,103 @@
/**
* @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-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.
*
************************************************************************************
*/
#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!
*/
typedef enum {
UNKNOWN = 0,
PULSE_DISTANCE,
PULSE_WIDTH,
DENON,
DISH,
JVC,
LG,
LG2,
NEC,
PANASONIC,
KASEIKYO,
KASEIKYO_JVC,
KASEIKYO_DENON,
KASEIKYO_SHARP,
KASEIKYO_MITSUBISHI,
RC5,
RC6,
SAMSUNG,
SHARP,
SONY,
ONKYO,
APPLE,
BOSEWAVE,
LEGO_PF,
MAGIQUEST,
WHYNTER,
} decode_type_t;
const __FlashStringHelper* getProtocolString(decode_type_t aProtocol);
#define PROTOCOL_IS_LSB_FIRST false
#define PROTOCOL_IS_MSB_FIRST true
/*
* Carrier frequencies for various protocols
*/
#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
/*
* Constants for some protocols
*/
#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
#define SIRCS_12_PROTOCOL 12
#define SIRCS_15_PROTOCOL 15
#define SIRCS_20_PROTOCOL 20
#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
#endif // _IR_PROTOCOL_H

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,645 @@
/*
* IRSend.hpp
*
* Contains common functions for sending
*
* This file is part of Arduino-IRremote https://github.com/Arduino-IRremote/Arduino-IRremote.
*
************************************************************************************
* MIT License
*
* Copyright (c) 2009-2022 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_SEND_HPP
#define _IR_SEND_HPP
/*
* This improves readability of code by avoiding a lot of #if defined clauses
*/
#if defined(IR_SEND_PIN)
#define sendPin IR_SEND_PIN
#endif
#include "digitalWriteFast.h"
/** \addtogroup Sending Sending IR data for multiple protocols
* @{
*/
// The sender instance
IRsend IrSender;
IRsend::IRsend() { // @suppress("Class members should be properly initialized")
#if !defined(IR_SEND_PIN)
sendPin = 0;
#endif
#if !defined(NO_LED_FEEDBACK_CODE)
setLEDFeedback(0, DO_NOT_ENABLE_LED_FEEDBACK);
#endif
}
#if defined(IR_SEND_PIN)
/**
* Simple start with defaults - LED feedback enabled! Used if IR_SEND_PIN is defined. Saves program memory.
*/
void IRsend::begin(){
# if !defined(NO_LED_FEEDBACK_CODE)
setLEDFeedback(USE_DEFAULT_FEEDBACK_LED_PIN, LED_FEEDBACK_ENABLED_FOR_SEND);
# endif
}
/**
* @param aFeedbackLEDPin if 0, then take board specific FEEDBACK_LED_ON() and FEEDBACK_LED_OFF() functions
*/
void IRsend::begin(bool aEnableLEDFeedback, uint_fast8_t aFeedbackLEDPin) {
#if !defined(NO_LED_FEEDBACK_CODE)
bool tEnableLEDFeedback = DO_NOT_ENABLE_LED_FEEDBACK;
if(aEnableLEDFeedback) {
tEnableLEDFeedback = LED_FEEDBACK_ENABLED_FOR_SEND;
}
setLEDFeedback(aFeedbackLEDPin, tEnableLEDFeedback);
#else
(void) aEnableLEDFeedback;
(void) aFeedbackLEDPin;
#endif
}
#else // defined(IR_SEND_PIN)
IRsend::IRsend(uint_fast8_t aSendPin) { // @suppress("Class members should be properly initialized")
sendPin = aSendPin;
# if !defined(NO_LED_FEEDBACK_CODE)
setLEDFeedback(0, DO_NOT_ENABLE_LED_FEEDBACK);
# endif
}
/**
* Initializes the send pin and enable LED feedback with board specific FEEDBACK_LED_ON() and FEEDBACK_LED_OFF() functions
* @param aSendPin The Arduino pin number, where a IR sender diode is connected.
*/
void IRsend::begin(uint_fast8_t aSendPin) {
sendPin = aSendPin;
# if !defined(NO_LED_FEEDBACK_CODE)
setLEDFeedback(USE_DEFAULT_FEEDBACK_LED_PIN, LED_FEEDBACK_ENABLED_FOR_SEND);
# endif
}
void IRsend::setSendPin(uint_fast8_t aSendPin) {
sendPin = aSendPin;
}
#endif // defined(IR_SEND_PIN)
/**
* Initializes the send and feedback pin
* @param aSendPin The Arduino pin number, where a IR sender diode is connected.
* @param aFeedbackLEDPin if 0, then take board specific FEEDBACK_LED_ON() and FEEDBACK_LED_OFF() functions
*/
void IRsend::begin(uint_fast8_t aSendPin, bool aEnableLEDFeedback, uint_fast8_t aFeedbackLEDPin) {
#if defined(IR_SEND_PIN)
(void) aSendPin; // for backwards compatibility
#else
sendPin = aSendPin;
#endif
#if !defined(NO_LED_FEEDBACK_CODE)
bool tEnableLEDFeedback = DO_NOT_ENABLE_LED_FEEDBACK;
if (aEnableLEDFeedback) {
tEnableLEDFeedback = LED_FEEDBACK_ENABLED_FOR_SEND;
}
setLEDFeedback(aFeedbackLEDPin, tEnableLEDFeedback);
#else
(void) aEnableLEDFeedback;
(void) aFeedbackLEDPin;
#endif
}
/**
* @param aIRSendData The values of protocol, address, command and repeat flag are taken for sending.
* @param aNumberOfRepeats Number of repeats to send after the initial data.
*/
size_t IRsend::write(IRData *aIRSendData, uint_fast8_t aNumberOfRepeats) {
auto tProtocol = aIRSendData->protocol;
auto tAddress = aIRSendData->address;
auto tCommand = aIRSendData->command;
bool tSendRepeat = (aIRSendData->flags & IRDATA_FLAGS_IS_REPEAT);
// switch (tProtocol) { // 26 bytes bigger than if, else if, else
// case NEC:
// sendNEC(tAddress, tCommand, aNumberOfRepeats, tSendRepeat);
// break;
// case SAMSUNG:
// sendSamsung(tAddress, tCommand, aNumberOfRepeats);
// break;
// case SONY:
// sendSony(tAddress, tCommand, aNumberOfRepeats, aIRSendData->numberOfBits);
// break;
// case PANASONIC:
// sendPanasonic(tAddress, tCommand, aNumberOfRepeats);
// break;
// case DENON:
// sendDenon(tAddress, tCommand, aNumberOfRepeats);
// break;
// case SHARP:
// sendSharp(tAddress, tCommand, aNumberOfRepeats);
// break;
// case JVC:
// sendJVC((uint8_t) tAddress, (uint8_t) tCommand, aNumberOfRepeats); // casts are required to specify the right function
// break;
// case RC5:
// sendRC5(tAddress, tCommand, aNumberOfRepeats, !tSendRepeat); // No toggle for repeats
// break;
// case RC6:
// // No toggle for repeats// sendRC6(tAddress, tCommand, aNumberOfRepeats, !tSendRepeat); // No toggle for repeats
// break;
// default:
// break;
// }
/*
* Order of protocols is in guessed relevance :-)
*/
if (tProtocol == NEC) {
sendNEC(tAddress, tCommand, aNumberOfRepeats, tSendRepeat);
} else if (tProtocol == SAMSUNG) {
sendSamsung(tAddress, tCommand, aNumberOfRepeats);
} else if (tProtocol == SONY) {
sendSony(tAddress, tCommand, aNumberOfRepeats, aIRSendData->numberOfBits);
} else if (tProtocol == PANASONIC) {
sendPanasonic(tAddress, tCommand, aNumberOfRepeats);
} else if (tProtocol == DENON) {
sendDenon(tAddress, tCommand, aNumberOfRepeats);
} else if (tProtocol == SHARP) {
sendSharp(tAddress, tCommand, aNumberOfRepeats);
} else if (tProtocol == LG) {
sendLG(tAddress, tCommand, aNumberOfRepeats, tSendRepeat);
} else if (tProtocol == JVC) {
sendJVC((uint8_t) tAddress, (uint8_t) tCommand, aNumberOfRepeats); // casts are required to specify the right function
} else if (tProtocol == RC5) {
sendRC5(tAddress, tCommand, aNumberOfRepeats, !tSendRepeat); // No toggle for repeats
} else if (tProtocol == RC6) {
sendRC6(tAddress, tCommand, aNumberOfRepeats, !tSendRepeat); // No toggle for repeats
} else if (tProtocol == KASEIKYO_JVC) {
sendKaseikyo_JVC(tAddress, tCommand, aNumberOfRepeats);
} else if (tProtocol == KASEIKYO_DENON) {
sendKaseikyo_Denon(tAddress, tCommand, aNumberOfRepeats);
} else if (tProtocol == KASEIKYO_SHARP) {
sendKaseikyo_Sharp(tAddress, tCommand, aNumberOfRepeats);
} else if (tProtocol == KASEIKYO_MITSUBISHI) {
sendKaseikyo_Mitsubishi(tAddress, tCommand, aNumberOfRepeats);
} else if (tProtocol == ONKYO) {
sendOnkyo(tAddress, tCommand, aNumberOfRepeats, tSendRepeat);
} else if (tProtocol == APPLE) {
sendApple(tAddress, tCommand, aNumberOfRepeats, tSendRepeat);
#if !defined(EXCLUDE_EXOTIC_PROTOCOLS)
} else if (tProtocol == BOSEWAVE) {
sendBoseWave(tCommand, aNumberOfRepeats);
} else if (tProtocol == LEGO_PF) {
sendLegoPowerFunctions(tAddress, tCommand, tCommand >> 4, tSendRepeat); // send 5 autorepeats
#endif
}
return 1;
}
/**
* Function using an 16 byte microsecond timing array for every purpose.
* Raw data starts with a Mark. No leading space as in received timing data!
*/
void IRsend::sendRaw(const uint16_t aBufferWithMicroseconds[], uint_fast16_t aLengthOfBuffer, uint_fast8_t aIRFrequencyKilohertz) {
// Set IR carrier frequency
enableIROut(aIRFrequencyKilohertz);
/*
* Raw data starts with a mark.
*/
for (uint_fast16_t i = 0; i < aLengthOfBuffer; i++) {
if (i & 1) {
// Odd
space(aBufferWithMicroseconds[i]);
} else {
mark(aBufferWithMicroseconds[i]);
}
}
IrReceiver.restartAfterSend();
}
/**
* New function using an 8 byte tick timing array to save program memory
* Raw data starts with a Mark. No leading space as in received timing data!
*/
void IRsend::sendRaw(const uint8_t aBufferWithTicks[], uint_fast16_t aLengthOfBuffer, uint_fast8_t aIRFrequencyKilohertz) {
// Set IR carrier frequency
enableIROut(aIRFrequencyKilohertz);
for (uint_fast16_t i = 0; i < aLengthOfBuffer; i++) {
if (i & 1) {
// Odd
space(aBufferWithTicks[i] * MICROS_PER_TICK);
} else {
mark(aBufferWithTicks[i] * MICROS_PER_TICK);
}
}
IRLedOff(); // Always end with the LED off
IrReceiver.restartAfterSend();
}
/**
* Function using an 16 byte microsecond timing array in FLASH for every purpose.
* Raw data starts with a Mark. No leading space as in received timing data!
*/
void IRsend::sendRaw_P(const uint16_t aBufferWithMicroseconds[], uint_fast16_t aLengthOfBuffer, uint_fast8_t aIRFrequencyKilohertz) {
#if !defined(__AVR__)
sendRaw(aBufferWithMicroseconds, aLengthOfBuffer, aIRFrequencyKilohertz); // Let the function work for non AVR platforms
#else
// Set IR carrier frequency
enableIROut(aIRFrequencyKilohertz);
/*
* Raw data starts with a mark
*/
for (uint_fast16_t i = 0; i < aLengthOfBuffer; i++) {
unsigned int duration = pgm_read_word(&aBufferWithMicroseconds[i]);
if (i & 1) {
// Odd
space(duration);
} else {
mark(duration);
}
}
IrReceiver.restartAfterSend();
#endif
}
/**
* New function using an 8 byte tick timing array in FLASH to save program memory
* Raw data starts with a Mark. No leading space as in received timing data!
*/
void IRsend::sendRaw_P(const uint8_t aBufferWithTicks[], uint_fast16_t aLengthOfBuffer, uint_fast8_t aIRFrequencyKilohertz) {
#if !defined(__AVR__)
sendRaw(aBufferWithTicks, aLengthOfBuffer, aIRFrequencyKilohertz); // Let the function work for non AVR platforms
#else
// Set IR carrier frequency
enableIROut(aIRFrequencyKilohertz);
for (uint_fast16_t i = 0; i < aLengthOfBuffer; i++) {
unsigned int duration = pgm_read_byte(&aBufferWithTicks[i]) * (unsigned int) MICROS_PER_TICK;
if (i & 1) {
// Odd
space(duration);
} else {
mark(duration);
}
}
IRLedOff(); // Always end with the LED off
IrReceiver.restartAfterSend();
#endif
}
/**
* Sends PulseDistance data
* The output always ends with a space
*/
void IRsend::sendPulseDistanceWidthData(unsigned int aOneMarkMicros, unsigned int aOneSpaceMicros, unsigned int aZeroMarkMicros,
unsigned int aZeroSpaceMicros, uint32_t aData, uint_fast8_t aNumberOfBits, bool aMSBfirst, bool aSendStopBit) {
if (aMSBfirst) { // Send the MSB first.
// send data from MSB to LSB until mask bit is shifted out
for (uint32_t tMask = 1UL << (aNumberOfBits - 1); tMask; tMask >>= 1) {
if (aData & tMask) {
IR_TRACE_PRINT('1');
mark(aOneMarkMicros);
space(aOneSpaceMicros);
} else {
IR_TRACE_PRINT('0');
mark(aZeroMarkMicros);
space(aZeroSpaceMicros);
}
}
} else { // Send the Least Significant Bit (LSB) first / MSB last.
for (uint_fast8_t bit = 0; bit < aNumberOfBits; bit++, aData >>= 1)
if (aData & 1) { // Send a 1
IR_TRACE_PRINT('1');
mark(aOneMarkMicros);
space(aOneSpaceMicros);
} else { // Send a 0
IR_TRACE_PRINT('0');
mark(aZeroMarkMicros);
space(aZeroSpaceMicros);
}
}
if (aSendStopBit) {
IR_TRACE_PRINT('S');
mark(aZeroMarkMicros); // seems like this is used for stop bits
}
IR_TRACE_PRINTLN(F(""));
}
/*
* Sends Biphase data MSB first
* Always send start bit, do not send the trailing space of the start bit
* 0 -> mark+space
* 1 -> space+mark
* The output always ends with a space
*/
void IRsend::sendBiphaseData(unsigned int aBiphaseTimeUnit, uint32_t aData, uint_fast8_t aNumberOfBits) {
// do not send the trailing space of the start bit
mark(aBiphaseTimeUnit);
IR_TRACE_PRINT('S');
uint_fast8_t tLastBitValue = 1; // Start bit is a 1
// Data - Biphase code MSB first
for (uint32_t tMask = 1UL << (aNumberOfBits - 1); tMask; tMask >>= 1) {
if (aData & tMask) {
IR_TRACE_PRINT('1');
space(aBiphaseTimeUnit);
mark(aBiphaseTimeUnit);
tLastBitValue = 1;
} else {
IR_TRACE_PRINT('0');
#if defined(SEND_PWM_BY_TIMER) || defined(USE_NO_SEND_PWM)
if (tLastBitValue) {
// Extend the current mark in order to generate a continuous signal without short breaks
delayMicroseconds(aBiphaseTimeUnit);
} else {
mark(aBiphaseTimeUnit);
}
#else
(void) tLastBitValue; // to avoid compiler warnings
mark(aBiphaseTimeUnit); // can not eventually delay here, we must call mark to generate the signal
#endif
space(aBiphaseTimeUnit);
tLastBitValue = 0;
}
}
IR_TRACE_PRINTLN(F(""));
}
/**
* Sends an IR mark for the specified number of microseconds.
* The mark output is modulated at the PWM frequency if USE_NO_SEND_PWM is not defined.
* The output is guaranteed to be OFF / inactive after after the call of the function.
* This function may affect the state of feedback LED.
*/
void IRsend::mark(unsigned int aMarkMicros) {
#if defined(SEND_PWM_BY_TIMER)
# if !defined(NO_LED_FEEDBACK_CODE)
if (FeedbackLEDControl.LedFeedbackEnabled == LED_FEEDBACK_ENABLED_FOR_SEND) {
setFeedbackLED(true);
}
# endif
ENABLE_SEND_PWM_BY_TIMER; // Enable timer or ledcWrite() generated PWM output
customDelayMicroseconds(aMarkMicros);
IRLedOff(); // manages also feedback LED
#elif defined(USE_NO_SEND_PWM)
# if !defined(NO_LED_FEEDBACK_CODE)
if (FeedbackLEDControl.LedFeedbackEnabled == LED_FEEDBACK_ENABLED_FOR_SEND) {
setFeedbackLED(true);
}
# endif
# if defined(USE_OPEN_DRAIN_OUTPUT_FOR_SEND_PIN) && !defined(OUTPUT_OPEN_DRAIN)
pinModeFast(sendPin, OUTPUT); // active state for mimicking open drain
# else
digitalWriteFast(sendPin, LOW); // Set output to active low.
# endif
customDelayMicroseconds(aMarkMicros);
IRLedOff();
# if !defined(NO_LED_FEEDBACK_CODE)
if (FeedbackLEDControl.LedFeedbackEnabled == LED_FEEDBACK_ENABLED_FOR_SEND) {
setFeedbackLED(false);
}
# endif
#else
unsigned long startMicros = micros();
unsigned long nextPeriodEnding = startMicros;
unsigned long tMicros;
# if !defined(NO_LED_FEEDBACK_CODE)
bool FeedbackLedIsActive = false;
# endif
do {
// digitalToggleFast(_IR_TIMING_TEST_PIN);
// Output the PWM pulse
noInterrupts(); // do not let interrupts extend the short on period
# if defined(USE_OPEN_DRAIN_OUTPUT_FOR_SEND_PIN)
# if defined(OUTPUT_OPEN_DRAIN)
digitalWriteFast(sendPin, LOW); // active state for open drain
# else
pinModeFast(sendPin, OUTPUT); // active state for mimicking open drain
# endif
# else
// 3.5 us from FeedbackLed on to pin setting. 5.7 us from call of mark() to pin setting incl. setting of feedback pin.
// 4.3 us from do{ to pin setting if sendPin is no constant
digitalWriteFast(sendPin, HIGH);
# endif
delayMicroseconds (periodOnTimeMicros); // this is normally implemented by a blocking wait
// Output the PWM pause
# if defined(USE_OPEN_DRAIN_OUTPUT_FOR_SEND_PIN) && !defined(OUTPUT_OPEN_DRAIN)
# if defined(OUTPUT_OPEN_DRAIN)
digitalWriteFast(sendPin, HIGH); // Set output with pin mode OUTPUT_OPEN_DRAIN to inactive high.
# else
pinModeFast(sendPin, INPUT); // to mimic the open drain inactive state
# endif
# else
digitalWriteFast(sendPin, LOW);
# endif
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)
# if !defined(NO_LED_FEEDBACK_CODE)
/*
* Delayed call of setFeedbackLED() to get better timing
*/
if (!FeedbackLedIsActive) {
FeedbackLedIsActive = true;
if (FeedbackLEDControl.LedFeedbackEnabled == LED_FEEDBACK_ENABLED_FOR_SEND) {
setFeedbackLED(true);
}
}
# endif
/*
* Pause timing
*/
nextPeriodEnding += periodTimeMicros;
do {
tMicros = micros(); // we have only 4 us resolution for AVR @16MHz
// check for aMarkMicros to be gone
unsigned int tDeltaMicros = tMicros - startMicros;
#if defined(__AVR__)
// tDeltaMicros += (160 / CLOCKS_PER_MICRO); // adding this once increases program size !
# if !defined(NO_LED_FEEDBACK_CODE)
if (tDeltaMicros >= aMarkMicros - (30 + (112 / CLOCKS_PER_MICRO))) { // 30 to be constant. Using periodTimeMicros increases program size too much.
// reset feedback led in the last pause before end
if (FeedbackLEDControl.LedFeedbackEnabled == LED_FEEDBACK_ENABLED_FOR_SEND) {
setFeedbackLED(false);
}
}
# endif
if (tDeltaMicros >= aMarkMicros - (112 / CLOCKS_PER_MICRO)) { // To compensate for call duration - 112 is an empirical value
#else
if (tDeltaMicros >= aMarkMicros) {
# if !defined(NO_LED_FEEDBACK_CODE)
if (FeedbackLEDControl.LedFeedbackEnabled == LED_FEEDBACK_ENABLED_FOR_SEND) {
setFeedbackLED(false);
}
# endif
#endif
return;
}
// digitalToggleFast(_IR_TIMING_TEST_PIN); // 3.0 us per call @16MHz
} while (tMicros < nextPeriodEnding); // 3.4 us @16MHz
} while (true);
# endif
}
/**
* Just switch the IR sending LED off to send an IR space
* A space is "no output", so the PWM output is disabled.
* This function may affect the state of feedback LED.
*/
void IRsend::IRLedOff() {
#if defined(SEND_PWM_BY_TIMER)
DISABLE_SEND_PWM_BY_TIMER; // Disable PWM output
#elif defined(USE_NO_SEND_PWM)
# if defined(USE_OPEN_DRAIN_OUTPUT_FOR_SEND_PIN) && !defined(OUTPUT_OPEN_DRAIN)
digitalWriteFast(sendPin, LOW); // prepare for all next active states.
pinModeFast(sendPin, INPUT); // inactive state for open drain
# else
digitalWriteFast(sendPin, HIGH); // Set output to inactive high.
# endif
#else
# if defined(USE_OPEN_DRAIN_OUTPUT_FOR_SEND_PIN)
# if defined(OUTPUT_OPEN_DRAIN)
digitalWriteFast(sendPin, HIGH); // Set output to inactive high.
# else
pinModeFast(sendPin, INPUT); // inactive state to mimic open drain
# endif
# else
digitalWriteFast(sendPin, LOW);
# endif
#endif
#if !defined(NO_LED_FEEDBACK_CODE)
if (FeedbackLEDControl.LedFeedbackEnabled == LED_FEEDBACK_ENABLED_FOR_SEND) {
setFeedbackLED(false);
}
#endif
}
/**
* Sends an IR space for the specified number of microseconds.
* A space is "no output", so just wait.
*/
void IRsend::space(unsigned int aSpaceMicros) {
customDelayMicroseconds(aSpaceMicros);
}
/**
* Custom delay function that circumvents Arduino's delayMicroseconds 16 bit limit
* and is (mostly) not extended by the duration of interrupt codes like the millis() interrupt
*/
void IRsend::customDelayMicroseconds(unsigned long aMicroseconds) {
#if defined(__AVR__)
unsigned long start = micros() - (64 / clockCyclesPerMicrosecond()); // - (64 / clockCyclesPerMicrosecond()) for reduced resolution and additional overhead
#else
unsigned long start = micros();
#endif
// overflow invariant comparison :-)
while (micros() - start < aMicroseconds) {
}
}
/**
* Enables IR output. The kHz value controls the modulation frequency in kilohertz.
* The IR output will be on pin 3 (OC2B).
* This routine is designed for 36-40 kHz and for software generation gives 26 us for 38.46 kHz, 27 us for 37.04 kHz and 25 us for 40 kHz.
* If you use it for other values, it's up to you to make sure it gives reasonable results. (Watch out for overflow / underflow / rounding.)
* TIMER2 is used in phase-correct PWM mode, with OCR2A controlling the frequency and OCR2B
* controlling the duty cycle.
* There is no prescaling, so the output frequency is 16 MHz / (2 * OCR2A)
* To turn the output on and off, we leave the PWM running, but connect and disconnect the output pin.
* A few hours staring at the ATmega documentation and this will all make sense.
* See my Secrets of Arduino PWM at http://www.righto.com/2009/07/secrets-of-arduino-pwm.html for details.
*/
void IRsend::enableIROut(uint_fast8_t aFrequencyKHz) {
#if defined(SEND_PWM_BY_TIMER)
timerConfigForSend(aFrequencyKHz); // must set output pin mode and disable receive interrupt if required, e.g. uses the same resource
#elif defined(USE_NO_SEND_PWM)
(void) aFrequencyKHz;
#else
periodTimeMicros = (1000U + (aFrequencyKHz / 2)) / aFrequencyKHz; // rounded value -> 26 for 38.46 kHz, 27 for 37.04 kHz, 25 for 40 kHz.
# if defined(IR_SEND_PIN)
periodOnTimeMicros = (((periodTimeMicros * IR_SEND_DUTY_CYCLE_PERCENT) + 50) / 100U); // +50 for rounding -> 830/100 for 30% and 16 MHz
# else
// Heuristics! We require a nanosecond correction for "slow" digitalWrite() functions
periodOnTimeMicros = (((periodTimeMicros * IR_SEND_DUTY_CYCLE_PERCENT) + 50 - (PULSE_CORRECTION_NANOS / 10)) / 100U); // +50 for rounding -> 530/100 for 30% and 16 MHz
# endif
#endif // defined(SEND_PWM_BY_TIMER)
#if defined(USE_OPEN_DRAIN_OUTPUT_FOR_SEND_PIN) && defined(OUTPUT_OPEN_DRAIN) // the mode INPUT for mimicking open drain is set at IRLedOff()
# if defined(IR_SEND_PIN)
pinModeFast(IR_SEND_PIN, OUTPUT_OPEN_DRAIN);
# else
pinModeFast(sendPin, OUTPUT_OPEN_DRAIN);
# endif
#else
# if !(defined(SEND_PWM_BY_TIMER) && defined(ESP32)) // ledcWrite since ESP 2.0.2 does not work if pin mode is set
# if defined(IR_SEND_PIN)
pinModeFast(IR_SEND_PIN, OUTPUT);
# else
pinModeFast(sendPin, OUTPUT);
# endif
# endif
#endif // defined(USE_OPEN_DRAIN_OUTPUT_FOR_SEND_PIN)
}
unsigned int IRsend::getPulseCorrectionNanos() {
return PULSE_CORRECTION_NANOS;
}
/** @}*/
#endif // _IR_SEND_HPP

View File

@@ -0,0 +1,14 @@
/**
* @file IRremote.h
*
* @brief Stub for backward compatibility
*/
#ifndef IRremote_h
#define IRremote_h
#include "IRremote.hpp"
#endif // IRremote_h
#pragma once

View File

@@ -0,0 +1,357 @@
/**
* @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-2022 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, BOSEWAVE, WHYNTER 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
#define VERSION_IRREMOTE "3.7.1"
#define VERSION_IRREMOTE_MAJOR 3
#define VERSION_IRREMOTE_MINOR 7
#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)
// 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, BOSEWAVE, MAGIQUEST,WHYNTER 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_SAMSUNG) \
|| defined(DECODE_SONY) || defined(DECODE_RC5) || defined(DECODE_RC6) \
|| defined(DECODE_DISTANCE) || defined(DECODE_HASH) || defined(DECODE_BOSEWAVE) \
|| defined(DECODE_LEGO_PF) || 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 // the same as 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_WHYNTER
#define DECODE_MAGIQUEST // It modifies the RAW_BUFFER_LENGTH from 100 to 112
# endif
# if !defined(EXCLUDE_UNIVERSAL_PROTOCOLS)
#define DECODE_DISTANCE // universal decoder for pulse distance 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)
#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
****************************************************/
/**
* The length of the buffer where the IR timing data is stored before decoding
* 100 is sufficient for most standard protocols, but air conditioners often send a longer protocol data stream
*/
#if !defined(RAW_BUFFER_LENGTH)
# if defined(DECODE_MAGIQUEST)
#define RAW_BUFFER_LENGTH 112 // MagiQuest requires 112 bytes.
# else
#define RAW_BUFFER_LENGTH 100 ///< Length of raw duration buffer. Must be even. 100 supports up to 48 bit codings inclusive 1 start and 1 stop bit.
//#define RAW_BUFFER_LENGTH 750 // 750 is 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
/**
* 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 to +50 us
* 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 24 ms.
* 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>"
#define RECORD_GAP_MICROS 5000 // FREDRICH28AC / LG2 header space is 9700, 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 20000
#endif
/** Minimum gap between IR transmissions, in MICROS_PER_TICK */
#define RECORD_GAP_TICKS (RECORD_GAP_MICROS / MICROS_PER_TICK) // 221 for 1100
/*
* 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
#warning INFO: For ESP32, RP2040, mbed and particle boards SEND_PWM_BY_TIMER is enabled by default. If this is not intended, deactivate the line 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 -> fall back to mode OUTPUT.
#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 50
#endif
#define MILLIS_IN_ONE_SECOND 1000L
#define MICROS_IN_ONE_SECOND 1000000L
#define MICROS_IN_ONE_MILLI 1000L
#include "IRremoteInt.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"
# endif
/*
* Include the sources here to enable compilation with macro values set by user program.
*/
#include "IRReceive.hpp"
#include "IRSend.hpp"
/*
* Include the sources of all decoders here to enable compilation with macro values set by user program.
*/
# if defined(DECODE_BOSEWAVE)
#include "ir_BoseWave.hpp"
# endif
# if defined(DECODE_DENON ) // Includes Sharp
#include "ir_Denon.hpp"
# endif
# if defined(DECODE_DISTANCE) // universal decoder for pulse distance protocols - requires up to 750 bytes additional program memory
#include "ir_DistanceProtocol.hpp"
# endif
# if defined(DECODE_JVC)
#include "ir_JVC.hpp"
# endif
# if defined(DECODE_KASEIKYO) || defined(DECODE_PANASONIC)
#include "ir_Kaseikyo.hpp"
# endif
# if defined(DECODE_LEGO_PF)
#include "ir_Lego.hpp"
# endif
# if defined(DECODE_LG)
#include "ir_LG.hpp"
# endif
# if defined(DECODE_MAGIQUEST)
#include "ir_MagiQuest.hpp"
# endif
# if defined(DECODE_NEC) // Includes Apple and Onkyo
#include "ir_NEC.hpp"
# endif
# if defined(DECODE_RC5) || defined(DECODE_RC6)
#include "ir_RC5_RC6.hpp"
# endif
# if defined(DECODE_SAMSUNG)
#include "ir_Samsung.hpp"
# endif
# if defined(DECODE_SONY)
#include "ir_Sony.hpp"
# endif
# if defined(DECODE_WHYNTER)
#include "ir_Whynter.hpp"
# endif
#include "ir_Pronto.hpp" // pronto is an universal decoder and encoder
#include "ir_Dish.hpp" // contains only sendDISH(unsigned long data, int nbits)
#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

View File

@@ -0,0 +1,532 @@
/**
* @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-2022 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
#include "IRProtocol.h"
/****************************************************
* Declarations for the receiver Interrupt Service Routine
****************************************************/
// ISR State-Machine : Receiver States
#define IR_REC_STATE_IDLE 0
#define IR_REC_STATE_MARK 1
#define IR_REC_STATE_SPACE 2
#define IR_REC_STATE_STOP 3 // set to IR_REC_STATE_IDLE only 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 over 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
uint_fast16_t TickCounterForISR; ///< Counts 50uS ticks. The value is copied into the rawbuf array on every transition.
bool OverflowFlag; ///< Raw buffer OverflowFlag occurred
#if RAW_BUFFER_LENGTH <= 254 // saves around 75 bytes program memory and speeds up ISR
uint_fast8_t rawlen; ///< counter of entries in rawbuf
#else
uint_fast16_t rawlen; ///< counter of entries in rawbuf
#endif
unsigned int rawbuf[RAW_BUFFER_LENGTH]; ///< raw data / tick counts per mark/space, first entry is the length of the gap between previous and current command
};
/*
* Debug directives
*/
#if defined(DEBUG)
# 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
****************************************************/
/*
* Definitions for member IRData.flags
*/
#define IRDATA_FLAGS_EMPTY 0x00
#define IRDATA_FLAGS_IS_REPEAT 0x01
#define IRDATA_FLAGS_IS_AUTO_REPEAT 0x02
#define IRDATA_FLAGS_PARITY_FAILED 0x04 ///< the current (autorepeat) frame violated parity check
#define IRDATA_TOGGLE_BIT_MASK 0x08 ///< 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)
#define IRDATA_FLAGS_WAS_OVERFLOW 0x40 ///< irparams.rawlen is 0 in this case to avoid endless OverflowFlag
#define IRDATA_FLAGS_IS_LSB_FIRST 0x00
#define IRDATA_FLAGS_IS_MSB_FIRST 0x80 ///< Just for info. Value is mainly determined by the protocol
/**
* 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, ...
uint16_t address; ///< Decoded address
uint16_t command; ///< Decoded command
uint16_t extra; ///< Contains MagiQuest magnitude, Kaseikyo unknown vendor ID and Distance protocol (SpaceTicksShort << 8) | SpaceTicksLong.
uint16_t numberOfBits; ///< Number of bits received for data (address + command + parity) - to determine protocol length if different length are possible.
uint8_t flags; ///< See IRDATA_FLAGS_* definitions above
uint32_t decodedRawData; ///< Up to 32 bit decoded raw data, to be used for send functions.
irparams_struct *rawDataPtr; ///< Pointer of the raw timing data to be decoded. Mainly the data buffer filled by receiving ISR.
};
/**
* 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 values - see IRremoteint.h
unsigned int *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);
IRrecv(uint_fast8_t aReceivePin, uint_fast8_t aFeedbackLEDPin);
void setReceivePin(uint_fast8_t aReceivePinNumber);
/*
* 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 start(uint32_t aMicrosecondsToAddToGapCounter);
void restartAfterSend();
bool available();
IRData* read(); // returns decoded data
// write is a method of class IRsend below
// size_t write(IRData *aIRSendData, uint_fast8_t aNumberOfRepeats = NO_REPEATS);
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 printIRResultShort(Print *aSerial);
void printIRResultMinimal(Print *aSerial);
void printIRResultRawFormatted(Print *aSerial, bool aOutputMicrosecondsInsteadOfTicks = true);
void printIRResultAsCVariables(Print *aSerial);
void compensateAndPrintIRResultAsCArray(Print *aSerial, bool aOutputMicrosecondsInsteadOfTicks = true);
void compensateAndPrintIRResultAsPronto(Print *aSerial, unsigned int frequency = 38000U);
/*
* Store the data for further processing
*/
void compensateAndStoreIRResultInArray(uint8_t *aArrayPtr);
size_t compensateAndStorePronto(String *aString, unsigned int frequency = 38000U);
/*
* The main decoding functions used by the individual decoders
*/
bool decodePulseDistanceData(uint_fast8_t aNumberOfBits, uint_fast8_t aStartOffset, unsigned int aBitMarkMicros,
unsigned int aOneSpaceMicros, unsigned int aZeroSpaceMicros, bool aMSBfirst);
bool decodePulseWidthData(uint_fast8_t aNumberOfBits, uint_fast8_t aStartOffset, unsigned int aOneMarkMicros,
unsigned int aZeroMarkMicros, unsigned int aBitSpaceMicros, bool aMSBfirst);
bool decodeBiPhaseData(uint_fast8_t aNumberOfBits, uint_fast8_t aStartOffset, uint_fast8_t aStartClockCount,
uint_fast8_t aValueOfSpaceToMarkTransition, unsigned int aBiphaseTimeUnit);
void initBiphaselevel(uint_fast8_t aRCDecodeRawbuffOffset, unsigned int aBiphaseTimeUnit);
uint_fast8_t getBiphaselevel();
/*
* All standard (decode address + command) protocol decoders
*/
bool decodeBoseWave();
bool decodeDenon();
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 decodeDistance();
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(
decode_results *aResults)
__attribute__ ((deprecated ("Please use IrReceiver.decode() without a parameter and IrReceiver.decodedIRData.<fieldname> ."))); // deprecated
// for backward compatibility. Now in IRFeedbackLED.hpp
void blink13(uint8_t aEnableLEDFeedback)
__attribute__ ((deprecated ("Please use setLEDFeedback() or enableLEDFeedback() / disableLEDFeedback()."))); // deprecated
/*
* Internal functions
*/
void initDecodedIRData();
uint_fast8_t compare(unsigned int oldval, unsigned int newval);
IRData decodedIRData; // New: decoded IR data for the application
// Last decoded IR data for repeat detection
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(unsigned int aMeasuredTicks, unsigned int aMatchValueMicros);
bool matchMark(unsigned int aMeasuredTicks, unsigned int aMatchValueMicros);
bool matchSpace(unsigned int aMeasuredTicks, unsigned int aMatchValueMicros);
/*
* Old function names
*/
bool MATCH(unsigned int measured, unsigned int desired);
bool MATCH_MARK(unsigned int measured_ticks, unsigned int desired_us);
bool MATCH_SPACE(unsigned int measured_ticks, unsigned int desired_us);
int getMarkExcessMicros();
void printActiveIRProtocols(Print *aSerial);
void printIRResultShort(Print *aSerial, IRData *aIRDataPtr, bool aPrintGap);
/****************************************************
* 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)
#define TOLERANCE_FOR_DECODERS_MARK_OR_SPACE_MATCHING 25 // Relative tolerance (in percent) for matchTicks(), matchMark() and matchSpace() functions used for protocol decoding.
#endif
/** Lower tolerance for comparison of measured data */
//#define LTOL (1.0 - (TOLERANCE/100.))
#define LTOL (100 - TOLERANCE_FOR_DECODERS_MARK_OR_SPACE_MATCHING)
/** Upper tolerance for comparison of measured data */
//#define UTOL (1.0 + (TOLERANCE/100.))
#define UTOL (100 + TOLERANCE_FOR_DECODERS_MARK_OR_SPACE_MATCHING)
//#define TICKS_LOW(us) ((int)(((us)*LTOL/MICROS_PER_TICK)))
//#define TICKS_HIGH(us) ((int)(((us)*UTOL/MICROS_PER_TICK + 1)))
#if MICROS_PER_TICK == 50 && TOLERANCE_FOR_DECODERS_MARK_OR_SPACE_MATCHING == 25 // Defaults
#define TICKS_LOW(us) ((us)/67 ) // (us) / ((MICROS_PER_TICK:50 / LTOL:75 ) * 100)
#define TICKS_HIGH(us) (((us)/40) + 1) // (us) / ((MICROS_PER_TICK:50 / UTOL:125) * 100) + 1
#else
#define TICKS_LOW(us) ((unsigned int ) ((long) (us) * LTOL / (MICROS_PER_TICK * 100) ))
#define TICKS_HIGH(us) ((unsigned int ) ((long) (us) * UTOL / (MICROS_PER_TICK * 100) + 1))
#endif
/*
* The receiver instance
*/
extern IRrecv IrReceiver;
/****************************************************
* SENDING
****************************************************/
/**
* Just for better readability of code
*/
#define NO_REPEATS 0
#define SEND_STOP_BIT true
#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
*/
#if defined(IR_SEND_PIN)
void begin();
void begin(bool aEnableLEDFeedback, uint_fast8_t aFeedbackLEDPin = USE_DEFAULT_FEEDBACK_LED_PIN);
#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
#endif
// Not guarded for backward compatibility
void begin(uint_fast8_t aSendPin, bool aEnableLEDFeedback, uint_fast8_t aFeedbackLEDPin = USE_DEFAULT_FEEDBACK_LED_PIN);
size_t write(IRData *aIRSendData, uint_fast8_t aNumberOfRepeats = NO_REPEATS);
void enableIROut(uint_fast8_t aFrequencyKHz);
void sendPulseDistanceWidthData(unsigned int aOneMarkMicros, unsigned int aOneSpaceMicros, unsigned int aZeroMarkMicros,
unsigned int aZeroSpaceMicros, uint32_t aData, uint_fast8_t aNumberOfBits, bool aMSBfirst, bool aSendStopBit = false);
void sendBiphaseData(unsigned int aBiphaseTimeUnit, uint32_t aData, uint_fast8_t aNumberOfBits);
void mark(unsigned int aMarkMicros);
void space(unsigned int 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 sendBoseWave(uint8_t aCommand, uint_fast8_t aNumberOfRepeats = NO_REPEATS);
void sendDenon(uint8_t aAddress, uint8_t aCommand, uint_fast8_t aNumberOfRepeats, bool aSendSharp = false);
void sendDenonRaw(uint16_t aRawData, uint_fast8_t aNumberOfRepeats = 0)
#if !defined (DOXYGEN)
__attribute__ ((deprecated ("Please use sendDenon(aAddress, aCommand, aNumberOfRepeats).")));
#endif
void sendJVC(uint8_t aAddress, uint8_t aCommand, uint_fast8_t aNumberOfRepeats);
void sendLGRepeat(bool aUseLG2Protocol = false);
void sendLG(uint8_t aAddress, uint16_t aCommand, uint_fast8_t aNumberOfRepeats, bool aIsRepeat = false, bool aUseLG2Protocol =
false);
void sendLGRaw(uint32_t aRawData, uint_fast8_t aNumberOfRepeats = 0, bool aIsRepeat = false, bool aUseLG2Protocol = false);
void sendNECRepeat();
void sendNEC(uint16_t aAddress, uint8_t aCommand, uint_fast8_t aNumberOfRepeats, bool aIsRepeat = false);
void sendNECRaw(uint32_t aRawData, uint_fast8_t aNumberOfRepeats = 0, bool aIsRepeat = false);
// NEC variants
void sendOnkyo(uint16_t aAddress, uint16_t aCommand, uint_fast8_t aNumberOfRepeats, bool aIsRepeat = false);
void sendApple(uint8_t aAddress, uint8_t aCommand, uint_fast8_t aNumberOfRepeats, bool aIsRepeat = false);
void sendKaseikyo(uint16_t aAddress, uint8_t aData, uint_fast8_t aNumberOfRepeats, uint16_t aVendorCode); // LSB first
void sendPanasonic(uint16_t aAddress, uint8_t aData, uint_fast8_t aNumberOfRepeats); // LSB first
void sendKaseikyo_Denon(uint16_t aAddress, uint8_t aData, uint_fast8_t aNumberOfRepeats); // LSB first
void sendKaseikyo_Mitsubishi(uint16_t aAddress, uint8_t aData, uint_fast8_t aNumberOfRepeats); // LSB first
void sendKaseikyo_Sharp(uint16_t aAddress, uint8_t aData, uint_fast8_t aNumberOfRepeats); // LSB first
void sendKaseikyo_JVC(uint16_t aAddress, uint8_t aData, uint_fast8_t aNumberOfRepeats); // LSB first
void sendRC5(uint8_t aAddress, uint8_t aCommand, uint_fast8_t aNumberOfRepeats, bool aEnableAutomaticToggle = true);
void sendRC6(uint8_t aAddress, uint8_t aCommand, uint_fast8_t aNumberOfRepeats, bool aEnableAutomaticToggle = true);
void sendSamsungRepeat();
void sendSamsung(uint16_t aAddress, uint16_t aCommand, uint_fast8_t aNumberOfRepeats);
void sendSharp(uint8_t aAddress, uint8_t aCommand, uint_fast8_t aNumberOfRepeats); // redirected to sendDenon
void sendSony(uint16_t aAddress, uint8_t aCommand, uint_fast8_t aNumberOfRepeats, uint8_t numberOfBits = 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 wand_id, uint16_t magnitude);
void sendPronto(const __FlashStringHelper *str, uint_fast8_t aNumberOfRepeats = NO_REPEATS);
void sendPronto(const char *prontoHexString, uint_fast8_t aNumberOfRepeats = NO_REPEATS);
void sendPronto(const uint16_t *data, unsigned int length, uint_fast8_t aNumberOfRepeats = NO_REPEATS);
#if defined(__AVR__)
void sendPronto_PF(uint_farptr_t str, uint_fast8_t aNumberOfRepeats = NO_REPEATS);
void sendPronto_P(const char *str, uint_fast8_t aNumberOfRepeats);
#endif
// Template protocol :-)
void sendShuzu(uint16_t aAddress, uint8_t aCommand, uint_fast8_t aNumberOfRepeats);
/*
* OLD send functions
*/
void sendDenon(unsigned long data, int nbits);
void sendDISH(unsigned long data, int nbits);
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);
void sendNEC(uint32_t aRawData,
uint8_t nbits)
__attribute__ ((deprecated ("This old function sends MSB first! Please use sendNEC(aAddress, aCommand, aNumberOfRepeats)."))) {
sendNECMSB(aRawData, nbits);
}
void sendNECMSB(uint32_t data, uint8_t nbits, bool repeat = false);
void sendPanasonic(uint16_t aAddress,
uint32_t aData)
__attribute__ ((deprecated ("This old function sends MSB first! Please use sendPanasonic(aAddress, aCommand, aNumberOfRepeats).")));
void sendRC5(uint32_t data, uint8_t nbits);
void sendRC5ext(uint8_t addr, uint8_t cmd, bool toggle);
void sendRC6(uint32_t data, uint8_t nbits);
void sendRC6(uint64_t data, uint8_t nbits);
void sendSharpRaw(unsigned long data, int nbits);
void sendSharp(unsigned int address, unsigned int command);
void sendSAMSUNG(unsigned long data, int nbits);
__attribute__ ((deprecated ("This old function sends MSB first! Please use sendSamsung().")));
void sendSony(unsigned long data,
int nbits)
__attribute__ ((deprecated ("This old function sends MSB first! Please use sendSony(aAddress, aCommand, aNumberOfRepeats).")));
;
void sendWhynter(unsigned long data, int nbits);
#if !defined(IR_SEND_PIN)
uint8_t sendPin;
#endif
unsigned int periodTimeMicros;
unsigned int periodOnTimeMicros; // compensated with PULSE_CORRECTION_NANOS for duration of digitalWrite.
unsigned int getPulseCorrectionNanos();
void customDelayMicroseconds(unsigned long aMicroseconds);
};
/*
* The sender instance
*/
extern IRsend IrSender;
#endif // _IR_REMOTE_INT_H

View File

@@ -0,0 +1,96 @@
/*
* LongUnion.h
*
* Copyright (C) 2020 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)
#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.
*/
union WordUnion {
struct {
uint8_t LowByte;
uint8_t HighByte;
} UByte;
struct {
int8_t LowByte;
int8_t HighByte;
} Byte;
uint8_t UBytes[2];
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;
struct {
uint8_t LowByte;
WordUnion MidWord;
uint8_t HighByte;
} ByteWord;
struct {
int16_t LowWord;
int16_t HighWord;
} Word;
struct {
WordUnion LowWord;
WordUnion HighWord;
} WordUnion;
struct {
uint16_t LowWord;
uint16_t HighWord;
} UWord;
uint8_t UBytes[4]; // seems to have the same code size as using struct UByte
int8_t Bytes[4];
uint16_t UWords[2];
int16_t Words[2];
uint32_t ULong;
int32_t Long;
};
#endif // _LONG_UNION_H
#endif // !defined(_WORD_UNION_H) || !defined(_LONG_UNION_H)

View File

@@ -0,0 +1,114 @@
/*
* TinyIRReceiver.h
*
*
* Copyright (C) 2021 Armin Joachimsmeyer
* armin.joachimsmeyer@gmail.com
*
* This file is part of IRMP https://github.com/ukw100/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_RECEIVER_H
#define _TINY_IR_RECEIVER_H
#include <Arduino.h>
#include "LongUnion.h"
/** \addtogroup TinyReceiver Minimal receiver for NEC protocol
* @{
*/
/*
* This function is called if a complete command was received and must be implemented by the including file (user code)
*/
void handleReceivedTinyIRData(uint16_t aAddress, uint8_t aCommand, bool isRepeat);
// LSB first, 1 start bit + 16 bit address + 8 bit data + 8 bit inverted data + 1 stop bit.
// see: https://www.sbprojects.net/knowledge/ir/nec.php
#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 // Not used yet - Commands are repeated every 110 ms (measured from start to start) for as long as the key on the remote control is held down.
/*
* 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;
/*
* Data
*/
uint32_t IRRawDataMask;
LongUnion IRRawData;
bool IRRepeatDetected;
};
/*
* Can be used by the callback to transfer received data to main loop for further processing
* E.g. with volatile struct TinyIRReceiverCallbackDataStruct sCallbackData;
*/
struct TinyIRReceiverCallbackDataStruct {
uint16_t Address;
uint8_t Command;
bool isRepeat;
bool justWritten;
};
void initPCIInterruptForTinyReceiver();
void enablePCIInterruptForTinyReceiver();
void disablePCIInterruptForTinyReceiver();
bool isTinyReceiverIdle();
/** @}*/
#endif // _TINY_IR_RECEIVER_H

View File

@@ -0,0 +1,530 @@
/*
* TinyIRReceiver.hpp
*
* Receives IR protocol data of NEC protocol using pin change interrupts.
* NEC is the protocol of most cheap remote controls for Arduino.
*
* No parity check is done!
* On a completely received IR command, the user function handleReceivedIRData(uint16_t aAddress, uint8_t aCommand, bool isRepetition)
* 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.
* !!!!!!!!!!!!!!!!!!!!!
*
*
* Copyright (C) 2021-2022 Armin Joachimsmeyer
* armin.joachimsmeyer@gmail.com
*
* This file is part of IRMP https://github.com/ukw100/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>.
*
*/
/*
* 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_INPUT_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.
*
*/
#ifndef _TINY_IR_RECEIVER_HPP
#define _TINY_IR_RECEIVER_HPP
#include <Arduino.h>
#include "TinyIRReceiver.h" // If not defined, it defines IR_INPUT_PIN, IR_FEEDBACK_LED_PIN and TINY_RECEIVER_USE_ARDUINO_ATTACH_INTERRUPT
#include "digitalWriteFast.h"
/** \addtogroup TinyReceiver Minimal receiver for NEC protocol
* @{
*/
//#define DEBUG // to see if attachInterrupt used
//#define TRACE // to see the state of the ISR state machine
//#define _IR_MEASURE_TIMING // Activate this if you want to enable internal hardware timing measurement.
//#define _IR_TIMING_TEST_PIN 7
TinyIRReceiverStruct TinyIRReceiverControl;
/*
* Set input pin and output pin definitions etc.
*/
#if !defined(IR_INPUT_PIN)
#if defined(__AVR_ATtiny1616__) || defined(__AVR_ATtiny3216__) || defined(__AVR_ATtiny3217__)
#warning "IR_INPUT_PIN is not defined, so it is set to 10"
#define IR_INPUT_PIN 10
#else
#warning "IR_INPUT_PIN is not defined, so it is set to 2"
#define IR_INPUT_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_INPUT_PIN == 3) || (IR_INPUT_PIN == 9))) /*ATtinyX7(digisparkpro) and pin 3 or 9 */\
|| (! defined(ARDUINO_AVR_DIGISPARKPRO) && ((IR_INPUT_PIN == 3) || (IR_INPUT_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.
*/
#if defined(ESP32) || defined(ESP8266)
void IRAM_ATTR handleReceivedIRData(uint16_t aAddress, uint8_t aCommand, bool isRepetition);
#else
void handleReceivedIRData(uint16_t aAddress, uint8_t aCommand, bool isRepetition);
#endif
/**
* The ISR 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(ESP32) || defined(ESP8266)
void IRAM_ATTR IRPinChangeInterruptHandler(void)
#else
void IRPinChangeInterruptHandler(void)
#endif
{
#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_INPUT_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
*/
uint32_t tCurrentMicros = micros();
uint16_t tMicrosOfMarkOrSpace = tCurrentMicros - TinyIRReceiverControl.LastChangeMicros;
TinyIRReceiverControl.LastChangeMicros = tCurrentMicros;
uint8_t tState = TinyIRReceiverControl.IRReceiverState;
#if defined(TRACE)
Serial.print(tState);
Serial.print(' ');
// Serial.print(F(" I="));
// Serial.print(tIRLevel);
// Serial.print(F(" D="));
// Serial.print(tDeltaMicros);
// Serial.println();
#endif
if (tIRLevel == LOW) {
/*
* We have a mark here
*/
if (tMicrosOfMarkOrSpace > 2 * NEC_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;
}
else if (tState == IR_RECEIVER_STATE_WAITING_FOR_FIRST_DATA_MARK) {
if (tMicrosOfMarkOrSpace >= lowerValue25Percent(NEC_HEADER_SPACE)
&& tMicrosOfMarkOrSpace <= upperValue25Percent(NEC_HEADER_SPACE)) {
/*
* We have a valid data header space here -> initialize data
*/
TinyIRReceiverControl.IRRawDataBitCounter = 0;
TinyIRReceiverControl.IRRawData.ULong = 0;
TinyIRReceiverControl.IRRawDataMask = 1;
TinyIRReceiverControl.IRRepeatDetected = false;
tState = IR_RECEIVER_STATE_WAITING_FOR_DATA_SPACE;
} else if (tMicrosOfMarkOrSpace >= lowerValue25Percent(NEC_REPEAT_HEADER_SPACE)
&& tMicrosOfMarkOrSpace <= upperValue25Percent(NEC_REPEAT_HEADER_SPACE)
&& TinyIRReceiverControl.IRRawDataBitCounter >= NEC_BITS) {
/*
* We have a repeat header here and no broken receive before -> set repeat flag
*/
TinyIRReceiverControl.IRRepeatDetected = true;
tState = IR_RECEIVER_STATE_WAITING_FOR_DATA_SPACE;
} 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(NEC_ZERO_SPACE)
&& tMicrosOfMarkOrSpace <= upperValue50Percent(NEC_ONE_SPACE)) {
// We have a valid bit here
tState = IR_RECEIVER_STATE_WAITING_FOR_DATA_SPACE;
if (tMicrosOfMarkOrSpace >= 2 * NEC_UNIT) {
// we received a 1
TinyIRReceiverControl.IRRawData.ULong |= TinyIRReceiverControl.IRRawDataMask;
} 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(NEC_HEADER_MARK)
&& tMicrosOfMarkOrSpace <= upperValue25Percent(NEC_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(NEC_BIT_MARK)
&& tMicrosOfMarkOrSpace <= upperValue50Percent(NEC_BIT_MARK)) {
/*
* We have a valid mark here, check for transmission complete
*/
if (TinyIRReceiverControl.IRRawDataBitCounter >= NEC_BITS || TinyIRReceiverControl.IRRepeatDetected) {
/*
* Code complete -> call callback, no parity check!
*/
// Reset state for new start
tState = IR_RECEIVER_STATE_WAITING_FOR_START_MARK;
#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
/*
* Address reduction to 8 bit
*/
if (TinyIRReceiverControl.IRRawData.UByte.LowByte
== (uint8_t) (~TinyIRReceiverControl.IRRawData.UByte.MidLowByte)) {
// standard 8 bit address NEC protocol
TinyIRReceiverControl.IRRawData.UByte.MidLowByte = 0; // Address is the first 8 bit
}
/*
* Call user provided callback here
*/
handleReceivedTinyIRData(TinyIRReceiverControl.IRRawData.UWord.LowWord,
TinyIRReceiverControl.IRRawData.UByte.MidHighByte, TinyIRReceiverControl.IRRepeatDetected);
} 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);
}
/**
* Sets IR_INPUT_PIN mode to INPUT_PULLUP, if required, sets feedback LED output mode and call enablePCIInterruptForTinyReceiver()
*/
void initPCIInterruptForTinyReceiver() {
pinModeFast(IR_INPUT_PIN, INPUT_PULLUP);
#if !defined(NO_LED_FEEDBACK_CODE) && defined(IR_FEEDBACK_LED_PIN)
pinModeFast(IR_FEEDBACK_LED_PIN, OUTPUT);
#endif
enablePCIInterruptForTinyReceiver();
}
#if defined (DEBUG) && !defined(STR)
// Helper macro for getting a macro definition as string
#define STR_HELPER(x) #x
#define STR(x) STR_HELPER(x)
#endif
/**
* Initializes hardware interrupt generation according to IR_INPUT_PIN or use attachInterrupt() function.
*/
void enablePCIInterruptForTinyReceiver() {
#if defined(_IR_MEASURE_TIMING) && defined(_IR_TIMING_TEST_PIN)
pinModeFast(_IR_TIMING_TEST_PIN, OUTPUT);
#endif
#if defined(__AVR_ATtiny1616__) || defined(__AVR_ATtiny3216__) || defined(__AVR_ATtiny3217__)
attachInterrupt(IR_INPUT_PIN, IRPinChangeInterruptHandler, CHANGE); // 2.2 us more than version configured with macros and not compatible
#elif !defined(__AVR__) || defined(TINY_RECEIVER_USE_ARDUINO_ATTACH_INTERRUPT)
// costs 112 bytes program memory + 4 bytes RAM
attachInterrupt(digitalPinToInterrupt(IR_INPUT_PIN), IRPinChangeInterruptHandler, CHANGE);
# if defined(DEBUG)
Serial.println(F("Use attachInterrupt for pin=" STR(IR_INPUT_PIN)));
# endif
#else
# if defined(DEBUG)
Serial.println(F("Use static interrupt for pin=" STR(IR_INPUT_PIN)));
# endif
# if defined(__AVR_ATtiny25__) || defined(__AVR_ATtiny45__) || defined(__AVR_ATtiny85__)
// use PinChangeInterrupt no INT0 for pin PB2
PCMSK = _BV(IR_INPUT_PIN);
// clear interrupt bit
GIFR |= 1 << PCIF;
// enable interrupt on next change
GIMSK |= 1 << PCIE;
# elif defined(__AVR_ATtiny87__) || defined(__AVR_ATtiny167__)
# if defined(ARDUINO_AVR_DIGISPARKPRO)
# if (IR_INPUT_PIN == 3)
// interrupt on any logical change
EICRA |= _BV(ISC00);
// clear interrupt bit
EIFR |= 1 << INTF0;
// enable interrupt on next change
EIMSK |= 1 << INT0;
# elif (IR_INPUT_PIN == 9)
EICRA |= _BV(ISC10);
// clear interrupt bit
EIFR |= 1 << INTF1;
// enable interrupt on next change
EIMSK |= 1 << INT1;
# else
# error "IR_INPUT_PIN must be 9 or 3."
# endif // if (IR_INPUT_PIN == 9)
# else // defined(ARDUINO_AVR_DIGISPARKPRO)
# if (IR_INPUT_PIN == 14)
// interrupt on any logical change
EICRA |= _BV(ISC00);
// clear interrupt bit
EIFR |= 1 << INTF0;
// enable interrupt on next change
EIMSK |= 1 << INT0;
# elif (IR_INPUT_PIN == 3)
EICRA |= _BV(ISC10);
// clear interrupt bit
EIFR |= 1 << INTF1;
// enable interrupt on next change
EIMSK |= 1 << INT1;
# else
# error "IR_INPUT_PIN must be 14 or 3."
# endif // if (IR_INPUT_PIN == 14)
# endif
# else // defined(__AVR_ATtiny25__)
/*
* ATmegas + ATtiny88 here
*/
# if (IR_INPUT_PIN == 2)
// interrupt on any logical change
EICRA |= _BV(ISC00);
// clear interrupt bit
EIFR |= 1 << INTF0;
// enable interrupt on next change
EIMSK |= 1 << INT0;
# elif (IR_INPUT_PIN == 3)
EICRA |= _BV(ISC10);
// clear interrupt bit
EIFR |= 1 << INTF1;
// enable interrupt on next change
EIMSK |= 1 << INT1;
# elif IR_INPUT_PIN == 4 || IR_INPUT_PIN == 5 || IR_INPUT_PIN == 6 || IR_INPUT_PIN == 7
//ATmega328 (Uno, Nano ) etc. Enable pin change interrupt 20 to 23 for port PD4 to PD7 (Arduino pin 4 to 7)
PCICR |= _BV(PCIE2);
PCMSK2 = digitalPinToBitMask(IR_INPUT_PIN);
# elif IR_INPUT_PIN == 8 || IR_INPUT_PIN == 9 || IR_INPUT_PIN == 10 || IR_INPUT_PIN == 11 || IR_INPUT_PIN == 12 || IR_INPUT_PIN == 13
//ATmega328 (Uno, Nano ) etc. Enable pin change interrupt 0 to 5 for port PB0 to PB5 (Arduino pin 8 to 13)
PCICR |= _BV(PCIE0);
PCMSK0 = digitalPinToBitMask(IR_INPUT_PIN);
# elif IR_INPUT_PIN == A0 || IR_INPUT_PIN == A1 || IR_INPUT_PIN == A2 || IR_INPUT_PIN == A3 || IR_INPUT_PIN == A4 || IR_INPUT_PIN == A5
//ATmega328 (Uno, Nano ) etc. Enable pin change interrupt 8 to 13 for port PC0 to PC5 (Arduino pin A0 to A5)
PCICR |= _BV(PCIE1);
PCMSK1 = digitalPinToBitMask(IR_INPUT_PIN);
# else
# error "IR_INPUT_PIN not allowed."
# endif // if (IR_INPUT_PIN == 2)
# endif // defined(__AVR_ATtiny25__)
#endif // ! defined(__AVR__) || defined(TINY_RECEIVER_USE_ARDUINO_ATTACH_INTERRUPT)
}
void disablePCIInterruptForTinyReceiver() {
#if defined(_IR_MEASURE_TIMING) && defined(_IR_TIMING_TEST_PIN)
pinModeFast(_IR_TIMING_TEST_PIN, OUTPUT);
#endif
#if defined(__AVR_ATtiny1616__) || defined(__AVR_ATtiny3216__) || defined(__AVR_ATtiny3217__)
detachInterrupt(IR_INPUT_PIN);
#elif !defined(__AVR__) || defined(TINY_RECEIVER_USE_ARDUINO_ATTACH_INTERRUPT)
// costs 112 bytes program memory + 4 bytes RAM
detachInterrupt(digitalPinToInterrupt(IR_INPUT_PIN));
#else
# if defined(__AVR_ATtiny25__) || defined(__AVR_ATtiny45__) || defined(__AVR_ATtiny85__)
// clear interrupt bit
GIFR |= 1 << PCIF;
// disable interrupt on next change
GIMSK &= ~(1 << PCIE);
# elif defined(__AVR_ATtiny87__) || defined(__AVR_ATtiny167__)
# if defined(ARDUINO_AVR_DIGISPARKPRO)
# if (IR_INPUT_PIN == 3)
// clear interrupt bit
EIFR |= 1 << INTF0;
// disable interrupt on next change
EIMSK &= ~( 1 << INT0);
# elif (IR_INPUT_PIN == 9)
// clear interrupt bit
EIFR |= 1 << INTF1;
// disable interrupt on next change
EIMSK &= ~(1 << INT1);
# else
# error "IR_INPUT_PIN must be 9 or 3."
# endif // if (IR_INPUT_PIN == 9)
# else // defined(ARDUINO_AVR_DIGISPARKPRO)
# if (IR_INPUT_PIN == 14)
// clear interrupt bit
EIFR |= 1 << INTF0;
// disable interrupt on next change
EIMSK &= ~(1 << INT0);
# elif (IR_INPUT_PIN == 3)
// clear interrupt bit
EIFR |= 1 << INTF1;
// disable interrupt on next change
EIMSK &= ~(1 << INT1);
# else
# error "IR_INPUT_PIN must be 14 or 3."
# endif // if (IR_INPUT_PIN == 14)
# endif
# else // defined(__AVR_ATtiny25__)
/*
* ATmegas + ATtiny88 here
*/
# if (IR_INPUT_PIN == 2)
// clear interrupt bit
EIFR |= 1 << INTF0;
// disable interrupt on next change
EIMSK &= ~(1 << INT0);
# elif (IR_INPUT_PIN == 3)
// clear interrupt bit
EIFR |= 1 << INTF1;
// disable interrupt on next change
EIMSK &= ~(1 << INT1);
# elif IR_INPUT_PIN == 4 || IR_INPUT_PIN == 5 || IR_INPUT_PIN == 6 || IR_INPUT_PIN == 7
//ATmega328 (Uno, Nano ) etc. disable pin change interrupt 20 to 23 for port PD4 to PD7 (Arduino pin 4 to 7)
PCICR &= ~(_BV(PCIE2));
# elif IR_INPUT_PIN == 8 || IR_INPUT_PIN == 9 || IR_INPUT_PIN == 10 || IR_INPUT_PIN == 11 || IR_INPUT_PIN == 12 || IR_INPUT_PIN == 13
//ATmega328 (Uno, Nano ) etc. disable pin change interrupt 0 to 5 for port PB0 to PB5 (Arduino pin 8 to 13)
PCICR &= ~(_BV(PCIE0));
# elif IR_INPUT_PIN == A0 || IR_INPUT_PIN == A1 || IR_INPUT_PIN == A2 || IR_INPUT_PIN == A3 || IR_INPUT_PIN == A4 || IR_INPUT_PIN == A5
//ATmega328 (Uno, Nano ) etc. disable pin change interrupt 8 to 13 for port PC0 to PC5 (Arduino pin A0 to A5)
PCICR &= ~(_BV(PCIE1));
# else
# error "IR_INPUT_PIN not allowed."
# endif // if (IR_INPUT_PIN == 2)
# endif // defined(__AVR_ATtiny25__)
#endif // ! defined(__AVR__) || defined(TINY_RECEIVER_USE_ARDUINO_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(__AVR__) && !defined(TINY_RECEIVER_USE_ARDUINO_ATTACH_INTERRUPT)
# if (IR_INPUT_PIN == 2)
ISR(INT0_vect) // Pin 2 global assignment
# elif (IR_INPUT_PIN == 3)
# if defined(ARDUINO_AVR_DIGISPARKPRO)
ISR(INT0_vect) // Pin 3 / PB6 / INT0 is connected to USB+ on DigisparkPro boards
# else
ISR(INT1_vect) // Pin 3 global assignment
# endif
# elif (IR_INPUT_PIN == 9) && defined(ARDUINO_AVR_DIGISPARKPRO) // Digispark pro
ISR(INT1_vect)
# elif (IR_INPUT_PIN == 14) && (defined(__AVR_ATtiny87__) || defined(__AVR_ATtiny167__))// For AVR_ATtiny167 INT0 is on pin 14 / PB6
ISR(INT0_vect)
# elif (! defined(ISC10)) || ((defined(__AVR_ATtiny87__) || defined(__AVR_ATtiny167__)) && INT1_PIN != 3)
// on ATtinyX5 we do not have a INT1_vect but we can use the PCINT0_vect
ISR(PCINT0_vect)
# elif IR_INPUT_PIN == 4 || IR_INPUT_PIN == 5 || IR_INPUT_PIN == 6 || IR_INPUT_PIN == 7
// PCINT for ATmega328 Arduino pins 4 (PD4) to 7 (PD7) - (PCINT 20 to 23)
ISR(PCINT2_vect)
# elif IR_INPUT_PIN == 8 || IR_INPUT_PIN == 9 || IR_INPUT_PIN == 10 || IR_INPUT_PIN == 11 || IR_INPUT_PIN == 12 || IR_INPUT_PIN == 13
// PCINT for ATmega328 Arduino pins 8 (PB0) to 13 (PB5) - (PCINT 0 to 5)
ISR(PCINT0_vect)
# elif IR_INPUT_PIN == A0 || IR_INPUT_PIN == A1 || IR_INPUT_PIN == A2 || IR_INPUT_PIN == A3 || IR_INPUT_PIN == A4 || IR_INPUT_PIN == A5
// PCINT for ATmega328 Arduino pins A1 (PC0) to A5 (PC5) - (PCINT 8 to 13)
ISR(PCINT1_vect)
# endif
{
IRPinChangeInterruptHandler();
}
#endif // defined(__AVR__) && ! defined(TINY_RECEIVER_USE_ARDUINO_ATTACH_INTERRUPT)
/** @}*/
#endif // _TINY_IR_RECEIVER_HPP

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

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.sendLG((uint8_t) LG_ADDRESS, aCommand, 0, false, useLG2Protocol);
}
/*
* 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

View File

@@ -0,0 +1,492 @@
/*
Optimized digital functions for AVR microcontrollers
by Watterott electronic (www.watterott.com)
based on http://code.google.com/p/digitalwritefast
*/
#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
#if !defined(SWAP)
# define SWAP(x,y) do{ (x)=(x)^(y); (y)=(x)^(y); (x)=(x)^(y); }while(0)
#endif
#if !defined(DEC)
# define DEC (10)
#endif
#if !defined(HEX)
# define HEX (16)
#endif
#if !defined(OCT)
# define OCT (8)
#endif
#if !defined(BIN)
# define BIN (2)
#endif
// workarounds for ARM microcontrollers
#if (!defined(__AVR__) || \
defined(ARDUINO_ARCH_SAM) || \
defined(ARDUINO_ARCH_SAMD))
#if !defined(PROGMEM)
# define PROGMEM
#endif
#if !defined(PGM_P)
# define PGM_P const char *
#endif
#if !defined(PSTR)
# define PSTR(str) (str)
#endif
#if !defined(memcpy_P)
# define memcpy_P(dest, src, num) memcpy((dest), (src), (num))
#endif
#if !defined(strcpy_P)
# define strcpy_P(dst, src) strcpy((dst), (src))
#endif
#if !defined(strcat_P)
# define strcat_P(dst, src) strcat((dst), (src))
#endif
#if !defined(strcmp_P)
# define strcmp_P(a, b) strcmp((a), (b))
#endif
#if !defined(strcasecmp_P)
# define strcasecmp_P(a, b) strcasecmp((a), (b))
#endif
#if !defined(strncmp_P)
# define strncmp_P(a, b, n) strncmp((a), (b), (n))
#endif
#if !defined(strncasecmp_P)
# define strncasecmp_P(a, b, n) strncasecmp((a), (b), (n))
#endif
#if !defined(strstr_P)
# define strstr_P(a, b) strstr((a), (b))
#endif
#if !defined(strlen_P)
# define strlen_P(a) strlen((a))
#endif
#if !defined(sprintf_P)
# define sprintf_P(s, f, ...) sprintf((s), (f), __VA_ARGS__)
#endif
#if !defined(pgm_read_byte)
# define pgm_read_byte(addr) (*(const unsigned char *)(addr))
#endif
#if !defined(pgm_read_word)
# define pgm_read_word(addr) (*(const unsigned short *)(addr))
#endif
#if !defined(pgm_read_dword)
# define pgm_read_dword(addr) (*(const unsigned long *)(addr))
#endif
#endif
// digital functions
//#if !defined(digitalPinToPortReg)
#define SPI_SW_SS_PIN (10) //SS on Uno (for software SPI)
#define SPI_SW_MOSI_PIN (11) //MOSI on Uno (for software SPI)
#define SPI_SW_MISO_PIN (12) //MISO on Uno (for software SPI)
#define SPI_SW_SCK_PIN (13) //SCK on Uno (for software SPI)
// --- Arduino Due and SAM3X8E based boards ---
#if (defined(ARDUINO_SAM_DUE) || \
defined(__SAM3X8E__))
#define UART_RX_PIN (0)
#define UART_TX_PIN (1)
#define I2C_SDA_PIN (20)
#define I2C_SCL_PIN (21)
#define SPI_HW_SS_PIN (78) //SS0:77, SS1:87, SS2:86, SS3:78
#define SPI_HW_MOSI_PIN (75) //75
#define SPI_HW_MISO_PIN (74) //74
#define SPI_HW_SCK_PIN (76) //76
// --- Arduino Zero and SAMD21G18 based boards ---
#elif (defined(ARDUINO_SAMD_ZERO) || \
defined(__SAMD21G18A__))
#define UART_RX_PIN (0)
#define UART_TX_PIN (1)
#define I2C_SDA_PIN (16)
#define I2C_SCL_PIN (17)
#define SPI_HW_SS_PIN (14) //14
#define SPI_HW_MOSI_PIN (21) //21
#define SPI_HW_MISO_PIN (18) //18
#define SPI_HW_SCK_PIN (20) //20
// --- Arduino Mega and ATmega128x/256x based boards ---
#elif (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 UART_RX_PIN (0) //PE0
#define UART_TX_PIN (1) //PE1
#define I2C_SDA_PIN (20)
#define I2C_SCL_PIN (21)
#define SPI_HW_SS_PIN (53) //PB0
#define SPI_HW_MOSI_PIN (51) //PB2
#define SPI_HW_MISO_PIN (50) //PB3
#define SPI_HW_SCK_PIN (52) //PB1
#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)
#define UART_RX_PIN (8) //PD0
#define UART_TX_PIN (9) //PD1
#define I2C_SDA_PIN (17) //PC1
#define I2C_SCL_PIN (16) //PC0
#define SPI_HW_SS_PIN (4) //PB4
#define SPI_HW_MOSI_PIN (5) //PB5
#define SPI_HW_MISO_PIN (6) //PB6
#define SPI_HW_SCK_PIN (7) //PB7
#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
#else
#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
#endif
// --- Arduino Leonardo and ATmega16U4/32U4 based boards ---
#elif (defined(ARDUINO_AVR_LEONARDO) || \
defined(__AVR_ATmega16U4__) || \
defined(__AVR_ATmega32U4__))
#define UART_RX_PIN (0) //PD2
#define UART_TX_PIN (1) //PD3
#define I2C_SDA_PIN (2) //PD1
#define I2C_SCL_PIN (3) //PD0
#define SPI_HW_SS_PIN (17) //PB0
#define SPI_HW_MOSI_PIN (16) //PB2
#define SPI_HW_MISO_PIN (14) //PB3
#define SPI_HW_SCK_PIN (15) //PB1
#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 )))))))))))))))))))
// --- 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__))
#define UART_RX_PIN (0) //PD0
#define UART_TX_PIN (1) //PD1
#define I2C_SDA_PIN (18) //A4
#define I2C_SCL_PIN (19) //A5
#define SPI_HW_SS_PIN (10) //PB0
#define SPI_HW_MOSI_PIN (11) //PB2
#define SPI_HW_MISO_PIN (12) //PB3
#define SPI_HW_SCK_PIN (13) //PB1
#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 UART_RX_PIN (0) //PB0
#define UART_TX_PIN (1) //PB1
#define I2C_SDA_PIN (22) //PA2
#define I2C_SCL_PIN (23) //PA3
#define SPI_HW_SS_PIN (8) //PE3
#define SPI_HW_MOSI_PIN (11) //PE0
#define SPI_HW_MISO_PIN (12) //PE1
#define SPI_HW_SCK_PIN (13) //PE2
#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))))) )
// --- 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_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)
#define __digitalPinToBit(P) (((P) <= 7) ? (P) : (P) - 8 )
# endif
// --- Other ---
#else
#define I2C_SDA_PIN SDA
#define I2C_SCL_PIN SCL
#define SPI_HW_SS_PIN SS
#define SPI_HW_MOSI_PIN MOSI
#define SPI_HW_MISO_PIN MISO
#define SPI_HW_SCK_PIN SCK
#endif
//#endif //#if !defined(digitalPinToPortReg)
#if !defined(digitalWriteFast)
#if (defined(__AVR__) || defined(ARDUINO_ARCH_AVR)) && defined(__digitalPinToPortReg)
#define digitalWriteFast(P, V) \
if (__builtin_constant_p(P)) { \
BIT_WRITE(*__digitalPinToPortReg(P), __digitalPinToBit(P), (V)); \
} else { \
digitalWrite((P), (V)); \
}
#else
#define digitalWriteFast digitalWrite
#endif
#endif
#if !defined(pinModeFast)
#if (defined(__AVR__) || defined(ARDUINO_ARCH_AVR)) && defined(__digitalPinToPortReg)
#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)); \
}
#else
#define pinModeFast pinMode
#endif
#endif // !defined(pinModeFast)
#if !defined(digitalReadFast)
#if (defined(__AVR__) || defined(ARDUINO_ARCH_AVR))
#define digitalReadFast(P) ( (int) __digitalReadFast((P)) )
#define __digitalReadFast(P ) \
(__builtin_constant_p(P) ) ? \
(( BIT_READ(*__digitalPinToPINReg(P), __digitalPinToBit(P))) ? HIGH:LOW ) : \
digitalRead((P))
#else
#define digitalReadFast digitalRead
#endif
#endif // !defined(digitalReadFast)
#if !defined(digitalToggleFast)
#if (defined(__AVR__) || defined(ARDUINO_ARCH_AVR))
#define digitalToggleFast(P) \
if (__builtin_constant_p(P)) { \
BIT_SET(*__digitalPinToPINReg(P), __digitalPinToBit(P)); \
} else { \
digitalWrite(P, ! digitalRead(P)); \
}
#else
#define digitalToggleFast(P) digitalWrite(P, ! digitalRead(P))
#endif
#endif // !defined(digitalToggleFast)
#endif //__digitalWriteFast_h_

View File

@@ -0,0 +1,137 @@
/*
* 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
#include <Arduino.h>
//#define DEBUG // Activate this for lots of lovely debug output from this decoder.
#include "IRremoteInt.h" // evaluates the DEBUG for IR_DEBUG_PRINT
/** \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 trusty 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, etc, etc.
// 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_SPACE 50000
//+=============================================================================
void IRsend::sendBoseWave(uint8_t aCommand, uint_fast8_t aNumberOfRepeats) {
// Set IR carrier frequency
enableIROut(BOSEWAVE_KHZ); // 38 kHz
uint_fast8_t tNumberOfCommands = aNumberOfRepeats + 1;
while (tNumberOfCommands > 0) {
// Header
mark(BOSEWAVE_HEADER_MARK);
space(BOSEWAVE_HEADER_SPACE);
// send 8 command bits and then 8 inverted command bits LSB first
uint16_t tData = ((~aCommand) << 8) | aCommand;
sendPulseDistanceWidthData(BOSEWAVE_BIT_MARK, BOSEWAVE_ONE_SPACE, BOSEWAVE_BIT_MARK, BOSEWAVE_ZERO_SPACE, tData,
BOSEWAVE_BITS, PROTOCOL_IS_LSB_FIRST, SEND_STOP_BIT);
tNumberOfCommands--;
// skip last delay!
if (tNumberOfCommands > 0) {
// send repeated command with a fixed space gap
delay( BOSEWAVE_REPEAT_SPACE / MICROS_IN_ONE_MILLI);
}
}
IrReceiver.restartAfterSend();
}
//+=============================================================================
bool IRrecv::decodeBoseWave() {
// Check header "mark"
if (!matchMark(decodedIRData.rawDataPtr->rawbuf[1], BOSEWAVE_HEADER_MARK)) {
// no debug output, since this check is mainly to determine the received protocol
return false;
}
// Check we have enough data +4 for initial gap, start bit mark and space + stop bit mark
if (decodedIRData.rawDataPtr->rawlen != (2 * BOSEWAVE_BITS) + 4) {
IR_DEBUG_PRINT(F("Bose: "));
IR_DEBUG_PRINT(F("Data length="));
IR_DEBUG_PRINT(decodedIRData.rawDataPtr->rawlen);
IR_DEBUG_PRINTLN(F(" is not 36"));
return false;
}
// Check header "space"
if (!matchSpace(decodedIRData.rawDataPtr->rawbuf[2], BOSEWAVE_HEADER_SPACE)) {
IR_DEBUG_PRINT(F("Bose: "));
IR_DEBUG_PRINTLN(F("Header space length is wrong"));
return false;
}
if (!decodePulseDistanceData(BOSEWAVE_BITS, 3, BOSEWAVE_BIT_MARK, BOSEWAVE_ONE_SPACE, BOSEWAVE_ZERO_SPACE,
PROTOCOL_IS_LSB_FIRST)) {
IR_DEBUG_PRINT(F("Bose: "));
IR_DEBUG_PRINTLN(F("Decode failed"));
return false;
}
// Stop bit
if (!matchMark(decodedIRData.rawDataPtr->rawbuf[3 + (2 * BOSEWAVE_BITS)], BOSEWAVE_BIT_MARK)) {
IR_DEBUG_PRINT(F("Bose: "));
IR_DEBUG_PRINTLN(F("Stop bit mark length is wrong"));
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) {
IR_DEBUG_PRINT(F("Bose: "));
IR_DEBUG_PRINT(F("Command and inverted command check failed"));
return false;
}
// check for repeat
if (decodedIRData.rawDataPtr->rawbuf[0] < ((BOSEWAVE_REPEAT_SPACE + (BOSEWAVE_REPEAT_SPACE / 4)) / MICROS_PER_TICK)) {
decodedIRData.flags = IRDATA_FLAGS_IS_REPEAT | IRDATA_FLAGS_IS_LSB_FIRST;
}
decodedIRData.command = tCommandNotInverted;
decodedIRData.protocol = BOSEWAVE;
decodedIRData.numberOfBits = BOSEWAVE_BITS;
return true;
}
/** @}*/
#endif // _IR_BOSEWAVE_HPP

View File

@@ -0,0 +1,253 @@
/*
* 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-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.
*
************************************************************************************
*/
#ifndef _IR_DENON_HPP
#define _IR_DENON_HPP
#include <Arduino.h>
//#define DEBUG // Activate this for lots of lovely debug output from this decoder.
#include "IRremoteInt.h" // evaluates the DEBUG for IR_DEBUG_PRINT
/** \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
//==============================================================================
// Denon publish all their IR codes:
// https://www.google.co.uk/search?q=DENON+MASTER+IR+Hex+Command+Sheet
// -> http://assets.denon.com/documentmaster/us/denon%20master%20ir%20hex.xls
// Having looked at the official Denon Pronto sheet and reverse engineered
// the timing values from it, it is obvious that Denon have a range of
// different timings and protocols ...the values here work for my AVR-3801 Amp!
// MSB first, no start bit, 5 address + 8 command + 2 frame + 1 stop bit - each frame 2 times
//
#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_SPACE 45000 // Every frame is auto repeated with a space period of 45 ms and the command 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
//+=============================================================================
void IRsend::sendSharp(uint8_t aAddress, uint8_t aCommand, uint_fast8_t aNumberOfRepeats) {
sendDenon(aAddress, aCommand, aNumberOfRepeats, true);
}
/*
* Only for backwards compatibility
*/
void IRsend::sendDenonRaw(uint16_t aRawData, uint_fast8_t aNumberOfRepeats) {
sendDenon(aRawData >> (DENON_COMMAND_BITS + DENON_FRAME_BITS), aRawData & 0xFF, aNumberOfRepeats);
}
//+=============================================================================
void IRsend::sendDenon(uint8_t aAddress, uint8_t aCommand, uint_fast8_t aNumberOfRepeats, bool aSendSharp) {
// Set IR carrier frequency
enableIROut(DENON_KHZ); // 38 kHz
// Shift command and add frame marker
uint16_t tCommand = aCommand << DENON_FRAME_BITS; // the lowest bits are 00 for Denon and 10 for Sharp
if (aSendSharp) {
tCommand |= 0x02;
}
uint16_t tData = tCommand | ((uint16_t) aAddress << (DENON_COMMAND_BITS + DENON_FRAME_BITS));
uint16_t tInvertedData = ((~tCommand) & 0x3FF) | (uint16_t) aAddress << (DENON_COMMAND_BITS + DENON_FRAME_BITS);
uint_fast8_t tNumberOfCommands = aNumberOfRepeats + 1;
while (tNumberOfCommands > 0) {
// Data
sendPulseDistanceWidthData(DENON_BIT_MARK, DENON_ONE_SPACE, DENON_BIT_MARK, DENON_ZERO_SPACE, tData, DENON_BITS,
PROTOCOL_IS_MSB_FIRST,
SEND_STOP_BIT);
// Inverted autorepeat frame
delay(DENON_AUTO_REPEAT_SPACE / MICROS_IN_ONE_MILLI);
sendPulseDistanceWidthData(DENON_BIT_MARK, DENON_ONE_SPACE, DENON_BIT_MARK, DENON_ZERO_SPACE, tInvertedData, DENON_BITS,
PROTOCOL_IS_MSB_FIRST, SEND_STOP_BIT);
tNumberOfCommands--;
// skip last delay!
if (tNumberOfCommands > 0) {
// send repeated command with a fixed space gap
delay( DENON_AUTO_REPEAT_SPACE / MICROS_IN_ONE_MILLI);
}
}
IrReceiver.restartAfterSend();
}
//+=============================================================================
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.rawDataPtr->rawlen != (2 * DENON_BITS) + 2) {
IR_DEBUG_PRINT(F("Denon: "));
IR_DEBUG_PRINT(F("Data length="));
IR_DEBUG_PRINT(decodedIRData.rawDataPtr->rawlen);
IR_DEBUG_PRINTLN(F(" is not 32"));
return false;
}
// Read the bits in
if (!decodePulseDistanceData(DENON_BITS, 1, DENON_BIT_MARK, DENON_ONE_SPACE, DENON_ZERO_SPACE, PROTOCOL_IS_MSB_FIRST)) {
IR_DEBUG_PRINT(F("Denon: "));
IR_DEBUG_PRINTLN(F("Decode failed"));
return false;
}
// Check for stop mark
if (!matchMark(decodedIRData.rawDataPtr->rawbuf[(2 * DENON_BITS) + 1], DENON_HEADER_MARK)) {
IR_DEBUG_PRINT(F("Denon: "));
IR_DEBUG_PRINTLN(F("Stop bit mark length is wrong"));
return false;
}
// Success
decodedIRData.flags = IRDATA_FLAGS_IS_MSB_FIRST;
uint8_t tFrameBits = decodedIRData.decodedRawData & 0x03;
decodedIRData.command = decodedIRData.decodedRawData >> DENON_FRAME_BITS;
decodedIRData.address = decodedIRData.command >> DENON_COMMAND_BITS;
uint8_t tCommand = decodedIRData.command & 0xFF;
decodedIRData.command = tCommand;
// check for autorepeated inverted command
if (decodedIRData.rawDataPtr->rawbuf[0] < ((DENON_AUTO_REPEAT_SPACE + (DENON_AUTO_REPEAT_SPACE / 4)) / MICROS_PER_TICK)) {
repeatCount++;
if (tFrameBits == 0x3 || tFrameBits == 0x1) {
// We are in the auto repeated frame with the inverted command
decodedIRData.flags = IRDATA_FLAGS_IS_AUTO_REPEAT | IRDATA_FLAGS_IS_MSB_FIRST;
// Check parity of consecutive received commands. There is no parity in one data set.
uint8_t tLastCommand = lastDecodedCommand;
if (tLastCommand != (uint8_t) (~tCommand)) {
decodedIRData.flags |= IRDATA_FLAGS_PARITY_FAILED;
}
// always take non inverted command
decodedIRData.command = tLastCommand;
}
if (repeatCount > 1) {
decodedIRData.flags |= IRDATA_FLAGS_IS_REPEAT;
}
} else {
repeatCount = 0;
}
decodedIRData.numberOfBits = DENON_BITS;
if (tFrameBits == 1 || tFrameBits == 2) {
decodedIRData.protocol = SHARP;
} else {
decodedIRData.protocol = DENON;
}
return true;
}
bool IRrecv::decodeDenonOld(decode_results *aResults) {
// Check we have the right amount of data
if (decodedIRData.rawDataPtr->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;
}
// Read the bits in
if (!decodePulseDistanceData(DENON_BITS, 3, DENON_BIT_MARK, DENON_ONE_SPACE, DENON_ZERO_SPACE, PROTOCOL_IS_MSB_FIRST)) {
return false;
}
// Success
aResults->value = decodedIRData.decodedRawData;
aResults->bits = DENON_BITS;
aResults->decode_type = DENON;
decodedIRData.protocol = DENON;
return true;
}
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,
SEND_STOP_BIT);
IrReceiver.restartAfterSend();
}
void IRsend::sendSharp(unsigned int aAddress, unsigned int aCommand) {
sendDenon(aAddress, aCommand, true, 0);
}
/** @}*/
#endif // _IR_DENON_HPP

View File

@@ -0,0 +1,46 @@
#include "IRremoteInt.h"
//==============================================================================
// 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
//==============================================================================
// Sharp and DISH support by Todd Treece ( http://unionbridge.org/design/ircommand )
//
// 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
#ifndef _IR_DISH_HPP
#define _IR_DISH_HPP
#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
//+=============================================================================
void IRsend::sendDISH(unsigned long data, int nbits) {
// Set IR carrier frequency
enableIROut(56);
mark(DISH_HEADER_MARK);
space(DISH_HEADER_SPACE);
sendPulseDistanceWidthData(DISH_BIT_MARK, DISH_ONE_SPACE, DISH_BIT_MARK, DISH_ZERO_SPACE, data, nbits, PROTOCOL_IS_MSB_FIRST);
mark(DISH_HEADER_MARK); //added 26th March 2016, by AnalysIR ( https://www.AnalysIR.com )
IrReceiver.restartAfterSend();
}
#endif // _IR_DISH_HPP

View File

@@ -0,0 +1,372 @@
/*
* ir_DistanceProtocol.hpp
*
* This decoder tries to decode a pulse width or pulse distance 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.
*
* Pulse distance data can be sent with the generic function:
* 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);
* see also: SendDemo example line 150
*
* 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_DISTANCE_HPP
#define _IR_DISTANCE_HPP
#include <Arduino.h>
// accept durations up to 50 * 50 (MICROS_PER_TICK) 2500 microseconds
#define DURATION_ARRAY_SIZE 50
// Switch the decoding according to your needs
#define DISTANCE_DO_MSB_DECODING PROTOCOL_IS_LSB_FIRST // this results in the same decodedRawData as e.g. the NEC and Kaseikyo/Panasonic decoder
//#define DISTANCE_DO_MSB_DECODING PROTOCOL_IS_MSB_FIRST // this resembles the JVC, Denon
#if defined(INFO) && !defined(LOCAL_INFO)
#define LOCAL_INFO
#else
#define LOCAL_INFO // This enables info output only for this file
#endif
//#define DEBUG // Activate this for lots of lovely debug output from this decoder.
#include "IRremoteInt.h" // evaluates the DEBUG for IR_DEBUG_PRINT
//#include "LongUnion.h"
/** \addtogroup Decoder Decoders and encoders for different protocols
* @{
*/
// see: https://www.mikrocontroller.net/articles/IRMP_-_english#Codings
#if defined(DEBUG)
void printDurations(uint8_t aArray[], uint8_t aMaxIndex) {
for (uint_fast8_t i = 0; i <= aMaxIndex; i++) {
if (i % 10 == 0) {
if (i == 0) {
Serial.print(' '); // indentation for the 0
} else {
Serial.println();
}
Serial.print(i);
Serial.print(F(":"));
}
Serial.print(F(" | "));
Serial.print(aArray[i]);
}
Serial.println();
}
#endif
/*
* @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;
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;
}
if ((tCurrentDurations == 0 || i == aMaxIndex) && tSum != 0) {
// here we have a sum and a gap after the values
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 width or pulse distance 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.
*/
bool IRrecv::decodeDistance() {
uint8_t tDurationArray[DURATION_ARRAY_SIZE];
/*
* Accept only protocols with at least 8 bits
*/
if (decodedIRData.rawDataPtr->rawlen < (2 * 8) + 4) {
IR_DEBUG_PRINT(F("PULSE_DISTANCE: "));
IR_DEBUG_PRINT(F("Data length="));
IR_DEBUG_PRINT(decodedIRData.rawDataPtr->rawlen);
IR_DEBUG_PRINTLN(F(" is less than 20"));
return false;
}
uint_fast8_t i;
// Reset duration array
memset(tDurationArray, 0, sizeof(tDurationArray));
uint8_t tMaxDurationIndex = 0;
/*
* Count number of mark durations. Skip leading start and trailing stop bit.
*/
for (i = 3; i < (uint_fast8_t) decodedIRData.rawDataPtr->rawlen - 2; i += 2) {
uint8_t tDurationTicks = decodedIRData.rawDataPtr->rawbuf[i];
if (tDurationTicks < sizeof(tDurationArray)) {
tDurationArray[tDurationTicks]++;
if (tMaxDurationIndex < tDurationTicks) {
tMaxDurationIndex = tDurationTicks;
}
}
}
/*
* Aggregate mark counts to one duration bin
*/
uint8_t tMarkTicksShort = 0;
uint8_t tMarkTicksLong = 0;
bool tSuccess = aggregateArrayCounts(tDurationArray, tMaxDurationIndex, &tMarkTicksShort, &tMarkTicksLong);
#if defined(DEBUG)
Serial.println(F("Mark:"));
printDurations(tDurationArray, tMaxDurationIndex);
#endif
if (!tSuccess) {
IR_DEBUG_PRINT(F("PULSE_DISTANCE: "));
IR_DEBUG_PRINTLN(F("Mark aggregation failed, more than 2 distinct mark duration values found"));
}
// Reset duration array
memset(tDurationArray, 0, sizeof(tDurationArray));
/*
* Count number of space durations. Skip leading start and trailing stop bit.
*/
tMaxDurationIndex = 0;
for (i = 4; i < (uint_fast8_t) decodedIRData.rawDataPtr->rawlen - 2; i += 2) {
uint8_t tDurationTicks = decodedIRData.rawDataPtr->rawbuf[i];
if (tDurationTicks < sizeof(tDurationArray)) {
tDurationArray[tDurationTicks]++;
if (tMaxDurationIndex < tDurationTicks) {
tMaxDurationIndex = tDurationTicks;
}
}
}
/*
* Aggregate space counts to one duration bin
*/
uint8_t tSpaceTicksShort = 0;
uint8_t tSpaceTicksLong = 0;
tSuccess = aggregateArrayCounts(tDurationArray, tMaxDurationIndex, &tSpaceTicksShort, &tSpaceTicksLong);
#if defined(DEBUG)
Serial.println(F("Space:"));
printDurations(tDurationArray, tMaxDurationIndex);
#endif
if (!tSuccess) {
IR_DEBUG_PRINT(F("PULSE_DISTANCE: "));
IR_DEBUG_PRINTLN(F("Space aggregation failed, more than 2 distinct space duration values found"));
return false;
}
// skip leading start and trailing stop bit.
uint16_t tNumberOfBits = (decodedIRData.rawDataPtr->rawlen / 2) - 2;
decodedIRData.numberOfBits = tNumberOfBits;
/*
* Print characteristics of this protocol. Durations are in ticks.
* Number of bits, start bit, start pause, short mark, long mark, short space, long space
*
* NEC: 32, 180, 90, 11, 0, 11, 34
* Samsung32: 32, 90, 90, 11, 0, 11, 34
* LG: 28, 180, 84, 10, 0, 11, 32
* JVC: 16, 168, 84, 10, 0, 10, 32
* Kaseikyo: 48. 69, 35, 9, 0, 9, 26
* Sony: 12|15|20, 48, 12, 12, 24, 12, 0 // the only known pulse width protocol
*/
IR_DEBUG_PRINT(F("Protocol characteristics for a " STR(MICROS_PER_TICK) " us tick: "));
IR_DEBUG_PRINT(decodedIRData.numberOfBits);
IR_DEBUG_PRINT(F(", "));
IR_DEBUG_PRINT(decodedIRData.rawDataPtr->rawbuf[1]);
IR_DEBUG_PRINT(F(", "));
IR_DEBUG_PRINT(decodedIRData.rawDataPtr->rawbuf[2]);
IR_DEBUG_PRINT(F(", "));
IR_DEBUG_PRINT(tMarkTicksShort);
IR_DEBUG_PRINT(F(", "));
IR_DEBUG_PRINT(tMarkTicksLong);
IR_DEBUG_PRINT(F(", "));
IR_DEBUG_PRINT(tSpaceTicksShort);
IR_DEBUG_PRINT(F(", "));
IR_DEBUG_PRINTLN(tSpaceTicksLong);
uint8_t tStartIndex = 3;
uint8_t tNumberOfAdditionalLong = (tNumberOfBits - 1) / 32;
/*
* decide, if we have an pulse width or distance protocol
*/
if (tSpaceTicksLong > 0) {
// // check if last bit can be decoded as data or not, in this case take it as a stop bit
// if (decodePulseDistanceData(1, decodedIRData.rawDataPtr->rawlen - 3, tMarkTicksShort * MICROS_PER_TICK,
// tSpaceTicksLong * MICROS_PER_TICK, tSpaceTicksShort * MICROS_PER_TICK, DISTANCE_DO_MSB_DECODING)) {
// Serial.print(F("tNumberOfBits++ "));
// tNumberOfBits++;
// }
/*
* Here short and long space duration found. Decode in 32 bit chunks.
*/
for (uint_fast8_t i = 0; i <= tNumberOfAdditionalLong; ++i) {
uint8_t tNumberOfBitsForOneDecode = tNumberOfBits;
if (tNumberOfBitsForOneDecode > 32) {
tNumberOfBitsForOneDecode = 32;
}
if (!decodePulseDistanceData(tNumberOfBitsForOneDecode, tStartIndex, tMarkTicksShort * MICROS_PER_TICK,
tSpaceTicksLong * MICROS_PER_TICK, tSpaceTicksShort * MICROS_PER_TICK, DISTANCE_DO_MSB_DECODING)) {
IR_DEBUG_PRINT(F("PULSE_DISTANCE: "));
IR_DEBUG_PRINTLN(F("Decode failed"));
return false;
} else {
#if defined(LOCAL_INFO)
/*
* Print usage :-)
*/
if (i == 0) {
// Print this only once
Serial.println();
Serial.println(F("PULSE_DISTANCE: Send with:"));
Serial.println(F(" IrSender.enableIROut(38);"));
Serial.print(F(" IrSender.mark("));
Serial.print(decodedIRData.rawDataPtr->rawbuf[1] * MICROS_PER_TICK);
Serial.println(F(");"));
Serial.print(F(" IrSender.space("));
Serial.print(decodedIRData.rawDataPtr->rawbuf[2] * MICROS_PER_TICK);
Serial.println(F(");"));
}
Serial.print(F(" IrSender.sendPulseDistanceWidthData("));
Serial.print(tMarkTicksShort * MICROS_PER_TICK); // aOneMarkMicros
Serial.print(F(", "));
Serial.print(tSpaceTicksLong * MICROS_PER_TICK); // aOneSpaceMicros
Serial.print(F(", "));
Serial.print(tMarkTicksShort * MICROS_PER_TICK); // aZeroMarkMicros
Serial.print(F(", "));
Serial.print(tSpaceTicksShort * MICROS_PER_TICK); // aZeroSpaceMicros
Serial.print(F(", 0x"));
Serial.print(decodedIRData.decodedRawData, HEX); // aData
if (tNumberOfBits < 32) {
Serial.print(F(", "));
Serial.print(tNumberOfBits); // aNumberOfBits
} else {
Serial.print(F(", 32"));
}
if (DISTANCE_DO_MSB_DECODING) {
Serial.print(F(", true, ")); // aMSBfirst
} else {
Serial.print(F(", false, ")); // aMSBfirst
}
if (i == tNumberOfAdditionalLong) {
Serial.println(F("true);")); // aSendStopBit - true for last data set
Serial.println();
} else {
Serial.println(F("false);"));// aSendStopBit
}
#endif
tStartIndex += 64;
tNumberOfBits -= 32;
}
}
// Store ticks used for decoding in extra
decodedIRData.extra = (tSpaceTicksShort << 8) | tSpaceTicksLong;
decodedIRData.protocol = PULSE_DISTANCE;
} else {
if (tMarkTicksLong == 0) {
IR_DEBUG_PRINT(F("PULSE_DISTANCE: "));
IR_DEBUG_PRINTLN(F("Only 1 distinct duration value for each space and mark found"));
return false;
}
//#define SUPPORT_PULSE_WIDTH_DECODING
#if defined(SUPPORT_PULSE_WIDTH_DECODING) // The only known pulse width protocol is Sony
// // check if last bit can be decoded as data or not, in this case take it as a stop bit
// if (decodePulseWidthData(1, decodedIRData.rawDataPtr->rawlen - 3, tMarkTicksLong * MICROS_PER_TICK,
// tMarkTicksShort * MICROS_PER_TICK, tSpaceTicksShort * MICROS_PER_TICK, DISTANCE_DO_MSB_DECODING)) {
// tNumberOfBits++;
// }
// decode without leading start bit. Currently only seen for sony protocol
for (uint_fast8_t i = 0; i <= tNumberOfAdditionalLong; ++i) {
uint8_t tNumberOfBitsForOneDecode = tNumberOfBits;
if (tNumberOfBitsForOneDecode > 32) {
tNumberOfBitsForOneDecode = 32;
}
if (!decodePulseWidthData(tNumberOfBitsForOneDecode, tStartIndex, tMarkTicksLong * MICROS_PER_TICK,
tMarkTicksShort * MICROS_PER_TICK, tSpaceTicksShort * MICROS_PER_TICK, DISTANCE_DO_MSB_DECODING)) {
IR_DEBUG_PRINT(F("PULSE_WIDTH: "));
IR_DEBUG_PRINTLN(F("Decode failed"));
return false;
}
tStartIndex += 64;
tNumberOfBits -= 32;
}
// Store ticks used for decoding in extra
decodedIRData.extra = (tMarkTicksShort << 8) | tMarkTicksLong;
decodedIRData.protocol = PULSE_WIDTH;
#endif
}
if (DISTANCE_DO_MSB_DECODING) {
decodedIRData.flags = IRDATA_FLAGS_IS_MSB_FIRST | IRDATA_FLAGS_EXTRA_INFO;
} else {
decodedIRData.flags = IRDATA_FLAGS_EXTRA_INFO;
}
return true;
}
/** @}*/
#endif // _IR_DISTANCE_HPP

View File

@@ -0,0 +1,242 @@
/*
* 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-2021 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
#include <Arduino.h>
//#define DEBUG // Activate this for lots of lovely debug output from this decoder.
#include "IRremoteInt.h" // evaluates the DEBUG for IR_DEBUG_PRINT
/** \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
//==============================================================================
// 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.
#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_SPACE (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.
//+=============================================================================
// JVC does NOT repeat by sending a separate code (like NEC does).
// The JVC protocol repeats by skipping the header.
//
void IRsend::sendJVC(uint8_t aAddress, uint8_t aCommand, uint_fast8_t aNumberOfRepeats) {
// Set IR carrier frequency
enableIROut(JVC_KHZ); // 38 kHz
// Header
mark(JVC_HEADER_MARK);
space(JVC_HEADER_SPACE);
uint_fast8_t tNumberOfCommands = aNumberOfRepeats + 1;
while (tNumberOfCommands > 0) {
// Address + command
sendPulseDistanceWidthData(JVC_BIT_MARK, JVC_ONE_SPACE, JVC_BIT_MARK, JVC_ZERO_SPACE,
aAddress | (aCommand << JVC_ADDRESS_BITS), JVC_BITS, PROTOCOL_IS_LSB_FIRST, SEND_STOP_BIT);
tNumberOfCommands--;
// skip last delay!
if (tNumberOfCommands > 0) {
// send repeated command in a fixed raster
delay(JVC_REPEAT_SPACE / MICROS_IN_ONE_MILLI);
}
}
IrReceiver.restartAfterSend();
}
/*
* First check for right data length
* Next check start bit
* Next try the decode
*/
bool IRrecv::decodeJVC() {
// 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. +2 is for repeats
if (decodedIRData.rawDataPtr->rawlen != ((2 * JVC_BITS) + 4) && decodedIRData.rawDataPtr->rawlen != ((2 * JVC_BITS) + 2)) {
IR_DEBUG_PRINT(F("JVC: "));
IR_DEBUG_PRINT(F("Data length="));
IR_DEBUG_PRINT(decodedIRData.rawDataPtr->rawlen);
IR_DEBUG_PRINTLN(F(" is not 34 or 36"));
return false;
}
if (decodedIRData.rawDataPtr->rawlen == ((2 * JVC_BITS) + 2)) {
/*
* Check for repeat
* Check leading space and first and last mark length
*/
if (decodedIRData.rawDataPtr->rawbuf[0] < ((JVC_REPEAT_SPACE + (JVC_REPEAT_SPACE / 4) / MICROS_PER_TICK))
&& matchMark(decodedIRData.rawDataPtr->rawbuf[1], JVC_BIT_MARK)
&& matchMark(decodedIRData.rawDataPtr->rawbuf[decodedIRData.rawDataPtr->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 {
// Check header "mark" and "space"
if (!matchMark(decodedIRData.rawDataPtr->rawbuf[1], JVC_HEADER_MARK)
|| !matchSpace(decodedIRData.rawDataPtr->rawbuf[2], JVC_HEADER_SPACE)) {
IR_DEBUG_PRINT(F("JVC: "));
IR_DEBUG_PRINTLN(F("Header mark or space length is wrong"));
return false;
}
if (!decodePulseDistanceData(JVC_BITS, 3, JVC_BIT_MARK, JVC_ONE_SPACE, JVC_ZERO_SPACE, PROTOCOL_IS_LSB_FIRST)) {
IR_DEBUG_PRINT(F("JVC: "));
IR_DEBUG_PRINTLN(F("Decode failed"));
return false;
}
// Success
// decodedIRData.flags = IRDATA_FLAGS_IS_LSB_FIRST; // Not required, since this is the start value
uint8_t tCommand = decodedIRData.decodedRawData >> JVC_ADDRESS_BITS; // upper 8 bits of LSB first value
uint8_t tAddress = decodedIRData.decodedRawData & 0xFF; // lowest 8 bit of LSB first value
decodedIRData.command = tCommand;
decodedIRData.address = tAddress;
decodedIRData.numberOfBits = JVC_BITS;
decodedIRData.protocol = JVC;
}
return true;
}
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 (!decodePulseDistanceData(JVC_BITS, offset, JVC_BIT_MARK, JVC_ONE_SPACE, JVC_ZERO_SPACE, PROTOCOL_IS_MSB_FIRST)) {
return false;
}
// Stop bit
if (!matchMark(aResults->rawbuf[offset + (2 * JVC_BITS)], JVC_BIT_MARK)) {
IR_DEBUG_PRINTLN(F("Stop bit mark length is wrong"));
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.
* 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,
SEND_STOP_BIT);
IrReceiver.restartAfterSend();
}
/** @}*/
#endif // _IR_JVC_HPP

View File

@@ -0,0 +1,336 @@
/*
* 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-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.
*
************************************************************************************
*/
#ifndef _IR_KASEIKYO_HPP
#define _IR_KASEIKYO_HPP
#include <Arduino.h>
//#define DEBUG // Activate this for lots of lovely debug output from this decoder.
#include "IRremoteInt.h" // evaluates the DEBUG for IR_DEBUG_PRINT
#include "LongUnion.h"
/** \addtogroup Decoder Decoders and encoders for different protocols
* @{
*/
//==============================================================================
// 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
//==============================================================================
// see: http://www.hifi-remote.com/johnsfine/DecodeIR.html#Panasonic and http://www.hifi-remote.com/johnsfine/DecodeIR.html#Kaseikyo
// see: http://www.remotecentral.com/cgi-bin/mboard/rc-pronto/thread.cgi?26152
// The first two (8-bit) bytes contains the vendor code.
// There are multiple interpretations of the next fields:
// 1. IRP notation: {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}
// 2. The next two bytes are 4 independent 4-bit fields or Device and Subdevice
// The second to last byte is the function and the last byte is xor of the three bytes before it.
// 0_______ 1_______ 2______ 3_______ 4_______ 5
// 76543210 76543210 76543210 76543210 76543210 76543210
// 00000010 00100000 Dev____ Sub Dev Fun____ XOR( B2, B3, B4)
// 3. LSB first, start bit + 16 Vendor + 4 Parity(of vendor) + 4 Genre1 + 4 Genre2 + 10 Command + 2 ID + 8 Parity + stop bit
//
// We reduce it to: start bit + 16 Vendor + 16 Address + 8 Command + 8 Parity + stop bit
//
#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)
#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_SPACE (KASEIKYO_REPEAT_PERIOD - KASEIKYO_AVERAGE_DURATION) // 74 ms
// for old decoder
#define KASEIKYO_DATA_BITS 32
//+=============================================================================
/*
* Send with LSB first
* Address is sub-device << 8 + device
*/
void IRsend::sendKaseikyo(uint16_t aAddress, uint8_t aCommand, uint_fast8_t aNumberOfRepeats, uint16_t aVendorCode) {
// Set IR carrier frequency
enableIROut(KASEIKYO_KHZ); // 37 kHz
uint_fast8_t tNumberOfCommands = aNumberOfRepeats + 1;
while (tNumberOfCommands > 0) {
// Header
mark(KASEIKYO_HEADER_MARK);
space(KASEIKYO_HEADER_SPACE);
// Vendor ID
sendPulseDistanceWidthData(KASEIKYO_BIT_MARK, KASEIKYO_ONE_SPACE, KASEIKYO_BIT_MARK, KASEIKYO_ZERO_SPACE, aVendorCode,
KASEIKYO_VENDOR_ID_BITS, PROTOCOL_IS_LSB_FIRST);
// Vendor Parity
uint8_t tVendorParity = aVendorCode ^ (aVendorCode >> 8);
tVendorParity = (tVendorParity ^ (tVendorParity >> 4)) & 0xF;
LongUnion tSendValue;
tSendValue.UWord.LowWord = aAddress << KASEIKYO_VENDOR_ID_PARITY_BITS;
tSendValue.UByte.LowByte |= tVendorParity; // set low nibble to parity
tSendValue.UByte.MidHighByte = aCommand;
tSendValue.UByte.HighByte = aCommand ^ tSendValue.UByte.LowByte ^ tSendValue.UByte.MidLowByte; // Parity
// Send address (device and subdevice) + command + parity + Stop bit
sendPulseDistanceWidthData(KASEIKYO_BIT_MARK, KASEIKYO_ONE_SPACE, KASEIKYO_BIT_MARK, KASEIKYO_ZERO_SPACE, tSendValue.ULong,
KASEIKYO_ADDRESS_BITS + KASEIKYO_VENDOR_ID_PARITY_BITS + KASEIKYO_COMMAND_BITS + KASEIKYO_PARITY_BITS,
PROTOCOL_IS_LSB_FIRST, SEND_STOP_BIT);
tNumberOfCommands--;
// skip last delay!
if (tNumberOfCommands > 0) {
// send repeated command in a fixed raster
delay(KASEIKYO_REPEAT_SPACE / MICROS_IN_ONE_MILLI);
}
}
IrReceiver.restartAfterSend();
}
/**
* Stub using Kaseikyo with PANASONIC_VENDOR_ID_CODE
*/
void IRsend::sendPanasonic(uint16_t aAddress, uint8_t aCommand, uint_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, uint_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, uint_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, uint_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, uint_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 (100)- +4 for initial gap, start bit mark and space + stop bit mark
if (decodedIRData.rawDataPtr->rawlen != ((2 * KASEIKYO_BITS) + 4)) {
IR_DEBUG_PRINT(F("Kaseikyo: "));
IR_DEBUG_PRINT(F("Data length="));
IR_DEBUG_PRINT(decodedIRData.rawDataPtr->rawlen);
IR_DEBUG_PRINTLN(F(" is not 100"));
return false;
}
if (!matchMark(decodedIRData.rawDataPtr->rawbuf[1], KASEIKYO_HEADER_MARK)) {
IR_DEBUG_PRINT(F("Kaseikyo: "));
IR_DEBUG_PRINTLN(F("Header mark length is wrong"));
return false;
}
if (!matchMark(decodedIRData.rawDataPtr->rawbuf[2], KASEIKYO_HEADER_SPACE)) {
IR_DEBUG_PRINT(F("Kaseikyo: "));
IR_DEBUG_PRINTLN(F("Header space length is wrong"));
return false;
}
// decode Vendor ID
if (!decodePulseDistanceData(KASEIKYO_VENDOR_ID_BITS, 3, KASEIKYO_BIT_MARK, KASEIKYO_ONE_SPACE, KASEIKYO_ZERO_SPACE,
PROTOCOL_IS_LSB_FIRST)) {
IR_DEBUG_PRINT(F("Kaseikyo: "));
IR_DEBUG_PRINTLN(F("Vendor ID decode failed"));
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 address (device and subdevice) + command + parity
if (!decodePulseDistanceData(
KASEIKYO_VENDOR_ID_PARITY_BITS + KASEIKYO_ADDRESS_BITS + KASEIKYO_COMMAND_BITS + KASEIKYO_PARITY_BITS,
3 + (2 * KASEIKYO_VENDOR_ID_BITS), KASEIKYO_BIT_MARK, KASEIKYO_ONE_SPACE,
KASEIKYO_ZERO_SPACE, PROTOCOL_IS_LSB_FIRST)) {
IR_DEBUG_PRINT(F("Kaseikyo: "));
IR_DEBUG_PRINTLN(F("Address, command + parity decode failed"));
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.address = (tValue.UWord.LowWord >> KASEIKYO_VENDOR_ID_PARITY_BITS); // remove 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)) {
IR_DEBUG_PRINT(F("Kaseikyo: "));
IR_DEBUG_PRINT(F("4 bit VendorID Parity is not correct. expected=0x"));
IR_DEBUG_PRINT(tVendorParity, HEX);
IR_DEBUG_PRINT(F(" received=0x"));
IR_DEBUG_PRINT(decodedIRData.decodedRawData, HEX);
IR_DEBUG_PRINT(F(" VendorID=0x"));
IR_DEBUG_PRINTLN(tVendorId, HEX);
decodedIRData.flags = IRDATA_FLAGS_PARITY_FAILED | IRDATA_FLAGS_IS_LSB_FIRST;
}
if (tProtocol != KASEIKYO) {
decodedIRData.flags |= IRDATA_FLAGS_EXTRA_INFO;
decodedIRData.extra = tVendorId; // Store vendor ID
}
if (tValue.UByte.HighByte != tParity) {
IR_DEBUG_PRINT(F("Kaseikyo: "));
IR_DEBUG_PRINT(F("8 bit Parity is not correct. expected=0x"));
IR_DEBUG_PRINT(tParity, HEX);
IR_DEBUG_PRINT(F(" received=0x"));
IR_DEBUG_PRINT(decodedIRData.decodedRawData >> KASEIKYO_COMMAND_BITS, HEX);
IR_DEBUG_PRINT(F(" address=0x"));
IR_DEBUG_PRINT(decodedIRData.address, HEX);
IR_DEBUG_PRINT(F(" command=0x"));
IR_DEBUG_PRINTLN(decodedIRData.command, HEX);
decodedIRData.flags |= IRDATA_FLAGS_PARITY_FAILED;
}
// check for repeat
if (decodedIRData.rawDataPtr->rawbuf[0] < (KASEIKYO_REPEAT_PERIOD / MICROS_PER_TICK)) {
decodedIRData.flags |= IRDATA_FLAGS_IS_REPEAT;
}
decodedIRData.protocol = tProtocol;
decodedIRData.numberOfBits = KASEIKYO_BITS;
return true;
}
/**
* Old MSB first decoder
*/
bool IRrecv::decodePanasonicMSB(decode_results *aResults) {
unsigned int offset = 1;
if (aResults->rawlen < (2 * KASEIKYO_BITS) + 2) {
return false;
}
if (!matchMark(aResults->rawbuf[offset], KASEIKYO_HEADER_MARK)) {
return false;
}
offset++;
if (!matchMark(aResults->rawbuf[offset], KASEIKYO_HEADER_SPACE)) {
return false;
}
offset++;
// decode address
if (!decodePulseDistanceData(KASEIKYO_ADDRESS_BITS + KASEIKYO_DATA_BITS, offset, KASEIKYO_BIT_MARK, KASEIKYO_ONE_SPACE,
KASEIKYO_ZERO_SPACE, PROTOCOL_IS_MSB_FIRST)) {
return false;
}
aResults->bits = KASEIKYO_BITS;
aResults->value = decodedIRData.decodedRawData;
aResults->address = PANASONIC_VENDOR_ID_CODE;
aResults->decode_type = PANASONIC;
decodedIRData.protocol = PANASONIC;
return true;
}
/**
* Old version with MSB first data
*/
void IRsend::sendPanasonic(uint16_t aAddress, uint32_t aData) {
// Set IR carrier frequency
enableIROut(KASEIKYO_KHZ); // 36.7kHz is the correct frequency
// Header
mark(KASEIKYO_HEADER_MARK);
space(KASEIKYO_HEADER_SPACE);
// Old version with MSB first Data Address
sendPulseDistanceWidthData(KASEIKYO_BIT_MARK, KASEIKYO_ONE_SPACE, KASEIKYO_BIT_MARK, KASEIKYO_ZERO_SPACE, aAddress,
KASEIKYO_ADDRESS_BITS, PROTOCOL_IS_MSB_FIRST);
// Old version with MSB first Data Data + stop bit
sendPulseDistanceWidthData(KASEIKYO_BIT_MARK, KASEIKYO_ONE_SPACE, KASEIKYO_BIT_MARK, KASEIKYO_ZERO_SPACE, aData,
KASEIKYO_DATA_BITS, PROTOCOL_IS_MSB_FIRST);
IrReceiver.restartAfterSend();
}
/** @}*/
#endif // _IR_KASEIKYO_HPP

View File

@@ -0,0 +1,325 @@
/*
* ir_LG.hpp
*
* Contains functions for receiving and sending LG 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) 2017-2021 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
#include <Arduino.h>
//#define DEBUG // Activate this for lots of lovely debug output from this decoder.
#include "IRremoteInt.h" // evaluates the DEBUG for IR_DEBUG_PRINT
/** \addtogroup Decoder Decoders and encoders for different protocols
* @{
*/
//==============================================================================
// L GGGG
// L G
// L G GG
// L G G
// LLLLL GGG
//==============================================================================
// 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 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, the next value can be sent after around 118 ms. Tested with the fan button.
*
* 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 // 84
#define LG2_HEADER_MARK (6 * LG_UNIT) // 3000
#define LG2_HEADER_SPACE (19 * LG_UNIT) // 9500
#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_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_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_REPEAT_SPACE (LG_REPEAT_PERIOD - LG_AVERAGE_DURATION) // 52 ms
//+=============================================================================
/*
* Send repeat
* Repeat commands should be sent in a 110 ms raster.
*/
void IRsend::sendLGRepeat(bool aUseLG2Protocol) {
enableIROut(LG_KHZ); // 38 kHz
if (aUseLG2Protocol) {
mark(LG2_HEADER_MARK);
} else {
mark(LG_HEADER_MARK);
}
space(LG_REPEAT_HEADER_SPACE);
mark(LG_BIT_MARK);
IrReceiver.restartAfterSend();
}
/**
* Repeat commands should be sent in a 110 ms raster.
* There is NO delay after the last sent repeat!
* @param aUseLG2Protocol if true use LG2 protocol, which has a different header
*/
void IRsend::sendLG(uint8_t aAddress, uint16_t aCommand, uint_fast8_t aNumberOfRepeats, bool aIsRepeat, bool aUseLG2Protocol) {
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
}
tRawData |= (tChecksum & 0xF);
sendLGRaw(tRawData, aNumberOfRepeats, aIsRepeat, aUseLG2Protocol);
}
/*
* Here you can put your raw data, even one with "wrong" checksum
*/
void IRsend::sendLGRaw(uint32_t aRawData, uint_fast8_t aNumberOfRepeats, bool aIsRepeat, bool aUseLG2Protocol) {
if (aIsRepeat) {
sendLGRepeat();
return;
}
// Set IR carrier frequency
enableIROut(LG_KHZ);
// Header
if (aUseLG2Protocol) {
mark(LG2_HEADER_MARK);
space(LG2_HEADER_SPACE);
// MSB first
sendPulseDistanceWidthData(LG_BIT_MARK, LG_ONE_SPACE, LG_BIT_MARK, LG_ZERO_SPACE, aRawData, LG_BITS, PROTOCOL_IS_MSB_FIRST,
SEND_STOP_BIT);
} else {
mark(LG_HEADER_MARK);
space(LG_HEADER_SPACE);
// MSB first
sendPulseDistanceWidthData(LG_BIT_MARK, LG_ONE_SPACE, LG_BIT_MARK, LG_ZERO_SPACE, aRawData, LG_BITS, PROTOCOL_IS_MSB_FIRST,
SEND_STOP_BIT);
}
for (uint_fast8_t i = 0; i < aNumberOfRepeats; ++i) {
// send repeat in a 110 ms raster
if (i == 0) {
delay(LG_REPEAT_SPACE / MICROS_IN_ONE_MILLI);
} else {
delay((LG_REPEAT_PERIOD - LG_REPEAT_DURATION) / MICROS_IN_ONE_MILLI);
}
// send repeat
sendLGRepeat(aUseLG2Protocol);
}
IrReceiver.restartAfterSend();
}
//+=============================================================================
// LGs has a repeat like NEC
//
/*
* First check for right data length
* Next check start bit
* Next try the decode
* Last check stop bit
*/
bool IRrecv::decodeLG() {
decode_type_t tProtocol = LG;
uint16_t tHeaderSpace = LG_HEADER_SPACE;
// 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.rawDataPtr->rawlen != ((2 * LG_BITS) + 4) && (decodedIRData.rawDataPtr->rawlen != 4)) {
IR_DEBUG_PRINT(F("LG: "));
IR_DEBUG_PRINT(F("Data length="));
IR_DEBUG_PRINT(decodedIRData.rawDataPtr->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)) {
IR_DEBUG_PRINT(F("LG: "));
IR_DEBUG_PRINTLN(F("Header mark is wrong"));
return false;
} else {
tProtocol = LG2;
tHeaderSpace = LG2_HEADER_SPACE;
}
}
// Check for repeat - here we have another header space length
if (decodedIRData.rawDataPtr->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;
}
IR_DEBUG_PRINT(F("LG: "));
IR_DEBUG_PRINT(F("Repeat header space is wrong"));
return false;
}
// Check command header space
if (!matchSpace(decodedIRData.rawDataPtr->rawbuf[2], tHeaderSpace)) {
IR_DEBUG_PRINT(F("LG: "));
IR_DEBUG_PRINTLN(F("Header space length is wrong"));
return false;
}
if (!decodePulseDistanceData(LG_BITS, 3, LG_BIT_MARK, LG_ONE_SPACE, LG_ZERO_SPACE, PROTOCOL_IS_MSB_FIRST)) {
IR_DEBUG_PRINT(F("LG: "));
IR_DEBUG_PRINTLN(F("Decode failed"));
return false;
}
// Stop bit
if (!matchMark(decodedIRData.rawDataPtr->rawbuf[3 + (2 * LG_BITS)], LG_BIT_MARK)) {
IR_DEBUG_PRINT(F("LG: "));
IR_DEBUG_PRINTLN(F("Stop bit mark length is wrong"));
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)) {
IR_DEBUG_PRINT(F("LG: "));
IR_DEBUG_PRINT(F("4 bit checksum is not correct. expected=0x"));
IR_DEBUG_PRINT(tChecksum, HEX);
IR_DEBUG_PRINT(F(" received=0x"));
IR_DEBUG_PRINT((decodedIRData.decodedRawData & 0xF), HEX);
IR_DEBUG_PRINT(F(" data=0x"));
IR_DEBUG_PRINTLN(decodedIRData.command, HEX);
decodedIRData.flags |= IRDATA_FLAGS_PARITY_FAILED;
}
decodedIRData.protocol = tProtocol; // LG or LG2
decodedIRData.numberOfBits = LG_BITS;
return true;
}
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 (!decodePulseDistanceData(LG_BITS, offset, LG_BIT_MARK, LG_ONE_SPACE, LG_ZERO_SPACE, PROTOCOL_IS_MSB_FIRST)) {
return false;
}
// Stop bit
if (!matchMark(aResults->rawbuf[offset + (2 * LG_BITS)], LG_BIT_MARK)) {
IR_DEBUG_PRINTLN(F("Stop bit mark length is wrong"));
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(
"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,
SEND_STOP_BIT);
IrReceiver.restartAfterSend();
}
/** @}*/
#endif // _IR_LG_HPP

View File

@@ -0,0 +1,227 @@
/*
* 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-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.
*
************************************************************************************
*/
#ifndef _IR_LEGO_HPP
#define _IR_LEGO_HPP
#include <Arduino.h>
//#define DEBUG // Activate this for lots of lovely debug output from this decoder.
#include "IRremoteInt.h" // evaluates the DEBUG for IR_DEBUG_PRINT
/** \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
//
// 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
/*
* Compatibility function for legacy code, this calls the send raw data function
*/
void IRsend::sendLegoPowerFunctions(uint16_t aRawData, bool aDoSend5Times) {
sendLegoPowerFunctions(aRawData, (aRawData >> (LEGO_MODE_BITS + LEGO_COMMAND_BITS + LEGO_PARITY_BITS)) & 0x3, aDoSend5Times);
}
/*
* Here we process the structured data, and call the send raw data function
*/
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) {
enableIROut(38);
IR_DEBUG_PRINT(F("sendLego aRawData=0x"));
IR_DEBUG_PRINTLN(aRawData, HEX);
aChannel &= 0x03; // we have 4 channels
uint_fast8_t tNumberOfCommands = 1;
if (aDoSend5Times) {
tNumberOfCommands = 5;
}
// required for repeat timing, see http://www.hackvandedam.nl/blog/?page_id=559
uint8_t tRepeatPeriod = (110 - (LEGO_AVERAGE_DURATION / MICROS_IN_ONE_MILLI)) + (aChannel * 40); // from 100 to 220
while (tNumberOfCommands > 0) {
// Header
mark(LEGO_HEADER_MARK);
space(LEGO_HEADER_SPACE);
sendPulseDistanceWidthData(LEGO_BIT_MARK, LEGO_ONE_SPACE, LEGO_BIT_MARK, LEGO_ZERO_SPACE, aRawData, LEGO_BITS,
PROTOCOL_IS_MSB_FIRST,
SEND_STOP_BIT);
tNumberOfCommands--;
// skip last delay!
if (tNumberOfCommands > 0) {
// send repeated command with a fixed space gap
delay(tRepeatPeriod);
}
}
IrReceiver.restartAfterSend();
}
/*
* Mode is stored in the upper nibble of command
*/
bool IRrecv::decodeLegoPowerFunctions() {
// Check header "mark"
if (!matchMark(decodedIRData.rawDataPtr->rawbuf[1], LEGO_HEADER_MARK)) {
// no debug output, since this check is mainly to determine the received protocol
return false;
}
// Check we have enough data - +4 for initial gap, start bit mark and space + stop bit mark
if (decodedIRData.rawDataPtr->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;
}
// Check header "space"
if (!matchSpace(decodedIRData.rawDataPtr->rawbuf[2], LEGO_HEADER_SPACE)) {
IR_DEBUG_PRINT(F("LEGO: "));
IR_DEBUG_PRINTLN(F("Header space length is wrong"));
return false;
}
if (!decodePulseDistanceData(LEGO_BITS, 3, LEGO_BIT_MARK, LEGO_ONE_SPACE, LEGO_ZERO_SPACE, PROTOCOL_IS_MSB_FIRST)) {
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.rawDataPtr->rawbuf[0] < (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.protocol = LEGO_PF;
decodedIRData.numberOfBits = LEGO_BITS;
return true;
}
/** @}*/
#endif // _IR_LEGO_HPP

View File

@@ -0,0 +1,179 @@
/*
* 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/
*
* This file is part of Arduino-IRremote https://github.com/Arduino-IRremote/Arduino-IRremote.
*
************************************************************************************
* MIT License
*
* Copyright (c) 2017-2022 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
#include <Arduino.h>
#include "IRremoteInt.h" // evaluates the DEBUG for IR_DEBUG_PRINT
//
//==============================================================================
//
// M A G I Q U E S T
//
//==============================================================================
// MSB first, 8 Start bits (zero), 32 wand id bits, 16 magnitude bits, one stop bit
// Not all start bits must be received, since protocol is MSB first and so the LSB ends up always at the right position.
#if !defined (DOXYGEN)
// MagiQuest packet is both Wand ID and magnitude of swish and flick
union magiquest_t {
uint64_t llword;
struct {
uint16_t magnitude;
uint32_t wand_id;
uint8_t StartBits; // first 8 MSB start bits are zero.
uint8_t HighByte; // just to pad the struct out to 64 bits so we can union with llword
} cmd;
};
#endif // !defined (DOXYGEN)
#define MAGIQUEST_MAGNITUDE_BITS 16 // magiquest_t.cmd.magnitude
#define MAGIQUEST_WAND_ID_BITS 32 // magiquest_t.cmd.wand_id
#define MAGIQUEST_START_BITS 8 // magiquest_t.cmd.StartBits
#define MAGIQUEST_PERIOD 1150 // Time for a full MagiQuest "bit" (1100 - 1200 usec)
#define MAGIQUEST_BITS (MAGIQUEST_MAGNITUDE_BITS + MAGIQUEST_WAND_ID_BITS) // 48 Size of the command without the start bits
// The maximum size of a packet is the sum of all 3 expected fields * 2
#define MAGIQUEST_PACKET_SIZE (MAGIQUEST_MAGNITUDE_BITS + MAGIQUEST_WAND_ID_BITS + MAGIQUEST_START_BITS) // 56
/*
* 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)
#define MAGIQUEST_ONE_MARK (2 * MAGIQUEST_UNIT) // 576
#define MAGIQUEST_ONE_SPACE (2 * MAGIQUEST_UNIT) // 576
#define MAGIQUEST_ZERO_MARK MAGIQUEST_UNIT
#define MAGIQUEST_ZERO_SPACE (3 * MAGIQUEST_UNIT) // 864
//+=============================================================================
//
void IRsend::sendMagiQuest(uint32_t wand_id, uint16_t magnitude) {
// Set IR carrier frequency
enableIROut(38);
// 8 start bits
sendPulseDistanceWidthData(
MAGIQUEST_ONE_MARK, MAGIQUEST_ONE_SPACE, MAGIQUEST_ZERO_MARK, MAGIQUEST_ZERO_SPACE, 0, 8, PROTOCOL_IS_MSB_FIRST);
// Data
sendPulseDistanceWidthData(
MAGIQUEST_ONE_MARK, MAGIQUEST_ONE_SPACE, MAGIQUEST_ZERO_MARK, MAGIQUEST_ZERO_SPACE, wand_id, MAGIQUEST_WAND_ID_BITS,
PROTOCOL_IS_MSB_FIRST);
sendPulseDistanceWidthData(
MAGIQUEST_ONE_MARK, MAGIQUEST_ONE_SPACE, MAGIQUEST_ZERO_MARK, MAGIQUEST_ZERO_SPACE, magnitude, MAGIQUEST_MAGNITUDE_BITS,
PROTOCOL_IS_MSB_FIRST,
SEND_STOP_BIT);
IrReceiver.restartAfterSend();
}
//+=============================================================================
//
/*
* decodes a 56 bit result, which is not really compatible with standard decoder layout
*/
bool IRrecv::decodeMagiQuest() {
magiquest_t data; // Somewhere to build our code
unsigned int tOffset = 1; // Skip the gap between packets
unsigned int tMark;
unsigned int tSpace;
#if defined(DEBUG)
char bitstring[(MAGIQUEST_PACKET_SIZE + 1)];
bitstring[MAGIQUEST_PACKET_SIZE] = '\0';
#endif
// Check we have the right amount of data, magnitude and ID bits and at least 2 start bits + 1 stop bit
if (decodedIRData.rawDataPtr->rawlen < (2 * (MAGIQUEST_BITS + 3))
|| decodedIRData.rawDataPtr->rawlen > (2 * (MAGIQUEST_PACKET_SIZE + 1))) {
IR_DEBUG_PRINT(F("MagiQuest: "));
IR_DEBUG_PRINT(F("Data length="));
IR_DEBUG_PRINT(decodedIRData.rawDataPtr->rawlen);
IR_DEBUG_PRINTLN(F(" is not between 102 and 114"));
return false;
}
// Read the bits in
data.llword = 0;
while (tOffset < (unsigned int) (decodedIRData.rawDataPtr->rawlen - 1)) {
// get one mark and space pair
tMark = decodedIRData.rawDataPtr->rawbuf[tOffset++];
tSpace = decodedIRData.rawDataPtr->rawbuf[tOffset++];
IR_TRACE_PRINT(F("MagiQuest: mark="));
IR_TRACE_PRINT(tMark * MICROS_PER_TICK);
IR_TRACE_PRINT(F(" space="));
IR_TRACE_PRINTLN(tSpace * MICROS_PER_TICK);
if (matchMark(tSpace + tMark, MAGIQUEST_PERIOD)) {
if (tSpace > tMark) {
// It's a 0
data.llword <<= 1;
#if defined(DEBUG)
bitstring[(tOffset / 2) - 1] = '0';
#endif
} else {
// It's a 1
data.llword = (data.llword << 1) | 1;
#if defined(DEBUG)
bitstring[(tOffset / 2) - 1] = '1';
#endif
}
} else {
IR_DEBUG_PRINTLN(F("Mark and space does not match the constant MagiQuest period"));
return false;
}
}
IR_DEBUG_PRINTLN(bitstring);
// Success
decodedIRData.protocol = MAGIQUEST;
decodedIRData.numberOfBits = tOffset / 2;
decodedIRData.flags = IRDATA_FLAGS_EXTRA_INFO | IRDATA_FLAGS_IS_MSB_FIRST;
decodedIRData.extra = data.cmd.magnitude;
decodedIRData.decodedRawData = data.cmd.wand_id;
return true;
}
#endif // _IR_MAGIQUEST_HPP

View File

@@ -0,0 +1,380 @@
/*
* 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-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.
*
************************************************************************************
*/
#ifndef _IR_NEC_HPP
#define _IR_NEC_HPP
#include <Arduino.h>
//#define DEBUG // Activate this for lots of lovely debug output from this decoder.
#include "IRremoteInt.h" // evaluates the DEBUG for IR_DEBUG_PRINT
#include "LongUnion.h"
/** \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
//==============================================================================
// see: https://www.sbprojects.net/knowledge/ir/nec.php
// for Apple see https://en.wikipedia.org/wiki/Apple_Remote
// ONKYO like NEC but 16 independent command bits
// LSB first, 1 start bit + 16 bit address (or 8 bit address and 8 bit inverted address) + 8 bit command + 8 bit inverted command + 1 stop bit.
//
#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_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_SPACE (NEC_REPEAT_PERIOD - NEC_AVERAGE_DURATION) // 48 ms
#define APPLE_ADDRESS 0x87EE
//+=============================================================================
/*
* Send repeat
* Repeat commands should be sent in a 110 ms raster.
*/
void IRsend::sendNECRepeat() {
enableIROut(NEC_KHZ); // 38 kHz
mark(NEC_HEADER_MARK);
space(NEC_REPEAT_HEADER_SPACE);
mark(NEC_BIT_MARK);
IrReceiver.restartAfterSend();
// ledOff(); // Always end with the LED off
}
/*
* Repeat commands should be sent in a 110 ms raster.
* There is NO delay after the last sent repeat!
* https://www.sbprojects.net/knowledge/ir/nec.php
* @param aIsRepeat if true, send only one repeat frame without leading and trailing space
*/
void IRsend::sendNEC(uint16_t aAddress, uint8_t aCommand, uint_fast8_t aNumberOfRepeats, bool aIsRepeat) {
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;
sendNECRaw(tRawData.ULong, aNumberOfRepeats, aIsRepeat);
}
/*
* Repeat commands should be sent in a 110 ms raster.
* There is NO delay after the last sent repeat!
* @param aIsRepeat if true, send only one repeat frame without leading and trailing space
*/
void IRsend::sendOnkyo(uint16_t aAddress, uint16_t aCommand, uint_fast8_t aNumberOfRepeats, bool aIsRepeat) {
LongUnion tRawData;
// Address 16 bit LSB first
tRawData.UWord.LowWord = aAddress;
// Command 16 bit LSB first
tRawData.UWord.HighWord = aCommand;
sendNECRaw(tRawData.ULong, aNumberOfRepeats, aIsRepeat);
}
/*
* Repeat commands should be sent in a 110 ms raster.
* There is NO delay after the last sent repeat!
* https://en.wikipedia.org/wiki/Apple_Remote
* https://gist.github.com/darconeous/4437f79a34e3b6441628
* @param aAddress is the DeviceId*
* @param aIsRepeat if true, send only one repeat frame without leading and trailing space
*/
void IRsend::sendApple(uint8_t aDeviceId, uint8_t aCommand, uint_fast8_t aNumberOfRepeats, bool aIsRepeat) {
LongUnion tRawData;
// Address 16 bit LSB first
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
sendNECRaw(tRawData.ULong, aNumberOfRepeats, aIsRepeat);
}
void IRsend::sendNECRaw(uint32_t aRawData, uint_fast8_t aNumberOfRepeats, bool aIsRepeat) {
if (aIsRepeat) {
sendNECRepeat();
return;
}
// Set IR carrier frequency
enableIROut(NEC_KHZ);
// Header
mark(NEC_HEADER_MARK);
space(NEC_HEADER_SPACE);
// LSB first + stop bit
sendPulseDistanceWidthData(NEC_BIT_MARK, NEC_ONE_SPACE, NEC_BIT_MARK, NEC_ZERO_SPACE, aRawData, NEC_BITS, PROTOCOL_IS_LSB_FIRST,
SEND_STOP_BIT);
for (uint_fast8_t i = 0; i < aNumberOfRepeats; ++i) {
// send repeat in a 110 ms raster
if (i == 0) {
delay(NEC_REPEAT_SPACE / MICROS_IN_ONE_MILLI);
} else {
delay((NEC_REPEAT_PERIOD - NEC_REPEAT_DURATION) / MICROS_IN_ONE_MILLI);
}
// send repeat
sendNECRepeat();
}
IrReceiver.restartAfterSend();
}
//+=============================================================================
// NECs have a repeat only 4 items long
//
/*
* First check for right data length
* Next check start bit
* Next try the decode
* Last check stop bit
*
* Decodes also Apple
*/
bool IRrecv::decodeNEC() {
// 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.rawDataPtr->rawlen != ((2 * NEC_BITS) + 4) && (decodedIRData.rawDataPtr->rawlen != 4)) {
IR_DEBUG_PRINT(F("NEC: "));
IR_DEBUG_PRINT(F("Data length="));
IR_DEBUG_PRINT(decodedIRData.rawDataPtr->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.rawDataPtr->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)) {
IR_DEBUG_PRINT(F("NEC: "));
IR_DEBUG_PRINTLN(F("Header space length is wrong"));
return false;
}
if (!decodePulseDistanceData(NEC_BITS, 3, NEC_BIT_MARK, NEC_ONE_SPACE, NEC_ZERO_SPACE, PROTOCOL_IS_LSB_FIRST)) {
IR_DEBUG_PRINT(F("NEC: "));
IR_DEBUG_PRINTLN(F("Decode failed"));
return false;
}
// Stop bit
if (!matchMark(decodedIRData.rawDataPtr->rawbuf[3 + (2 * NEC_BITS)], NEC_BIT_MARK)) {
IR_DEBUG_PRINT(F("NEC: "));
IR_DEBUG_PRINTLN(F("Stop bit mark length is wrong"));
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
// 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
/*
* Old NEC plausibility check below, now it is just ONKYO :-)
*/
// IR_DEBUG_PRINT(F("NEC: "));
// IR_DEBUG_PRINT(F("Command=0x"));
// IR_DEBUG_PRINT(tValue.UByte.MidHighByte, HEX);
// IR_DEBUG_PRINT(F(" is not inverted value of 0x"));
// IR_DEBUG_PRINTLN(tValue.UByte.HighByte, HEX);
// decodedIRData.flags = IRDATA_FLAGS_PARITY_FAILED | IRDATA_FLAGS_IS_LSB_FIRST;
}
}
decodedIRData.numberOfBits = NEC_BITS;
return true;
}
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)) {
IR_DEBUG_PRINT(F("NEC MSB: "));
IR_DEBUG_PRINTLN(F("Header space length is wrong"));
return false;
}
offset++;
if (!decodePulseDistanceData(NEC_BITS, offset, NEC_BIT_MARK, NEC_ONE_SPACE, NEC_ZERO_SPACE, PROTOCOL_IS_MSB_FIRST)) {
IR_DEBUG_PRINT(F("NEC MSB: "));
IR_DEBUG_PRINTLN(F("Decode failed"));
return false;
}
// Stop bit
if (!matchMark(aResults->rawbuf[offset + (2 * NEC_BITS)], NEC_BIT_MARK)) {
IR_DEBUG_PRINT(F("NEC MSB: "));
IR_DEBUG_PRINTLN(F("Stop bit mark length is wrong"));
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.
* 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,
SEND_STOP_BIT);
IrReceiver.restartAfterSend();
}
/** @}*/
#endif // _IR_NEC_HPP

View File

@@ -0,0 +1,335 @@
/*
* @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
// The first number, here 0000, denotes the type of the signal. 0000 denotes a raw IR signal with modulation,
// The second number, here 006C, denotes a frequency code
#include <Arduino.h>
//#define DEBUG // Activate this for lots of lovely debug output from this decoder.
#include "IRremoteInt.h" // evaluates the DEBUG for IR_DEBUG_PRINT"
/** \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 unsigned int bitsInHexadecimal = 4U;
static const unsigned int digitsInProntoNumber = 4U;
static const unsigned int numbersInPreamble = 4U;
static const unsigned int 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 unsigned int 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.
*/
void IRsend::sendPronto(const uint16_t *data, unsigned int length, uint_fast8_t aNumberOfRepeats) {
unsigned int timebase = (microsecondsInSeconds * data[1] + referenceFrequency / 2) / referenceFrequency;
unsigned int 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.
}
unsigned int intros = 2 * data[2];
unsigned int repeats = 2 * data[3];
IR_DEBUG_PRINT(F("sendPronto intros="));
IR_DEBUG_PRINT(intros);
IR_DEBUG_PRINT(F(" repeats="));
IR_DEBUG_PRINTLN(repeats);
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 (unsigned int i = 0; i < intros + repeats; i++) {
uint32_t duration = ((uint32_t) data[i + numbersInPreamble]) * timebase;
durations[i] = (unsigned int) ((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 (unsigned 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, uint_fast8_t aNumberOfRepeats) {
size_t len = strlen(str) / (digitsInProntoNumber + 1) + 1;
uint16_t data[len];
const char *p = str;
char *endptr[1];
for (unsigned int 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, uint_fast8_t aNumberOfRepeats) {
size_t len = strlen_PF(str);
char work[len + 1];
strncpy_PF(work, str, len);
sendPronto(work, aNumberOfRepeats);
}
//standard pointer
void IRsend::sendPronto_P(const char *str, uint_fast8_t aNumberOfRepeats) {
size_t len = strlen_P(str);
char work[len + 1];
strncpy_P(work, str, len);
sendPronto(work, aNumberOfRepeats);
}
#endif
void IRsend::sendPronto(const __FlashStringHelper *str, uint_fast8_t aNumberOfRepeats) {
size_t len = strlen_P(reinterpret_cast<const char*>(str));
char work[len + 1];
strncpy_P(work, reinterpret_cast<const char*>(str), len);
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(unsigned int x) {
return (char) (x <= 9 ? ('0' + x) : ('A' + (x - 10)));
}
static void dumpDigit(Print *aSerial, unsigned int number) {
aSerial->print(hexDigit(number));
}
static void dumpNumber(Print *aSerial, uint16_t number) {
for (unsigned int i = 0; i < digitsInProntoNumber; i++) {
unsigned int 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 unsigned int *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, unsigned int 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.rawDataPtr->rawlen + 1) / 2);
dumpNumber(aSerial, 0);
unsigned int timebase = toTimebase(aFrequencyHertz);
compensateAndDumpSequence(aSerial, &decodedIRData.rawDataPtr->rawbuf[1], decodedIRData.rawDataPtr->rawlen - 1, timebase); // skip leading space
aSerial->println("\";");
}
/*
* 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, unsigned int number) {
aString->concat(hexDigit(number));
return number;
}
static size_t dumpNumber(String *aString, uint16_t number) {
size_t size = 0;
for (unsigned int i = 0; i < digitsInProntoNumber; i++) {
unsigned int 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 unsigned int *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, unsigned int frequency) {
size_t size = 0;
unsigned int timebase = toTimebase(frequency);
size += dumpNumber(aString, frequency > 0 ? learnedToken : learnedNonModulatedToken);
size += dumpNumber(aString, toFrequencyCode(frequency));
size += dumpNumber(aString, (decodedIRData.rawDataPtr->rawlen + 1) / 2);
size += dumpNumber(aString, 0);
size += compensateAndDumpSequence(aString, &decodedIRData.rawDataPtr->rawbuf[1], decodedIRData.rawDataPtr->rawlen - 1,
timebase); // skip leading space
return size;
}
/** @}*/
#endif // _IR_PRONTO_HPP

View File

@@ -0,0 +1,559 @@
/*
* ir_RC5_RC6.hpp
*
* Contains functions for receiving and sending RC5, RC5X 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.
*
************************************************************************************
*/
#ifndef _IR_RC5_RC6_HPP
#define _IR_RC5_RC6_HPP
#include <Arduino.h>
//#define DEBUG // Activate this for lots of lovely debug output from this decoder.
//#define TRACE // Activate this for more debug output from this decoder.
#include "IRremoteInt.h" // evaluates the DEBUG for IR_DEBUG_PRINT
#include "LongUnion.h"
/** \addtogroup Decoder Decoders and encoders for different protocols
* @{
*/
bool sLastSendToggleValue = false;
//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
//==============================================================================
//
// see: https://www.sbprojects.net/knowledge/ir/rc5.php
// https://en.wikipedia.org/wiki/Manchester_code
// mark->space => 0
// space->mark => 1
// MSB first 1 start bit, 1 field bit, 1 toggle bit + 5 bit address + 6 bit command (6 bit command plus one field bit for RC5X), no stop bit
// 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
#define RC5_DURATION (15L * RC5_UNIT) // 13335
#define RC5_REPEAT_PERIOD (128L * RC5_UNIT) // 113792
#define RC5_REPEAT_SPACE (RC5_REPEAT_PERIOD - RC5_DURATION) // 100 ms
/**
* @param aCommand If aCommand is >=64 then we switch automatically to RC5X
*/
void IRsend::sendRC5(uint8_t aAddress, uint8_t aCommand, uint_fast8_t aNumberOfRepeats, bool aEnableAutomaticToggle) {
// Set IR carrier frequency
enableIROut(RC5_RC6_KHZ);
uint16_t tIRData = ((aAddress & 0x1F) << RC5_COMMAND_BITS);
if (aCommand < 0x40) {
// set field bit to lower field / set inverted upper command bit
tIRData |= 1 << (RC5_TOGGLE_BIT + RC5_ADDRESS_BITS + RC5_COMMAND_BITS);
} else {
// let field bit zero
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_SPACE / MICROS_IN_ONE_MILLI);
}
}
IrReceiver.restartAfterSend();
}
/**
* 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.rawDataPtr->rawlen < MIN_RC5_MARKS + 2 && decodedIRData.rawDataPtr->rawlen > ((2 * RC5_BITS) + 2)) {
// 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.rawDataPtr->rawlen);
IR_DEBUG_PRINTLN(F(" is not between 11 and 26"));
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.rawDataPtr->rawlen; tBitIndex++) {
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 {
// IR_TRACE_PRINT since I saw this too often
IR_DEBUG_PRINT(F("RC5: "));
IR_DEBUG_PRINTLN(F("Decode failed"));
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_TOGGLE_BIT_MASK | IRDATA_FLAGS_IS_MSB_FIRST;
}
// check for repeat
if (decodedIRData.rawDataPtr->rawbuf[0] < (RC5_REPEAT_PERIOD / MICROS_PER_TICK)) {
decodedIRData.flags |= IRDATA_FLAGS_IS_REPEAT;
}
decodedIRData.protocol = RC5;
return true;
}
//+=============================================================================
// RRRR CCCC 6666
// R R C 6
// RRRR C 6666
// R R C 6 6
// R R CCCC 666
//
//
// 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.
#define RC6_ADDRESS_BITS 8
#define RC6_COMMAND_BITS 8
#define RC6_BITS (RC6_LEADING_BIT + RC6_MODE_BITS + RC6_TOGGLE_BIT + RC6_ADDRESS_BITS + RC6_COMMAND_BITS) // 13
#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_SPACE 107000 // just a guess but > 2.666ms
/**
* Main RC6 send function
*/
void IRsend::sendRC6(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 == 4) ? (RC6_UNIT * 2) : (RC6_UNIT);
if (aRawData & mask) {
mark(t);
space(t);
} else {
space(t);
mark(t);
}
}
IrReceiver.restartAfterSend();
}
/**
* Send RC6 64 bit raw data
* We do not wait for the minimal trailing space of 2666 us
*/
void IRsend::sendRC6(uint64_t data, uint8_t nbits) {
// 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 << (nbits - 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 == 4) ? (RC6_UNIT * 2) : (RC6_UNIT);
if (data & mask) {
mark(t);
space(t);
} else {
space(t);
mark(t);
}
}
IrReceiver.restartAfterSend();
}
/**
* 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, uint_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;
}
}
IR_DEBUG_PRINT(F("RC6: "));
IR_DEBUG_PRINT(F("sLastSendToggleValue="));
IR_DEBUG_PRINT(sLastSendToggleValue);
IR_DEBUG_PRINT(F(" RawData="));
IR_DEBUG_PRINTLN(tIRRawData.ULong, HEX);
uint_fast8_t tNumberOfCommands = aNumberOfRepeats + 1;
while (tNumberOfCommands > 0) {
// start and leading bits are sent by sendRC6
sendRC6(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_SPACE / 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.rawDataPtr->rawlen < MIN_RC6_MARKS + 3 && decodedIRData.rawDataPtr->rawlen > ((2 * RC6_BITS) + 3)) {
IR_DEBUG_PRINT(F("RC6: "));
IR_DEBUG_PRINT(F("Data length="));
IR_DEBUG_PRINT(decodedIRData.rawDataPtr->rawlen);
IR_DEBUG_PRINTLN(F(" is not between 15 and 45"));
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.rawDataPtr->rawlen; tBitIndex++) {
uint8_t tStartLevel; // start level of coded bit
uint8_t tEndLevel; // end level of coded bit
tStartLevel = getBiphaselevel();
if (tBitIndex == 3) {
// Toggle bit is double wide; make sure second half is equal first half
if (tStartLevel != getBiphaselevel()) {
IR_DEBUG_PRINT(F("RC6: "));
IR_DEBUG_PRINTLN(F("Toggle mark or space length is wrong"));
return false;
}
}
tEndLevel = getBiphaselevel();
if (tBitIndex == 3) {
// Toggle bit is double wide; make sure second half matches
if (tEndLevel != getBiphaselevel()) {
IR_DEBUG_PRINT(F("RC6: "));
IR_DEBUG_PRINTLN(F("Toggle mark or space length is wrong"));
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 {
IR_DEBUG_PRINT(F("RC6: "));
IR_DEBUG_PRINTLN(F("Decode failed"));
// 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 < 36) {
// RC6
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_TOGGLE_BIT_MASK | IRDATA_FLAGS_IS_MSB_FIRST;
}
} else {
// RC6A
decodedIRData.flags = IRDATA_FLAGS_IS_MSB_FIRST;
if ((tValue.UByte.MidLowByte & 0x80) != 0) {
decodedIRData.flags = IRDATA_TOGGLE_BIT_MASK | IRDATA_FLAGS_IS_MSB_FIRST;
}
tValue.UByte.MidLowByte &= 0x87F; // mask toggle bit
decodedIRData.command = tValue.UByte.LowByte;
decodedIRData.address = tValue.UByte.MidLowByte;
}
// check for repeat, do not check toggle bit yet
if (decodedIRData.rawDataPtr->rawbuf[0] < ((RC6_REPEAT_SPACE + (RC6_REPEAT_SPACE / 4)) / MICROS_PER_TICK)) {
decodedIRData.flags |= IRDATA_FLAGS_IS_REPEAT;
}
decodedIRData.protocol = RC6;
return true;
}
/**
* 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);
}
}
IrReceiver.restartAfterSend();
}
/*
* Not longer required, use sendRC5 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);
}
}
IrReceiver.restartAfterSend();
}
/** @}*/
#endif // _IR_RC5_RC6_HPP

View File

@@ -0,0 +1,270 @@
/*
* 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-2021 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
#include <Arduino.h>
//#define DEBUG // Activate this for lots of lovely debug output from this decoder.
#include "IRremoteInt.h" // evaluates the DEBUG for IR_DEBUG_PRINT
#include "LongUnion.h"
/** \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
//==============================================================================
// see http://www.hifi-remote.com/wiki/index.php?title=DecodeIR#Samsung
// https://www.mikrocontroller.net/articles/IRMP_-_english#SAMSUNG32
// LSB first, 1 start bit + 16 bit address + 16,32,20 bit data + 1 stop bit.
// On my Samsung remote they are plain repeats of the complete frame.
#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)
#define SAMSUNG_UNIT 553 // 21 periods of 38 kHz (552,631) TICKS_LOW = 8.253 TICKS_HIGH = 14.825
#define SAMSUNG_HEADER_MARK (8 * SAMSUNG_UNIT) // 4400
#define SAMSUNG_HEADER_SPACE (8 * SAMSUNG_UNIT) // 4400
#define SAMSUNG_BIT_MARK SAMSUNG_UNIT
#define SAMSUNG_ONE_SPACE (3 * SAMSUNG_UNIT) // 1650 TICKS_LOW = 24.62 TICKS_HIGH = 42.25
#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_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.
/**
* Please note me (armin.arduino@gmail.com) if you ever see this special repeat in the wild
* Send repeat
* Repeat commands should be sent in a 110 ms raster.
*/
void IRsend::sendSamsungRepeat() {
enableIROut(SAMSUNG_KHZ); // 38 kHz
mark(SAMSUNG_HEADER_MARK);
space(SAMSUNG_HEADER_SPACE);
mark(SAMSUNG_BIT_MARK);
space(SAMSUNG_ZERO_SPACE);
mark(SAMSUNG_BIT_MARK);
IrReceiver.restartAfterSend();
}
void IRsend::sendSamsung(uint16_t aAddress, uint16_t aCommand, uint_fast8_t aNumberOfRepeats) {
// Set IR carrier frequency
enableIROut(SAMSUNG_KHZ);
uint_fast8_t tNumberOfCommands = aNumberOfRepeats + 1;
while (tNumberOfCommands > 0) {
unsigned long tStartOfFrameMillis = millis();
// Header
mark(SAMSUNG_HEADER_MARK);
space(SAMSUNG_HEADER_SPACE);
// Address
sendPulseDistanceWidthData(SAMSUNG_BIT_MARK, SAMSUNG_ONE_SPACE, SAMSUNG_BIT_MARK, SAMSUNG_ZERO_SPACE, aAddress,
SAMSUNG_ADDRESS_BITS, PROTOCOL_IS_LSB_FIRST);
// Command
// send 8 command bits and then 8 inverted command bits LSB first
aCommand = aCommand & 0xFF;
aCommand = ((~aCommand) << 8) | aCommand;
sendPulseDistanceWidthData(SAMSUNG_BIT_MARK, SAMSUNG_ONE_SPACE, SAMSUNG_BIT_MARK, SAMSUNG_ZERO_SPACE, aCommand,
SAMSUNG_COMMAND16_BITS, PROTOCOL_IS_LSB_FIRST, SEND_STOP_BIT);
tNumberOfCommands--;
// skip last delay!
if (tNumberOfCommands > 0) {
// send repeat in a 110 ms raster
while (millis() - tStartOfFrameMillis < (SAMSUNG_REPEAT_PERIOD / MICROS_IN_ONE_MILLI)) {
delay(1);
}
}
}
IrReceiver.restartAfterSend();
}
//+=============================================================================
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.rawDataPtr->rawlen != ((2 * SAMSUNG_BITS) + 4)
&& decodedIRData.rawDataPtr->rawlen != ((2 * SAMSUNG48_BITS) + 4)) {
IR_DEBUG_PRINT(F("Samsung: "));
IR_DEBUG_PRINT(F("Data length="));
IR_DEBUG_PRINT(decodedIRData.rawDataPtr->rawlen);
IR_DEBUG_PRINTLN(F(" is not 68 or 100"));
return false;
}
// Check header "mark" + "space"
if (!matchMark(decodedIRData.rawDataPtr->rawbuf[1], SAMSUNG_HEADER_MARK)
|| !matchSpace(decodedIRData.rawDataPtr->rawbuf[2], SAMSUNG_HEADER_SPACE)) {
IR_DEBUG_PRINT(F("Samsung: "));
IR_DEBUG_PRINTLN(F("Header mark or space length is wrong"));
return false;
}
if (decodedIRData.rawDataPtr->rawlen == (2 * SAMSUNG48_BITS) + 4) {
/*
* Samsung48
*/
// decode address
if (!decodePulseDistanceData(SAMSUNG_ADDRESS_BITS, 3, SAMSUNG_BIT_MARK, SAMSUNG_ONE_SPACE, SAMSUNG_ZERO_SPACE,
PROTOCOL_IS_LSB_FIRST)) {
IR_DEBUG_PRINT(F("Samsung: "));
IR_DEBUG_PRINTLN(F("Decode failed"));
return false;
}
decodedIRData.address = decodedIRData.decodedRawData;
// decode 32 bit command
if (!decodePulseDistanceData(SAMSUNG_COMMAND32_BITS, 3, SAMSUNG_BIT_MARK, SAMSUNG_ONE_SPACE, SAMSUNG_ZERO_SPACE,
PROTOCOL_IS_LSB_FIRST)) {
IR_DEBUG_PRINT(F("Samsung: "));
IR_DEBUG_PRINTLN(F("Decode failed"));
return false;
}
// Success
// decodedIRData.flags = IRDATA_FLAGS_IS_LSB_FIRST; // Not required, since this is the start value
LongUnion tValue;
tValue.ULong = decodedIRData.decodedRawData;
// receive 2 * (8 bits then 8 inverted bits) LSB first
if (tValue.UByte.HighByte != (uint8_t) (~tValue.UByte.MidHighByte)
&& tValue.UByte.MidLowByte != (uint8_t) (~tValue.UByte.LowByte)) {
decodedIRData.flags = IRDATA_FLAGS_PARITY_FAILED | IRDATA_FLAGS_IS_LSB_FIRST;
}
decodedIRData.command = tValue.UByte.HighByte << 8 | tValue.UByte.MidLowByte;
decodedIRData.numberOfBits = SAMSUNG48_BITS;
} else {
/*
* Samsung32
*/
if (!decodePulseDistanceData(SAMSUNG_BITS, 3, SAMSUNG_BIT_MARK, SAMSUNG_ONE_SPACE, SAMSUNG_ZERO_SPACE,
PROTOCOL_IS_LSB_FIRST)) {
IR_DEBUG_PRINT(F("Samsung: "));
IR_DEBUG_PRINTLN(F("Decode failed"));
return false;
}
LongUnion tValue;
tValue.ULong = decodedIRData.decodedRawData;
decodedIRData.address = tValue.UWord.LowWord;
if (tValue.UByte.MidHighByte == (uint8_t) (~tValue.UByte.HighByte)) {
// 8 bit command protocol
decodedIRData.command = tValue.UByte.MidHighByte; // first 8 bit
} else {
// 16 bit command protocol
decodedIRData.command = tValue.UWord.HighWord; // first 16 bit
}
decodedIRData.numberOfBits = SAMSUNG_BITS;
}
// check for repeat
if (decodedIRData.rawDataPtr->rawbuf[0] < (SAMSUNG_REPEAT_PERIOD / MICROS_PER_TICK)) {
decodedIRData.flags |= IRDATA_FLAGS_IS_REPEAT;
}
decodedIRData.protocol = SAMSUNG;
return true;
}
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 (!decodePulseDistanceData(SAMSUNG_BITS, offset, SAMSUNG_BIT_MARK, SAMSUNG_ONE_SPACE, SAMSUNG_ZERO_SPACE,
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::sendSAMSUNG(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, SEND_STOP_BIT);
IrReceiver.restartAfterSend();
}
/** @}*/
#endif // _IR_SAMSUNG_HPP

View File

@@ -0,0 +1,230 @@
/*
* 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
#include <Arduino.h>
//#define DEBUG // Activate this for lots of lovely debug output from this decoder.
#include "IRremoteInt.h" // evaluates the DEBUG for IR_DEBUG_PRINT
/** \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
//==============================================================================
// see https://www.sbprojects.net/knowledge/ir/sirc.php
// Here http://picprojects.org.uk/projects/sirc/ it is claimed, that many Sony remotes repeat each frame a minimum of 3 times
// LSB first, start bit + 7 command + 5 to 13 address, no stop bit
//
#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 21000 // SONY_HEADER_MARK + SONY_SPACE + 12 * 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_REPEAT_SPACE (SONY_REPEAT_PERIOD - SONY_AVERAGE_DURATION) // 24 ms
/*
* Repeat commands should be sent in a 45 ms raster.
* There is NO delay after the last sent command / repeat!
* @param numberOfBits if == 20 send 13 address bits otherwise only 5 address bits
*/
void IRsend::sendSony(uint16_t aAddress, uint8_t aCommand, uint_fast8_t aNumberOfRepeats, uint8_t numberOfBits) {
// Set IR carrier frequency
enableIROut(SONY_KHZ); // 40 kHz
uint_fast8_t tNumberOfCommands = aNumberOfRepeats + 1;
while (tNumberOfCommands > 0) {
// Header
mark(SONY_HEADER_MARK);
space(SONY_SPACE);
// send 7 command bits LSB first
sendPulseDistanceWidthData(SONY_ONE_MARK, SONY_SPACE, SONY_ZERO_MARK, SONY_SPACE, aCommand, SONY_COMMAND_BITS,
PROTOCOL_IS_LSB_FIRST);
// send 5, 8, 13 address bits LSB first
sendPulseDistanceWidthData(SONY_ONE_MARK, SONY_SPACE, SONY_ZERO_MARK, SONY_SPACE, aAddress,
(numberOfBits - SONY_COMMAND_BITS), PROTOCOL_IS_LSB_FIRST);
tNumberOfCommands--;
// skip last delay!
if (tNumberOfCommands > 0) {
// send repeated command in a 45 ms raster
delay(SONY_REPEAT_SPACE / MICROS_IN_ONE_MILLI);
}
}
IrReceiver.restartAfterSend();
}
//+=============================================================================
bool IRrecv::decodeSony() {
// Check header "mark"
if (!matchMark(decodedIRData.rawDataPtr->rawbuf[1], SONY_HEADER_MARK)) {
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.rawDataPtr->rawlen != (2 * SONY_BITS_MIN) + 2 && decodedIRData.rawDataPtr->rawlen != (2 * SONY_BITS_MAX) + 2
&& decodedIRData.rawDataPtr->rawlen != (2 * SONY_BITS_15) + 2) {
// ??? IR_TRACE_PRINT since I saw this too often
IR_DEBUG_PRINT(F("Sony: "));
IR_DEBUG_PRINT(F("Data length="));
IR_DEBUG_PRINT(decodedIRData.rawDataPtr->rawlen);
IR_DEBUG_PRINTLN(F(" is not 12, 15 or 20"));
return false;
}
// Check header "space"
if (!matchSpace(decodedIRData.rawDataPtr->rawbuf[2], SONY_SPACE)) {
IR_DEBUG_PRINT(F("Sony: "));
IR_DEBUG_PRINTLN(F("Header space length is wrong"));
return false;
}
if (!decodePulseWidthData((decodedIRData.rawDataPtr->rawlen - 1) / 2, 3, SONY_ONE_MARK, SONY_ZERO_MARK, SONY_SPACE,
PROTOCOL_IS_LSB_FIRST)) {
IR_DEBUG_PRINT(F("Sony: "));
IR_DEBUG_PRINTLN(F("Decode failed"));
return false;
}
// Success
// decodedIRData.flags = IRDATA_FLAGS_IS_LSB_FIRST; // Not required, since this is the start value
uint8_t tCommand = decodedIRData.decodedRawData & 0x7F; // first 7 bits
uint16_t tAddress = decodedIRData.decodedRawData >> 7; // next 5 or 8 or 13 bits
/*
* Check for repeat
*/
if (decodedIRData.rawDataPtr->rawbuf[0] < (SONY_REPEAT_PERIOD / MICROS_PER_TICK)) {
decodedIRData.flags = IRDATA_FLAGS_IS_REPEAT | IRDATA_FLAGS_IS_LSB_FIRST;
}
decodedIRData.command = tCommand;
decodedIRData.address = tAddress;
decodedIRData.numberOfBits = (decodedIRData.rawDataPtr->rawlen - 1) / 2;
decodedIRData.protocol = SONY;
return true;
}
#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)) {
IR_DEBUG_PRINTLN(F("IR Gap found"));
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::sendSony(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);
IrReceiver.restartAfterSend();
}
/** @}*/
#endif // _IR_SONY_HPP

View File

@@ -0,0 +1,218 @@
/*
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 + uint_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.cpp
*
* 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
#include <Arduino.h>
//#define DEBUG // Activate this for lots of lovely debug output from this decoder.
#include "IRremoteInt.h" // evaluates the DEBUG for IR_DEBUG_PRINT
//#define SEND_SHUZU 1 // for testing
//#define DECODE_SHUZU 1 // for testing
//==============================================================================
//
//
// 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
#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_SPACE 45000
#define SHUZU_OTHER 1234 // Other things you may need to define
//+=============================================================================
//
void IRsend::sendShuzu(uint16_t aAddress, uint8_t aCommand, uint_fast8_t aNumberOfRepeats) {
// Set IR carrier frequency
enableIROut(38);
uint_fast8_t tNumberOfCommands = aNumberOfRepeats + 1;
while (tNumberOfCommands > 0) {
// Header
mark(SHUZU_HEADER_MARK);
space(SHUZU_HEADER_SPACE);
// Address (device and subdevice)
sendPulseDistanceWidthData(SHUZU_BIT_MARK, SHUZU_ONE_SPACE, SHUZU_BIT_MARK, SHUZU_ZERO_SPACE, aAddress,
SHUZU_ADDRESS_BITS, PROTOCOL_IS_LSB_FIRST); // false -> LSB first
// Command + stop bit
sendPulseDistanceWidthData(SHUZU_BIT_MARK, SHUZU_ONE_SPACE, SHUZU_BIT_MARK, SHUZU_ZERO_SPACE, aCommand,
SHUZU_COMMAND_BITS, PROTOCOL_IS_LSB_FIRST, SEND_STOP_BIT); // false, true -> LSB first, stop bit
tNumberOfCommands--;
// skip last delay!
if (tNumberOfCommands > 0) {
// send repeated command in a fixed raster
delay(SHUZU_REPEAT_SPACE / MICROS_IN_ONE_MILLI);
}
}
IrReceiver.restartAfterSend();
}
//+=============================================================================
//
/*
* First check for right data length
* Next check start bit
* Next try the decode
* Last check stop bit
*/
bool IRrecv::decodeShuzu() {
// 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 "space"
if (!matchMark(decodedIRData.rawDataPtr->rawbuf[1], SHUZU_HEADER_MARK)
|| !matchSpace(decodedIRData.rawDataPtr->rawbuf[2], SHUZU_HEADER_SPACE)) {
IR_DEBUG_PRINT(F("Shuzu: "));
IR_DEBUG_PRINTLN(F("Header mark or space length is wrong"));
return false;
}
// false -> LSB first
if (!decodePulseDistanceData(SHUZU_BITS, 3, SHUZU_BIT_MARK, SHUZU_ONE_SPACE, SHUZU_ZERO_SPACE, PROTOCOL_IS_LSB_FIRST)) {
IR_DEBUG_PRINT(F("Shuzu: "));
IR_DEBUG_PRINTLN(F("Decode failed"));
return false;
}
// Stop bit
if (!matchMark(decodedIRData.rawDataPtr->rawbuf[3 + (2 * SHUZU_BITS)], SHUZU_BIT_MARK)) {
IR_DEBUG_PRINT(F("Shuzu: "));
IR_DEBUG_PRINTLN(F("Stop bit mark length is wrong"));
return false;
}
// Success
// decodedIRData.flags = IRDATA_FLAGS_IS_LSB_FIRST; // Not required, since this is the start value
uint8_t tCommand = decodedIRData.decodedRawData >> SHUZU_ADDRESS_BITS; // upper 8 bits of LSB first value
uint8_t tAddress = decodedIRData.decodedRawData & 0xFFFF; // lowest 16 bit of LSB first value
/*
* Check for repeat
*/
if (decodedIRData.rawDataPtr->rawbuf[0] < ((SHUZU_REPEAT_SPACE + (SHUZU_REPEAT_SPACE / 4)) / MICROS_PER_TICK)) {
decodedIRData.flags = IRDATA_FLAGS_IS_REPEAT | IRDATA_FLAGS_IS_LSB_FIRST;
}
decodedIRData.command = tCommand;
decodedIRData.address = tAddress;
decodedIRData.numberOfBits = SHUZU_BITS;
decodedIRData.protocol = BOSEWAVE; // we have no SHUZU code
return true;
}
#endif // _IR_SHUZU_HPP

View File

@@ -0,0 +1,82 @@
#ifndef _IR_WHYNTER_HPP
#define _IR_WHYNTER_HPP
#include <Arduino.h>
//#define DEBUG // Activate this for lots of lovely debug output from this decoder.
#include "IRremoteInt.h" // evaluates the DEBUG for IR_DEBUG_PRINT
/** \addtogroup Decoder Decoders and encoders for different protocols
* @{
*/
//==============================================================================
// 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
#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
//+=============================================================================
void IRsend::sendWhynter(unsigned long data, int nbits) {
// Set IR carrier frequency
enableIROut(38);
// Start
mark(WHYNTER_BIT_MARK);
space(WHYNTER_ZERO_SPACE);
// Header
mark(WHYNTER_HEADER_MARK);
space(WHYNTER_HEADER_SPACE);
// Data + stop bit
sendPulseDistanceWidthData(WHYNTER_BIT_MARK, WHYNTER_ONE_SPACE, WHYNTER_BIT_MARK, WHYNTER_ZERO_SPACE, data, nbits,
PROTOCOL_IS_MSB_FIRST,
SEND_STOP_BIT);
IrReceiver.restartAfterSend();
}
//+=============================================================================
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.rawDataPtr->rawlen != (2 * WHYNTER_BITS) + 4) {
return false;
}
// Sequence begins with a bit mark and a zero space
if (!matchMark(decodedIRData.rawDataPtr->rawbuf[1], WHYNTER_BIT_MARK)
|| !matchSpace(decodedIRData.rawDataPtr->rawbuf[2], WHYNTER_HEADER_SPACE)) {
IR_DEBUG_PRINT(F("Whynter: "));
IR_DEBUG_PRINTLN(F("Header mark or space length is wrong"));
return false;
}
if (!decodePulseDistanceData(WHYNTER_BITS, 3, WHYNTER_BIT_MARK, WHYNTER_ONE_SPACE, WHYNTER_ZERO_SPACE, PROTOCOL_IS_MSB_FIRST)) {
return false;
}
// trailing mark / stop bit
if (!matchMark(decodedIRData.rawDataPtr->rawbuf[3 + (2 * WHYNTER_BITS)], WHYNTER_BIT_MARK)) {
IR_DEBUG_PRINTLN(F("Stop bit mark length is wrong"));
return false;
}
// Success
decodedIRData.flags = IRDATA_FLAGS_IS_MSB_FIRST;
decodedIRData.numberOfBits = WHYNTER_BITS;
decodedIRData.protocol = WHYNTER;
return true;
}
/** @}*/
#endif // _IR_WHYNTER_HPP

File diff suppressed because it is too large Load Diff