Tasmota/lib/IRremoteESP8266/IRremoteESP8266.cpp

1648 lines
45 KiB
C++

/***************************************************
* IRremote for ESP8266
*
* Based on the IRremote library for Arduino by Ken Shirriff
* Version 0.11 August, 2009
* Copyright 2009 Ken Shirriff
* For details, see
* http://arcfn.com/2009/08/multi-protocol-infrared-remote-library.html
*
* Modified by Paul Stoffregen <paul@pjrc.com> to support other boards and
* timers
* Modified by Mitra Ardron <mitra@mitra.biz>
* Added Sanyo and Mitsubishi controllers
* Modified Sony to spot the repeat codes that some Sony's send
*
* Interrupt code based on NECIRrcv by Joe Knapp
* http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1210243556
* Also influenced by
* http://zovirl.com/2008/11/12/building-a-universal-remote-with-an-arduino/
*
* JVC and Panasonic protocol added by Kristian Lauszus (Thanks to zenwheel and
* other people at the original blog post)
* LG added by Darryl Smith (based on the JVC protocol)
* Whynter A/C ARC-110WD added by Francesco Meschia
* Global Cache IR format sender added by Hisham Khalifa
* (http://www.hishamkhalifa.com)
* Coolix A/C / heatpump added by bakrus
* Denon: sendDenon, decodeDenon added by Massimiliano Pinto
* (from https://github.com/z3t0/Arduino-IRremote/blob/master/ir_Denon.cpp)
* Kelvinator A/C and Sherwood added by crankyoldgit
* Mitsubishi A/C added by crankyoldgit
* (derived from https://github.com/r45635/HVAC-IR-Control)
*
* Updated by markszabo (https://github.com/markszabo/IRremoteESP8266) for
* sending IR code on ESP8266
* Updated by Sebastien Warin (http://sebastien.warin.fr) for receiving IR code
* on ESP8266
*
* GPL license, all text above must be included in any redistribution
****************************************************/
#include "IRremoteESP8266.h"
#include "IRremoteInt.h"
#include "IRKelvinator.h"
#include "IRMitsubishiAC.h"
// These versions of MATCH, MATCH_MARK, and MATCH_SPACE are only for debugging.
// To use them, set DEBUG in IRremoteInt.h
// Normally macros are used for efficiency
#ifdef DEBUG
int MATCH(int measured, int desired) {
Serial.print("Testing: ");
Serial.print(TICKS_LOW(desired), DEC);
Serial.print(" <= ");
Serial.print(measured, DEC);
Serial.print(" <= ");
Serial.println(TICKS_HIGH(desired), DEC);
return measured >= TICKS_LOW(desired) && measured <= TICKS_HIGH(desired);
}
int MATCH_MARK(int measured_ticks, int desired_us) {
Serial.print("Testing mark ");
Serial.print(measured_ticks * USECPERTICK, DEC);
Serial.print(" vs ");
Serial.print(desired_us, DEC);
Serial.print(": ");
Serial.print(TICKS_LOW(desired_us + MARK_EXCESS), DEC);
Serial.print(" <= ");
Serial.print(measured_ticks, DEC);
Serial.print(" <= ");
Serial.println(TICKS_HIGH(desired_us + MARK_EXCESS), DEC);
return measured_ticks >= TICKS_LOW(desired_us + MARK_EXCESS) &&
measured_ticks <= TICKS_HIGH(desired_us + MARK_EXCESS);
}
int MATCH_SPACE(int measured_ticks, int desired_us) {
Serial.print("Testing space ");
Serial.print(measured_ticks * USECPERTICK, DEC);
Serial.print(" vs ");
Serial.print(desired_us, DEC);
Serial.print(": ");
Serial.print(TICKS_LOW(desired_us - MARK_EXCESS), DEC);
Serial.print(" <= ");
Serial.print(measured_ticks, DEC);
Serial.print(" <= ");
Serial.println(TICKS_HIGH(desired_us - MARK_EXCESS), DEC);
return measured_ticks >= TICKS_LOW(desired_us - MARK_EXCESS) &&
measured_ticks <= TICKS_HIGH(desired_us - MARK_EXCESS);
}
#else
int MATCH(int measured, int desired) {return measured >= TICKS_LOW(desired) &&
measured <= TICKS_HIGH(desired);}
int MATCH_MARK(int measured_ticks, int desired_us)
{return MATCH(measured_ticks, (desired_us + MARK_EXCESS));}
int MATCH_SPACE(int measured_ticks, int desired_us)
{return MATCH(measured_ticks, (desired_us - MARK_EXCESS));}
// Debugging versions are in IRremote.cpp
#endif
// IRtimer ---------------------------------------------------------------------
// This class performs a simple time in useconds since instantiated.
// Handles when the system timer wraps around (once).
IRtimer::IRtimer() {
reset();
}
void IRtimer::reset() {
start = micros();
}
uint32_t IRtimer::elapsed() {
uint32_t now = micros();
if (start <= now) // Check if the system timer has wrapped.
return (now - start); // No wrap.
else
return (0xFFFFFFFF - start + now); // Has wrapped.
}
// IRsend ----------------------------------------------------------------------
IRsend::IRsend(int IRsendPin) {
IRpin = IRsendPin;
}
void IRsend::begin() {
pinMode(IRpin, OUTPUT);
}
// Generic method for sending data that is common to most protocols.
// Default to transmitting the Most Significant Bit (MSB) first.
void IRsend::sendData(uint16_t onemark, uint32_t onespace,
uint16_t zeromark, uint32_t zerospace,
uint32_t data, uint8_t nbits, bool MSBfirst) {
if (MSBfirst) // Send the MSB first.
for (uint32_t mask = 1UL << (nbits - 1); mask; mask >>= 1)
if (data & mask) { // 1
mark(onemark);
space(onespace);
} else { // 0
mark(zeromark);
space(zerospace);
}
else { // Send the Least Significant Bit (LSB) first / MSB last.
for (uint8_t bit = 0; bit < nbits; bit++, data >>= 1)
if (data & 1) { // 1
mark(onemark);
space(onespace);
} else { // 0
mark(zeromark);
space(zerospace);
}
}
}
void IRsend::sendCOOLIX(unsigned long data, int nbits) {
// Set IR carrier frequency
enableIROut(38);
// Header
mark(COOLIX_HDR_MARK);
space(COOLIX_HDR_SPACE);
// Data
// Sending 3 bytes of data. Each byte first being sent straight, then followed
// by an inverted version.
unsigned long COOLIXmask;
bool invert = 0; // Initializing
for (int j = 0; j < COOLIX_NBYTES * 2; j++) {
for (int i = nbits; i > nbits-8; i--) {
// Type cast necessary to perform correct for the one byte above 16bit
COOLIXmask = (unsigned long) 1 << (i-1);
if (data & COOLIXmask) { // 1
mark(COOLIX_BIT_MARK);
space(COOLIX_ONE_SPACE);
} else { // 0
mark(COOLIX_BIT_MARK);
space(COOLIX_ZERO_SPACE);
}
}
// Inverts all of the data each time we need to send an inverted byte
data ^= 0xFFFFFFFF;
invert = !invert;
// Subtract 8 from nbits each time we switch to a new byte.
nbits -= invert ? 0 : 8;
}
// Footer
mark(COOLIX_BIT_MARK);
space(COOLIX_ZERO_SPACE); // Stop bit (0)
space(COOLIX_HDR_SPACE); // Pause before repeating
}
void IRsend::sendNEC (unsigned long data, int nbits, unsigned int repeat) {
// Details about timings can be found at:
// http://www.sbprojects.com/knowledge/ir/nec.php
// Set IR carrier frequency
enableIROut(38);
IRtimer usecs = IRtimer();
// Header
mark(NEC_HDR_MARK);
space(NEC_HDR_SPACE);
// Data
sendData(NEC_BIT_MARK, NEC_ONE_SPACE, NEC_BIT_MARK, NEC_ZERO_SPACE,
data, nbits, true);
// Footer
mark(NEC_BIT_MARK);
// Gap to next command.
space(NEC_MIN_COMMAND_LENGTH - usecs.elapsed());
// Optional command repeat sequence.
for (unsigned int i = 0; i < repeat; i++) {
usecs.reset();
mark(NEC_HDR_MARK);
space(NEC_RPT_SPACE);
mark(NEC_BIT_MARK);
// Gap till next command.
space(NEC_MIN_COMMAND_LENGTH - usecs.elapsed());
}
}
void IRsend::sendLG (unsigned long data, int nbits) {
// Set IR carrier frequency
enableIROut(38);
// Header
mark(LG_HDR_MARK);
space(LG_HDR_SPACE);
mark(LG_BIT_MARK);
// Data
for (unsigned long mask = 1UL << (nbits - 1); mask; mask >>= 1) {
if (data & mask) { // 1
space(LG_ONE_SPACE);
mark(LG_BIT_MARK);
} else { // 0
space(LG_ZERO_SPACE);
mark(LG_BIT_MARK);
}
}
// Footer
ledOff();
}
void IRsend::sendWhynter(unsigned long data, int nbits) {
// Set IR carrier frequency
enableIROut(38);
// Header
mark(WHYNTER_ZERO_MARK);
space(WHYNTER_ZERO_SPACE);
mark(WHYNTER_HDR_MARK);
space(WHYNTER_HDR_SPACE);
// Data
for (unsigned long mask = 1UL << (nbits - 1); mask; mask >>= 1) {
if (data & mask) { // 1
mark(WHYNTER_ONE_MARK);
space(WHYNTER_ONE_SPACE);
} else { // 0
mark(WHYNTER_ZERO_MARK);
space(WHYNTER_ZERO_SPACE);
}
}
// Footer
mark(WHYNTER_ZERO_MARK);
space(WHYNTER_ZERO_SPACE);
}
void IRsend::sendSony(unsigned long data, int nbits, unsigned int repeat) {
// Send an IR command to a compatible Sony device.
//
// Args:
// data: IR command to be sent.
// nbits: Nr. of bits of the IR command to be sent.
// repeat: Nr. of additional times the IR command is to be sent.
//
// sendSony() should typically be called with repeat=2 as Sony devices
// expect the code to be sent at least 3 times.
//
// Timings and details are taken from:
// http://www.sbprojects.com/knowledge/ir/sirc.php
enableIROut(40); // Sony devices use a 40kHz IR carrier frequency.
IRtimer usecs = IRtimer();
for (uint16_t i = 0; i <= repeat; i++) { // Typically loop 3 or more times.
usecs.reset();
// Header
mark(SONY_HDR_MARK);
space(SONY_HDR_SPACE);
// Data
sendData(SONY_ONE_MARK, SONY_HDR_SPACE, SONY_ZERO_MARK, SONY_HDR_SPACE,
data, nbits, true);
// Footer
// The Sony protocol requires us to wait 45ms from start of a code to the
// start of the next one. A 10ms minimum gap is also required.
space(max(10000, 45000 - usecs.elapsed()));
}
// A space() is always performed last, so no need to turn off the LED.
}
void IRsend::sendRaw(unsigned int buf[], int len, int hz) {
// Set IR carrier frequency
enableIROut(hz);
for (int i = 0; i < len; i++) {
if (i & 1) { // Odd bit.
space(buf[i]);
} else { // Even bit.
mark(buf[i]);
}
}
ledOff();
}
// Global Cache format w/o emitter ID or request ID. Starts from hertz,
// followed by number of times to emit (count),
// followed by offset for repeats, followed by code as units of periodic time.
void IRsend::sendGC(unsigned int buf[], int len) {
int khz = buf[0]/1000; // GC data starts with frequency in Hz.
enableIROut(khz);
int periodic_time = 1000/khz;
int count = buf[1]; // Max 50 as per GC.
// Data
for (int i = 0; i < count; i++) {
// Account for offset if we're repeating, otherwise start at index 3.
int j = i > 0 ? buf[2] + 2 : 3;
for (; j < len; j++) {
// Convert periodic units to microseconds. Minimum is 80 for actual GC
// units.
int microseconds = buf[j] * periodic_time;
if (j & 1) { // Odd bit.
// Our codes start at an odd index (not even as with sendRaw).
mark(microseconds);
} else { // Even bit.
space(microseconds);
}
}
}
// Footer
ledOff();
}
// Note: first bit must be a one (start bit)
void IRsend::sendRC5(unsigned long data, int nbits) {
// Set IR carrier frequency
enableIROut(36);
// Header
mark(RC5_T1); // First start bit
space(RC5_T1); // Second start bit
mark(RC5_T1); // Second start bit
// Data
for (unsigned long mask = 1UL << (nbits - 1); mask; mask >>= 1) {
if (data & mask) { // 1
space(RC5_T1); // 1 is space, then mark
mark(RC5_T1);
} else { // 0
mark(RC5_T1);
space(RC5_T1);
}
}
// Footer
ledOff();
}
// Caller needs to take care of flipping the toggle bit
void IRsend::sendRC6(unsigned long data, int nbits) {
// Set IR carrier frequency
enableIROut(36);
// Header
mark(RC6_HDR_MARK);
space(RC6_HDR_SPACE);
mark(RC6_T1); // Start bit
space(RC6_T1);
int t;
// Data
for (unsigned long i = 0, mask = 1UL << (nbits - 1); mask; i++, mask >>= 1) {
// The fourth bit we send is a "double width trailer bit".
if (i == 3) {
// double-wide trailer bit
t = 2 * RC6_T1;
} else {
t = RC6_T1;
}
if (data & mask) { // 1
mark(t);
space(t);
} else { // 0
space(t);
mark(t);
}
}
// Footer
ledOff();
}
void IRsend::sendPanasonic(unsigned int address, unsigned long data) {
// Set IR carrier frequency
enableIROut(35);
// Header
mark(PANASONIC_HDR_MARK);
space(PANASONIC_HDR_SPACE);
// Address (16 bits)
sendData(PANASONIC_BIT_MARK, PANASONIC_ONE_SPACE,
PANASONIC_BIT_MARK, PANASONIC_ZERO_SPACE,
address, 16, true);
// Data (32 bits)
sendData(PANASONIC_BIT_MARK, PANASONIC_ONE_SPACE,
PANASONIC_BIT_MARK, PANASONIC_ZERO_SPACE,
data, 32, true);
// Footer
mark(PANASONIC_BIT_MARK);
ledOff();
}
void IRsend::sendJVC(unsigned long data, int nbits, int repeat) {
// Set IR carrier frequency
enableIROut(38);
// Header
if (!repeat) {
mark(JVC_HDR_MARK);
space(JVC_HDR_SPACE);
}
// Data
for (unsigned long mask = 1UL << (nbits - 1); mask; mask >>= 1) {
if (data & mask) { // 1
mark(JVC_BIT_MARK);
space(JVC_ONE_SPACE);
} else { // 0
mark(JVC_BIT_MARK);
space(JVC_ZERO_SPACE);
}
}
// Footer
mark(JVC_BIT_MARK);
ledOff();
}
void IRsend::sendSAMSUNG(unsigned long data, int nbits) {
// Set IR carrier frequency
enableIROut(38);
// Header
mark(SAMSUNG_HDR_MARK);
space(SAMSUNG_HDR_SPACE);
// Data
for (unsigned long mask = 1UL << (nbits - 1); mask; mask >>= 1) {
if (data & mask) { // 1
mark(SAMSUNG_BIT_MARK);
space(SAMSUNG_ONE_SPACE);
} else { // 0
mark(SAMSUNG_BIT_MARK);
space(SAMSUNG_ZERO_SPACE);
}
}
// Footer
mark(SAMSUNG_BIT_MARK);
ledOff();
}
// Denon, from https://github.com/z3t0/Arduino-IRremote/blob/master/ir_Denon.cpp
void IRsend::sendDenon (unsigned long data, int nbits) {
// Set IR carrier frequency
enableIROut(38);
// Header
mark(DENON_HDR_MARK);
space(DENON_HDR_SPACE);
// Data
for (unsigned long mask = 1UL << (nbits - 1); mask; mask >>= 1) {
if (data & mask) { // 1
mark (DENON_BIT_MARK);
space(DENON_ONE_SPACE);
} else { // 0
mark (DENON_BIT_MARK);
space(DENON_ZERO_SPACE);
}
}
// Footer
mark(DENON_BIT_MARK);
ledOff();
}
void IRsend::mark(unsigned int usec) {
// Sends an IR mark for the specified number of microseconds.
// The mark output is modulated at the PWM frequency.
IRtimer usecTimer = IRtimer();
while (usecTimer.elapsed() < usec) {
digitalWrite(IRpin, HIGH);
delayMicroseconds(halfPeriodicTime);
digitalWrite(IRpin, LOW);
// e.g. 38 kHz -> T = 26.31 microsec (periodic time), half of it is 13
delayMicroseconds(halfPeriodicTime);
}
}
void IRsend::ledOff() {
digitalWrite(IRpin, LOW);
}
/* Leave pin off for time (given in microseconds) */
void IRsend::space(unsigned long time) {
// Sends an IR space for the specified number of microseconds.
// A space is no output, so the PWM output is disabled.
ledOff();
if (time == 0) return;
if (time <= 16383) // delayMicroseconds is only accurate to 16383us.
delayMicroseconds(time);
else {
// Invoke a delay(), where possible, to avoid triggering the WDT.
delay(time / 1000UL); // Delay for as many whole ms as we can.
delayMicroseconds((int) time % 1000UL); // Delay the remaining sub-msecond.
}
}
void IRsend::enableIROut(int khz) {
// Enables IR output.
// The khz value controls the modulation frequency in kilohertz.
// T = 1/f but we need T/2 in microsecond and f is in kHz
halfPeriodicTime = 500/khz;
}
/* Sharp and DISH support by Todd Treece
( http://unionbridge.org/design/ircommand )
The Dish send function needs to be repeated 4 times, and the Sharp function
has the necessary repeat built in because of the need to invert the signal.
Sharp protocol documentation:
http://www.sbprojects.com/knowledge/ir/sharp.htm
Here are the LIRC files that I found that seem to match the remote codes
from the oscilloscope:
Sharp LCD TV:
http://lirc.sourceforge.net/remotes/sharp/GA538WJSA
DISH NETWORK (echostar 301):
http://lirc.sourceforge.net/remotes/echostar/301_501_3100_5100_58xx_59xx
For the DISH codes, only send the last for characters of the hex.
i.e. use 0x1C10 instead of 0x0000000000001C10 which is listed in the
linked LIRC file.
*/
void IRsend::sendSharpRaw(unsigned long data, int nbits) {
// Set IR carrier frequency
enableIROut(38);
// Sending codes in bursts of 3 (normal, inverted, normal) makes transmission
// much more reliable. That's the exact behaviour of CD-S6470 remote control.
for (int n = 0; n < 3; n++) {
// Data
for (unsigned long mask = 1UL << (nbits - 1); mask; mask >>= 1) {
if (data & mask) { // 1
mark(SHARP_BIT_MARK);
space(SHARP_ONE_SPACE);
} else { // 0
mark(SHARP_BIT_MARK);
space(SHARP_ZERO_SPACE);
}
}
// Footer
mark(SHARP_BIT_MARK);
space(SHARP_ZERO_SPACE);
delay(40);
data = data ^ SHARP_TOGGLE_MASK;
}
}
// Sharp send compatible with data obtained through decodeSharp
void IRsend::sendSharp(unsigned int address, unsigned int command) {
sendSharpRaw((address << 10) | (command << 2) | 2, 15);
}
void IRsend::sendDISH(unsigned long data, int nbits) {
// Set IR carrier frequency
enableIROut(56);
// Header
mark(DISH_HDR_MARK);
space(DISH_HDR_SPACE);
for (int i = 0; i < nbits; i++) {
if (data & DISH_TOP_BIT) {
mark(DISH_BIT_MARK);
space(DISH_ONE_SPACE);
} else {
mark(DISH_BIT_MARK);
space(DISH_ZERO_SPACE);
}
data <<= 1;
}
// Footer
ledOff();
}
// From https://github.com/mharizanov/Daikin-AC-remote-control-over-the-Internet/tree/master/IRremote
void IRsend::sendDaikin(unsigned char daikin[]) {
sendDaikinChunk(daikin, 8,0);
delay(29);
sendDaikinChunk(daikin, 19,8);
}
void IRsend::sendDaikinChunk(unsigned char buf[], int len, int start) {
int data2;
// Set IR carrier frequency
enableIROut(38);
// Header
mark(DAIKIN_HDR_MARK);
space(DAIKIN_HDR_SPACE);
// Data
for (int i = start; i < start+len; i++) {
data2=buf[i];
for (int j = 0; j < 8; j++) {
if ((1 << j & data2)) {
mark(DAIKIN_ONE_MARK);
space(DAIKIN_ONE_SPACE);
} else {
mark(DAIKIN_ZERO_MARK);
space(DAIKIN_ZERO_SPACE);
}
}
}
// Footer
mark(DAIKIN_ONE_MARK);
space(DAIKIN_ZERO_SPACE);
}
void IRsend::sendKelvinator(unsigned char data[]) {
uint8_t i = 0;
// Set IR carrier frequency
enableIROut(38);
// Header #1
mark(KELVINATOR_HDR_MARK);
space(KELVINATOR_HDR_SPACE);
// Data (command)
// Send the first command data (4 bytes)
for (; i < 4; i++)
sendData(KELVINATOR_BIT_MARK, KELVINATOR_ONE_SPACE, KELVINATOR_BIT_MARK,
KELVINATOR_ZERO_SPACE, data[i], 8, false);
// Send Footer for the command data (3 bits (B010))
sendData(KELVINATOR_BIT_MARK, KELVINATOR_ONE_SPACE, KELVINATOR_BIT_MARK,
KELVINATOR_ZERO_SPACE, KELVINATOR_CMD_FOOTER, 3, false);
// Send an interdata gap.
mark(KELVINATOR_BIT_MARK);
space(KELVINATOR_GAP_SPACE);
// Data (options)
// Send the 1st option chunk of data (4 bytes).
for (; i < 8; i++)
sendData(KELVINATOR_BIT_MARK, KELVINATOR_ONE_SPACE, KELVINATOR_BIT_MARK,
KELVINATOR_ZERO_SPACE, data[i], 8, false);
// Send a double data gap to signify we are starting a new command sequence.
mark(KELVINATOR_BIT_MARK);
space(KELVINATOR_GAP_SPACE * 2);
// Header #2
mark(KELVINATOR_HDR_MARK);
space(KELVINATOR_HDR_SPACE);
// Data (command)
// Send the 2nd command data (4 bytes).
// Basically an almost identical repeat of the earlier command data.
for (; i < 12; i++)
sendData(KELVINATOR_BIT_MARK, KELVINATOR_ONE_SPACE, KELVINATOR_BIT_MARK,
KELVINATOR_ZERO_SPACE, data[i], 8, false);
// Send Footer for the command data (3 bits (B010))
sendData(KELVINATOR_BIT_MARK, KELVINATOR_ONE_SPACE, KELVINATOR_BIT_MARK,
KELVINATOR_ZERO_SPACE, KELVINATOR_CMD_FOOTER, 3, false);
// Send an interdata gap.
mark(KELVINATOR_BIT_MARK);
space(KELVINATOR_GAP_SPACE);
// Data (options)
// Send the 2nd option chunk of data (4 bytes).
// Unlike the commands, definately not a repeat of the earlier option data.
for (; i < KELVINATOR_STATE_LENGTH; i++)
sendData(KELVINATOR_BIT_MARK, KELVINATOR_ONE_SPACE, KELVINATOR_BIT_MARK,
KELVINATOR_ZERO_SPACE, data[i], 8, false);
// Footer
mark(KELVINATOR_BIT_MARK);
ledOff();
}
void IRsend::sendSherwood(unsigned long data, int nbits, unsigned int repeat) {
// Sherwood remote codes appear to be NEC codes with a manditory repeat code.
// i.e. repeat should be >= 1.
sendNEC(data, nbits, max(1, repeat));
}
void IRsend::sendMitsubishiACChunk(uint8_t data) {
// send a chunk(byte) of Mitsubishi AC data
for (uint8_t bit = 0; bit < 8; bit++, data >>= 1) {
if (data & B1) { // 1
mark(MITSUBISHI_AC_BIT_MARK);
space(MITSUBISHI_AC_ONE_SPACE);
} else { // 0
mark(MITSUBISHI_AC_BIT_MARK);
space(MITSUBISHI_AC_ZERO_SPACE);
}
}
}
void IRsend::sendMitsubishiAC(unsigned char data[]) {
// Set IR carrier frequency
enableIROut(38);
// Mitsubishi AC remote sends the packet twice.
for (uint8_t count = 0; count < 2; count++) {
// Header
mark(MITSUBISHI_AC_HDR_MARK);
space(MITSUBISHI_AC_HDR_SPACE);
// Data
for (uint8_t i = 0; i < MITSUBISHI_AC_STATE_LENGTH; i++)
sendMitsubishiACChunk(data[i]);
// Footer
mark(MITSUBISHI_AC_RPT_MARK);
space(MITSUBISHI_AC_RPT_SPACE);
}
}
// ---------------------------------------------------------------
//IRRecv------------------------------------------------------
extern "C" {
#include "user_interface.h"
#include "gpio.h"
}
static ETSTimer timer;
volatile irparams_t irparams;
static void ICACHE_RAM_ATTR read_timeout(void *arg __attribute__((unused))) {
os_intr_lock();
if (irparams.rawlen) {
irparams.rcvstate = STATE_STOP;
}
os_intr_unlock();
}
static void ICACHE_RAM_ATTR gpio_intr() {
uint32_t now = system_get_time();
uint32_t gpio_status = GPIO_REG_READ(GPIO_STATUS_ADDRESS);
static uint32_t start = 0;
os_timer_disarm(&timer);
GPIO_REG_WRITE(GPIO_STATUS_W1TC_ADDRESS, gpio_status);
if (irparams.rawlen >= RAWBUF) {
irparams.overflow = true;
irparams.rcvstate = STATE_STOP;
}
if (irparams.rcvstate == STATE_STOP) {
return;
}
if (irparams.rcvstate == STATE_IDLE) {
irparams.overflow = false;
irparams.rcvstate = STATE_MARK;
irparams.rawbuf[irparams.rawlen++] = 1;
} else {
if (now < start)
irparams.rawbuf[irparams.rawlen++] = (0xFFFFFFFF - start + now) / USECPERTICK + 1;
else
irparams.rawbuf[irparams.rawlen++] = (now - start) / USECPERTICK + 1;
}
start = now;
#define ONCE 0
os_timer_arm(&timer, 15, ONCE);
}
IRrecv::IRrecv(int recvpin) {
irparams.recvpin = recvpin;
}
// initialization
void IRrecv::enableIRIn() {
// initialize state machine variables
irparams.rcvstate = STATE_IDLE;
irparams.rawlen = 0;
// Initialize timer
os_timer_disarm(&timer);
os_timer_setfn(&timer, (os_timer_func_t *)read_timeout, NULL);
// Attach Interrupt
attachInterrupt(irparams.recvpin, gpio_intr, CHANGE);
}
void IRrecv::disableIRIn() {
os_timer_disarm(&timer);
detachInterrupt(irparams.recvpin);
}
void IRrecv::resume() {
irparams.rcvstate = STATE_IDLE;
irparams.rawlen = 0;
}
// Decodes the received IR message
// Returns true if is data ready
// Results of decoding are stored in results
bool IRrecv::decode(decode_results *results) {
results->rawbuf = irparams.rawbuf;
results->rawlen = irparams.rawlen;
results->overflow = irparams.overflow;
if (irparams.rcvstate != STATE_STOP) {
return false;
}
#ifdef DEBUG
Serial.println("Attempting NEC decode");
#endif
if (decodeNEC(results)) {
return true;
}
#ifdef DEBUG
Serial.println("Attempting Sony decode");
#endif
if (decodeSony(results)) {
return true;
}
/*
#ifdef DEBUG
Serial.println("Attempting Sanyo decode");
#endif
if (decodeSanyo(results)) {
return true;
}*/
#ifdef DEBUG
Serial.println("Attempting Mitsubishi decode");
#endif
if (decodeMitsubishi(results)) {
return true;
}
#ifdef DEBUG
Serial.println("Attempting RC5 decode");
#endif
if (decodeRC5(results)) {
return true;
}
#ifdef DEBUG
Serial.println("Attempting RC6 decode");
#endif
if (decodeRC6(results)) {
return true;
}
#ifdef DEBUG
Serial.println("Attempting Panasonic decode");
#endif
if (decodePanasonic(results)) {
return true;
}
#ifdef DEBUG
Serial.println("Attempting LG decode");
#endif
if (decodeLG(results)) {
return true;
}
#ifdef DEBUG
Serial.println("Attempting JVC decode");
#endif
if (decodeJVC(results)) {
return true;
}
#ifdef DEBUG
Serial.println("Attempting SAMSUNG decode");
#endif
if (decodeSAMSUNG(results)) {
return true;
}
#ifdef DEBUG
Serial.println("Attempting Whynter decode");
#endif
if (decodeWhynter(results)) {
return true;
}
// decodeHash returns a hash on any input.
// Thus, it needs to be last in the list.
// If you add any decodes, add them before this.
if (decodeHash(results)) {
return true;
}
// Throw away and start over
resume();
return false;
}
// NECs have a repeat only 4 items long
bool IRrecv::decodeNEC(decode_results *results) {
long data = 0;
int offset = 1; // Skip initial space
// Initial mark
if (!MATCH_MARK(results->rawbuf[offset], NEC_HDR_MARK)) {
return false;
}
offset++;
// Check for repeat
if (irparams.rawlen == 4 &&
MATCH_SPACE(results->rawbuf[offset], NEC_RPT_SPACE) &&
MATCH_MARK(results->rawbuf[offset+1], NEC_BIT_MARK)) {
results->bits = 0;
results->value = REPEAT;
results->decode_type = NEC;
return true;
}
if (irparams.rawlen < 2 * NEC_BITS + 4) {
return false;
}
// Initial space
if (!MATCH_SPACE(results->rawbuf[offset], NEC_HDR_SPACE)) {
return false;
}
offset++;
for (int i = 0; i < NEC_BITS; i++) {
if (!MATCH_MARK(results->rawbuf[offset], NEC_BIT_MARK)) {
return false;
}
offset++;
if (MATCH_SPACE(results->rawbuf[offset], NEC_ONE_SPACE)) {
data = (data << 1) | 1;
} else if (MATCH_SPACE(results->rawbuf[offset], NEC_ZERO_SPACE)) {
data <<= 1;
} else {
return false;
}
offset++;
}
// Success
results->bits = NEC_BITS;
results->value = data;
results->decode_type = NEC;
return true;
}
bool IRrecv::decodeSony(decode_results *results) {
long data = 0;
if (irparams.rawlen < 2 * SONY_BITS + 2) {
return false;
}
int offset = 0; // Dont skip first space, check its size
/*
// Some Sony's deliver repeats fast after first
// unfortunately can't spot difference from of repeat from two fast clicks
if (results->rawbuf[offset] < SONY_DOUBLE_SPACE_USECS) {
// Serial.print("IR Gap found: ");
results->bits = 0;
results->value = REPEAT;
results->decode_type = SANYO;
return true;
}*/
offset++;
// Initial mark
if (!MATCH_MARK(results->rawbuf[offset], SONY_HDR_MARK)) {
return false;
}
offset++;
while (offset + 1 < irparams.rawlen) {
if (!MATCH_SPACE(results->rawbuf[offset], SONY_HDR_SPACE)) {
break;
}
offset++;
if (MATCH_MARK(results->rawbuf[offset], SONY_ONE_MARK)) {
data = (data << 1) | 1;
} else if (MATCH_MARK(results->rawbuf[offset], SONY_ZERO_MARK)) {
data <<= 1;
} else {
return false;
}
offset++;
}
// Success
results->bits = (offset - 1) / 2;
if (results->bits < 12) {
results->bits = 0;
return false;
}
results->value = data;
results->decode_type = SONY;
return true;
}
bool IRrecv::decodeWhynter(decode_results *results) {
long data = 0;
if (irparams.rawlen < 2 * WHYNTER_BITS + 6) {
return false;
}
int offset = 1; // Skip first space
// sequence begins with a bit mark and a zero space
if (!MATCH_MARK(results->rawbuf[offset], WHYNTER_BIT_MARK)) {
return false;
}
offset++;
if (!MATCH_SPACE(results->rawbuf[offset], WHYNTER_ZERO_SPACE)) {
return false;
}
offset++;
// header mark and space
if (!MATCH_MARK(results->rawbuf[offset], WHYNTER_HDR_MARK)) {
return false;
}
offset++;
if (!MATCH_SPACE(results->rawbuf[offset], WHYNTER_HDR_SPACE)) {
return false;
}
offset++;
// data bits
for (int i = 0; i < WHYNTER_BITS; i++) {
if (!MATCH_MARK(results->rawbuf[offset], WHYNTER_BIT_MARK)) {
return false;
}
offset++;
if (MATCH_SPACE(results->rawbuf[offset], WHYNTER_ONE_SPACE)) {
data = (data << 1) | 1;
} else if (MATCH_SPACE(results->rawbuf[offset],WHYNTER_ZERO_SPACE)) {
data <<= 1;
} else {
return false;
}
offset++;
}
// trailing mark
if (!MATCH_MARK(results->rawbuf[offset], WHYNTER_BIT_MARK)) {
return false;
}
// Success
results->bits = WHYNTER_BITS;
results->value = data;
results->decode_type = WHYNTER;
return true;
}
// I think this is a Sanyo decoder - serial = SA 8650B
// Looks like Sony except for timings, 48 chars of data and time/space different
bool IRrecv::decodeSanyo(decode_results *results) {
long data = 0;
if (irparams.rawlen < 2 * SANYO_BITS + 2) {
return false;
}
int offset = 1; // Skip first space
// Initial space
/* Put this back in for debugging - note can't use #DEBUG as if Debug on we don't see the repeat cos of the delay
Serial.print("IR Gap: ");
Serial.println( results->rawbuf[offset]);
Serial.println( "test against:");
Serial.println(results->rawbuf[offset]);
*/
if (results->rawbuf[offset] < SANYO_DOUBLE_SPACE_USECS) {
// Serial.print("IR Gap found: ");
results->bits = 0;
results->value = REPEAT;
results->decode_type = SANYO;
return true;
}
offset++;
// Initial mark
if (!MATCH_MARK(results->rawbuf[offset], SANYO_HDR_MARK)) {
return false;
}
offset++;
// Skip Second Mark
if (!MATCH_MARK(results->rawbuf[offset], SANYO_HDR_MARK)) {
return false;
}
offset++;
while (offset + 1 < irparams.rawlen) {
if (!MATCH_SPACE(results->rawbuf[offset], SANYO_HDR_SPACE)) {
break;
}
offset++;
if (MATCH_MARK(results->rawbuf[offset], SANYO_ONE_MARK)) {
data = (data << 1) | 1;
} else if (MATCH_MARK(results->rawbuf[offset], SANYO_ZERO_MARK)) {
data <<= 1;
} else {
return false;
}
offset++;
}
// Success
results->bits = (offset - 1) / 2;
if (results->bits < 12) {
results->bits = 0;
return false;
}
results->value = data;
results->decode_type = SANYO;
return true;
}
// Looks like Sony except for timings, 48 chars of data and time/space different
bool IRrecv::decodeMitsubishi(decode_results *results) {
// Serial.print("?!? decoding Mitsubishi:");Serial.print(irparams.rawlen);
// Serial.print(" want "); Serial.println( 2 * MITSUBISHI_BITS + 2);
long data = 0;
if (irparams.rawlen < 2 * MITSUBISHI_BITS + 2) {
return false;
}
int offset = 1; // Skip first space
// Initial space
/* Put this back in for debugging - note can't use #DEBUG as if Debug on we
don't see the repeat cos of the delay
Serial.print("IR Gap: ");
Serial.println( results->rawbuf[offset]);
Serial.println( "test against:");
Serial.println(results->rawbuf[offset]);
*/
/* Not seeing double keys from Mitsubishi
if (results->rawbuf[offset] < MITSUBISHI_DOUBLE_SPACE_USECS) {
// Serial.print("IR Gap found: ");
results->bits = 0;
results->value = REPEAT;
results->decode_type = MITSUBISHI;
return true;
}
*/
offset++;
// Typical
// 14200 7 41 7 42 7 42 7 17 7 17 7 18 7 41 7 18 7 17 7 17 7 18 7 41 8 17 7 17 7 18 7 17 7
// Initial Space
if (!MATCH_MARK(results->rawbuf[offset], MITSUBISHI_HDR_SPACE)) {
return false;
}
offset++;
while (offset + 1 < irparams.rawlen) {
if (MATCH_MARK(results->rawbuf[offset], MITSUBISHI_ONE_MARK)) {
data = (data << 1) | 1;
} else if (MATCH_MARK(results->rawbuf[offset], MITSUBISHI_ZERO_MARK)) {
data <<= 1;
} else {
// Serial.println("A"); Serial.println(offset); Serial.println(results->rawbuf[offset]);
return false;
}
offset++;
if (!MATCH_SPACE(results->rawbuf[offset], MITSUBISHI_HDR_SPACE)) {
// Serial.println("B"); Serial.println(offset); Serial.println(results->rawbuf[offset]);
break;
}
offset++;
}
// Success
results->bits = (offset - 1) / 2;
if (results->bits < MITSUBISHI_BITS) {
results->bits = 0;
return false;
}
results->value = data;
results->decode_type = MITSUBISHI;
return true;
}
// Gets one undecoded level at a time from the raw buffer.
// The RC5/6 decoding is easier if the data is broken into time intervals.
// E.g. if the buffer has MARK for 2 time intervals and SPACE for 1,
// successive calls to getRClevel will return MARK, MARK, SPACE.
// offset and used are updated to keep track of the current position.
// t1 is the time interval for a single bit in microseconds.
// Returns -1 for error (measured time interval is not a multiple of t1).
int IRrecv::getRClevel(decode_results *results, int *offset, int *used,
int t1) {
if (*offset >= results->rawlen) {
// After end of recorded buffer, assume SPACE.
return SPACE;
}
int width = results->rawbuf[*offset];
int val = ((*offset) % 2) ? MARK : SPACE;
int correction = (val == MARK) ? MARK_EXCESS : - MARK_EXCESS;
int avail;
if (MATCH(width, t1 + correction)) {
avail = 1;
} else if (MATCH(width, 2*t1 + correction)) {
avail = 2;
} else if (MATCH(width, 3*t1 + correction)) {
avail = 3;
} else {
return -1;
}
(*used)++;
if (*used >= avail) {
*used = 0;
(*offset)++;
}
#ifdef DEBUG
if (val == MARK) {
Serial.println("MARK");
} else {
Serial.println("SPACE");
}
#endif
return val;
}
bool IRrecv::decodeRC5(decode_results *results) {
if (irparams.rawlen < MIN_RC5_SAMPLES + 2) {
return false;
}
int offset = 1; // Skip gap space
long data = 0;
int used = 0;
// Get start bits
if (getRClevel(results, &offset, &used, RC5_T1) != MARK) return false;
if (getRClevel(results, &offset, &used, RC5_T1) != SPACE) return false;
if (getRClevel(results, &offset, &used, RC5_T1) != MARK) return false;
int nbits;
for (nbits = 0; offset < irparams.rawlen; nbits++) {
int levelA = getRClevel(results, &offset, &used, RC5_T1);
int levelB = getRClevel(results, &offset, &used, RC5_T1);
if (levelA == SPACE && levelB == MARK) {
// 1 bit
data = (data << 1) | 1;
} else if (levelA == MARK && levelB == SPACE) {
// zero bit
data <<= 1;
} else {
return false;
}
}
// Success
results->bits = nbits;
results->value = data;
results->decode_type = RC5;
return true;
}
bool IRrecv::decodeRC6(decode_results *results) {
if (results->rawlen < MIN_RC6_SAMPLES) {
return false;
}
int offset = 1; // Skip first space
// Initial mark
if (!MATCH_MARK(results->rawbuf[offset], RC6_HDR_MARK)) {
return false;
}
offset++;
if (!MATCH_SPACE(results->rawbuf[offset], RC6_HDR_SPACE)) {
return false;
}
offset++;
long data = 0;
int used = 0;
// Get start bit (1)
if (getRClevel(results, &offset, &used, RC6_T1) != MARK) return false;
if (getRClevel(results, &offset, &used, RC6_T1) != SPACE) return false;
int nbits;
for (nbits = 0; offset < results->rawlen; nbits++) {
int levelA, levelB; // Next two levels
levelA = getRClevel(results, &offset, &used, RC6_T1);
if (nbits == 3) {
// T bit is double wide; make sure second half matches
if (levelA != getRClevel(results, &offset, &used, RC6_T1)) return false;
}
levelB = getRClevel(results, &offset, &used, RC6_T1);
if (nbits == 3) {
// T bit is double wide; make sure second half matches
if (levelB != getRClevel(results, &offset, &used, RC6_T1)) return false;
}
if (levelA == MARK && levelB == SPACE) { // reversed compared to RC5
// 1 bit
data = (data << 1) | 1;
} else if (levelA == SPACE && levelB == MARK) {
// zero bit
data <<= 1;
} else {
return false; // Error
}
}
// Success
results->bits = nbits;
results->value = data;
results->decode_type = RC6;
return true;
}
bool IRrecv::decodePanasonic(decode_results *results) {
unsigned long long data = 0;
int offset = 1; // Dont skip first space
if (!MATCH_MARK(results->rawbuf[offset], PANASONIC_HDR_MARK)) {
return false;
}
offset++;
if (!MATCH_MARK(results->rawbuf[offset], PANASONIC_HDR_SPACE)) {
return false;
}
offset++;
// decode address
for (int i = 0; i < PANASONIC_BITS; i++) {
if (!MATCH(results->rawbuf[offset++], PANASONIC_BIT_MARK)) {
return false;
}
if (MATCH(results->rawbuf[offset],PANASONIC_ONE_SPACE)) {
data = (data << 1) | 1;
} else if (MATCH(results->rawbuf[offset],PANASONIC_ZERO_SPACE)) {
data <<= 1;
} else {
return false;
}
offset++;
}
results->value = (unsigned long)data;
results->panasonicAddress = (unsigned int)(data >> 32);
results->decode_type = PANASONIC;
results->bits = PANASONIC_BITS;
return true;
}
bool IRrecv::decodeLG(decode_results *results) {
long data = 0;
int offset = 1; // Skip first space
// Initial mark
if (!MATCH_MARK(results->rawbuf[offset], LG_HDR_MARK)) {
return false;
}
offset++;
if (irparams.rawlen < 2 * LG_BITS + 1 ) {
return false;
}
// Initial space
if (!MATCH_SPACE(results->rawbuf[offset], LG_HDR_SPACE)) {
return false;
}
offset++;
for (int i = 0; i < LG_BITS; i++) {
if (!MATCH_MARK(results->rawbuf[offset], LG_BIT_MARK)) {
return false;
}
offset++;
if (MATCH_SPACE(results->rawbuf[offset], LG_ONE_SPACE)) {
data = (data << 1) | 1;
} else if (MATCH_SPACE(results->rawbuf[offset], LG_ZERO_SPACE)) {
data <<= 1;
} else {
return false;
}
offset++;
}
//Stop bit
if (!MATCH_MARK(results->rawbuf[offset], LG_BIT_MARK)){
return false;
}
// Success
results->bits = LG_BITS;
results->value = data;
results->decode_type = LG;
return true;
}
bool IRrecv::decodeJVC(decode_results *results) {
long data = 0;
int offset = 1; // Skip first space
// Check for repeat
if (irparams.rawlen - 1 == 33 &&
MATCH_MARK(results->rawbuf[offset], JVC_BIT_MARK) &&
MATCH_MARK(results->rawbuf[irparams.rawlen-1], JVC_BIT_MARK)) {
results->bits = 0;
results->value = REPEAT;
results->decode_type = JVC;
return true;
}
// Initial mark
if (!MATCH_MARK(results->rawbuf[offset], JVC_HDR_MARK)) {
return false;
}
offset++;
if (irparams.rawlen < 2 * JVC_BITS + 1 ) {
return false;
}
// Initial space
if (!MATCH_SPACE(results->rawbuf[offset], JVC_HDR_SPACE)) {
return false;
}
offset++;
for (int i = 0; i < JVC_BITS; i++) {
if (!MATCH_MARK(results->rawbuf[offset], JVC_BIT_MARK)) {
return false;
}
offset++;
if (MATCH_SPACE(results->rawbuf[offset], JVC_ONE_SPACE)) {
data = (data << 1) | 1;
} else if (MATCH_SPACE(results->rawbuf[offset], JVC_ZERO_SPACE)) {
data <<= 1;
} else {
return false;
}
offset++;
}
//Stop bit
if (!MATCH_MARK(results->rawbuf[offset], JVC_BIT_MARK)) {
return false;
}
// Success
results->bits = JVC_BITS;
results->value = data;
results->decode_type = JVC;
return true;
}
// SAMSUNGs have a repeat only 4 items long
bool IRrecv::decodeSAMSUNG(decode_results *results) {
long data = 0;
int offset = 1; // Dont skip first space
// Initial mark
if (!MATCH_MARK(results->rawbuf[offset], SAMSUNG_HDR_MARK)) {
return false;
}
offset++;
// Check for repeat
if (irparams.rawlen == 4 &&
MATCH_SPACE(results->rawbuf[offset], SAMSUNG_RPT_SPACE) &&
MATCH_MARK(results->rawbuf[offset+1], SAMSUNG_BIT_MARK)) {
results->bits = 0;
results->value = REPEAT;
results->decode_type = SAMSUNG;
return true;
}
if (irparams.rawlen < 2 * SAMSUNG_BITS + 2) {
return false;
}
// Initial space
if (!MATCH_SPACE(results->rawbuf[offset], SAMSUNG_HDR_SPACE)) {
return false;
}
offset++;
for (int i = 0; i < SAMSUNG_BITS; i++) {
if (!MATCH_MARK(results->rawbuf[offset], SAMSUNG_BIT_MARK)) {
return false;
}
offset++;
if (MATCH_SPACE(results->rawbuf[offset], SAMSUNG_ONE_SPACE)) {
data = (data << 1) | 1;
} else if (MATCH_SPACE(results->rawbuf[offset], SAMSUNG_ZERO_SPACE)) {
data <<= 1;
} else {
return false;
}
offset++;
}
// Success
results->bits = SAMSUNG_BITS;
results->value = data;
results->decode_type = SAMSUNG;
return true;
}
// From https://github.com/mharizanov/Daikin-AC-remote-control-over-the-Internet/tree/master/IRremote
// decoding not actually tested
bool IRrecv::decodeDaikin(decode_results *results) {
long data = 0;
int offset = 1; // Skip first space
if (irparams.rawlen < 2 * DAIKIN_BITS + 4) {
//return false;
}
// Initial mark
if (!MATCH_MARK(results->rawbuf[offset], DAIKIN_HDR_MARK)) {
return false;
}
offset++;
if (!MATCH_SPACE(results->rawbuf[offset], DAIKIN_HDR_SPACE)) {
return false;
}
offset++;
for (int i = 0; i < 32; i++) {
if (!MATCH_MARK(results->rawbuf[offset], DAIKIN_ONE_MARK)) {
return false;
}
offset++;
if (MATCH_SPACE(results->rawbuf[offset], DAIKIN_ONE_SPACE)) {
data = (data << 1) | 1;
} else if (MATCH_SPACE(results->rawbuf[offset], DAIKIN_ZERO_SPACE)) {
data <<= 1;
} else {
return false;
}
offset++;
}
unsigned long number = data ; // some number...
int bits = 32 ; // nr of bits in some number
unsigned long reversed = 0;
for ( int b=0 ; b < bits ; b++ ) {
reversed = ( reversed << 1 ) | ( 0x0001 & ( number >> b ) );
}
Serial.print ("Code ");
Serial.println (reversed, HEX);
//==========
for (int i = 0; i < 32; i++) {
if (!MATCH_MARK(results->rawbuf[offset], DAIKIN_ONE_MARK)) {
return false;
}
offset++;
if (MATCH_SPACE(results->rawbuf[offset], DAIKIN_ONE_SPACE)) {
data = (data << 1) | 1;
} else if (MATCH_SPACE(results->rawbuf[offset], DAIKIN_ZERO_SPACE)) {
data <<= 1;
} else {
return false;
}
offset++;
}
number = data ; // some number...
bits = 32 ; // nr of bits in some number
reversed = 0;
for ( int b=0 ; b < bits ; b++ ) {
reversed = ( reversed << 1 ) | ( 0x0001 & ( number >> b ) );
}
//Serial.print ("Code2 ");
//Serial.println (reversed, HEX);
//===========
if (!MATCH_SPACE(results->rawbuf[offset], 29000)) {
//Serial.println ("no gap");
return false;
}
offset++;
// Success
results->bits = DAIKIN_BITS;
results->value = reversed;
results->decode_type = DAIKIN;
return true;
}
// Denon, from https://github.com/z3t0/Arduino-IRremote/blob/master/ir_Denon.cpp
bool IRrecv::decodeDenon (decode_results *results) {
unsigned long data = 0; // Somewhere to build our code
int offset = 1; // Skip the Gap reading
// Check we have the right amount of data
if (irparams.rawlen != 1 + 2 + (2 * DENON_BITS) + 1) {
return false;
}
// Check initial Mark+Space match
if (!MATCH_MARK (results->rawbuf[offset++], DENON_HDR_MARK )) {
return false;
}
if (!MATCH_SPACE(results->rawbuf[offset++], DENON_HDR_SPACE)) {
return false;
}
// Read the bits in
for (int i = 0; i < DENON_BITS; i++) {
// Each bit looks like: DENON_MARK + DENON_SPACE_1 -> 1
// or : DENON_MARK + DENON_SPACE_0 -> 0
if (!MATCH_MARK(results->rawbuf[offset++], DENON_BIT_MARK)) {
return false;
}
// IR data is big-endian, so we shuffle it in from the right:
if (MATCH_SPACE(results->rawbuf[offset], DENON_ONE_SPACE)) {
data = (data << 1) | 1;
} else if (MATCH_SPACE(results->rawbuf[offset], DENON_ZERO_SPACE)) {
data = (data << 1) | 0;
} else {
return false;
}
offset++;
}
// Success
results->bits = DENON_BITS;
results->value = data;
results->decode_type = DENON;
return true;
}
/* -----------------------------------------------------------------------
* hashdecode - decode an arbitrary IR code.
* Instead of decoding using a standard encoding scheme
* (e.g. Sony, NEC, RC5), the code is hashed to a 32-bit value.
*
* The algorithm: look at the sequence of MARK signals, and see if each one
* is shorter (0), the same length (1), or longer (2) than the previous.
* Do the same with the SPACE signals. Hszh the resulting sequence of 0's,
* 1's, and 2's to a 32-bit value. This will give a unique value for each
* different code (probably), for most code systems.
*
* http://arcfn.com/2010/01/using-arbitrary-remotes-with-arduino.html
*/
// Compare two tick values, returning 0 if newval is shorter,
// 1 if newval is equal, and 2 if newval is longer
// Use a tolerance of 20%
int IRrecv::compare(unsigned int oldval, unsigned int newval) {
if (newval < oldval * .8) {
return 0;
} else if (oldval < newval * .8) {
return 2;
} else {
return 1;
}
}
// Use FNV hash algorithm: http://isthe.com/chongo/tech/comp/fnv/#FNV-param
#define FNV_PRIME_32 16777619
#define FNV_BASIS_32 2166136261
/* Converts the raw code values into a 32-bit hash code.
* Hopefully this code is unique for each button.
* This isn't a "real" decoding, just an arbitrary value.
*/
bool IRrecv::decodeHash(decode_results *results) {
// Require at least 6 samples to prevent triggering on noise
if (results->rawlen < 6) {
return false;
}
long hash = FNV_BASIS_32;
for (int i = 1; i+2 < results->rawlen; i++) {
int value = compare(results->rawbuf[i], results->rawbuf[i+2]);
// Add value into the hash
hash = (hash * FNV_PRIME_32) ^ value;
}
results->value = hash;
results->bits = 32;
results->decode_type = UNKNOWN;
return true;
}
// ---------------------------------------------------------------