diff --git a/.travis.yml b/.travis.yml index 151da2f..e85b03e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -18,6 +18,7 @@ before_deploy: - git config --global user.name "Caffreyfans" - git config --global user.email "Caffreyfans@163.com" - mkdir tmp && cd tmp + - cp ../.pio/build/esp8266-1m*/firmware.bin IRbaby1m.bin - cp ../.pio/build/esp8266-2m*/firmware.bin IRbaby2m.bin - cp ../.pio/build/esp8266-4m*/firmware.bin IRbaby4m.bin - wget irbaby.caffreyfans.top/tools/FlashESP8266.exe diff --git a/README.md b/README.md index 107427b..f6c461b 100644 --- a/README.md +++ b/README.md @@ -8,6 +8,6 @@ - [x] OTA - [x] MQTT - [x] UDP -- [x] IREXT +- [x] IRext - [x] IRsend - [x] IRreceive \ No newline at end of file diff --git a/README_zh.md b/README_zh.md index 46c2d1c..2f8fef8 100644 --- a/README_zh.md +++ b/README_zh.md @@ -1,4 +1,5 @@ # IRbaby-Firmware + ## 编译状态 [![Build Status](https://www.travis-ci.org/Caffreyfans/IRbaby-firmware.svg?branch=master)](https://www.travis-ci.org/Caffreyfans/IRbaby-firmware) @@ -8,6 +9,6 @@ - [x] OTA - [x] MQTT - [x] UDP -- [x] IREXT +- [x] IRext - [x] IRsend - [x] IRreceive \ No newline at end of file diff --git a/lib/IRremoteESP8266_ID1089/src/IRremoteESP8266.h b/lib/IRremoteESP8266_ID1089/src/IRremoteESP8266.h index 3d1860d..fb8098c 100644 --- a/lib/IRremoteESP8266_ID1089/src/IRremoteESP8266.h +++ b/lib/IRremoteESP8266_ID1089/src/IRremoteESP8266.h @@ -1,4 +1,4 @@ - /*************************************************** +/*************************************************** * IRremote for ESP8266 * * Based on the IRremote library for Arduino by Ken Shirriff @@ -54,163 +54,163 @@ // Disable (set to false) all the protocols you do not need/want! // The Air Conditioner protocols are the most expensive memory-wise. // -#define DECODE_HASH true // Semi-unique code for unknown messages +#define DECODE_HASH true // Semi-unique code for unknown messages -#define SEND_RAW true +#define SEND_RAW true -#define DECODE_NEC true -#define SEND_NEC true +#define DECODE_NEC false +#define SEND_NEC false -#define DECODE_SHERWOOD true // Doesn't exist. Actually is DECODE_NEC -#define SEND_SHERWOOD true +#define DECODE_SHERWOOD false // Doesn't exist. Actually is DECODE_NEC +#define SEND_SHERWOOD false -#define DECODE_RC5 true -#define SEND_RC5 true +#define DECODE_RC5 false +#define SEND_RC5 false -#define DECODE_RC6 true -#define SEND_RC6 true +#define DECODE_RC6 false +#define SEND_RC6 false -#define DECODE_RCMM true -#define SEND_RCMM true +#define DECODE_RCMM false +#define SEND_RCMM false -#define DECODE_SONY true -#define SEND_SONY true +#define DECODE_SONY false +#define SEND_SONY false -#define DECODE_PANASONIC true -#define SEND_PANASONIC true +#define DECODE_PANASONIC false +#define SEND_PANASONIC false -#define DECODE_JVC true -#define SEND_JVC true +#define DECODE_JVC false +#define SEND_JVC false -#define DECODE_SAMSUNG true -#define SEND_SAMSUNG true +#define DECODE_SAMSUNG false +#define SEND_SAMSUNG false -#define DECODE_SAMSUNG_AC true -#define SEND_SAMSUNG_AC true +#define DECODE_SAMSUNG_AC false +#define SEND_SAMSUNG_AC false -#define DECODE_WHYNTER true -#define SEND_WHYNTER true +#define DECODE_WHYNTER false +#define SEND_WHYNTER false -#define DECODE_AIWA_RC_T501 true -#define SEND_AIWA_RC_T501 true +#define DECODE_AIWA_RC_T501 false +#define SEND_AIWA_RC_T501 false -#define DECODE_LG true -#define SEND_LG true +#define DECODE_LG false +#define SEND_LG false -#define DECODE_SANYO true -#define SEND_SANYO true +#define DECODE_SANYO false +#define SEND_SANYO false -#define DECODE_MITSUBISHI true -#define SEND_MITSUBISHI true +#define DECODE_MITSUBISHI false +#define SEND_MITSUBISHI false -#define DECODE_MITSUBISHI2 true -#define SEND_MITSUBISHI2 true +#define DECODE_MITSUBISHI2 false +#define SEND_MITSUBISHI2 false -#define DECODE_DISH true -#define SEND_DISH true +#define DECODE_DISH false +#define SEND_DISH false -#define DECODE_SHARP true -#define SEND_SHARP true +#define DECODE_SHARP false +#define SEND_SHARP false -#define DECODE_DENON true -#define SEND_DENON true +#define DECODE_DENON false +#define SEND_DENON false -#define DECODE_KELVINATOR true -#define SEND_KELVINATOR true +#define DECODE_KELVINATOR false +#define SEND_KELVINATOR false -#define DECODE_MITSUBISHI_AC true // Beta. -#define SEND_MITSUBISHI_AC true +#define DECODE_MITSUBISHI_AC false // Beta. +#define SEND_MITSUBISHI_AC false -#define DECODE_FUJITSU_AC true -#define SEND_FUJITSU_AC true +#define DECODE_FUJITSU_AC false +#define SEND_FUJITSU_AC false -#define DECODE_DAIKIN true -#define SEND_DAIKIN true +#define DECODE_DAIKIN false +#define SEND_DAIKIN false -#define DECODE_COOLIX true -#define SEND_COOLIX true +#define DECODE_COOLIX false +#define SEND_COOLIX false -#define DECODE_GLOBALCACHE false // Not written. -#define SEND_GLOBALCACHE true +#define DECODE_GLOBALCACHE false // Not written. +#define SEND_GLOBALCACHE false -#define DECODE_GREE true -#define SEND_GREE true +#define DECODE_GREE false +#define SEND_GREE false -#define DECODE_PRONTO false // Not written. -#define SEND_PRONTO true +#define DECODE_PRONTO false // Not written. +#define SEND_PRONTO false -#define DECODE_ARGO false // Not written. -#define SEND_ARGO true +#define DECODE_ARGO false // Not written. +#define SEND_ARGO false -#define DECODE_TROTEC false // Not implemented. -#define SEND_TROTEC true +#define DECODE_TROTEC false // Not implemented. +#define SEND_TROTEC false -#define DECODE_NIKAI true -#define SEND_NIKAI true +#define DECODE_NIKAI false +#define SEND_NIKAI false -#define DECODE_TOSHIBA_AC true -#define SEND_TOSHIBA_AC true +#define DECODE_TOSHIBA_AC false +#define SEND_TOSHIBA_AC false -#define DECODE_MAGIQUEST true -#define SEND_MAGIQUEST true +#define DECODE_MAGIQUEST false +#define SEND_MAGIQUEST false -#define DECODE_MIDEA true -#define SEND_MIDEA true +#define DECODE_MIDEA false +#define SEND_MIDEA false -#define DECODE_LASERTAG true -#define SEND_LASERTAG true +#define DECODE_LASERTAG false +#define SEND_LASERTAG false -#define DECODE_CARRIER_AC true -#define SEND_CARRIER_AC true +#define DECODE_CARRIER_AC false +#define SEND_CARRIER_AC false -#define DECODE_HAIER_AC true -#define SEND_HAIER_AC true +#define DECODE_HAIER_AC false +#define SEND_HAIER_AC false -#define DECODE_HITACHI_AC true -#define SEND_HITACHI_AC true +#define DECODE_HITACHI_AC false +#define SEND_HITACHI_AC false -#define DECODE_HITACHI_AC1 true -#define SEND_HITACHI_AC1 true +#define DECODE_HITACHI_AC1 false +#define SEND_HITACHI_AC1 false -#define DECODE_HITACHI_AC2 true -#define SEND_HITACHI_AC2 true +#define DECODE_HITACHI_AC2 false +#define SEND_HITACHI_AC2 false -#define DECODE_GICABLE true -#define SEND_GICABLE true +#define DECODE_GICABLE false +#define SEND_GICABLE false -#define DECODE_HAIER_AC_YRW02 true -#define SEND_HAIER_AC_YRW02 true +#define DECODE_HAIER_AC_YRW02 false +#define SEND_HAIER_AC_YRW02 false -#define DECODE_WHIRLPOOL_AC true -#define SEND_WHIRLPOOL_AC true +#define DECODE_WHIRLPOOL_AC false +#define SEND_WHIRLPOOL_AC false -#define DECODE_LUTRON true -#define SEND_LUTRON true +#define DECODE_LUTRON false +#define SEND_LUTRON false -#define DECODE_ELECTRA_AC true -#define SEND_ELECTRA_AC true +#define DECODE_ELECTRA_AC false +#define SEND_ELECTRA_AC false -#define DECODE_PANASONIC_AC true -#define SEND_PANASONIC_AC true +#define DECODE_PANASONIC_AC false +#define SEND_PANASONIC_AC false -#define DECODE_MWM true -#define SEND_MWM true +#define DECODE_MWM false +#define SEND_MWM false -#define DECODE_PIONEER true -#define SEND_PIONEER true +#define DECODE_PIONEER false +#define SEND_PIONEER false -#define DECODE_DAIKIN2 true -#define SEND_DAIKIN2 true +#define DECODE_DAIKIN2 false +#define SEND_DAIKIN2 false -#if (DECODE_ARGO || DECODE_DAIKIN || DECODE_FUJITSU_AC || DECODE_GREE || \ - DECODE_KELVINATOR || DECODE_MITSUBISHI_AC || DECODE_TOSHIBA_AC || \ - DECODE_TROTEC || DECODE_HAIER_AC || DECODE_HITACHI_AC || \ +#if (DECODE_ARGO || DECODE_DAIKIN || DECODE_FUJITSU_AC || DECODE_GREE || \ + DECODE_KELVINATOR || DECODE_MITSUBISHI_AC || DECODE_TOSHIBA_AC || \ + DECODE_TROTEC || DECODE_HAIER_AC || DECODE_HITACHI_AC || \ DECODE_HITACHI_AC1 || DECODE_HITACHI_AC2 || DECODE_HAIER_AC_YRW02 || \ - DECODE_WHIRLPOOL_AC || DECODE_SAMSUNG_AC || DECODE_ELECTRA_AC || \ + DECODE_WHIRLPOOL_AC || DECODE_SAMSUNG_AC || DECODE_ELECTRA_AC || \ DECODE_PANASONIC_AC || DECODE_MWM || DECODE_DAIKIN2) -#define DECODE_AC true // We need some common infrastructure for decoding A/Cs. +#define DECODE_AC false // We need some common infrastructure for decoding A/Cs. #else -#define DECODE_AC false // We don't need that infrastructure. +#define DECODE_AC false // We don't need that infrastructure. #endif // Use millisecond 'delay()' calls where we can to avoid tripping the WDT. @@ -224,59 +224,60 @@ * or change order. Projects may save the type number for later usage * so numbering should always stay the same. */ -enum decode_type_t { +enum decode_type_t +{ UNKNOWN = -1, UNUSED = 0, RC5, RC6, NEC, SONY, - PANASONIC, // (5) + PANASONIC, // (5) JVC, SAMSUNG, WHYNTER, AIWA_RC_T501, - LG, // (10) + LG, // (10) SANYO, MITSUBISHI, DISH, SHARP, - COOLIX, // (15) + COOLIX, // (15) DAIKIN, DENON, KELVINATOR, SHERWOOD, - MITSUBISHI_AC, // (20) + MITSUBISHI_AC, // (20) RCMM, SANYO_LC7461, RC5X, GREE, - PRONTO, // Technically not a protocol, but an encoding. (25) + PRONTO, // Technically not a protocol, but an encoding. (25) NEC_LIKE, ARGO, TROTEC, NIKAI, - RAW, // Technically not a protocol, but an encoding. (30) - GLOBALCACHE, // Technically not a protocol, but an encoding. + RAW, // Technically not a protocol, but an encoding. (30) + GLOBALCACHE, // Technically not a protocol, but an encoding. TOSHIBA_AC, FUJITSU_AC, MIDEA, - MAGIQUEST, // (35) + MAGIQUEST, // (35) LASERTAG, CARRIER_AC, HAIER_AC, MITSUBISHI2, - HITACHI_AC, // (40) + HITACHI_AC, // (40) HITACHI_AC1, HITACHI_AC2, GICABLE, HAIER_AC_YRW02, - WHIRLPOOL_AC, // (45) + WHIRLPOOL_AC, // (45) SAMSUNG_AC, LUTRON, ELECTRA_AC, PANASONIC_AC, - PIONEER, // 50 + PIONEER, // 50 LG2, MWM, DAIKIN2, @@ -364,8 +365,8 @@ const uint16_t kProntoMinLength = 6; const uint16_t kRC5RawBits = 14; const uint16_t kRC5Bits = kRC5RawBits - 2; const uint16_t kRC5XBits = kRC5RawBits - 1; -const uint16_t kRC6Mode0Bits = 20; // Excludes the 'start' bit. -const uint16_t kRC6_36Bits = 36; // Excludes the 'start' bit. +const uint16_t kRC6Mode0Bits = 20; // Excludes the 'start' bit. +const uint16_t kRC6_36Bits = 36; // Excludes the 'start' bit. const uint16_t kRCMMBits = 24; const uint16_t kSamsungBits = 32; const uint16_t kSamsungAcStateLength = 14; @@ -377,11 +378,12 @@ const uint16_t kSanyoSA8650BBits = 12; const uint16_t kSanyoLC7461AddressBits = 13; const uint16_t kSanyoLC7461CommandBits = 8; const uint16_t kSanyoLC7461Bits = (kSanyoLC7461AddressBits + - kSanyoLC7461CommandBits) * 2; -const uint8_t kSharpAddressBits = 5; -const uint8_t kSharpCommandBits = 8; -const uint16_t kSharpBits = kSharpAddressBits + kSharpCommandBits + 2; // 15 -const uint8_t kSherwoodBits = kNECBits; + kSanyoLC7461CommandBits) * + 2; +const uint8_t kSharpAddressBits = 5; +const uint8_t kSharpCommandBits = 8; +const uint16_t kSharpBits = kSharpAddressBits + kSharpCommandBits + 2; // 15 +const uint8_t kSherwoodBits = kNECBits; const uint16_t kSherwoodMinRepeat = kSingleRepeat; const uint16_t kSony12Bits = 12; const uint16_t kSony15Bits = 15; @@ -399,74 +401,90 @@ const uint16_t kWhirlpoolAcDefaultRepeat = kNoRepeat; const uint16_t kWhynterBits = 32; // Legacy defines. (Deprecated) -#define AIWA_RC_T501_BITS kAiwaRcT501Bits -#define ARGO_COMMAND_LENGTH kArgoStateLength -#define COOLIX_BITS kCoolixBits -#define CARRIER_AC_BITS kCarrierAcBits -#define DAIKIN_COMMAND_LENGTH kDaikinStateLength -#define DENON_BITS kDenonBits -#define DENON_48_BITS kPanasonicBits -#define DENON_LEGACY_BITS kDenonLegacyBits -#define DISH_BITS kDishBits -#define FUJITSU_AC_MIN_REPEAT kFujitsuAcMinRepeat -#define FUJITSU_AC_STATE_LENGTH kFujitsuAcStateLength +#define AIWA_RC_T501_BITS kAiwaRcT501Bits +#define ARGO_COMMAND_LENGTH kArgoStateLength +#define COOLIX_BITS kCoolixBits +#define CARRIER_AC_BITS kCarrierAcBits +#define DAIKIN_COMMAND_LENGTH kDaikinStateLength +#define DENON_BITS kDenonBits +#define DENON_48_BITS kPanasonicBits +#define DENON_LEGACY_BITS kDenonLegacyBits +#define DISH_BITS kDishBits +#define FUJITSU_AC_MIN_REPEAT kFujitsuAcMinRepeat +#define FUJITSU_AC_STATE_LENGTH kFujitsuAcStateLength #define FUJITSU_AC_STATE_LENGTH_SHORT kFujitsuAcStateLengthShort -#define FUJITSU_AC_BITS kFujitsuAcBits -#define FUJITSU_AC_MIN_BITS kFujitsuAcMinBits -#define GICABLE_BITS kGicableBits -#define GREE_STATE_LENGTH kGreeStateLength -#define HAIER_AC_STATE_LENGTH kHaierACStateLength -#define HAIER_AC_YRW02_STATE_LENGTH kHaierACYRW02StateLength -#define HITACHI_AC_STATE_LENGTH kHitachiAcStateLength -#define HITACHI_AC_BITS kHitachiAcBits -#define HITACHI_AC1_STATE_LENGTH kHitachiAc1StateLength -#define HITACHI_AC1_BITS kHitachiAc1Bits -#define HITACHI_AC2_STATE_LENGTH kHitachiAc2StateLength -#define HITACHI_AC2_BITS kHitachiAc2Bits -#define JVC_BITS kJvcBits -#define KELVINATOR_STATE_LENGTH kKelvinatorStateLength -#define LASERTAG_BITS kLasertagBits -#define LG_BITS kLgBits -#define LG32_BITS kLg32Bits -#define MAGIQUEST_BITS kMagiquestBits -#define MIDEA_BITS kMideaBits -#define MITSUBISHI_BITS kMitsubishiBits -#define MITSUBISHI_AC_STATE_LENGTH kMitsubishiACStateLength -#define NEC_BITS kNECBits -#define NIKAI_BITS kNikaiBits -#define PANASONIC_BITS kPanasonicBits -#define RC5_BITS kRC5Bits -#define RC5X_BITS kRC5XBits -#define RC6_MODE0_BITS kRC6Mode0Bits -#define RC6_36_BITS kRC6_36Bits -#define RCMM_BITS kRCMMBits -#define SANYO_LC7461_BITS kSanyoLC7461Bits -#define SAMSUNG_BITS kSamsungBits -#define SANYO_SA8650B_BITS kSanyoSA8650BBits -#define SHARP_BITS kSharpBits -#define SHERWOOD_BITS kSherwoodBits -#define SONY_12_BITS kSony12Bits -#define SONY_15_BITS kSony15Bits -#define SONY_20_BITS kSony20Bits -#define TOSHIBA_AC_STATE_LENGTH kToshibaACStateLength -#define TROTEC_COMMAND_LENGTH kTrotecStateLength -#define WHYNTER_BITS kWhynterBits +#define FUJITSU_AC_BITS kFujitsuAcBits +#define FUJITSU_AC_MIN_BITS kFujitsuAcMinBits +#define GICABLE_BITS kGicableBits +#define GREE_STATE_LENGTH kGreeStateLength +#define HAIER_AC_STATE_LENGTH kHaierACStateLength +#define HAIER_AC_YRW02_STATE_LENGTH kHaierACYRW02StateLength +#define HITACHI_AC_STATE_LENGTH kHitachiAcStateLength +#define HITACHI_AC_BITS kHitachiAcBits +#define HITACHI_AC1_STATE_LENGTH kHitachiAc1StateLength +#define HITACHI_AC1_BITS kHitachiAc1Bits +#define HITACHI_AC2_STATE_LENGTH kHitachiAc2StateLength +#define HITACHI_AC2_BITS kHitachiAc2Bits +#define JVC_BITS kJvcBits +#define KELVINATOR_STATE_LENGTH kKelvinatorStateLength +#define LASERTAG_BITS kLasertagBits +#define LG_BITS kLgBits +#define LG32_BITS kLg32Bits +#define MAGIQUEST_BITS kMagiquestBits +#define MIDEA_BITS kMideaBits +#define MITSUBISHI_BITS kMitsubishiBits +#define MITSUBISHI_AC_STATE_LENGTH kMitsubishiACStateLength +#define NEC_BITS kNECBits +#define NIKAI_BITS kNikaiBits +#define PANASONIC_BITS kPanasonicBits +#define RC5_BITS kRC5Bits +#define RC5X_BITS kRC5XBits +#define RC6_MODE0_BITS kRC6Mode0Bits +#define RC6_36_BITS kRC6_36Bits +#define RCMM_BITS kRCMMBits +#define SANYO_LC7461_BITS kSanyoLC7461Bits +#define SAMSUNG_BITS kSamsungBits +#define SANYO_SA8650B_BITS kSanyoSA8650BBits +#define SHARP_BITS kSharpBits +#define SHERWOOD_BITS kSherwoodBits +#define SONY_12_BITS kSony12Bits +#define SONY_15_BITS kSony15Bits +#define SONY_20_BITS kSony20Bits +#define TOSHIBA_AC_STATE_LENGTH kToshibaACStateLength +#define TROTEC_COMMAND_LENGTH kTrotecStateLength +#define WHYNTER_BITS kWhynterBits // Turn on Debugging information by uncommenting the following line. // #define DEBUG 1 #ifdef DEBUG #ifdef UNIT_TEST -#define DPRINT(x) do { std::cout << x; } while (0) -#define DPRINTLN(x) do { std::cout << x << std::endl; } while (0) -#endif // UNIT_TEST +#define DPRINT(x) \ + do \ + { \ + std::cout << x; \ + } while (0) +#define DPRINTLN(x) \ + do \ + { \ + std::cout << x << std::endl; \ + } while (0) +#endif // UNIT_TEST #ifdef ARDUINO -#define DPRINT(x) do { Serial.print(x); } while (0) -#define DPRINTLN(x) do { Serial.println(x); } while (0) -#endif // ARDUINO +#define DPRINT(x) \ + do \ + { \ + Serial.print(x); \ + } while (0) +#define DPRINTLN(x) \ + do \ + { \ + Serial.println(x); \ + } while (0) +#endif // ARDUINO #else // DEBUG #define DPRINT(x) #define DPRINTLN(x) -#endif // DEBUG +#endif // DEBUG -#endif // IRREMOTEESP8266_H_ +#endif // IRREMOTEESP8266_H_ diff --git a/lib/IRremoteESP8266_ID1089/src/ir_Aiwa.cpp b/lib/IRremoteESP8266_ID1089/src/ir_Aiwa.cpp deleted file mode 100644 index 617711a..0000000 --- a/lib/IRremoteESP8266_ID1089/src/ir_Aiwa.cpp +++ /dev/null @@ -1,114 +0,0 @@ -// Copyright 2017 David Conran - -#include "IRrecv.h" -#include "IRsend.h" - -// AAA IIIII W W AAA -// A A I W W A A -// AAAAA I W W W AAAAA -// A A I W W W A A -// A A IIIII WWW A A - -// Based off the RC-T501 RCU -// Added by David Conran. (Inspired by IRremoteESP8266's implementation: -// https://github.com/z3t0/Arduino-IRremote) - -const uint16_t kAiwaRcT501PreBits = 26; -const uint16_t kAiwaRcT501PostBits = 1; -// NOTE: These are the compliment (inverted) of lirc values as -// lirc uses a '0' for a mark, and a '1' for a space. -const uint64_t kAiwaRcT501PreData = 0x1D8113FULL; // 26-bits -const uint64_t kAiwaRcT501PostData = 1ULL; - -#if SEND_AIWA_RC_T501 -// Send an Aiwa RC T501 formatted message. -// -// Args: -// data: The message to be sent. -// nbits: The number of bits of the message to be sent. -// Typically kAiwaRcT501Bits. Max is 37 = (64 - 27) -// repeat: The number of times the command is to be repeated. -// -// Status: BETA / Should work. -// -// Ref: -// http://lirc.sourceforge.net/remotes/aiwa/RC-T501 -void IRsend::sendAiwaRCT501(uint64_t data, uint16_t nbits, uint16_t repeat) { - // Appears to be an extended NEC1 protocol. i.e. 42 bits instead of 32 bits. - // So use sendNEC instead, however the twist is it has a fixed 26 bit - // prefix, and a fixed postfix bit. - uint64_t new_data = ((kAiwaRcT501PreData << (nbits + kAiwaRcT501PostBits)) | - (data << kAiwaRcT501PostBits) | kAiwaRcT501PostData); - nbits += kAiwaRcT501PreBits + kAiwaRcT501PostBits; - if (nbits > sizeof(new_data) * 8) - return; // We are overflowing. Abort, and don't send. - sendNEC(new_data, nbits, repeat); -} -#endif - -#if DECODE_AIWA_RC_T501 -// Decode the supplied Aiwa RC T501 message. -// -// Args: -// results: Ptr to the data to decode and where to store the decode result. -// nbits: The number of data bits to expect. Typically kAiwaRcT501Bits. -// strict: Flag indicating if we should perform strict matching. -// Returns: -// boolean: True if it can decode it, false if it can't. -// -// Status: BETA / Should work. -// -// Notes: -// Aiwa RC T501 appears to be a 42 bit variant of the NEC1 protocol. -// However, we historically (original Arduino IRremote project) treats it as -// a 15 bit (data) protocol. So, we expect nbits to typically be 15, and we -// will remove the prefix and postfix from the raw data, and use that as -// the result. -// -// Ref: -// http://www.sbprojects.com/knowledge/ir/nec.php -bool IRrecv::decodeAiwaRCT501(decode_results *results, uint16_t nbits, - bool strict) { - // Compliance - if (strict && nbits != kAiwaRcT501Bits) - return false; // Doesn't match our protocol defn. - - // Add on the pre & post bits to our requested bit length. - uint16_t expected_nbits = nbits + kAiwaRcT501PreBits + kAiwaRcT501PostBits; - uint64_t new_data; - if (expected_nbits > sizeof(new_data) * 8) - return false; // We can't possibly match something that big. - // Decode it as a much bigger (non-standard) NEC message, so we have to turn - // off strict mode checking for NEC. - if (!decodeNEC(results, expected_nbits, false)) - return false; // The NEC decode had a problem, so we should too. - uint16_t actual_bits = results->bits; - new_data = results->value; - if (actual_bits < expected_nbits) - return false; // The data we caught was undersized. Throw it back. - if ((new_data & 0x1ULL) != kAiwaRcT501PostData) - return false; // The post data doesn't match, so it can't be this protocol. - // Trim off the post data bit. - new_data >>= kAiwaRcT501PostBits; - actual_bits -= kAiwaRcT501PostBits; - - // Extract out our likely new value and put it back in the results. - actual_bits -= kAiwaRcT501PreBits; - results->value = new_data & ((1ULL << actual_bits) - 1); - - // Check the prefix data matches. - new_data >>= actual_bits; // Trim off the new data to expose the prefix. - if (new_data != kAiwaRcT501PreData) // Check the prefix. - return false; - - // Compliance - if (strict && results->bits != expected_nbits) return false; - - // Success - results->decode_type = AIWA_RC_T501; - results->bits = actual_bits; - results->address = 0; - results->command = 0; - return true; -} -#endif diff --git a/lib/IRremoteESP8266_ID1089/src/ir_Argo.cpp b/lib/IRremoteESP8266_ID1089/src/ir_Argo.cpp deleted file mode 100644 index 8246b26..0000000 --- a/lib/IRremoteESP8266_ID1089/src/ir_Argo.cpp +++ /dev/null @@ -1,230 +0,0 @@ -/* -Node MCU/ESP8266 Sketch to emulate Argo Ulisse 13 DCI remote -Controls Argo Ulisse 13 DCI A/C -Copyright 2017 Schmolders -*/ - -#include "ir_Argo.h" -#include -#include "IRremoteESP8266.h" -#include "IRutils.h" - -// Constants -// using SPACE modulation. MARK is always const 400u -const uint16_t kArgoHdrMark = 6400; -const uint16_t kArgoHdrSpace = 3300; -const uint16_t kArgoBitMark = 400; -const uint16_t kArgoOneSpace = 2200; -const uint16_t kArgoZeroSpace = 900; - -#if SEND_ARGO -// Send an Argo A/C message. -// -// Args: -// data: An array of kArgoStateLength bytes containing the IR command. -// -// Status: ALPHA / Untested. - -void IRsend::sendArgo(unsigned char data[], uint16_t nbytes, uint16_t repeat) { - // Check if we have enough bytes to send a proper message. - if (nbytes < kArgoStateLength) return; - // TODO(kaschmo): validate - sendGeneric(kArgoHdrMark, kArgoHdrSpace, kArgoBitMark, kArgoOneSpace, - kArgoBitMark, kArgoZeroSpace, 0, 0, // No Footer. - data, nbytes, 38, false, repeat, kDutyDefault); -} -#endif // SEND_ARGO - -IRArgoAC::IRArgoAC(uint16_t pin) : _irsend(pin) { stateReset(); } - -void IRArgoAC::begin() { _irsend.begin(); } - -#if SEND_ARGO -void IRArgoAC::send(const uint16_t repeat) { - checksum(); // Create valid checksum before sending - _irsend.sendArgo(argo, kArgoStateLength, repeat); -} -#endif // SEND_ARGO - -void IRArgoAC::checksum() { - uint8_t sum = 2; // Corresponds to byte 11 being constant 0b01 - uint8_t i; - - // Only add up bytes to 9. byte 10 is 0b01 constant anyway. - // Assume that argo array is MSB first (left) - for (i = 0; i < 10; i++) sum += argo[i]; - - sum = sum % 256; // modulo 256 - // Append sum to end of array - // Set const part of checksum bit 10 - argo[10] = 0b00000010; - argo[10] += sum << 2; // Shift up 2 bits and append to byte 10 - argo[11] = sum >> 6; // Shift down 6 bits and add in two LSBs of bit 11 -} - -void IRArgoAC::stateReset() { - for (uint8_t i = 0; i < kArgoStateLength; i++) argo[i] = 0x0; - - // Argo Message. Store MSB left. - // Default message: - argo[0] = 0b10101100; // LSB first (as sent) 0b00110101; //const preamble - argo[1] = 0b11110101; // LSB first: 0b10101111; //const preamble - // Keep payload 2-9 at zero - argo[10] = 0b00000010; // Const 01, checksum 6bit - argo[11] = 0b00000000; // Checksum 2bit - - this->off(); - this->setTemp(20); - this->setRoomTemp(25); - this->setCoolMode(kArgoCoolAuto); - this->setFan(kArgoFanAuto); -} - -uint8_t* IRArgoAC::getRaw() { - checksum(); // Ensure correct bit array before returning - return argo; -} - -void IRArgoAC::on() { - // state = ON; - ac_state = 1; - // Bit 5 of byte 9 is on/off - // in MSB first - argo[9] = argo[9] | 0b00100000; // Set ON/OFF bit to 1 -} - -void IRArgoAC::off() { - // state = OFF; - ac_state = 0; - // in MSB first - // bit 5 of byte 9 to off - argo[9] = argo[9] & 0b11011111; // Set on/off bit to 0 -} - -void IRArgoAC::setPower(bool state) { - if (state) - on(); - else - off(); -} - -uint8_t IRArgoAC::getPower() { return ac_state; } - -void IRArgoAC::setMax(bool state) { - max_mode = state; - if (max_mode) - argo[9] |= 0b00001000; - else - argo[9] &= 0b11110111; -} - -bool IRArgoAC::getMax() { return max_mode; } - -// Set the temp in deg C -// Sending 0 equals +4 -void IRArgoAC::setTemp(uint8_t temp) { - if (temp < kArgoMinTemp) - temp = kArgoMinTemp; - else if (temp > kArgoMaxTemp) - temp = kArgoMaxTemp; - - // Store in attributes - set_temp = temp; - // offset 4 degrees. "If I want 12 degrees, I need to send 8" - temp -= 4; - // Settemp = Bit 6,7 of byte 2, and bit 0-2 of byte 3 - // mask out bits - // argo[13] & 0x00000100; // mask out ON/OFF Bit - argo[2] &= 0b00111111; - argo[3] &= 0b11111000; - - argo[2] += temp << 6; // append to bit 6,7 - argo[3] += temp >> 2; // remove lowest to bits and append in 0-2 -} - -uint8_t IRArgoAC::getTemp() { return set_temp; } - -// Set the speed of the fan -void IRArgoAC::setFan(uint8_t fan) { - // Set the fan speed bits, leave low 4 bits alone - fan_mode = fan; - // Mask out bits - argo[3] &= 0b11100111; - // Set fan mode at bit positions - argo[3] += fan << 3; -} - -uint8_t IRArgoAC::getFan() { return fan_mode; } - -void IRArgoAC::setFlap(uint8_t flap) { - flap_mode = flap; - // TODO(kaschmo): set correct bits for flap mode -} - -uint8_t IRArgoAC::getFlap() { return flap_mode; } - -uint8_t IRArgoAC::getMode() { - // return cooling 0, heating 1 - return ac_mode; -} - -void IRArgoAC::setCoolMode(uint8_t mode) { - ac_mode = 0; // Set ac mode to cooling - cool_mode = mode; - // Mask out bits, also leave bit 5 on 0 for cooling - argo[2] &= 0b11000111; - - // Set cool mode at bit positions - argo[2] += mode << 3; -} - -uint8_t IRArgoAC::getCoolMode() { return cool_mode; } - -void IRArgoAC::setHeatMode(uint8_t mode) { - ac_mode = 1; // Set ac mode to heating - heat_mode = mode; - // Mask out bits - argo[2] &= 0b11000111; - // Set heating bit - argo[2] |= 0b00100000; - // Set cool mode at bit positions - argo[2] += mode << 3; -} - -uint8_t IRArgoAC::getHeatMode() { return heat_mode; } - -void IRArgoAC::setNight(bool state) { - night_mode = state; - if (night_mode) - // Set bit at night position: bit 2 - argo[9] |= 0b00000100; - else - argo[9] &= 0b11111011; -} - -bool IRArgoAC::getNight() { return night_mode; } - -void IRArgoAC::setiFeel(bool state) { - ifeel_mode = state; - if (ifeel_mode) - // Set bit at iFeel position: bit 7 - argo[9] |= 0b10000000; - else - argo[9] &= 0b01111111; -} - -bool IRArgoAC::getiFeel() { return ifeel_mode; } - -void IRArgoAC::setTime() { - // TODO(kaschmo): use function call from checksum to set time first -} - -void IRArgoAC::setRoomTemp(uint8_t temp) { - temp -= 4; - // Mask out bits - argo[3] &= 0b00011111; - argo[4] &= 0b11111100; - - argo[3] += temp << 5; // Append to bit 5,6,7 - argo[4] += temp >> 3; // Remove lowest 3 bits and append in 0,1 -} diff --git a/lib/IRremoteESP8266_ID1089/src/ir_Argo.h b/lib/IRremoteESP8266_ID1089/src/ir_Argo.h deleted file mode 100644 index 0174d61..0000000 --- a/lib/IRremoteESP8266_ID1089/src/ir_Argo.h +++ /dev/null @@ -1,142 +0,0 @@ -/* Copyright 2017 Schmolders -// Adds support for Argo Ulisse 13 DCI Mobile Split ACs. -*/ -#ifndef IR_ARGO_H_ -#define IR_ARGO_H_ - -#include "IRremoteESP8266.h" -#include "IRsend.h" - -// ARGO Ulisse DCI - -/* - Protocol Description: - All in LSB first as it is sent. argo message array will be stored MSB first! - do LSB-MSB conversion in sendData - Byte 0: const 0 0 1 1 0 1 0 1 - Byte 1: const 1 0 1 0 1 1 1 1 - Byte 2: 0 0 0, 3bit Cool/Heat Mode, 2bit start SetTemp LSB first - Byte 3: 3bit End SetTemp, 2bit Fan Mode, 3bit RoomTemp LSB first - Byte 4: 2bit RoomTemp, 3bit Flap Mode, 3bit OnTimer - Byte 5: 8bit OnTimer - Byte 6: 8Bit OffTimer - Byte 7: 3bit OffTimer, 5bit Time - Byte 8: 6bit Time, 1bit Timer On/Off, 1bit Timer Program - Byte 9: 1bit Timer Program, 1bit Timer 1h, 1 bit Night Mode, 1bit Max Mode, 1bit Filter, 1bit on/off, 1bit const 0, 1bit iFeel - Byte 10: 2bit const 0 1, 6bit Checksum - Byte 11: 2bit Checksum -*/ - -// Constants. Store MSB left. - -const uint8_t kArgoCoolOn = 0; // 0b000 -const uint8_t kArgoCoolOff = 3; // 0b110 -const uint8_t kArgoCoolAuto = 2; // 0b010 -const uint8_t kArgoCoolHum = 1; // 0b100 -const uint8_t kArgoHeatOn = 0; // 0b001 -const uint8_t kArgoHeatAuto = 1; // 0b101 -const uint8_t kArgoHeatBlink = 2; // 0b011 // ??no idea what mode that is -const uint8_t kArgoMinTemp = 10; // Celsius offset +4 -const uint8_t kArgoMaxTemp = 32; // Celsius -const uint8_t kArgoFanAuto = 0; // 0b00 -const uint8_t kArgoFan3 = 3; // 0b11 -const uint8_t kArgoFan2 = 2; // 0b01 -const uint8_t kArgoFan1 = 1; // 0b10 -const uint8_t kArgoFlapAuto = 0; // 0b000 -const uint8_t kArgoFlap1 = 1; // 0b100 -const uint8_t kArgoFlap2 = 2; // 0b010 -const uint8_t kArgoFlap3 = 3; // 0b110 -const uint8_t kArgoFlap4 = 4; // 0b001 -const uint8_t kArgoFlap5 = 5; // 0b101 -const uint8_t kArgoFlap6 = 6; // 0b011 -const uint8_t kArgoFlapFull = 7; // 0b111 - -// Legacy defines. (Deperecated) -#define ARGO_COOL_ON kArgoCoolOn -#define ARGO_COOL_OFF kArgoCoolOff -#define ARGO_COOL_AUTO kArgoCoolAuto -#define ARGO_COOl_HUM kArgoCoolHum -#define ARGO_HEAT_ON kArgoHeatOn -#define ARGO_HEAT_AUTO kArgoHeatAuto -#define ARGO_HEAT_BLINK kArgoHeatBlink -#define ARGO_MIN_TEMP kArgoMinTemp -#define ARGO_MAX_TEMP kArgoMaxTemp -#define ARGO_FAN_AUTO kArgoFanAuto -#define ARGO_FAN_3 kArgoFan3 -#define ARGO_FAN_2 kArgoFan2 -#define ARGO_FAN_1 kArgoFan1 -#define ARGO_FLAP_AUTO kArgoFlapAuto -#define ARGO_FLAP_1 kArgoFlap1 -#define ARGO_FLAP_2 kArgoFlap2 -#define ARGO_FLAP_3 kArgoFlap3 -#define ARGO_FLAP_4 kArgoFlap4 -#define ARGO_FLAP_5 kArgoFlap5 -#define ARGO_FLAP_6 kArgoFlap6 -#define ARGO_FLAP_FULL kArgoFlapFull - - -class IRArgoAC { - public: - explicit IRArgoAC(uint16_t pin); - -#if SEND_ARGO - void send(const uint16_t repeat = kArgoDefaultRepeat); -#endif // SEND_ARGO - void begin(); - void on(); - void off(); - - void setPower(bool state); - uint8_t getPower(); - - void setTemp(uint8_t temp); - uint8_t getTemp(); - - void setFan(uint8_t fan); - uint8_t getFan(); - - void setFlap(uint8_t flap); - uint8_t getFlap(); - - void setCoolMode(uint8_t mode); - uint8_t getCoolMode(); - - void setHeatMode(uint8_t mode); - uint8_t getHeatMode(); - uint8_t getMode(); - - void setMax(bool state); - bool getMax(); - - void setNight(bool state); - bool getNight(); - - void setiFeel(bool state); - bool getiFeel(); - - void setTime(); - void setRoomTemp(uint8_t temp); - - uint8_t* getRaw(); - - private: - // # of bytes per command - uint8_t argo[kArgoStateLength]; // Defined in IRremoteESP8266.h - void stateReset(); - void checksum(); - IRsend _irsend; // instance of the IR send class - - // Attributes - uint8_t set_temp; - uint8_t fan_mode; - uint8_t flap_mode; - uint8_t ac_state; - uint8_t ac_mode; // heat 1, cool 0 - uint8_t heat_mode; - uint8_t cool_mode; - uint8_t night_mode; // on/off - uint8_t max_mode; // on/off - uint8_t ifeel_mode; // on/off -}; - -#endif // IR_ARGO_H_ diff --git a/lib/IRremoteESP8266_ID1089/src/ir_Carrier.cpp b/lib/IRremoteESP8266_ID1089/src/ir_Carrier.cpp deleted file mode 100644 index 350d61c..0000000 --- a/lib/IRremoteESP8266_ID1089/src/ir_Carrier.cpp +++ /dev/null @@ -1,111 +0,0 @@ -// Copyright 2018 David Conran - -#include "IRrecv.h" -#include "IRsend.h" -#include "IRutils.h" - -// CCCCC AAA RRRRRR RRRRRR IIIII EEEEEEE RRRRRR -// CC C AAAAA RR RR RR RR III EE RR RR -// CC AA AA RRRRRR RRRRRR III EEEEE RRRRRR -// CC C AAAAAAA RR RR RR RR III EE RR RR -// CCCCC AA AA RR RR RR RR IIIII EEEEEEE RR RR - -// Suits Carrier/Surrey HVAC models: -// 42QG5A55970 (remote) -// 619EGX0090E0 / 619EGX0120E0 / 619EGX0180E0 / 619EGX0220E0 (indoor units) -// 53NGK009/012 (inverter) - -// Constants -// Ref: -// https://github.com/markszabo/IRremoteESP8266/issues/385 -const uint16_t kCarrierAcHdrMark = 8532; -const uint16_t kCarrierAcHdrSpace = 4228; -const uint16_t kCarrierAcBitMark = 628; -const uint16_t kCarrierAcOneSpace = 1320; -const uint16_t kCarrierAcZeroSpace = 532; -const uint16_t kCarrierAcGap = 20000; - -#if SEND_CARRIER_AC -// Send a Carrier HVAC formatted message. -// -// Args: -// data: The message to be sent. -// nbits: The bit size of the message being sent. typically kCarrierAcBits. -// repeat: The number of times the message is to be repeated. -// -// Status: BETA / Appears to work on real devices. -// -void IRsend::sendCarrierAC(uint64_t data, uint16_t nbits, uint16_t repeat) { - for (uint16_t r = 0; r <= repeat; r++) { - uint64_t temp_data = data; - // Carrier sends the data block three times. normal + inverted + normal. - for (uint16_t i = 0; i < 3; i++) { - sendGeneric(kCarrierAcHdrMark, kCarrierAcHdrSpace, kCarrierAcBitMark, - kCarrierAcOneSpace, kCarrierAcBitMark, kCarrierAcZeroSpace, - kCarrierAcBitMark, kCarrierAcGap, temp_data, nbits, 38, true, - 0, kDutyDefault); - temp_data = invertBits(temp_data, nbits); - } - } -} -#endif - -#if DECODE_CARRIER_AC -// Decode the supplied Carrier HVAC message. -// Carrier HVAC messages contain only 32 bits, but it is sent three(3) times. -// i.e. normal + inverted + normal -// Args: -// results: Ptr to the data to decode and where to store the decode result. -// nbits: Nr. of bits to expect in the data portion. -// Typically kCarrierAcBits. -// strict: Flag to indicate if we strictly adhere to the specification. -// Returns: -// boolean: True if it can decode it, false if it can't. -// -// Status: ALPHA / Untested. -// -bool IRrecv::decodeCarrierAC(decode_results *results, uint16_t nbits, - bool strict) { - if (results->rawlen < ((2 * nbits + kHeader + kFooter) * 3) - 1) - return false; // Can't possibly be a valid Carrier message. - if (strict && nbits != kCarrierAcBits) - return false; // We expect Carrier to be 32 bits of message. - - uint64_t data = 0; - uint64_t prev_data = 0; - uint16_t offset = kStartOffset; - - for (uint8_t i = 0; i < 3; i++) { - prev_data = data; - // Header - if (!matchMark(results->rawbuf[offset++], kCarrierAcHdrMark)) return false; - if (!matchSpace(results->rawbuf[offset++], kCarrierAcHdrSpace)) - return false; - // Data - match_result_t data_result = - matchData(&(results->rawbuf[offset]), nbits, kCarrierAcBitMark, - kCarrierAcOneSpace, kCarrierAcBitMark, kCarrierAcZeroSpace); - if (data_result.success == false) return false; - data = data_result.data; - offset += data_result.used; - // Footer - if (!matchMark(results->rawbuf[offset++], kCarrierAcBitMark)) return false; - if (offset < results->rawlen && - !matchAtLeast(results->rawbuf[offset++], kCarrierAcGap)) - return false; - // Compliance. - if (strict) { - // Check if the data is an inverted copy of the previous data. - if (i > 0 && prev_data != invertBits(data, nbits)) return false; - } - } - - // Success - results->bits = nbits; - results->value = data; - results->decode_type = CARRIER_AC; - results->address = data >> 16; - results->command = data & 0xFFFF; - return true; -} -#endif diff --git a/lib/IRremoteESP8266_ID1089/src/ir_Coolix.cpp b/lib/IRremoteESP8266_ID1089/src/ir_Coolix.cpp deleted file mode 100644 index b0cb0e2..0000000 --- a/lib/IRremoteESP8266_ID1089/src/ir_Coolix.cpp +++ /dev/null @@ -1,429 +0,0 @@ -// Copyright bakrus -// Copyright 2017 David Conran - -#include "ir_Coolix.h" -#include -#ifndef ARDUINO -#include -#endif -#include "IRrecv.h" -#include "IRsend.h" -#include "IRutils.h" - -// CCCCC OOOOO OOOOO LL IIIII XX XX -// CC C OO OO OO OO LL III XX XX -// CC OO OO OO OO LL III XXXX -// CC C OO OO OO OO LL III XX XX -// CCCCC OOOO0 OOOO0 LLLLLLL IIIII XX XX - -// Coolix A/C / heatpump added by (send) bakrus & (decode) crankyoldgit -// -// Supports: -// RG57K7(B)/BGEF remote control for Beko BINR 070/071 split-type aircon. -// Ref: -// https://github.com/markszabo/IRremoteESP8266/issues/484 - -// Constants -// Pulse parms are *50-100 for the Mark and *50+100 for the space -// First MARK is the one after the long gap -// pulse parameters in usec -const uint16_t kCoolixTick = 560; // Approximately 21 cycles at 38kHz -const uint16_t kCoolixBitMarkTicks = 1; -const uint16_t kCoolixBitMark = kCoolixBitMarkTicks * kCoolixTick; -const uint16_t kCoolixOneSpaceTicks = 3; -const uint16_t kCoolixOneSpace = kCoolixOneSpaceTicks * kCoolixTick; -const uint16_t kCoolixZeroSpaceTicks = 1; -const uint16_t kCoolixZeroSpace = kCoolixZeroSpaceTicks * kCoolixTick; -const uint16_t kCoolixHdrMarkTicks = 8; -const uint16_t kCoolixHdrMark = kCoolixHdrMarkTicks * kCoolixTick; -const uint16_t kCoolixHdrSpaceTicks = 8; -const uint16_t kCoolixHdrSpace = kCoolixHdrSpaceTicks * kCoolixTick; -const uint16_t kCoolixMinGapTicks = kCoolixHdrMarkTicks + kCoolixZeroSpaceTicks; -const uint16_t kCoolixMinGap = kCoolixMinGapTicks * kCoolixTick; - -#if SEND_COOLIX -// Send a Coolix message -// -// Args: -// data: Contents of the message to be sent. -// nbits: Nr. of bits of data to be sent. Typically kCoolixBits. -// repeat: Nr. of additional times the message is to be sent. -// -// Status: BETA / Probably works. -// -// Ref: -// https://github.com/z3t0/Arduino-IRremote/blob/master/ir_COOLIX.cpp -// TODO(anyone): Verify repeat functionality against a real unit. -void IRsend::sendCOOLIX(uint64_t data, uint16_t nbits, uint16_t repeat) { - if (nbits % 8 != 0) return; // nbits is required to be a multiple of 8. - - // Set IR carrier frequency - enableIROut(38); - - for (uint16_t r = 0; r <= repeat; r++) { - // Header - mark(kCoolixHdrMark); - space(kCoolixHdrSpace); - - // Data - // Break data into byte segments, starting at the Most Significant - // Byte. Each byte then being sent normal, then followed inverted. - for (uint16_t i = 8; i <= nbits; i += 8) { - // Grab a bytes worth of data. - uint8_t segment = (data >> (nbits - i)) & 0xFF; - // Normal - sendData(kCoolixBitMark, kCoolixOneSpace, kCoolixBitMark, - kCoolixZeroSpace, segment, 8, true); - // Inverted. - sendData(kCoolixBitMark, kCoolixOneSpace, kCoolixBitMark, - kCoolixZeroSpace, segment ^ 0xFF, 8, true); - } - - // Footer - mark(kCoolixBitMark); - space(kCoolixMinGap); // Pause before repeating - } -} -#endif - -// IRCoolixAC class -// Supports: -// RG57K7(B)/BGEF remote control for Beko BINR 070/071 split-type aircon. -// Ref: -// https://github.com/markszabo/IRremoteESP8266/issues/484 -IRCoolixAC::IRCoolixAC(uint16_t pin) : _irsend(pin) { stateReset(); } - -void IRCoolixAC::stateReset() { remote_state = kCoolixDefaultState; } - -void IRCoolixAC::begin() { _irsend.begin(); } - -#if SEND_COOLIX -void IRCoolixAC::send(const uint16_t repeat) { - _irsend.sendCOOLIX(remote_state, kCoolixBits, repeat); -} -#endif // SEND_COOLIX - -uint32_t IRCoolixAC::getRaw() { return remote_state; } - -void IRCoolixAC::setRaw(const uint32_t new_code) { remote_state = new_code; } - -void IRCoolixAC::setTempRaw(const uint8_t code) { - remote_state &= ~kCoolixTempMask; // Clear the old temp. - remote_state |= (code << 4); -} - -uint8_t IRCoolixAC::getTempRaw() { - return (remote_state & kCoolixTempMask) >> 4; -} - -void IRCoolixAC::setTemp(const uint8_t desired) { - // Range check. - uint8_t temp = std::min(desired, kCoolixTempMax); - temp = std::max(temp, kCoolixTempMin); - setTempRaw(kCoolixTempMap[temp - kCoolixTempMin]); -} - -uint8_t IRCoolixAC::getTemp() { - uint8_t code = getTempRaw(); - uint8_t i; - for (i = 0; i < kCoolixTempRange; i++) - if (kCoolixTempMap[i] == code) return kCoolixTempMin + i; - return kCoolixUnknown; // Not a temp we expected. -} - -void IRCoolixAC::setSensorTempRaw(const uint8_t code) { - remote_state &= ~kCoolixSensorTempMask; // Clear previous sensor temp. - remote_state |= ((code & 0xF) << 8); -} - -void IRCoolixAC::setSensorTemp(const uint8_t desired) { - uint8_t temp = desired; - temp = std::min(temp, kCoolixSensorTempMax); - temp = std::max(temp, kCoolixSensorTempMin); - setSensorTempRaw(temp - kCoolixSensorTempMin); - setZoneFollow(true); // Setting a Sensor temp means you want to Zone Follow. -} - -uint8_t IRCoolixAC::getSensorTemp() { - return ((remote_state & kCoolixSensorTempMask) >> 8) + kCoolixSensorTempMin; -} - -bool IRCoolixAC::getPower() { - // There is only an off state. Everything else is "on". - return remote_state != kCoolixOff; -} - -void IRCoolixAC::setPower(const bool power) { - if (!power) remote_state = kCoolixOff; - // There really is no distinct "on" setting, so do nothing. -} - -bool IRCoolixAC::getSwing() { return remote_state == kCoolixSwing; } - -void IRCoolixAC::setSwing() { - // Assumes that repeated sending "swing" toggles the action on the device. - remote_state = kCoolixSwing; -} - -bool IRCoolixAC::getSleep() { return remote_state == kCoolixSleep; } - -void IRCoolixAC::setSleep() { remote_state = kCoolixSleep; } - -bool IRCoolixAC::getTurbo() { return remote_state == kCoolixTurbo; } - -void IRCoolixAC::setTurbo() { - // Assumes that repeated sending "turbo" toggles the action on the device. - remote_state = kCoolixTurbo; -} - -bool IRCoolixAC::getLed() { return remote_state == kCoolixLed; } - -void IRCoolixAC::setLed() { - // Assumes that repeated sending "Led" toggles the action on the device. - remote_state = kCoolixLed; -} - -bool IRCoolixAC::getClean() { return remote_state == kCoolixClean; } - -void IRCoolixAC::setClean() { remote_state = kCoolixClean; } - -bool IRCoolixAC::getZoneFollow() { - return remote_state & kCoolixZoneFollowMask; -} - -// Internal use only. -void IRCoolixAC::setZoneFollow(bool state) { - if (state) { - remote_state |= kCoolixZoneFollowMask; - } else { - remote_state &= ~kCoolixZoneFollowMask; - } -} - -void IRCoolixAC::clearSensorTemp() { - setZoneFollow(false); - setSensorTempRaw(kCoolixSensorTempIgnoreCode); -} - -void IRCoolixAC::setMode(const uint8_t mode) { - uint32_t actualmode = mode; - // Fan mode is a special case of Dry. - if (mode == kCoolixFan) actualmode = kCoolixDry; - switch (actualmode) { - case kCoolixCool: - case kCoolixAuto: - case kCoolixHeat: - case kCoolixDry: - remote_state = (remote_state & ~kCoolixModeMask) | (actualmode << 2); - // Force the temp into a known-good state. - setTemp(getTemp()); - } - if (mode == kCoolixFan) setTempRaw(kCoolixFanTempCode); -} - -uint8_t IRCoolixAC::getMode() { - uint8_t mode = (remote_state & kCoolixModeMask) >> 2; - if (mode == kCoolixDry) - if (getTempRaw() == kCoolixFanTempCode) return kCoolixFan; - return mode; -} - -uint8_t IRCoolixAC::getFan() { return (remote_state & kCoolixFanMask) >> 13; } - -void IRCoolixAC::setFan(const uint8_t speed) { - uint8_t newspeed = speed; - switch (speed) { - case kCoolixFanMin: - case kCoolixFanMed: - case kCoolixFanMax: - case kCoolixFanAuto: - case kCoolixFanAuto0: - case kCoolixFanZoneFollow: - case kCoolixFanFixed: - break; - default: // Unknown speed requested. - newspeed = kCoolixFanAuto; - } - remote_state &= ~kCoolixFanMask; - remote_state |= ((newspeed << 13) & kCoolixFanMask); -} - -// Convert the internal state into a human readable string. -#ifdef ARDUINO -String IRCoolixAC::toString() { - String result = ""; -#else -std::string IRCoolixAC::toString() { - std::string result = ""; -#endif // ARDUINO - result += "Power: "; - if (getPower()) { - result += "On"; - } else { - result += "Off"; - return result; // If it's off, there is no other info. - } - result += ", Fan: " + uint64ToString(getFan()); - switch (getFan()) { - case kCoolixFanAuto: - result += " (AUTO)"; - break; - case kCoolixFanAuto0: - result += " (AUTO0)"; - break; - case kCoolixFanMax: - result += " (MAX)"; - break; - case kCoolixFanMin: - result += " (MIN)"; - break; - case kCoolixFanMed: - result += " (MED)"; - break; - case kCoolixFanZoneFollow: - result += " (ZONEFOLLOW)"; - break; - case kCoolixFanFixed: - result += " (FIXED)"; - break; - default: - result += " (UNKNOWN)"; - } - // Special modes. - if (getSwing()) { - result += ", Swing: Toggle"; - return result; - } - if (getSleep()) { - result += ", Sleep: Toggle"; - return result; - } - if (getTurbo()) { - result += ", Turbo: Toggle"; - return result; - } - if (getLed()) { - result += ", Led: Toggle"; - return result; - } - if (getClean()) { - result += ", Mode: Self clean"; - return result; - } - result += ", Mode: " + uint64ToString(getMode()); - switch (getMode()) { - case kCoolixAuto: - result += " (AUTO)"; - break; - case kCoolixCool: - result += " (COOL)"; - break; - case kCoolixHeat: - result += " (HEAT)"; - break; - case kCoolixDry: - result += " (DRY)"; - break; - case kCoolixFan: - result += " (FAN)"; - break; - default: - result += " (UNKNOWN)"; - } - if (getMode() != kCoolixFan) // Fan mode doesn't have a temperature. - result += ", Temp: " + uint64ToString(getTemp()) + "C"; - result += ", Zone Follow: "; - if (getZoneFollow()) - result += "On"; - else - result += "Off"; - result += ", Sensor Temp: "; - if (getSensorTemp() > kCoolixSensorTempMax) - result += "Ignored"; - else - result += uint64ToString(getSensorTemp()) + "C"; - return result; -} - -#if DECODE_COOLIX -// Decode the supplied Coolix message. -// -// Args: -// results: Ptr to the data to decode and where to store the decode result. -// nbits: The number of data bits to expect. Typically kCoolixBits. -// strict: Flag indicating if we should perform strict matching. -// Returns: -// boolean: True if it can decode it, false if it can't. -// -// Status: BETA / Probably working. -bool IRrecv::decodeCOOLIX(decode_results *results, uint16_t nbits, - bool strict) { - // The protocol sends the data normal + inverted, alternating on - // each byte. Hence twice the number of expected data bits. - if (results->rawlen < 2 * 2 * nbits + kHeader + kFooter - 1) - return false; // Can't possibly be a valid COOLIX message. - if (strict && nbits != kCoolixBits) - return false; // Not strictly a COOLIX message. - if (nbits % 8 != 0) // nbits has to be a multiple of nr. of bits in a byte. - return false; - - uint64_t data = 0; - uint64_t inverted = 0; - uint16_t offset = kStartOffset; - - if (nbits > sizeof(data) * 8) - return false; // We can't possibly capture a Coolix packet that big. - - // Header - if (!matchMark(results->rawbuf[offset], kCoolixHdrMark)) return false; - // Calculate how long the common tick time is based on the header mark. - uint32_t m_tick = results->rawbuf[offset++] * kRawTick / kCoolixHdrMarkTicks; - if (!matchSpace(results->rawbuf[offset], kCoolixHdrSpace)) return false; - // Calculate how long the common tick time is based on the header space. - uint32_t s_tick = results->rawbuf[offset++] * kRawTick / kCoolixHdrSpaceTicks; - - // Data - // Twice as many bits as there are normal plus inverted bits. - for (uint16_t i = 0; i < nbits * 2; i++, offset++) { - bool flip = (i / 8) % 2; - if (!matchMark(results->rawbuf[offset++], kCoolixBitMarkTicks * m_tick)) - return false; - if (matchSpace(results->rawbuf[offset], kCoolixOneSpaceTicks * s_tick)) { - if (flip) - inverted = (inverted << 1) | 1; - else - data = (data << 1) | 1; - } else if (matchSpace(results->rawbuf[offset], - kCoolixZeroSpaceTicks * s_tick)) { - if (flip) - inverted <<= 1; - else - data <<= 1; - } else { - return false; - } - } - - // Footer - if (!matchMark(results->rawbuf[offset++], kCoolixBitMarkTicks * m_tick)) - return false; - if (offset < results->rawlen && - !matchAtLeast(results->rawbuf[offset], kCoolixMinGapTicks * s_tick)) - return false; - - // Compliance - uint64_t orig = data; // Save a copy of the data. - if (strict) { - for (uint16_t i = 0; i < nbits; i += 8, data >>= 8, inverted >>= 8) - if ((data & 0xFF) != ((inverted & 0xFF) ^ 0xFF)) return false; - } - - // Success - results->decode_type = COOLIX; - results->bits = nbits; - results->value = orig; - results->address = 0; - results->command = 0; - return true; -} -#endif diff --git a/lib/IRremoteESP8266_ID1089/src/ir_Coolix.h b/lib/IRremoteESP8266_ID1089/src/ir_Coolix.h deleted file mode 100644 index fa61437..0000000 --- a/lib/IRremoteESP8266_ID1089/src/ir_Coolix.h +++ /dev/null @@ -1,140 +0,0 @@ -// Coolix A/C -// -// Copyright 2018 David Conran - -#ifndef IR_COOLIX_H_ -#define IR_COOLIX_H_ - -#define __STDC_LIMIT_MACROS -#include -#ifndef UNIT_TEST -#include -#else -#include -#endif -#include "IRremoteESP8266.h" -#include "IRsend.h" - -// CCCCC OOOOO OOOOO LL IIIII XX XX -// CC C OO OO OO OO LL III XX XX -// CC OO OO OO OO LL III XXXX -// CC C OO OO OO OO LL III XX XX -// CCCCC OOOO0 OOOO0 LLLLLLL IIIII XX XX - -// Supports: -// RG57K7(B)/BGEF remote control for Beko BINR 070/071 split-type aircon. -// Ref: -// https://github.com/markszabo/IRremoteESP8266/issues/484 -// Kudos: -// Hamper: For the breakdown and mapping of the bit values. - -// Constants -// Modes -const uint8_t kCoolixCool = 0b00; -const uint8_t kCoolixDry = 0b01; -const uint8_t kCoolixAuto = 0b10; -const uint8_t kCoolixHeat = 0b11; -const uint8_t kCoolixFan = 4; // Synthetic. -const uint32_t kCoolixModeMask = 0b000000000000000000001100; // 0xC -const uint32_t kCoolixZoneFollowMask = 0b000010000000000000000000; // 0x80000 -// Fan Control -const uint8_t kCoolixFanMin = 0b100; -const uint8_t kCoolixFanMed = 0b010; -const uint8_t kCoolixFanMax = 0b001; -const uint8_t kCoolixFanAuto = 0b101; -const uint8_t kCoolixFanAuto0 = 0b000; -const uint8_t kCoolixFanZoneFollow = 0b110; -const uint8_t kCoolixFanFixed = 0b111; -const uint32_t kCoolixFanMask = 0b000000001110000000000000; // 0x00E000 -// Temperature -const uint8_t kCoolixTempMin = 17; // Celsius -const uint8_t kCoolixTempMax = 30; // Celsius -const uint8_t kCoolixTempRange = kCoolixTempMax - kCoolixTempMin + 1; -const uint8_t kCoolixFanTempCode = 0b1110; // Part of Fan Mode. -const uint32_t kCoolixTempMask = 0b11110000; -const uint8_t kCoolixTempMap[kCoolixTempRange] = { - 0b0000, // 17C - 0b0001, // 18c - 0b0011, // 19C - 0b0010, // 20C - 0b0110, // 21C - 0b0111, // 22C - 0b0101, // 23C - 0b0100, // 24C - 0b1100, // 25C - 0b1101, // 26C - 0b1001, // 27C - 0b1000, // 28C - 0b1010, // 29C - 0b1011 // 30C -}; -const uint8_t kCoolixSensorTempMin = 16; // Celsius -const uint8_t kCoolixSensorTempMax = 30; // Celsius -const uint8_t kCoolixSensorTempIgnoreCode = 0b1111; -const uint32_t kCoolixSensorTempMask = 0b000000000000111100000000; // 0xF00 -// Fixed states/messages. -const uint8_t kCoolixPrefix = 0b1011; // 0xB -const uint8_t kCoolixUnknown = 0xFF; -const uint32_t kCoolixOff = 0b101100100111101111100000; // 0xB27BE0 -const uint32_t kCoolixSwing = 0b101100100110101111100000; // 0xB26BE0 -const uint32_t kCoolixSleep = 0b101100101110000000000011; // 0xB2E003 -const uint32_t kCoolixTurbo = 0b101101011111010110100010; // 0xB5F5A2 -const uint32_t kCoolixLed = 0b101101011111010110100101; // 0xB5F5A5 -const uint32_t kCoolixClean = 0b101101011111010110101010; // 0xB5F5AA -// On, 25C, Mode: Auto, Fan: Auto, Zone Follow: Off, Sensor Temp: Ignore. -const uint32_t kCoolixDefaultState = 0b101100101011111111001000; // 0xB2BFC8 - -// Classes -class IRCoolixAC { - public: - explicit IRCoolixAC(uint16_t pin); - - void stateReset(); -#if SEND_COOLIX - void send(const uint16_t repeat = kCoolixDefaultRepeat); -#endif // SEND_COOLIX - void begin(); - void on(); - void off(); - void setPower(const bool state); - bool getPower(); - void setTemp(const uint8_t temp); - uint8_t getTemp(); - void setSensorTemp(const uint8_t desired); - uint8_t getSensorTemp(); - void clearSensorTemp(); - void setFan(const uint8_t fan); - uint8_t getFan(); - void setMode(const uint8_t mode); - uint8_t getMode(); - void setSwing(); - bool getSwing(); - void setSleep(); - bool getSleep(); - void setTurbo(); - bool getTurbo(); - void setLed(); - bool getLed(); - void setClean(); - bool getClean(); - bool getZoneFollow(); - uint32_t getRaw(); - void setRaw(const uint32_t new_code); - -#ifdef ARDUINO - String toString(); -#else - std::string toString(); -#endif - - private: - // The state of the IR remote in IR code form. - uint32_t remote_state; - IRsend _irsend; - void setTempRaw(const uint8_t code); - uint8_t getTempRaw(); - void setSensorTempRaw(const uint8_t code); - void setZoneFollow(const bool state); -}; - -#endif // IR_COOLIX_H_ diff --git a/lib/IRremoteESP8266_ID1089/src/ir_Daikin.cpp b/lib/IRremoteESP8266_ID1089/src/ir_Daikin.cpp deleted file mode 100644 index f041340..0000000 --- a/lib/IRremoteESP8266_ID1089/src/ir_Daikin.cpp +++ /dev/null @@ -1,868 +0,0 @@ -/* -An Arduino sketch to emulate IR Daikin ARC433** remote control unit -Read more at: -http://harizanov.com/2012/02/control-daikin-air-conditioner-over-the-internet/ - -Copyright 2016 sillyfrog -Copyright 2017 sillyfrog, crankyoldgit -Copyright 2018 crankyoldgit -*/ - -#include "ir_Daikin.h" -#include -#ifndef ARDUINO -#include -#endif -#include "IRrecv.h" -#include "IRremoteESP8266.h" -#include "IRsend.h" -#include "IRutils.h" - -// DDDDD AAA IIIII KK KK IIIII NN NN -// DD DD AAAAA III KK KK III NNN NN -// DD DD AA AA III KKKK III NN N NN -// DD DD AAAAAAA III KK KK III NN NNN -// DDDDDD AA AA IIIII KK KK IIIII NN NN - -// Constants -// Ref: -// https://github.com/mharizanov/Daikin-AC-remote-control-over-the-Internet/tree/master/IRremote -// http://rdlab.cdmt.vn/project-2013/daikin-ir-protocol -// https://github.com/markszabo/IRremoteESP8266/issues/582 - -#if SEND_DAIKIN -// Original header -// static uint8_t header1[DAIKIN_HEADER1_LENGTH]; -// header1[0] = 0b00010001; -// header1[1] = 0b11011010; -// header1[2] = 0b00100111; -// header1[3] = 0b00000000; -// header1[4] = 0b11000101; -// header1[5] = 0b00000000; -// header1[6] = 0b00000000; -// header1[7] = 0b11010111; - -// Send a Daikin A/C message. -// -// Args: -// data: An array of kDaikinStateLength bytes containing the IR command. -// -// Status: STABLE -// -// Ref: -// IRDaikinESP.cpp -// https://github.com/mharizanov/Daikin-AC-remote-control-over-the-Internet/tree/master/IRremote -void IRsend::sendDaikin(unsigned char data[], uint16_t nbytes, - uint16_t repeat) { - if (nbytes < kDaikinStateLength) - return; // Not enough bytes to send a proper message. - - for (uint16_t r = 0; r <= repeat; r++) { - // Send the header, 0b00000 - sendGeneric(0, 0, // No header for the header - kDaikinBitMark, kDaikinOneSpace, kDaikinBitMark, - kDaikinZeroSpace, kDaikinBitMark, kDaikinZeroSpace + kDaikinGap, - (uint64_t)0b00000, 5, 38, false, 0, 50); - // Leading header - // Do this as a constant to save RAM and keep in flash memory - sendGeneric(kDaikinHdrMark, kDaikinHdrSpace, kDaikinBitMark, - kDaikinOneSpace, kDaikinBitMark, kDaikinZeroSpace, - kDaikinBitMark, kDaikinZeroSpace + kDaikinGap, - kDaikinFirstHeader64, 64, 38, false, 0, 50); - // Data #1 - sendGeneric(kDaikinHdrMark, kDaikinHdrSpace, kDaikinBitMark, - kDaikinOneSpace, kDaikinBitMark, kDaikinZeroSpace, - kDaikinBitMark, kDaikinZeroSpace + kDaikinGap, data, 8, 38, - false, 0, 50); - // Data #2 - sendGeneric(kDaikinHdrMark, kDaikinHdrSpace, kDaikinBitMark, - kDaikinOneSpace, kDaikinBitMark, kDaikinZeroSpace, - kDaikinBitMark, kDaikinZeroSpace + kDaikinGap, data + 8, - nbytes - 8, 38, false, 0, 50); - } -} -#endif // SEND_DAIKIN - -IRDaikinESP::IRDaikinESP(uint16_t pin) : _irsend(pin) { stateReset(); } - -void IRDaikinESP::begin() { _irsend.begin(); } - -#if SEND_DAIKIN -void IRDaikinESP::send(const uint16_t repeat) { - checksum(); - _irsend.sendDaikin(daikin, kDaikinStateLength, repeat); -} -#endif // SEND_DAIKIN - -// Calculate the checksum for a given data block. -// Args: -// block: Ptr to the start of the data block. -// length: Nr. of bytes to checksum. -// Returns: -// A byte containing the calculated checksum. -uint8_t IRDaikinESP::calcBlockChecksum(const uint8_t *block, - const uint16_t length) { - uint8_t sum = 0; - // Daikin checksum is just the addition of all the data bytes - // in the block but capped to 8 bits. - for (uint16_t i = 0; i < length; i++, block++) sum += *block; - return sum & 0xFFU; -} - -// Verify the checksum is valid for a given state. -// Args: -// state: The array to verify the checksum of. -// length: The size of the state. -// Returns: -// A boolean. -bool IRDaikinESP::validChecksum(const uint8_t state[], const uint16_t length) { - if (length < 8 || state[7] != calcBlockChecksum(state, 7)) return false; - if (length < 10 || - state[length - 1] != calcBlockChecksum(state + 8, length - 9)) - return false; - return true; -} - -// Calculate and set the checksum values for the internal state. -void IRDaikinESP::checksum() { - daikin[7] = calcBlockChecksum(daikin, 7); - daikin[26] = calcBlockChecksum(daikin + 8, 17); -} - -void IRDaikinESP::stateReset() { - for (uint8_t i = 0; i < kDaikinStateLength; i++) daikin[i] = 0x0; - - daikin[0] = 0x11; - daikin[1] = 0xDA; - daikin[2] = 0x27; - daikin[4] = 0x42; - // daikin[7] is a checksum byte, it will be set by checksum(). - daikin[8] = 0x11; - daikin[9] = 0xDA; - daikin[10] = 0x27; - daikin[13] = 0x49; - daikin[14] = 0x1E; - daikin[16] = 0xB0; - daikin[19] = 0x06; - daikin[20] = 0x60; - daikin[23] = 0xC0; - // daikin[26] is a checksum byte, it will be set by checksum(). - checksum(); -} - -uint8_t *IRDaikinESP::getRaw() { - checksum(); // Ensure correct settings before sending. - return daikin; -} - -void IRDaikinESP::setRaw(uint8_t new_code[]) { - for (uint8_t i = 0; i < kDaikinStateLength; i++) daikin[i] = new_code[i]; -} - -void IRDaikinESP::on() { - // state = ON; - setBit(kDaikinBytePower, kDaikinBitPower); -} - -void IRDaikinESP::off() { - // state = OFF; - clearBit(kDaikinBytePower, kDaikinBitPower); -} - -void IRDaikinESP::setPower(bool state) { - if (state) - on(); - else - off(); -} - -bool IRDaikinESP::getPower() { - return (getBit(kDaikinBytePower, kDaikinBitPower) > 0); -} - -// Set the temp in deg C -void IRDaikinESP::setTemp(uint8_t temp) { - if (temp < kDaikinMinTemp) - temp = kDaikinMinTemp; - else if (temp > kDaikinMaxTemp) - temp = kDaikinMaxTemp; - daikin[14] = temp * 2; -} - -uint8_t IRDaikinESP::getTemp() { return daikin[14] / 2; } - -// Set the speed of the fan, 1-5 or kDaikinFanAuto or kDaikinFanQuiet -void IRDaikinESP::setFan(uint8_t fan) { - // Set the fan speed bits, leave low 4 bits alone - uint8_t fanset; - if (fan == kDaikinFanQuiet || fan == kDaikinFanAuto) - fanset = fan; - else if (fan < kDaikinFanMin || fan > kDaikinFanMax) - fanset = kDaikinFanAuto; - else - fanset = 2 + fan; - daikin[16] &= 0x0F; - daikin[16] |= (fanset << 4); -} - -uint8_t IRDaikinESP::getFan() { - uint8_t fan = daikin[16] >> 4; - if (fan != kDaikinFanQuiet && fan != kDaikinFanAuto) fan -= 2; - return fan; -} - -uint8_t IRDaikinESP::getMode() { - /* - kDaikinCool - kDaikinHeat - kDaikinFan - kDaikinAuto - kDaikinDry - */ - return daikin[13] >> 4; -} - -void IRDaikinESP::setMode(uint8_t mode) { - switch (mode) { - case kDaikinCool: - case kDaikinHeat: - case kDaikinFan: - case kDaikinDry: - break; - default: - mode = kDaikinAuto; - } - mode <<= 4; - daikin[13] &= 0b10001111; - daikin[13] |= mode; -} - -void IRDaikinESP::setSwingVertical(bool state) { - if (state) - daikin[16] |= 0x0F; - else - daikin[16] &= 0xF0; -} - -bool IRDaikinESP::getSwingVertical() { return daikin[16] & 0x01; } - -void IRDaikinESP::setSwingHorizontal(bool state) { - if (state) - daikin[17] |= 0x0F; - else - daikin[17] &= 0xF0; -} - -bool IRDaikinESP::getSwingHorizontal() { return daikin[17] & 0x01; } - -void IRDaikinESP::setQuiet(bool state) { - if (state) { - setBit(kDaikinByteSilent, kDaikinBitSilent); - // Powerful & Quiet mode being on are mutually exclusive. - setPowerful(false); - } else { - clearBit(kDaikinByteSilent, kDaikinBitSilent); - } -} - -bool IRDaikinESP::getQuiet() { - return (getBit(kDaikinByteSilent, kDaikinBitSilent) > 0); -} - -void IRDaikinESP::setPowerful(bool state) { - if (state) { - setBit(kDaikinBytePowerful, kDaikinBitPowerful); - // Powerful, Quiet, & Econo mode being on are mutually exclusive. - setQuiet(false); - setEcono(false); - } else { - clearBit(kDaikinBytePowerful, kDaikinBitPowerful); - } -} - -bool IRDaikinESP::getPowerful() { - return (getBit(kDaikinBytePowerful, kDaikinBitPowerful) > 0); -} - -void IRDaikinESP::setSensor(bool state) { - if (state) - setBit(kDaikinByteSensor, kDaikinBitSensor); - else - clearBit(kDaikinByteSensor, kDaikinBitSensor); -} - -bool IRDaikinESP::getSensor() { - return (getBit(kDaikinByteSensor, kDaikinBitSensor) > 0); -} - -void IRDaikinESP::setEcono(bool state) { - if (state) { - setBit(kDaikinByteEcono, kDaikinBitEcono); - // Powerful & Econo mode being on are mutually exclusive. - setPowerful(false); - } else { - clearBit(kDaikinByteEcono, kDaikinBitEcono); - } -} - -bool IRDaikinESP::getEcono() { - return (getBit(kDaikinByteEcono, kDaikinBitEcono) > 0); -} - -void IRDaikinESP::setEye(bool state) { - if (state) - setBit(kDaikinByteEye, kDaikinBitEye); - else - clearBit(kDaikinByteEye, kDaikinBitEye); -} - -bool IRDaikinESP::getEye() { - return (getBit(kDaikinByteEye, kDaikinBitEye) > 0); -} - -void IRDaikinESP::setMold(bool state) { - if (state) - setBit(kDaikinByteMold, kDaikinBitMold); - else - clearBit(kDaikinByteMold, kDaikinBitMold); -} - -bool IRDaikinESP::getMold() { - return (getBit(kDaikinByteMold, kDaikinBitMold) > 0); -} - -void IRDaikinESP::setBit(uint8_t byte, uint8_t bitmask) { - daikin[byte] |= bitmask; -} - -void IRDaikinESP::clearBit(uint8_t byte, uint8_t bitmask) { - bitmask = ~bitmask; - daikin[byte] &= bitmask; -} - -uint8_t IRDaikinESP::getBit(uint8_t byte, uint8_t bitmask) { - return daikin[byte] & bitmask; -} - -// starttime: Number of minutes after midnight, in 10 minutes increments -void IRDaikinESP::enableOnTimer(uint16_t starttime) { - setBit(kDaikinByteOnTimer, kDaikinBitOnTimer); - daikin[18] = (uint8_t)(starttime & 0x00FF); - // only keep 4 bits - daikin[19] &= 0xF0; - daikin[19] |= (uint8_t)((starttime >> 8) & 0x0F); -} - -void IRDaikinESP::disableOnTimer() { - enableOnTimer(0x600); - clearBit(kDaikinByteOnTimer, kDaikinBitOnTimer); -} - -uint16_t IRDaikinESP::getOnTime() { - uint16_t ret; - ret = daikin[19] & 0x0F; - ret = ret << 8; - ret += daikin[18]; - return ret; -} - -bool IRDaikinESP::getOnTimerEnabled() { - return getBit(kDaikinByteOnTimer, kDaikinBitOnTimer); -} - -// endtime: Number of minutes after midnight, in 10 minutes increments -void IRDaikinESP::enableOffTimer(uint16_t endtime) { - setBit(kDaikinByteOffTimer, kDaikinBitOffTimer); - daikin[20] = (uint8_t)((endtime >> 4) & 0xFF); - daikin[19] &= 0x0F; - daikin[19] |= (uint8_t)((endtime & 0x000F) << 4); -} - -void IRDaikinESP::disableOffTimer() { - enableOffTimer(0x600); - clearBit(kDaikinByteOffTimer, kDaikinBitOffTimer); -} - -uint16_t IRDaikinESP::getOffTime() { - uint16_t ret, tmp; - ret = daikin[20]; - ret <<= 4; - tmp = daikin[19] & 0xF0; - tmp >>= 4; - ret += tmp; - return ret; -} - -bool IRDaikinESP::getOffTimerEnabled() { - return getBit(kDaikinByteOffTimer, kDaikinBitOffTimer); -} - -void IRDaikinESP::setCurrentTime(uint16_t numMins) { - if (numMins > 24 * 60) numMins = 0; // If > 23:59, set to 00:00 - daikin[5] = (uint8_t)(numMins & 0x00FF); - // only keep 4 bits - daikin[6] &= 0xF0; - daikin[6] |= (uint8_t)((numMins >> 8) & 0x0F); -} - -uint16_t IRDaikinESP::getCurrentTime() { - uint16_t ret; - ret = daikin[6] & 0x0F; - ret <<= 8; - ret += daikin[5]; - return ret; -} - -#ifdef ARDUINO -String IRDaikinESP::renderTime(uint16_t timemins) { - String ret; -#else // ARDUINO -std::string IRDaikinESP::renderTime(uint16_t timemins) { - std::string ret; -#endif // ARDUINO - uint16_t hours, mins; - hours = timemins / 60; - ret = uint64ToString(hours) + ":"; - mins = timemins - (hours * 60); - if (mins < 10) ret += "0"; - ret += uint64ToString(mins); - return ret; -} - -// Convert the internal state into a human readable string. -#ifdef ARDUINO -String IRDaikinESP::toString() { - String result = ""; -#else // ARDUINO -std::string IRDaikinESP::toString() { - std::string result = ""; -#endif // ARDUINO - result += "Power: "; - if (getPower()) - result += "On"; - else - result += "Off"; - result += ", Mode: " + uint64ToString(getMode()); - switch (getMode()) { - case kDaikinAuto: - result += " (AUTO)"; - break; - case kDaikinCool: - result += " (COOL)"; - break; - case kDaikinHeat: - result += " (HEAT)"; - break; - case kDaikinDry: - result += " (DRY)"; - break; - case kDaikinFan: - result += " (FAN)"; - break; - default: - result += " (UNKNOWN)"; - } - result += ", Temp: " + uint64ToString(getTemp()) + "C"; - result += ", Fan: " + uint64ToString(getFan()); - switch (getFan()) { - case kDaikinFanAuto: - result += " (AUTO)"; - break; - case kDaikinFanQuiet: - result += " (QUIET)"; - break; - case kDaikinFanMin: - result += " (MIN)"; - break; - case kDaikinFanMax: - result += " (MAX)"; - break; - } - result += ", Powerful: "; - if (getPowerful()) - result += "On"; - else - result += "Off"; - result += ", Quiet: "; - if (getQuiet()) - result += "On"; - else - result += "Off"; - result += ", Sensor: "; - if (getSensor()) - result += "On"; - else - result += "Off"; - result += ", Eye: "; - if (getEye()) - result += "On"; - else - result += "Off"; - result += ", Mold: "; - if (getMold()) - result += "On"; - else - result += "Off"; - result += ", Swing (Horizontal): "; - if (getSwingHorizontal()) - result += "On"; - else - result += "Off"; - result += ", Swing (Vertical): "; - if (getSwingVertical()) - result += "On"; - else - result += "Off"; - result += ", Current Time: " + renderTime(getCurrentTime()); - result += ", On Time: "; - if (getOnTimerEnabled()) - result += renderTime(getOnTime()); - else - result += "Off"; - result += ", Off Time: "; - if (getOffTimerEnabled()) - result += renderTime(getOffTime()); - else - result += "Off"; - - return result; -} - -#if DAIKIN_DEBUG -// Print what we have -void IRDaikinESP::printState() { -#ifdef ARDUINO - String strbits; -#else // ARDUINO - std::string strbits; -#endif // ARDUINO - DPRINTLN("Raw Bits:"); - for (uint8_t i = 0; i < kDaikinStateLength; i++) { - strbits = uint64ToString(daikin[i], BIN); - while (strbits.length() < 8) strbits = "0" + strbits; - DPRINT(strbits); - DPRINT(" "); - } - DPRINTLN(""); - DPRINTLN(toString()); -} -#endif // DAIKIN_DEBUG - -/* - * Return most important bits to allow replay - * layout is: - * 0: Power - * 1-3: Mode - * 4-7: Fan speed/mode - * 8-14: Target Temperature - * 15: Econo - * 16: Powerful - * 17: Quiet - * 18: Sensor - * 19: Swing Vertical - * 20-31: Current time (mins since midnight) - * */ -uint32_t IRDaikinESP::getCommand() { - uint32_t ret = 0; - uint32_t tmp = 0; - if (getPower()) ret |= 0b00000000000000000000000000000001; - tmp = getMode(); - tmp = tmp << 1; - ret |= tmp; - - tmp = getFan(); - tmp <<= 4; - ret |= tmp; - - tmp = getTemp(); - tmp <<= 8; - ret |= tmp; - - if (getEcono()) ret |= 0b00000000000000001000000000000000; - if (getPowerful()) ret |= 0b00000000000000010000000000000000; - if (getQuiet()) ret |= 0b00000000000000100000000000000000; - if (getSensor()) ret |= 0b00000000000001000000000000000000; - if (getSwingVertical()) ret |= 0b00000000000010000000000000000000; - ret |= (getCurrentTime() << 20); - return ret; -} - -void IRDaikinESP::setCommand(uint32_t value) { - uint32_t tmp = 0; - if (value & 0b00000000000000000000000000000001) setPower(true); - tmp = value & 0b00000000000000000000000000001110; - tmp >>= 1; - setMode(tmp); - - tmp = value & 0b00000000000000000000000011110000; - tmp >>= 4; - setFan(tmp); - - tmp = value & 0b00000000000000000111111100000000; - tmp >>= 8; - setTemp(tmp); - - if (value & 0b00000000000000001000000000000000) setEcono(true); - if (value & 0b00000000000000010000000000000000) setPowerful(true); - if (value & 0b00000000000000100000000000000000) setQuiet(true); - if (value & 0b00000000000001000000000000000000) setSensor(true); - if (value & 0b00000000000010000000000000000000) setSwingVertical(true); - - value >>= 20; - setCurrentTime(value); -} - -#if DECODE_DAIKIN - -void addbit(bool val, unsigned char data[]) { - uint8_t curbit = data[kDaikinCurBit]; - uint8_t curindex = data[kDaikinCurIndex]; - if (val) { - unsigned char bit = 1; - bit = bit << curbit; - data[curindex] |= bit; - } - curbit++; - if (curbit == 8) { - curbit = 0; - curindex++; - } - data[kDaikinCurBit] = curbit; - data[kDaikinCurIndex] = curindex; -} - -bool checkheader(decode_results *results, uint16_t *offset) { - if (!IRrecv::matchMark(results->rawbuf[(*offset)++], kDaikinBitMark, - kDaikinTolerance, kDaikinMarkExcess)) - return false; - if (!IRrecv::matchSpace(results->rawbuf[(*offset)++], - kDaikinZeroSpace + kDaikinGap, kDaikinTolerance, - kDaikinMarkExcess)) - return false; - if (!IRrecv::matchMark(results->rawbuf[(*offset)++], kDaikinHdrMark, - kDaikinTolerance, kDaikinMarkExcess)) - return false; - if (!IRrecv::matchSpace(results->rawbuf[(*offset)++], kDaikinHdrSpace, - kDaikinTolerance, kDaikinMarkExcess)) - return false; - - return true; -} - -bool readbits(decode_results *results, uint16_t *offset, - unsigned char daikin_code[], uint16_t countbits) { - for (uint16_t i = 0; i < countbits && *offset < results->rawlen - 1; - i++, (*offset)++) { - if (!IRrecv::matchMark(results->rawbuf[(*offset)++], kDaikinBitMark, - kDaikinTolerance, kDaikinMarkExcess)) - return false; - if (IRrecv::matchSpace(results->rawbuf[*offset], kDaikinOneSpace, - kDaikinTolerance, kDaikinMarkExcess)) - addbit(1, daikin_code); - else if (IRrecv::matchSpace(results->rawbuf[*offset], kDaikinZeroSpace, - kDaikinTolerance, kDaikinMarkExcess)) - addbit(0, daikin_code); - else - return false; - } - return true; -} - -// Decode the supplied Daikin A/C message. -// Args: -// results: Ptr to the data to decode and where to store the decode result. -// nbits: Nr. of bits to expect in the data portion. (kDaikinRawBits) -// strict: Flag to indicate if we strictly adhere to the specification. -// Returns: -// boolean: True if it can decode it, false if it can't. -// -// Status: BETA / Should be working. -// -// Notes: -// If DAIKIN_DEBUG enabled, will print all the set options and values. -// -// Ref: -// https://github.com/mharizanov/Daikin-AC-remote-control-over-the-Internet/tree/master/IRremote -bool IRrecv::decodeDaikin(decode_results *results, uint16_t nbits, - bool strict) { - if (results->rawlen < kDaikinRawBits) return false; - - // Compliance - if (strict && nbits != kDaikinRawBits) return false; - - uint16_t offset = kStartOffset; - unsigned char daikin_code[kDaikinStateLength + 2]; - for (uint8_t i = 0; i < kDaikinStateLength + 2; i++) daikin_code[i] = 0; - - // Header (#1) - for (uint8_t i = 0; i < 10; i++) { - if (!matchMark(results->rawbuf[offset++], kDaikinBitMark)) return false; - } - if (!checkheader(results, &offset)) return false; - - // Data (#1) - if (!readbits(results, &offset, daikin_code, 8 * 8)) return false; - - // Ignore everything that has just been captured as it is not needed. - // Some remotes may not send this portion, my remote did, but it's not - // required. - for (uint8_t i = 0; i < kDaikinStateLength + 2; i++) daikin_code[i] = 0; - - // Header (#2) - if (!checkheader(results, &offset)) return false; - - // Data (#2) - if (!readbits(results, &offset, daikin_code, 8 * 8)) return false; - - // Header (#3) - if (!checkheader(results, &offset)) return false; - - // Data (#3), read up everything else - if (!readbits(results, &offset, daikin_code, kDaikinBits - (8 * 8))) - return false; - - // Footer - if (!matchMark(results->rawbuf[offset++], kDaikinBitMark)) return false; - if (offset < results->rawlen && - !matchAtLeast(results->rawbuf[offset], kDaikinGap)) - return false; - - // Compliance - if (strict) { - if (!IRDaikinESP::validChecksum(daikin_code)) return false; - } - - // Success -#if DAIKIN_DEBUG - IRDaikinESP dako = IRDaikinESP(0); - dako.setRaw(daikin_code); -#ifdef ARDUINO - yield(); -#endif // ARDUINO - dako.printState(); -#endif // DAIKIN_DEBUG - - // Copy across the bits to state - for (uint8_t i = 0; i < kDaikinStateLength; i++) - results->state[i] = daikin_code[i]; - results->bits = kDaikinStateLength * 8; - results->decode_type = DAIKIN; - return true; -} -#endif // DECODE_DAIKIN - -#if SEND_DAIKIN2 -// Send a Daikin2 A/C message. -// -// Args: -// data: An array of kDaikin2StateLength bytes containing the IR command. -// -// Status: Alpha/Untested. -// -// Ref: -// https://github.com/markszabo/IRremoteESP8266/issues/582 -void IRsend::sendDaikin2(unsigned char data[], uint16_t nbytes, - uint16_t repeat) { - if (nbytes < kDaikin2Section1Length) - return; // Not enough bytes to send a partial message. - - for (uint16_t r = 0; r <= repeat; r++) { - // Leader - sendGeneric(kDaikin2LeaderMark, kDaikin2LeaderSpace, - 0, 0, 0, 0, 0, 0, (uint64_t) 0, // No data payload. - 0, 38000, false, 0, 50); - // Section #1 - sendGeneric(kDaikin2HdrMark, kDaikin2HdrSpace, kDaikin2BitMark, - kDaikin2OneSpace, kDaikin2BitMark, kDaikin2ZeroSpace, - kDaikin2BitMark, kDaikin2Gap, data, kDaikin2Section1Length, - 38000, false, 0, 50); - // Section #2 - sendGeneric(kDaikin2HdrMark, kDaikin2HdrSpace, kDaikin2BitMark, - kDaikin2OneSpace, kDaikin2BitMark, kDaikin2ZeroSpace, - kDaikin2BitMark, kDaikin2Gap, data + kDaikin2Section1Length, - nbytes - kDaikin2Section1Length, - 38000, false, 0, 50); - } -} -#endif // SEND_DAIKIN2 - -#if DECODE_DAIKIN2 -// Decode the supplied Daikin2 A/C message. -// Args: -// results: Ptr to the data to decode and where to store the decode result. -// nbits: Nr. of bits to expect in the data portion. (kDaikin2Bits) -// strict: Flag to indicate if we strictly adhere to the specification. -// Returns: -// boolean: True if it can decode it, false if it can't. -// -// Status: Alpha / Untested. -// -// Ref: -// https://github.com/mharizanov/Daikin-AC-remote-control-over-the-Internet/tree/master/IRremote -bool IRrecv::decodeDaikin2(decode_results *results, uint16_t nbits, - bool strict) { - if (results->rawlen < 2 * (nbits + kHeader + kFooter) + kHeader - 1) - return false; - - // Compliance - if (strict && nbits != kDaikin2Bits) return false; - - uint16_t offset = kStartOffset; - uint16_t dataBitsSoFar = 0; - uint16_t i = 0; - match_result_t data_result; - uint8_t sectionSize[kDaikin2Sections] = {kDaikin2Section1Length, - kDaikin2Section2Length}; - - // Leader - if (!matchMark(results->rawbuf[offset++], kDaikin2LeaderMark)) return false; - if (!matchSpace(results->rawbuf[offset++], kDaikin2LeaderSpace)) - return false; - - // Sections - // Keep reading bytes until we either run out of section or state to fill. - for (uint8_t section = 0, pos = 0; section < kDaikin2Sections; - section++) { - pos += sectionSize[section]; - - // Section Header - if (!matchMark(results->rawbuf[offset++], kDaikin2HdrMark)) return false; - if (!matchSpace(results->rawbuf[offset++], kDaikin2HdrSpace)) return false; - - // Section Data - for (; offset <= results->rawlen - 16 && i < pos; - i++, dataBitsSoFar += 8, offset += data_result.used) { - // Read in a byte at a time. - data_result = - matchData(&(results->rawbuf[offset]), 8, kDaikin2BitMark, - kDaikin2OneSpace, kDaikin2BitMark, - kDaikin2ZeroSpace, kTolerance, kMarkExcess, false); - if (data_result.success == false) break; // Fail - results->state[i] = (uint8_t)data_result.data; - } - - // Section Footer - if (!matchMark(results->rawbuf[offset++], kDaikin2BitMark)) return false; - if (section < kDaikin2Sections - 1) { // Inter-section gaps. - if (!matchSpace(results->rawbuf[offset++], kDaikin2Gap)) return false; - } else { // Last section / End of message gap. - if (offset <= results->rawlen && - !matchAtLeast(results->rawbuf[offset++], kDaikin2Gap)) return false; - } - } - - // Compliance - if (strict) { - // Re-check we got the correct size/length due to the way we read the data. - if (dataBitsSoFar != kDaikin2Bits) return false; - } - - // Success - results->decode_type = DAIKIN2; - results->bits = dataBitsSoFar; - // No need to record the state as we stored it as we decoded it. - // As we use result->state, we don't record value, address, or command as it - // is a union data type. - return true; -} -#endif // DECODE_DAIKIN2 diff --git a/lib/IRremoteESP8266_ID1089/src/ir_Daikin.h b/lib/IRremoteESP8266_ID1089/src/ir_Daikin.h deleted file mode 100644 index 077bd55..0000000 --- a/lib/IRremoteESP8266_ID1089/src/ir_Daikin.h +++ /dev/null @@ -1,221 +0,0 @@ -// Copyright 2016 sillyfrog -// Copyright 2017 sillyfrog, crankyoldgit -#ifndef IR_DAIKIN_H_ -#define IR_DAIKIN_H_ - -#ifndef UNIT_TEST -#include -#else -#include -#endif -#include "IRrecv.h" -#include "IRremoteESP8266.h" -#include "IRsend.h" - -// Option to disable the additional Daikin debug info to conserve memory -#define DAIKIN_DEBUG false - -// DDDDD AAA IIIII KK KK IIIII NN NN -// DD DD AAAAA III KK KK III NNN NN -// DD DD AA AA III KKKK III NN N NN -// DD DD AAAAAAA III KK KK III NN NNN -// DDDDDD AA AA IIIII KK KK IIIII NN NN - -/* - Daikin AC map - byte 5=Current time, mins past midnight, low bits - byte 6 - b0-b3=Current time, mins past midnight, high bits - byte 7= checksum of the first part (and last byte before a 29ms pause) - byte 13=mode - b7 = 0 - b6+b5+b4 = Mode - Modes: b6+b5+b4 - 011 = Cool - 100 = Heat (temp 23) - 110 = FAN (temp not shown, but 25) - 000 = Fully Automatic (temp 25) - 010 = DRY (temp 0xc0 = 96 degrees c) - b3 = 1 - b2 = OFF timer set - b1 = ON timer set - b0 = Air Conditioner ON - byte 14=temp*2 (Temp should be between 10 - 32) - byte 16=Fan - FAN control - b7+b6+b5+b4 = Fan speed - Fan: b7+b6+b5+b4 - 0×3 = 1 bar - 0×4 = 2 bar - 0×5 = 3 bar - 0×6 = 4 bar - 0×7 = 5 bar - 0xa = Auto - 0xb = Quite - b3+b2+b1+b0 = Swing control up/down - Swing control up/down: - 0000 = Swing up/down off - 1111 = Swing up/down on - byte 17 - Swing control left/right: - 0000 = Swing left/right off - 1111 = Swing left/right on - byte 18=On timer mins past midnight, low bits - byte 19 - b0-b3=On timer mins past midnight, high bits - b4-b7=Off timer mins past midnight, low bits - byte 20=Off timer mins past midnight, high bits - byte 21=Aux -> Powerful (bit 1), Silent (bit 5) - byte 24=Aux2 - b1: Sensor - b2: Econo mode - b7: Intelligent eye on - byte 25=Aux3 - b1: Mold Proof - byte 26= checksum of the second part -*/ - -// Constants -const uint8_t kDaikinAuto = 0b000; -const uint8_t kDaikinDry = 0b010; -const uint8_t kDaikinCool = 0b011; -const uint8_t kDaikinHeat = 0b100; -const uint8_t kDaikinFan = 0b110; -const uint8_t kDaikinMinTemp = 10; // Celsius -const uint8_t kDaikinMaxTemp = 32; // Celsius -const uint8_t kDaikinFanMin = 1; -const uint8_t kDaikinFanMax = 5; -const uint8_t kDaikinFanAuto = 0b1010; -const uint8_t kDaikinFanQuiet = 0b1011; -const uint8_t kDaikinBytePower = 13; -const uint8_t kDaikinBitPower = 0b00000001; -const uint8_t kDaikinBytePowerful = 21; -const uint8_t kDaikinBitPowerful = 0b00000001; -const uint8_t kDaikinByteSilent = 21; -const uint8_t kDaikinBitSilent = 0b00100000; -const uint8_t kDaikinByteSensor = 24; -const uint8_t kDaikinBitSensor = 0b00000010; -const uint8_t kDaikinByteEcono = 24; -const uint8_t kDaikinBitEcono = 0b00000100; -const uint8_t kDaikinByteEye = 24; -const uint8_t kDaikinBitEye = 0b10000000; -const uint8_t kDaikinByteMold = 25; -const uint8_t kDaikinBitMold = 0b00000010; -const uint8_t kDaikinByteOffTimer = 13; -const uint8_t kDaikinBitOffTimer = 0b00000100; -const uint8_t kDaikinByteOnTimer = 13; -const uint8_t kDaikinBitOnTimer = 0b00000010; -const uint8_t kDaikinCurBit = kDaikinStateLength; -const uint8_t kDaikinCurIndex = kDaikinStateLength + 1; -const uint8_t kDaikinTolerance = 35; -const uint16_t kDaikinMarkExcess = kMarkExcess; -const uint16_t kDaikinHdrMark = 3650; // kDaikinBitMark * 8 -const uint16_t kDaikinHdrSpace = 1623; // kDaikinBitMark * 4 -const uint16_t kDaikinBitMark = 428; -const uint16_t kDaikinZeroSpace = 428; -const uint16_t kDaikinOneSpace = 1280; -const uint16_t kDaikinGap = 29000; -// Note bits in each octet swapped so can be sent as a single value -const uint64_t kDaikinFirstHeader64 = - 0b1101011100000000000000001100010100000000001001111101101000010001; - -// Another variant of the protocol. -const uint16_t kDaikin2LeaderMark = 10024; -const uint16_t kDaikin2LeaderSpace = 25180; -const uint16_t kDaikin2Gap = kDaikin2LeaderMark + kDaikin2LeaderSpace; -const uint16_t kDaikin2HdrMark = 3500; -const uint16_t kDaikin2HdrSpace = 1728; -const uint16_t kDaikin2BitMark = 418; -const uint16_t kDaikin2OneSpace = 1315; -const uint16_t kDaikin2ZeroSpace = 451; -const uint16_t kDaikin2Sections = 2; -const uint16_t kDaikin2Section1Length = 20; -const uint16_t kDaikin2Section2Length = 19; - -// Legacy defines. -#define DAIKIN_COOL kDaikinCool -#define DAIKIN_HEAT kDaikinHeat -#define DAIKIN_FAN kDaikinFan -#define DAIKIN_AUTO kDaikinAuto -#define DAIKIN_DRY kDaikinDry -#define DAIKIN_MIN_TEMP kDaikinMinTemp -#define DAIKIN_MAX_TEMP kDaikinMaxTemp -#define DAIKIN_FAN_MIN kDaikinFanMin -#define DAIKIN_FAN_MAX kDaikinFanMax -#define DAIKIN_FAN_AUTO kDaikinFanAuto -#define DAIKIN_FAN_QUIET kDaikinFanQuiet - -class IRDaikinESP { - public: - explicit IRDaikinESP(uint16_t pin); - -#if SEND_DAIKIN - void send(const uint16_t repeat = kDaikinDefaultRepeat); -#endif - void begin(); - void on(); - void off(); - void setPower(bool state); - bool getPower(); - void setTemp(uint8_t temp); - uint8_t getTemp(); - void setFan(uint8_t fan); - uint8_t getFan(); - uint8_t getMode(); - void setMode(uint8_t mode); - void setSwingVertical(bool state); - bool getSwingVertical(); - void setSwingHorizontal(bool state); - bool getSwingHorizontal(); - bool getQuiet(); - void setQuiet(bool state); - bool getPowerful(); - void setPowerful(bool state); - void setSensor(bool state); - bool getSensor(); - void setEcono(bool state); - bool getEcono(); - void setEye(bool state); - bool getEye(); - void setMold(bool state); - bool getMold(); - void enableOnTimer(uint16_t starttime); - void disableOnTimer(); - uint16_t getOnTime(); - bool getOnTimerEnabled(); - void enableOffTimer(uint16_t endtime); - void disableOffTimer(); - uint16_t getOffTime(); - bool getOffTimerEnabled(); - void setCurrentTime(uint16_t time); - uint16_t getCurrentTime(); - uint8_t* getRaw(); - void setRaw(uint8_t new_code[]); -#if DAIKIN_DEBUG - void printState(); -#endif // DAIKIN_DEBUG - uint32_t getCommand(); - void setCommand(uint32_t value); - static bool validChecksum(const uint8_t state[], - const uint16_t length = kDaikinStateLength); -#ifdef ARDUINO - String toString(); - static String renderTime(uint16_t timemins); -#else - std::string toString(); - static std::string renderTime(uint16_t timemins); -#endif - - private: - // # of bytes per command - uint8_t daikin[kDaikinStateLength]; - void stateReset(); - static uint8_t calcBlockChecksum(const uint8_t* block, const uint16_t length); - void checksum(); - void setBit(uint8_t byte, uint8_t bitmask); - void clearBit(uint8_t byte, uint8_t bitmask); - uint8_t getBit(uint8_t byte, uint8_t bitmask); - IRsend _irsend; -}; - -#endif // IR_DAIKIN_H_ diff --git a/lib/IRremoteESP8266_ID1089/src/ir_Denon.cpp b/lib/IRremoteESP8266_ID1089/src/ir_Denon.cpp deleted file mode 100644 index 6798e02..0000000 --- a/lib/IRremoteESP8266_ID1089/src/ir_Denon.cpp +++ /dev/null @@ -1,148 +0,0 @@ -// Copyright 2016 Massimiliano Pinto -// Copyright 2017 David Conran - -#include -#include "IRrecv.h" -#include "IRsend.h" -#include "IRutils.h" - -// 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 - -// Original Denon support added by https://github.com/csBlueChip -// Ported over by Massimiliano Pinto - -// Constants -// Ref: -// https://github.com/z3t0/Arduino-IRremote/blob/master/ir_Denon.cpp -const uint16_t kDenonTick = 263; -const uint16_t kDenonHdrMarkTicks = 1; -const uint16_t kDenonHdrMark = kDenonHdrMarkTicks * kDenonTick; -const uint16_t kDenonHdrSpaceTicks = 3; -const uint16_t kDenonHdrSpace = kDenonHdrSpaceTicks * kDenonTick; -const uint16_t kDenonBitMarkTicks = 1; -const uint16_t kDenonBitMark = kDenonBitMarkTicks * kDenonTick; -const uint16_t kDenonOneSpaceTicks = 7; -const uint16_t kDenonOneSpace = kDenonOneSpaceTicks * kDenonTick; -const uint16_t kDenonZeroSpaceTicks = 3; -const uint16_t kDenonZeroSpace = kDenonZeroSpaceTicks * kDenonTick; -const uint16_t kDenonMinCommandLengthTicks = 510; -const uint16_t kDenonMinGapTicks = - kDenonMinCommandLengthTicks - - (kDenonHdrMarkTicks + kDenonHdrSpaceTicks + - kDenonBits * (kDenonBitMarkTicks + kDenonOneSpaceTicks) + - kDenonBitMarkTicks); -const uint32_t kDenonMinGap = kDenonMinGapTicks * kDenonTick; -const uint64_t kDenonManufacturer = 0x2A4CULL; - -#if SEND_DENON -// Send a Denon message -// -// Args: -// data: Contents of the message to be sent. -// nbits: Nr. of bits of data to be sent. Typically DENON_BITS. -// repeat: Nr. of additional times the message is to be sent. -// -// Status: BETA / Should be working. -// -// Notes: -// Some Denon devices use a Kaseikyo/Panasonic 48-bit format -// Others use the Sharp protocol. -// Ref: -// https://github.com/z3t0/Arduino-IRremote/blob/master/ir_Denon.cpp -// http://assets.denon.com/documentmaster/us/denon%20master%20ir%20hex.xls -void IRsend::sendDenon(uint64_t data, uint16_t nbits, uint16_t repeat) { - if (nbits >= kPanasonicBits) // Is this really Panasonic? - sendPanasonic64(data, nbits, repeat); - else if (nbits == kDenonLegacyBits) - // Support legacy (broken) calls of sendDenon(). - sendSharpRaw(data & (~0x2000ULL), nbits + 1, repeat); - else - sendSharpRaw(data, nbits, repeat); -} -#endif - -#if DECODE_DENON -// Decode a Denon message. -// -// Args: -// results: Ptr to the data to decode and where to store the decode result. -// nbits: Expected nr. of data bits. (Typically DENON_BITS) -// Returns: -// boolean: True if it can decode it, false if it can't. -// -// Status: BETA / Should work fine. -// -// Ref: -// https://github.com/z3t0/Arduino-IRremote/blob/master/ir_Denon.cpp -bool IRrecv::decodeDenon(decode_results *results, uint16_t nbits, bool strict) { - // Compliance - if (strict) { - switch (nbits) { - case DENON_BITS: - case DENON_48_BITS: - case kDenonLegacyBits: - break; - default: - return false; - } - } - - // Denon uses the Sharp & Panasonic(Kaseikyo) protocol for some - // devices, so check for those first. - // It is not exactly like Sharp's protocols, but close enough. - // e.g. The expansion bit is not set for Denon vs. set for Sharp. - // Ditto for Panasonic, it's the same except for a different - // manufacturer code. - - if (!decodeSharp(results, nbits, true, false) && - !decodePanasonic(results, nbits, true, kDenonManufacturer)) { - // We couldn't decode it as expected, so try the old legacy method. - // NOTE: I don't think this following protocol actually exists. - // Looks like a partial version of the Sharp protocol. - // Check we have enough data - if (results->rawlen < 2 * nbits + kHeader + kFooter - 1) return false; - if (strict && nbits != kDenonLegacyBits) return false; - - uint64_t data = 0; - uint16_t offset = kStartOffset; - - // Header - if (!matchMark(results->rawbuf[offset], kDenonHdrMark)) return false; - // Calculate how long the common tick time is based on the header mark. - uint32_t m_tick = results->rawbuf[offset++] * kRawTick / kDenonHdrMarkTicks; - if (!matchSpace(results->rawbuf[offset], kDenonHdrSpace)) return false; - uint32_t s_tick = - results->rawbuf[offset++] * kRawTick / kDenonHdrSpaceTicks; - - // Data - match_result_t data_result = - matchData(&(results->rawbuf[offset]), nbits, - kDenonBitMarkTicks * m_tick, kDenonOneSpaceTicks * s_tick, - kDenonBitMarkTicks * m_tick, kDenonZeroSpaceTicks * s_tick); - if (data_result.success == false) return false; - data = data_result.data; - offset += data_result.used; - - // Footer - if (!matchMark(results->rawbuf[offset++], kDenonBitMarkTicks * m_tick)) - return false; - - // Success - results->bits = nbits; - results->value = data; - results->address = 0; - results->command = 0; - } // Legacy decode. - - // Compliance - if (strict && nbits != results->bits) return false; - - // Success - results->decode_type = DENON; - return true; -} -#endif diff --git a/lib/IRremoteESP8266_ID1089/src/ir_Dish.cpp b/lib/IRremoteESP8266_ID1089/src/ir_Dish.cpp deleted file mode 100644 index 040aa3b..0000000 --- a/lib/IRremoteESP8266_ID1089/src/ir_Dish.cpp +++ /dev/null @@ -1,136 +0,0 @@ -// Copyright Todd Treece -// Copyright 2017 David Conran - -#include "IRrecv.h" -#include "IRsend.h" -#include "IRutils.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 - -// DISH support originally by Todd Treece -// http://unionbridge.org/design/ircommand - -// Constants -// Ref: -// https://github.com/marcosamarinho/IRremoteESP8266/blob/master/ir_Dish.cpp -// http://www.hifi-remote.com/wiki/index.php?title=Dish -const uint16_t kDishTick = 100; -const uint16_t kDishHdrMarkTicks = 4; -const uint16_t kDishHdrMark = kDishHdrMarkTicks * kDishTick; -const uint16_t kDishHdrSpaceTicks = 61; -const uint16_t kDishHdrSpace = kDishHdrSpaceTicks * kDishTick; -const uint16_t kDishBitMarkTicks = 4; -const uint16_t kDishBitMark = kDishBitMarkTicks * kDishTick; -const uint16_t kDishOneSpaceTicks = 17; -const uint16_t kDishOneSpace = kDishOneSpaceTicks * kDishTick; -const uint16_t kDishZeroSpaceTicks = 28; -const uint16_t kDishZeroSpace = kDishZeroSpaceTicks * kDishTick; -const uint16_t kDishRptSpaceTicks = kDishHdrSpaceTicks; -const uint16_t kDishRptSpace = kDishRptSpaceTicks * kDishTick; - -#if SEND_DISH -// Send an IR command to a DISH NETWORK device. -// -// Args: -// data: The contents of the command you want to send. -// nbits: The bit size of the command being sent. -// repeat: The number of times you want the command to be repeated. -// -// Status: BETA / Previously working. -// -// Note: -// Dishplayer is a different protocol. -// Typically a DISH device needs to get a command a total of at least 4 -// times to accept it. e.g. repeat=3 -// -// 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 -// -// Ref: -// http://www.hifi-remote.com/wiki/index.php?title=Dish -void IRsend::sendDISH(uint64_t data, uint16_t nbits, uint16_t repeat) { - enableIROut(57600); // Set modulation freq. to 57.6kHz. - // Header is only ever sent once. - mark(kDishHdrMark); - space(kDishHdrSpace); - - sendGeneric(0, 0, // No headers from here on in. - kDishBitMark, kDishOneSpace, kDishBitMark, kDishZeroSpace, - kDishBitMark, kDishRptSpace, data, nbits, 57600, true, repeat, - 50); -} -#endif - -#if DECODE_DISH -// Decode the supplied DISH NETWORK message. -// -// Args: -// results: Ptr to the data to decode and where to store the decode result. -// nbits: Nr. of bits to expect in the data portion. Typically kDishBits. -// strict: Flag to indicate if we strictly adhere to the specification. -// Returns: -// boolean: True if it can decode it, false if it can't. -// -// Status: ALPHA (untested and unconfirmed.) -// -// Note: -// Dishplayer is a different protocol. -// Typically a DISH device needs to get a command a total of at least 4 -// times to accept it. -// Ref: -// http://www.hifi-remote.com/wiki/index.php?title=Dish -// http://lirc.sourceforge.net/remotes/echostar/301_501_3100_5100_58xx_59xx -// https://github.com/marcosamarinho/IRremoteESP8266/blob/master/ir_Dish.cpp -bool IRrecv::decodeDISH(decode_results *results, uint16_t nbits, bool strict) { - if (results->rawlen < 2 * nbits + kHeader + kFooter - 1) - return false; // Not enough entries to be valid. - if (strict && nbits != kDishBits) return false; // Not strictly compliant. - - uint64_t data = 0; - uint16_t offset = kStartOffset; - - // Header - if (!match(results->rawbuf[offset], kDishHdrMark)) return false; - // Calculate how long the common tick time is based on the header mark. - uint32_t m_tick = results->rawbuf[offset++] * kRawTick / kDishHdrMarkTicks; - if (!matchSpace(results->rawbuf[offset], kDishHdrSpace)) return false; - // Calculate how long the common tick time is based on the header space. - uint32_t s_tick = results->rawbuf[offset++] * kRawTick / kDishHdrSpaceTicks; - - // Data - match_result_t data_result = - matchData(&(results->rawbuf[offset]), nbits, kDishBitMarkTicks * m_tick, - kDishOneSpaceTicks * s_tick, kDishBitMarkTicks * m_tick, - kDishZeroSpaceTicks * s_tick); - if (data_result.success == false) return false; - data = data_result.data; - offset += data_result.used; - - // Footer - if (!matchMark(results->rawbuf[offset++], kDishBitMarkTicks * m_tick)) - return false; - - // Compliance - if (strict) { - // The DISH protocol calls for a repeated message, so strictly speaking - // there should be a code following this. Only require it if we are set to - // strict matching. - if (!matchSpace(results->rawbuf[offset], kDishRptSpaceTicks * s_tick)) - return false; - } - - // Success - results->decode_type = DISH; - results->bits = nbits; - results->value = data; - results->address = 0; - results->command = 0; - return true; -} -#endif diff --git a/lib/IRremoteESP8266_ID1089/src/ir_Electra.cpp b/lib/IRremoteESP8266_ID1089/src/ir_Electra.cpp deleted file mode 100644 index df69be7..0000000 --- a/lib/IRremoteESP8266_ID1089/src/ir_Electra.cpp +++ /dev/null @@ -1,112 +0,0 @@ -// Copyright 2018 David Conran - -#include "IRrecv.h" -#include "IRsend.h" -#include "IRutils.h" - -// EEEEEEE LL EEEEEEE CCCCC TTTTTTT RRRRRR AAA -// EE LL EE CC C TTT RR RR AAAAA -// EEEEE LL EEEEE CC TTT RRRRRR AA AA -// EE LL EE CC C TTT RR RR AAAAAAA -// EEEEEEE LLLLLLL EEEEEEE CCCCC TTT RR RR AA AA - -// Electra A/C added by crankyoldgit -// -// Equipment it seems compatible with: -// * - -// Ref: -// https://github.com/markszabo/IRremoteESP8266/issues/527 - -// Constants -const uint16_t kElectraAcHdrMark = 9166; -const uint16_t kElectraAcBitMark = 646; -const uint16_t kElectraAcHdrSpace = 4470; -const uint16_t kElectraAcOneSpace = 1647; -const uint16_t kElectraAcZeroSpace = 547; -const uint32_t kElectraAcMessageGap = 100000; // Completely made-up guess. - -#if SEND_ELECTRA_AC -// Send a Electra message -// -// Args: -// data: Contents of the message to be sent. (Guessing MSBF order) -// nbits: Nr. of bits of data to be sent. Typically kElectraAcBits. -// repeat: Nr. of additional times the message is to be sent. -// -// Status: Alpha / Needs testing against a real device. -// -void IRsend::sendElectraAC(uint8_t data[], uint16_t nbytes, uint16_t repeat) { - for (uint16_t r = 0; r <= repeat; r++) - sendGeneric(kElectraAcHdrMark, kElectraAcHdrSpace, kElectraAcBitMark, - kElectraAcOneSpace, kElectraAcBitMark, kElectraAcZeroSpace, - kElectraAcBitMark, kElectraAcMessageGap, data, nbytes, - 38000, // Complete guess of the modulation frequency. - true, 0, 50); -} -#endif - -#if DECODE_ELECTRA_AC -// Decode the supplied Electra A/C message. -// -// Args: -// results: Ptr to the data to decode and where to store the decode result. -// nbits: The number of data bits to expect. Typically kElectraAcBits. -// strict: Flag indicating if we should perform strict matching. -// Returns: -// boolean: True if it can decode it, false if it can't. -// -// Status: Alpha / Needs testing against a real device. -// -bool IRrecv::decodeElectraAC(decode_results *results, uint16_t nbits, - bool strict) { - if (nbits % 8 != 0) // nbits has to be a multiple of nr. of bits in a byte. - return false; - - if (strict) { - if (nbits != kElectraAcBits) - return false; // Not strictly a ELECTRA_AC message. - } - - // The protocol sends the data normal + inverted, alternating on - // each byte. Hence twice the number of expected data bits. - if (results->rawlen < 2 * nbits + kHeader + kFooter - 1) - return false; // Can't possibly be a valid ELECTRA_AC message. - - uint16_t offset = kStartOffset; - - // Message Header - if (!matchMark(results->rawbuf[offset++], kElectraAcHdrMark)) return false; - if (!matchSpace(results->rawbuf[offset++], kElectraAcHdrSpace)) return false; - - // Data Section - match_result_t data_result; - uint16_t dataBitsSoFar = 0; - // Keep reading bytes until we either run out of section or state to fill. - for (uint16_t i = 0; offset <= results->rawlen - 16 && i < nbits / 8; - i++, dataBitsSoFar += 8, offset += data_result.used) { - data_result = matchData(&(results->rawbuf[offset]), 8, kElectraAcBitMark, - kElectraAcOneSpace, kElectraAcBitMark, - kElectraAcZeroSpace, kTolerance, 0, true); - if (data_result.success == false) return false; // Fail - results->state[i] = data_result.data; - } - - // Message Footer - if (!matchMark(results->rawbuf[offset++], kElectraAcBitMark)) return false; - if (offset <= results->rawlen && - !matchAtLeast(results->rawbuf[offset++], kElectraAcMessageGap)) - return false; - - // Compliance - if (strict && dataBitsSoFar != nbits) return false; - - // Success - results->decode_type = ELECTRA_AC; - results->bits = dataBitsSoFar; - // No need to record the state as we stored it as we decoded it. - // As we use result->state, we don't record value, address, or command as it - // is a union data type. - return true; -} -#endif // DECODE_ELECTRA_AC diff --git a/lib/IRremoteESP8266_ID1089/src/ir_Fujitsu.cpp b/lib/IRremoteESP8266_ID1089/src/ir_Fujitsu.cpp deleted file mode 100644 index 60bb96e..0000000 --- a/lib/IRremoteESP8266_ID1089/src/ir_Fujitsu.cpp +++ /dev/null @@ -1,519 +0,0 @@ -// Copyright 2017 Jonny Graham, David Conran -#include "ir_Fujitsu.h" -#include -#ifndef ARDUINO -#include -#endif -#include "IRsend.h" -#include "IRutils.h" - -// Fujitsu A/C support added by Jonny Graham & David Conran - -// Equipment it seems compatible with: -// * Fujitsu ASYG30LFCA with remote AR-RAH2E -// * Fujitsu AST9RSGCW with remote AR-DB1 -// * - -// Ref: -// These values are based on averages of measurements -const uint16_t kFujitsuAcHdrMark = 3324; -const uint16_t kFujitsuAcHdrSpace = 1574; -const uint16_t kFujitsuAcBitMark = 448; -const uint16_t kFujitsuAcOneSpace = 1182; -const uint16_t kFujitsuAcZeroSpace = 390; -const uint16_t kFujitsuAcMinGap = 8100; - -#if SEND_FUJITSU_AC -// Send a Fujitsu A/C message. -// -// Args: -// data: An array of bytes containing the IR command. -// nbytes: Nr. of bytes of data in the array. Typically one of: -// kFujitsuAcStateLength -// kFujitsuAcStateLength - 1 -// kFujitsuAcStateLengthShort -// kFujitsuAcStateLengthShort - 1 -// repeat: Nr. of times the message is to be repeated. -// (Default = kFujitsuAcMinRepeat). -// -// Status: BETA / Appears to be working. -// -void IRsend::sendFujitsuAC(unsigned char data[], uint16_t nbytes, - uint16_t repeat) { - sendGeneric(kFujitsuAcHdrMark, kFujitsuAcHdrSpace, kFujitsuAcBitMark, - kFujitsuAcOneSpace, kFujitsuAcBitMark, kFujitsuAcZeroSpace, - kFujitsuAcBitMark, kFujitsuAcMinGap, data, nbytes, 38, false, - repeat, 50); -} -#endif // SEND_FUJITSU_AC - -// Code to emulate Fujitsu A/C IR remote control unit. - -// Initialise the object. -IRFujitsuAC::IRFujitsuAC(uint16_t pin, fujitsu_ac_remote_model_t model) - : _irsend(pin) { - setModel(model); - stateReset(); -} - -void IRFujitsuAC::setModel(fujitsu_ac_remote_model_t model) { - _model = model; - switch (model) { - case ARDB1: - _state_length = kFujitsuAcStateLength - 1; - _state_length_short = kFujitsuAcStateLengthShort - 1; - break; - default: - _state_length = kFujitsuAcStateLength; - _state_length_short = kFujitsuAcStateLengthShort; - } -} - -// Reset the state of the remote to a known good state/sequence. -void IRFujitsuAC::stateReset() { - _temp = 24; - _fanSpeed = kFujitsuAcFanHigh; - _mode = kFujitsuAcModeCool; - _swingMode = kFujitsuAcSwingBoth; - _cmd = kFujitsuAcCmdTurnOn; - buildState(); -} - -// Configure the pin for output. -void IRFujitsuAC::begin() { _irsend.begin(); } - -#if SEND_FUJITSU_AC -// Send the current desired state to the IR LED. -void IRFujitsuAC::send(const uint16_t repeat) { - getRaw(); - _irsend.sendFujitsuAC(remote_state, getStateLength(), repeat); -} -#endif // SEND_FUJITSU_AC - -void IRFujitsuAC::buildState() { - remote_state[0] = 0x14; - remote_state[1] = 0x63; - remote_state[2] = 0x00; - remote_state[3] = 0x10; - remote_state[4] = 0x10; - bool fullCmd = false; - switch (_cmd) { - case kFujitsuAcCmdTurnOff: - remote_state[5] = 0x02; - break; - case kFujitsuAcCmdStepHoriz: - remote_state[5] = 0x79; - break; - case kFujitsuAcCmdStepVert: - remote_state[5] = 0x6C; - break; - default: - switch (_model) { - case ARRAH2E: - remote_state[5] = 0xFE; - break; - case ARDB1: - remote_state[5] = 0xFC; - break; - } - fullCmd = true; - break; - } - if (fullCmd) { // long codes - uint8_t tempByte = _temp - kFujitsuAcMinTemp; - // Nr. of bytes in the message after this byte. - remote_state[6] = _state_length - 7; - - remote_state[7] = 0x30; - remote_state[8] = (_cmd == kFujitsuAcCmdTurnOn) | (tempByte << 4); - remote_state[9] = _mode | 0 << 4; // timer off - remote_state[10] = _fanSpeed | _swingMode << 4; - remote_state[11] = 0; // timerOff values - remote_state[12] = 0; // timerOff/On values - remote_state[13] = 0; // timerOn values - if (_model == ARRAH2E) - remote_state[14] = 0x20; - else - remote_state[14] = 0x00; - - uint8_t checksum = 0; - uint8_t checksum_complement = 0; - if (_model == ARRAH2E) { - checksum = sumBytes(remote_state + _state_length_short, - _state_length - _state_length_short - 1); - } else if (_model == ARDB1) { - checksum = sumBytes(remote_state, _state_length - 1); - checksum_complement = 0x9B; - } - // and negate the checksum and store it in the last byte. - remote_state[_state_length - 1] = checksum_complement - checksum; - } else { // short codes - if (_model == ARRAH2E) - // The last byte is the inverse of penultimate byte - remote_state[_state_length_short - 1] = - ~remote_state[_state_length_short - 2]; - // Zero the rest of the state. - for (uint8_t i = _state_length_short; i < kFujitsuAcStateLength; i++) - remote_state[i] = 0; - } -} - -uint8_t IRFujitsuAC::getStateLength() { - buildState(); // Force an update of the internal state. - if ((_model == ARRAH2E && remote_state[5] != 0xFE) || - (_model == ARDB1 && remote_state[5] != 0xFC)) - return _state_length_short; - else - return _state_length; -} - -// Return a pointer to the internal state date of the remote. -uint8_t* IRFujitsuAC::getRaw() { - buildState(); - return remote_state; -} - -void IRFujitsuAC::buildFromState(const uint16_t length) { - switch (length) { - case kFujitsuAcStateLength - 1: - case kFujitsuAcStateLengthShort - 1: - setModel(ARDB1); - break; - default: - setModel(ARRAH2E); - } - switch (remote_state[6]) { - case 8: - setModel(ARDB1); - break; - case 9: - setModel(ARRAH2E); - break; - } - setTemp((remote_state[8] >> 4) + kFujitsuAcMinTemp); - if (remote_state[8] & 0x1) - setCmd(kFujitsuAcCmdTurnOn); - else - setCmd(kFujitsuAcCmdStayOn); - setMode(remote_state[9] & 0b111); - setFanSpeed(remote_state[10] & 0b111); - setSwing(remote_state[10] >> 4); - switch (remote_state[5]) { - case kFujitsuAcCmdTurnOff: - case kFujitsuAcCmdStepHoriz: - case kFujitsuAcCmdStepVert: - setCmd(remote_state[5]); - break; - } -} - -bool IRFujitsuAC::setRaw(const uint8_t newState[], const uint16_t length) { - if (length > kFujitsuAcStateLength) return false; - for (uint16_t i = 0; i < kFujitsuAcStateLength; i++) { - if (i < length) - remote_state[i] = newState[i]; - else - remote_state[i] = 0; - } - buildFromState(length); - return true; -} - -// Set the requested power state of the A/C to off. -void IRFujitsuAC::off() { _cmd = kFujitsuAcCmdTurnOff; } - -void IRFujitsuAC::stepHoriz() { - switch (_model) { - case ARDB1: - break; // This remote doesn't have a horizontal option. - default: - _cmd = kFujitsuAcCmdStepHoriz; - } -} - -void IRFujitsuAC::stepVert() { _cmd = kFujitsuAcCmdStepVert; } - -// Set the requested command of the A/C. -void IRFujitsuAC::setCmd(uint8_t cmd) { - switch (cmd) { - case kFujitsuAcCmdTurnOff: - case kFujitsuAcCmdTurnOn: - case kFujitsuAcCmdStayOn: - case kFujitsuAcCmdStepVert: - _cmd = cmd; - break; - case kFujitsuAcCmdStepHoriz: - if (_model != ARDB1) // AR-DB1 remote doesn't have step horizontal. - _cmd = cmd; - // FALLTHRU - default: - _cmd = kFujitsuAcCmdStayOn; - break; - } -} - -uint8_t IRFujitsuAC::getCmd() { return _cmd; } - -bool IRFujitsuAC::getPower() { return _cmd != kFujitsuAcCmdTurnOff; } - -// Set the temp. in deg C -void IRFujitsuAC::setTemp(uint8_t temp) { - temp = std::max((uint8_t)kFujitsuAcMinTemp, temp); - temp = std::min((uint8_t)kFujitsuAcMaxTemp, temp); - _temp = temp; -} - -uint8_t IRFujitsuAC::getTemp() { return _temp; } - -// Set the speed of the fan -void IRFujitsuAC::setFanSpeed(uint8_t fanSpeed) { - if (fanSpeed > kFujitsuAcFanQuiet) - fanSpeed = kFujitsuAcFanHigh; // Set the fan to maximum if out of range. - _fanSpeed = fanSpeed; -} -uint8_t IRFujitsuAC::getFanSpeed() { return _fanSpeed; } - -// Set the requested climate operation mode of the a/c unit. -void IRFujitsuAC::setMode(uint8_t mode) { - if (mode > kFujitsuAcModeHeat) - mode = kFujitsuAcModeHeat; // Set the mode to maximum if out of range. - _mode = mode; -} - -uint8_t IRFujitsuAC::getMode() { return _mode; } -// Set the requested swing operation mode of the a/c unit. -void IRFujitsuAC::setSwing(uint8_t swingMode) { - switch (_model) { - case ARDB1: - // Set the mode to max if out of range - if (swingMode > kFujitsuAcSwingVert) swingMode = kFujitsuAcSwingVert; - break; - case ARRAH2E: - default: - // Set the mode to max if out of range - if (swingMode > kFujitsuAcSwingBoth) swingMode = kFujitsuAcSwingBoth; - } - _swingMode = swingMode; -} - -uint8_t IRFujitsuAC::getSwing() { return _swingMode; } - -bool IRFujitsuAC::validChecksum(uint8_t state[], uint16_t length) { - uint8_t sum = 0; - uint8_t sum_complement = 0; - uint8_t checksum = state[length - 1]; - switch (length) { - case kFujitsuAcStateLengthShort: // ARRAH2E - return state[length - 1] == (uint8_t)~state[length - 2]; - case kFujitsuAcStateLength - 1: // ARDB1 - sum = sumBytes(state, length - 1); - sum_complement = 0x9B; - break; - case kFujitsuAcStateLength: // ARRAH2E - sum = sumBytes(state + kFujitsuAcStateLengthShort, - length - 1 - kFujitsuAcStateLengthShort); - break; - default: // Includes ARDB1 short. - return true; // Assume the checksum is valid for other lengths. - } - return checksum == (uint8_t)(sum_complement - sum); // Does it match? -} - -// Convert the internal state into a human readable string. -#ifdef ARDUINO -String IRFujitsuAC::toString() { - String result = ""; -#else -std::string IRFujitsuAC::toString() { - std::string result = ""; -#endif // ARDUINO - result += "Power: "; - if (getPower()) - result += "On"; - else - result += "Off"; - result += ", Mode: " + uint64ToString(getMode()); - switch (getMode()) { - case kFujitsuAcModeAuto: - result += " (AUTO)"; - break; - case kFujitsuAcModeCool: - result += " (COOL)"; - break; - case kFujitsuAcModeHeat: - result += " (HEAT)"; - break; - case kFujitsuAcModeDry: - result += " (DRY)"; - break; - case kFujitsuAcModeFan: - result += " (FAN)"; - break; - default: - result += " (UNKNOWN)"; - } - result += ", Temp: " + uint64ToString(getTemp()) + "C"; - result += ", Fan: " + uint64ToString(getFanSpeed()); - switch (getFanSpeed()) { - case kFujitsuAcFanAuto: - result += " (AUTO)"; - break; - case kFujitsuAcFanHigh: - result += " (HIGH)"; - break; - case kFujitsuAcFanMed: - result += " (MED)"; - break; - case kFujitsuAcFanLow: - result += " (LOW)"; - break; - case kFujitsuAcFanQuiet: - result += " (QUIET)"; - break; - } - result += ", Swing: "; - switch (getSwing()) { - case kFujitsuAcSwingOff: - result += "Off"; - break; - case kFujitsuAcSwingVert: - result += "Vert"; - break; - case kFujitsuAcSwingHoriz: - result += "Horiz"; - break; - case kFujitsuAcSwingBoth: - result += "Vert + Horiz"; - break; - default: - result += "UNKNOWN"; - } - result += ", Command: "; - switch (getCmd()) { - case kFujitsuAcCmdStepHoriz: - result += "Step vane horizontally"; - break; - case kFujitsuAcCmdStepVert: - result += "Step vane vertically"; - break; - default: - result += "N/A"; - } - return result; -} - -#if DECODE_FUJITSU_AC -// Decode a Fujitsu AC IR message if possible. -// Places successful decode information in the results pointer. -// Args: -// results: Ptr to the data to decode and where to store the decode result. -// nbits: The number of data bits to expect. Typically kFujitsuAcBits. -// strict: Flag to indicate if we strictly adhere to the specification. -// Returns: -// boolean: True if it can decode it, false if it can't. -// -// Status: ALPHA / Untested. -// -// Ref: -// -bool IRrecv::decodeFujitsuAC(decode_results* results, uint16_t nbits, - bool strict) { - uint16_t offset = kStartOffset; - uint16_t dataBitsSoFar = 0; - - // Have we got enough data to successfully decode? - if (results->rawlen < (2 * kFujitsuAcMinBits) + kHeader + kFooter - 1) - return false; // Can't possibly be a valid message. - - // Compliance - if (strict) { - switch (nbits) { - case kFujitsuAcBits: - case kFujitsuAcBits - 8: - case kFujitsuAcMinBits: - case kFujitsuAcMinBits + 8: - break; - default: - return false; // Must be called with the correct nr. of bits. - } - } - - // Header - if (!matchMark(results->rawbuf[offset++], kFujitsuAcHdrMark)) return false; - if (!matchSpace(results->rawbuf[offset++], kFujitsuAcHdrSpace)) return false; - - // Data (Fixed signature) - match_result_t data_result = - matchData(&(results->rawbuf[offset]), kFujitsuAcMinBits - 8, - kFujitsuAcBitMark, kFujitsuAcOneSpace, kFujitsuAcBitMark, - kFujitsuAcZeroSpace, kTolerance, kMarkExcess, false); - if (data_result.success == false) return false; // Fail - if (data_result.data != 0x1010006314) return false; // Signature failed. - dataBitsSoFar += kFujitsuAcMinBits - 8; - offset += data_result.used; - results->state[0] = 0x14; - results->state[1] = 0x63; - results->state[2] = 0x00; - results->state[3] = 0x10; - results->state[4] = 0x10; - - // Keep reading bytes until we either run out of message or state to fill. - for (uint16_t i = 5; - offset <= results->rawlen - 16 && i < kFujitsuAcStateLength; - i++, dataBitsSoFar += 8, offset += data_result.used) { - data_result = matchData( - &(results->rawbuf[offset]), 8, kFujitsuAcBitMark, kFujitsuAcOneSpace, - kFujitsuAcBitMark, kFujitsuAcZeroSpace, kTolerance, kMarkExcess, false); - if (data_result.success == false) break; // Fail - results->state[i] = data_result.data; - } - - // Footer - if (offset > results->rawlen || - !matchMark(results->rawbuf[offset++], kFujitsuAcBitMark)) - return false; - // The space is optional if we are out of capture. - if (offset < results->rawlen && - !matchAtLeast(results->rawbuf[offset], kFujitsuAcMinGap)) - return false; - - // Compliance - if (strict) { - if (dataBitsSoFar != nbits) return false; - } - - results->decode_type = FUJITSU_AC; - results->bits = dataBitsSoFar; - - // Compliance - switch (dataBitsSoFar) { - case kFujitsuAcMinBits: - // Check if this values indicate that this should have been a long state - // message. - if (results->state[5] == 0xFC) return false; - return true; // Success - case kFujitsuAcMinBits + 8: - // Check if this values indicate that this should have been a long state - // message. - if (results->state[5] == 0xFE) return false; - // The last byte needs to be the inverse of the penultimate byte. - if (results->state[5] != (uint8_t)~results->state[6]) return false; - return true; // Success - case kFujitsuAcBits - 8: - // Long messages of this size require this byte be correct. - if (results->state[5] != 0xFC) return false; - break; - case kFujitsuAcBits: - // Long messages of this size require this byte be correct. - if (results->state[5] != 0xFE) return false; - break; - default: - return false; // Unexpected size. - } - if (!IRFujitsuAC::validChecksum(results->state, dataBitsSoFar / 8)) - return false; - - // Success - return true; // All good. -} -#endif // DECODE_FUJITSU_AC diff --git a/lib/IRremoteESP8266_ID1089/src/ir_Fujitsu.h b/lib/IRremoteESP8266_ID1089/src/ir_Fujitsu.h deleted file mode 100644 index d5dc837..0000000 --- a/lib/IRremoteESP8266_ID1089/src/ir_Fujitsu.h +++ /dev/null @@ -1,123 +0,0 @@ -// Copyright 2017 Jonny Graham -// Copyright 2018 David Conran -#ifndef IR_FUJITSU_H_ -#define IR_FUJITSU_H_ - -#define __STDC_LIMIT_MACROS -#include -#ifdef ARDUINO -#include -#else -#include -#endif -#include "IRrecv.h" -#include "IRremoteESP8266.h" -#include "IRsend.h" - -// FUJITSU A/C support added by Jonny Graham - -// Constants -const uint8_t kFujitsuAcModeAuto = 0x00; -const uint8_t kFujitsuAcModeCool = 0x01; -const uint8_t kFujitsuAcModeDry = 0x02; -const uint8_t kFujitsuAcModeFan = 0x03; -const uint8_t kFujitsuAcModeHeat = 0x04; - -const uint8_t kFujitsuAcCmdStayOn = 0x00; -const uint8_t kFujitsuAcCmdTurnOn = 0x01; -const uint8_t kFujitsuAcCmdTurnOff = 0x02; -const uint8_t kFujitsuAcCmdStepHoriz = 0x79; -const uint8_t kFujitsuAcCmdStepVert = 0x6C; - -const uint8_t kFujitsuAcFanAuto = 0x00; -const uint8_t kFujitsuAcFanHigh = 0x01; -const uint8_t kFujitsuAcFanMed = 0x02; -const uint8_t kFujitsuAcFanLow = 0x03; -const uint8_t kFujitsuAcFanQuiet = 0x04; - -const uint8_t kFujitsuAcMinTemp = 16; // 16C -const uint8_t kFujitsuAcMaxTemp = 30; // 30C - -const uint8_t kFujitsuAcSwingOff = 0x00; -const uint8_t kFujitsuAcSwingVert = 0x01; -const uint8_t kFujitsuAcSwingHoriz = 0x02; -const uint8_t kFujitsuAcSwingBoth = 0x03; - -// Legacy defines. -#define FUJITSU_AC_MODE_AUTO kFujitsuAcModeAuto -#define FUJITSU_AC_MODE_COOL kFujitsuAcModeCool -#define FUJITSU_AC_MODE_DRY kFujitsuAcModeDry -#define FUJITSU_AC_MODE_FAN kFujitsuAcModeFan -#define FUJITSU_AC_MODE_HEAT kFujitsuAcModeHeat -#define FUJITSU_AC_CMD_STAY_ON kFujitsuAcCmdStayOn -#define FUJITSU_AC_CMD_TURN_ON kFujitsuAcCmdTurnOn -#define FUJITSU_AC_CMD_TURN_OFF kFujitsuAcCmdTurnOff -#define FUJITSU_AC_CMD_STEP_HORIZ kFujitsuAcCmdStepHoriz -#define FUJITSU_AC_CMD_STEP_VERT kFujitsuAcCmdStepVert -#define FUJITSU_AC_FAN_AUTO kFujitsuAcFanAuto -#define FUJITSU_AC_FAN_HIGH kFujitsuAcFanHigh -#define FUJITSU_AC_FAN_MED kFujitsuAcFanMed -#define FUJITSU_AC_FAN_LOW kFujitsuAcFanLow -#define FUJITSU_AC_FAN_QUIET kFujitsuAcFanQuiet -#define FUJITSU_AC_MIN_TEMP kFujitsuAcMinTemp -#define FUJITSU_AC_MAX_TEMP kFujitsuAcMaxTemp -#define FUJITSU_AC_SWING_OFF kFujitsuAcSwingOff -#define FUJITSU_AC_SWING_VERT kFujitsuAcSwingVert -#define FUJITSU_AC_SWING_HORIZ kFujitsuAcSwingHoriz -#define FUJITSU_AC_SWING_BOTH kFujitsuAcSwingBoth - -enum fujitsu_ac_remote_model_t { - ARRAH2E = 1, - ARDB1, -}; - -class IRFujitsuAC { - public: - explicit IRFujitsuAC(uint16_t pin, fujitsu_ac_remote_model_t model = ARRAH2E); - - void setModel(fujitsu_ac_remote_model_t model); - void stateReset(); -#if SEND_FUJITSU_AC - void send(const uint16_t repeat = kFujitsuAcMinRepeat); -#endif // SEND_FUJITSU_AC - void begin(); - void off(); - void stepHoriz(); - void stepVert(); - void setCmd(uint8_t cmd); - uint8_t getCmd(); - void setTemp(uint8_t temp); - uint8_t getTemp(); - void setFanSpeed(uint8_t fan); - uint8_t getFanSpeed(); - void setMode(uint8_t mode); - uint8_t getMode(); - void setSwing(uint8_t mode); - uint8_t getSwing(); - uint8_t* getRaw(); - bool setRaw(const uint8_t newState[], const uint16_t length); - uint8_t getStateLength(); - static bool validChecksum(uint8_t* state, uint16_t length); - bool getPower(); -#ifdef ARDUINO - String toString(); -#else - std::string toString(); -#endif - - private: - uint8_t remote_state[kFujitsuAcStateLength]; - IRsend _irsend; - uint8_t _temp; - uint8_t _fanSpeed; - uint8_t _mode; - uint8_t _swingMode; - uint8_t _cmd; - fujitsu_ac_remote_model_t _model; - uint8_t _state_length; - uint8_t _state_length_short; - void buildState(); - void buildFromState(const uint16_t length); -}; - -#endif // IR_FUJITSU_H_ diff --git a/lib/IRremoteESP8266_ID1089/src/ir_GICable.cpp b/lib/IRremoteESP8266_ID1089/src/ir_GICable.cpp deleted file mode 100644 index 229e4e5..0000000 --- a/lib/IRremoteESP8266_ID1089/src/ir_GICable.cpp +++ /dev/null @@ -1,116 +0,0 @@ -// Copyright 2018 David Conran - -#define __STDC_LIMIT_MACROS -#include -#include -#include "IRrecv.h" -#include "IRsend.h" -#include "IRutils.h" - -// GGGG IIIII CCCCC AAA BBBBB LL EEEEEEE -// GG GG III CC C AAAAA BB B LL EE -// GG III CC AA AA BBBBBB LL EEEEE -// GG GG ... III ... CC C AAAAAAA BB BB LL EE -// GGGGGG ... IIIII ... CCCCC AA AA BBBBBB LLLLLLL EEEEEEE -// -// Ref: -// https://github.com/cyborg5/IRLib2/blob/master/IRLibProtocols/IRLib_P09_GICable.h -// https://github.com/markszabo/IRremoteESP8266/issues/447 - -// Constants -const uint16_t kGicableHdrMark = 9000; -const uint16_t kGicableHdrSpace = 4400; -const uint16_t kGicableBitMark = 550; -const uint16_t kGicableOneSpace = 4400; -const uint16_t kGicableZeroSpace = 2200; -const uint16_t kGicableRptSpace = 2200; -const uint32_t kGicableMinCommandLength = 99600; -const uint32_t kGicableMinGap = - kGicableMinCommandLength - - (kGicableHdrMark + kGicableHdrSpace + - kGicableBits * (kGicableBitMark + kGicableOneSpace) + kGicableBitMark); - -#if SEND_GICABLE -// Send a raw G.I. Cable formatted message. -// -// Args: -// data: The message to be sent. -// nbits: The number of bits of the message to be sent. -// Typically kGicableBits. -// repeat: The number of times the command is to be repeated. -// -// Status: Alpha / Untested. -// -// Ref: -void IRsend::sendGICable(uint64_t data, uint16_t nbits, uint16_t repeat) { - sendGeneric(kGicableHdrMark, kGicableHdrSpace, kGicableBitMark, - kGicableOneSpace, kGicableBitMark, kGicableZeroSpace, - kGicableBitMark, kGicableMinGap, kGicableMinCommandLength, data, - nbits, 39, true, 0, // Repeats are handled later. - 50); - // Message repeat sequence. - if (repeat) - sendGeneric(kGicableHdrMark, kGicableRptSpace, 0, 0, 0, - 0, // No actual data sent. - kGicableBitMark, kGicableMinGap, kGicableMinCommandLength, 0, - 0, // No data to be sent. - 39, true, repeat - 1, 50); -} -#endif // SEND_GICABLE - -#if DECODE_GICABLE -// Decode the supplied G.I. Cable message. -// -// Args: -// results: Ptr to the data to decode and where to store the decode result. -// nbits: The number of data bits to expect. Typically kGicableBits. -// strict: Flag indicating if we should perform strict matching. -// Returns: -// boolean: True if it can decode it, false if it can't. -// -// Status: Alpha / Not tested against a real device. -bool IRrecv::decodeGICable(decode_results *results, uint16_t nbits, - bool strict) { - if (results->rawlen < 2 * (nbits + kHeader + kFooter) - 1) - return false; // Can't possibly be a valid GICABLE message. - if (strict && nbits != kGicableBits) - return false; // Not strictly an GICABLE message. - - uint64_t data = 0; - uint16_t offset = kStartOffset; - - // Header - if (!matchMark(results->rawbuf[offset++], kGicableHdrMark)) return false; - if (!matchSpace(results->rawbuf[offset++], kGicableHdrSpace)) return false; - - // Data - match_result_t data_result = - matchData(&(results->rawbuf[offset]), nbits, kGicableBitMark, - kGicableOneSpace, kGicableBitMark, kGicableZeroSpace); - if (data_result.success == false) return false; - data = data_result.data; - offset += data_result.used; - - // Footer - if (!matchMark(results->rawbuf[offset++], kGicableBitMark)) return false; - if (offset < results->rawlen && - !matchAtLeast(results->rawbuf[offset++], kGicableMinGap)) - return false; - - // Compliance - if (strict) { - // We expect a repeat frame. - if (!matchMark(results->rawbuf[offset++], kGicableHdrMark)) return false; - if (!matchSpace(results->rawbuf[offset++], kGicableRptSpace)) return false; - if (!matchMark(results->rawbuf[offset++], kGicableBitMark)) return false; - } - - // Success - results->bits = nbits; - results->value = data; - results->decode_type = GICABLE; - results->command = 0; - results->address = 0; - return true; -} -#endif // DECODE_GICABLE diff --git a/lib/IRremoteESP8266_ID1089/src/ir_GlobalCache.cpp b/lib/IRremoteESP8266_ID1089/src/ir_GlobalCache.cpp deleted file mode 100644 index daa9dd2..0000000 --- a/lib/IRremoteESP8266_ID1089/src/ir_GlobalCache.cpp +++ /dev/null @@ -1,70 +0,0 @@ -// Copyright 2016 Hisham Khalifa -// Copyright 2017 David Conran - -#include -#include "IRsend.h" - -// GGG L OOOO BBBB AA L CCCC AA CCCC H H EEEEEE -// G G L O O B B AAAA L C C AAAA C C H H E -// G L O O BBBBB A A L C A A C HHHHHH EEEE -// G GG L O O B BB AAAAAA L C C AAAAAA C C H H E -// GGGGG LLLLLL OOOO BBBBB A A LLLLLL CCCC A A CCCC H H EEEEEE - -// Global Cache IR format sender originally added by Hisham Khalifa -// (http://www.hishamkhalifa.com) - -// Constants -const uint16_t kGlobalCacheMaxRepeat = 50; -const uint32_t kGlobalCacheMinUsec = 80; -const uint8_t kGlobalCacheFreqIndex = 0; -const uint8_t kGlobalCacheRptIndex = kGlobalCacheFreqIndex + 1; -const uint8_t kGlobalCacheRptStartIndex = kGlobalCacheRptIndex + 1; -const uint8_t kGlobalCacheStartIndex = kGlobalCacheRptStartIndex + 1; - -#if SEND_GLOBALCACHE -// Send a shortened GlobalCache (GC) IRdb/control tower formatted message. -// -// Args: -// buf: An array of uint16_t containing the shortened GlobalCache data. -// len: Nr. of entries in the buf[] array. -// -// Status: STABLE / Known working. -// -// Note: -// Global Cache format without the emitter ID or request ID. -// Starts at the frequency (Hertz), followed by nr. of times to emit (count), -// then the offset for repeats (where a repeat will start from), -// then the rest of entries are the actual IR message as units of periodic -// time. -// e.g. sendir,1:1,1,38000,1,1,9,70,9,30,9,... -> 38000,1,1,9,70,9,30,9,... -// Ref: -// https://irdb.globalcache.com/Home/Database -void IRsend::sendGC(uint16_t buf[], uint16_t len) { - uint16_t hz = buf[kGlobalCacheFreqIndex]; // GC frequency is in Hz. - enableIROut(hz); - uint32_t periodic_time = calcUSecPeriod(hz, false); - uint8_t emits = - std::min(buf[kGlobalCacheRptIndex], (uint16_t)kGlobalCacheMaxRepeat); - // Repeat - for (uint8_t repeat = 0; repeat < emits; repeat++) { - // First time through, start at the beginning (kGlobalCacheStartIndex), - // otherwise for repeats, we start a specified offset from that. - uint16_t offset = kGlobalCacheStartIndex; - if (repeat) offset += buf[kGlobalCacheRptStartIndex] - 1; - // Data - for (; offset < len; offset++) { - // Convert periodic units to microseconds. - // Minimum is kGlobalCacheMinUsec for actual GC units. - uint32_t microseconds = - std::max(buf[offset] * periodic_time, kGlobalCacheMinUsec); - // These codes start at an odd index (not even as with sendRaw). - if (offset & 1) // Odd bit. - mark(microseconds); - else // Even bit. - space(microseconds); - } - } - // It's possible that we've ended on a mark(), thus ensure the LED is off. - ledOff(); -} -#endif diff --git a/lib/IRremoteESP8266_ID1089/src/ir_Gree.cpp b/lib/IRremoteESP8266_ID1089/src/ir_Gree.cpp deleted file mode 100644 index 0740d5f..0000000 --- a/lib/IRremoteESP8266_ID1089/src/ir_Gree.cpp +++ /dev/null @@ -1,478 +0,0 @@ -// Copyright 2017 Ville Skyttä (scop) -// Copyright 2017, 2018 David Conran -// -// Code to emulate Gree protocol compatible HVAC devices. -// Should be compatible with: -// * Heat pumps carrying the "Ultimate" brand name. -// * EKOKAI air conditioners. -// - -#include "ir_Gree.h" -#include -#ifndef ARDUINO -#include -#endif -#include "IRrecv.h" -#include "IRremoteESP8266.h" -#include "IRsend.h" -#include "IRutils.h" -#include "ir_Kelvinator.h" - -// GGGG RRRRRR EEEEEEE EEEEEEE -// GG GG RR RR EE EE -// GG RRRRRR EEEEE EEEEE -// GG GG RR RR EE EE -// GGGGGG RR RR EEEEEEE EEEEEEE - -// Constants -// Ref: https://github.com/ToniA/arduino-heatpumpir/blob/master/GreeHeatpumpIR.h -const uint16_t kGreeHdrMark = 9000; -const uint16_t kGreeHdrSpace = 4000; -const uint16_t kGreeBitMark = 620; -const uint16_t kGreeOneSpace = 1600; -const uint16_t kGreeZeroSpace = 540; -const uint16_t kGreeMsgSpace = 19000; -const uint8_t kGreeBlockFooter = 0b010; -const uint8_t kGreeBlockFooterBits = 3; - -#if SEND_GREE -// Send a Gree Heat Pump message. -// -// Args: -// data: An array of bytes containing the IR command. -// nbytes: Nr. of bytes of data in the array. (>=kGreeStateLength) -// repeat: Nr. of times the message is to be repeated. (Default = 0). -// -// Status: ALPHA / Untested. -// -// Ref: -// https://github.com/ToniA/arduino-heatpumpir/blob/master/GreeHeatpumpIR.cpp -void IRsend::sendGree(unsigned char data[], uint16_t nbytes, uint16_t repeat) { - if (nbytes < kGreeStateLength) - return; // Not enough bytes to send a proper message. - - for (uint16_t r = 0; r <= repeat; r++) { - // Block #1 - sendGeneric(kGreeHdrMark, kGreeHdrSpace, kGreeBitMark, kGreeOneSpace, - kGreeBitMark, kGreeZeroSpace, 0, 0, // No Footer. - data, 4, 38, false, 0, 50); - // Footer #1 - sendGeneric(0, 0, // No Header - kGreeBitMark, kGreeOneSpace, kGreeBitMark, kGreeZeroSpace, - kGreeBitMark, kGreeMsgSpace, 0b010, 3, 38, true, 0, false); - - // Block #2 - sendGeneric(0, 0, // No Header for Block #2 - kGreeBitMark, kGreeOneSpace, kGreeBitMark, kGreeZeroSpace, - kGreeBitMark, kGreeMsgSpace, data + 4, nbytes - 4, 38, false, 0, - 50); - } -} - -// Send a Gree Heat Pump message. -// -// Args: -// data: The raw message to be sent. -// nbits: Nr. of bits of data in the message. (Default is kGreeBits) -// repeat: Nr. of times the message is to be repeated. (Default = 0). -// -// Status: ALPHA / Untested. -// -// Ref: -// https://github.com/ToniA/arduino-heatpumpir/blob/master/GreeHeatpumpIR.cpp -void IRsend::sendGree(uint64_t data, uint16_t nbits, uint16_t repeat) { - if (nbits != kGreeBits) - return; // Wrong nr. of bits to send a proper message. - // Set IR carrier frequency - enableIROut(38); - - for (uint16_t r = 0; r <= repeat; r++) { - // Header - mark(kGreeHdrMark); - space(kGreeHdrSpace); - - // Data - for (int16_t i = 8; i <= nbits; i += 8) { - sendData(kGreeBitMark, kGreeOneSpace, kGreeBitMark, kGreeZeroSpace, - (data >> (nbits - i)) & 0xFF, 8, false); - if (i == nbits / 2) { - // Send the mid-message Footer. - sendData(kGreeBitMark, kGreeOneSpace, kGreeBitMark, kGreeZeroSpace, - 0b010, 3); - mark(kGreeBitMark); - space(kGreeMsgSpace); - } - } - // Footer - mark(kGreeBitMark); - space(kGreeMsgSpace); - } -} -#endif // SEND_GREE - -IRGreeAC::IRGreeAC(uint16_t pin) : _irsend(pin) { stateReset(); } - -void IRGreeAC::stateReset() { - // This resets to a known-good state to Power Off, Fan Auto, Mode Auto, 25C. - for (uint8_t i = 0; i < kGreeStateLength; i++) remote_state[i] = 0x0; - remote_state[1] = 0x09; - remote_state[2] = 0x20; - remote_state[3] = 0x50; - remote_state[5] = 0x20; - remote_state[7] = 0x50; -} - -void IRGreeAC::fixup() { - checksum(); // Calculate the checksums -} - -void IRGreeAC::begin() { _irsend.begin(); } - -#if SEND_GREE -void IRGreeAC::send(const uint16_t repeat) { - fixup(); // Ensure correct settings before sending. - _irsend.sendGree(remote_state, kGreeStateLength, repeat); -} -#endif // SEND_GREE - -uint8_t* IRGreeAC::getRaw() { - fixup(); // Ensure correct settings before sending. - return remote_state; -} - -void IRGreeAC::setRaw(uint8_t new_code[]) { - for (uint8_t i = 0; i < kGreeStateLength; i++) { - remote_state[i] = new_code[i]; - } -} - -void IRGreeAC::checksum(const uint16_t length) { - // Gree uses the same checksum alg. as Kelvinator's block checksum. - uint8_t sum = IRKelvinatorAC::calcBlockChecksum(remote_state, length); - remote_state[length - 1] = (sum << 4) | (remote_state[length - 1] & 0xFU); -} - -// Verify the checksum is valid for a given state. -// Args: -// state: The array to verify the checksum of. -// length: The size of the state. -// Returns: -// A boolean. -bool IRGreeAC::validChecksum(const uint8_t state[], const uint16_t length) { - // Top 4 bits of the last byte in the state is the state's checksum. - if (state[length - 1] >> 4 == - IRKelvinatorAC::calcBlockChecksum(state, length)) - return true; - else - return false; -} - -void IRGreeAC::on() { - remote_state[0] |= kGreePower1Mask; - remote_state[2] |= kGreePower2Mask; -} - -void IRGreeAC::off() { - remote_state[0] &= ~kGreePower1Mask; - remote_state[2] &= ~kGreePower2Mask; -} - -void IRGreeAC::setPower(const bool state) { - if (state) - on(); - else - off(); -} - -bool IRGreeAC::getPower() { - return (remote_state[0] & kGreePower1Mask) && - (remote_state[2] & kGreePower2Mask); -} - -// Set the temp. in deg C -void IRGreeAC::setTemp(const uint8_t temp) { - uint8_t new_temp = std::max((uint8_t)kGreeMinTemp, temp); - new_temp = std::min((uint8_t)kGreeMaxTemp, new_temp); - if (getMode() == kGreeAuto) new_temp = 25; - remote_state[1] = (remote_state[1] & 0xF0U) | (new_temp - kGreeMinTemp); -} - -// Return the set temp. in deg C -uint8_t IRGreeAC::getTemp() { - return ((remote_state[1] & 0xFU) + kGreeMinTemp); -} - -// Set the speed of the fan, 0-3, 0 is auto, 1-3 is the speed -void IRGreeAC::setFan(const uint8_t speed) { - uint8_t fan = std::min((uint8_t)kGreeFanMax, speed); // Bounds check - - if (getMode() == kGreeDry) fan = 1; // DRY mode is always locked to fan 1. - // Set the basic fan values. - remote_state[0] &= ~kGreeFanMask; - remote_state[0] |= (fan << 4); -} - -uint8_t IRGreeAC::getFan() { return ((remote_state[0] & kGreeFanMask) >> 4); } - -void IRGreeAC::setMode(const uint8_t new_mode) { - uint8_t mode = new_mode; - switch (mode) { - case kGreeAuto: - // AUTO is locked to 25C - setTemp(25); - break; - case kGreeDry: - // DRY always sets the fan to 1. - setFan(1); - break; - case kGreeCool: - case kGreeFan: - case kGreeHeat: - break; - default: - // If we get an unexpected mode, default to AUTO. - mode = kGreeAuto; - } - remote_state[0] &= ~kGreeModeMask; - remote_state[0] |= mode; -} - -uint8_t IRGreeAC::getMode() { return (remote_state[0] & kGreeModeMask); } - -void IRGreeAC::setLight(const bool state) { - remote_state[2] &= ~kGreeLightMask; - remote_state[2] |= (state << 5); -} - -bool IRGreeAC::getLight() { return remote_state[2] & kGreeLightMask; } - -void IRGreeAC::setXFan(const bool state) { - remote_state[2] &= ~kGreeXfanMask; - remote_state[2] |= (state << 7); -} - -bool IRGreeAC::getXFan() { return remote_state[2] & kGreeXfanMask; } - -void IRGreeAC::setSleep(const bool state) { - remote_state[0] &= ~kGreeSleepMask; - remote_state[0] |= (state << 7); -} - -bool IRGreeAC::getSleep() { return remote_state[0] & kGreeSleepMask; } - -void IRGreeAC::setTurbo(const bool state) { - remote_state[2] &= ~kGreeTurboMask; - remote_state[2] |= (state << 4); -} - -bool IRGreeAC::getTurbo() { return remote_state[2] & kGreeTurboMask; } - -void IRGreeAC::setSwingVertical(const bool automatic, const uint8_t position) { - remote_state[0] &= ~kGreeSwingAutoMask; - remote_state[0] |= (automatic << 6); - uint8_t new_position = position; - if (!automatic) { - switch (position) { - case kGreeSwingUp: - case kGreeSwingMiddleUp: - case kGreeSwingMiddle: - case kGreeSwingMiddleDown: - case kGreeSwingDown: - break; - default: - new_position = kGreeSwingLastPos; - } - } else { - switch (position) { - case kGreeSwingAuto: - case kGreeSwingDownAuto: - case kGreeSwingMiddleAuto: - case kGreeSwingUpAuto: - break; - default: - new_position = kGreeSwingAuto; - } - } - remote_state[4] &= ~kGreeSwingPosMask; - remote_state[4] |= new_position; -} - -bool IRGreeAC::getSwingVerticalAuto() { - return remote_state[0] & kGreeSwingAutoMask; -} - -uint8_t IRGreeAC::getSwingVerticalPosition() { - return remote_state[4] & kGreeSwingPosMask; -} - -// Convert the internal state into a human readable string. -#ifdef ARDUINO -String IRGreeAC::toString() { - String result = ""; -#else -std::string IRGreeAC::toString() { - std::string result = ""; -#endif // ARDUINO - result += "Power: "; - if (getPower()) - result += "On"; - else - result += "Off"; - result += ", Mode: " + uint64ToString(getMode()); - switch (getMode()) { - case kGreeAuto: - result += " (AUTO)"; - break; - case kGreeCool: - result += " (COOL)"; - break; - case kGreeHeat: - result += " (HEAT)"; - break; - case kGreeDry: - result += " (DRY)"; - break; - case kGreeFan: - result += " (FAN)"; - break; - default: - result += " (UNKNOWN)"; - } - result += ", Temp: " + uint64ToString(getTemp()) + "C"; - result += ", Fan: " + uint64ToString(getFan()); - switch (getFan()) { - case 0: - result += " (AUTO)"; - break; - case kGreeFanMax: - result += " (MAX)"; - break; - } - result += ", Turbo: "; - if (getTurbo()) - result += "On"; - else - result += "Off"; - result += ", XFan: "; - if (getXFan()) - result += "On"; - else - result += "Off"; - result += ", Light: "; - if (getLight()) - result += "On"; - else - result += "Off"; - result += ", Sleep: "; - if (getSleep()) - result += "On"; - else - result += "Off"; - result += ", Swing Vertical Mode: "; - if (getSwingVerticalAuto()) - result += "Auto"; - else - result += "Manual"; - result += - ", Swing Vertical Pos: " + uint64ToString(getSwingVerticalPosition()); - switch (getSwingVerticalPosition()) { - case kGreeSwingLastPos: - result += " (Last Pos)"; - break; - case kGreeSwingAuto: - result += " (Auto)"; - break; - } - return result; -} - -#if DECODE_GREE -// Decode the supplied Gree message. -// -// Args: -// results: Ptr to the data to decode and where to store the decode result. -// nbits: The number of data bits to expect. Typically kGreeBits. -// strict: Flag indicating if we should perform strict matching. -// Returns: -// boolean: True if it can decode it, false if it can't. -// -// Status: ALPHA / Untested. -bool IRrecv::decodeGree(decode_results* results, uint16_t nbits, bool strict) { - if (results->rawlen < - 2 * (nbits + kGreeBlockFooterBits) + (kHeader + kFooter + 1)) - return false; // Can't possibly be a valid Gree message. - if (strict && nbits != kGreeBits) - return false; // Not strictly a Gree message. - - uint32_t data; - uint16_t offset = kStartOffset; - - // There are two blocks back-to-back in a full Gree IR message - // sequence. - int8_t state_pos = 0; - match_result_t data_result; - - // Header - if (!matchMark(results->rawbuf[offset++], kGreeHdrMark)) return false; - if (!matchSpace(results->rawbuf[offset++], kGreeHdrSpace)) return false; - // Data Block #1 (32 bits) - data_result = - matchData(&(results->rawbuf[offset]), 32, kGreeBitMark, kGreeOneSpace, - kGreeBitMark, kGreeZeroSpace, kTolerance, kMarkExcess, false); - if (data_result.success == false) return false; - data = data_result.data; - offset += data_result.used; - - // Record Data Block #1 in the state. - for (uint16_t i = 0; i < 4; i++, data >>= 8) - results->state[state_pos + i] = data & 0xFF; - state_pos += 4; - - // Block #1 footer (3 bits, B010) - data_result = matchData(&(results->rawbuf[offset]), kGreeBlockFooterBits, - kGreeBitMark, kGreeOneSpace, kGreeBitMark, - kGreeZeroSpace, kTolerance, kMarkExcess, false); - if (data_result.success == false) return false; - if (data_result.data != kGreeBlockFooter) return false; - offset += data_result.used; - - // Inter-block gap. - if (!matchMark(results->rawbuf[offset++], kGreeBitMark)) return false; - if (!matchSpace(results->rawbuf[offset++], kGreeMsgSpace)) return false; - - // Data Block #2 (32 bits) - data_result = - matchData(&(results->rawbuf[offset]), 32, kGreeBitMark, kGreeOneSpace, - kGreeBitMark, kGreeZeroSpace, kTolerance, kMarkExcess, false); - if (data_result.success == false) return false; - data = data_result.data; - offset += data_result.used; - - // Record Data Block #2 in the state. - for (uint16_t i = 0; i < 4; i++, data >>= 8) - results->state[state_pos + i] = data & 0xFF; - state_pos += 4; - - // Footer. - if (!matchMark(results->rawbuf[offset++], kGreeBitMark)) return false; - if (offset <= results->rawlen && - !matchAtLeast(results->rawbuf[offset], kGreeMsgSpace)) - return false; - - // Compliance - if (strict) { - // Correct size/length) - if (state_pos != kGreeStateLength) return false; - // Verify the message's checksum is correct. - if (!IRGreeAC::validChecksum(results->state)) return false; - } - - // Success - results->decode_type = GREE; - results->bits = state_pos * 8; - // No need to record the state as we stored it as we decoded it. - // As we use result->state, we don't record value, address, or command as it - // is a union data type. - return true; -} -#endif // DECODE_GREE diff --git a/lib/IRremoteESP8266_ID1089/src/ir_Gree.h b/lib/IRremoteESP8266_ID1089/src/ir_Gree.h deleted file mode 100644 index 489cefb..0000000 --- a/lib/IRremoteESP8266_ID1089/src/ir_Gree.h +++ /dev/null @@ -1,130 +0,0 @@ -// Kelvinator A/C -// -// Copyright 2016 David Conran - -#ifndef IR_GREE_H_ -#define IR_GREE_H_ - -#define __STDC_LIMIT_MACROS -#include -#ifndef UNIT_TEST -#include -#else -#include -#endif -#include "IRremoteESP8266.h" -#include "IRsend.h" - -// GGGG RRRRRR EEEEEEE EEEEEEE -// GG GG RR RR EE EE -// GG RRRRRR EEEEE EEEEE -// GG GG RR RR EE EE -// GGGGGG RR RR EEEEEEE EEEEEEE - -// Constants -const uint8_t kGreeAuto = 0; -const uint8_t kGreeCool = 1; -const uint8_t kGreeDry = 2; -const uint8_t kGreeFan = 3; -const uint8_t kGreeHeat = 4; - -// Byte 0 -const uint8_t kGreeModeMask = 0b00000111; -const uint8_t kGreePower1Mask = 0b00001000; -const uint8_t kGreeFanMask = 0b00110000; -const uint8_t kGreeSwingAutoMask = 0b01000000; -const uint8_t kGreeSleepMask = 0b10000000; -// Byte 2 -const uint8_t kGreeTurboMask = 0b00010000; -const uint8_t kGreeLightMask = 0b00100000; -const uint8_t kGreePower2Mask = 0b01000000; -const uint8_t kGreeXfanMask = 0b10000000; -// Byte 4 -const uint8_t kGreeSwingPosMask = 0b00001111; - -const uint8_t kGreeMinTemp = 16; // Celsius -const uint8_t kGreeMaxTemp = 30; // Celsius -const uint8_t kGreeFanMax = 3; - -const uint8_t kGreeSwingLastPos = 0b00000000; -const uint8_t kGreeSwingAuto = 0b00000001; -const uint8_t kGreeSwingUp = 0b00000010; -const uint8_t kGreeSwingMiddleUp = 0b00000011; -const uint8_t kGreeSwingMiddle = 0b00000100; -const uint8_t kGreeSwingMiddleDown = 0b00000101; -const uint8_t kGreeSwingDown = 0b00000110; -const uint8_t kGreeSwingDownAuto = 0b00000111; -const uint8_t kGreeSwingMiddleAuto = 0b00001001; -const uint8_t kGreeSwingUpAuto = 0b00001011; - -// Legacy defines. -#define GREE_AUTO kGreeAuto -#define GREE_COOL kGreeCool -#define GREE_DRY kGreeDry -#define GREE_FAN kGreeFan -#define GREE_HEAT kGreeHeat -#define GREE_MIN_TEMP kGreeMinTemp -#define GREE_MAX_TEMP kGreeMaxTemp -#define GREE_FAN_MAX kGreeFanMax -#define GREE_SWING_LAST_POS kGreeSwingLastPos -#define GREE_SWING_AUTO kGreeSwingAuto -#define GREE_SWING_UP kGreeSwingUp -#define GREE_SWING_MIDDLE_UP kGreeSwingMiddleUp -#define GREE_SWING_MIDDLE kGreeSwingMiddle -#define GREE_SWING_MIDDLE_DOWN kGreeSwingMiddleDown -#define GREE_SWING_DOWN kGreeSwingDown -#define GREE_SWING_DOWN_AUTO kGreeSwingDownAuto -#define GREE_SWING_MIDDLE_AUTO kGreeSwingMiddleAuto -#define GREE_SWING_UP_AUTO kGreeSwingUpAuto - -// Classes -class IRGreeAC { - public: - explicit IRGreeAC(uint16_t pin); - - void stateReset(); -#if SEND_GREE - void send(const uint16_t repeat = kGreeDefaultRepeat); -#endif // SEND_GREE - void begin(); - void on(); - void off(); - void setPower(const bool state); - bool getPower(); - void setTemp(const uint8_t temp); - uint8_t getTemp(); - void setFan(const uint8_t speed); - uint8_t getFan(); - void setMode(const uint8_t new_mode); - uint8_t getMode(); - void setLight(const bool state); - bool getLight(); - void setXFan(const bool state); - bool getXFan(); - void setSleep(const bool state); - bool getSleep(); - void setTurbo(const bool state); - bool getTurbo(); - void setSwingVertical(const bool automatic, const uint8_t position); - bool getSwingVerticalAuto(); - uint8_t getSwingVerticalPosition(); - - uint8_t* getRaw(); - void setRaw(uint8_t new_code[]); - static bool validChecksum(const uint8_t state[], - const uint16_t length = kGreeStateLength); -#ifdef ARDUINO - String toString(); -#else - std::string toString(); -#endif - - private: - // The state of the IR remote in IR code form. - uint8_t remote_state[kGreeStateLength]; - void checksum(const uint16_t length = kGreeStateLength); - void fixup(); - IRsend _irsend; -}; - -#endif // IR_GREE_H_ diff --git a/lib/IRremoteESP8266_ID1089/src/ir_Haier.cpp b/lib/IRremoteESP8266_ID1089/src/ir_Haier.cpp deleted file mode 100644 index 719e8c5..0000000 --- a/lib/IRremoteESP8266_ID1089/src/ir_Haier.cpp +++ /dev/null @@ -1,865 +0,0 @@ -// Copyright 2018 crankyoldgit -// The specifics of reverse engineering the protocols details: -// * HSU07-HEA03 by kuzin2006. -// * YR-W02/HSU-09HMC203 by non7top. - -#include "ir_Haier.h" -#ifndef UNIT_TEST -#include -#else -#include -#endif -#include "IRremoteESP8266.h" -#include "IRutils.h" - -// HH HH AAA IIIII EEEEEEE RRRRRR -// HH HH AAAAA III EE RR RR -// HHHHHHH AA AA III EEEEE RRRRRR -// HH HH AAAAAAA III EE RR RR -// HH HH AA AA IIIII EEEEEEE RR RR - -// Supported devices: -// * Haier HSU07-HEA03 Remote control. -// * Haier YR-W02 Remote control -// * Haier HSU-09HMC203 A/C unit. - -// Ref: -// https://github.com/markszabo/IRremoteESP8266/issues/404 -// https://www.dropbox.com/s/mecyib3lhdxc8c6/IR%20data%20reverse%20engineering.xlsx?dl=0 -// https://github.com/markszabo/IRremoteESP8266/issues/485 -// https://www.dropbox.com/sh/w0bt7egp0fjger5/AADRFV6Wg4wZskJVdFvzb8Z0a?dl=0&preview=haer2.ods - -// Constants -const uint16_t kHaierAcHdr = 3000; -const uint16_t kHaierAcHdrGap = 4300; -const uint16_t kHaierAcBitMark = 520; -const uint16_t kHaierAcOneSpace = 1650; -const uint16_t kHaierAcZeroSpace = 650; -const uint32_t kHaierAcMinGap = 150000; // Completely made up value. - -#if (SEND_HAIER_AC || SEND_HAIER_AC_YRW02) -// Send a Haier A/C message. (HSU07-HEA03 remote) -// -// Args: -// data: An array of bytes containing the IR command. -// nbytes: Nr. of bytes of data in the array. (>=kHaierACStateLength) -// repeat: Nr. of times the message is to be repeated. (Default = 0). -// -// Status: Beta / Probably working. -// -void IRsend::sendHaierAC(unsigned char data[], uint16_t nbytes, - uint16_t repeat) { - if (nbytes < kHaierACStateLength) return; - - for (uint16_t r = 0; r <= repeat; r++) { - enableIROut(38000); - mark(kHaierAcHdr); - space(kHaierAcHdr); - sendGeneric(kHaierAcHdr, kHaierAcHdrGap, kHaierAcBitMark, kHaierAcOneSpace, - kHaierAcBitMark, kHaierAcZeroSpace, kHaierAcBitMark, - kHaierAcMinGap, data, nbytes, 38, true, - 0, // Repeats handled elsewhere - 50); - } -} -#endif // (SEND_HAIER_AC || SEND_HAIER_AC_YRW02) - -#if SEND_HAIER_AC_YRW02 -// Send a Haier YR-W02 remote A/C message. -// -// Args: -// data: An array of bytes containing the IR command. -// nbytes: Nr. of bytes of data in the array. (>=kHaierACYRW02StateLength) -// repeat: Nr. of times the message is to be repeated. (Default = 0). -// -// Status: Alpha / Untested on a real device. -// -void IRsend::sendHaierACYRW02(unsigned char data[], uint16_t nbytes, - uint16_t repeat) { - if (nbytes >= kHaierACYRW02StateLength) sendHaierAC(data, nbytes, repeat); -} -#endif // SEND_HAIER_AC_YRW02 - -// Class for emulating a Haier HSU07-HEA03 remote -IRHaierAC::IRHaierAC(uint16_t pin) : _irsend(pin) { stateReset(); } - -void IRHaierAC::begin() { _irsend.begin(); } - -#if SEND_HAIER_AC -void IRHaierAC::send(const uint16_t repeat) { - checksum(); - _irsend.sendHaierAC(remote_state, kHaierACStateLength, repeat); -} -#endif // SEND_HAIER_AC - -void IRHaierAC::checksum() { - remote_state[8] = sumBytes(remote_state, kHaierACStateLength - 1); -} - -bool IRHaierAC::validChecksum(uint8_t state[], const uint16_t length) { - if (length < 2) return false; // 1 byte of data can't have a checksum. - return (state[length - 1] == sumBytes(state, length - 1)); -} - -void IRHaierAC::stateReset() { - for (uint8_t i = 1; i < kHaierACStateLength; i++) remote_state[i] = 0x0; - remote_state[0] = kHaierAcPrefix; - remote_state[2] = 0b00100000; - - setTemp(kHaierAcDefTemp); - setFan(kHaierAcFanAuto); - setMode(kHaierAcAuto); - setCommand(kHaierAcCmdOn); -} - -uint8_t* IRHaierAC::getRaw() { - checksum(); - return remote_state; -} - -void IRHaierAC::setRaw(uint8_t new_code[]) { - for (uint8_t i = 0; i < kHaierACStateLength; i++) { - remote_state[i] = new_code[i]; - } -} - -void IRHaierAC::setCommand(uint8_t state) { - remote_state[1] &= 0b11110000; - switch (state) { - case kHaierAcCmdOff: - case kHaierAcCmdOn: - case kHaierAcCmdMode: - case kHaierAcCmdFan: - case kHaierAcCmdTempUp: - case kHaierAcCmdTempDown: - case kHaierAcCmdSleep: - case kHaierAcCmdTimerSet: - case kHaierAcCmdTimerCancel: - case kHaierAcCmdHealth: - case kHaierAcCmdSwing: - remote_state[1] |= (state & 0b00001111); - } -} - -uint8_t IRHaierAC::getCommand() { return remote_state[1] & (0b00001111); } - -void IRHaierAC::setFan(uint8_t speed) { - uint8_t new_speed = kHaierAcFanAuto; - switch (speed) { - case kHaierAcFanLow: - new_speed = 3; - break; - case kHaierAcFanMed: - new_speed = 1; - break; - case kHaierAcFanHigh: - new_speed = 2; - break; - default: - new_speed = kHaierAcFanAuto; // Default to auto for anything else. - } - - if (speed != getFan()) setCommand(kHaierAcCmdFan); - remote_state[5] &= 0b11111100; - remote_state[5] |= new_speed; -} - -uint8_t IRHaierAC::getFan() { - switch (remote_state[5] & 0b00000011) { - case 1: - return kHaierAcFanMed; - case 2: - return kHaierAcFanHigh; - case 3: - return kHaierAcFanLow; - default: - return kHaierAcFanAuto; - } -} - -void IRHaierAC::setMode(uint8_t mode) { - uint8_t new_mode = mode; - setCommand(kHaierAcCmdMode); - if (mode > kHaierAcFan) // If out of range, default to auto mode. - new_mode = kHaierAcAuto; - remote_state[7] &= 0b00011111; - remote_state[7] |= (new_mode << 5); -} - -uint8_t IRHaierAC::getMode() { return (remote_state[7] & 0b11100000) >> 5; } - -void IRHaierAC::setTemp(const uint8_t celsius) { - uint8_t temp = celsius; - if (temp < kHaierAcMinTemp) - temp = kHaierAcMinTemp; - else if (temp > kHaierAcMaxTemp) - temp = kHaierAcMaxTemp; - - uint8_t old_temp = getTemp(); - if (old_temp == temp) return; - if (old_temp > temp) - setCommand(kHaierAcCmdTempDown); - else - setCommand(kHaierAcCmdTempUp); - - remote_state[1] &= 0b00001111; // Clear the previous temp. - remote_state[1] |= ((temp - kHaierAcMinTemp) << 4); -} - -uint8_t IRHaierAC::getTemp() { - return ((remote_state[1] & 0b11110000) >> 4) + kHaierAcMinTemp; -} - -void IRHaierAC::setHealth(bool state) { - setCommand(kHaierAcCmdHealth); - remote_state[4] &= 0b11011111; - remote_state[4] |= (state << 5); -} - -bool IRHaierAC::getHealth(void) { return remote_state[4] & (1 << 5); } - -void IRHaierAC::setSleep(bool state) { - setCommand(kHaierAcCmdSleep); - remote_state[7] &= 0b10111111; - remote_state[7] |= (state << 6); -} - -bool IRHaierAC::getSleep(void) { return remote_state[7] & 0b01000000; } - -uint16_t IRHaierAC::getTime(const uint8_t ptr[]) { - return (ptr[0] & 0b00011111) * 60 + (ptr[1] & 0b00111111); -} - -int16_t IRHaierAC::getOnTimer() { - if (remote_state[3] & 0b10000000) // Check if the timer is turned on. - return getTime(remote_state + 6); - else - return -1; -} - -int16_t IRHaierAC::getOffTimer() { - if (remote_state[3] & 0b01000000) // Check if the timer is turned on. - return getTime(remote_state + 4); - else - return -1; -} - -uint16_t IRHaierAC::getCurrTime() { return getTime(remote_state + 2); } - -void IRHaierAC::setTime(uint8_t ptr[], const uint16_t nr_mins) { - uint16_t mins = nr_mins; - if (nr_mins > kHaierAcMaxTime) mins = kHaierAcMaxTime; - - // Hours - ptr[0] &= 0b11100000; - ptr[0] |= (mins / 60); - // Minutes - ptr[1] &= 0b11000000; - ptr[1] |= (mins % 60); -} - -void IRHaierAC::setOnTimer(const uint16_t nr_mins) { - setCommand(kHaierAcCmdTimerSet); - remote_state[3] |= 0b10000000; - setTime(remote_state + 6, nr_mins); -} - -void IRHaierAC::setOffTimer(const uint16_t nr_mins) { - setCommand(kHaierAcCmdTimerSet); - remote_state[3] |= 0b01000000; - setTime(remote_state + 4, nr_mins); -} - -void IRHaierAC::cancelTimers() { - setCommand(kHaierAcCmdTimerCancel); - remote_state[3] &= 0b00111111; -} - -void IRHaierAC::setCurrTime(const uint16_t nr_mins) { - setTime(remote_state + 2, nr_mins); -} - -uint8_t IRHaierAC::getSwing() { return (remote_state[2] & 0b11000000) >> 6; } - -void IRHaierAC::setSwing(const uint8_t state) { - if (state == getSwing()) return; // Nothing to do. - setCommand(kHaierAcCmdSwing); - switch (state) { - case kHaierAcSwingOff: - case kHaierAcSwingUp: - case kHaierAcSwingDown: - case kHaierAcSwingChg: - remote_state[2] &= 0b00111111; - remote_state[2] |= (state << 6); - break; - } -} - -// Convert a Haier time into a human readable string. -#ifdef ARDUINO -String IRHaierAC::timeToString(const uint16_t nr_mins) { - String result = ""; -#else -std::string IRHaierAC::timeToString(const uint16_t nr_mins) { - std::string result = ""; -#endif // ARDUINO - - if (nr_mins / 24 < 10) result += "0"; // Zero pad. - result += uint64ToString(nr_mins / 60); - result += ":"; - if (nr_mins % 60 < 10) result += "0"; // Zero pad. - result += uint64ToString(nr_mins % 60); - return result; -} - -// Convert the internal state into a human readable string. -#ifdef ARDUINO -String IRHaierAC::toString() { - String result = ""; -#else -std::string IRHaierAC::toString() { - std::string result = ""; -#endif // ARDUINO - uint8_t cmd = getCommand(); - result += "Command: " + uint64ToString(cmd) + " ("; - switch (cmd) { - case kHaierAcCmdOff: - result += "Off"; - break; - case kHaierAcCmdOn: - result += "On"; - break; - case kHaierAcCmdMode: - result += "Mode"; - break; - case kHaierAcCmdFan: - result += "Fan"; - break; - case kHaierAcCmdTempUp: - result += "Temp Up"; - break; - case kHaierAcCmdTempDown: - result += "Temp Down"; - break; - case kHaierAcCmdSleep: - result += "Sleep"; - break; - case kHaierAcCmdTimerSet: - result += "Timer Set"; - break; - case kHaierAcCmdTimerCancel: - result += "Timer Cancel"; - break; - case kHaierAcCmdHealth: - result += "Health"; - break; - case kHaierAcCmdSwing: - result += "Swing"; - break; - default: - result += "Unknown"; - } - result += ")"; - result += ", Mode: " + uint64ToString(getMode()); - switch (getMode()) { - case kHaierAcAuto: - result += " (AUTO)"; - break; - case kHaierAcCool: - result += " (COOL)"; - break; - case kHaierAcHeat: - result += " (HEAT)"; - break; - case kHaierAcDry: - result += " (DRY)"; - break; - case kHaierAcFan: - result += " (FAN)"; - break; - default: - result += " (UNKNOWN)"; - } - result += ", Temp: " + uint64ToString(getTemp()) + "C"; - result += ", Fan: " + uint64ToString(getFan()); - switch (getFan()) { - case kHaierAcFanAuto: - result += " (AUTO)"; - break; - case kHaierAcFanHigh: - result += " (MAX)"; - break; - } - result += ", Swing: " + uint64ToString(getSwing()) + " ("; - switch (getSwing()) { - case kHaierAcSwingOff: - result += "Off"; - break; - case kHaierAcSwingUp: - result += "Up"; - break; - case kHaierAcSwingDown: - result += "Down"; - break; - case kHaierAcSwingChg: - result += "Chg"; - break; - default: - result += "Unknown"; - } - result += ")"; - result += ", Sleep: "; - if (getSleep()) - result += "On"; - else - result += "Off"; - result += ", Health: "; - if (getHealth()) - result += "On"; - else - result += "Off"; - result += ", Current Time: " + timeToString(getCurrTime()); - result += ", On Timer: "; - if (getOnTimer() >= 0) - result += timeToString(getOnTimer()); - else - result += "Off"; - result += ", Off Timer: "; - if (getOffTimer() >= 0) - result += timeToString(getOffTimer()); - else - result += "Off"; - - return result; -} -// End of IRHaierAC class. - -// Class for emulating a Haier YRW02 remote -IRHaierACYRW02::IRHaierACYRW02(uint16_t pin) : _irsend(pin) { stateReset(); } - -void IRHaierACYRW02::begin() { _irsend.begin(); } - -#if SEND_HAIER_AC_YRW02 -void IRHaierACYRW02::send(const uint16_t repeat) { - checksum(); - _irsend.sendHaierACYRW02(remote_state, kHaierACYRW02StateLength, repeat); -} -#endif // SEND_HAIER_AC_YRW02 - -void IRHaierACYRW02::checksum() { - remote_state[kHaierACYRW02StateLength - 1] = - sumBytes(remote_state, kHaierACYRW02StateLength - 1); -} - -bool IRHaierACYRW02::validChecksum(uint8_t state[], const uint16_t length) { - if (length < 2) return false; // 1 byte of data can't have a checksum. - return (state[length - 1] == sumBytes(state, length - 1)); -} - -void IRHaierACYRW02::stateReset() { - for (uint8_t i = 1; i < kHaierACYRW02StateLength; i++) remote_state[i] = 0x0; - remote_state[0] = kHaierAcYrw02Prefix; - - setTemp(kHaierAcDefTemp); - setHealth(true); - setTurbo(kHaierAcYrw02TurboOff); - setSleep(false); - setFan(kHaierAcYrw02FanAuto); - setSwing(kHaierAcYrw02SwingOff); - setMode(kHaierAcYrw02Auto); - setPower(true); -} - -uint8_t* IRHaierACYRW02::getRaw() { - checksum(); - return remote_state; -} - -void IRHaierACYRW02::setRaw(uint8_t new_code[]) { - for (uint8_t i = 0; i < kHaierACYRW02StateLength; i++) { - remote_state[i] = new_code[i]; - } -} - -void IRHaierACYRW02::setButton(uint8_t button) { - switch (button) { - case kHaierAcYrw02ButtonTempUp: - case kHaierAcYrw02ButtonTempDown: - case kHaierAcYrw02ButtonSwing: - case kHaierAcYrw02ButtonFan: - case kHaierAcYrw02ButtonPower: - case kHaierAcYrw02ButtonMode: - case kHaierAcYrw02ButtonHealth: - case kHaierAcYrw02ButtonTurbo: - case kHaierAcYrw02ButtonSleep: - remote_state[12] &= 0b11110000; - remote_state[12] |= (button & 0b00001111); - } -} - -uint8_t IRHaierACYRW02::getButton() { return remote_state[12] & (0b00001111); } - -void IRHaierACYRW02::setMode(uint8_t mode) { - uint8_t new_mode = mode; - setButton(kHaierAcYrw02ButtonMode); - switch (mode) { - case kHaierAcYrw02Auto: - case kHaierAcYrw02Cool: - case kHaierAcYrw02Dry: - case kHaierAcYrw02Heat: - case kHaierAcYrw02Fan: - break; - default: // If unexpected, default to auto mode. - new_mode = kHaierAcYrw02Auto; - } - remote_state[7] &= 0b0001111; - remote_state[7] |= (new_mode << 4); -} - -uint8_t IRHaierACYRW02::getMode() { return remote_state[7] >> 4; } - -void IRHaierACYRW02::setTemp(const uint8_t celcius) { - uint8_t temp = celcius; - if (temp < kHaierAcMinTemp) - temp = kHaierAcMinTemp; - else if (temp > kHaierAcMaxTemp) - temp = kHaierAcMaxTemp; - - uint8_t old_temp = getTemp(); - if (old_temp == temp) return; - if (old_temp > temp) - setButton(kHaierAcYrw02ButtonTempDown); - else - setButton(kHaierAcYrw02ButtonTempUp); - - remote_state[1] &= 0b00001111; // Clear the previous temp. - remote_state[1] |= ((temp - kHaierAcMinTemp) << 4); -} - -uint8_t IRHaierACYRW02::getTemp() { - return ((remote_state[1] & 0b11110000) >> 4) + kHaierAcMinTemp; -} - -void IRHaierACYRW02::setHealth(bool state) { - setButton(kHaierAcYrw02ButtonHealth); - remote_state[3] &= 0b11111101; - remote_state[3] |= (state << 1); -} - -bool IRHaierACYRW02::getHealth(void) { return remote_state[3] & 0b00000010; } - -bool IRHaierACYRW02::getPower() { return remote_state[4] & kHaierAcYrw02Power; } - -void IRHaierACYRW02::setPower(bool state) { - setButton(kHaierAcYrw02ButtonPower); - if (state) - remote_state[4] |= kHaierAcYrw02Power; - else - remote_state[4] &= ~kHaierAcYrw02Power; -} - -void IRHaierACYRW02::on() { setPower(true); } - -void IRHaierACYRW02::off() { setPower(false); } - -bool IRHaierACYRW02::getSleep() { return remote_state[8] & kHaierAcYrw02Sleep; } - -void IRHaierACYRW02::setSleep(bool state) { - setButton(kHaierAcYrw02ButtonSleep); - if (state) - remote_state[8] |= kHaierAcYrw02Sleep; - else - remote_state[8] &= ~kHaierAcYrw02Sleep; -} - -uint8_t IRHaierACYRW02::getTurbo() { return remote_state[6] >> 6; } - -void IRHaierACYRW02::setTurbo(uint8_t speed) { - switch (speed) { - case kHaierAcYrw02TurboOff: - case kHaierAcYrw02TurboLow: - case kHaierAcYrw02TurboHigh: - remote_state[6] &= 0b00111111; - remote_state[6] |= (speed << 6); - setButton(kHaierAcYrw02ButtonTurbo); - } -} - -uint8_t IRHaierACYRW02::getFan() { return remote_state[5] >> 4; } - -void IRHaierACYRW02::setFan(uint8_t speed) { - switch (speed) { - case kHaierAcYrw02FanLow: - case kHaierAcYrw02FanMed: - case kHaierAcYrw02FanHigh: - case kHaierAcYrw02FanAuto: - remote_state[5] &= 0b00001111; - remote_state[5] |= (speed << 4); - setButton(kHaierAcYrw02ButtonFan); - } -} - -uint8_t IRHaierACYRW02::getSwing() { return remote_state[1] & 0b00001111; } - -void IRHaierACYRW02::setSwing(uint8_t state) { - uint8_t newstate = state; - switch (state) { - case kHaierAcYrw02SwingOff: - case kHaierAcYrw02SwingAuto: - case kHaierAcYrw02SwingTop: - case kHaierAcYrw02SwingMiddle: - case kHaierAcYrw02SwingBottom: - case kHaierAcYrw02SwingDown: - setButton(kHaierAcYrw02ButtonSwing); - break; - default: - return; // Unexpected value so don't do anything. - } - - // Heat mode has no MIDDLE setting, use BOTTOM instead. - if (state == kHaierAcYrw02SwingMiddle && getMode() == kHaierAcYrw02Heat) - newstate = kHaierAcYrw02SwingBottom; - - // BOTTOM is only allowed if we are in Heat mode, otherwise MIDDLE. - if (state == kHaierAcYrw02SwingBottom && getMode() != kHaierAcYrw02Heat) - newstate = kHaierAcYrw02SwingMiddle; - - remote_state[1] &= 0b11110000; - remote_state[1] |= newstate; -} - -// Convert the internal state into a human readable string. -#ifdef ARDUINO -String IRHaierACYRW02::toString() { - String result = ""; -#else -std::string IRHaierACYRW02::toString() { - std::string result = ""; -#endif // ARDUINO - result += "Power: "; - if (getPower()) - result += "On"; - else - result += "Off"; - uint8_t cmd = getButton(); - result += ", Button: " + uint64ToString(cmd) + " ("; - switch (cmd) { - case kHaierAcYrw02ButtonPower: - result += "Power"; - break; - case kHaierAcYrw02ButtonMode: - result += "Mode"; - break; - case kHaierAcYrw02ButtonFan: - result += "Fan"; - break; - case kHaierAcYrw02ButtonTempUp: - result += "Temp Up"; - break; - case kHaierAcYrw02ButtonTempDown: - result += "Temp Down"; - break; - case kHaierAcYrw02ButtonSleep: - result += "Sleep"; - break; - case kHaierAcYrw02ButtonHealth: - result += "Health"; - break; - case kHaierAcYrw02ButtonSwing: - result += "Swing"; - break; - case kHaierAcYrw02ButtonTurbo: - result += "Turbo"; - break; - default: - result += "Unknown"; - } - result += ")"; - result += ", Mode: " + uint64ToString(getMode()); - switch (getMode()) { - case kHaierAcYrw02Auto: - result += " (Auto)"; - break; - case kHaierAcYrw02Cool: - result += " (Cool)"; - break; - case kHaierAcYrw02Heat: - result += " (Heat)"; - break; - case kHaierAcYrw02Dry: - result += " (Dry)"; - break; - case kHaierAcYrw02Fan: - result += " (Fan)"; - break; - default: - result += " (UNKNOWN)"; - } - result += ", Temp: " + uint64ToString(getTemp()) + "C"; - result += ", Fan: " + uint64ToString(getFan()); - switch (getFan()) { - case kHaierAcYrw02FanAuto: - result += " (Auto)"; - break; - case kHaierAcYrw02FanHigh: - result += " (High)"; - break; - case kHaierAcYrw02FanLow: - result += " (Low)"; - break; - case kHaierAcYrw02FanMed: - result += " (Med)"; - break; - default: - result += " (Unknown)"; - } - result += ", Turbo: " + uint64ToString(getTurbo()) + " ("; - switch (getTurbo()) { - case kHaierAcYrw02TurboOff: - result += "Off"; - break; - case kHaierAcYrw02TurboLow: - result += "Low"; - break; - case kHaierAcYrw02TurboHigh: - result += "High"; - break; - default: - result += "Unknown"; - } - result += ")"; - result += ", Swing: " + uint64ToString(getSwing()) + " ("; - switch (getSwing()) { - case kHaierAcYrw02SwingOff: - result += "Off"; - break; - case kHaierAcYrw02SwingAuto: - result += "Auto"; - break; - case kHaierAcYrw02SwingBottom: - result += "Bottom"; - break; - case kHaierAcYrw02SwingDown: - result += "Down"; - break; - case kHaierAcYrw02SwingTop: - result += "Top"; - break; - case kHaierAcYrw02SwingMiddle: - result += "Middle"; - break; - default: - result += "Unknown"; - } - result += ")"; - result += ", Sleep: "; - if (getSleep()) - result += "On"; - else - result += "Off"; - result += ", Health: "; - if (getHealth()) - result += "On"; - else - result += "Off"; - - return result; -} -// End of IRHaierACYRW02 class. - -#if (DECODE_HAIER_AC || DECODE_HAIER_AC_YRW02) -// Decode the supplied Haier HSU07-HEA03 remote message. -// -// Args: -// results: Ptr to the data to decode and where to store the decode result. -// nbits: The number of data bits to expect. Typically kHaierACBits. -// strict: Flag indicating if we should perform strict matching. -// Returns: -// boolean: True if it can decode it, false if it can't. -// -// Status: BETA / Appears to be working. -// -bool IRrecv::decodeHaierAC(decode_results* results, uint16_t nbits, - bool strict) { - if (nbits % 8 != 0) // nbits has to be a multiple of nr. of bits in a byte. - return false; - - if (strict) { - if (nbits != kHaierACBits) - return false; // Not strictly a HAIER_AC message. - } - - if (results->rawlen < (2 * nbits + kHeader) + kFooter - 1) - return false; // Can't possibly be a valid HAIER_AC message. - - uint16_t offset = kStartOffset; - - // Header - if (!matchMark(results->rawbuf[offset++], kHaierAcHdr)) return false; - if (!matchSpace(results->rawbuf[offset++], kHaierAcHdr)) return false; - if (!matchMark(results->rawbuf[offset++], kHaierAcHdr)) return false; - if (!matchSpace(results->rawbuf[offset++], kHaierAcHdrGap)) return false; - - // Data - for (uint16_t i = 0; i < nbits / 8; i++) { - match_result_t data_result = - matchData(&(results->rawbuf[offset]), 8, kHaierAcBitMark, - kHaierAcOneSpace, kHaierAcBitMark, kHaierAcZeroSpace); - if (data_result.success == false) return false; - offset += data_result.used; - results->state[i] = (uint8_t)data_result.data; - } - - // Footer - if (!matchMark(results->rawbuf[offset++], kHaierAcBitMark)) return false; - if (offset < results->rawlen && - !matchAtLeast(results->rawbuf[offset++], kHaierAcMinGap)) - return false; - - // Compliance - if (strict) { - if (results->state[0] != kHaierAcPrefix) return false; - if (!IRHaierAC::validChecksum(results->state, nbits / 8)) return false; - } - - // Success - results->decode_type = HAIER_AC; - results->bits = nbits; - return true; -} -#endif // (DECODE_HAIER_AC || DECODE_HAIER_AC_YRW02) - -#if DECODE_HAIER_AC_YRW02 -// Decode the supplied Haier YR-W02 remote A/C message. -// -// Args: -// results: Ptr to the data to decode and where to store the decode result. -// nbits: The number of data bits to expect. Typically kHaierACYRW02Bits. -// strict: Flag indicating if we should perform strict matching. -// Returns: -// boolean: True if it can decode it, false if it can't. -// -// Status: BETA / Appears to be working. -// -bool IRrecv::decodeHaierACYRW02(decode_results* results, uint16_t nbits, - bool strict) { - if (strict) { - if (nbits != kHaierACYRW02Bits) - return false; // Not strictly a HAIER_AC_YRW02 message. - } - - // The protocol is almost exactly the same as HAIER_AC - if (!decodeHaierAC(results, nbits, false)) return false; - - // Compliance - if (strict) { - if (results->state[0] != kHaierAcYrw02Prefix) return false; - if (!IRHaierACYRW02::validChecksum(results->state, nbits / 8)) return false; - } - - // Success - // It looks correct, but we haven't check the checksum etc. - results->decode_type = HAIER_AC_YRW02; - return true; -} -#endif // DECODE_HAIER_AC_YRW02 diff --git a/lib/IRremoteESP8266_ID1089/src/ir_Haier.h b/lib/IRremoteESP8266_ID1089/src/ir_Haier.h deleted file mode 100644 index f4a0d32..0000000 --- a/lib/IRremoteESP8266_ID1089/src/ir_Haier.h +++ /dev/null @@ -1,297 +0,0 @@ -// Copyright 2018 crankyoldgit -// The specifics of reverse engineering the protocol details by kuzin2006 - -#ifndef IR_HAIER_H_ -#define IR_HAIER_H_ - -#ifndef UNIT_TEST -#include -#else -#include -#endif -#include "IRremoteESP8266.h" -#include "IRsend.h" - -// HH HH AAA IIIII EEEEEEE RRRRRR -// HH HH AAAAA III EE RR RR -// HHHHHHH AA AA III EEEEE RRRRRR -// HH HH AAAAAAA III EE RR RR -// HH HH AA AA IIIII EEEEEEE RR RR - -// Ref: -// https://github.com/markszabo/IRremoteESP8266/issues/404 -// https://www.dropbox.com/s/mecyib3lhdxc8c6/IR%20data%20reverse%20engineering.xlsx?dl=0 -// https://github.com/markszabo/IRremoteESP8266/issues/485 -// https://www.dropbox.com/sh/w0bt7egp0fjger5/AADRFV6Wg4wZskJVdFvzb8Z0a?dl=0&preview=haer2.ods - -// Constants - -// Haier HSU07-HEA03 remote -// Byte 0 -const uint8_t kHaierAcPrefix = 0b10100101; - -// Byte 1 -const uint8_t kHaierAcMinTemp = 16; -const uint8_t kHaierAcDefTemp = 25; -const uint8_t kHaierAcMaxTemp = 30; -const uint8_t kHaierAcCmdOff = 0b00000000; -const uint8_t kHaierAcCmdOn = 0b00000001; -const uint8_t kHaierAcCmdMode = 0b00000010; -const uint8_t kHaierAcCmdFan = 0b00000011; -const uint8_t kHaierAcCmdTempUp = 0b00000110; -const uint8_t kHaierAcCmdTempDown = 0b00000111; -const uint8_t kHaierAcCmdSleep = 0b00001000; -const uint8_t kHaierAcCmdTimerSet = 0b00001001; -const uint8_t kHaierAcCmdTimerCancel = 0b00001010; -const uint8_t kHaierAcCmdHealth = 0b00001100; -const uint8_t kHaierAcCmdSwing = 0b00001101; - -// Byte 2 -const uint8_t kHaierAcSwingOff = 0b00000000; -const uint8_t kHaierAcSwingUp = 0b00000001; -const uint8_t kHaierAcSwingDown = 0b00000010; -const uint8_t kHaierAcSwingChg = 0b00000011; - -// Byte 6 -const uint8_t kHaierAcAuto = 0; -const uint8_t kHaierAcCool = 1; -const uint8_t kHaierAcDry = 2; -const uint8_t kHaierAcHeat = 3; -const uint8_t kHaierAcFan = 4; - -const uint8_t kHaierAcFanAuto = 0; -const uint8_t kHaierAcFanLow = 1; -const uint8_t kHaierAcFanMed = 2; -const uint8_t kHaierAcFanHigh = 3; - -const uint16_t kHaierAcMaxTime = (23 * 60) + 59; - -// Legacy Haier AC defines. -#define HAIER_AC_MIN_TEMP kHaierAcMinTemp -#define HAIER_AC_DEF_TEMP kHaierAcDefTemp -#define HAIER_AC_MAX_TEMP kHaierAcMaxTemp -#define HAIER_AC_CMD_OFF kHaierAcCmdOff -#define HAIER_AC_CMD_ON kHaierAcCmdOn -#define HAIER_AC_CMD_MODE kHaierAcCmdMode -#define HAIER_AC_CMD_FAN kHaierAcCmdFan -#define HAIER_AC_CMD_TEMP_UP kHaierAcCmdTempUp -#define HAIER_AC_CMD_TEMP_DOWN kHaierAcCmdTempDown -#define HAIER_AC_CMD_SLEEP kHaierAcCmdSleep -#define HAIER_AC_CMD_TIMER_SET kHaierAcCmdTimerSet -#define HAIER_AC_CMD_TIMER_CANCEL kHaierAcCmdTimerCancel -#define HAIER_AC_CMD_HEALTH kHaierAcCmdHealth -#define HAIER_AC_CMD_SWING kHaierAcCmdSwing -#define HAIER_AC_SWING_OFF kHaierAcSwingOff -#define HAIER_AC_SWING_UP kHaierAcSwingUp -#define HAIER_AC_SWING_DOWN kHaierAcSwingDown -#define HAIER_AC_SWING_CHG kHaierAcSwingChg -#define HAIER_AC_AUTO kHaierAcAuto -#define HAIER_AC_COOL kHaierAcCool -#define HAIER_AC_DRY kHaierAcDry -#define HAIER_AC_HEAT kHaierAcHeat -#define HAIER_AC_FAN kHaierAcFan -#define HAIER_AC_FAN_AUTO kHaierAcFanAuto -#define HAIER_AC_FAN_LOW kHaierAcFanLow -#define HAIER_AC_FAN_MED kHaierAcFanMed -#define HAIER_AC_FAN_HIGH kHaierAcFanHigh - -// Haier YRW02 remote -// Byte 0 -const uint8_t kHaierAcYrw02Prefix = 0xA6; - -// Byte 1 -// Bits 0-3 -// 0x0 = 16DegC, ... 0xE = 30DegC -// Bits 4-7 - Swing -const uint8_t kHaierAcYrw02SwingOff = 0x0; -const uint8_t kHaierAcYrw02SwingTop = 0x1; -const uint8_t kHaierAcYrw02SwingMiddle = 0x2; // Not available in heat mode. -const uint8_t kHaierAcYrw02SwingBottom = 0x3; // Only available in heat mode. -const uint8_t kHaierAcYrw02SwingDown = 0xA; -const uint8_t kHaierAcYrw02SwingAuto = 0xC; // Airflow - -// Byte 3 -// Bit 7 - Health mode - -// Byte 4 -const uint8_t kHaierAcYrw02Power = 0b01000000; - -// Byte 5 -// Bits 0-3 -const uint8_t kHaierAcYrw02FanHigh = 0x2; -const uint8_t kHaierAcYrw02FanMed = 0x4; -const uint8_t kHaierAcYrw02FanLow = 0x6; -const uint8_t kHaierAcYrw02FanAuto = 0xA; - -// Byte 6 -// Bits 0-1 -const uint8_t kHaierAcYrw02TurboOff = 0x0; -const uint8_t kHaierAcYrw02TurboHigh = 0x1; -const uint8_t kHaierAcYrw02TurboLow = 0x2; - -// Byte 7 -// Bits 0-3 -const uint8_t kHaierAcYrw02Auto = 0x0; -const uint8_t kHaierAcYrw02Cool = 0x2; -const uint8_t kHaierAcYrw02Dry = 0x4; -const uint8_t kHaierAcYrw02Heat = 0x8; -const uint8_t kHaierAcYrw02Fan = 0xC; - -// Byte 8 -const uint8_t kHaierAcYrw02Sleep = 0b10000000; - -// Byte 12 -// Bits 4-7 -const uint8_t kHaierAcYrw02ButtonTempUp = 0x0; -const uint8_t kHaierAcYrw02ButtonTempDown = 0x1; -const uint8_t kHaierAcYrw02ButtonSwing = 0x2; -const uint8_t kHaierAcYrw02ButtonFan = 0x4; -const uint8_t kHaierAcYrw02ButtonPower = 0x5; -const uint8_t kHaierAcYrw02ButtonMode = 0x6; -const uint8_t kHaierAcYrw02ButtonHealth = 0x7; -const uint8_t kHaierAcYrw02ButtonTurbo = 0x8; -const uint8_t kHaierAcYrw02ButtonSleep = 0xB; - -// Legacy Haier YRW02 remote defines. -#define HAIER_AC_YRW02_SWING_OFF kHaierAcYrw02SwingOff -#define HAIER_AC_YRW02_SWING_TOP kHaierAcYrw02SwingTop -#define HAIER_AC_YRW02_SWING_MIDDLE kHaierAcYrw02SwingMiddle -#define HAIER_AC_YRW02_SWING_BOTTOM kHaierAcYrw02SwingBottom -#define HAIER_AC_YRW02_SWING_DOWN kHaierAcYrw02SwingDown -#define HAIER_AC_YRW02_SWING_AUTO kHaierAcYrw02SwingAuto -#define HAIER_AC_YRW02_FAN_HIGH kHaierAcYrw02FanHigh -#define HAIER_AC_YRW02_FAN_MED kHaierAcYrw02FanMed -#define HAIER_AC_YRW02_FAN_LOW kHaierAcYrw02FanLow -#define HAIER_AC_YRW02_FAN_AUTO kHaierAcYrw02FanAuto -#define HAIER_AC_YRW02_TURBO_OFF kHaierAcYrw02TurboOff -#define HAIER_AC_YRW02_TURBO_HIGH kHaierAcYrw02TurboHigh -#define HAIER_AC_YRW02_TURBO_LOW kHaierAcYrw02TurboLow -#define HAIER_AC_YRW02_AUTO kHaierAcYrw02Auto -#define HAIER_AC_YRW02_COOL kHaierAcYrw02Cool -#define HAIER_AC_YRW02_DRY kHaierAcYrw02Dry -#define HAIER_AC_YRW02_HEAT kHaierAcYrw02Heat -#define HAIER_AC_YRW02_FAN kHaierAcYrw02Fan -#define HAIER_AC_YRW02_BUTTON_TEMP_UP kHaierAcYrw02ButtonTempUp -#define HAIER_AC_YRW02_BUTTON_TEMP_DOWN kHaierAcYrw02ButtonTempDown -#define HAIER_AC_YRW02_BUTTON_SWING kHaierAcYrw02ButtonSwing -#define HAIER_AC_YRW02_BUTTON_FAN kHaierAcYrw02ButtonFan -#define HAIER_AC_YRW02_BUTTON_POWER kHaierAcYrw02ButtonPower -#define HAIER_AC_YRW02_BUTTON_MODE kHaierAcYrw02ButtonMode -#define HAIER_AC_YRW02_BUTTON_HEALTH kHaierAcYrw02ButtonHealth -#define HAIER_AC_YRW02_BUTTON_TURBO kHaierAcYrw02ButtonTurbo -#define HAIER_AC_YRW02_BUTTON_SLEEP kHaierAcYrw02ButtonSleep - -class IRHaierAC { - public: - explicit IRHaierAC(uint16_t pin); - -#if SEND_HAIER_AC - void send(const uint16_t repeat = kHaierAcDefaultRepeat); -#endif // SEND_HAIER_AC - void begin(); - - void setCommand(const uint8_t command); - uint8_t getCommand(); - - void setTemp(const uint8_t temp); - uint8_t getTemp(); - - void setFan(const uint8_t speed); - uint8_t getFan(); - - uint8_t getMode(); - void setMode(const uint8_t mode); - - bool getSleep(); - void setSleep(const bool state); - bool getHealth(); - void setHealth(const bool state); - - int16_t getOnTimer(); - void setOnTimer(const uint16_t mins); - int16_t getOffTimer(); - void setOffTimer(const uint16_t mins); - void cancelTimers(); - - uint16_t getCurrTime(); - void setCurrTime(const uint16_t mins); - - uint8_t getSwing(); - void setSwing(const uint8_t state); - - uint8_t* getRaw(); - void setRaw(uint8_t new_code[]); - static bool validChecksum(uint8_t state[], - const uint16_t length = kHaierACStateLength); -#ifdef ARDUINO - String toString(); - static String timeToString(const uint16_t nr_mins); -#else - std::string toString(); - static std::string timeToString(const uint16_t nr_mins); -#endif - - private: - uint8_t remote_state[kHaierACStateLength]; - void stateReset(); - void checksum(); - static uint16_t getTime(const uint8_t ptr[]); - static void setTime(uint8_t ptr[], const uint16_t nr_mins); - IRsend _irsend; -}; - -class IRHaierACYRW02 { - public: - explicit IRHaierACYRW02(uint16_t pin); - -#if SEND_HAIER_AC_YRW02 - void send(const uint16_t repeat = kHaierAcYrw02DefaultRepeat); -#endif // SEND_HAIER_AC_YRW02 - void begin(); - - void setButton(const uint8_t button); - uint8_t getButton(); - - void setTemp(const uint8_t temp); - uint8_t getTemp(); - - void setFan(const uint8_t speed); - uint8_t getFan(); - - uint8_t getMode(); - void setMode(const uint8_t mode); - - bool getPower(); - void setPower(const bool state); - void on(); - void off(); - - bool getSleep(); - void setSleep(const bool state); - bool getHealth(); - void setHealth(const bool state); - - uint8_t getTurbo(); - void setTurbo(const uint8_t speed); - - uint8_t getSwing(); - void setSwing(const uint8_t state); - - uint8_t* getRaw(); - void setRaw(uint8_t new_code[]); - static bool validChecksum(uint8_t state[], - const uint16_t length = kHaierACYRW02StateLength); -#ifdef ARDUINO - String toString(); -#else - std::string toString(); -#endif - - private: - uint8_t remote_state[kHaierACYRW02StateLength]; - void stateReset(); - void checksum(); - IRsend _irsend; -}; - -#endif // IR_HAIER_H_ diff --git a/lib/IRremoteESP8266_ID1089/src/ir_Hitachi.cpp b/lib/IRremoteESP8266_ID1089/src/ir_Hitachi.cpp deleted file mode 100644 index 248f2d5..0000000 --- a/lib/IRremoteESP8266_ID1089/src/ir_Hitachi.cpp +++ /dev/null @@ -1,424 +0,0 @@ -// Copyright 2018 David Conran -// -// Code to emulate Hitachi protocol compatible devices. -// Should be compatible with: -// * Hitachi RAS-35THA6 remote -// - -#include "ir_Hitachi.h" -#include -#ifndef ARDUINO -#include -#endif -#include "IRrecv.h" -#include "IRremoteESP8266.h" -#include "IRsend.h" -#include "IRutils.h" - -// HH HH IIIII TTTTTTT AAA CCCCC HH HH IIIII -// HH HH III TTT AAAAA CC C HH HH III -// HHHHHHH III TTT AA AA CC HHHHHHH III -// HH HH III TTT AAAAAAA CC C HH HH III -// HH HH IIIII TTT AA AA CCCCC HH HH IIIII - -// Constants -// Ref: https://github.com/markszabo/IRremoteESP8266/issues/417 -const uint16_t kHitachiAcHdrMark = 3300; -const uint16_t kHitachiAcHdrSpace = 1700; -const uint16_t kHitachiAc1HdrMark = 3400; -const uint16_t kHitachiAc1HdrSpace = 3400; -const uint16_t kHitachiAcBitMark = 400; -const uint16_t kHitachiAcOneSpace = 1250; -const uint16_t kHitachiAcZeroSpace = 500; -const uint32_t kHitachiAcMinGap = 100000; // Completely made up value. - -#if (SEND_HITACHI_AC || SEND_HITACHI_AC2) -// Send a Hitachi A/C message. -// -// Args: -// data: An array of bytes containing the IR command. -// nbytes: Nr. of bytes of data in the array. (>=kHitachiAcStateLength) -// repeat: Nr. of times the message is to be repeated. (Default = 0). -// -// Status: ALPHA / Untested. -// -// Ref: -// https://github.com/markszabo/IRremoteESP8266/issues/417 -void IRsend::sendHitachiAC(unsigned char data[], uint16_t nbytes, - uint16_t repeat) { - if (nbytes < kHitachiAcStateLength) - return; // Not enough bytes to send a proper message. - sendGeneric(kHitachiAcHdrMark, kHitachiAcHdrSpace, kHitachiAcBitMark, - kHitachiAcOneSpace, kHitachiAcBitMark, kHitachiAcZeroSpace, - kHitachiAcBitMark, kHitachiAcMinGap, data, nbytes, 38, true, - repeat, 50); -} -#endif // (SEND_HITACHI_AC || SEND_HITACHI_AC2) - -#if SEND_HITACHI_AC1 -// Send a Hitachi A/C 13-byte message. -// -// For devices: -// Hitachi A/C Series VI (Circa 2007) / Remote: LT0541-HTA -// -// Args: -// data: An array of bytes containing the IR command. -// nbytes: Nr. of bytes of data in the array. (>=kHitachiAc1StateLength) -// repeat: Nr. of times the message is to be repeated. (Default = 0). -// -// Status: BETA / Appears to work. -// -// Ref: -// https://github.com/markszabo/IRremoteESP8266/issues/453 -// Basically the same as sendHitatchiAC() except different size and header. -void IRsend::sendHitachiAC1(unsigned char data[], uint16_t nbytes, - uint16_t repeat) { - if (nbytes < kHitachiAc1StateLength) - return; // Not enough bytes to send a proper message. - sendGeneric(kHitachiAc1HdrMark, kHitachiAc1HdrSpace, kHitachiAcBitMark, - kHitachiAcOneSpace, kHitachiAcBitMark, kHitachiAcZeroSpace, - kHitachiAcBitMark, kHitachiAcMinGap, data, nbytes, 38, true, - repeat, 50); -} -#endif // SEND_HITACHI_AC1 - -#if SEND_HITACHI_AC2 -// Send a Hitachi A/C 53-byte message. -// -// For devices: -// Hitachi A/C Series VI (Circa 2007) / Remote: LT0541-HTA -// -// Args: -// data: An array of bytes containing the IR command. -// nbytes: Nr. of bytes of data in the array. (>=kHitachiAc2StateLength) -// repeat: Nr. of times the message is to be repeated. (Default = 0). -// -// Status: BETA / Appears to work. -// -// Ref: -// https://github.com/markszabo/IRremoteESP8266/issues/417 -// Basically the same as sendHitatchiAC() except different size. -void IRsend::sendHitachiAC2(unsigned char data[], uint16_t nbytes, - uint16_t repeat) { - if (nbytes < kHitachiAc2StateLength) - return; // Not enough bytes to send a proper message. - sendHitachiAC(data, nbytes, repeat); -} -#endif // SEND_HITACHI_AC2 - -// Class for handling the remote control oh a Hitachi 28 byte A/C message. -// Inspired by: -// https://github.com/ToniA/arduino-heatpumpir/blob/master/HitachiHeatpumpIR.cpp - -IRHitachiAc::IRHitachiAc(uint16_t pin) : _irsend(pin) { stateReset(); } - -void IRHitachiAc::stateReset() { - remote_state[0] = 0x80; - remote_state[1] = 0x08; - remote_state[2] = 0x0C; - remote_state[3] = 0x02; - remote_state[4] = 0xFD; - remote_state[5] = 0x80; - remote_state[6] = 0x7F; - remote_state[7] = 0x88; - remote_state[8] = 0x48; - remote_state[9] = 0x10; - for (uint8_t i = 10; i < kHitachiAcStateLength; i++) remote_state[i] = 0x00; - remote_state[14] = 0x60; - remote_state[15] = 0x60; - remote_state[24] = 0x80; - setTemp(23); -} - -void IRHitachiAc::begin() { _irsend.begin(); } - -uint8_t IRHitachiAc::calcChecksum(const uint8_t state[], - const uint16_t length) { - int8_t sum = 62; - for (uint16_t i = 0; i < length - 1; i++) sum -= reverseBits(state[i], 8); - return reverseBits((uint8_t)sum, 8); -} - -void IRHitachiAc::checksum(const uint16_t length) { - remote_state[length - 1] = calcChecksum(remote_state, length); -} - -bool IRHitachiAc::validChecksum(const uint8_t state[], const uint16_t length) { - if (length < 2) return true; // Assume true for lengths that are too short. - return (state[length - 1] == calcChecksum(state, length)); -} - -uint8_t *IRHitachiAc::getRaw() { - checksum(); - return remote_state; -} - -void IRHitachiAc::setRaw(const uint8_t new_code[], const uint16_t length) { - for (uint8_t i = 0; i < length && i < kHitachiAcStateLength; i++) - remote_state[i] = new_code[i]; -} - -#if SEND_HITACHI_AC -void IRHitachiAc::send(const uint16_t repeat) { - checksum(); - _irsend.sendHitachiAC(remote_state, kHitachiAcStateLength, repeat); -} -#endif // SEND_HITACHI_AC - -bool IRHitachiAc::getPower() { return (remote_state[17] & 0x01); } - -void IRHitachiAc::setPower(const bool on) { - if (on) - remote_state[17] |= 0x01; - else - remote_state[17] &= 0xFE; -} - -void IRHitachiAc::on() { setPower(true); } - -void IRHitachiAc::off() { setPower(false); } - -uint8_t IRHitachiAc::getMode() { return reverseBits(remote_state[10], 8); } - -void IRHitachiAc::setMode(const uint8_t mode) { - uint8_t newmode = mode; - switch (mode) { - case kHitachiAcFan: - // Fan mode sets a special temp. - setTemp(64); - break; - case kHitachiAcAuto: - case kHitachiAcHeat: - case kHitachiAcCool: - case kHitachiAcDry: - break; - default: - newmode = kHitachiAcAuto; - } - remote_state[10] = reverseBits(newmode, 8); - if (mode != kHitachiAcFan) setTemp(_previoustemp); - setFan(getFan()); // Reset the fan speed after the mode change. -} - -uint8_t IRHitachiAc::getTemp() { return reverseBits(remote_state[11], 8) >> 1; } - -void IRHitachiAc::setTemp(const uint8_t celsius) { - uint8_t temp; - if (celsius != 64) _previoustemp = celsius; - switch (celsius) { - case 64: - temp = celsius; - break; - default: - temp = std::min(celsius, kHitachiAcMaxTemp); - temp = std::max(temp, kHitachiAcMinTemp); - } - remote_state[11] = reverseBits(temp << 1, 8); - if (temp == kHitachiAcMinTemp) - remote_state[9] = 0x90; - else - remote_state[9] = 0x10; -} - -uint8_t IRHitachiAc::getFan() { return reverseBits(remote_state[13], 8); } - -void IRHitachiAc::setFan(const uint8_t speed) { - uint8_t fanmin = kHitachiAcFanAuto; - uint8_t fanmax = kHitachiAcFanHigh; - switch (getMode()) { - case kHitachiAcDry: // Only 2 x low speeds in Dry mode. - fanmin = kHitachiAcFanLow; - fanmax = kHitachiAcFanLow + 1; - break; - case kHitachiAcFan: - fanmin = kHitachiAcFanLow; // No Auto in Fan mode. - break; - } - uint8_t newspeed = std::max(speed, fanmin); - newspeed = std::min(newspeed, fanmax); - remote_state[13] = reverseBits(newspeed, 8); -} - -bool IRHitachiAc::getSwingVertical() { return remote_state[14] & 0x80; } - -void IRHitachiAc::setSwingVertical(const bool on) { - if (on) - remote_state[14] |= 0x80; - else - remote_state[14] &= 0x7F; -} - -bool IRHitachiAc::getSwingHorizontal() { return remote_state[15] & 0x80; } - -void IRHitachiAc::setSwingHorizontal(const bool on) { - if (on) - remote_state[15] |= 0x80; - else - remote_state[15] &= 0x7F; -} - -// Convert the internal state into a human readable string. -#ifdef ARDUINO -String IRHitachiAc::toString() { - String result = ""; -#else -std::string IRHitachiAc::toString() { - std::string result = ""; -#endif // ARDUINO - result += "Power: "; - if (getPower()) - result += "On"; - else - result += "Off"; - result += ", Mode: " + uint64ToString(getMode()); - switch (getMode()) { - case kHitachiAcAuto: - result += " (AUTO)"; - break; - case kHitachiAcCool: - result += " (COOL)"; - break; - case kHitachiAcHeat: - result += " (HEAT)"; - break; - case kHitachiAcDry: - result += " (DRY)"; - break; - case kHitachiAcFan: - result += " (FAN)"; - break; - default: - result += " (UNKNOWN)"; - } - result += ", Temp: " + uint64ToString(getTemp()) + "C"; - result += ", Fan: " + uint64ToString(getFan()); - switch (getFan()) { - case kHitachiAcFanAuto: - result += " (AUTO)"; - break; - case kHitachiAcFanLow: - result += " (LOW)"; - break; - case kHitachiAcFanHigh: - result += " (HIGH)"; - break; - default: - result += " (UNKNOWN)"; - break; - } - result += ", Swing (Vertical): "; - if (getSwingVertical()) - result += "On"; - else - result += "Off"; - result += ", Swing (Horizontal): "; - if (getSwingHorizontal()) - result += "On"; - else - result += "Off"; - return result; -} - -#if (DECODE_HITACHI_AC || DECODE_HITACHI_AC1 || DECODE_HITACHI_AC2) -// Decode the supplied Hitachi A/C message. -// -// Args: -// results: Ptr to the data to decode and where to store the decode result. -// nbits: The number of data bits to expect. -// Typically kHitachiAcBits, kHitachiAc1Bits, kHitachiAc2Bits -// strict: Flag indicating if we should perform strict matching. -// Returns: -// boolean: True if it can decode it, false if it can't. -// -// Status: ALPHA / Untested. -// -// Supported devices: -// Hitachi A/C Series VI (Circa 2007) / Remote: LT0541-HTA -// -// Ref: -// https://github.com/markszabo/IRremoteESP8266/issues/417 -// https://github.com/markszabo/IRremoteESP8266/issues/453 -bool IRrecv::decodeHitachiAC(decode_results *results, uint16_t nbits, - bool strict) { - const uint8_t kTolerance = 30; - if (results->rawlen < 2 * nbits + kHeader + kFooter - 1) - return false; // Can't possibly be a valid HitachiAC message. - if (strict) { - switch (nbits) { - case kHitachiAcBits: - case kHitachiAc1Bits: - case kHitachiAc2Bits: - break; // Okay to continue. - default: - return false; // Not strictly a Hitachi message. - } - } - uint16_t offset = kStartOffset; - uint16_t dataBitsSoFar = 0; - match_result_t data_result; - - // Header - if (nbits == kHitachiAc1Bits) { - if (!matchMark(results->rawbuf[offset++], kHitachiAc1HdrMark, kTolerance)) - return false; - if (!matchSpace(results->rawbuf[offset++], kHitachiAc1HdrSpace, kTolerance)) - return false; - } else { // Everything else. - if (!matchMark(results->rawbuf[offset++], kHitachiAcHdrMark, kTolerance)) - return false; - if (!matchSpace(results->rawbuf[offset++], kHitachiAcHdrSpace, kTolerance)) - return false; - } - // Data - // Keep reading bytes until we either run out of message or state to fill. - for (uint16_t i = 0; offset <= results->rawlen - 16 && i < nbits / 8; - i++, dataBitsSoFar += 8, offset += data_result.used) { - data_result = matchData(&(results->rawbuf[offset]), 8, kHitachiAcBitMark, - kHitachiAcOneSpace, kHitachiAcBitMark, - kHitachiAcZeroSpace, kTolerance); - if (data_result.success == false) break; // Fail - results->state[i] = (uint8_t)data_result.data; - } - - // Footer - if (!matchMark(results->rawbuf[offset++], kHitachiAcBitMark, kTolerance)) - return false; - if (offset <= results->rawlen && - !matchAtLeast(results->rawbuf[offset], kHitachiAcMinGap, kTolerance)) - return false; - - // Compliance - if (strict) { - // Re-check we got the correct size/length due to the way we read the data. - switch (dataBitsSoFar / 8) { - case kHitachiAcStateLength: - case kHitachiAc1StateLength: - case kHitachiAc2StateLength: - break; // Continue - default: - return false; - } - if (dataBitsSoFar / 8 == kHitachiAcStateLength && - !IRHitachiAc::validChecksum(results->state, kHitachiAcStateLength)) - return false; - } - - // Success - switch (dataBitsSoFar) { - case kHitachiAc1Bits: - results->decode_type = HITACHI_AC1; - break; - case kHitachiAc2Bits: - results->decode_type = HITACHI_AC2; - break; - case kHitachiAcBits: - default: - results->decode_type = HITACHI_AC; - } - results->bits = dataBitsSoFar; - // No need to record the state as we stored it as we decoded it. - // As we use result->state, we don't record value, address, or command as it - // is a union data type. - return true; -} -#endif // (DECODE_HITACHI_AC || DECODE_HITACHI_AC1 || DECODE_HITACHI_AC2) diff --git a/lib/IRremoteESP8266_ID1089/src/ir_Hitachi.h b/lib/IRremoteESP8266_ID1089/src/ir_Hitachi.h deleted file mode 100644 index efc775e..0000000 --- a/lib/IRremoteESP8266_ID1089/src/ir_Hitachi.h +++ /dev/null @@ -1,76 +0,0 @@ -// Hitachi A/C -// -// Copyright 2018 David Conran - -#ifndef IR_HITACHI_H_ -#define IR_HITACHI_H_ - -#define __STDC_LIMIT_MACROS -#include -#ifndef UNIT_TEST -#include -#else -#include -#endif -#include "IRremoteESP8266.h" -#include "IRsend.h" - -// Constants -const uint8_t kHitachiAcAuto = 2; -const uint8_t kHitachiAcHeat = 3; -const uint8_t kHitachiAcCool = 4; -const uint8_t kHitachiAcDry = 5; -const uint8_t kHitachiAcFan = 0xC; -const uint8_t kHitachiAcFanAuto = 1; -const uint8_t kHitachiAcFanLow = 2; -const uint8_t kHitachiAcFanHigh = 5; -const uint8_t kHitachiAcMinTemp = 16; // 16C -const uint8_t kHitachiAcMaxTemp = 32; // 32C -const uint8_t kHitachiAcAutoTemp = 23; // 23C - -// Classes -class IRHitachiAc { - public: - explicit IRHitachiAc(uint16_t pin); - - void stateReset(); -#if SEND_HITACHI_AC - void send(const uint16_t repeat = kHitachiAcDefaultRepeat); -#endif // SEND_HITACHI_AC - void begin(); - void on(); - void off(); - void setPower(const bool on); - bool getPower(); - void setTemp(const uint8_t temp); - uint8_t getTemp(); - void setFan(const uint8_t speed); - uint8_t getFan(); - void setMode(const uint8_t mode); - uint8_t getMode(); - void setSwingVertical(const bool on); - bool getSwingVertical(); - void setSwingHorizontal(const bool on); - bool getSwingHorizontal(); - uint8_t* getRaw(); - void setRaw(const uint8_t new_code[], - const uint16_t length = kHitachiAcStateLength); - static bool validChecksum(const uint8_t state[], - const uint16_t length = kHitachiAcStateLength); - static uint8_t calcChecksum(const uint8_t state[], - const uint16_t length = kHitachiAcStateLength); -#ifdef ARDUINO - String toString(); -#else - std::string toString(); -#endif - - private: - // The state of the IR remote in IR code form. - uint8_t remote_state[kHitachiAcStateLength]; - void checksum(const uint16_t length = kHitachiAcStateLength); - IRsend _irsend; - uint8_t _previoustemp; -}; - -#endif // IR_HITACHI_H_ diff --git a/lib/IRremoteESP8266_ID1089/src/ir_JVC.cpp b/lib/IRremoteESP8266_ID1089/src/ir_JVC.cpp deleted file mode 100644 index 47df29d..0000000 --- a/lib/IRremoteESP8266_ID1089/src/ir_JVC.cpp +++ /dev/null @@ -1,166 +0,0 @@ -// Copyright 2015 Kristian Lauszus -// Copyright 2017 David Conran - -#include -#include "IRrecv.h" -#include "IRsend.h" -#include "IRtimer.h" -#include "IRutils.h" - -// JJJJJ V V CCCC -// J V V C -// J V V C -// J J V V C -// J V CCCC - -// JVC originally added by Kristian Lauszus -// (Thanks to zenwheel and other people at the original blog post) - -// Constants -// Ref: -// http://www.sbprojects.com/knowledge/ir/jvc.php -const uint16_t kJvcTick = 75; -const uint16_t kJvcHdrMarkTicks = 112; -const uint16_t kJvcHdrMark = kJvcHdrMarkTicks * kJvcTick; -const uint16_t kJvcHdrSpaceTicks = 56; -const uint16_t kJvcHdrSpace = kJvcHdrSpaceTicks * kJvcTick; -const uint16_t kJvcBitMarkTicks = 7; -const uint16_t kJvcBitMark = kJvcBitMarkTicks * kJvcTick; -const uint16_t kJvcOneSpaceTicks = 23; -const uint16_t kJvcOneSpace = kJvcOneSpaceTicks * kJvcTick; -const uint16_t kJvcZeroSpaceTicks = 7; -const uint16_t kJvcZeroSpace = kJvcZeroSpaceTicks * kJvcTick; -const uint16_t kJvcRptLengthTicks = 800; -const uint16_t kJvcRptLength = kJvcRptLengthTicks * kJvcTick; -const uint16_t kJvcMinGapTicks = - kJvcRptLengthTicks - - (kJvcHdrMarkTicks + kJvcHdrSpaceTicks + - kJvcBits * (kJvcBitMarkTicks + kJvcOneSpaceTicks) + kJvcBitMarkTicks); -const uint16_t kJvcMinGap = kJvcMinGapTicks * kJvcTick; - -#if SEND_JVC -// Send a JVC message. -// -// Args: -// data: The contents of the command you want to send. -// nbits: The bit size of the command being sent. (kJvcBits) -// repeat: The number of times you want the command to be repeated. -// -// Status: STABLE. -// -// Ref: -// http://www.sbprojects.com/knowledge/ir/jvc.php -void IRsend::sendJVC(uint64_t data, uint16_t nbits, uint16_t repeat) { - // Set 38kHz IR carrier frequency & a 1/3 (33%) duty cycle. - enableIROut(38, 33); - - IRtimer usecs = IRtimer(); - // Header - // Only sent for the first message. - mark(kJvcHdrMark); - space(kJvcHdrSpace); - - // We always send the data & footer at least once, hence '<= repeat'. - for (uint16_t i = 0; i <= repeat; i++) { - sendGeneric(0, 0, // No Header - kJvcBitMark, kJvcOneSpace, kJvcBitMark, kJvcZeroSpace, - kJvcBitMark, kJvcMinGap, data, nbits, 38, true, - 0, // Repeats are handles elsewhere. - 33); - // Wait till the end of the repeat time window before we send another code. - uint32_t elapsed = usecs.elapsed(); - // Avoid potential unsigned integer underflow. - // e.g. when elapsed > kJvcRptLength. - if (elapsed < kJvcRptLength) space(kJvcRptLength - elapsed); - usecs.reset(); - } -} - -// Calculate the raw JVC data based on address and command. -// -// Args: -// address: An 8-bit address value. -// command: An 8-bit command value. -// Returns: -// A raw JVC message. -// -// Status: BETA / Should work fine. -// -// Ref: -// http://www.sbprojects.com/knowledge/ir/jvc.php -uint16_t IRsend::encodeJVC(uint8_t address, uint8_t command) { - return reverseBits((command << 8) | address, 16); -} -#endif - -#if DECODE_JVC -// Decode the supplied JVC message. -// -// Args: -// results: Ptr to the data to decode and where to store the decode result. -// nbits: Nr. of bits of data to expect. Typically kJvcBits. -// strict: Flag indicating if we should perform strict matching. -// Returns: -// boolean: True if it can decode it, false if it can't. -// -// Status: STABLE -// -// Note: -// JVC repeat codes don't have a header. -// Ref: -// http://www.sbprojects.com/knowledge/ir/jvc.php -bool IRrecv::decodeJVC(decode_results *results, uint16_t nbits, bool strict) { - if (strict && nbits != kJvcBits) - return false; // Must be called with the correct nr. of bits. - if (results->rawlen < 2 * nbits + kFooter - 1) - return false; // Can't possibly be a valid JVC message. - - uint64_t data = 0; - uint16_t offset = kStartOffset; - bool isRepeat = true; - - uint32_t m_tick; - uint32_t s_tick; - // Header - // (Optional as repeat codes don't have the header) - if (matchMark(results->rawbuf[offset], kJvcHdrMark)) { - isRepeat = false; - m_tick = results->rawbuf[offset++] * kRawTick / kJvcHdrMarkTicks; - if (results->rawlen < 2 * nbits + 4) - return false; // Can't possibly be a valid JVC message with a header. - if (!matchSpace(results->rawbuf[offset], kJvcHdrSpace)) return false; - s_tick = results->rawbuf[offset++] * kRawTick / kJvcHdrSpaceTicks; - } else { - // We can't easily auto-calibrate as there is no header, so assume - // the default tick time. - m_tick = kJvcTick; - s_tick = kJvcTick; - } - - // Data - match_result_t data_result = - matchData(&(results->rawbuf[offset]), nbits, kJvcBitMarkTicks * m_tick, - kJvcOneSpaceTicks * s_tick, kJvcBitMarkTicks * m_tick, - kJvcZeroSpaceTicks * s_tick); - if (data_result.success == false) return false; - data = data_result.data; - offset += data_result.used; - - // Footer - if (!matchMark(results->rawbuf[offset++], kJvcBitMarkTicks * m_tick)) - return false; - if (offset < results->rawlen && - !matchAtLeast(results->rawbuf[offset], kJvcMinGapTicks * s_tick)) - return false; - - // Success - results->decode_type = JVC; - results->bits = nbits; - results->value = data; - // command & address are transmitted LSB first, so we need to reverse them. - results->address = reverseBits(data >> 8, 8); // The first 8 bits sent. - results->command = reverseBits(data & 0xFF, 8); // The last 8 bits sent. - results->repeat = isRepeat; - return true; -} -#endif diff --git a/lib/IRremoteESP8266_ID1089/src/ir_Kelvinator.cpp b/lib/IRremoteESP8266_ID1089/src/ir_Kelvinator.cpp deleted file mode 100644 index e172c81..0000000 --- a/lib/IRremoteESP8266_ID1089/src/ir_Kelvinator.cpp +++ /dev/null @@ -1,550 +0,0 @@ -// Copyright 2016 David Conran -// -// Code to emulate IR Kelvinator YALIF remote control unit, which should control -// at least the following Kelvinator A/C units: -// KSV26CRC, KSV26HRC, KSV35CRC, KSV35HRC, KSV53HRC, KSV62HRC, KSV70CRC, -// KSV70HRC, KSV80HRC. -// -// Note: -// * Unsupported: -// - All Sleep modes. -// - All Timer modes. -// - "I Feel" button & mode. -// - Energy Saving mode. -// - Low Heat mode. -// - Fahrenheit. - -#include "ir_Kelvinator.h" -#include -#ifndef ARDUINO -#include -#endif -#include "IRrecv.h" -#include "IRsend.h" -#include "IRutils.h" - -// KK KK EEEEEEE LL VV VV IIIII NN NN AAA TTTTTTT OOOOO RRRRRR -// KK KK EE LL VV VV III NNN NN AAAAA TTT OO OO RR RR -// KKKK EEEEE LL VV VV III NN N NN AA AA TTT OO OO RRRRRR -// KK KK EE LL VV VV III NN NNN AAAAAAA TTT OO OO RR RR -// KK KK EEEEEEE LLLLLLL VVV IIIII NN NN AA AA TTT OOOO0 RR RR - -// Constants - -const uint16_t kKelvinatorTick = 85; -const uint16_t kKelvinatorHdrMarkTicks = 106; -const uint16_t kKelvinatorHdrMark = kKelvinatorHdrMarkTicks * kKelvinatorTick; -const uint16_t kKelvinatorHdrSpaceTicks = 53; -const uint16_t kKelvinatorHdrSpace = kKelvinatorHdrSpaceTicks * kKelvinatorTick; -const uint16_t kKelvinatorBitMarkTicks = 8; -const uint16_t kKelvinatorBitMark = kKelvinatorBitMarkTicks * kKelvinatorTick; -const uint16_t kKelvinatorOneSpaceTicks = 18; -const uint16_t kKelvinatorOneSpace = kKelvinatorOneSpaceTicks * kKelvinatorTick; -const uint16_t kKelvinatorZeroSpaceTicks = 6; -const uint16_t kKelvinatorZeroSpace = - kKelvinatorZeroSpaceTicks * kKelvinatorTick; -const uint16_t kKelvinatorGapSpaceTicks = 235; -const uint16_t kKelvinatorGapSpace = kKelvinatorGapSpaceTicks * kKelvinatorTick; - -const uint8_t kKelvinatorCmdFooter = 2; -const uint8_t kKelvinatorCmdFooterBits = 3; - -const uint8_t kKelvinatorPower = 8; -const uint8_t kKelvinatorModeMask = 0xF8; -const uint8_t kKelvinatorFanOffset = 4; -const uint8_t kKelvinatorBasicFanMask = 0xFF ^ (3U << kKelvinatorFanOffset); -const uint8_t kKelvinatorFanMask = 0xFF ^ (7U << kKelvinatorFanOffset); -const uint8_t kKelvinatorChecksumStart = 10; -const uint8_t kKelvinatorVentSwingOffset = 6; -const uint8_t kKelvinatorVentSwing = 1 << kKelvinatorVentSwingOffset; -const uint8_t kKelvinatorVentSwingV = 1; -const uint8_t kKelvinatorVentSwingH = 1 << 4; -const uint8_t kKelvinatorSleep1And3 = 1 << 7; -const uint8_t kKelvinatorQuietOffset = 7; -const uint8_t kKelvinatorQuiet = 1 << kKelvinatorQuietOffset; -const uint8_t kKelvinatorIonFilterOffset = 6; -const uint8_t kKelvinatorIonFilter = 1 << kKelvinatorIonFilterOffset; -const uint8_t kKelvinatorLightOffset = 5; -const uint8_t kKelvinatorLight = 1 << kKelvinatorLightOffset; -const uint8_t kKelvinatorXfanOffset = 7; -const uint8_t kKelvinatorXfan = 1 << kKelvinatorXfanOffset; -const uint8_t kKelvinatorTurboOffset = 4; -const uint8_t kKelvinatorTurbo = 1 << kKelvinatorTurboOffset; - -#if SEND_KELVINATOR -// Send a Kelvinator A/C message. -// -// Args: -// data: An array of bytes containing the IR command. -// nbytes: Nr. of bytes of data in the array. (>=kKelvinatorStateLength) -// repeat: Nr. of times the message is to be repeated. (Default = 0). -// -// Status: STABLE / Known working. -// -void IRsend::sendKelvinator(unsigned char data[], uint16_t nbytes, - uint16_t repeat) { - if (nbytes < kKelvinatorStateLength) - return; // Not enough bytes to send a proper message. - - for (uint16_t r = 0; r <= repeat; r++) { - // Command Block #1 (4 bytes) - sendGeneric(kKelvinatorHdrMark, kKelvinatorHdrSpace, kKelvinatorBitMark, - kKelvinatorOneSpace, kKelvinatorBitMark, kKelvinatorZeroSpace, - 0, 0, // No Footer yet. - data, 4, 38, false, 0, 50); - // Send Footer for the command block (3 bits (b010)) - sendGeneric(0, 0, // No Header - kKelvinatorBitMark, kKelvinatorOneSpace, kKelvinatorBitMark, - kKelvinatorZeroSpace, kKelvinatorBitMark, kKelvinatorGapSpace, - kKelvinatorCmdFooter, kKelvinatorCmdFooterBits, 38, false, 0, - 50); - // Data Block #1 (4 bytes) - sendGeneric(0, 0, // No header - kKelvinatorBitMark, kKelvinatorOneSpace, kKelvinatorBitMark, - kKelvinatorZeroSpace, kKelvinatorBitMark, - kKelvinatorGapSpace * 2, data + 4, 4, 38, false, 0, 50); - // Command Block #2 (4 bytes) - sendGeneric(kKelvinatorHdrMark, kKelvinatorHdrSpace, kKelvinatorBitMark, - kKelvinatorOneSpace, kKelvinatorBitMark, kKelvinatorZeroSpace, - 0, 0, // No Footer yet. - data + 8, 4, 38, false, 0, 50); - // Send Footer for the command block (3 bits (B010)) - sendGeneric(0, 0, // No Header - kKelvinatorBitMark, kKelvinatorOneSpace, kKelvinatorBitMark, - kKelvinatorZeroSpace, kKelvinatorBitMark, kKelvinatorGapSpace, - kKelvinatorCmdFooter, kKelvinatorCmdFooterBits, 38, false, 0, - 50); - // Data Block #2 (4 bytes) - sendGeneric(0, 0, // No header - kKelvinatorBitMark, kKelvinatorOneSpace, kKelvinatorBitMark, - kKelvinatorZeroSpace, kKelvinatorBitMark, - kKelvinatorGapSpace * 2, data + 12, 4, 38, false, 0, 50); - } -} -#endif // SEND_KELVINATOR - -IRKelvinatorAC::IRKelvinatorAC(uint16_t pin) : _irsend(pin) { stateReset(); } - -void IRKelvinatorAC::stateReset() { - for (uint8_t i = 0; i < kKelvinatorStateLength; i++) remote_state[i] = 0x0; - remote_state[3] = 0x50; - remote_state[11] = 0x70; -} - -void IRKelvinatorAC::begin() { _irsend.begin(); } - -void IRKelvinatorAC::fixup() { - // X-Fan mode is only valid in COOL or DRY modes. - if (getMode() != kKelvinatorCool && getMode() != kKelvinatorDry) - setXFan(false); - checksum(); // Calculate the checksums -} - -#if SEND_KELVINATOR -void IRKelvinatorAC::send(const uint16_t repeat) { - fixup(); // Ensure correct settings before sending. - _irsend.sendKelvinator(remote_state, kKelvinatorStateLength, repeat); -} -#endif // SEND_KELVINATOR - -uint8_t *IRKelvinatorAC::getRaw() { - fixup(); // Ensure correct settings before sending. - return remote_state; -} - -void IRKelvinatorAC::setRaw(uint8_t new_code[]) { - for (uint8_t i = 0; i < kKelvinatorStateLength; i++) { - remote_state[i] = new_code[i]; - } -} - -uint8_t IRKelvinatorAC::calcBlockChecksum(const uint8_t *block, - const uint16_t length) { - uint8_t sum = kKelvinatorChecksumStart; - // Sum the lower half of the first 4 bytes of this block. - for (uint8_t i = 0; i < 4 && i < length - 1; i++, block++) - sum += (*block & 0x0FU); - // then sum the upper half of the next 3 bytes. - for (uint8_t i = 4; i < length - 1; i++, block++) sum += (*block >> 4); - // Trim it down to fit into the 4 bits allowed. i.e. Mod 16. - return sum & 0x0FU; -} - -// Many Bothans died to bring us this information. -void IRKelvinatorAC::checksum(const uint16_t length) { - // For each command + options block. - for (uint16_t offset = 0; offset + 7 < length; offset += 8) { - uint8_t sum = calcBlockChecksum(remote_state + offset); - remote_state[7 + offset] = (sum << 4) | (remote_state[7 + offset] & 0xFU); - } -} - -// Verify the checksum is valid for a given state. -// Args: -// state: The array to verify the checksum of. -// length: The size of the state. -// Returns: -// A boolean. -bool IRKelvinatorAC::validChecksum(const uint8_t state[], - const uint16_t length) { - for (uint16_t offset = 0; offset + 7 < length; offset += 8) { - // Top 4 bits of the last byte in the block is the block's checksum. - if (state[offset + 7] >> 4 != calcBlockChecksum(state + offset)) - return false; - } - return true; -} - -void IRKelvinatorAC::on() { - remote_state[0] |= kKelvinatorPower; - remote_state[8] = remote_state[0]; // Duplicate to the 2nd command chunk. -} - -void IRKelvinatorAC::off() { - remote_state[0] &= ~kKelvinatorPower; - remote_state[8] = remote_state[0]; // Duplicate to the 2nd command chunk. -} - -void IRKelvinatorAC::setPower(bool state) { - if (state) - on(); - else - off(); -} - -bool IRKelvinatorAC::getPower() { - return ((remote_state[0] & kKelvinatorPower) != 0); -} - -// Set the temp. in deg C -void IRKelvinatorAC::setTemp(uint8_t temp) { - temp = std::max(kKelvinatorMinTemp, temp); - temp = std::min(kKelvinatorMaxTemp, temp); - remote_state[1] = (remote_state[1] & 0xF0U) | (temp - kKelvinatorMinTemp); - remote_state[9] = remote_state[1]; // Duplicate to the 2nd command chunk. -} - -// Return the set temp. in deg C -uint8_t IRKelvinatorAC::getTemp() { - return ((remote_state[1] & 0xFU) + kKelvinatorMinTemp); -} - -// Set the speed of the fan, 0-5, 0 is auto, 1-5 is the speed -void IRKelvinatorAC::setFan(uint8_t fan) { - fan = std::min(kKelvinatorFanMax, fan); // Bounds check - - // Only change things if we need to. - if (fan != getFan()) { - // Set the basic fan values. - uint8_t fan_basic = std::min(kKelvinatorBasicFanMax, fan); - remote_state[0] = (remote_state[0] & kKelvinatorBasicFanMask) | - (fan_basic << kKelvinatorFanOffset); - remote_state[8] = remote_state[0]; // Duplicate to the 2nd command chunk. - // Set the advanced(?) fan value. - remote_state[14] = - (remote_state[14] & kKelvinatorFanMask) | (fan << kKelvinatorFanOffset); - setTurbo(false); // Turbo mode is turned off if we change the fan settings. - } -} - -uint8_t IRKelvinatorAC::getFan() { - return ((remote_state[14] & ~kKelvinatorFanMask) >> kKelvinatorFanOffset); -} - -uint8_t IRKelvinatorAC::getMode() { - return (remote_state[0] & ~kKelvinatorModeMask); -} - -void IRKelvinatorAC::setMode(uint8_t mode) { - // If we get an unexpected mode, default to AUTO. - if (mode > kKelvinatorHeat) mode = kKelvinatorAuto; - remote_state[0] = (remote_state[0] & kKelvinatorModeMask) | mode; - remote_state[8] = remote_state[0]; // Duplicate to the 2nd command chunk. - if (mode == kKelvinatorAuto || kKelvinatorDry) - // When the remote is set to Auto or Dry, it defaults to 25C and doesn't - // show it. - setTemp(kKelvinatorAutoTemp); -} - -void IRKelvinatorAC::setSwingVertical(bool state) { - if (state) { - remote_state[0] |= kKelvinatorVentSwing; - remote_state[4] |= kKelvinatorVentSwingV; - } else { - remote_state[4] &= ~kKelvinatorVentSwingV; - if (!getSwingHorizontal()) remote_state[0] &= ~kKelvinatorVentSwing; - } - remote_state[8] = remote_state[0]; // Duplicate to the 2nd command chunk. -} - -bool IRKelvinatorAC::getSwingVertical() { - return ((remote_state[4] & kKelvinatorVentSwingV) != 0); -} - -void IRKelvinatorAC::setSwingHorizontal(bool state) { - if (state) { - remote_state[0] |= kKelvinatorVentSwing; - remote_state[4] |= kKelvinatorVentSwingH; - } else { - remote_state[4] &= ~kKelvinatorVentSwingH; - if (!getSwingVertical()) remote_state[0] &= ~kKelvinatorVentSwing; - } - remote_state[8] = remote_state[0]; // Duplicate to the 2nd command chunk. -} - -bool IRKelvinatorAC::getSwingHorizontal() { - return ((remote_state[4] & kKelvinatorVentSwingH) != 0); -} - -void IRKelvinatorAC::setQuiet(bool state) { - remote_state[12] &= ~kKelvinatorQuiet; - remote_state[12] |= (state << kKelvinatorQuietOffset); -} - -bool IRKelvinatorAC::getQuiet() { - return ((remote_state[12] & kKelvinatorQuiet) != 0); -} - -void IRKelvinatorAC::setIonFilter(bool state) { - remote_state[2] &= ~kKelvinatorIonFilter; - remote_state[2] |= (state << kKelvinatorIonFilterOffset); - remote_state[10] = remote_state[2]; // Duplicate to the 2nd command chunk. -} - -bool IRKelvinatorAC::getIonFilter() { - return ((remote_state[2] & kKelvinatorIonFilter) != 0); -} - -void IRKelvinatorAC::setLight(bool state) { - remote_state[2] &= ~kKelvinatorLight; - remote_state[2] |= (state << kKelvinatorLightOffset); - remote_state[10] = remote_state[2]; // Duplicate to the 2nd command chunk. -} - -bool IRKelvinatorAC::getLight() { - return ((remote_state[2] & kKelvinatorLight) != 0); -} - -// Note: XFan mode is only valid in Cool or Dry mode. -void IRKelvinatorAC::setXFan(bool state) { - remote_state[2] &= ~kKelvinatorXfan; - remote_state[2] |= (state << kKelvinatorXfanOffset); - remote_state[10] = remote_state[2]; // Duplicate to the 2nd command chunk. -} - -bool IRKelvinatorAC::getXFan() { - return ((remote_state[2] & kKelvinatorXfan) != 0); -} - -// Note: Turbo mode is turned off if the fan speed is changed. -void IRKelvinatorAC::setTurbo(bool state) { - remote_state[2] &= ~kKelvinatorTurbo; - remote_state[2] |= (state << kKelvinatorTurboOffset); - remote_state[10] = remote_state[2]; // Duplicate to the 2nd command chunk. -} - -bool IRKelvinatorAC::getTurbo() { - return ((remote_state[2] & kKelvinatorTurbo) != 0); -} - -// Convert the internal state into a human readable string. -#ifdef ARDUINO -String IRKelvinatorAC::toString() { - String result = ""; -#else -std::string IRKelvinatorAC::toString() { - std::string result = ""; -#endif // ARDUINO - result += "Power: "; - if (getPower()) - result += "On"; - else - result += "Off"; - result += ", Mode: " + uint64ToString(getMode()); - switch (getMode()) { - case kKelvinatorAuto: - result += " (AUTO)"; - break; - case kKelvinatorCool: - result += " (COOL)"; - break; - case kKelvinatorHeat: - result += " (HEAT)"; - break; - case kKelvinatorDry: - result += " (DRY)"; - break; - case kKelvinatorFan: - result += " (FAN)"; - break; - default: - result += " (UNKNOWN)"; - } - result += ", Temp: " + uint64ToString(getTemp()) + "C"; - result += ", Fan: " + uint64ToString(getFan()); - switch (getFan()) { - case kKelvinatorFanAuto: - result += " (AUTO)"; - break; - case kKelvinatorFanMax: - result += " (MAX)"; - break; - } - result += ", Turbo: "; - if (getTurbo()) - result += "On"; - else - result += "Off"; - result += ", Quiet: "; - if (getQuiet()) - result += "On"; - else - result += "Off"; - result += ", XFan: "; - if (getXFan()) - result += "On"; - else - result += "Off"; - result += ", IonFilter: "; - if (getIonFilter()) - result += "On"; - else - result += "Off"; - result += ", Light: "; - if (getLight()) - result += "On"; - else - result += "Off"; - result += ", Swing (Horizontal): "; - if (getSwingHorizontal()) - result += "On"; - else - result += "Off"; - result += ", Swing (Vertical): "; - if (getSwingVertical()) - result += "On"; - else - result += "Off"; - return result; -} - -#if DECODE_KELVINATOR -// Decode the supplied Kelvinator message. -// -// Args: -// results: Ptr to the data to decode and where to store the decode result. -// nbits: The number of data bits to expect. Typically kKelvinatorBits. -// strict: Flag indicating if we should perform strict matching. -// Returns: -// boolean: True if it can decode it, false if it can't. -// -// Status: ALPHA / Untested. -bool IRrecv::decodeKelvinator(decode_results *results, uint16_t nbits, - bool strict) { - if (results->rawlen < - 2 * (nbits + kKelvinatorCmdFooterBits) + (kHeader + kFooter + 1) * 2 - 1) - return false; // Can't possibly be a valid Kelvinator message. - if (strict && nbits != kKelvinatorBits) - return false; // Not strictly a Kelvinator message. - - uint32_t data; - uint16_t offset = kStartOffset; - - // There are two messages back-to-back in a full Kelvinator IR message - // sequence. - int8_t state_pos = 0; - for (uint8_t s = 0; s < 2; s++) { - match_result_t data_result; - - // Header - if (!matchMark(results->rawbuf[offset], kKelvinatorHdrMark)) return false; - // Calculate how long the lowest tick time is based on the header mark. - uint32_t mark_tick = - results->rawbuf[offset++] * kRawTick / kKelvinatorHdrMarkTicks; - if (!matchSpace(results->rawbuf[offset], kKelvinatorHdrSpace)) return false; - // Calculate how long the common tick time is based on the header space. - uint32_t space_tick = - results->rawbuf[offset++] * kRawTick / kKelvinatorHdrSpaceTicks; - - // Data (Command) (32 bits) - data_result = matchData( - &(results->rawbuf[offset]), 32, kKelvinatorBitMarkTicks * mark_tick, - kKelvinatorOneSpaceTicks * space_tick, - kKelvinatorBitMarkTicks * mark_tick, - kKelvinatorZeroSpaceTicks * space_tick, kTolerance, kMarkExcess, false); - if (data_result.success == false) return false; - data = data_result.data; - offset += data_result.used; - - // Record command data in the state. - for (uint16_t i = 0; i < 4; i++, data >>= 8) - results->state[state_pos + i] = data & 0xFF; - state_pos += 4; - - // Command data footer (3 bits, B010) - data_result = matchData( - &(results->rawbuf[offset]), kKelvinatorCmdFooterBits, - kKelvinatorBitMarkTicks * mark_tick, - kKelvinatorOneSpaceTicks * space_tick, - kKelvinatorBitMarkTicks * mark_tick, - kKelvinatorZeroSpaceTicks * space_tick, kTolerance, kMarkExcess, false); - if (data_result.success == false) return false; - if (data_result.data != kKelvinatorCmdFooter) return false; - offset += data_result.used; - - // Interdata gap. - if (!matchMark(results->rawbuf[offset++], - kKelvinatorBitMarkTicks * mark_tick)) - return false; - if (!matchSpace(results->rawbuf[offset++], - kKelvinatorGapSpaceTicks * space_tick)) - return false; - - // Data (Options) (32 bits) - data_result = matchData( - &(results->rawbuf[offset]), 32, kKelvinatorBitMarkTicks * mark_tick, - kKelvinatorOneSpaceTicks * space_tick, - kKelvinatorBitMarkTicks * mark_tick, - kKelvinatorZeroSpaceTicks * space_tick, kTolerance, kMarkExcess, false); - if (data_result.success == false) return false; - data = data_result.data; - offset += data_result.used; - - // Record option data in the state. - for (uint16_t i = 0; i < 4; i++, data >>= 8) - results->state[state_pos + i] = data & 0xFF; - state_pos += 4; - - // Inter-sequence gap. (Double length gap) - if (!matchMark(results->rawbuf[offset++], - kKelvinatorBitMarkTicks * mark_tick)) - return false; - if (s == 0) { - if (!matchSpace(results->rawbuf[offset++], - kKelvinatorGapSpaceTicks * space_tick * 2)) - return false; - } else { - if (offset <= results->rawlen && - !matchAtLeast(results->rawbuf[offset], - kKelvinatorGapSpaceTicks * 2 * space_tick)) - return false; - } - } - - // Compliance - if (strict) { - // Correct size/length) - if (state_pos != kKelvinatorStateLength) return false; - // Verify the message's checksum is correct. - if (!IRKelvinatorAC::validChecksum(results->state)) return false; - } - - // Success - results->decode_type = KELVINATOR; - results->bits = state_pos * 8; - // No need to record the state as we stored it as we decoded it. - // As we use result->state, we don't record value, address, or command as it - // is a union data type. - return true; -} -#endif // DECODE_KELVINATOR diff --git a/lib/IRremoteESP8266_ID1089/src/ir_Kelvinator.h b/lib/IRremoteESP8266_ID1089/src/ir_Kelvinator.h deleted file mode 100644 index 2cc4e5a..0000000 --- a/lib/IRremoteESP8266_ID1089/src/ir_Kelvinator.h +++ /dev/null @@ -1,180 +0,0 @@ -// Kelvinator A/C -// -// Copyright 2016 David Conran - -#ifndef IR_KELVINATOR_H_ -#define IR_KELVINATOR_H_ - -#define __STDC_LIMIT_MACROS -#include -#ifndef UNIT_TEST -#include -#else -#include -#endif -#include "IRremoteESP8266.h" -#include "IRsend.h" - -// KK KK EEEEEEE LL VV VV IIIII NN NN AAA TTTTTTT OOOOO RRRRRR -// KK KK EE LL VV VV III NNN NN AAAAA TTT OO OO RR RR -// KKKK EEEEE LL VV VV III NN N NN AA AA TTT OO OO RRRRRR -// KK KK EE LL VV VV III NN NNN AAAAAAA TTT OO OO RR RR -// KK KK EEEEEEE LLLLLLL VVV IIIII NN NN AA AA TTT OOOO0 RR RR - -// Constants -const uint8_t kKelvinatorAuto = 0; -const uint8_t kKelvinatorCool = 1; -const uint8_t kKelvinatorDry = 2; -const uint8_t kKelvinatorFan = 3; -const uint8_t kKelvinatorHeat = 4; -const uint8_t kKelvinatorBasicFanMax = 3; -const uint8_t kKelvinatorFanAuto = 0; -const uint8_t kKelvinatorFanMax = 5; -const uint8_t kKelvinatorMinTemp = 16; // 16C -const uint8_t kKelvinatorMaxTemp = 30; // 30C -const uint8_t kKelvinatorAutoTemp = 25; // 25C - -// Legacy defines (Deprecated) -#define KELVINATOR_MIN_TEMP kKelvinatorMinTemp -#define KELVINATOR_MAX_TEMP kKelvinatorMaxTemp -#define KELVINATOR_HEAT kKelvinatorHeat -#define KELVINATOR_FAN_MAX kKelvinatorFanMax -#define KELVINATOR_FAN_AUTO kKelvinatorFanAuto -#define KELVINATOR_FAN kKelvinatorFan -#define KELVINATOR_DRY kKelvinatorDry -#define KELVINATOR_COOL kKelvinatorCool -#define KELVINATOR_BASIC_FAN_MAX kKelvinatorBasicFanMax -#define KELVINATOR_AUTO_TEMP kKelvinatorAutoTemp -#define KELVINATOR_AUTO kKelvinatorAuto - -/* - Kelvinator AC map - - (header mark and space) - byte 0 = Basic Modes - b2-0 = Modes - Modes: - 000 = Auto (temp = 25C) - 001 = Cool - 010 = Dry (temp = 25C, but not shown) - 011 = Fan - 100 = Heat - b3 = Power Status (1 = On, 0 = Off) - b5-4 = Fan (Basic modes) - Fan: - 00 = Auto - 01 = Fan 1 - 10 = Fan 2 - 11 = Fan 3 or higher (See byte 14) - b6 = Vent swing (1 = On, 0 = Off) (See byte 4) - b7 = Sleep Modes 1 & 3 (1 = On, 0 = Off) - byte 1 = Temperature - b3-0: Degrees C. - 0000 (0) = 16C - 0001 (1) = 17C - 0010 (2) = 18C - ... - 1101 (13) = 29C - 1110 (14) = 30C - byte 2 = Extras - b3-0 = UNKNOWN, typically 0. - b4 = Turbo Fan (1 = On, 0 = Off) - b5 = Light (Display) (1 = On, 0 = Off) - b6 = Ion Filter (1 = On, 0 = Off) - b7 = X-Fan (Fan runs for a while after power off) (1 = On, 0 = Off) - byte 3 = Section Indicator - b3-0 = Unused (Typically 0) - b5-4 = Unknown (possibly timer related) (Typically 0b01) - b7-6 = End of command block (B01) - (B010 marker and a gap of 20ms) - byte 4 = Extended options - b0 = Swing Vent Vertical (1 = On, 0 = Off) - b4 = Swing Vent Horizontal (1 = On, 0 = Off) - byte 5-6 = Timer related. Typically 0 except when timer in use. - byte 7 = checksum - b3-0 = Unknown (Used in Timer mode) - b7-4 = checksum of the previous bytes (0-6) - (gap of 40ms) - (header mark and space) - byte 8 = Repeat of byte 0 - byte 9 = Repeat of byte 1 - byte 10 = Repeat of byte 2 - byte 11 = Section Indicator - b3-0 = Unused (Typically 0) - b5-4 = Unknown (possibly timer related) (Typically 0b11) - b7-6 = End of command block (B01) - (B010 marker and a gap of 20ms) - byte 12 = Extended options - b0 = Sleep mode 2 (1 = On, 0=Off) - b6-1 = Unknown (Used in Sleep Mode 3, Typically 0b000000) - b7 = Quiet Mode (1 = On, 0=Off) - byte 13 = Unknown (Sleep Mode 3 related, Typically 0x00) - byte 14 = Fan control - b3-0 = Unknown (Sleep Mode 3 related, Typically 0b0000) - b6-4 = Fan speed - 0b000 (0) = Automatic - 0b001 (1) = Fan 1 - 0b010 (2) = Fan 2 - 0b011 (3) = Fan 3 - 0b100 (4) = Fan 4 - 0b101 (5) = Fan 5 - byte 15 = checksum - b3-0 = Unknown (Typically 0b0000) - b7-4 = checksum of the previous bytes (8-14) -*/ - -// Classes -class IRKelvinatorAC { - public: - explicit IRKelvinatorAC(uint16_t pin); - - void stateReset(); -#if SEND_KELVINATOR - void send(const uint16_t repeat = kKelvinatorDefaultRepeat); -#endif // SEND_KELVINATOR - void begin(); - void on(); - void off(); - void setPower(bool state); - bool getPower(); - void setTemp(uint8_t temp); - uint8_t getTemp(); - void setFan(uint8_t fan); - uint8_t getFan(); - void setMode(uint8_t mode); - uint8_t getMode(); - void setSwingVertical(bool state); - bool getSwingVertical(); - void setSwingHorizontal(bool state); - bool getSwingHorizontal(); - void setQuiet(bool state); - bool getQuiet(); - void setIonFilter(bool state); - bool getIonFilter(); - void setLight(bool state); - bool getLight(); - void setXFan(bool state); - bool getXFan(); - void setTurbo(bool state); - bool getTurbo(); - uint8_t* getRaw(); - void setRaw(uint8_t new_code[]); - static uint8_t calcBlockChecksum( - const uint8_t* block, const uint16_t length = kKelvinatorStateLength / 2); - static bool validChecksum(const uint8_t state[], - const uint16_t length = kKelvinatorStateLength); -#ifdef ARDUINO - String toString(); -#else - std::string toString(); -#endif - - private: - // The state of the IR remote in IR code form. - uint8_t remote_state[kKelvinatorStateLength]; - void checksum(const uint16_t length = kKelvinatorStateLength); - void fixup(); - IRsend _irsend; -}; - -#endif // IR_KELVINATOR_H_ diff --git a/lib/IRremoteESP8266_ID1089/src/ir_LG.cpp b/lib/IRremoteESP8266_ID1089/src/ir_LG.cpp deleted file mode 100644 index f9d922f..0000000 --- a/lib/IRremoteESP8266_ID1089/src/ir_LG.cpp +++ /dev/null @@ -1,290 +0,0 @@ -// Copyright 2015 Darryl Smith -// Copyright 2015 cheaplin -// Copyright 2017, 2018 David Conran - -#include "ir_LG.h" -#include -#include "IRrecv.h" -#include "IRsend.h" -#include "IRutils.h" - -// L GGGG -// L G -// L G GG -// L G G -// LLLLL GGG - -// LG decode originally added by Darryl Smith (based on the JVC protocol) -// LG send originally added by https://github.com/chaeplin -// -// Known supported devices: -// IR Remotes: -// 6711A20083V -// AKB74395308 - -// Constants -const uint16_t kLgTick = 50; -const uint16_t kLgHdrMarkTicks = 170; -const uint16_t kLgHdrMark = kLgHdrMarkTicks * kLgTick; // 8500 -const uint16_t kLgHdrSpaceTicks = 85; -const uint16_t kLgHdrSpace = kLgHdrSpaceTicks * kLgTick; // 4250 -const uint16_t kLgBitMarkTicks = 11; -const uint16_t kLgBitMark = kLgBitMarkTicks * kLgTick; // 550 -const uint16_t kLgOneSpaceTicks = 32; -const uint16_t kLgOneSpace = kLgOneSpaceTicks * kLgTick; // 1600 -const uint16_t kLgZeroSpaceTicks = 11; -const uint16_t kLgZeroSpace = kLgZeroSpaceTicks * kLgTick; // 550 -const uint16_t kLgRptSpaceTicks = 45; -const uint16_t kLgRptSpace = kLgRptSpaceTicks * kLgTick; // 2250 -const uint16_t kLgMinGapTicks = 795; -const uint16_t kLgMinGap = kLgMinGapTicks * kLgTick; // 39750 -const uint16_t kLgMinMessageLengthTicks = 2161; -const uint32_t kLgMinMessageLength = kLgMinMessageLengthTicks * kLgTick; - -const uint16_t kLg32HdrMarkTicks = 90; -const uint16_t kLg32HdrMark = kLg32HdrMarkTicks * kLgTick; // 4500 -const uint16_t kLg32HdrSpaceTicks = 89; -const uint16_t kLg32HdrSpace = kLg32HdrSpaceTicks * kLgTick; // 4450 -const uint16_t kLg32RptHdrMarkTicks = 179; -const uint16_t kLg32RptHdrMark = kLg32RptHdrMarkTicks * kLgTick; // 8950 - -const uint16_t kLg2HdrMarkTicks = 64; -const uint16_t kLg2HdrMark = kLg2HdrMarkTicks * kLgTick; // 3200 -const uint16_t kLg2HdrSpaceTicks = 197; -const uint16_t kLg2HdrSpace = kLg2HdrSpaceTicks * kLgTick; // 9850 -const uint16_t kLg2BitMarkTicks = 10; -const uint16_t kLg2BitMark = kLg2BitMarkTicks * kLgTick; // 500 - -#if (SEND_LG || DECODE_LG) -// Calculate the rolling 4-bit wide checksum over all of the data. -// Args: -// data: The value to be checksum'ed. -// Returns: -// A 4-bit checksum. -uint8_t calcLGChecksum(uint16_t data) { - return (((data >> 12) + ((data >> 8) & 0xF) + ((data >> 4) & 0xF) + - (data & 0xF)) & - 0xF); -} -#endif - -#if SEND_LG -// Send an LG formatted message. -// -// Args: -// data: The contents of the message you want to send. -// nbits: The bit size of the message being sent. -// Typically kLgBits or kLg32Bits. -// repeat: The number of times you want the message to be repeated. -// -// Status: Beta / Should be working. -// -// Notes: -// LG has a separate message to indicate a repeat, like NEC does. -// Supports: -// IR Remote models: 6711A20083V -void IRsend::sendLG(uint64_t data, uint16_t nbits, uint16_t repeat) { - uint16_t repeatHeaderMark = 0; - - if (nbits >= kLg32Bits) { - // LG 32bit protocol is near identical to Samsung except for repeats. - sendSAMSUNG(data, nbits, 0); // Send it as a single Samsung message. - repeatHeaderMark = kLg32RptHdrMark; - repeat++; - } else { - // LG (28-bit) protocol. - repeatHeaderMark = kLgHdrMark; - sendGeneric(kLgHdrMark, kLgHdrSpace, kLgBitMark, kLgOneSpace, kLgBitMark, - kLgZeroSpace, kLgBitMark, kLgMinGap, kLgMinMessageLength, data, - nbits, 38, true, 0, // Repeats are handled later. - 50); - } - - // Repeat - // Protocol has a mandatory repeat-specific code sent after every command. - if (repeat) - sendGeneric(repeatHeaderMark, kLgRptSpace, 0, 0, 0, 0, // No data is sent. - kLgBitMark, kLgMinGap, kLgMinMessageLength, 0, 0, // No data. - 38, true, repeat - 1, 50); -} - -// Send an LG Variant-2 formatted message. -// -// Args: -// data: The contents of the message you want to send. -// nbits: The bit size of the message being sent. -// Typically kLgBits or kLg32Bits. -// repeat: The number of times you want the message to be repeated. -// -// Status: Beta / Should be working. -// -// Notes: -// LG has a separate message to indicate a repeat, like NEC does. -// Supports: -// IR Remote models: AKB74395308 -void IRsend::sendLG2(uint64_t data, uint16_t nbits, uint16_t repeat) { - if (nbits >= kLg32Bits) { - // Let the original routine handle it. - sendLG(data, nbits, repeat); // Send it as a single Samsung message. - return; - } - - // LGv2 (28-bit) protocol. - sendGeneric(kLg2HdrMark, kLg2HdrSpace, kLgBitMark, kLgOneSpace, kLgBitMark, - kLgZeroSpace, kLgBitMark, kLgMinGap, kLgMinMessageLength, data, - nbits, 38, true, 0, // Repeats are handled later. - 50); - - // TODO(crackn): Verify the details of what repeat messages look like. - // Repeat - // Protocol has a mandatory repeat-specific code sent after every command. - if (repeat) - sendGeneric(kLg2HdrMark, kLgRptSpace, 0, 0, 0, 0, // No data is sent. - kLgBitMark, kLgMinGap, kLgMinMessageLength, 0, 0, // No data. - 38, true, repeat - 1, 50); -} - -// Construct a raw 28-bit LG message code from the supplied address & command. -// -// Args: -// address: The address code. -// command: The command code. -// Returns: -// A raw 28-bit LG message code suitable for sendLG() etc. -// -// Status: BETA / Should work. -// -// Notes: -// e.g. Sequence of bits = address + command + checksum. -uint32_t IRsend::encodeLG(uint16_t address, uint16_t command) { - return ((address << 20) | (command << 4) | calcLGChecksum(command)); -} -#endif - -#if DECODE_LG -// Decode the supplied LG message. -// LG protocol has a repeat code which is 4 items long. -// Even though the protocol has 28/32 bits of data, only 24/28 bits are -// distinct. -// In transmission order, the 28/32 bits are constructed as follows: -// 8/12 bits of address + 16 bits of command + 4 bits of checksum. -// -// Args: -// results: Ptr to the data to decode and where to store the decode result. -// nbits: Nr. of bits to expect in the data portion. -// Typically kLgBits or kLg32Bits. -// strict: Flag to indicate if we strictly adhere to the specification. -// Returns: -// boolean: True if it can decode it, false if it can't. -// -// Status: BETA / Should work. -// -// Note: -// LG 32bit protocol appears near identical to the Samsung protocol. -// They possibly differ on how they repeat and initial HDR mark. -// -// Supports: -// IR Remote models: 6711A20083V, AKB74395308 - -// Ref: -// https://funembedded.wordpress.com/2014/11/08/ir-remote-control-for-lg-conditioner-using-stm32f302-mcu-on-mbed-platform/ -bool IRrecv::decodeLG(decode_results *results, uint16_t nbits, bool strict) { - if (nbits >= kLg32Bits) { - if (results->rawlen < 2 * nbits + 2 * (kHeader + kFooter) - 1) - return false; // Can't possibly be a valid LG32 message. - } else { - if (results->rawlen < 2 * nbits + kHeader + kFooter - 1) - return false; // Can't possibly be a valid LG message. - } - if (strict && nbits != kLgBits && nbits != kLg32Bits) - return false; // Doesn't comply with expected LG protocol. - - uint64_t data = 0; - uint16_t offset = kStartOffset; - bool isLg2 = false; - - // Header - uint32_t m_tick; - if (matchMark(results->rawbuf[offset], kLgHdrMark)) { - m_tick = results->rawbuf[offset++] * kRawTick / kLgHdrMarkTicks; - } else if (matchMark(results->rawbuf[offset], kLg2HdrMark)) { - m_tick = results->rawbuf[offset++] * kRawTick / kLg2HdrMarkTicks; - isLg2 = true; - } else if (matchMark(results->rawbuf[offset], kLg32HdrMark)) { - m_tick = results->rawbuf[offset++] * kRawTick / kLg32HdrMarkTicks; - } else { - return false; - } - uint32_t s_tick; - if (isLg2) { - if (matchSpace(results->rawbuf[offset], kLg2HdrSpace)) - s_tick = results->rawbuf[offset++] * kRawTick / kLg2HdrSpaceTicks; - else - return false; - } else { - if (matchSpace(results->rawbuf[offset], kLgHdrSpace)) - s_tick = results->rawbuf[offset++] * kRawTick / kLgHdrSpaceTicks; - else if (matchSpace(results->rawbuf[offset], kLg2HdrSpace)) - s_tick = results->rawbuf[offset++] * kRawTick / kLg32HdrSpaceTicks; - else - return false; - } - - // Set up the expected tick sizes based on variant. - uint16_t bitmarkticks; - if (isLg2) { - bitmarkticks = kLg2BitMarkTicks; - } else { - bitmarkticks = kLgBitMarkTicks; - } - - // Data - match_result_t data_result = - matchData(&(results->rawbuf[offset]), nbits, bitmarkticks * m_tick, - kLgOneSpaceTicks * s_tick, bitmarkticks * m_tick, - kLgZeroSpaceTicks * s_tick, kTolerance, 0); - if (data_result.success == false) return false; - data = data_result.data; - offset += data_result.used; - - // Footer - if (!matchMark(results->rawbuf[offset++], bitmarkticks * m_tick)) - return false; - if (offset < results->rawlen && - !matchAtLeast(results->rawbuf[offset], kLgMinGapTicks * s_tick)) - return false; - - // Repeat - if (nbits >= kLg32Bits) { - // If we are expecting the LG 32-bit protocol, there is always - // a repeat message. So, check for it. - offset++; - if (!matchMark(results->rawbuf[offset++], kLg32RptHdrMarkTicks * m_tick)) - return false; - if (!matchSpace(results->rawbuf[offset++], kLgRptSpaceTicks * s_tick)) - return false; - if (!matchMark(results->rawbuf[offset++], bitmarkticks * m_tick)) - return false; - if (offset < results->rawlen && - !matchAtLeast(results->rawbuf[offset], kLgMinGapTicks * s_tick)) - return false; - } - - // Compliance - uint16_t command = (data >> 4) & 0xFFFF; // The 16 bits before the checksum. - - if (strict && (data & 0xF) != calcLGChecksum(command)) - return false; // The last 4 bits sent are the expected checksum. - - // Success - if (isLg2) - results->decode_type = LG2; - else - results->decode_type = LG; - results->bits = nbits; - results->value = data; - results->command = command; - results->address = data >> 20; // The bits before the command. - return true; -} -#endif diff --git a/lib/IRremoteESP8266_ID1089/src/ir_LG.h b/lib/IRremoteESP8266_ID1089/src/ir_LG.h deleted file mode 100644 index 25d56bc..0000000 --- a/lib/IRremoteESP8266_ID1089/src/ir_LG.h +++ /dev/null @@ -1,17 +0,0 @@ -// Copyright 2017 David Conran - -#ifndef IR_LG_H_ -#define IR_LG_H_ - -// L GGGG -// L G -// L G GG -// L G G -// LLLLL GGG - -#define __STDC_LIMIT_MACROS -#include - -uint8_t calcLGChecksum(uint16_t data); - -#endif // IR_LG_H_ diff --git a/lib/IRremoteESP8266_ID1089/src/ir_Lasertag.cpp b/lib/IRremoteESP8266_ID1089/src/ir_Lasertag.cpp deleted file mode 100644 index 7f0b89a..0000000 --- a/lib/IRremoteESP8266_ID1089/src/ir_Lasertag.cpp +++ /dev/null @@ -1,122 +0,0 @@ -// Copyright 2017 David Conran - -#include -#include "IRrecv.h" -#include "IRsend.h" -#include "IRutils.h" - -// LL AAA SSSSS EEEEEEE RRRRRR TTTTTTT AAA GGGG -// LL AAAAA SS EE RR RR TTT AAAAA GG GG -// LL AA AA SSSSS EEEEE RRRRRR TTT AA AA GG -// LL AAAAAAA SS EE RR RR TTT AAAAAAA GG GG -// LLLLLLL AA AA SSSSS EEEEEEE RR RR TTT AA AA GGGGGG - -// Constants -const uint16_t kLasertagMinSamples = 13; -const uint16_t kLasertagTick = 333; -const uint32_t kLasertagMinGap = 100000; // Completely made up amount. -const uint8_t kLasertagTolerance = 0; // Percentage error margin. -const uint16_t kLasertagExcess = 0; // See kMarkExcess. -const uint16_t kLasertagDelta = 150; // Use instead of Excess and Tolerance. -const int16_t kSpace = 1; -const int16_t kMark = 0; - -#if SEND_LASERTAG -// Send a Lasertag packet. -// This protocol is pretty much just raw Manchester encoding. -// -// Args: -// data: The message you wish to send. -// nbits: Bit size of the protocol you want to send. -// repeat: Nr. of extra times the data will be sent. -// -// Status: STABLE / Working. -// -void IRsend::sendLasertag(uint64_t data, uint16_t nbits, uint16_t repeat) { - if (nbits > sizeof(data) * 8) return; // We can't send something that big. - - // Set 36kHz IR carrier frequency & a 1/4 (25%) duty cycle. - // NOTE: duty cycle is not confirmed. Just guessing based on RC5/6 protocols. - enableIROut(36, 25); - - for (uint16_t i = 0; i <= repeat; i++) { - // Data - for (uint64_t mask = 1ULL << (nbits - 1); mask; mask >>= 1) - if (data & mask) { // 1 - space(kLasertagTick); // 1 is space, then mark. - mark(kLasertagTick); - } else { // 0 - mark(kLasertagTick); // 0 is mark, then space. - space(kLasertagTick); - } - // Footer - space(kLasertagMinGap); - } -} -#endif // SEND_LASERTAG - -#if DECODE_LASERTAG -// Decode the supplied Lasertag message. -// This protocol is pretty much just raw Manchester encoding. -// -// Args: -// results: Ptr to the data to decode and where to store the decode result. -// nbits: The number of data bits to expect. -// strict: Flag indicating if we should perform strict matching. -// Returns: -// boolean: True if it can decode it, false if it can't. -// -// Status: BETA / Appears to be working 90% of the time. -// -// Ref: -// http://www.sbprojects.com/knowledge/ir/rc5.php -// https://en.wikipedia.org/wiki/RC-5 -// https://en.wikipedia.org/wiki/Manchester_code -bool IRrecv::decodeLasertag(decode_results *results, uint16_t nbits, - bool strict) { - if (results->rawlen < kLasertagMinSamples) return false; - - // Compliance - if (strict && nbits != kLasertagBits) return false; - - uint16_t offset = kStartOffset; - uint16_t used = 0; - uint64_t data = 0; - uint16_t actual_bits = 0; - - // No Header - - // Data - for (; offset <= results->rawlen; actual_bits++) { - int16_t levelA = - getRClevel(results, &offset, &used, kLasertagTick, kLasertagTolerance, - kLasertagExcess, kLasertagDelta); - int16_t levelB = - getRClevel(results, &offset, &used, kLasertagTick, kLasertagTolerance, - kLasertagExcess, kLasertagDelta); - if (levelA == kSpace && levelB == kMark) { - data = (data << 1) | 1; // 1 - } else { - if (levelA == kMark && levelB == kSpace) { - data <<= 1; // 0 - } else { - break; - } - } - } - // Footer (None) - - // Compliance - if (actual_bits < nbits) return false; // Less data than we expected. - if (strict && actual_bits != kLasertagBits) return false; - - // Success - results->decode_type = LASERTAG; - results->value = data; - results->address = data & 0xF; // Unit - results->command = data >> 4; // Team - results->repeat = false; - results->bits = actual_bits; - return true; -} -#endif // DECODE_LASERTAG diff --git a/lib/IRremoteESP8266_ID1089/src/ir_Lutron.cpp b/lib/IRremoteESP8266_ID1089/src/ir_Lutron.cpp deleted file mode 100644 index 00eb938..0000000 --- a/lib/IRremoteESP8266_ID1089/src/ir_Lutron.cpp +++ /dev/null @@ -1,156 +0,0 @@ -// Copyright 2018 David Conran - -#define __STDC_LIMIT_MACROS -#include -#include -#include "IRrecv.h" -#include "IRsend.h" -#include "IRutils.h" - -// LL UU UU TTTTTTT RRRRRR OOOOO NN NN -// LL UU UU TTT RR RR OO OO NNN NN -// LL UU UU TTT RRRRRR OO OO NN N NN -// LL UU UU TTT RR RR OO OO NN NNN -// LLLLLLL UUUUU TTT RR RR OOOO0 NN NN - -// Notes: -// The Lutron protocol uses a sort of Run Length encoding to encode -// its data. There is no header or footer per-se. -// As a mark is the first data we will notice, we always assume the First -// bit of the technically 36-bit protocol is '1'. So it is assumed, and thus -// we only care about the 35 bits of data. - -// Constants -// Ref: -// https://github.com/markszabo/IRremoteESP8266/issues/515 -const uint16_t kLutronTick = 2288; -const uint32_t kLutronGap = 150000; // Completely made up value. -const uint16_t kLutronDelta = 400; // +/- 300 usecs. - -#if SEND_LUTRON -// Send a Lutron formatted message. -// -// Args: -// data: The message to be sent. -// nbits: The number of bits of the message to be sent. Typically kLutronBits -// repeat: The number of times the command is to be repeated. -// -// Status: Stable / Appears to be working for real devices. - -// Notes: -// Protocol is really 36 bits long, but the first bit is always a 1. -// So, assume the 1 and only have a normal payload of 35 bits. -// -// Ref: -// https://github.com/markszabo/IRremoteESP8266/issues/515 -void IRsend::sendLutron(uint64_t data, uint16_t nbits, uint16_t repeat) { - enableIROut(40000, 40); // 40Khz & 40% dutycycle. - for (uint16_t r = 0; r <= repeat; r++) { - mark(kLutronTick); // 1st bit is always '1'. - // Send the supplied data in MSB First order. - for (uint64_t mask = 1ULL << (nbits - 1); mask; mask >>= 1) - if (data & mask) - mark(kLutronTick); // Send a 1 - else - space(kLutronTick); // Send a 0 - space(kLutronGap); // Inter-message gap. - } -} -#endif // SEND_LUTRON - -#if DECODE_LUTRON -// Decode the supplied Lutron message. -// -// Args: -// results: Ptr to the data to decode and where to store the decode result. -// nbits: The number of data bits to expect. Typically kLutronBits. -// strict: Flag indicating if we should perform strict matching. -// Returns: -// boolean: True if it can decode it, false if it can't. -// -// Status: ALPHA / Untested. -// -// Notes: -// -// Ref: -// https://github.com/markszabo/IRremoteESP8266/issues/515 -bool IRrecv::decodeLutron(decode_results *results, uint16_t nbits, - bool strict) { - // Technically the smallest number of entries for the smallest message is '1'. - // i.e. All the bits set to 1, would produce a single huge mark signal. - // So no minimum length check is required. - if (strict && nbits != kLutronBits) - return false; // Not strictly an Lutron message. - - uint64_t data = 0; - int16_t bitsSoFar = -1; - - if (nbits > sizeof(data) * 8) return false; // To large to store the data. - for (uint16_t offset = kStartOffset; - bitsSoFar < nbits && offset < results->rawlen; offset++) { - uint16_t entry = results->rawbuf[offset]; - // It has to be large enough to qualify as a bit. - if (!matchAtLeast(entry, kLutronTick, 0, kLutronDelta)) { - DPRINTLN("Entry too small. Aborting."); - return false; - } - // Keep reading bits of the same value until we run out. - while (entry != 0 && matchAtLeast(entry, kLutronTick, 0, kLutronDelta)) { - bitsSoFar++; - DPRINT("Bit: "); - DPRINT(bitsSoFar); - if (offset % 2) { // Is Odd? - data = (data << 1) + 1; // Append a '1'. - DPRINTLN(" is a 1."); - } else { // Is it Even? - data <<= 1; // Append a '0'. - DPRINTLN(" is a 0."); - if (bitsSoFar == nbits && matchAtLeast(entry, kLutronGap)) - break; // We've likely reached the end of a message. - } - // Remove a bit length from the current entry. - entry = std::max(entry, (uint16_t)(kLutronTick / kRawTick)) - - kLutronTick / kRawTick; - } - if (offset % 2 && !match(entry, kLutronDelta, 0, kLutronDelta)) { - DPRINT("offset = "); - DPRINTLN(offset); - DPRINT("rawlen = "); - DPRINTLN(results->rawlen); - DPRINT("entry = "); - DPRINTLN(entry); - DPRINTLN("Odd Entry has too much left over. Aborting."); - return false; // Too much left over to be a good value. Reject it. - } - if (offset % 2 == 0 && offset <= results->rawlen - 1 && - !matchAtLeast(entry, kLutronDelta, 0, kLutronDelta)) { - DPRINT("offset = "); - DPRINTLN(offset); - DPRINT("rawlen = "); - DPRINTLN(results->rawlen); - DPRINT("entry = "); - DPRINTLN(entry); - DPRINTLN("Entry has too much left over. Aborting."); - return false; // Too much left over to be a good value. Reject it. - } - } - - // We got too many bits. - if (bitsSoFar > nbits || bitsSoFar < 0) { - DPRINTLN("Wrong number of bits found. Aborting."); - return false; - } - // If we got less bits than we were expecting, we need to pad with zeros - // until we get the correct number of bits. - if (bitsSoFar < nbits) data <<= (nbits - bitsSoFar); - - // Success - DPRINTLN("Lutron Success!"); - results->decode_type = LUTRON; - results->bits = bitsSoFar; - results->value = data ^ (1ULL << nbits); // Mask off the initial '1'. - results->address = 0; - results->command = 0; - return true; -} -#endif // DECODE_LUTRON diff --git a/lib/IRremoteESP8266_ID1089/src/ir_MWM.cpp b/lib/IRremoteESP8266_ID1089/src/ir_MWM.cpp deleted file mode 100644 index a75e99e..0000000 --- a/lib/IRremoteESP8266_ID1089/src/ir_MWM.cpp +++ /dev/null @@ -1,202 +0,0 @@ -// Copyright 2018 Brett T. Warden -// derived from ir_Lasertag.cpp, Copyright 2017 David Conran - -#include -#include "IRrecv.h" -#include "IRsend.h" -#include "IRutils.h" - -// MM MM WW WW MM MM -// MMM MMM WW WW MMM MMM -// MM M MM WW W WW MM M MM -// MM MM WWW WWW MM MM -// MM MM WW WW MM MM - -// Constants -const uint16_t kMWMMinSamples = 6; // Msgs are >=3 bytes, bytes have >=2 - // samples -const uint16_t kMWMTick = 417; -const uint32_t kMWMMinGap = 30000; // Typical observed delay b/w commands -const uint8_t kMWMTolerance = 0; // Percentage error margin. -const uint16_t kMWMExcess = 0; // See kMarkExcess. -const uint16_t kMWMDelta = 150; // Use instead of Excess and Tolerance. -const uint8_t kMWMMaxWidth = 9; // Maximum number of successive bits at a - // single level - worst case -const int16_t kSpace = 1; -const int16_t kMark = 0; - -#if SEND_MWM -// Send a MWM packet. -// This protocol is 2400 bps serial, 1 start bit (mark), 1 stop bit (space), no -// parity -// -// Args: -// data: The message you wish to send. -// nbits: Bit size of the protocol you want to send. -// repeat: Nr. of extra times the data will be sent. -// -// Status: Implemented. -// -void IRsend::sendMWM(uint8_t data[], uint16_t nbytes, uint16_t repeat) { - if (nbytes < 3) return; // Shortest possible message is 3 bytes - - // Set 38kHz IR carrier frequency & a 1/4 (25%) duty cycle. - // NOTE: duty cycle is not confirmed. Just guessing based on RC5/6 protocols. - enableIROut(38, 25); - - for (uint16_t r = 0; r <= repeat; r++) { - // Data - for (uint16_t i = 0; i < nbytes; i++) { - uint8_t byte = data[i]; - - // Start bit - mark(kMWMTick); - - // LSB first, space=1 - for (uint8_t mask = 0x1; mask; mask <<= 1) { - if (byte & mask) { // 1 - space(kMWMTick); - } else { // 0 - mark(kMWMTick); - } - } - // Stop bit - space(kMWMTick); - } - // Footer - space(kMWMMinGap); - } -} -#endif // SEND_MWM - -#if DECODE_MWM -// Decode the supplied MWM message. -// This protocol is 2400 bps serial, 1 start bit (mark), 1 stop bit (space), no -// parity -// -// Args: -// results: Ptr to the data to decode and where to store the decode result. -// nbits: The number of data bits to expect. -// strict: Flag indicating if we should perform strict matching. -// Returns: -// boolean: True if it can decode it, false if it can't. -// -// Status: Implemented. -// -bool IRrecv::decodeMWM(decode_results *results, uint16_t nbits, bool strict) { - DPRINTLN("DEBUG: decodeMWM"); - - // Compliance - if (results->rawlen < kMWMMinSamples) { - DPRINTLN("DEBUG: decodeMWM: too few samples"); - return false; - } - - uint16_t offset = kStartOffset; - uint16_t used = 0; - uint64_t data = 0; - uint16_t frame_bits = 0; - uint16_t data_bits = 0; - - // No Header - - // Data - uint8_t bits_per_frame = 10; - for (; offset < results->rawlen && results->bits < 8 * kStateSizeMax; - frame_bits++) { - DPRINT("DEBUG: decodeMWM: offset = "); - DPRINTLN(uint64ToString(offset)); - int16_t level = getRClevel(results, &offset, &used, kMWMTick, kMWMTolerance, - kMWMExcess, kMWMDelta, kMWMMaxWidth); - if (level < 0) { - DPRINTLN("DEBUG: decodeMWM: getRClevel returned error"); - break; - } - switch (frame_bits % bits_per_frame) { - case 0: - // Start bit - if (level != kMark) { - DPRINTLN("DEBUG: decodeMWM: framing error - invalid start bit"); - goto done; - } - break; - case 9: - // Stop bit - if (level != kSpace) { - DPRINTLN("DEBUG: decodeMWM: framing error - invalid stop bit"); - return false; - } else { - DPRINT("DEBUG: decodeMWM: data_bits = "); - DPRINTLN(data_bits); - DPRINT("DEBUG: decodeMWM: Finished byte: "); - DPRINTLN(data); - results->state[data_bits / 8 - 1] = data & 0xFF; - results->bits = data_bits; - data = 0; - } - break; - default: - // Data bits - DPRINT("DEBUG: decodeMWM: Storing bit: "); - DPRINTLN((level == kSpace)); - // Transmission is LSB-first, space=1 - data |= ((level == kSpace)) << 8; - data >>= 1; - data_bits++; - break; - } - } - -done: - // Footer (None) - - // Compliance - DPRINT("DEBUG: decodeMWM: frame_bits = "); - DPRINTLN(frame_bits); - DPRINT("DEBUG: decodeMWM: data_bits = "); - DPRINTLN(data_bits); - if (data_bits < nbits) { - DPRINT("DEBUG: decodeMWM: too few bits; expected "); - DPRINTLN(nbits); - return false; // Less data than we expected. - } - - uint16_t payload_length = 0; - switch (results->state[0] & 0xf0) { - case 0x90: - case 0xf0: - // Normal commands - payload_length = results->state[0] & 0x0f; - DPRINT("DEBUG: decodeMWM: payload_length = "); - DPRINTLN(payload_length); - break; - default: - if (strict) { - // Show commands - if (results->state[0] != 0x55 && results->state[1] != 0xAA) { - return false; - } - } - break; - } - if (data_bits < (payload_length + 3) * 8) { - DPRINT("DEBUG: decodeMWM: too few bytes; expected "); - DPRINTLN((payload_length + 3)); - return false; - } - if (strict) { - if (payload_length && (data_bits > (payload_length + 3) * 8)) { - DPRINT("DEBUG: decodeMWM: too many bytes; expected "); - DPRINTLN((payload_length + 3)); - return false; - } - } - - // Success - results->decode_type = MWM; - results->repeat = false; - return true; -} -#endif // DECODE_MWM - -// vim: et:ts=2:sw=2 diff --git a/lib/IRremoteESP8266_ID1089/src/ir_Magiquest.cpp b/lib/IRremoteESP8266_ID1089/src/ir_Magiquest.cpp deleted file mode 100644 index 863aa0e..0000000 --- a/lib/IRremoteESP8266_ID1089/src/ir_Magiquest.cpp +++ /dev/null @@ -1,166 +0,0 @@ -// Copyright 2013 mpflaga -// Copyright 2015 kitlaan -// Copyright 2017 Jason kendall, David Conran - -#include "ir_Magiquest.h" -#include -#include "IRrecv.h" -#include "IRsend.h" -#include "IRutils.h" - -#define IS_ZERO(m, s) (((m)*100 / ((m) + (s))) <= kMagiQuestZeroRatio) -#define IS_ONE(m, s) (((m)*100 / ((m) + (s))) >= kMagiQuestOneRatio) - -// Strips taken from: -// https://github.com/kitlaan/Arduino-IRremote/blob/master/ir_Magiquest.cpp -// and -// https://github.com/mpflaga/Arduino-IRremote - -// Source: https://github.com/mpflaga/Arduino-IRremote - -#if SEND_MAGIQUEST -// Send a MagiQuest formatted message. -// -// Args: -// data: The contents of the message you want to send. -// nbits: The bit size of the message being sent. -// Typically kMagiquestBits. -// repeat: The number of times you want the message to be repeated. -// -// Status: Alpha / Should be working. -// -void IRsend::sendMagiQuest(uint64_t data, uint16_t nbits, uint16_t repeat) { - sendGeneric(0, 0, // No Headers - Technically it's included in the data. - // i.e. 8 zeros. - kMagiQuestMarkOne, kMagiQuestSpaceOne, kMagiQuestMarkZero, - kMagiQuestSpaceZero, - 0, // No footer mark. - kMagiQuestGap, data, nbits, 36, true, repeat, 50); -} - -// Encode a MagiQuest wand_id, and a magnitude into a single 64bit value. -// (Only 48 bits of real data + 8 leading zero bits) -// This is suitable for calling sendMagiQuest() with. -// e.g. sendMagiQuest(encodeMagiQuest(wand_id, magnitude)); -uint64_t IRsend::encodeMagiQuest(uint32_t wand_id, uint16_t magnitude) { - uint64_t result = 0; - result = wand_id; - result <<= 16; - result |= magnitude; - // Shouldn't be needed, but ensure top 8/16 bit are zero. - result &= 0xFFFFFFFFFFFFULL; - return result; -} -#endif - -// Source: -// https://github.com/kitlaan/Arduino-IRremote/blob/master/ir_Magiquest.cpp - -#if DECODE_MAGIQUEST -// Decode the supplied MagiQuest message. -// MagiQuest protocol appears to be a header of 8 'zero' bits, followed -// by 32 bits of "wand ID" and finally 16 bits of "magnitude". -// Even though we describe this protocol as 56 bits, it really only has -// 48 bits of data that matter. -// -// In transmission order, 8 zeros + 32 wand_id + 16 magnitude. -// -// Args: -// results: Ptr to the data to decode and where to store the decode result. -// nbits: Nr. of bits to expect in the data portion, inc. the 8 bit header. -// Typically kMagiquestBits. -// strict: Flag to indicate if we strictly adhere to the specification. -// Returns: -// boolean: True if it can decode it, false if it can't. -// -// Status: Alpha / Should work. -// -// Ref: -// https://github.com/kitlaan/Arduino-IRremote/blob/master/ir_Magiquest.cpp -bool IRrecv::decodeMagiQuest(decode_results *results, uint16_t nbits, - bool strict) { - uint16_t bits = 0; - uint64_t data = 0; - uint16_t offset = kStartOffset; - - if (results->rawlen < (2 * kMagiquestBits)) { - DPRINT("Not enough bits to be Magiquest - Rawlen: "); - DPRINT(results->rawlen); - DPRINT(" Expected: "); - DPRINTLN((2 * kMagiquestBits)); - return false; - } - - // Compliance - if (strict && nbits != kMagiquestBits) return false; - - // Of six wands as datapoints, so far they all start with 8 ZEROs. - // For example, here is the data from two wands - // 00000000 00100011 01001100 00100110 00000010 00000010 00010111 - // 00000000 00100000 10001000 00110001 00000010 00000010 10110100 - - // Decode the (MARK + SPACE) bits - while (offset + 1 < results->rawlen && bits < nbits - 1) { - uint16_t mark = results->rawbuf[offset]; - uint16_t space = results->rawbuf[offset + 1]; - if (!matchMark(mark + space, kMagiQuestTotalUsec)) { - DPRINT("Not enough time to be Magiquest - Mark: "); - DPRINT(mark); - DPRINT(" Space: "); - DPRINT(space); - DPRINT(" Total: "); - DPRINT(mark + space); - DPRINT("Expected: "); - DPRINTLN(kMagiQuestTotalUsec); - return false; - } - - if (IS_ZERO(mark, space)) - data = (data << 1) | 0; - else if (IS_ONE(mark, space)) - data = (data << 1) | 1; - else - return false; - - bits++; - offset += 2; - - // Compliance - // The first 8 bits of this protocol are supposed to all be 0. - // Exit out early as it is never going to match. - if (strict && bits == 8 && data != 0) return false; - } - - // Last bit is special as the protocol ends with a SPACE, not a MARK. - // Grab the last MARK bit, assuming a good SPACE after it - if (offset < results->rawlen) { - uint16_t mark = results->rawbuf[offset]; - uint16_t space = (kMagiQuestTotalUsec / kRawTick) - mark; - - if (IS_ZERO(mark, space)) - data = (data << 1) | 0; - else if (IS_ONE(mark, space)) - data = (data << 1) | 1; - else - return false; - - bits++; - } - - if (bits != nbits) return false; - - if (strict) { - // The top 8 bits of the 56 bits needs to be 0x00 to be valid. - // i.e. bits 56 to 49 are all zero. - if ((data >> (nbits - 8)) != 0) return false; - } - - // Success - results->decode_type = MAGIQUEST; - results->bits = bits; - results->value = data; - results->address = data >> 16; // Wand ID - results->command = data & 0xFFFF; // Magnitude - return true; -} -#endif diff --git a/lib/IRremoteESP8266_ID1089/src/ir_Magiquest.h b/lib/IRremoteESP8266_ID1089/src/ir_Magiquest.h deleted file mode 100644 index d2d82d1..0000000 --- a/lib/IRremoteESP8266_ID1089/src/ir_Magiquest.h +++ /dev/null @@ -1,35 +0,0 @@ -// Copyright 2013 mpflaga -// Copyright 2015 kitlaan -// Copyright 2017 Jason kendall, David Conran - -#ifndef IR_MAGIQUEST_H_ -#define IR_MAGIQUEST_H_ - -#define __STDC_LIMIT_MACROS -#include -#include "IRremoteESP8266.h" -#include "IRsend.h" - -// MagiQuest packet is both Wand ID and magnitude of swish and flick -union magiquest { - uint64_t llword; - uint8_t byte[8]; - // uint16_t word[4]; - uint32_t lword[2]; - struct { - uint16_t magnitude; - uint32_t wand_id; - uint8_t padding; - uint8_t scrap; - } cmd; -}; - -const uint16_t kMagiQuestTotalUsec = 1150; -const uint8_t kMagiQuestZeroRatio = 30; // usually <= ~25% -const uint8_t kMagiQuestOneRatio = 38; // usually >= ~50% -const uint16_t kMagiQuestMarkZero = 280; -const uint16_t kMagiQuestSpaceZero = 850; -const uint16_t kMagiQuestMarkOne = 580; -const uint16_t kMagiQuestSpaceOne = 600; -const uint32_t kMagiQuestGap = 100000; // A guess of the gap between messages -#endif // IR_MAGIQUEST_H_ diff --git a/lib/IRremoteESP8266_ID1089/src/ir_Midea.cpp b/lib/IRremoteESP8266_ID1089/src/ir_Midea.cpp deleted file mode 100644 index af40fcc..0000000 --- a/lib/IRremoteESP8266_ID1089/src/ir_Midea.cpp +++ /dev/null @@ -1,403 +0,0 @@ -// Copyright 2017 bwze, crankyoldgit - -#include "ir_Midea.h" -#include -#ifndef ARDUINO -#include -#endif -#include "IRrecv.h" -#include "IRsend.h" -#include "IRutils.h" - -// MM MM IIIII DDDDD EEEEEEE AAA -// MMM MMM III DD DD EE AAAAA -// MM MM MM III DD DD EEEEE AA AA -// MM MM III DD DD EE AAAAAAA -// MM MM IIIII DDDDDD EEEEEEE AA AA - -// Midea A/C added by (send) bwze/crankyoldgit & (decode) crankyoldgit -// -// Equipment it seems compatible with: -// * Pioneer System Model RYBO12GMFILCAD (12K BTU) -// * Pioneer System Model RUBO18GMFILCAD (18K BTU) -// * - -// Ref: -// https://docs.google.com/spreadsheets/d/1TZh4jWrx4h9zzpYUI9aYXMl1fYOiqu-xVuOOMqagxrs/edit?usp=sharing - -// Constants -const uint16_t kMideaTick = 80; -const uint16_t kMideaBitMarkTicks = 7; -const uint16_t kMideaBitMark = kMideaBitMarkTicks * kMideaTick; -const uint16_t kMideaOneSpaceTicks = 21; -const uint16_t kMideaOneSpace = kMideaOneSpaceTicks * kMideaTick; -const uint16_t kMideaZeroSpaceTicks = 7; -const uint16_t kMideaZeroSpace = kMideaZeroSpaceTicks * kMideaTick; -const uint16_t kMideaHdrMarkTicks = 56; -const uint16_t kMideaHdrMark = kMideaHdrMarkTicks * kMideaTick; -const uint16_t kMideaHdrSpaceTicks = 56; -const uint16_t kMideaHdrSpace = kMideaHdrSpaceTicks * kMideaTick; -const uint16_t kMideaMinGapTicks = - kMideaHdrMarkTicks + kMideaZeroSpaceTicks + kMideaBitMarkTicks; -const uint16_t kMideaMinGap = kMideaMinGapTicks * kMideaTick; -const uint8_t kMideaTolerance = 30; // Percent - -#if SEND_MIDEA -// Send a Midea message -// -// Args: -// data: Contents of the message to be sent. -// nbits: Nr. of bits of data to be sent. Typically kMideaBits. -// repeat: Nr. of additional times the message is to be sent. -// -// Status: Alpha / Needs testing against a real device. -// -void IRsend::sendMidea(uint64_t data, uint16_t nbits, uint16_t repeat) { - if (nbits % 8 != 0) return; // nbits is required to be a multiple of 8. - - // Set IR carrier frequency - enableIROut(38); - - for (uint16_t r = 0; r <= repeat; r++) { - // The protcol sends the message, then follows up with an entirely - // inverted payload. - for (size_t inner_loop = 0; inner_loop < 2; inner_loop++) { - // Header - mark(kMideaHdrMark); - space(kMideaHdrSpace); - // Data - // Break data into byte segments, starting at the Most Significant - // Byte. Each byte then being sent normal, then followed inverted. - for (uint16_t i = 8; i <= nbits; i += 8) { - // Grab a bytes worth of data. - uint8_t segment = (data >> (nbits - i)) & 0xFF; - sendData(kMideaBitMark, kMideaOneSpace, kMideaBitMark, kMideaZeroSpace, - segment, 8, true); - } - // Footer - mark(kMideaBitMark); - space(kMideaMinGap); // Pause before repeating - - // Invert the data for the 2nd phase of the message. - // As we get called twice in the inner loop, we will always revert - // to the original 'data' state. - data = ~data; - } - } -} -#endif - -// Code to emulate Midea A/C IR remote control unit. -// Warning: Consider this very alpha code. - -// Initialise the object. -IRMideaAC::IRMideaAC(uint16_t pin) : _irsend(pin) { stateReset(); } - -// Reset the state of the remote to a known good state/sequence. -void IRMideaAC::stateReset() { - // Power On, Mode Auto, Fan Auto, Temp = 25C/77F - remote_state = 0xA1826FFFFF62; -} - -// Configure the pin for output. -void IRMideaAC::begin() { _irsend.begin(); } - -#if SEND_MIDEA -// Send the current desired state to the IR LED. -void IRMideaAC::send(const uint16_t repeat) { - checksum(); // Ensure correct checksum before sending. - _irsend.sendMidea(remote_state, kMideaBits, repeat); -} -#endif // SEND_MIDEA - -// Return a pointer to the internal state date of the remote. -uint64_t IRMideaAC::getRaw() { - checksum(); - return remote_state & kMideaACStateMask; -} - -// Override the internal state with the new state. -void IRMideaAC::setRaw(uint64_t newState) { - remote_state = newState & kMideaACStateMask; -} - -// Set the requested power state of the A/C to off. -void IRMideaAC::on() { remote_state |= kMideaACPower; } - -// Set the requested power state of the A/C to off. -void IRMideaAC::off() { remote_state &= (kMideaACStateMask ^ kMideaACPower); } - -// Set the requested power state of the A/C. -void IRMideaAC::setPower(const bool state) { - if (state) - on(); - else - off(); -} - -// Return the requested power state of the A/C. -bool IRMideaAC::getPower() { return (remote_state & kMideaACPower); } - -// Set the temperature. -// Args: -// temp: Temp. in degrees. -// useCelsius: Degree type to use. Celsius (true) or Fahrenheit (false) -void IRMideaAC::setTemp(const uint8_t temp, const bool useCelsius) { - uint8_t new_temp = temp; - if (useCelsius) { - new_temp = std::max(kMideaACMinTempC, new_temp); - new_temp = std::min(kMideaACMaxTempC, new_temp); - new_temp = (uint8_t)((new_temp * 1.8) + 32.5); // 0.5 so we rounding. - } - new_temp = std::max(kMideaACMinTempF, new_temp); - new_temp = std::min(kMideaACMaxTempF, new_temp); - new_temp -= kMideaACMinTempF; - remote_state &= kMideaACTempMask; - remote_state |= ((uint64_t)new_temp << 24); -} - -// Return the set temp. -// Args: -// useCelsius: Flag indicating if the results are in Celsius or Fahrenheit. -// Returns: -// A uint8_t containing the temperature. -uint8_t IRMideaAC::getTemp(const bool useCelsius) { - uint8_t temp = ((remote_state >> 24) & 0x1F) + kMideaACMinTempF; - if (useCelsius) { - temp = (uint8_t)((temp - 32) / 1.8); - } - return temp; -} - -// Set the speed of the fan, -// 1-3 set the speed, 0 or anything else set it to auto. -void IRMideaAC::setFan(const uint8_t fan) { - uint64_t new_fan; - switch (fan) { - case kMideaACFanLow: - case kMideaACFanMed: - case kMideaACFanHigh: - new_fan = fan; - break; - default: - new_fan = kMideaACFanAuto; - } - remote_state &= kMideaACFanMask; - remote_state |= (new_fan << 35); -} - -// Return the requested state of the unit's fan. -uint8_t IRMideaAC::getFan() { return (remote_state >> 35) & 0b111; } - -// Get the requested climate operation mode of the a/c unit. -// Returns: -// A uint8_t containing the A/C mode. -uint8_t IRMideaAC::getMode() { return ((remote_state >> 32) & 0b111); } - -// Set the requested climate operation mode of the a/c unit. -void IRMideaAC::setMode(const uint8_t mode) { - // If we get an unexpected mode, default to AUTO. - uint64_t new_mode; - switch (mode) { - case kMideaACAuto: - case kMideaACCool: - case kMideaACHeat: - case kMideaACDry: - case kMideaACFan: - new_mode = mode; - break; - default: - new_mode = kMideaACAuto; - } - remote_state &= kMideaACModeMask; - remote_state |= (new_mode << 32); -} - -// Set the Sleep state of the A/C. -void IRMideaAC::setSleep(const bool state) { - if (state) - remote_state |= kMideaACSleep; - else - remote_state &= (kMideaACStateMask ^ kMideaACSleep); -} - -// Return the Sleep state of the A/C. -bool IRMideaAC::getSleep() { return (remote_state & kMideaACSleep); } - -// Calculate the checksum for a given array. -// Args: -// state: The state to calculate the checksum over. -// Returns: -// The 8 bit checksum value. -uint8_t IRMideaAC::calcChecksum(const uint64_t state) { - uint8_t sum = 0; - uint64_t temp_state = state; - - for (uint8_t i = 0; i < 5; i++) { - temp_state >>= 8; - sum += reverseBits((temp_state & 0xFF), 8); - } - sum = 256 - sum; - return reverseBits(sum, 8); -} - -// Verify the checksum is valid for a given state. -// Args: -// state: The state to verify the checksum of. -// Returns: -// A boolean. -bool IRMideaAC::validChecksum(const uint64_t state) { - return ((state & 0xFF) == calcChecksum(state)); -} - -// Calculate & set the checksum for the current internal state of the remote. -void IRMideaAC::checksum() { - // Stored the checksum value in the last byte. - remote_state &= kMideaACChecksumMask; - remote_state |= calcChecksum(remote_state); -} - -// Convert the internal state into a human readable string. -#ifdef ARDUINO -String IRMideaAC::toString() { - String result = ""; -#else -std::string IRMideaAC::toString() { - std::string result = ""; -#endif // ARDUINO - result += "Power: "; - if (getPower()) - result += "On"; - else - result += "Off"; - result += ", Mode: " + uint64ToString(getMode()); - switch (getMode()) { - case kMideaACAuto: - result += " (AUTO)"; - break; - case kMideaACCool: - result += " (COOL)"; - break; - case kMideaACHeat: - result += " (HEAT)"; - break; - case kMideaACDry: - result += " (DRY)"; - break; - case kMideaACFan: - result += " (FAN)"; - break; - default: - result += " (UNKNOWN)"; - } - result += ", Temp: " + uint64ToString(getTemp(true)) + "C/" + - uint64ToString(getTemp(false)) + "F"; - result += ", Fan: " + uint64ToString(getFan()); - switch (getFan()) { - case kMideaACFanAuto: - result += " (AUTO)"; - break; - case kMideaACFanLow: - result += " (LOW)"; - break; - case kMideaACFanMed: - result += " (MED)"; - break; - case kMideaACFanHigh: - result += " (HI)"; - break; - } - result += ", Sleep: "; - if (getSleep()) - result += "On"; - else - result += "Off"; - return result; -} - -#if DECODE_MIDEA -// Decode the supplied Midea message. -// -// Args: -// results: Ptr to the data to decode and where to store the decode result. -// nbits: The number of data bits to expect. Typically kMideaBits. -// strict: Flag indicating if we should perform strict matching. -// Returns: -// boolean: True if it can decode it, false if it can't. -// -// Status: Alpha / Needs testing against a real device. -// -bool IRrecv::decodeMidea(decode_results *results, uint16_t nbits, bool strict) { - if (nbits % 8 != 0) // nbits has to be a multiple of nr. of bits in a byte. - return false; - - uint8_t min_nr_of_messages = 1; - if (strict) { - if (nbits != kMideaBits) return false; // Not strictly a MIDEA message. - min_nr_of_messages = 2; - } - - // The protocol sends the data normal + inverted, alternating on - // each byte. Hence twice the number of expected data bits. - if (results->rawlen < - min_nr_of_messages * (2 * nbits + kHeader + kFooter) - 1) - return false; // Can't possibly be a valid MIDEA message. - - uint64_t data = 0; - uint64_t inverted = 0; - uint16_t offset = kStartOffset; - - if (nbits > sizeof(data) * 8) - return false; // We can't possibly capture a Midea packet that big. - - for (uint8_t i = 0; i < min_nr_of_messages; i++) { - // Header - if (!matchMark(results->rawbuf[offset], kMideaHdrMark)) return false; - // Calculate how long the common tick time is based on the header mark. - uint32_t m_tick = results->rawbuf[offset++] * kRawTick / kMideaHdrMarkTicks; - if (!matchSpace(results->rawbuf[offset], kMideaHdrSpace)) return false; - // Calculate how long the common tick time is based on the header space. - uint32_t s_tick = - results->rawbuf[offset++] * kRawTick / kMideaHdrSpaceTicks; - - // Data (Normal) - match_result_t data_result = matchData( - &(results->rawbuf[offset]), nbits, kMideaBitMarkTicks * m_tick, - kMideaOneSpaceTicks * s_tick, kMideaBitMarkTicks * m_tick, - kMideaZeroSpaceTicks * s_tick, kMideaTolerance); - if (data_result.success == false) return false; - offset += data_result.used; - if (i % 2 == 0) - data = data_result.data; - else - inverted = data_result.data; - - // Footer - if (!matchMark(results->rawbuf[offset++], kMideaBitMarkTicks * m_tick, - kMideaTolerance)) - return false; - if (offset < results->rawlen && - !matchAtLeast(results->rawbuf[offset++], kMideaMinGapTicks * s_tick, - kMideaTolerance)) - return false; - } - - // Compliance - if (strict) { - // Protocol requires a second message with all the data bits inverted. - // We should have checked we got a second message in the previous loop. - // Just need to check it's value is an inverted copy of the first message. - uint64_t mask = (1ULL << kMideaBits) - 1; - if ((data & mask) != ((inverted ^ mask) & mask)) return false; - if (!IRMideaAC::validChecksum(data)) return false; - } - - // Success - results->decode_type = MIDEA; - results->bits = nbits; - results->value = data; - results->address = 0; - results->command = 0; - return true; -} -#endif // DECODE_MIDEA diff --git a/lib/IRremoteESP8266_ID1089/src/ir_Midea.h b/lib/IRremoteESP8266_ID1089/src/ir_Midea.h deleted file mode 100644 index c749b32..0000000 --- a/lib/IRremoteESP8266_ID1089/src/ir_Midea.h +++ /dev/null @@ -1,103 +0,0 @@ -// Copyright 2017 David Conran -#ifndef IR_MIDEA_H_ -#define IR_MIDEA_H_ - -#define __STDC_LIMIT_MACROS -#include -#ifdef ARDUINO -#include -#else -#include -#endif -#include "IRremoteESP8266.h" -#include "IRsend.h" - -// MM MM IIIII DDDDD EEEEEEE AAA -// MMM MMM III DD DD EE AAAAA -// MM MM MM III DD DD EEEEE AA AA -// MM MM III DD DD EE AAAAAAA -// MM MM IIIII DDDDDD EEEEEEE AA AA - -// Midea added by crankyoldgit & bwze -// Ref: -// https://docs.google.com/spreadsheets/d/1TZh4jWrx4h9zzpYUI9aYXMl1fYOiqu-xVuOOMqagxrs/edit?usp=sharing - -// Constants -const uint8_t kMideaACCool = 0; // 0b000 -const uint8_t kMideaACDry = 1; // 0b001 -const uint8_t kMideaACAuto = 2; // 0b010 -const uint8_t kMideaACHeat = 3; // 0b011 -const uint8_t kMideaACFan = 4; // 0b100 -const uint8_t kMideaACFanAuto = 0; // 0b000 -const uint8_t kMideaACFanLow = 1; // 0b001 -const uint8_t kMideaACFanMed = 2; // 0b010 -const uint8_t kMideaACFanHigh = 3; // 0b011 -const uint64_t kMideaACPower = 1ULL << 39; -const uint64_t kMideaACSleep = 1ULL << 38; -const uint8_t kMideaACMinTempF = 62; // Fahrenheit -const uint8_t kMideaACMaxTempF = 86; // Fahrenheit -const uint8_t kMideaACMinTempC = 16; // Celsius -const uint8_t kMideaACMaxTempC = 30; // Celsius -const uint64_t kMideaACStateMask = 0x0000FFFFFFFFFFFF; -const uint64_t kMideaACTempMask = 0x0000FFFFE0FFFFFF; -const uint64_t kMideaACFanMask = 0x0000FFC7FFFFFFFF; -const uint64_t kMideaACModeMask = 0x0000FFF8FFFFFFFF; -const uint64_t kMideaACChecksumMask = 0x0000FFFFFFFFFF00; - -// Legacy defines. (Deprecated) -#define MIDEA_AC_COOL kMideaACCool -#define MIDEA_AC_DRY kMideaACDry -#define MIDEA_AC_AUTO kMideaACAuto -#define MIDEA_AC_HEAT kMideaACHeat -#define MIDEA_AC_FAN kMideaACFan -#define MIDEA_AC_FAN_AUTO kMideaACFanAuto -#define MIDEA_AC_FAN_LOW kMideaACFanLow -#define MIDEA_AC_FAN_MED kMideaACFanMed -#define MIDEA_AC_FAN_HI kMideaACFanHigh -#define MIDEA_AC_POWER kMideaACPower -#define MIDEA_AC_SLEEP kMideaACSleep -#define MIDEA_AC_MIN_TEMP_F kMideaACMinTempF -#define MIDEA_AC_MAX_TEMP_F kMideaACMaxTempF -#define MIDEA_AC_MIN_TEMP_C kMideaACMinTempC -#define MIDEA_AC_MAX_TEMP_C kMideaACMaxTempC - -class IRMideaAC { - public: - explicit IRMideaAC(uint16_t pin); - - void stateReset(); -#if SEND_MIDEA - void send(const uint16_t repeat = kMideaMinRepeat); -#endif // SEND_MIDEA - void begin(); - void on(); - void off(); - void setPower(const bool state); - bool getPower(); - void setTemp(const uint8_t temp, const bool useCelsius = false); - uint8_t getTemp(const bool useCelsius = false); - void setFan(const uint8_t fan); - uint8_t getFan(); - void setMode(const uint8_t mode); - uint8_t getMode(); - void setRaw(uint64_t newState); - uint64_t getRaw(); - static bool validChecksum(const uint64_t state); - void setSleep(const bool state); - bool getSleep(); -#ifdef ARDUINO - String toString(); -#else - std::string toString(); -#endif -#ifndef UNIT_TEST - - private: -#endif - uint64_t remote_state; - void checksum(); - static uint8_t calcChecksum(const uint64_t state); - IRsend _irsend; -}; - -#endif // IR_MIDEA_H_ diff --git a/lib/IRremoteESP8266_ID1089/src/ir_Mitsubishi.cpp b/lib/IRremoteESP8266_ID1089/src/ir_Mitsubishi.cpp deleted file mode 100644 index 655dc5b..0000000 --- a/lib/IRremoteESP8266_ID1089/src/ir_Mitsubishi.cpp +++ /dev/null @@ -1,714 +0,0 @@ -// Copyright 2009 Ken Shirriff -// Copyright 2017-2018 David Conran -// Copyright 2018 Denes Varga - -#include "ir_Mitsubishi.h" -#include -#ifndef ARDUINO -#include -#endif -#include "IRrecv.h" -#include "IRsend.h" -#include "IRutils.h" - -// MMMMM IIIII TTTTT SSSS U U BBBB IIIII SSSS H H IIIII -// M M M I T S U U B B I S H H I -// M M M I T SSS U U BBBB I SSS HHHHH I -// M M I T S U U B B I S H H I -// M M IIIII T SSSS UUU BBBBB IIIII SSSS H H IIIII - -// Mitsubishi (TV) decoding added from https://github.com/z3t0/Arduino-IRremote -// Mitsubishi (TV) sending & Mitsubishi A/C support added by David Conran - -// Constants -// Mitsubishi TV -// period time is 1/33000Hz = 30.303 uSeconds (T) -// Ref: -// GlobalCache's Control Tower's Mitsubishi TV data. -// https://github.com/marcosamarinho/IRremoteESP8266/blob/master/ir_Mitsubishi.cpp -const uint16_t kMitsubishiTick = 30; -const uint16_t kMitsubishiBitMarkTicks = 10; -const uint16_t kMitsubishiBitMark = kMitsubishiBitMarkTicks * kMitsubishiTick; -const uint16_t kMitsubishiOneSpaceTicks = 70; -const uint16_t kMitsubishiOneSpace = kMitsubishiOneSpaceTicks * kMitsubishiTick; -const uint16_t kMitsubishiZeroSpaceTicks = 30; -const uint16_t kMitsubishiZeroSpace = - kMitsubishiZeroSpaceTicks * kMitsubishiTick; -const uint16_t kMitsubishiMinCommandLengthTicks = 1786; -const uint16_t kMitsubishiMinCommandLength = - kMitsubishiMinCommandLengthTicks * kMitsubishiTick; -const uint16_t kMitsubishiMinGapTicks = 936; -const uint16_t kMitsubishiMinGap = kMitsubishiMinGapTicks * kMitsubishiTick; - -// Mitsubishi Projector (HC3000) -// Ref: -// https://github.com/markszabo/IRremoteESP8266/issues/441 - -const uint16_t kMitsubishi2HdrMark = 8400; -const uint16_t kMitsubishi2HdrSpace = kMitsubishi2HdrMark / 2; -const uint16_t kMitsubishi2BitMark = 560; -const uint16_t kMitsubishi2ZeroSpace = 520; -const uint16_t kMitsubishi2OneSpace = kMitsubishi2ZeroSpace * 3; -const uint16_t kMitsubishi2MinGap = 28500; - -// Mitsubishi A/C -// Ref: -// https://github.com/r45635/HVAC-IR-Control/blob/master/HVAC_ESP8266/HVAC_ESP8266.ino#L84 - -const uint16_t kMitsubishiAcHdrMark = 3400; -const uint16_t kMitsubishiAcHdrSpace = 1750; -const uint16_t kMitsubishiAcBitMark = 450; -const uint16_t kMitsubishiAcOneSpace = 1300; -const uint16_t kMitsubishiAcZeroSpace = 420; -const uint16_t kMitsubishiAcRptMark = 440; -const uint16_t kMitsubishiAcRptSpace = 17100; - -#if SEND_MITSUBISHI -// Send a Mitsubishi message -// -// Args: -// data: Contents of the message to be sent. -// nbits: Nr. of bits of data to be sent. Typically kMitsubishiBits. -// repeat: Nr. of additional times the message is to be sent. -// -// Status: ALPHA / untested. -// -// Notes: -// This protocol appears to have no header. -// Ref: -// https://github.com/marcosamarinho/IRremoteESP8266/blob/master/ir_Mitsubishi.cpp -// GlobalCache's Control Tower's Mitsubishi TV data. -void IRsend::sendMitsubishi(uint64_t data, uint16_t nbits, uint16_t repeat) { - sendGeneric(0, 0, // No Header - kMitsubishiBitMark, kMitsubishiOneSpace, kMitsubishiBitMark, - kMitsubishiZeroSpace, kMitsubishiBitMark, kMitsubishiMinGap, - kMitsubishiMinCommandLength, data, nbits, 33, true, repeat, 50); -} -#endif // SEND_MITSUBISHI - -#if DECODE_MITSUBISHI -// Decode the supplied Mitsubishi message. -// -// Args: -// results: Ptr to the data to decode and where to store the decode result. -// nbits: Nr. of data bits to expect. -// strict: Flag indicating if we should perform strict matching. -// Returns: -// boolean: True if it can decode it, false if it can't. -// -// Status: BETA / previously working. -// -// Notes: -// This protocol appears to have no header. -// -// Ref: -// GlobalCache's Control Tower's Mitsubishi TV data. -bool IRrecv::decodeMitsubishi(decode_results *results, uint16_t nbits, - bool strict) { - if (results->rawlen < 2 * nbits + kFooter - 1) - return false; // Shorter than shortest possibly expected. - if (strict && nbits != kMitsubishiBits) - return false; // Request is out of spec. - - uint16_t offset = kStartOffset; - uint64_t data = 0; - - // No Header - // But try to auto-calibrate off the initial mark signal. - if (!matchMark(results->rawbuf[offset], kMitsubishiBitMark, 30)) return false; - // Calculate how long the common tick time is based on the initial mark. - uint32_t tick = results->rawbuf[offset] * kRawTick / kMitsubishiBitMarkTicks; - - // Data - match_result_t data_result = matchData( - &(results->rawbuf[offset]), nbits, kMitsubishiBitMarkTicks * tick, - kMitsubishiOneSpaceTicks * tick, kMitsubishiBitMarkTicks * tick, - kMitsubishiZeroSpaceTicks * tick); - if (data_result.success == false) return false; - data = data_result.data; - offset += data_result.used; - uint16_t actualBits = data_result.used / 2; - - // Footer - if (!matchMark(results->rawbuf[offset++], kMitsubishiBitMarkTicks * tick, 30)) - return false; - if (offset < results->rawlen && - !matchAtLeast(results->rawbuf[offset], kMitsubishiMinGapTicks * tick)) - return false; - - // Compliance - if (actualBits < nbits) return false; - if (strict && actualBits != nbits) return false; // Not as we expected. - - // Success - results->decode_type = MITSUBISHI; - results->bits = actualBits; - results->value = data; - results->address = 0; - results->command = 0; - return true; -} -#endif // DECODE_MITSUBISHI - -#if SEND_MITSUBISHI2 -// Send a Mitsubishi2 message -// -// Args: -// data: Contents of the message to be sent. -// nbits: Nr. of bits of data to be sent. Typically kMitsubishiBits. -// repeat: Nr. of additional times the message is to be sent. -// -// Status: ALPHA / untested. -// -// Notes: -// Based on a Mitsubishi HC3000 projector's remote. -// This protocol appears to have a manditory in-protocol repeat. -// That is in *addition* to the entire message needing to be sent twice -// for the device to accept the command. That is separate from the repeat. -// i.e. Allegedly, the real remote requires the "OFF" button pressed twice. -// You will need to add a suitable gap yourself. -// Ref: -// https://github.com/markszabo/IRremoteESP8266/issues/441 -void IRsend::sendMitsubishi2(uint64_t data, uint16_t nbits, uint16_t repeat) { - for (uint16_t i = 0; i <= repeat; i++) { - // First half of the data. - sendGeneric(kMitsubishi2HdrMark, kMitsubishi2HdrSpace, kMitsubishi2BitMark, - kMitsubishi2OneSpace, kMitsubishi2BitMark, - kMitsubishi2ZeroSpace, kMitsubishi2BitMark, - kMitsubishi2HdrSpace, data >> (nbits / 2), nbits / 2, 33, true, - 0, 50); - // Second half of the data. - sendGeneric(0, 0, // No header for the second data block - kMitsubishi2BitMark, kMitsubishi2OneSpace, kMitsubishi2BitMark, - kMitsubishi2ZeroSpace, kMitsubishi2BitMark, kMitsubishi2MinGap, - data & ((1 << (nbits / 2)) - 1), nbits / 2, 33, true, 0, 50); - } -} -#endif // SEND_MITSUBISHI2 - -#if DECODE_MITSUBISHI2 -// Decode the supplied Mitsubishi2 message. -// -// Args: -// results: Ptr to the data to decode and where to store the decode result. -// nbits: Nr. of data bits to expect. -// strict: Flag indicating if we should perform strict matching. -// Returns: -// boolean: True if it can decode it, false if it can't. -// -// Status: BETA / Works with simulated data. -// -// Notes: -// Hardware supported: -// * Mitsubishi HC3000 projector's remote. -// -// Ref: -// https://github.com/markszabo/IRremoteESP8266/issues/441 -bool IRrecv::decodeMitsubishi2(decode_results *results, uint16_t nbits, - bool strict) { - if (results->rawlen < 2 * nbits + kHeader + (kFooter * 2) - 1) - return false; // Shorter than shortest possibly expected. - if (strict && nbits != kMitsubishiBits) - return false; // Request is out of spec. - - uint16_t offset = kStartOffset; - uint64_t data = 0; - uint16_t actualBits = 0; - - // Header - if (!matchMark(results->rawbuf[offset++], kMitsubishi2HdrMark)) return false; - if (!matchSpace(results->rawbuf[offset++], kMitsubishi2HdrSpace)) - return false; - for (uint8_t i = 1; i <= 2; i++) { - // Data - match_result_t data_result = matchData( - &(results->rawbuf[offset]), nbits / 2, kMitsubishi2BitMark, - kMitsubishi2OneSpace, kMitsubishi2BitMark, kMitsubishi2ZeroSpace); - if (data_result.success == false) return false; - data <<= nbits / 2; - data += data_result.data; - offset += data_result.used; - actualBits += data_result.used / 2; - - // Footer - if (!matchMark(results->rawbuf[offset++], kMitsubishi2BitMark)) - return false; - if (i % 2) { // Every odd data block, we expect a HDR space. - if (!matchSpace(results->rawbuf[offset++], kMitsubishi2HdrSpace)) - return false; - } else { // Every even data block, we expect Min Gap or end of the message. - if (offset < results->rawlen && - !matchAtLeast(results->rawbuf[offset++], kMitsubishi2MinGap)) - return false; - } - } - - // Compliance - if (actualBits < nbits) return false; - if (strict && actualBits != nbits) return false; // Not as we expected. - - // Success - results->decode_type = MITSUBISHI2; - results->bits = actualBits; - results->value = data; - results->address = data >> actualBits / 2; - results->command = data & ((1 << (actualBits / 2)) - 1); - return true; -} -#endif // DECODE_MITSUBISHI2 - -#if SEND_MITSUBISHI_AC -// Send a Mitsubishi A/C message. -// -// Args: -// data: An array of bytes containing the IR command. -// nbytes: Nr. of bytes of data in the array. (>=kMitsubishiACStateLength) -// repeat: Nr. of times the message is to be repeated. -// (Default = kMitsubishiACMinRepeat). -// -// Status: BETA / Appears to be working. -// -void IRsend::sendMitsubishiAC(unsigned char data[], uint16_t nbytes, - uint16_t repeat) { - if (nbytes < kMitsubishiACStateLength) - return; // Not enough bytes to send a proper message. - - sendGeneric(kMitsubishiAcHdrMark, kMitsubishiAcHdrSpace, kMitsubishiAcBitMark, - kMitsubishiAcOneSpace, kMitsubishiAcBitMark, - kMitsubishiAcZeroSpace, kMitsubishiAcRptMark, - kMitsubishiAcRptSpace, data, nbytes, 38, false, repeat, 50); -} -#endif // SEND_MITSUBISHI_AC - -#if DECODE_MITSUBISHI_AC -// Decode the supplied Mitsubishi message. -// -// Args: -// results: Ptr to the data to decode and where to store the decode result. -// nbits: Nr. of data bits to expect. -// strict: Flag indicating if we should perform strict matching. -// Returns: -// boolean: True if it can decode it, false if it can't. -// -// Status: ALPHA / Under development -// -// Ref: -// https://www.analysir.com/blog/2015/01/06/reverse-engineering-mitsubishi-ac-infrared-protocol/ -bool IRrecv::decodeMitsubishiAC(decode_results *results, uint16_t nbits, - bool strict) { - if (results->rawlen < ((kMitsubishiACBits * 2) + 2)) { - DPRINTLN("Shorter than shortest possibly expected."); - return false; // Shorter than shortest possibly expected. - } - if (strict && nbits != kMitsubishiACBits) { - DPRINTLN("Request is out of spec."); - return false; // Request is out of spec. - } - uint16_t offset = kStartOffset; - for (uint8_t i = 0; i < kMitsubishiACStateLength; i++) { - results->state[i] = 0; - } - bool failure = false; - uint8_t rep = 0; - do { - failure = false; - // Header: - // Somtime happens that junk signals arrives before the real message - bool headerFound = false; - while (!headerFound && - offset < (results->rawlen - (kMitsubishiACBits * 2 + 2))) { - headerFound = - matchMark(results->rawbuf[offset++], kMitsubishiAcHdrMark) && - matchSpace(results->rawbuf[offset++], kMitsubishiAcHdrSpace); - } - if (!headerFound) { - DPRINTLN("Header mark not found."); - failure = true; - } - // Decode byte-by-byte: - match_result_t data_result; - for (uint8_t i = 0; i < kMitsubishiACStateLength && !failure; i++) { - results->state[i] = 0; - data_result = - matchData(&(results->rawbuf[offset]), 8, kMitsubishiAcBitMark, - kMitsubishiAcOneSpace, kMitsubishiAcBitMark, - kMitsubishiAcZeroSpace, kTolerance, kMarkExcess, false); - if (data_result.success == false) { - failure = true; - DPRINT("Byte decode failed at #"); - DPRINTLN((uint16_t)i); - } else { - results->state[i] = data_result.data; - offset += data_result.used; - DPRINT((uint16_t)results->state[i]); - DPRINT(","); - } - DPRINTLN(""); - } - // HEADER validation: - if (failure || results->state[0] != 0x23 || results->state[1] != 0xCB || - results->state[2] != 0x26 || results->state[3] != 0x01 || - results->state[4] != 0x00) { - DPRINTLN("Header mismatch."); - failure = true; - } else { - // DATA part: - - // FOOTER checksum: - if (IRMitsubishiAC::calculateChecksum(results->state) != - results->state[kMitsubishiACStateLength - 1]) { - DPRINTLN("Checksum error."); - failure = true; - } - } - if (rep != kMitsubishiACMinRepeat && failure) { - bool repeatMarkFound = false; - while (!repeatMarkFound && - offset < (results->rawlen - (kMitsubishiACBits * 2 + 4))) { - repeatMarkFound = - matchMark(results->rawbuf[offset++], kMitsubishiAcRptMark) && - matchSpace(results->rawbuf[offset++], kMitsubishiAcRptSpace); - } - if (!repeatMarkFound) { - DPRINTLN("First attempt failure and repeat mark not found."); - return false; - } - } - rep++; - // Check if the repeat is correct if we need strict decode: - if (strict && !failure) { - DPRINTLN("Strict repeat check enabled."); - // Repeat mark and space: - if (!matchMark(results->rawbuf[offset++], kMitsubishiAcRptMark) || - !matchSpace(results->rawbuf[offset++], kMitsubishiAcRptSpace)) { - DPRINTLN("Repeat mark error."); - return false; - } - // Header mark and space: - if (!matchMark(results->rawbuf[offset++], kMitsubishiAcHdrMark) || - !matchSpace(results->rawbuf[offset++], kMitsubishiAcHdrSpace)) { - DPRINTLN("Repeat header error."); - return false; - } - // Payload: - for (uint8_t i = 0; i < kMitsubishiACStateLength; i++) { - data_result = - matchData(&(results->rawbuf[offset]), 8, kMitsubishiAcBitMark, - kMitsubishiAcOneSpace, kMitsubishiAcBitMark, - kMitsubishiAcZeroSpace, kTolerance, kMarkExcess, false); - if (data_result.success == false || - data_result.data != results->state[i]) { - DPRINTLN("Repeat payload error."); - return false; - } - offset += data_result.used; - } - } // strict repeat check - } while (failure && rep <= kMitsubishiACMinRepeat); - results->decode_type = MITSUBISHI_AC; - results->bits = kMitsubishiACStateLength * 8; - return true; -} -#endif // DECODE_MITSUBISHI_AC - -// Code to emulate Mitsubishi A/C IR remote control unit. -// Inspired and derived from the work done at: -// https://github.com/r45635/HVAC-IR-Control -// -// Warning: Consider this very alpha code. Seems to work, but not validated. -// -// Equipment it seems compatible with: -// * -// Initialise the object. -IRMitsubishiAC::IRMitsubishiAC(uint16_t pin) : _irsend(pin) { stateReset(); } - -// Reset the state of the remote to a known good state/sequence. -void IRMitsubishiAC::stateReset() { - // The state of the IR remote in IR code form. - // Known good state obtained from: - // https://github.com/r45635/HVAC-IR-Control/blob/master/HVAC_ESP8266/HVAC_ESP8266.ino#L108 - // Note: Can't use the following because it requires -std=c++11 - // uint8_t known_good_state[kMitsubishiACStateLength] = { - // 0x23, 0xCB, 0x26, 0x01, 0x00, 0x20, 0x08, 0x06, 0x30, 0x45, 0x67, 0x00, - // 0x00, 0x00, 0x00, 0x00, 0x00, 0x1F}; - remote_state[0] = 0x23; - remote_state[1] = 0xCB; - remote_state[2] = 0x26; - remote_state[3] = 0x01; - remote_state[4] = 0x00; - remote_state[5] = 0x20; - remote_state[6] = 0x08; - remote_state[7] = 0x06; - remote_state[8] = 0x30; - remote_state[9] = 0x45; - remote_state[10] = 0x67; - for (uint8_t i = 11; i < kMitsubishiACStateLength - 1; i++) - remote_state[i] = 0; - remote_state[kMitsubishiACStateLength - 1] = 0x1F; - checksum(); // Calculate the checksum -} - -// Configure the pin for output. -void IRMitsubishiAC::begin() { _irsend.begin(); } - -#if SEND_MITSUBISHI_AC -// Send the current desired state to the IR LED. -void IRMitsubishiAC::send(const uint16_t repeat) { - checksum(); // Ensure correct checksum before sending. - _irsend.sendMitsubishiAC(remote_state, kMitsubishiACStateLength, repeat); -} -#endif // SEND_MITSUBISHI_AC - -// Return a pointer to the internal state date of the remote. -uint8_t *IRMitsubishiAC::getRaw() { - checksum(); - return remote_state; -} - -void IRMitsubishiAC::setRaw(uint8_t *data) { - for (uint8_t i = 0; i < (kMitsubishiACStateLength - 1); i++) { - remote_state[i] = data[i]; - } - checksum(); -} - -// Calculate the checksum for the current internal state of the remote. -void IRMitsubishiAC::checksum() { - remote_state[17] = calculateChecksum(remote_state); -} - -uint8_t IRMitsubishiAC::calculateChecksum(uint8_t *data) { - uint8_t sum = 0; - // Checksum is simple addition of all previous bytes stored - // as an 8 bit value. - for (uint8_t i = 0; i < 17; i++) sum += data[i]; - return sum & 0xFFU; -} - -// Set the requested power state of the A/C to off. -void IRMitsubishiAC::on() { - // state = ON; - remote_state[5] |= kMitsubishiAcPower; -} - -// Set the requested power state of the A/C to off. -void IRMitsubishiAC::off() { - // state = OFF; - remote_state[5] &= ~kMitsubishiAcPower; -} - -// Set the requested power state of the A/C. -void IRMitsubishiAC::setPower(bool state) { - if (state) - on(); - else - off(); -} - -// Return the requested power state of the A/C. -bool IRMitsubishiAC::getPower() { - return ((remote_state[5] & kMitsubishiAcPower) != 0); -} - -// Set the temp. in deg C -void IRMitsubishiAC::setTemp(uint8_t temp) { - temp = std::max((uint8_t)kMitsubishiAcMinTemp, temp); - temp = std::min((uint8_t)kMitsubishiAcMaxTemp, temp); - remote_state[7] = temp - kMitsubishiAcMinTemp; -} - -// Return the set temp. in deg C -uint8_t IRMitsubishiAC::getTemp() { - return (remote_state[7] + kMitsubishiAcMinTemp); -} - -// Set the speed of the fan, 0-6. -// 0 is auto, 1-5 is the speed, 6 is silent. -void IRMitsubishiAC::setFan(uint8_t fan) { - // Bounds check - if (fan > kMitsubishiAcFanSilent) - fan = kMitsubishiAcFanMax; // Set the fan to maximum if out of range. - if (fan == kMitsubishiAcFanAuto) { // Automatic is a special case. - remote_state[9] = 0b10000000 | (remote_state[9] & 0b01111000); - return; - } else if (fan >= kMitsubishiAcFanMax) { - fan--; // There is no spoon^H^H^Heed 5 (max), pretend it doesn't exist. - } - remote_state[9] &= 0b01111000; // Clear the previous state - remote_state[9] |= fan; -} - -// Return the requested state of the unit's fan. -uint8_t IRMitsubishiAC::getFan() { - uint8_t fan = remote_state[9] & 0b111; - if (fan == kMitsubishiAcFanMax) return kMitsubishiAcFanSilent; - return fan; -} - -// Return the requested climate operation mode of the a/c unit. -uint8_t IRMitsubishiAC::getMode() { return (remote_state[6]); } - -// Set the requested climate operation mode of the a/c unit. -void IRMitsubishiAC::setMode(uint8_t mode) { - // If we get an unexpected mode, default to AUTO. - switch (mode) { - case kMitsubishiAcAuto: - remote_state[8] = 0b00110000; - break; - case kMitsubishiAcCool: - remote_state[8] = 0b00110110; - break; - case kMitsubishiAcDry: - remote_state[8] = 0b00110010; - break; - case kMitsubishiAcHeat: - remote_state[8] = 0b00110000; - break; - default: - mode = kMitsubishiAcAuto; - remote_state[8] = 0b00110000; - } - remote_state[6] = mode; -} - -// Set the requested vane operation mode of the a/c unit. -void IRMitsubishiAC::setVane(uint8_t mode) { - mode = std::min(mode, (uint8_t)0b111); // bounds check - mode |= 0b1000; - mode <<= 3; - remote_state[9] &= 0b11000111; // Clear the previous setting. - remote_state[9] |= mode; -} - -// Return the requested vane operation mode of the a/c unit. -uint8_t IRMitsubishiAC::getVane() { - return ((remote_state[9] & 0b00111000) >> 3); -} - -// Return the clock setting of the message. 1=1/6 hour. e.g. 4pm = 48 -uint8_t IRMitsubishiAC::getClock() { return remote_state[10]; } - -// Set the current time. 1 = 1/6 hour. e.g. 6am = 36. -void IRMitsubishiAC::setClock(uint8_t clock) { remote_state[10] = clock; } - -// Return the desired start time. 1 = 1/6 hour. e.g. 1am = 6 -uint8_t IRMitsubishiAC::getStartClock() { return remote_state[12]; } - -// Set the desired start tiem of the AC. 1 = 1/6 hour. e.g. 8pm = 120 -void IRMitsubishiAC::setStartClock(uint8_t clock) { remote_state[12] = clock; } - -// Return the desired stop time of the AC. 1 = 1/6 hour. e.g 10pm = 132 -uint8_t IRMitsubishiAC::getStopClock() { return remote_state[11]; } - -// Set the desired stop time of the AC. 1 = 1/6 hour. e.g 10pm = 132 -void IRMitsubishiAC::setStopClock(uint8_t clock) { remote_state[11] = clock; } - -// Return the timer setting. Possible values: kMitsubishiAcNoTimer, -// kMitsubishiAcStartTimer, kMitsubishiAcStopTimer, -// kMitsubishiAcStartStopTimer -uint8_t IRMitsubishiAC::getTimer() { return remote_state[13] & 0b111; } - -// Set the timer setting. Possible values: kMitsubishiAcNoTimer, -// kMitsubishiAcStartTimer, kMitsubishiAcStopTimer, -// kMitsubishiAcStartStopTimer -void IRMitsubishiAC::setTimer(uint8_t timer) { - remote_state[13] = timer & 0b111; -} - -#ifdef ARDUINO -String IRMitsubishiAC::timeToString(uint64_t time) { - String result = ""; -#else -std::string IRMitsubishiAC::timeToString(uint64_t time) { - std::string result = ""; -#endif // ARDUINO - if (time / 6 < 10) result += "0"; - result += uint64ToString(time / 6); - result += ":"; - if (time * 10 % 60 < 10) result += "0"; - result += uint64ToString(time * 10 % 60); - return result; -} - -// Convert the internal state into a human readable string. -#ifdef ARDUINO -String IRMitsubishiAC::toString() { - String result = ""; -#else -std::string IRMitsubishiAC::toString() { - std::string result = ""; -#endif // ARDUINO - result += "Power: "; - if (getPower()) - result += "On"; - else - result += "Off"; - switch (getMode()) { - case MITSUBISHI_AC_AUTO: - result += " (AUTO)"; - break; - case MITSUBISHI_AC_COOL: - result += " (COOL)"; - break; - case MITSUBISHI_AC_DRY: - result += " (DRY)"; - break; - case MITSUBISHI_AC_HEAT: - result += " (HEAT)"; - break; - default: - result += " (UNKNOWN)"; - } - result += ", Temp: " + uint64ToString(getTemp()) + "C"; - result += ", FAN: "; - switch (getFan()) { - case MITSUBISHI_AC_FAN_AUTO: - result += "AUTO"; - break; - case MITSUBISHI_AC_FAN_MAX: - result += "MAX"; - break; - case MITSUBISHI_AC_FAN_SILENT: - result += "SILENT"; - break; - default: - result += uint64ToString(getFan()); - } - result += ", VANE: "; - switch (getVane()) { - case MITSUBISHI_AC_VANE_AUTO: - result += "AUTO"; - break; - case MITSUBISHI_AC_VANE_AUTO_MOVE: - result += "AUTO MOVE"; - break; - default: - result += uint64ToString(getVane()); - } - result += ", Time: "; - result += timeToString(getClock()); - result += ", On timer: "; - result += timeToString(getStartClock()); - result += ", Off timer: "; - result += timeToString(getStopClock()); - result += ", Timer: "; - switch (getTimer()) { - case kMitsubishiAcNoTimer: - result += "-"; - break; - case kMitsubishiAcStartTimer: - result += "Start"; - break; - case kMitsubishiAcStopTimer: - result += "Stop"; - break; - case kMitsubishiAcStartStopTimer: - result += "Start+Stop"; - break; - default: - result += "? ("; - result += getTimer(); - result += ")\n"; - } - return result; -} diff --git a/lib/IRremoteESP8266_ID1089/src/ir_Mitsubishi.h b/lib/IRremoteESP8266_ID1089/src/ir_Mitsubishi.h deleted file mode 100644 index 0d897bc..0000000 --- a/lib/IRremoteESP8266_ID1089/src/ir_Mitsubishi.h +++ /dev/null @@ -1,109 +0,0 @@ -// Copyright 2009 Ken Shirriff -// Copyright 2017 David Conran -#ifndef IR_MITSUBISHI_H_ -#define IR_MITSUBISHI_H_ - -#define __STDC_LIMIT_MACROS -#include -#ifndef UNIT_TEST -#include -#else -#include -#endif -#include "IRremoteESP8266.h" -#include "IRsend.h" - -// MMMMM IIIII TTTTT SSSS U U BBBB IIIII SSSS H H IIIII -// M M M I T S U U B B I S H H I -// M M M I T SSS U U BBBB I SSS HHHHH I -// M M I T S U U B B I S H H I -// M M IIIII T SSSS UUU BBBBB IIIII SSSS H H IIIII - -// Mitsubishi (TV) decoding added from https://github.com/z3t0/Arduino-IRremote -// Mitsubishi (TV) sending & Mitsubishi A/C support added by David Conran - -// Constants -const uint8_t kMitsubishiAcAuto = 0x20; -const uint8_t kMitsubishiAcCool = 0x18; -const uint8_t kMitsubishiAcDry = 0x10; -const uint8_t kMitsubishiAcHeat = 0x08; -const uint8_t kMitsubishiAcPower = 0x20; -const uint8_t kMitsubishiAcFanAuto = 0; -const uint8_t kMitsubishiAcFanMax = 5; -const uint8_t kMitsubishiAcFanRealMax = 4; -const uint8_t kMitsubishiAcFanSilent = 6; -const uint8_t kMitsubishiAcMinTemp = 16; // 16C -const uint8_t kMitsubishiAcMaxTemp = 31; // 31C -const uint8_t kMitsubishiAcVaneAuto = 0; -const uint8_t kMitsubishiAcVaneAutoMove = 7; -const uint8_t kMitsubishiAcNoTimer = 0; -const uint8_t kMitsubishiAcStartTimer = 5; -const uint8_t kMitsubishiAcStopTimer = 3; -const uint8_t kMitsubishiAcStartStopTimer = 7; - -// Legacy defines (Deprecated) -#define MITSUBISHI_AC_VANE_AUTO_MOVE kMitsubishiAcVaneAutoMove -#define MITSUBISHI_AC_VANE_AUTO kMitsubishiAcVaneAuto -#define MITSUBISHI_AC_POWER kMitsubishiAcPower -#define MITSUBISHI_AC_MIN_TEMP kMitsubishiAcMinTemp -#define MITSUBISHI_AC_MAX_TEMP kMitsubishiAcMaxTemp -#define MITSUBISHI_AC_HEAT kMitsubishiAcHeat -#define MITSUBISHI_AC_FAN_SILENT kMitsubishiAcFanSilent -#define MITSUBISHI_AC_FAN_REAL_MAX kMitsubishiAcFanRealMax -#define MITSUBISHI_AC_FAN_MAX kMitsubishiAcFanMax -#define MITSUBISHI_AC_FAN_AUTO kMitsubishiAcFanAuto -#define MITSUBISHI_AC_DRY kMitsubishiAcDry -#define MITSUBISHI_AC_COOL kMitsubishiAcCool -#define MITSUBISHI_AC_AUTO kMitsubishiAcAuto - -class IRMitsubishiAC { - public: - explicit IRMitsubishiAC(uint16_t pin); - - static uint8_t calculateChecksum(uint8_t* data); - - void stateReset(); -#if SEND_MITSUBISHI_AC - void send(const uint16_t repeat = kMitsubishiACMinRepeat); -#endif // SEND_MITSUBISHI_AC - void begin(); - void on(); - void off(); - void setPower(bool state); - bool getPower(); - void setTemp(uint8_t temp); - uint8_t getTemp(); - void setFan(uint8_t fan); - uint8_t getFan(); - void setMode(uint8_t mode); - uint8_t getMode(); - void setVane(uint8_t mode); - uint8_t getVane(); - uint8_t* getRaw(); - void setRaw(uint8_t* data); - uint8_t getClock(); - void setClock(uint8_t clock); - uint8_t getStartClock(); - void setStartClock(uint8_t clock); - uint8_t getStopClock(); - void setStopClock(uint8_t clock); - uint8_t getTimer(); - void setTimer(uint8_t timer); -#ifdef ARDUINO - String toString(); -#else - std::string toString(); -#endif - - private: -#ifdef ARDUINO - String timeToString(uint64_t time); -#else - std::string timeToString(uint64_t time); -#endif - uint8_t remote_state[kMitsubishiACStateLength]; - void checksum(); - IRsend _irsend; -}; - -#endif // IR_MITSUBISHI_H_ diff --git a/lib/IRremoteESP8266_ID1089/src/ir_NEC.cpp b/lib/IRremoteESP8266_ID1089/src/ir_NEC.cpp deleted file mode 100644 index 660b511..0000000 --- a/lib/IRremoteESP8266_ID1089/src/ir_NEC.cpp +++ /dev/null @@ -1,169 +0,0 @@ -// Copyright 2009 Ken Shirriff -// Copyright 2017 David Conran - -#define __STDC_LIMIT_MACROS -#include "ir_NEC.h" -#include -#include -#include "IRrecv.h" -#include "IRsend.h" -#include "IRutils.h" - -// N N EEEEE CCCC -// NN N E C -// N N N EEE C -// N NN E C -// N N EEEEE CCCC - -// NEC originally added from https://github.com/shirriff/Arduino-IRremote/ - -#if (SEND_NEC || SEND_SHERWOOD || SEND_AIWA_RC_T501 || SEND_SANYO || \ - SEND_PIONEER) -// Send a raw NEC(Renesas) formatted message. -// -// Args: -// data: The message to be sent. -// nbits: The number of bits of the message to be sent. Typically kNECBits. -// repeat: The number of times the command is to be repeated. -// -// Status: STABLE / Known working. -// -// Ref: -// http://www.sbprojects.com/knowledge/ir/nec.php -void IRsend::sendNEC(uint64_t data, uint16_t nbits, uint16_t repeat) { - sendGeneric(kNecHdrMark, kNecHdrSpace, kNecBitMark, kNecOneSpace, kNecBitMark, - kNecZeroSpace, kNecBitMark, kNecMinGap, kNecMinCommandLength, - data, nbits, 38, true, 0, // Repeats are handled later. - 33); - // Optional command repeat sequence. - if (repeat) - sendGeneric(kNecHdrMark, kNecRptSpace, 0, 0, 0, 0, // No actual data sent. - kNecBitMark, kNecMinGap, kNecMinCommandLength, 0, - 0, // No data to be sent. - 38, true, repeat - 1, // We've already sent a one message. - 33); -} - -// Calculate the raw NEC data based on address and command. -// Args: -// address: An address value. -// command: An 8-bit command value. -// Returns: -// A raw 32-bit NEC message. -// -// Status: BETA / Expected to work. -// -// Ref: -// http://www.sbprojects.com/knowledge/ir/nec.php -uint32_t IRsend::encodeNEC(uint16_t address, uint16_t command) { - command &= 0xFF; // We only want the least significant byte of command. - // sendNEC() sends MSB first, but protocol says this is LSB first. - command = reverseBits(command, 8); - command = (command << 8) + (command ^ 0xFF); // Calculate the new command. - if (address > 0xFF) { // Is it Extended NEC? - address = reverseBits(address, 16); - return ((address << 16) + command); // Extended. - } else { - address = reverseBits(address, 8); - return (address << 24) + ((address ^ 0xFF) << 16) + command; // Normal. - } -} -#endif - -#if (DECODE_NEC || DECODE_SHERWOOD || DECODE_AIWA_RC_T501 || DECODE_SANYO) -// Decode the supplied NEC message. -// -// Args: -// results: Ptr to the data to decode and where to store the decode result. -// nbits: The number of data bits to expect. Typically kNECBits. -// strict: Flag indicating if we should perform strict matching. -// Returns: -// boolean: True if it can decode it, false if it can't. -// -// Status: STABLE / Known good. -// -// Notes: -// NEC protocol has three varients/forms. -// Normal: an 8 bit address & an 8 bit command in 32 bit data form. -// i.e. address + inverted(address) + command + inverted(command) -// Extended: a 16 bit address & an 8 bit command in 32 bit data form. -// i.e. address + command + inverted(command) -// Repeat: a 0-bit code. i.e. No data bits. Just the header + footer. -// -// Ref: -// http://www.sbprojects.com/knowledge/ir/nec.php -bool IRrecv::decodeNEC(decode_results *results, uint16_t nbits, bool strict) { - if (results->rawlen < 2 * nbits + kHeader + kFooter - 1 && - results->rawlen != kNecRptLength) - return false; // Can't possibly be a valid NEC message. - if (strict && nbits != kNECBits) - return false; // Not strictly an NEC message. - - uint64_t data = 0; - uint16_t offset = kStartOffset; - - // Header - if (!matchMark(results->rawbuf[offset], kNecHdrMark)) return false; - // Calculate how long the lowest tick time is based on the header mark. - uint32_t mark_tick = results->rawbuf[offset++] * kRawTick / kNecHdrMarkTicks; - // Check if it is a repeat code. - if (results->rawlen == kNecRptLength && - matchSpace(results->rawbuf[offset], kNecRptSpace) && - matchMark(results->rawbuf[offset + 1], kNecBitMarkTicks * mark_tick)) { - results->value = kRepeat; - results->decode_type = NEC; - results->bits = 0; - results->address = 0; - results->command = 0; - results->repeat = true; - return true; - } - - // Header (cont.) - if (!matchSpace(results->rawbuf[offset], kNecHdrSpace)) return false; - // Calculate how long the common tick time is based on the header space. - uint32_t space_tick = - results->rawbuf[offset++] * kRawTick / kNecHdrSpaceTicks; - // Data - match_result_t data_result = - matchData(&(results->rawbuf[offset]), nbits, kNecBitMarkTicks * mark_tick, - kNecOneSpaceTicks * space_tick, kNecBitMarkTicks * mark_tick, - kNecZeroSpaceTicks * space_tick); - if (data_result.success == false) return false; - data = data_result.data; - offset += data_result.used; - - // Footer - if (!matchMark(results->rawbuf[offset++], kNecBitMarkTicks * mark_tick)) - return false; - if (offset < results->rawlen && - !matchAtLeast(results->rawbuf[offset], kNecMinGapTicks * space_tick)) - return false; - - // Compliance - // Calculate command and optionally enforce integrity checking. - uint8_t command = (data & 0xFF00) >> 8; - // Command is sent twice, once as plain and then inverted. - if ((command ^ 0xFF) != (data & 0xFF)) { - if (strict) return false; // Command integrity failed. - command = 0; // The command value isn't valid, so default to zero. - } - - // Success - results->bits = nbits; - results->value = data; - results->decode_type = NEC; - // NEC command and address are technically in LSB first order so the - // final versions have to be reversed. - results->command = reverseBits(command, 8); - // Normal NEC protocol has an 8 bit address sent, followed by it inverted. - uint8_t address = (data & 0xFF000000) >> 24; - uint8_t address_inverted = (data & 0x00FF0000) >> 16; - if (address == (address_inverted ^ 0xFF)) - // Inverted, so it is normal NEC protocol. - results->address = reverseBits(address, 8); - else // Not inverted, so must be Extended NEC protocol, thus 16 bit address. - results->address = reverseBits((data >> 16) & UINT16_MAX, 16); - return true; -} -#endif diff --git a/lib/IRremoteESP8266_ID1089/src/ir_NEC.h b/lib/IRremoteESP8266_ID1089/src/ir_NEC.h deleted file mode 100644 index c274c10..0000000 --- a/lib/IRremoteESP8266_ID1089/src/ir_NEC.h +++ /dev/null @@ -1,46 +0,0 @@ -// Copyright 2009 Ken Shirriff -// Copyright 2017, 2018 David Conran - -#ifndef IR_NEC_H_ -#define IR_NEC_H_ - -#include -#include "IRremoteESP8266.h" - -// N N EEEEE CCCC -// NN N E C -// N N N EEE C -// N NN E C -// N N EEEEE CCCC - -// NEC originally added from https://github.com/shirriff/Arduino-IRremote/ - -// Constants -// Ref: -// http://www.sbprojects.com/knowledge/ir/nec.php -const uint16_t kNecTick = 560; -const uint16_t kNecHdrMarkTicks = 16; -const uint16_t kNecHdrMark = kNecHdrMarkTicks * kNecTick; -const uint16_t kNecHdrSpaceTicks = 8; -const uint16_t kNecHdrSpace = kNecHdrSpaceTicks * kNecTick; -const uint16_t kNecBitMarkTicks = 1; -const uint16_t kNecBitMark = kNecBitMarkTicks * kNecTick; -const uint16_t kNecOneSpaceTicks = 3; -const uint16_t kNecOneSpace = kNecOneSpaceTicks * kNecTick; -const uint16_t kNecZeroSpaceTicks = 1; -const uint16_t kNecZeroSpace = kNecZeroSpaceTicks * kNecTick; -const uint16_t kNecRptSpaceTicks = 4; -const uint16_t kNecRptSpace = kNecRptSpaceTicks * kNecTick; -const uint16_t kNecRptLength = 4; -const uint16_t kNecMinCommandLengthTicks = 193; -const uint32_t kNecMinCommandLength = kNecMinCommandLengthTicks * kNecTick; -const uint32_t kNecMinGap = - kNecMinCommandLength - - (kNecHdrMark + kNecHdrSpace + kNECBits * (kNecBitMark + kNecOneSpace) + - kNecBitMark); -const uint16_t kNecMinGapTicks = - kNecMinCommandLengthTicks - - (kNecHdrMarkTicks + kNecHdrSpaceTicks + - kNECBits * (kNecBitMarkTicks + kNecOneSpaceTicks) + kNecBitMarkTicks); - -#endif // IR_NEC_H_ diff --git a/lib/IRremoteESP8266_ID1089/src/ir_Nikai.cpp b/lib/IRremoteESP8266_ID1089/src/ir_Nikai.cpp deleted file mode 100644 index 9ac22a8..0000000 --- a/lib/IRremoteESP8266_ID1089/src/ir_Nikai.cpp +++ /dev/null @@ -1,104 +0,0 @@ -// Copyright 2009 Ken Shirriff -// Copyright 2017 David Conran - -#include -#include "IRrecv.h" -#include "IRsend.h" -#include "IRutils.h" - -// NN NN IIIII KK KK AAA IIIII -// NNN NN III KK KK AAAAA III -// NN N NN III KKKK AA AA III -// NN NNN III KK KK AAAAAAA III -// NN NN IIIII KK KK AA AA IIIII - -// Constants -// Ref: -// https://github.com/markszabo/IRremoteESP8266/issues/309 -const uint16_t kNikaiTick = 500; -const uint16_t kNikaiHdrMarkTicks = 8; -const uint16_t kNikaiHdrMark = kNikaiHdrMarkTicks * kNikaiTick; -const uint16_t kNikaiHdrSpaceTicks = 8; -const uint16_t kNikaiHdrSpace = kNikaiHdrSpaceTicks * kNikaiTick; -const uint16_t kNikaiBitMarkTicks = 1; -const uint16_t kNikaiBitMark = kNikaiBitMarkTicks * kNikaiTick; -const uint16_t kNikaiOneSpaceTicks = 2; -const uint16_t kNikaiOneSpace = kNikaiOneSpaceTicks * kNikaiTick; -const uint16_t kNikaiZeroSpaceTicks = 4; -const uint16_t kNikaiZeroSpace = kNikaiZeroSpaceTicks * kNikaiTick; -const uint16_t kNikaiMinGapTicks = 17; -const uint16_t kNikaiMinGap = kNikaiMinGapTicks * kNikaiTick; - -#if SEND_NIKAI -// Send a Nikai TV formatted message. -// -// Args: -// data: The message to be sent. -// nbits: The bit size of the message being sent. typically kNikaiBits. -// repeat: The number of times the message is to be repeated. -// -// Status: STABLE / Working. -// -// Ref: https://github.com/markszabo/IRremoteESP8266/issues/309 -void IRsend::sendNikai(uint64_t data, uint16_t nbits, uint16_t repeat) { - sendGeneric(kNikaiHdrMark, kNikaiHdrSpace, kNikaiBitMark, kNikaiOneSpace, - kNikaiBitMark, kNikaiZeroSpace, kNikaiBitMark, kNikaiMinGap, data, - nbits, 38, true, repeat, 33); -} -#endif - -#if DECODE_NIKAI -// Decode the supplied Nikai message. -// -// Args: -// results: Ptr to the data to decode and where to store the decode result. -// nbits: Nr. of bits to expect in the data portion. -// Typically kNikaiBits. -// strict: Flag to indicate if we strictly adhere to the specification. -// Returns: -// boolean: True if it can decode it, false if it can't. -// -// Status: STABLE / Working. -// -bool IRrecv::decodeNikai(decode_results *results, uint16_t nbits, bool strict) { - if (results->rawlen < 2 * nbits + kHeader + kFooter - 1) - return false; // Can't possibly be a valid Nikai message. - if (strict && nbits != kNikaiBits) - return false; // We expect Nikai to be a certain sized message. - - uint64_t data = 0; - uint16_t offset = kStartOffset; - - // Header - if (!matchMark(results->rawbuf[offset], kNikaiHdrMark)) return false; - // Calculate how long the common tick time is based on the header mark. - uint32_t m_tick = results->rawbuf[offset++] * kRawTick / kNikaiHdrMarkTicks; - if (!matchSpace(results->rawbuf[offset], kNikaiHdrSpace)) return false; - // Calculate how long the common tick time is based on the header space. - uint32_t s_tick = results->rawbuf[offset++] * kRawTick / kNikaiHdrSpaceTicks; - // Data - match_result_t data_result = - matchData(&(results->rawbuf[offset]), nbits, kNikaiBitMarkTicks * m_tick, - kNikaiOneSpaceTicks * s_tick, kNikaiBitMarkTicks * m_tick, - kNikaiZeroSpaceTicks * s_tick); - if (data_result.success == false) return false; - data = data_result.data; - offset += data_result.used; - // Footer - if (!matchMark(results->rawbuf[offset++], kNikaiBitMarkTicks * m_tick)) - return false; - if (offset < results->rawlen && - !matchAtLeast(results->rawbuf[offset], kNikaiMinGapTicks * s_tick)) - return false; - - // Compliance - - // Success - results->bits = nbits; - results->value = data; - results->decode_type = NIKAI; - results->command = 0; - results->address = 0; - return true; -} -#endif diff --git a/lib/IRremoteESP8266_ID1089/src/ir_Panasonic.cpp b/lib/IRremoteESP8266_ID1089/src/ir_Panasonic.cpp deleted file mode 100644 index be96ce0..0000000 --- a/lib/IRremoteESP8266_ID1089/src/ir_Panasonic.cpp +++ /dev/null @@ -1,863 +0,0 @@ -// Copyright 2015 Kristian Lauszus -// Copyright 2017, 2018 David Conran - -#include "ir_Panasonic.h" -#include -#ifndef ARDUINO -#include -#endif -#include "IRrecv.h" -#include "IRsend.h" -#include "IRutils.h" - -// 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 - -// Panasonic protocol originally added by Kristian Lauszus from: -// https://github.com/z3t0/Arduino-IRremote -// (Thanks to zenwheel and other people at the original blog post) -// -// Panasonic A/C support add by crankyoldgit but heavily influenced by: -// https://github.com/ToniA/ESPEasy/blob/HeatpumpIR/lib/HeatpumpIR/PanasonicHeatpumpIR.cpp -// Panasonic A/C Clock & Timer support: -// Reverse Engineering by MikkelTb -// Code by crankyoldgit -// Panasonic A/C models supported: -// A/C Series/models: -// JKE, LKE, DKE, CKP, & NKE series. (In theory) -// CS-YW9MKD (confirmed) -// CS-ME14CKPG / CS-ME12CKPG / CS-ME10CKPG -// A/C Remotes: -// A75C3747 (confirmed) -// A75C3704 -// A75C2311 (CKP) - -// Constants -// Ref: -// http://www.remotecentral.com/cgi-bin/mboard/rc-pronto/thread.cgi?26152 - -const uint16_t kPanasonicTick = 432; -const uint16_t kPanasonicHdrMarkTicks = 8; -const uint16_t kPanasonicHdrMark = kPanasonicHdrMarkTicks * kPanasonicTick; -const uint16_t kPanasonicHdrSpaceTicks = 4; -const uint16_t kPanasonicHdrSpace = kPanasonicHdrSpaceTicks * kPanasonicTick; -const uint16_t kPanasonicBitMarkTicks = 1; -const uint16_t kPanasonicBitMark = kPanasonicBitMarkTicks * kPanasonicTick; -const uint16_t kPanasonicOneSpaceTicks = 3; -const uint16_t kPanasonicOneSpace = kPanasonicOneSpaceTicks * kPanasonicTick; -const uint16_t kPanasonicZeroSpaceTicks = 1; -const uint16_t kPanasonicZeroSpace = kPanasonicZeroSpaceTicks * kPanasonicTick; -const uint16_t kPanasonicMinCommandLengthTicks = 378; -const uint32_t kPanasonicMinCommandLength = - kPanasonicMinCommandLengthTicks * kPanasonicTick; -const uint16_t kPanasonicEndGap = 5000; // See issue #245 -const uint16_t kPanasonicMinGapTicks = - kPanasonicMinCommandLengthTicks - - (kPanasonicHdrMarkTicks + kPanasonicHdrSpaceTicks + - kPanasonicBits * (kPanasonicBitMarkTicks + kPanasonicOneSpaceTicks) + - kPanasonicBitMarkTicks); -const uint32_t kPanasonicMinGap = kPanasonicMinGapTicks * kPanasonicTick; - -const uint16_t kPanasonicAcSectionGap = 10000; -const uint16_t kPanasonicAcSection1Length = 8; -const uint32_t kPanasonicAcMessageGap = 100000; // A complete guess. - -#if (SEND_PANASONIC || SEND_DENON) -// Send a Panasonic formatted message. -// -// Args: -// data: The message to be sent. -// nbits: The number of bits of the message to be sent. (kPanasonicBits). -// repeat: The number of times the command is to be repeated. -// -// Status: BETA / Should be working. -// -// Note: -// This protocol is a modified version of Kaseikyo. -void IRsend::sendPanasonic64(uint64_t data, uint16_t nbits, uint16_t repeat) { - sendGeneric(kPanasonicHdrMark, kPanasonicHdrSpace, kPanasonicBitMark, - kPanasonicOneSpace, kPanasonicBitMark, kPanasonicZeroSpace, - kPanasonicBitMark, kPanasonicMinGap, kPanasonicMinCommandLength, - data, nbits, kPanasonicFreq, true, repeat, 50); -} - -// Send a Panasonic formatted message. -// -// Args: -// address: The manufacturer code. -// data: The data portion to be sent. -// nbits: The number of bits of the message to be sent. (kPanasonicBits). -// repeat: The number of times the command is to be repeated. -// -// Status: STABLE. -// -// Note: -// This protocol is a modified version of Kaseikyo. -void IRsend::sendPanasonic(uint16_t address, uint32_t data, uint16_t nbits, - uint16_t repeat) { - sendPanasonic64(((uint64_t)address << 32) | (uint64_t)data, nbits, repeat); -} - -// Calculate the raw Panasonic data based on device, subdevice, & function. -// -// Args: -// manufacturer: A 16-bit manufacturer code. e.g. 0x4004 is Panasonic. -// device: An 8-bit code. -// subdevice: An 8-bit code. -// function: An 8-bit code. -// Returns: -// A raw uint64_t Panasonic message. -// -// Status: BETA / Should be working.. -// -// Note: -// Panasonic 48-bit protocol is a modified version of Kaseikyo. -// Ref: -// http://www.remotecentral.com/cgi-bin/mboard/rc-pronto/thread.cgi?2615 -uint64_t IRsend::encodePanasonic(uint16_t manufacturer, uint8_t device, - uint8_t subdevice, uint8_t function) { - uint8_t checksum = device ^ subdevice ^ function; - return (((uint64_t)manufacturer << 32) | ((uint64_t)device << 24) | - ((uint64_t)subdevice << 16) | ((uint64_t)function << 8) | checksum); -} -#endif // (SEND_PANASONIC || SEND_DENON) - -#if (DECODE_PANASONIC || DECODE_DENON) -// Decode the supplied Panasonic message. -// -// Args: -// results: Ptr to the data to decode and where to store the decode result. -// nbits: Nr. of data bits to expect. -// strict: Flag indicating if we should perform strict matching. -// Returns: -// boolean: True if it can decode it, false if it can't. -// -// Status: BETA / Should be working. -// Note: -// Panasonic 48-bit protocol is a modified version of Kaseikyo. -// Ref: -// http://www.remotecentral.com/cgi-bin/mboard/rc-pronto/thread.cgi?26152 -// http://www.hifi-remote.com/wiki/index.php?title=Panasonic -bool IRrecv::decodePanasonic(decode_results *results, uint16_t nbits, - bool strict, uint32_t manufacturer) { - if (results->rawlen < 2 * nbits + kHeader + kFooter - 1) - return false; // Not enough entries to be a Panasonic message. - if (strict && nbits != kPanasonicBits) - return false; // Request is out of spec. - - uint64_t data = 0; - uint16_t offset = kStartOffset; - - // Header - if (!matchMark(results->rawbuf[offset], kPanasonicHdrMark)) return false; - // Calculate how long the common tick time is based on the header mark. - uint32_t m_tick = - results->rawbuf[offset++] * kRawTick / kPanasonicHdrMarkTicks; - if (!matchSpace(results->rawbuf[offset], kPanasonicHdrSpace)) return false; - // Calculate how long the common tick time is based on the header space. - uint32_t s_tick = - results->rawbuf[offset++] * kRawTick / kPanasonicHdrSpaceTicks; - - // Data - match_result_t data_result = matchData( - &(results->rawbuf[offset]), nbits, kPanasonicBitMarkTicks * m_tick, - kPanasonicOneSpaceTicks * s_tick, kPanasonicBitMarkTicks * m_tick, - kPanasonicZeroSpaceTicks * s_tick); - if (data_result.success == false) return false; - data = data_result.data; - offset += data_result.used; - - // Footer - if (!match(results->rawbuf[offset++], kPanasonicBitMarkTicks * m_tick)) - return false; - if (offset < results->rawlen && - !matchAtLeast(results->rawbuf[offset], kPanasonicEndGap)) - return false; - - // Compliance - uint32_t address = data >> 32; - uint32_t command = data & 0xFFFFFFFF; - if (strict) { - if (address != manufacturer) // Verify the Manufacturer code. - return false; - // Verify the checksum. - uint8_t checksumOrig = data & 0xFF; - uint8_t checksumCalc = ((data >> 24) ^ (data >> 16) ^ (data >> 8)) & 0xFF; - if (checksumOrig != checksumCalc) return false; - } - - // Success - results->value = data; - results->address = address; - results->command = command; - results->decode_type = PANASONIC; - results->bits = nbits; - return true; -} -#endif // (DECODE_PANASONIC || DECODE_DENON) - -#if SEND_PANASONIC_AC -// Send a Panasonic A/C message. -// -// Args: -// data: Contents of the message to be sent. (Guessing MSBF order) -// nbits: Nr. of bits of data to be sent. Typically kPanasonicAcBits. -// repeat: Nr. of additional times the message is to be sent. -// -// Status: Beta / Appears to work with real device(s). -//: -// Panasonic A/C models supported: -// A/C Series/models: -// JKE, LKE, DKE, & NKE series. -// CS-YW9MKD -// A/C Remotes: -// A75C3747 -// A75C3704 -// -void IRsend::sendPanasonicAC(uint8_t data[], uint16_t nbytes, uint16_t repeat) { - if (nbytes < kPanasonicAcSection1Length) return; - for (uint16_t r = 0; r <= repeat; r++) { - // First section. (8 bytes) - sendGeneric(kPanasonicHdrMark, kPanasonicHdrSpace, kPanasonicBitMark, - kPanasonicOneSpace, kPanasonicBitMark, kPanasonicZeroSpace, - kPanasonicBitMark, kPanasonicAcSectionGap, data, - kPanasonicAcSection1Length, kPanasonicFreq, false, 0, 50); - // First section. (The rest of the data bytes) - sendGeneric(kPanasonicHdrMark, kPanasonicHdrSpace, kPanasonicBitMark, - kPanasonicOneSpace, kPanasonicBitMark, kPanasonicZeroSpace, - kPanasonicBitMark, kPanasonicAcMessageGap, - data + kPanasonicAcSection1Length, - nbytes - kPanasonicAcSection1Length, kPanasonicFreq, false, 0, - 50); - } -} -#endif // SEND_PANASONIC_AC - -IRPanasonicAc::IRPanasonicAc(uint16_t pin) : _irsend(pin) { stateReset(); } - -void IRPanasonicAc::stateReset() { - for (uint8_t i = 0; i < kPanasonicAcStateLength; i++) - remote_state[i] = kPanasonicKnownGoodState[i]; - _temp = 25; // An initial saved desired temp. Completely made up. - _swingh = kPanasonicAcSwingHMiddle; // A similar made up value for H Swing. -} - -void IRPanasonicAc::begin() { _irsend.begin(); } - -// Verify the checksum is valid for a given state. -// Args: -// state: The array to verify the checksum of. -// length: The size of the state. -// Returns: -// A boolean. -bool IRPanasonicAc::validChecksum(uint8_t state[], const uint16_t length) { - if (length < 2) return false; // 1 byte of data can't have a checksum. - return (state[length - 1] == - sumBytes(state, length - 1, kPanasonicAcChecksumInit)); -} - -uint8_t IRPanasonicAc::calcChecksum(uint8_t state[], const uint16_t length) { - return sumBytes(state, length - 1, kPanasonicAcChecksumInit); -} - -void IRPanasonicAc::fixChecksum(const uint16_t length) { - remote_state[length - 1] = calcChecksum(remote_state, length); -} - -#if SEND_PANASONIC_AC -void IRPanasonicAc::send(const uint16_t repeat) { - fixChecksum(); - _irsend.sendPanasonicAC(remote_state, kPanasonicAcStateLength, repeat); -} -#endif // SEND_PANASONIC_AC - -void IRPanasonicAc::setModel(const panasonic_ac_remote_model_t model) { - switch (model) { - case kPanasonicDke: - case kPanasonicJke: - case kPanasonicLke: - case kPanasonicNke: - case kPanasonicCkp: - break; - default: // Only proceed if we know what to do. - return; - } - // clear & set the various bits and bytes. - remote_state[13] &= 0xF0; - remote_state[17] = 0x00; - remote_state[21] &= 0b11101111; - remote_state[23] = 0x81; - remote_state[25] = 0x00; - - switch (model) { - case kPanasonicLke: - remote_state[13] |= 0x02; - remote_state[17] = 0x06; - break; - case kPanasonicDke: - remote_state[23] = 0x01; - remote_state[25] = 0x06; - // Has to be done last as setSwingHorizontal has model check built-in - setSwingHorizontal(_swingh); - break; - case kPanasonicNke: - remote_state[17] = 0x06; - break; - case kPanasonicJke: - break; - case kPanasonicCkp: - remote_state[21] |= 0x10; - remote_state[23] = 0x01; - default: - break; - } -} - -panasonic_ac_remote_model_t IRPanasonicAc::getModel() { - if (remote_state[17] == 0x00) { - if ((remote_state[21] & 0x10) && (remote_state[23] & 0x01)) - return kPanasonicCkp; - if (remote_state[23] & 0x80) return kPanasonicJke; - } - if (remote_state[17] == 0x06 && (remote_state[13] & 0x0F) == 0x02) - return kPanasonicLke; - if (remote_state[23] == 0x01) return kPanasonicDke; - if (remote_state[17] == 0x06) return kPanasonicNke; - return kPanasonicUnknown; -} - -uint8_t *IRPanasonicAc::getRaw() { - fixChecksum(); - return remote_state; -} - -void IRPanasonicAc::setRaw(const uint8_t state[]) { - for (uint8_t i = 0; i < kPanasonicAcStateLength; i++) { - remote_state[i] = state[i]; - } -} - -// Control the power state of the A/C unit. -// -// For CKP models, the remote has no memory of the power state the A/C unit -// should be in. For those models setting this on/true will toggle the power -// state of the Panasonic A/C unit with the next meessage. -// e.g. If the A/C unit is already on, setPower(true) will turn it off. -// If the A/C unit is already off, setPower(true) will turn it on. -// setPower(false) will leave the A/C power state as it was. -// -// For all other models, setPower(true) should set the internal state to -// turn it on, and setPower(false) should turn it off. -void IRPanasonicAc::setPower(const bool state) { - if (state) - on(); - else - off(); -} - -// Return the A/C power state of the remote. -// Except for CKP models, where it returns if the power state will be toggled -// on the A/C unit when the next message is sent. -bool IRPanasonicAc::getPower() { - return (remote_state[13] & kPanasonicAcPower) == kPanasonicAcPower; -} - -void IRPanasonicAc::on() { remote_state[13] |= kPanasonicAcPower; } - -void IRPanasonicAc::off() { remote_state[13] &= ~kPanasonicAcPower; } - -uint8_t IRPanasonicAc::getMode() { return remote_state[13] >> 4; } - -void IRPanasonicAc::setMode(const uint8_t desired) { - uint8_t mode = kPanasonicAcAuto; // Default to Auto mode. - switch (desired) { - case kPanasonicAcFan: - // Allegedly Fan mode has a temperature of 27. - setTemp(kPanasonicAcFanModeTemp, false); - mode = desired; - break; - case kPanasonicAcAuto: - case kPanasonicAcCool: - case kPanasonicAcHeat: - case kPanasonicAcDry: - mode = desired; - // Set the temp to the saved temp, just incase our previous mode was Fan. - setTemp(_temp); - break; - } - remote_state[13] &= 0x0F; // Clear the previous mode bits. - remote_state[13] |= mode << 4; -} - -uint8_t IRPanasonicAc::getTemp() { return remote_state[14] >> 1; } - -// Set the desitred temperature in Celcius. -// Args: -// celsius: The temperature to set the A/C unit to. -// remember: A boolean flag for the class to remember the temperature. -// -// Automatically safely limits the temp to the operating range supported. -void IRPanasonicAc::setTemp(const uint8_t celsius, const bool remember) { - uint8_t temperature; - temperature = std::max(celsius, kPanasonicAcMinTemp); - temperature = std::min(temperature, kPanasonicAcMaxTemp); - remote_state[14] = temperature << 1; - if (remember) _temp = temperature; -} - -uint8_t IRPanasonicAc::getSwingVertical() { return remote_state[16] & 0x0F; } - -void IRPanasonicAc::setSwingVertical(const uint8_t desired_elevation) { - uint8_t elevation = desired_elevation; - if (elevation != kPanasonicAcSwingVAuto) { - elevation = std::max(elevation, kPanasonicAcSwingVUp); - elevation = std::min(elevation, kPanasonicAcSwingVDown); - } - remote_state[16] &= 0xF0; - remote_state[16] |= elevation; -} - -uint8_t IRPanasonicAc::getSwingHorizontal() { return remote_state[17]; } - -void IRPanasonicAc::setSwingHorizontal(const uint8_t desired_direction) { - switch (desired_direction) { - case kPanasonicAcSwingHAuto: - case kPanasonicAcSwingHMiddle: - case kPanasonicAcSwingHFullLeft: - case kPanasonicAcSwingHLeft: - case kPanasonicAcSwingHRight: - case kPanasonicAcSwingHFullRight: - break; - default: // Ignore anything that isn't valid. - return; - } - _swingh = desired_direction; // Store the direction for later. - uint8_t direction = desired_direction; - switch (getModel()) { - case kPanasonicDke: - break; - case kPanasonicNke: - case kPanasonicLke: - direction = kPanasonicAcSwingHMiddle; - break; - default: // Ignore everything else. - return; - } - remote_state[17] = direction; -} - -void IRPanasonicAc::setFan(const uint8_t speed) { - if (speed <= kPanasonicAcFanMax || speed == kPanasonicAcFanAuto) - remote_state[16] = - (remote_state[16] & 0x0F) | ((speed + kPanasonicAcFanOffset) << 4); -} - -uint8_t IRPanasonicAc::getFan() { - return (remote_state[16] >> 4) - kPanasonicAcFanOffset; -} - -bool IRPanasonicAc::getQuiet() { - if (getModel() == kPanasonicCkp) - return remote_state[21] & kPanasonicAcQuietCkp; - else - return remote_state[21] & kPanasonicAcQuiet; -} - -void IRPanasonicAc::setQuiet(const bool state) { - uint8_t quiet; - if (getModel() == kPanasonicCkp) - quiet = kPanasonicAcQuietCkp; - else - quiet = kPanasonicAcQuiet; - - if (state) { - setPowerful(false); // Powerful is mutually exclusive. - remote_state[21] |= quiet; - } else { - remote_state[21] &= ~quiet; - } -} - -bool IRPanasonicAc::getPowerful() { - if (getModel() == kPanasonicCkp) - return remote_state[21] & kPanasonicAcPowerfulCkp; - else - return remote_state[21] & kPanasonicAcPowerful; -} - -void IRPanasonicAc::setPowerful(const bool state) { - uint8_t powerful; - if (getModel() == kPanasonicCkp) - powerful = kPanasonicAcPowerfulCkp; - else - powerful = kPanasonicAcPowerful; - - if (state) { - setQuiet(false); // Quiet is mutually exclusive. - remote_state[21] |= powerful; - } else { - remote_state[21] &= ~powerful; - } -} - -uint16_t IRPanasonicAc::encodeTime(const uint8_t hours, const uint8_t mins) { - return std::min(hours, (uint8_t)23) * 60 + std::min(mins, (uint8_t)59); -} - -uint16_t IRPanasonicAc::getClock() { - uint16_t result = ((remote_state[25] & 0b00000111) << 8) + remote_state[24]; - if (result == kPanasonicAcTimeSpecial) return 0; - return result; -} - -void IRPanasonicAc::setClock(const uint16_t mins_since_midnight) { - uint16_t corrected = std::min(mins_since_midnight, kPanasonicAcTimeMax); - if (mins_since_midnight == kPanasonicAcTimeSpecial) - corrected = kPanasonicAcTimeSpecial; - remote_state[24] = corrected & 0xFF; - remote_state[25] &= 0b11111000; - remote_state[25] |= (corrected >> 8); -} - -uint16_t IRPanasonicAc::getOnTimer() { - uint16_t result = ((remote_state[19] & 0b00000111) << 8) + remote_state[18]; - if (result == kPanasonicAcTimeSpecial) return 0; - return result; -} - -void IRPanasonicAc::setOnTimer(const uint16_t mins_since_midnight, - const bool enable) { - // Ensure it's on a 10 minute boundary and no overflow. - uint16_t corrected = std::min(mins_since_midnight, kPanasonicAcTimeMax); - corrected -= corrected % 10; - if (mins_since_midnight == kPanasonicAcTimeSpecial) - corrected = kPanasonicAcTimeSpecial; - - if (enable) - remote_state[13] |= kPanasonicAcOnTimer; // Set the Ontimer flag. - else - remote_state[13] &= ~kPanasonicAcOnTimer; // Clear the Ontimer flag. - // Store the time. - remote_state[18] = corrected & 0xFF; - remote_state[19] &= 0b11111000; - remote_state[19] |= (corrected >> 8); -} - -void IRPanasonicAc::cancelOnTimer() { setOnTimer(0, false); } - -bool IRPanasonicAc::isOnTimerEnabled() { - return remote_state[13] & kPanasonicAcOnTimer; -} - -uint16_t IRPanasonicAc::getOffTimer() { - uint16_t result = - ((remote_state[20] & 0b01111111) << 4) + (remote_state[19] >> 4); - if (result == kPanasonicAcTimeSpecial) return 0; - return result; -} - -void IRPanasonicAc::setOffTimer(const uint16_t mins_since_midnight, - const bool enable) { - // Ensure its on a 10 minute boundary and no overflow. - uint16_t corrected = std::min(mins_since_midnight, kPanasonicAcTimeMax); - corrected -= corrected % 10; - if (mins_since_midnight == kPanasonicAcTimeSpecial) - corrected = kPanasonicAcTimeSpecial; - - if (enable) - remote_state[13] |= kPanasonicAcOffTimer; // Set the OffTimer flag. - else - remote_state[13] &= ~kPanasonicAcOffTimer; // Clear the OffTimer flag. - // Store the time. - remote_state[19] &= 0b00001111; - remote_state[19] |= (corrected & 0b00001111) << 4; - remote_state[20] &= 0b10000000; - remote_state[20] |= corrected >> 4; -} - -void IRPanasonicAc::cancelOffTimer() { setOffTimer(0, false); } - -bool IRPanasonicAc::isOffTimerEnabled() { - return remote_state[13] & kPanasonicAcOffTimer; -} - -#ifdef ARDUINO -String IRPanasonicAc::timeToString(const uint16_t mins_since_midnight) { - String result = ""; -#else -std::string IRPanasonicAc::timeToString(const uint16_t mins_since_midnight) { - std::string result = ""; -#endif // ARDUINO - result += uint64ToString(mins_since_midnight / 60) + ":"; - uint8_t mins = mins_since_midnight % 60; - if (mins < 10) result += "0"; // Zero pad the minutes. - return result + uint64ToString(mins); -} - -// Convert the internal state into a human readable string. -#ifdef ARDUINO -String IRPanasonicAc::toString() { - String result = ""; -#else -std::string IRPanasonicAc::toString() { - std::string result = ""; -#endif // ARDUINO - result += "Model: " + uint64ToString(getModel()); - switch (getModel()) { - case kPanasonicDke: - result += " (DKE)"; - break; - case kPanasonicJke: - result += " (JKE)"; - break; - case kPanasonicNke: - result += " (NKE)"; - break; - case kPanasonicLke: - result += " (LKE)"; - break; - case kPanasonicCkp: - result += " (CKP)"; - break; - default: - result += " (UNKNOWN)"; - } - result += ", Power: "; - if (getPower()) - result += "On"; - else - result += "Off"; - result += ", Mode: " + uint64ToString(getMode()); - switch (getMode()) { - case kPanasonicAcAuto: - result += " (AUTO)"; - break; - case kPanasonicAcCool: - result += " (COOL)"; - break; - case kPanasonicAcHeat: - result += " (HEAT)"; - break; - case kPanasonicAcDry: - result += " (DRY)"; - break; - case kPanasonicAcFan: - result += " (FAN)"; - break; - default: - result += " (UNKNOWN)"; - } - result += ", Temp: " + uint64ToString(getTemp()) + "C"; - result += ", Fan: " + uint64ToString(getFan()); - switch (getFan()) { - case kPanasonicAcFanAuto: - result += " (AUTO)"; - break; - case kPanasonicAcFanMax: - result += " (MAX)"; - break; - case kPanasonicAcFanMin: - result += " (MIN)"; - break; - default: - result += " (UNKNOWN)"; - break; - } - result += ", Swing (Vertical): " + uint64ToString(getSwingVertical()); - switch (getSwingVertical()) { - case kPanasonicAcSwingVAuto: - result += " (AUTO)"; - break; - case kPanasonicAcSwingVUp: - result += " (Full Up)"; - break; - case kPanasonicAcSwingVDown: - result += " (Full Down)"; - break; - case 2: - case 3: - case 4: - break; - default: - result += " (UNKNOWN)"; - break; - } - switch (getModel()) { - case kPanasonicJke: - case kPanasonicCkp: - break; // No Horizontal Swing support. - default: - result += ", Swing (Horizontal): " + uint64ToString(getSwingHorizontal()); - switch (getSwingHorizontal()) { - case kPanasonicAcSwingHAuto: - result += " (AUTO)"; - break; - case kPanasonicAcSwingHFullLeft: - result += " (Full Left)"; - break; - case kPanasonicAcSwingHLeft: - result += " (Left)"; - break; - case kPanasonicAcSwingHMiddle: - result += " (Middle)"; - break; - case kPanasonicAcSwingHFullRight: - result += " (Full Right)"; - break; - case kPanasonicAcSwingHRight: - result += " (Right)"; - break; - default: - result += " (UNKNOWN)"; - break; - } - } - result += ", Quiet: "; - if (getQuiet()) - result += "On"; - else - result += "Off"; - result += ", Powerful: "; - if (getPowerful()) - result += "On"; - else - result += "Off"; - result += ", Clock: " + timeToString(getClock()); - result += ", On Timer: "; - if (isOnTimerEnabled()) - result += timeToString(getOnTimer()); - else - result += "Off"; - result += ", Off Timer: "; - if (isOffTimerEnabled()) - result += timeToString(getOffTimer()); - else - result += "Off"; - return result; -} - -#if DECODE_PANASONIC_AC -// Decode the supplied Panasonic AC message. -// -// Args: -// results: Ptr to the data to decode and where to store the decode result. -// nbits: The number of data bits to expect. Typically kPanasonicAcBits. -// strict: Flag indicating if we should perform strict matching. -// Returns: -// boolean: True if it can decode it, false if it can't. -// -// Status: Beta / Appears to work with real device(s). -// -// Panasonic A/C models supported: -// A/C Series/models: -// JKE, LKE, DKE, & NKE series. -// CS-YW9MKD -// A/C Remotes: -// A75C3747 (Confirmed) -// A75C3704 -bool IRrecv::decodePanasonicAC(decode_results *results, uint16_t nbits, - bool strict) { - if (nbits % 8 != 0) // nbits has to be a multiple of nr. of bits in a byte. - return false; - - uint8_t min_nr_of_messages = 1; - if (strict) { - if (nbits != kPanasonicAcBits && nbits != kPanasonicAcShortBits) - return false; // Not strictly a PANASONIC_AC message. - } - - if (results->rawlen < - min_nr_of_messages * (2 * nbits + kHeader + kFooter) - 1) - return false; // Can't possibly be a valid PANASONIC_AC message. - - uint16_t dataBitsSoFar = 0; - uint16_t offset = kStartOffset; - match_result_t data_result; - - // Header - if (!matchMark(results->rawbuf[offset], kPanasonicHdrMark, - kPanasonicAcTolerance, kPanasonicAcExcess)) - return false; - // Calculate how long the common tick time is based on the header mark. - uint32_t m_tick = - results->rawbuf[offset++] * kRawTick / kPanasonicHdrMarkTicks; - if (!matchSpace(results->rawbuf[offset], kPanasonicHdrSpace, - kPanasonicAcTolerance, kPanasonicAcExcess)) - return false; - // Calculate how long the common tick time is based on the header space. - uint32_t s_tick = - results->rawbuf[offset++] * kRawTick / kPanasonicHdrSpaceTicks; - - uint16_t i = 0; - // Data (Section #1) - // Keep reading bytes until we either run out of section or state to fill. - for (; offset <= results->rawlen - 16 && i < kPanasonicAcSection1Length; - i++, dataBitsSoFar += 8, offset += data_result.used) { - data_result = matchData( - &(results->rawbuf[offset]), 8, kPanasonicBitMarkTicks * m_tick, - kPanasonicOneSpaceTicks * s_tick, kPanasonicBitMarkTicks * m_tick, - kPanasonicZeroSpaceTicks * s_tick, kPanasonicAcTolerance, - kPanasonicAcExcess, false); - if (data_result.success == false) { - DPRINT("DEBUG: offset = "); - DPRINTLN(offset + data_result.used); - return false; // Fail - } - results->state[i] = data_result.data; - } - // Section footer. - if (!matchMark(results->rawbuf[offset++], kPanasonicBitMarkTicks * m_tick, - kPanasonicAcTolerance, kPanasonicAcExcess)) - return false; - if (!matchSpace(results->rawbuf[offset++], kPanasonicAcSectionGap, - kPanasonicAcTolerance, kPanasonicAcExcess)) - return false; - // Header. - if (!matchMark(results->rawbuf[offset++], kPanasonicHdrMarkTicks * m_tick, - kPanasonicAcTolerance, kPanasonicAcExcess)) - return false; - if (!matchSpace(results->rawbuf[offset++], kPanasonicHdrSpaceTicks * s_tick, - kPanasonicAcTolerance, kPanasonicAcExcess)) - return false; - // Data (Section #2) - // Keep reading bytes until we either run out of data. - for (; offset <= results->rawlen - 16 && i < nbits / 8; - i++, dataBitsSoFar += 8, offset += data_result.used) { - data_result = matchData( - &(results->rawbuf[offset]), 8, kPanasonicBitMarkTicks * m_tick, - kPanasonicOneSpaceTicks * s_tick, kPanasonicBitMarkTicks * m_tick, - kPanasonicZeroSpaceTicks * s_tick, kPanasonicAcTolerance, - kPanasonicAcExcess, false); - if (data_result.success == false) { - DPRINT("DEBUG: offset = "); - DPRINTLN(offset + data_result.used); - return false; // Fail - } - results->state[i] = data_result.data; - } - // Message Footer. - if (!matchMark(results->rawbuf[offset++], kPanasonicBitMarkTicks * m_tick, - kPanasonicAcTolerance, kPanasonicAcExcess)) - return false; - if (offset <= results->rawlen && - !matchAtLeast(results->rawbuf[offset++], kPanasonicAcMessageGap)) - return false; - - // Compliance - if (strict) { - // Check the signatures of the section blocks. They start with 0x02& 0x20. - if (results->state[0] != 0x02 || results->state[1] != 0x20 || - results->state[8] != 0x02 || results->state[9] != 0x20) - return false; - if (!IRPanasonicAc::validChecksum(results->state, nbits / 8)) return false; - } - - // Success - results->decode_type = PANASONIC_AC; - results->bits = nbits; - return true; -} -#endif // DECODE_PANASONIC_AC diff --git a/lib/IRremoteESP8266_ID1089/src/ir_Panasonic.h b/lib/IRremoteESP8266_ID1089/src/ir_Panasonic.h deleted file mode 100644 index 9d9da38..0000000 --- a/lib/IRremoteESP8266_ID1089/src/ir_Panasonic.h +++ /dev/null @@ -1,145 +0,0 @@ -// Copyright 2018 David Conran - -#ifndef IR_PANASONIC_H_ -#define IR_PANASONIC_H_ - -#define __STDC_LIMIT_MACROS -#include -#ifdef ARDUINO -#include -#else -#include -#endif -#include "IRremoteESP8266.h" -#include "IRsend.h" - -// 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 - -// Panasonic A/C support heavily influenced by: -// https://github.com/ToniA/ESPEasy/blob/HeatpumpIR/lib/HeatpumpIR/PanasonicHeatpumpIR.cpp - -// Constants -const uint16_t kPanasonicFreq = 36700; -const uint16_t kPanasonicAcExcess = 0; -// Much higher than usual. See issue #540. -const uint16_t kPanasonicAcTolerance = 40; - -const uint8_t kPanasonicAcAuto = 0; // 0b0000 -const uint8_t kPanasonicAcDry = 2; // 0b0010 -const uint8_t kPanasonicAcCool = 3; // 0b0011 -const uint8_t kPanasonicAcHeat = 4; // 0b0010 -const uint8_t kPanasonicAcFan = 6; // 0b0110 -const uint8_t kPanasonicAcFanMin = 0; -const uint8_t kPanasonicAcFanMax = 4; -const uint8_t kPanasonicAcFanAuto = 7; -const uint8_t kPanasonicAcFanOffset = 3; -const uint8_t kPanasonicAcPower = 1; // 0b1 -const uint8_t kPanasonicAcMinTemp = 16; // Celsius -const uint8_t kPanasonicAcMaxTemp = 30; // Celsius -const uint8_t kPanasonicAcFanModeTemp = 27; // Celsius -const uint8_t kPanasonicAcQuiet = 1; // 0b1 -const uint8_t kPanasonicAcPowerful = 0x20; // 0b100000 -// CKP models have Powerful and Quiet bits swapped. -const uint8_t kPanasonicAcQuietCkp = 0x20; // 0b100000 -const uint8_t kPanasonicAcPowerfulCkp = 1; // 0b1 -const uint8_t kPanasonicAcSwingVAuto = 0xF; -const uint8_t kPanasonicAcSwingVUp = 0x1; -const uint8_t kPanasonicAcSwingVDown = 0x5; -const uint8_t kPanasonicAcSwingHAuto = 0xD; -const uint8_t kPanasonicAcSwingHMiddle = 0x6; -const uint8_t kPanasonicAcSwingHFullLeft = 0x9; -const uint8_t kPanasonicAcSwingHLeft = 0xA; -const uint8_t kPanasonicAcSwingHRight = 0xB; -const uint8_t kPanasonicAcSwingHFullRight = 0xC; -const uint8_t kPanasonicAcChecksumInit = 0xF4; -const uint8_t kPanasonicAcOnTimer = 0b00000010; -const uint8_t kPanasonicAcOffTimer = 0b00000100; -const uint16_t kPanasonicAcTimeMax = 23 * 60 + 59; // Mins since midnight. -const uint16_t kPanasonicAcTimeSpecial = 0x600; - -const uint8_t kPanasonicKnownGoodState[kPanasonicAcStateLength] = { - 0x02, 0x20, 0xE0, 0x04, 0x00, 0x00, 0x00, 0x06, 0x02, - 0x20, 0xE0, 0x04, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, - 0x00, 0x0E, 0xE0, 0x00, 0x00, 0x81, 0x00, 0x00, 0x00}; - -enum panasonic_ac_remote_model_t { - kPanasonicUnknown = 0, - kPanasonicLke = 1, - kPanasonicNke = 2, - kPanasonicDke = 3, - kPanasonicJke = 4, - kPanasonicCkp = 5, -}; - -class IRPanasonicAc { - public: - explicit IRPanasonicAc(uint16_t pin); - - void stateReset(); -#if SEND_PANASONIC - void send(const uint16_t repeat = kPanasonicAcDefaultRepeat); -#endif // SEND_PANASONIC - void begin(); - void on(); - void off(); - void setPower(const bool state); - bool getPower(); - void setTemp(const uint8_t temp, const bool remember = true); - uint8_t getTemp(); - void setFan(const uint8_t fan); - uint8_t getFan(); - void setMode(const uint8_t mode); - uint8_t getMode(); - void setRaw(const uint8_t state[]); - uint8_t *getRaw(); - static bool validChecksum(uint8_t *state, - const uint16_t length = kPanasonicAcStateLength); - static uint8_t calcChecksum(uint8_t *state, - const uint16_t length = kPanasonicAcStateLength); - void setQuiet(const bool state); - bool getQuiet(); - void setPowerful(const bool state); - bool getPowerful(); - void setModel(const panasonic_ac_remote_model_t model); - panasonic_ac_remote_model_t getModel(); - void setSwingVertical(const uint8_t elevation); - uint8_t getSwingVertical(); - void setSwingHorizontal(const uint8_t direction); - uint8_t getSwingHorizontal(); - static uint16_t encodeTime(const uint8_t hours, const uint8_t mins); - uint16_t getClock(); - void setClock(const uint16_t mins_since_midnight); - uint16_t getOnTimer(); - void setOnTimer(const uint16_t mins_since_midnight, const bool enable = true); - void cancelOnTimer(); - bool isOnTimerEnabled(); - uint16_t getOffTimer(); - void setOffTimer(const uint16_t mins_since_midnight, - const bool enable = true); - void cancelOffTimer(); - bool isOffTimerEnabled(); -#ifdef ARDUINO - String toString(); - static String timeToString(const uint16_t mins_since_midnight); -#else - std::string toString(); - static std::string timeToString(const uint16_t mins_since_midnight); -#endif -#ifndef UNIT_TEST - - private: -#endif - uint8_t remote_state[kPanasonicAcStateLength]; - uint8_t _swingh; - uint8_t _temp; - void fixChecksum(const uint16_t length = kPanasonicAcStateLength); - static uint8_t calcChecksum(const uint8_t *state, - const uint16_t length = kPanasonicAcStateLength); - IRsend _irsend; -}; - -#endif // IR_PANASONIC_H_ diff --git a/lib/IRremoteESP8266_ID1089/src/ir_Pioneer.cpp b/lib/IRremoteESP8266_ID1089/src/ir_Pioneer.cpp deleted file mode 100644 index 9134e36..0000000 --- a/lib/IRremoteESP8266_ID1089/src/ir_Pioneer.cpp +++ /dev/null @@ -1,143 +0,0 @@ -// Copyright 2009 Ken Shirriff -// Copyright 2017, 2018 David Conran -// Copyright 2018 Kamil Palczewski - -#define __STDC_LIMIT_MACROS -#include -#include -#include "IRrecv.h" -#include "IRsend.h" -#include "IRutils.h" -#include "ir_NEC.h" - -// PPPP III OOO N N EEEE EEEE RRRR -// P P I O O NN N E E R R -// PPPP I O O N N N EEE EEE RRRR -// P I O O N NN E E R R -// P III OOO N N EEEE EEEE R RR - -// Ref: -// http://adrian-kingston.com/IRFormatPioneer.htm - -#if SEND_PIONEER -// Send a raw Pioneer formatted message. -// -// Args: -// data: The message to be sent. -// nbits: The number of bits of the message to be sent. -// Typically kPioneerBits. -// repeat: The number of times the command is to be repeated. -// -// Status: BETA / Expected to be working. -// -// Ref: -// http://adrian-kingston.com/IRFormatPioneer.htm -void IRsend::sendPioneer(const uint64_t data, const uint16_t nbits, - const uint16_t repeat) { - // If nbits is to big, or is odd, abort. - if (nbits > sizeof(data) * 8 || nbits % 2 == 1) return; - - // send 1st part of the code - sendNEC(data >> (nbits / 2), nbits / 2, 0); - // send 2nd part of the code - sendNEC(data & (((uint64_t)1 << (nbits / 2)) - 1), nbits / 2, repeat); -} - -// Calculate the raw Pioneer data code based on two NEC sub-codes -// Args: -// address A 16-bit "published" NEC value. -// command: A 16-bit "published" NEC value. -// Returns: -// A raw 64-bit Pioneer message code. -// -// Status: BETA / Expected to work. -// -// Note: -// Address & Command can be take from a decode result OR from the spreadsheets -// located at: -// https://www.pioneerelectronics.com/PUSA/Support/Home-Entertainment-Custom-Install/IR+Codes/A+V+Receivers -// where the first part is considered the address, -// and the second the command. -// e.g. -// "A556+AF20" is an Address of 0xA556 & a Command of 0xAF20. -uint64_t IRsend::encodePioneer(const uint16_t address, const uint16_t command) { - return (((uint64_t)encodeNEC(address >> 8, address & 0xFF)) << 32) | - encodeNEC(command >> 8, command & 0xFF); -} -#endif // SEND_PIONEER - -#if DECODE_PIONEER -// Decode the supplied Pioneer message. -// -// Args: -// results: Ptr to the data to decode and where to store the decode result. -// nbits: The number of data bits to expect. Typically kPioneerBits. -// strict: Flag indicating if we should perform strict matching. -// Returns: -// boolean: True if it can decode it, false if it can't. -// -// Status: BETA / Should be working. (Self decodes & real examples) -// -bool IRrecv::decodePioneer(decode_results *results, const uint16_t nbits, - const bool strict) { - if (results->rawlen < 2 * (nbits + kHeader + kFooter) - 1) - return false; // Can't possibly be a valid Pioneer message. - if (strict && nbits != kPioneerBits) - return false; // Not strictly an Pioneer message. - - uint64_t data = 0; - uint16_t offset = kStartOffset; - - for (uint16_t section = 0; section < 2; section++) { - // Header - if (!matchMark(results->rawbuf[offset], kNecHdrMark)) return false; - // Calculate how long the lowest tick time is based on the header mark. - uint32_t mark_tick = - results->rawbuf[offset++] * kRawTick / kNecHdrMarkTicks; - if (!matchSpace(results->rawbuf[offset], kNecHdrSpace)) return false; - // Calculate how long the common tick time is based on the header space. - uint32_t space_tick = - results->rawbuf[offset++] * kRawTick / kNecHdrSpaceTicks; - // - // Data - match_result_t data_result = matchData( - &(results->rawbuf[offset]), nbits / 2, kNecBitMarkTicks * mark_tick, - kNecOneSpaceTicks * space_tick, kNecBitMarkTicks * mark_tick, - kNecZeroSpaceTicks * space_tick); - if (data_result.success == false) return false; - uint8_t command = data_result.data >> 8; - uint8_t command_inverted = data_result.data; - uint8_t address = data_result.data >> 24; - uint8_t address_inverted = data_result.data >> 16; - // Compliance - if (strict) { - if (command != (command_inverted ^ 0xFF)) - return false; // Command integrity failed. - if (address != (address_inverted ^ 0xFF)) - return false; // Address integrity failed. - } - data = (data << (nbits / 2)) + data_result.data; - offset += data_result.used; - // NEC-like commands and addresses are technically in LSB first order so the - // final versions have to be reversed. - uint16_t code = reverseBits((command << 8) + address, 16); - if (section) - results->command = code; - else - results->address = code; - - // Footer - if (!matchMark(results->rawbuf[offset++], kNecBitMarkTicks * mark_tick)) - return false; - if (offset < results->rawlen && - !matchAtLeast(results->rawbuf[offset++], kNecMinGapTicks * space_tick)) - return false; - } - - // Success - results->bits = nbits; - results->value = data; - results->decode_type = PIONEER; - return true; -} -#endif // DECODE_PIONEER diff --git a/lib/IRremoteESP8266_ID1089/src/ir_Pronto.cpp b/lib/IRremoteESP8266_ID1089/src/ir_Pronto.cpp deleted file mode 100644 index 9ab5c76..0000000 --- a/lib/IRremoteESP8266_ID1089/src/ir_Pronto.cpp +++ /dev/null @@ -1,108 +0,0 @@ -// Copyright 2017 David Conran - -#include -#include "IRsend.h" - -// PPPPPP tt -// PP PP rr rr oooo nn nnn tt oooo -// PPPPPP rrr r oo oo nnn nn tttt oo oo -// PP rr oo oo nn nn tt oo oo -// PP rr oooo nn nn tttt oooo - -// Constants -const float kProntoFreqFactor = 0.241246; -const uint16_t kProntoTypeOffset = 0; -const uint16_t kProntoFreqOffset = 1; -const uint16_t kProntoSeq1LenOffset = 2; -const uint16_t kProntoSeq2LenOffset = 3; -const uint16_t kProntoDataOffset = 4; - -#if SEND_PRONTO -// Send a Pronto Code formatted message. -// -// Args: -// data: An array of uint16_t containing the pronto codes. -// len: Nr. of entries in the data[] array. -// repeat: Nr. of times to repeat the message. -// -// Status: ALPHA / Not tested in the real world. -// -// Note: -// Pronto codes are typically represented in hexadecimal. -// You will need to convert the code to an array of integers, and calculate -// it's length. -// e.g. -// A Sony 20 bit DVD remote command. -// "0000 0067 0000 0015 0060 0018 0018 0018 0030 0018 0030 0018 0030 0018 -// 0018 0018 0030 0018 0018 0018 0018 0018 0030 0018 0018 0018 0030 0018 -// 0030 0018 0030 0018 0018 0018 0018 0018 0030 0018 0018 0018 0018 0018 -// 0030 0018 0018 03f6" -// -// converts to: -// -// uint16_t prontoCode[46] = { -// 0x0000, 0x0067, 0x0000, 0x0015, -// 0x0060, 0x0018, 0x0018, 0x0018, 0x0030, 0x0018, 0x0030, 0x0018, -// 0x0030, 0x0018, 0x0018, 0x0018, 0x0030, 0x0018, 0x0018, 0x0018, -// 0x0018, 0x0018, 0x0030, 0x0018, 0x0018, 0x0018, 0x0030, 0x0018, -// 0x0030, 0x0018, 0x0030, 0x0018, 0x0018, 0x0018, 0x0018, 0x0018, -// 0x0030, 0x0018, 0x0018, 0x0018, 0x0018, 0x0018, 0x0030, 0x0018, -// 0x0018, 0x03f6}; -// // Send the Pronto(Sony) code. Repeat twice as Sony's require that. -// sendPronto(prontoCode, 46, kSonyMinRepeat); -// -// Ref: -// http://www.etcwiki.org/wiki/Pronto_Infrared_Format -// http://www.remotecentral.com/features/irdisp2.htm -void IRsend::sendPronto(uint16_t data[], uint16_t len, uint16_t repeat) { - // Check we have enough data to work out what to send. - if (len < kProntoMinLength) return; - - // We only know how to deal with 'raw' pronto codes types. Reject all others. - if (data[kProntoTypeOffset] != 0) return; - - // Pronto frequency is in Hz. - uint16_t hz = - (uint16_t)(1000000U / (data[kProntoFreqOffset] * kProntoFreqFactor)); - enableIROut(hz); - - // Grab the length of the two sequences. - uint16_t seq_1_len = data[kProntoSeq1LenOffset] * 2; - uint16_t seq_2_len = data[kProntoSeq2LenOffset] * 2; - // Calculate where each sequence starts in the buffer. - uint16_t seq_1_start = kProntoDataOffset; - uint16_t seq_2_start = kProntoDataOffset + seq_1_len; - - uint32_t periodic_time = calcUSecPeriod(hz, false); - - // Normal (1st sequence) case. - // Is there a first (normal) sequence to send? - if (seq_1_len > 0) { - // Check we have enough data to send the complete first sequence. - if (seq_1_len + seq_1_start > len) return; - // Send the contents of the 1st sequence. - for (uint16_t i = seq_1_start; i < seq_1_start + seq_1_len; i += 2) { - mark(data[i] * periodic_time); - space(data[i + 1] * periodic_time); - } - } else { - // There was no first sequence to send, it is implied that we have to send - // the 2nd/repeat sequence an additional time. i.e. At least once. - repeat++; - } - - // Repeat (2nd sequence) case. - // Is there a second (repeat) sequence to be sent? - if (seq_2_len > 0) { - // Check we have enough data to send the complete second sequence. - if (seq_2_len + seq_2_start > len) return; - - // Send the contents of the 2nd sequence. - for (uint16_t r = 0; r < repeat; r++) - for (uint16_t i = seq_2_start; i < seq_2_start + seq_2_len; i += 2) { - mark(data[i] * periodic_time); - space(data[i + 1] * periodic_time); - } - } -} -#endif diff --git a/lib/IRremoteESP8266_ID1089/src/ir_RC5_RC6.cpp b/lib/IRremoteESP8266_ID1089/src/ir_RC5_RC6.cpp deleted file mode 100644 index ef1500d..0000000 --- a/lib/IRremoteESP8266_ID1089/src/ir_RC5_RC6.cpp +++ /dev/null @@ -1,524 +0,0 @@ -// Copyright 2009 Ken Shirriff -// Copyright 2017 David Conran - -#include -#include "IRrecv.h" -#include "IRsend.h" -#include "IRtimer.h" -#include "IRutils.h" - -// RRRRRR CCCCC 555555 XX XX RRRRRR CCCCC 666 -// RR RR CC C 55 XX XX RR RR CC C 66 -// RRRRRR CC _____ 555555 XXXX RRRRRR CC _____ 666666 -// RR RR CC C 5555 XX XX RR RR CC C 66 66 -// RR RR CCCCC 555555 XX XX RR RR CCCCC 66666 - -// RC-5 & RC-6 support added from https://github.com/z3t0/Arduino-IRremote -// RC-5X support added by David Conran - -// Constants -// RC-5/RC-5X -// Ref: -// https://en.wikipedia.org/wiki/RC-5 -// http://www.sbprojects.com/knowledge/ir/rc5.php - -const uint16_t kRc5T1 = 889; -const uint32_t kRc5MinCommandLength = 113778; -const uint32_t kRc5MinGap = kRc5MinCommandLength - kRC5RawBits * (2 * kRc5T1); -const uint16_t kRc5ToggleMask = 0x800; // The 12th bit. -const uint16_t kRc5SamplesMin = 11; - -// RC-6 -// Ref: -// https://en.wikipedia.org/wiki/RC-6 -// http://www.pcbheaven.com/userpages/The_Philips_RC6_Protocol/ - -const uint16_t kRc6Tick = 444; -const uint16_t kRc6HdrMarkTicks = 6; -const uint16_t kRc6HdrMark = kRc6HdrMarkTicks * kRc6Tick; -const uint16_t kRc6HdrSpaceTicks = 2; -const uint16_t kRc6HdrSpace = kRc6HdrSpaceTicks * kRc6Tick; -const uint16_t kRc6RptLengthTicks = 187; -const uint32_t kRc6RptLength = kRc6RptLengthTicks * kRc6Tick; -const uint32_t kRc6ToggleMask = 0x10000UL; // The 17th bit. -const uint16_t kRc6_36ToggleMask = 0x8000; // The 16th bit. - -// Common (getRClevel()) -const int16_t kMark = 0; -const int16_t kSpace = 1; - -#if SEND_RC5 -// Send a Philips RC-5/RC-5X packet. -// -// Args: -// data: The message you wish to send. -// nbits: Bit size of the protocol you want to send. -// repeat: Nr. of extra times the data will be sent. -// -// Status: RC-5 (stable), RC-5X (alpha) -// -// Note: -// Caller needs to take care of flipping the toggle bit. -// That bit differentiates between key press & key release. -// For RC-5 it is the MSB of the data. -// For RC-5X it is the 2nd MSB of the data. -// Ref: -// http://www.sbprojects.com/knowledge/ir/rc5.php -// https://en.wikipedia.org/wiki/RC-5 -// https://en.wikipedia.org/wiki/Manchester_code -// TODO(anyone): -// Testing of the RC-5X components. -void IRsend::sendRC5(uint64_t data, uint16_t nbits, uint16_t repeat) { - if (nbits > sizeof(data) * 8) return; // We can't send something that big. - bool skipSpace = true; - bool field_bit = true; - // Set 36kHz IR carrier frequency & a 1/4 (25%) duty cycle. - enableIROut(36, 25); - - if (nbits >= kRC5XBits) { // Is this a RC-5X message? - // field bit is the inverted MSB of RC-5X data. - field_bit = ((data >> (nbits - 1)) ^ 1) & 1; - nbits--; - } - - IRtimer usecTimer = IRtimer(); - for (uint16_t i = 0; i <= repeat; i++) { - usecTimer.reset(); - - // Header - // First start bit (0x1). space, then mark. - if (skipSpace) - skipSpace = false; // First time through, we assume the leading space(). - else - space(kRc5T1); - mark(kRc5T1); - // Field/Second start bit. - if (field_bit) { // Send a 1. Normal for RC-5. - space(kRc5T1); - mark(kRc5T1); - } else { // Send a 0. Special case for RC-5X. Means 7th command bit is 1. - mark(kRc5T1); - space(kRc5T1); - } - - // Data - for (uint64_t mask = 1ULL << (nbits - 1); mask; mask >>= 1) - if (data & mask) { // 1 - space(kRc5T1); // 1 is space, then mark. - mark(kRc5T1); - } else { // 0 - mark(kRc5T1); // 0 is mark, then space. - space(kRc5T1); - } - // Footer - space(std::max(kRc5MinGap, kRc5MinCommandLength - usecTimer.elapsed())); - } -} - -// Encode a Philips RC-5 data message. -// -// Args: -// address: The 5-bit address value for the message. -// command: The 6-bit command value for the message. -// key_released: Boolean flag indicating if the remote key has been released. -// -// Returns: -// A data message suitable for use in sendRC5(). -// -// Status: Beta / Should be working. -// -// Ref: -// http://www.sbprojects.com/knowledge/ir/rc5.php -// https://en.wikipedia.org/wiki/RC-5 -uint16_t IRsend::encodeRC5(uint8_t address, uint8_t command, - bool key_released) { - return (key_released << (kRC5Bits - 1)) | ((address & 0x1f) << 6) | - (command & 0x3F); -} - -// Encode a Philips RC-5X data message. -// -// Args: -// address: The 5-bit address value for the message. -// command: The 7-bit command value for the message. -// key_released: Boolean flag indicating if the remote key has been released. -// -// Returns: -// A data message suitable for use in sendRC5(). -// -// Status: Beta / Should be working. -// -// Ref: -// http://www.sbprojects.com/knowledge/ir/rc5.php -// https://en.wikipedia.org/wiki/RC-5 -uint16_t IRsend::encodeRC5X(uint8_t address, uint8_t command, - bool key_released) { - // The 2nd start/field bit (MSB of the return value) is the value of the 7th - // command bit. - bool s2 = (command >> 6) & 1; - return ((uint16_t)s2 << (kRC5XBits - 1)) | - encodeRC5(address, command, key_released); -} - -// Flip the toggle bit of a Philips RC-5/RC-5X data message. -// Used to indicate a change of remote button's state. -// -// Args: -// data: The existing RC-5/RC-5X message. -// -// Returns: -// A data message suitable for use in sendRC5() with the toggle bit flipped. -// -// Status: STABLE. -// -// Ref: -// http://www.sbprojects.com/knowledge/ir/rc5.php -// https://en.wikipedia.org/wiki/RC-5 -uint64_t IRsend::toggleRC5(uint64_t data) { return data ^ kRc5ToggleMask; } -#endif // SEND_RC5 - -#if SEND_RC6 -// Flip the toggle bit of a Philips RC-6 data message. -// Used to indicate a change of remote button's state. -// For RC-6 (20-bits), it is the 17th least significant bit. -// for RC-6 (36-bits/Xbox-360), it is the 16th least significant bit. -// -// Args: -// data: The existing RC-6 message. -// nbits: Nr. of bits in the RC-6 protocol. -// -// Returns: -// A data message suitable for use in sendRC6() with the toggle bit flipped. -// -// Status: BETA / Should work fine. -// -// Ref: -// http://www.sbprojects.com/knowledge/ir/rc6.php -// http://www.righto.com/2010/12/64-bit-rc6-codes-arduino-and-xbox.html -uint64_t IRsend::toggleRC6(uint64_t data, uint16_t nbits) { - if (nbits == kRC6_36Bits) return data ^ kRc6_36ToggleMask; - return data ^ kRc6ToggleMask; -} - -// Encode a Philips RC-6 data message. -// -// Args: -// address: The address (aka. control) value for the message. -// Includes the field/mode/toggle bits. -// command: The 8-bit command value for the message. (aka. information) -// mode: Which protocol to use. Defined by nr. of bits in the protocol. -// -// Returns: -// A data message suitable for use in sendRC6(). -// -// Status: Beta / Should be working. -// -// Ref: -// http://www.sbprojects.com/knowledge/ir/rc6.php -// http://www.righto.com/2010/12/64-bit-rc6-codes-arduino-and-xbox.html -// http://www.pcbheaven.com/userpages/The_Philips_RC6_Protocol/ -uint64_t IRsend::encodeRC6(uint32_t address, uint8_t command, uint16_t mode) { - switch (mode) { - case kRC6Mode0Bits: - return ((address & 0xFFF) << 8) | (command & 0xFF); - case kRC6_36Bits: - return ((uint64_t)(address & 0xFFFFFFF) << 8) | (command & 0xFF); - default: - return 0; - } -} - -// Send a Philips RC-6 packet. -// Note: Caller needs to take care of flipping the toggle bit (The 4th Most -// Significant Bit). That bit differentiates between key press & key release. -// -// Args: -// data: The message you wish to send. -// nbits: Bit size of the protocol you want to send. -// repeat: Nr. of extra times the data will be sent. -// -// Status: Stable. -// -// Ref: -// http://www.sbprojects.com/knowledge/ir/rc6.php -// http://www.righto.com/2010/12/64-bit-rc6-codes-arduino-and-xbox.html -// https://en.wikipedia.org/wiki/Manchester_code -void IRsend::sendRC6(uint64_t data, uint16_t nbits, uint16_t repeat) { - // Check we can send the number of bits requested. - if (nbits > sizeof(data) * 8) return; - // Set 36kHz IR carrier frequency & a 1/3 (33%) duty cycle. - enableIROut(36, 33); - for (uint16_t r = 0; r <= repeat; r++) { - // Header - mark(kRc6HdrMark); - space(kRc6HdrSpace); - // Start bit. - mark(kRc6Tick); // mark, then space == 0x1. - space(kRc6Tick); - // Data - uint16_t bitTime; - for (uint64_t i = 1, mask = 1ULL << (nbits - 1); mask; i++, mask >>= 1) { - if (i == 4) // The fourth bit we send is a "double width trailer bit". - bitTime = 2 * kRc6Tick; // double-wide trailer bit - else - bitTime = kRc6Tick; // Normal bit - if (data & mask) { // 1 - mark(bitTime); - space(bitTime); - } else { // 0 - space(bitTime); - mark(bitTime); - } - } - // Footer - space(kRc6RptLength); - } -} -#endif // SEND_RC6 - -#if (DECODE_RC5 || DECODE_RC6 || DECODE_LASERTAG) -// 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. -// -// Args: -// results: Ptr to the data to decode and where to store the decode result. -// offset: Ptr to the currect offset to the rawbuf. -// used: Ptr to the current used counter. -// bitTime: Time interval of single bit in microseconds. -// maxwidth: Maximum number of successive levels to find in a single level -// (default 3) -// Returns: -// int: MARK, SPACE, or -1 for error (The measured time interval is not a -// multiple of t1.) -// Ref: -// https://en.wikipedia.org/wiki/Manchester_code -int16_t IRrecv::getRClevel(decode_results *results, uint16_t *offset, - uint16_t *used, uint16_t bitTime, uint8_t tolerance, - int16_t excess, uint16_t delta, uint8_t maxwidth) { - DPRINT("DEBUG: getRClevel: offset = "); - DPRINTLN(uint64ToString(*offset)); - DPRINT("DEBUG: getRClevel: rawlen = "); - DPRINTLN(uint64ToString(results->rawlen)); - if (*offset >= results->rawlen) { - DPRINTLN("DEBUG: getRClevel: SPACE, past end of rawbuf"); - return kSpace; // After end of recorded buffer, assume SPACE. - } - uint16_t width = results->rawbuf[*offset]; - // If the value of offset is odd, it's a MARK. Even, it's a SPACE. - uint16_t val = ((*offset) % 2) ? kMark : kSpace; - // Check to see if we have hit an inter-message gap (> 20ms). - if (val == kSpace && - (width > 20000 - delta || width > maxwidth * bitTime + delta)) { - DPRINTLN("DEBUG: getRClevel: SPACE, hit end of mesg gap."); - return kSpace; - } - int16_t correction = (val == kMark) ? excess : -excess; - - // Calculate the look-ahead for our current position in the buffer. - uint16_t avail; - // Note: We want to match in greedy order as the other way leads to - // mismatches due to overlaps induced by the correction and tolerance - // values. - for (avail = maxwidth; avail > 0; avail--) { - if (match(width, avail * bitTime + correction, tolerance, delta)) { - break; - } - } - if (!avail) { - DPRINTLN("DEBUG: getRClevel: Unexpected width. Exiting."); - return -1; // The width is not what we expected. - } - - (*used)++; // Count another one of the avail slots as used. - if (*used >= avail) { // Are we out of look-ahead/avail slots? - // Yes, so reset the used counter, and move the offset ahead. - *used = 0; - (*offset)++; - } - if (val == kMark) { - DPRINTLN("DEBUG: getRClevel: MARK"); - } else { - DPRINTLN("DEBUG: getRClevel: SPACE"); - } - - return val; -} -#endif // (DECODE_RC5 || DECODE_RC6 || DECODE_LASERTAG) - -#if DECODE_RC5 -// Decode the supplied RC-5/RC5X message. -// -// Args: -// results: Ptr to the data to decode and where to store the decode result. -// nbits: The number of data bits to expect. -// strict: Flag indicating if we should perform strict matching. -// Returns: -// boolean: True if it can decode it, false if it can't. -// -// Status: RC-5 (stable), RC-5X (alpha) -// -// Note: -// The 'toggle' bit is included as the 6th (MSB) address bit, the MSB of data, -// & in the count of bits decoded. -// Ref: -// http://www.sbprojects.com/knowledge/ir/rc5.php -// https://en.wikipedia.org/wiki/RC-5 -// https://en.wikipedia.org/wiki/Manchester_code -// TODO(anyone): -// Serious testing of the RC-5X and strict aspects needs to be done. -bool IRrecv::decodeRC5(decode_results *results, uint16_t nbits, bool strict) { - if (results->rawlen < kRc5SamplesMin + kHeader - 1) return false; - - // Compliance - if (strict && nbits != kRC5Bits && nbits != kRC5XBits) - return false; // It's neither RC-5 or RC-5X. - - uint16_t offset = kStartOffset; - uint16_t used = 0; - bool is_rc5x = false; - uint64_t data = 0; - - // Header - // Get start bit #1. - if (getRClevel(results, &offset, &used, kRc5T1) != kMark) return false; - // Get field/start bit #2 (inverted bit-7 of the command if RC-5X protocol) - uint16_t actual_bits = 1; - int16_t levelA = getRClevel(results, &offset, &used, kRc5T1); - int16_t levelB = getRClevel(results, &offset, &used, kRc5T1); - if (levelA == kSpace && levelB == kMark) { // Matched a 1. - is_rc5x = false; - } else if (levelA == kMark && levelB == kSpace) { // Matched a 0. - if (nbits <= kRC5Bits) return false; // Field bit must be '1' for RC5. - is_rc5x = true; - data = 1; - } else { - return false; // Not what we expected. - } - - // Data - for (; offset < results->rawlen; actual_bits++) { - int16_t levelA = getRClevel(results, &offset, &used, kRc5T1); - int16_t levelB = getRClevel(results, &offset, &used, kRc5T1); - if (levelA == kSpace && levelB == kMark) - data = (data << 1) | 1; // 1 - else if (levelA == kMark && levelB == kSpace) - data <<= 1; // 0 - else - break; - } - // Footer (None) - - // Compliance - if (actual_bits < nbits) return false; // Less data than we expected. - if (strict && actual_bits != kRC5Bits && actual_bits != kRC5XBits) - return false; - - // Success - results->value = data; - results->address = (data >> 6) & 0x1F; - results->command = data & 0x3F; - results->repeat = false; - if (is_rc5x) { - results->decode_type = RC5X; - results->command |= ((uint32_t)is_rc5x) << 6; - } else { - results->decode_type = RC5; - actual_bits--; // RC5 doesn't count the field bit as data. - } - results->bits = actual_bits; - return true; -} -#endif // DECODE_RC5 - -#if DECODE_RC6 -// Decode the supplied RC6 message. -// -// Args: -// results: Ptr to the data to decode and where to store the decode result. -// nbits: The number of data bits to expect. -// strict: Flag indicating if we should perform strict matching. -// Returns: -// boolean: True if it can decode it, false if it can't. -// -// Status: Stable. -// -// Ref: -// http://www.sbprojects.com/knowledge/ir/rc6.php -// https://en.wikipedia.org/wiki/Manchester_code -// TODO(anyone): -// Testing of the strict compliance aspects. -bool IRrecv::decodeRC6(decode_results *results, uint16_t nbits, bool strict) { - if (results->rawlen < kHeader + 2 + 4) // Up to the double-wide T bit. - return false; // Smaller than absolute smallest possible RC6 message. - - if (strict) { // Compliance - // Unlike typical protocols, the ability to have mark+space, and space+mark - // as data bits means it is possible to only have nbits of entries for the - // data portion, rather than the typically required 2 * nbits. - // Also due to potential melding with the start bit, we can only count - // the start bit as 1, instead of a more typical 2 value. The header still - // remains as normal. - if (results->rawlen < nbits + kHeader + 1) - return false; // Don't have enough entries/samples to be valid. - switch (nbits) { - case kRC6Mode0Bits: - case kRC6_36Bits: - break; - default: - return false; // Asking for the wrong number of bits. - } - } - - uint16_t offset = kStartOffset; - - // Header - if (!matchMark(results->rawbuf[offset], kRc6HdrMark)) return false; - // Calculate how long the common tick time is based on the header mark. - uint32_t tick = results->rawbuf[offset++] * kRawTick / kRc6HdrMarkTicks; - if (!matchSpace(results->rawbuf[offset++], kRc6HdrSpaceTicks * tick)) - return false; - - uint16_t used = 0; - - // Get the start bit. e.g. 1. - if (getRClevel(results, &offset, &used, tick) != kMark) return false; - if (getRClevel(results, &offset, &used, tick) != kSpace) return false; - - uint16_t actual_bits; - uint64_t data = 0; - - // Data (Warning: Here be dragons^Wpointers!!) - for (actual_bits = 0; offset < results->rawlen; actual_bits++) { - int16_t levelA, levelB; // Next two levels - levelA = getRClevel(results, &offset, &used, tick); - // T bit is double wide; make sure second half matches - if (actual_bits == 3 && levelA != getRClevel(results, &offset, &used, tick)) - return false; - levelB = getRClevel(results, &offset, &used, tick); - // T bit is double wide; make sure second half matches - if (actual_bits == 3 && levelB != getRClevel(results, &offset, &used, tick)) - return false; - if (levelA == kMark && levelB == kSpace) // reversed compared to RC5 - data = (data << 1) | 1; // 1 - else if (levelA == kSpace && levelB == kMark) - data <<= 1; // 0 - else - break; - } - - // More compliance - if (strict && actual_bits != nbits) - return false; // Actual nr. of bits didn't match expected. - - // Success - results->decode_type = RC6; - results->bits = actual_bits; - results->value = data; - results->address = data >> 8; - results->command = data & 0xFF; - return true; -} -#endif // DECODE_RC6 diff --git a/lib/IRremoteESP8266_ID1089/src/ir_RCMM.cpp b/lib/IRremoteESP8266_ID1089/src/ir_RCMM.cpp deleted file mode 100644 index 1b03d2c..0000000 --- a/lib/IRremoteESP8266_ID1089/src/ir_RCMM.cpp +++ /dev/null @@ -1,180 +0,0 @@ -// Copyright 2017 David Conran - -#include -#include "IRrecv.h" -#include "IRsend.h" -#include "IRtimer.h" -#include "IRutils.h" - -// RRRRRR CCCCC MM MM MM MM -// RR RR CC C MMM MMM MMM MMM -// RRRRRR CC _____ MM MM MM MM MM MM -// RR RR CC C MM MM MM MM -// RR RR CCCCC MM MM MM MM - -// Send & decode support for RC-MM added by David Conran - -// Constants -// Ref: -// http://www.sbprojects.com/knowledge/ir/rcmm.php -const uint16_t kRcmmTick = 28; // Technically it would be 27.777* -const uint16_t kRcmmHdrMarkTicks = 15; -const uint16_t kRcmmHdrMark = 416; -const uint16_t kRcmmHdrSpaceTicks = 10; -const uint16_t kRcmmHdrSpace = 277; -const uint16_t kRcmmBitMarkTicks = 6; -const uint16_t kRcmmBitMark = 166; -const uint16_t kRcmmBitSpace0Ticks = 10; -const uint16_t kRcmmBitSpace0 = 277; -const uint16_t kRcmmBitSpace1Ticks = 16; -const uint16_t kRcmmBitSpace1 = 444; -const uint16_t kRcmmBitSpace2Ticks = 22; -const uint16_t kRcmmBitSpace2 = 611; -const uint16_t kRcmmBitSpace3Ticks = 28; -const uint16_t kRcmmBitSpace3 = 777; -const uint16_t kRcmmRptLengthTicks = 992; -const uint32_t kRcmmRptLength = 27778; -const uint16_t kRcmmMinGapTicks = 120; -const uint32_t kRcmmMinGap = 3360; -// Use a tolerance of +/-10% when matching some data spaces. -const uint8_t kRcmmTolerance = 10; -const uint16_t kRcmmExcess = 50; - -#if SEND_RCMM -// Send a Philips RC-MM packet. -// -// Args: -// data: The data we want to send. MSB first. -// nbits: The number of bits of data to send. (Typically 12, 24, or 32[Nokia]) -// repeat: The nr. of times the message should be sent. -// -// Status: BETA / Should be working. -// -// Ref: -// http://www.sbprojects.com/knowledge/ir/rcmm.php -void IRsend::sendRCMM(uint64_t data, uint16_t nbits, uint16_t repeat) { - // Set 36kHz IR carrier frequency & a 1/3 (33%) duty cycle. - enableIROut(36, 33); - IRtimer usecs = IRtimer(); - - for (uint16_t r = 0; r <= repeat; r++) { - usecs.reset(); - // Header - mark(kRcmmHdrMark); - space(kRcmmHdrSpace); - // Data - uint64_t mask = 0b11ULL << (nbits - 2); - // RC-MM sends data 2 bits at a time. - for (int32_t i = nbits; i > 0; i -= 2) { - mark(kRcmmBitMark); - // Grab the next Most Significant Bits to send. - switch ((data & mask) >> (i - 2)) { - case 0b00: - space(kRcmmBitSpace0); - break; - case 0b01: - space(kRcmmBitSpace1); - break; - case 0b10: - space(kRcmmBitSpace2); - break; - case 0b11: - space(kRcmmBitSpace3); - break; - } - mask >>= 2; - } - // Footer - mark(kRcmmBitMark); - // Protocol requires us to wait at least kRcmmRptLength usecs from the - // start or kRcmmMinGap usecs. - space(std::max(kRcmmRptLength - usecs.elapsed(), kRcmmMinGap)); - } -} -#endif - -#if DECODE_RCMM -// Decode a Philips RC-MM packet (between 12 & 32 bits) if possible. -// Places successful decode information in the results pointer. -// Args: -// results: Ptr to the data to decode and where to store the decode result. -// nbits: Nr. of bits to expect in the data portion. Typically kRCMMBits. -// strict: Flag to indicate if we strictly adhere to the specification. -// Returns: -// boolean: True if it can decode it, false if it can't. -// -// Status: BETA / Should be working. -// -// Ref: -// http://www.sbprojects.com/knowledge/ir/rcmm.php -bool IRrecv::decodeRCMM(decode_results *results, uint16_t nbits, bool strict) { - uint64_t data = 0; - uint16_t offset = kStartOffset; - - if (results->rawlen <= 4) - return false; // Not enough entries to ever be RCMM. - - // Calc the maximum size in bits, the message can be, or that we can accept. - int16_t maxBitSize = - std::min((uint16_t)results->rawlen - 5, (uint16_t)sizeof(data) * 8); - // Compliance - if (strict) { - // Technically the spec says bit sizes should be 12 xor 24. however - // 32 bits has been seen from a device. We are going to assume - // 12 <= bits <= 32 is the 'required' bit length for the spec. - if (maxBitSize < 12 || maxBitSize > 32) return false; - if (maxBitSize < nbits) - return false; // Short cut, we can never reach the expected nr. of bits. - } - // Header decode - if (!matchMark(results->rawbuf[offset], kRcmmHdrMark)) return false; - // Calculate how long the common tick time is based on the header mark. - uint32_t m_tick = results->rawbuf[offset++] * kRawTick / kRcmmHdrMarkTicks; - if (!matchSpace(results->rawbuf[offset], kRcmmHdrSpace)) return false; - // Calculate how long the common tick time is based on the header space. - uint32_t s_tick = results->rawbuf[offset++] * kRawTick / kRcmmHdrSpaceTicks; - - // Data decode - // RC-MM has two bits of data per mark/space pair. - uint16_t actualBits; - for (actualBits = 0; actualBits < maxBitSize; actualBits += 2, offset++) { - if (!match(results->rawbuf[offset++], kRcmmBitMarkTicks * m_tick)) - return false; - - data <<= 2; - // Use non-default tolerance & excess for matching some of the spaces as the - // defaults are too generous and causes mis-matches in some cases. - if (match(results->rawbuf[offset], kRcmmBitSpace0Ticks * s_tick, - kTolerance)) - data += 0; - else if (match(results->rawbuf[offset], kRcmmBitSpace1Ticks * s_tick, - kTolerance)) - data += 1; - else if (match(results->rawbuf[offset], kRcmmBitSpace2Ticks * s_tick, - kRcmmTolerance)) - data += 2; - else if (match(results->rawbuf[offset], kRcmmBitSpace3Ticks * s_tick, - kRcmmTolerance)) - data += 3; - else - return false; - } - // Footer decode - if (!match(results->rawbuf[offset++], kRcmmBitMarkTicks * m_tick)) - return false; - if (offset < results->rawlen && - !matchAtLeast(results->rawbuf[offset], kRcmmMinGapTicks * s_tick)) - return false; - - // Compliance - if (strict && actualBits != nbits) return false; - - // Success - results->value = data; - results->decode_type = RCMM; - results->bits = actualBits; - results->address = 0; - results->command = 0; - return true; -} -#endif diff --git a/lib/IRremoteESP8266_ID1089/src/ir_Samsung.cpp b/lib/IRremoteESP8266_ID1089/src/ir_Samsung.cpp deleted file mode 100644 index a9bbb5e..0000000 --- a/lib/IRremoteESP8266_ID1089/src/ir_Samsung.cpp +++ /dev/null @@ -1,590 +0,0 @@ -// Copyright 2009 Ken Shirriff -// Copyright 2017 David Conran - -#include "ir_Samsung.h" -#include -#ifndef ARDUINO -#include -#endif -#include "IRrecv.h" -#include "IRsend.h" -#include "IRutils.h" - -// 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 - -// Samsung originally added from https://github.com/shirriff/Arduino-IRremote/ - -// Constants -// Ref: -// http://elektrolab.wz.cz/katalog/samsung_protocol.pdf -const uint16_t kSamsungTick = 560; -const uint16_t kSamsungHdrMarkTicks = 8; -const uint16_t kSamsungHdrMark = kSamsungHdrMarkTicks * kSamsungTick; -const uint16_t kSamsungHdrSpaceTicks = 8; -const uint16_t kSamsungHdrSpace = kSamsungHdrSpaceTicks * kSamsungTick; -const uint16_t kSamsungBitMarkTicks = 1; -const uint16_t kSamsungBitMark = kSamsungBitMarkTicks * kSamsungTick; -const uint16_t kSamsungOneSpaceTicks = 3; -const uint16_t kSamsungOneSpace = kSamsungOneSpaceTicks * kSamsungTick; -const uint16_t kSamsungZeroSpaceTicks = 1; -const uint16_t kSamsungZeroSpace = kSamsungZeroSpaceTicks * kSamsungTick; -const uint16_t kSamsungRptSpaceTicks = 4; -const uint16_t kSamsungRptSpace = kSamsungRptSpaceTicks * kSamsungTick; -const uint16_t kSamsungMinMessageLengthTicks = 193; -const uint32_t kSamsungMinMessageLength = - kSamsungMinMessageLengthTicks * kSamsungTick; -const uint16_t kSamsungMinGapTicks = - kSamsungMinMessageLengthTicks - - (kSamsungHdrMarkTicks + kSamsungHdrSpaceTicks + - kSamsungBits * (kSamsungBitMarkTicks + kSamsungOneSpaceTicks) + - kSamsungBitMarkTicks); -const uint32_t kSamsungMinGap = kSamsungMinGapTicks * kSamsungTick; - -const uint16_t kSamsungAcHdrMark = 690; -const uint16_t kSamsungAcHdrSpace = 17844; -const uint8_t kSamsungAcSections = 2; -const uint16_t kSamsungAcSectionMark = 3086; -const uint16_t kSamsungAcSectionSpace = 8864; -const uint16_t kSamsungAcSectionGap = 2886; -const uint16_t kSamsungAcBitMark = 586; -const uint16_t kSamsungAcOneSpace = 1432; -const uint16_t kSamsungAcZeroSpace = 436; - -#if SEND_SAMSUNG -// Send a Samsung formatted message. -// Samsung has a separate message to indicate a repeat, like NEC does. -// TODO(crankyoldgit): Confirm that is actually how Samsung sends a repeat. -// The refdoc doesn't indicate it is true. -// -// Args: -// data: The message to be sent. -// nbits: The bit size of the message being sent. typically kSamsungBits. -// repeat: The number of times the message is to be repeated. -// -// Status: BETA / Should be working. -// -// Ref: http://elektrolab.wz.cz/katalog/samsung_protocol.pdf -void IRsend::sendSAMSUNG(uint64_t data, uint16_t nbits, uint16_t repeat) { - sendGeneric(kSamsungHdrMark, kSamsungHdrSpace, kSamsungBitMark, - kSamsungOneSpace, kSamsungBitMark, kSamsungZeroSpace, - kSamsungBitMark, kSamsungMinGap, kSamsungMinMessageLength, data, - nbits, 38, true, repeat, 33); -} - -// Construct a raw Samsung message from the supplied customer(address) & -// command. -// -// Args: -// customer: The customer code. (aka. Address) -// command: The command code. -// Returns: -// A raw 32-bit Samsung message suitable for sendSAMSUNG(). -// -// Status: BETA / Should be working. -uint32_t IRsend::encodeSAMSUNG(uint8_t customer, uint8_t command) { - customer = reverseBits(customer, sizeof(customer) * 8); - command = reverseBits(command, sizeof(command) * 8); - return ((command ^ 0xFF) | (command << 8) | (customer << 16) | - (customer << 24)); -} -#endif - -#if DECODE_SAMSUNG -// Decode the supplied Samsung message. -// Samsung messages whilst 32 bits in size, only contain 16 bits of distinct -// data. e.g. In transmition order: -// customer_byte + customer_byte(same) + address_byte + invert(address_byte) -// -// Args: -// results: Ptr to the data to decode and where to store the decode result. -// nbits: Nr. of bits to expect in the data portion. Typically kSamsungBits. -// strict: Flag to indicate if we strictly adhere to the specification. -// Returns: -// boolean: True if it can decode it, false if it can't. -// -// Status: STABLE -// -// Note: -// LG 32bit protocol appears near identical to the Samsung protocol. -// They differ on their compliance criteria and how they repeat. -// Ref: -// http://elektrolab.wz.cz/katalog/samsung_protocol.pdf -bool IRrecv::decodeSAMSUNG(decode_results *results, uint16_t nbits, - bool strict) { - if (results->rawlen < 2 * nbits + kHeader + kFooter - 1) - return false; // Can't possibly be a valid Samsung message. - if (strict && nbits != kSamsungBits) - return false; // We expect Samsung to be 32 bits of message. - - uint64_t data = 0; - uint16_t offset = kStartOffset; - - // Header - if (!matchMark(results->rawbuf[offset], kSamsungHdrMark)) return false; - // Calculate how long the common tick time is based on the header mark. - uint32_t m_tick = results->rawbuf[offset++] * kRawTick / kSamsungHdrMarkTicks; - if (!matchSpace(results->rawbuf[offset], kSamsungHdrSpace)) return false; - // Calculate how long the common tick time is based on the header space. - uint32_t s_tick = - results->rawbuf[offset++] * kRawTick / kSamsungHdrSpaceTicks; - // Data - match_result_t data_result = - matchData(&(results->rawbuf[offset]), nbits, - kSamsungBitMarkTicks * m_tick, kSamsungOneSpaceTicks * s_tick, - kSamsungBitMarkTicks * m_tick, kSamsungZeroSpaceTicks * s_tick); - if (data_result.success == false) return false; - data = data_result.data; - offset += data_result.used; - // Footer - if (!matchMark(results->rawbuf[offset++], kSamsungBitMarkTicks * m_tick)) - return false; - if (offset < results->rawlen && - !matchAtLeast(results->rawbuf[offset], kSamsungMinGapTicks * s_tick)) - return false; - - // Compliance - - // According to the spec, the customer (address) code is the first 8 - // transmitted bits. It's then repeated. Check for that. - uint8_t address = data >> 24; - if (strict && address != ((data >> 16) & 0xFF)) return false; - // Spec says the command code is the 3rd block of transmitted 8-bits, - // followed by the inverted command code. - uint8_t command = (data & 0xFF00) >> 8; - if (strict && command != ((data & 0xFF) ^ 0xFF)) return false; - - // Success - results->bits = nbits; - results->value = data; - results->decode_type = SAMSUNG; - // command & address need to be reversed as they are transmitted LSB first, - results->command = reverseBits(command, sizeof(command) * 8); - results->address = reverseBits(address, sizeof(address) * 8); - return true; -} -#endif - -#if SEND_SAMSUNG_AC -// Send a Samsung A/C message. -// -// Args: -// data: An array of bytes containing the IR command. -// nbytes: Nr. of bytes of data in the array. (>=kSamsungAcStateLength) -// repeat: Nr. of times the message is to be repeated. (Default = 0). -// -// Status: ALPHA / Untested. -// -// Ref: -// https://github.com/markszabo/IRremoteESP8266/issues/505 -void IRsend::sendSamsungAC(uint8_t data[], uint16_t nbytes, uint16_t repeat) { - if (nbytes < kSamsungAcStateLength && nbytes % kSamsungACSectionLength) - return; // Not an appropriate number of bytes to send a proper message. - - enableIROut(38); - for (uint16_t r = 0; r <= repeat; r++) { - // Header - mark(kSamsungAcHdrMark); - space(kSamsungAcHdrSpace); - // Send in 7 byte sections. - for (uint16_t offset = 0; offset < nbytes; - offset += kSamsungACSectionLength) { - sendGeneric(kSamsungAcSectionMark, kSamsungAcSectionSpace, - kSamsungAcBitMark, kSamsungAcOneSpace, kSamsungAcBitMark, - kSamsungAcZeroSpace, kSamsungAcBitMark, kSamsungAcSectionGap, - data + offset, kSamsungACSectionLength, // 7 bytes == 56 bits - 38000, false, 0, 50); // Send in LSBF order - } - // Complete made up guess at inter-message gap. - space(100000 - kSamsungAcSectionGap); - } -} -#endif // SEND_SAMSUNG_AC - -IRSamsungAc::IRSamsungAc(uint16_t pin) : _irsend(pin) { stateReset(); } - -void IRSamsungAc::stateReset() { - for (uint8_t i = 0; i < kSamsungAcExtendedStateLength; i++) - remote_state[i] = 0x0; - remote_state[0] = 0x02; - remote_state[1] = 0x92; - remote_state[2] = 0x0F; - remote_state[6] = 0xF0; - remote_state[7] = 0x01; - remote_state[8] = 0x02; - remote_state[9] = 0xAE; - remote_state[10] = 0x71; - remote_state[12] = 0x15; - remote_state[13] = 0xF0; -} - -void IRSamsungAc::begin() { _irsend.begin(); } - -uint8_t IRSamsungAc::calcChecksum(const uint8_t state[], - const uint16_t length) { - uint8_t sum = 0; - uint8_t currentbyte; - // Safety check so we don't go outside the array. - if (length <= 5) return 255; - // Shamelessly inspired by: - // https://github.com/adafruit/Raw-IR-decoder-for-Arduino/pull/3/files - // Count most of the '1' bits after the checksum location. - for (uint8_t i = length - 5; i < length - 1; i++) { - currentbyte = state[i]; - if (i == length - 5) currentbyte = state[length - 5] & 0b11111110; - for (; currentbyte; currentbyte >>= 1) - if (currentbyte & 1) sum++; - } - return (28 - sum) & 0xF; -} - -bool IRSamsungAc::validChecksum(const uint8_t state[], const uint16_t length) { - if (length <= 5) return true; // No checksum to compare with. Assume okay. - return (state[length - 6] >> 4) == calcChecksum(state, length); -} - -// Update the checksum for the internal state. -void IRSamsungAc::checksum(uint16_t length) { - if (length < 9) return; - remote_state[length - 6] &= 0x0F; - remote_state[length - 6] |= (calcChecksum(remote_state, length) << 4); -} - -#if SEND_SAMSUNG_AC -void IRSamsungAc::send(const uint16_t repeat, const bool calcchecksum) { - if (calcchecksum) checksum(); - _irsend.sendSamsungAC(remote_state, kSamsungAcStateLength, repeat); -} -#endif // SEND_SAMSUNG_AC - -#if SEND_SAMSUNG_AC -void IRSamsungAc::sendExtended(const uint16_t repeat, const bool calcchecksum) { - if (calcchecksum) checksum(); - uint8_t extended_state[kSamsungAcExtendedStateLength] = { - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xD2, 0x0F, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; - // Copy/convert the internal state to an extended state. - for (uint16_t i = 0; i < kSamsungACSectionLength; i++) - extended_state[i] = remote_state[i]; - for (uint16_t i = kSamsungACSectionLength; i < kSamsungAcStateLength; i++) - extended_state[i + kSamsungACSectionLength] = remote_state[i]; - // Send it. - _irsend.sendSamsungAC(extended_state, kSamsungAcExtendedStateLength, repeat); -} -#endif // SEND_SAMSUNG_AC - -uint8_t *IRSamsungAc::getRaw() { - checksum(); - return remote_state; -} - -void IRSamsungAc::setRaw(const uint8_t new_code[], const uint16_t length) { - for (uint8_t i = 0; i < length && i < kSamsungAcExtendedStateLength; i++) { - remote_state[i] = new_code[i]; - } - // Shrink the extended state into a normal state. - if (length > kSamsungAcStateLength) { - for (uint8_t i = kSamsungAcStateLength; i < length; i++) - remote_state[i - kSamsungACSectionLength] = remote_state[i]; - } -} - -void IRSamsungAc::on() { - remote_state[1] &= ~kSamsungAcPowerMask1; - remote_state[6] |= kSamsungAcPowerMask2; -} - -void IRSamsungAc::off() { - remote_state[1] |= kSamsungAcPowerMask1; - remote_state[6] &= ~kSamsungAcPowerMask2; -} - -void IRSamsungAc::setPower(const bool state) { - if (state) - on(); - else - off(); -} - -bool IRSamsungAc::getPower() { - return ((remote_state[6] & kSamsungAcPowerMask2) != 0) && - ((remote_state[1] & kSamsungAcPowerMask1) == 0); -} - -// Set the temp. in deg C -void IRSamsungAc::setTemp(const uint8_t temp) { - uint8_t newtemp = std::max(kSamsungAcMinTemp, temp); - newtemp = std::min(kSamsungAcMaxTemp, newtemp); - remote_state[11] = (remote_state[11] & ~kSamsungAcTempMask) | - ((newtemp - kSamsungAcMinTemp) << 4); -} - -// Return the set temp. in deg C -uint8_t IRSamsungAc::getTemp() { - return ((remote_state[11] & kSamsungAcTempMask) >> 4) + kSamsungAcMinTemp; -} - -void IRSamsungAc::setMode(const uint8_t mode) { - // If we get an unexpected mode, default to AUTO. - uint8_t newmode = mode; - if (newmode > kSamsungAcHeat) newmode = kSamsungAcAuto; - remote_state[12] = (remote_state[12] & ~kSamsungAcModeMask) | (newmode << 4); - - // Auto mode has a special fan setting valid only in auto mode. - if (newmode == kSamsungAcAuto) { - setFan(kSamsungAcFanAuto2); - } else { - if (getFan() == kSamsungAcFanAuto2) // Non-Auto can't have this fan setting - setFan(kSamsungAcFanAuto); // Default to something safe. - } -} - -uint8_t IRSamsungAc::getMode() { - return (remote_state[12] & kSamsungAcModeMask) >> 4; -} - -void IRSamsungAc::setFan(const uint8_t speed) { - switch (speed) { - case kSamsungAcFanAuto: - case kSamsungAcFanLow: - case kSamsungAcFanMed: - case kSamsungAcFanHigh: - case kSamsungAcFanTurbo: - if (getMode() == kSamsungAcAuto) return; // Not valid in Auto mode. - break; - case kSamsungAcFanAuto2: // Special fan setting for when in Auto mode. - if (getMode() != kSamsungAcAuto) return; - break; - default: - return; - } - remote_state[12] = (remote_state[12] & ~kSamsungAcFanMask) | (speed << 1); -} - -uint8_t IRSamsungAc::getFan() { - return ((remote_state[12] & kSamsungAcFanMask) >> 1); -} - -bool IRSamsungAc::getSwing() { - // TODO(Hollako): Explain why sometimes the LSB of remote_state[9] is a 1. - // e.g. 0xAE or 0XAF for swing move. - return ((remote_state[9] & kSamsungAcSwingMask) >> 4) == kSamsungAcSwingMove; -} - -void IRSamsungAc::setSwing(const bool state) { - // TODO(Hollako): Explain why sometimes the LSB of remote_state[9] is a 1. - // e.g. 0xAE or 0XAF for swing move. - remote_state[9] &= ~kSamsungAcSwingMask; // Clear the previous swing state. - if (state) - remote_state[9] |= (kSamsungAcSwingMove << 4); - else - remote_state[9] |= (kSamsungAcSwingStop << 4); -} - -bool IRSamsungAc::getBeep() { return remote_state[13] & kSamsungAcBeepMask; } - -void IRSamsungAc::setBeep(const bool state) { - if (state) - remote_state[13] |= kSamsungAcBeepMask; - else - remote_state[13] &= ~kSamsungAcBeepMask; -} - -bool IRSamsungAc::getClean() { - return (remote_state[10] & kSamsungAcCleanMask10) && - (remote_state[11] & kSamsungAcCleanMask11); -} - -void IRSamsungAc::setClean(const bool state) { - if (state) { - remote_state[10] |= kSamsungAcCleanMask10; - remote_state[11] |= kSamsungAcCleanMask11; - } else { - remote_state[10] &= ~kSamsungAcCleanMask10; - remote_state[11] &= ~kSamsungAcCleanMask11; - } -} - -// Very unsure this is correct. -bool IRSamsungAc::getQuiet() { - return remote_state[11] & kSamsungAcQuietMask11; -} - -// Very unsure this is correct. -void IRSamsungAc::setQuiet(const bool state) { - if (state) { - remote_state[11] |= kSamsungAcQuietMask11; - setFan(kSamsungAcFanAuto); // Quiet mode seems to set fan speed to auto. - } else { - remote_state[11] &= ~kSamsungAcQuietMask11; - } -} - -// Convert the internal state into a human readable string. -#ifdef ARDUINO -String IRSamsungAc::toString() { - String result = ""; -#else -std::string IRSamsungAc::toString() { - std::string result = ""; -#endif // ARDUINO - result += "Power: "; - if (getPower()) - result += "On"; - else - result += "Off"; - result += ", Mode: " + uint64ToString(getMode()); - switch (getMode()) { - case kSamsungAcAuto: - result += " (AUTO)"; - break; - case kSamsungAcCool: - result += " (COOL)"; - break; - case kSamsungAcHeat: - result += " (HEAT)"; - break; - case kSamsungAcDry: - result += " (DRY)"; - break; - case kSamsungAcFan: - result += " (FAN)"; - break; - default: - result += " (UNKNOWN)"; - } - result += ", Temp: " + uint64ToString(getTemp()) + "C"; - result += ", Fan: " + uint64ToString(getFan()); - switch (getFan()) { - case kSamsungAcFanAuto: - case kSamsungAcFanAuto2: - result += " (AUTO)"; - break; - case kSamsungAcFanLow: - result += " (LOW)"; - break; - case kSamsungAcFanMed: - result += " (MED)"; - break; - case kSamsungAcFanHigh: - result += " (HIGH)"; - break; - case kSamsungAcFanTurbo: - result += " (TURBO)"; - break; - default: - result += " (UNKNOWN)"; - break; - } - result += ", Swing: "; - if (getSwing()) - result += "On"; - else - result += "Off"; - result += ", Beep: "; - if (getBeep()) - result += "On"; - else - result += "Off"; - result += ", Clean: "; - if (getBeep()) - result += "On"; - else - result += "Off"; - result += ", Quiet: "; - if (getQuiet()) - result += "On"; - else - result += "Off"; - return result; -} - -#if DECODE_SAMSUNG_AC -// Decode the supplied Samsung A/C message. -// -// Args: -// results: Ptr to the data to decode and where to store the decode result. -// nbits: The number of data bits to expect. Typically kSamsungAcBits -// strict: Flag indicating if we should perform strict matching. -// Returns: -// boolean: True if it can decode it, false if it can't. -// -// Status: BETA / Appears to mostly work. -// -// Ref: -// https://github.com/markszabo/IRremoteESP8266/issues/505 -bool IRrecv::decodeSamsungAC(decode_results *results, uint16_t nbits, - bool strict) { - if (results->rawlen < 2 * nbits + kHeader * 3 + kFooter * 2 - 1) - return false; // Can't possibly be a valid Samsung A/C message. - if (nbits != kSamsungAcBits && nbits != kSamsungAcExtendedBits) return false; - - uint16_t offset = kStartOffset; - uint16_t dataBitsSoFar = 0; - match_result_t data_result; - - // Message Header - if (!matchMark(results->rawbuf[offset++], kSamsungAcBitMark)) return false; - if (!matchSpace(results->rawbuf[offset++], kSamsungAcHdrSpace)) return false; - // Section(s) - for (uint16_t pos = kSamsungACSectionLength, i = 0; pos <= nbits / 8; - pos += kSamsungACSectionLength) { - uint64_t sectiondata = 0; - // Section Header - if (!matchMark(results->rawbuf[offset++], kSamsungAcSectionMark)) - return false; - if (!matchSpace(results->rawbuf[offset++], kSamsungAcSectionSpace)) - return false; - // Section Data - // Keep reading bytes until we either run out of section or state to fill. - for (; offset <= results->rawlen - 16 && i < pos; - i++, dataBitsSoFar += 8, offset += data_result.used) { - data_result = matchData(&(results->rawbuf[offset]), 8, kSamsungAcBitMark, - kSamsungAcOneSpace, kSamsungAcBitMark, - kSamsungAcZeroSpace, kTolerance, 0, false); - if (data_result.success == false) { - DPRINT("DEBUG: offset = "); - DPRINTLN(offset + data_result.used); - return false; // Fail - } - results->state[i] = data_result.data; - sectiondata = (sectiondata << 8) + data_result.data; - } - DPRINTLN("DEBUG: sectiondata = 0x" + uint64ToString(sectiondata, 16)); - // Section Footer - if (!matchMark(results->rawbuf[offset++], kSamsungAcBitMark)) return false; - if (pos < nbits / 8) { // Inter-section gap. - if (!matchSpace(results->rawbuf[offset++], kSamsungAcSectionGap)) - return false; - } else { // Last section / End of message gap. - if (offset <= results->rawlen && - !matchAtLeast(results->rawbuf[offset++], kSamsungAcSectionGap)) - return false; - } - } - // Compliance - // Re-check we got the correct size/length due to the way we read the data. - if (dataBitsSoFar != nbits) return false; - // Is the signature correct? - DPRINTLN("DEBUG: Checking signature."); - if (results->state[0] != 0x02 || results->state[2] != 0x0F) return false; - if (results->state[1] != 0x92 && results->state[1] != 0xB2) return false; - if (strict) { - // Is the checksum valid? - if (!IRSamsungAc::validChecksum(results->state, nbits / 8)) { - DPRINTLN("DEBUG: Checksum failed!"); - return false; - } - } - // Success - results->decode_type = SAMSUNG_AC; - results->bits = dataBitsSoFar; - // No need to record the state as we stored it as we decoded it. - // As we use result->state, we don't record value, address, or command as it - // is a union data type. - return true; -} -#endif // DECODE_SAMSUNG_AC diff --git a/lib/IRremoteESP8266_ID1089/src/ir_Samsung.h b/lib/IRremoteESP8266_ID1089/src/ir_Samsung.h deleted file mode 100644 index e8bcc4c..0000000 --- a/lib/IRremoteESP8266_ID1089/src/ir_Samsung.h +++ /dev/null @@ -1,109 +0,0 @@ -// Samsung A/C -// -// Copyright 2018 David Conran - -#ifndef IR_SAMSUNG_H_ -#define IR_SAMSUNG_H_ - -#define __STDC_LIMIT_MACROS -#include -#ifndef UNIT_TEST -#include -#else -#include -#endif -#include "IRremoteESP8266.h" -#include "IRsend.h" - -// 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 - -// Ref: -// https://github.com/markszabo/IRremoteESP8266/issues/505 - -// Constants -const uint8_t kSamsungAcAuto = 0; -const uint8_t kSamsungAcCool = 1; -const uint8_t kSamsungAcDry = 2; -const uint8_t kSamsungAcFan = 3; -const uint8_t kSamsungAcHeat = 4; -const uint8_t kSamsungAcModeMask = 0x70; -const uint8_t kSamsungAcFanAuto = 0; -const uint8_t kSamsungAcFanLow = 2; -const uint8_t kSamsungAcFanMed = 4; -const uint8_t kSamsungAcFanHigh = 5; -const uint8_t kSamsungAcFanAuto2 = 6; -const uint8_t kSamsungAcFanTurbo = 7; -const uint8_t kSamsungAcMinTemp = 16; // 16C -const uint8_t kSamsungAcMaxTemp = 30; // 30C -const uint8_t kSamsungAcAutoTemp = 25; // 25C -const uint8_t kSamsungAcTempMask = 0xF0; -const uint8_t kSamsungAcPowerMask1 = 0x20; -const uint8_t kSamsungAcPowerMask2 = 0x30; -const uint8_t kSamsungAcFanMask = 0x0E; -const uint8_t kSamsungAcSwingMask = 0x70; -const uint8_t kSamsungAcSwingMove = 0b010; -const uint8_t kSamsungAcSwingStop = 0b111; -const uint8_t kSamsungAcBeepMask = 0x02; -const uint8_t kSamsungAcCleanMask10 = 0x80; -const uint8_t kSamsungAcCleanMask11 = 0x02; -const uint8_t kSamsungAcQuietMask11 = 0x01; - -const uint16_t kSamsungACSectionLength = 7; -const uint64_t kSamsungAcPowerSection = 0x1D20F00000000; - -// Classes -class IRSamsungAc { - public: - explicit IRSamsungAc(uint16_t pin); - - void stateReset(); -#if SEND_SAMSUNG_AC - void send(const uint16_t repeat = kSamsungAcDefaultRepeat, - const bool calcchecksum = true); - void sendExtended(const uint16_t repeat = kSamsungAcDefaultRepeat, - const bool calcchecksum = true); -#endif // SEND_SAMSUNG_AC - void begin(); - void on(); - void off(); - void setPower(const bool state); - bool getPower(); - void setTemp(const uint8_t temp); - uint8_t getTemp(); - void setFan(const uint8_t speed); - uint8_t getFan(); - void setMode(const uint8_t mode); - uint8_t getMode(); - void setSwing(const bool state); - bool getSwing(); - void setBeep(const bool state); - bool getBeep(); - void setClean(const bool state); - bool getClean(); - void setQuiet(const bool state); - bool getQuiet(); - uint8_t* getRaw(); - void setRaw(const uint8_t new_code[], - const uint16_t length = kSamsungAcStateLength); - static bool validChecksum(const uint8_t state[], - const uint16_t length = kSamsungAcStateLength); - static uint8_t calcChecksum(const uint8_t state[], - const uint16_t length = kSamsungAcStateLength); -#ifdef ARDUINO - String toString(); -#else - std::string toString(); -#endif - - private: - // The state of the IR remote in IR code form. - uint8_t remote_state[kSamsungAcExtendedStateLength]; - void checksum(const uint16_t length = kSamsungAcStateLength); - IRsend _irsend; -}; - -#endif // IR_SAMSUNG_H_ diff --git a/lib/IRremoteESP8266_ID1089/src/ir_Sanyo.cpp b/lib/IRremoteESP8266_ID1089/src/ir_Sanyo.cpp deleted file mode 100644 index b2b4d78..0000000 --- a/lib/IRremoteESP8266_ID1089/src/ir_Sanyo.cpp +++ /dev/null @@ -1,241 +0,0 @@ -// Copyright 2009 Ken Shirriff -// Copyright 2016 marcosamarinho -// Copyright 2017 David Conran - -#include -#include "IRrecv.h" -#include "IRsend.h" - -// SSSS AAA N N Y Y OOO -// S A A NN N Y Y O O -// SSS AAAAA N N N Y O O -// S A A N NN Y O O -// SSSS A A N N Y OOO - -// Sanyo SA 8650B originally added from: -// https://github.com/shirriff/Arduino-IRremote/ -// Sanyo LC7461 support originally by marcosamarinho - -// Constants -// Sanyo SA 8650B -// Ref: -// https://github.com/z3t0/Arduino-IRremote/blob/master/ir_Sanyo.cpp - -const uint16_t kSanyoSa8650bHdrMark = 3500; // seen range 3500 -const uint16_t kSanyoSa8650bHdrSpace = 950; // seen 950 -const uint16_t kSanyoSa8650bOneMark = 2400; // seen 2400 -const uint16_t kSanyoSa8650bZeroMark = 700; // seen 700 -// usually see 713 - not using ticks as get number wrapround -const uint16_t kSanyoSa8650bDoubleSpaceUsecs = 800; -const uint16_t kSanyoSa8650bRptLength = 45000; -// Sanyo LC7461 -// Ref: -// https://github.com/marcosamarinho/IRremoteESP8266/blob/master/ir_Sanyo.cpp -// http://slydiman.narod.ru/scr/kb/sanyo.htm -// http://pdf.datasheetcatalog.com/datasheet/sanyo/LC7461.pdf - -const uint16_t kSanyoLc7461AddressMask = (1 << kSanyoLC7461AddressBits) - 1; -const uint16_t kSanyoLc7461CommandMask = (1 << kSanyoLC7461CommandBits) - 1; -const uint16_t kSanyoLc7461HdrMark = 9000; -const uint16_t kSanyoLc7461HdrSpace = 4500; -const uint16_t kSanyoLc7461BitMark = 560; // 1T -const uint16_t kSanyoLc7461OneSpace = 1690; // 3T -const uint16_t kSanyoLc7461ZeroSpace = 560; // 1T -const uint32_t kSanyoLc7461MinCommandLength = 108000; - -const uint16_t kSanyoLc7461MinGap = - kSanyoLc7461MinCommandLength - - (kSanyoLc7461HdrMark + kSanyoLc7461HdrSpace + - kSanyoLC7461Bits * (kSanyoLc7461BitMark + - (kSanyoLc7461OneSpace + kSanyoLc7461ZeroSpace) / 2) + - kSanyoLc7461BitMark); - -#if SEND_SANYO -// Construct a Sanyo LC7461 message. -// -// Args: -// address: The 13 bit value of the address(Custom) portion of the protocol. -// command: The 8 bit value of the command(Key) portion of the protocol. -// Returns: -// An uint64_t with the encoded raw 42 bit Sanyo LC7461 data value. -// -// Notes: -// This protocol uses the NEC protocol timings. However, data is -// formatted as : address(13 bits), !address, command(8 bits), !command. -// According with LIRC, this protocol is used on Sanyo, Aiwa and Chinon -uint64_t IRsend::encodeSanyoLC7461(uint16_t address, uint8_t command) { - // Mask our input values to ensure the correct bit sizes. - address &= kSanyoLc7461AddressMask; - command &= kSanyoLc7461CommandMask; - - uint64_t data = address; - address ^= kSanyoLc7461AddressMask; // Invert the 13 LSBs. - // Append the now inverted address. - data = (data << kSanyoLC7461AddressBits) | address; - // Append the command. - data = (data << kSanyoLC7461CommandBits) | command; - command ^= kSanyoLc7461CommandMask; // Invert the command. - // Append the now inverted command. - data = (data << kSanyoLC7461CommandBits) | command; - - return data; -} - -// Send a Sanyo LC7461 message. -// -// Args: -// data: The contents of the command you want to send. -// nbits: The bit size of the command being sent. -// repeat: The number of times you want the command to be repeated. -// -// Status: BETA / Probably works. -// -// Notes: -// Based on @marcosamarinho's work. -// This protocol uses the NEC protocol timings. However, data is -// formatted as : address(13 bits), !address, command (8 bits), !command. -// According with LIRC, this protocol is used on Sanyo, Aiwa and Chinon -// Information for this protocol is available at the Sanyo LC7461 datasheet. -// Repeats are performed similar to the NEC method of sending a special -// repeat message, rather than duplicating the entire message. -// Ref: -// https://github.com/marcosamarinho/IRremoteESP8266/blob/master/ir_Sanyo.cpp -// http://pdf.datasheetcatalog.com/datasheet/sanyo/LC7461.pdf -void IRsend::sendSanyoLC7461(uint64_t data, uint16_t nbits, uint16_t repeat) { - // This protocol appears to be another 42-bit variant of the NEC protcol. - sendNEC(data, nbits, repeat); -} -#endif // SEND_SANYO - -#if DECODE_SANYO -// Decode the supplied SANYO LC7461 message. -// -// Args: -// results: Ptr to the data to decode and where to store the decode result. -// nbits: Nr. of data bits to expect. -// strict: Flag indicating if we should perform strict matching. -// Returns: -// boolean: True if it can decode it, false if it can't. -// -// Status: BETA / Probably works. -// -// Notes: -// Based on @marcosamarinho's work. -// This protocol uses the NEC protocol. However, data is -// formatted as : address(13 bits), !address, command (8 bits), !command. -// According with LIRC, this protocol is used on Sanyo, Aiwa and Chinon -// Information for this protocol is available at the Sanyo LC7461 datasheet. -// Ref: -// http://slydiman.narod.ru/scr/kb/sanyo.htm -// https://github.com/marcosamarinho/IRremoteESP8266/blob/master/ir_Sanyo.cpp -// http://pdf.datasheetcatalog.com/datasheet/sanyo/LC7461.pdf -bool IRrecv::decodeSanyoLC7461(decode_results *results, uint16_t nbits, - bool strict) { - if (strict && nbits != kSanyoLC7461Bits) - return false; // Not strictly in spec. - // This protocol is basically a 42-bit variant of the NEC protocol. - if (!decodeNEC(results, nbits, false)) - return false; // Didn't match a NEC format (without strict) - - // Bits 30 to 42+. - uint16_t address = - results->value >> (kSanyoLC7461Bits - kSanyoLC7461AddressBits); - // Bits 9 to 16. - uint8_t command = - (results->value >> kSanyoLC7461CommandBits) & kSanyoLc7461CommandMask; - // Compliance - if (strict) { - if (results->bits != nbits) return false; - // Bits 17 to 29. - uint16_t inverted_address = - (results->value >> (kSanyoLC7461CommandBits * 2)) & - kSanyoLc7461AddressMask; - // Bits 1-8. - uint8_t inverted_command = results->value & kSanyoLc7461CommandMask; - if ((address ^ kSanyoLc7461AddressMask) != inverted_address) - return false; // Address integrity check failed. - if ((command ^ kSanyoLc7461CommandMask) != inverted_command) - return false; // Command integrity check failed. - } - - // Success - results->decode_type = SANYO_LC7461; - results->address = address; - results->command = command; - return true; -} - -/* NOTE: Disabled due to poor quality. -// Decode the supplied Sanyo SA 8650B message. -// -// Args: -// results: Ptr to the data to decode and where to store the decode result. -// nbits: Nr. of data bits to expect. -// strict: Flag indicating if we should perform strict matching. -// Returns: -// boolean: True if it can decode it, false if it can't. -// -// Status: Depricated. -// -// NOTE: This decoder looks like rubbish. Only keeping it for compatibility -// with the Arduino IRremote library. Seriously, don't trust it. -// If someone has a device that this is supposed to be for, please log an -// Issue on github with a rawData dump please. We should probably remove -// it. We think this is a Sanyo decoder - serial = SA 8650B -// Ref: -// https://github.com/z3t0/Arduino-IRremote/blob/master/ir_Sanyo.cpp -bool IRrecv::decodeSanyo(decode_results *results, uint16_t nbits, bool strict) { - if (results->rawlen < 2 * nbits + kHeader - 1) - return false; // Shorter than shortest possible. - if (strict && nbits != kSanyoSA8650BBits) - return false; // Doesn't match the spec. - - uint16_t offset = 0; - - // TODO(crankyoldgit): This repeat code looks like garbage, it should never - // match or if it does, it won't be reliable. We should probably just - // remove it. - if (results->rawbuf[offset++] < kSanyoSa8650bDoubleSpaceUsecs) { - results->bits = 0; - results->value = kRepeat; - results->decode_type = SANYO; - results->address = 0; - results->command = 0; - results->repeat = true; - return true; - } - - // Header - if (!matchMark(results->rawbuf[offset++], kSanyoSa8650bHdrMark)) - return false; - // NOTE: These next two lines look very wrong. Treat as suspect. - if (!matchMark(results->rawbuf[offset++], kSanyoSa8650bHdrMark)) - return false; - // Data - uint64_t data = 0; - while (offset + 1 < results->rawlen) { - if (!matchSpace(results->rawbuf[offset], kSanyoSa8650bHdrSpace)) - break; - offset++; - if (matchMark(results->rawbuf[offset], kSanyoSa8650bOneMark)) - data = (data << 1) | 1; // 1 - else if (matchMark(results->rawbuf[offset], kSanyoSa8650bZeroMark)) - data <<= 1; // 0 - else - return false; - offset++; - } - - if (strict && kSanyoSA8650BBits > (offset - 1U) / 2U) - return false; - - // Success - results->bits = (offset - 1) / 2; - results->decode_type = SANYO; - results->value = data; - results->address = 0; - results->command = 0; - return true; -} -*/ -#endif // DECODE_SANYO diff --git a/lib/IRremoteESP8266_ID1089/src/ir_Sharp.cpp b/lib/IRremoteESP8266_ID1089/src/ir_Sharp.cpp deleted file mode 100644 index ae1b59c..0000000 --- a/lib/IRremoteESP8266_ID1089/src/ir_Sharp.cpp +++ /dev/null @@ -1,267 +0,0 @@ -// Copyright 2009 Ken Shirriff -// Copyright 2017 David Conran - -#include -#include "IRrecv.h" -#include "IRsend.h" -#include "IRutils.h" - -// 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 - -// Equipment it seems compatible with: -// * Sharp LC-52D62U -// * -// - -// Constants -// period time = 1/38000Hz = 26.316 microseconds. -// Ref: -// GlobalCache's IR Control Tower data. -// http://www.sbprojects.com/knowledge/ir/sharp.php -const uint16_t kSharpTick = 26; -const uint16_t kSharpBitMarkTicks = 10; -const uint16_t kSharpBitMark = kSharpBitMarkTicks * kSharpTick; -const uint16_t kSharpOneSpaceTicks = 70; -const uint16_t kSharpOneSpace = kSharpOneSpaceTicks * kSharpTick; -const uint16_t kSharpZeroSpaceTicks = 30; -const uint16_t kSharpZeroSpace = kSharpZeroSpaceTicks * kSharpTick; -const uint16_t kSharpGapTicks = 1677; -const uint16_t kSharpGap = kSharpGapTicks * kSharpTick; -// Address(5) + Command(8) + Expansion(1) + Check(1) -const uint64_t kSharpToggleMask = - ((uint64_t)1 << (kSharpBits - kSharpAddressBits)) - 1; -const uint64_t kSharpAddressMask = ((uint64_t)1 << kSharpAddressBits) - 1; -const uint64_t kSharpCommandMask = ((uint64_t)1 << kSharpCommandBits) - 1; - -#if (SEND_SHARP || SEND_DENON) -// Send a (raw) Sharp message -// -// Args: -// data: Contents of the message to be sent. -// nbits: Nr. of bits of data to be sent. Typically kSharpBits. -// repeat: Nr. of additional times the message is to be sent. -// -// Status: BETA / Previously working fine. -// -// Notes: -// This procedure handles the inversion of bits required per protocol. -// The protocol spec says to send the LSB first, but legacy code & usage -// has us sending the MSB first. Grrrr. Normal invocation of encodeSharp() -// handles this for you, assuming you are using the correct/standard values. -// e.g. sendSharpRaw(encodeSharp(address, command)); -// -// Ref: -// http://www.sbprojects.com/knowledge/ir/sharp.htm -// http://lirc.sourceforge.net/remotes/sharp/GA538WJSA -// http://www.mwftr.com/ucF08/LEC14%20PIC%20IR.pdf -// http://www.hifi-remote.com/johnsfine/DecodeIR.html#Sharp -void IRsend::sendSharpRaw(uint64_t data, uint16_t nbits, uint16_t repeat) { - for (uint16_t i = 0; i <= repeat; i++) { - // Protocol demands that the data be sent twice; once normally, - // then with all but the address bits inverted. - // Note: Previously this used to be performed 3 times (normal, inverted, - // normal), however all data points to that being incorrect. - for (uint8_t n = 0; n < 2; n++) { - sendGeneric(0, 0, // No Header - kSharpBitMark, kSharpOneSpace, kSharpBitMark, kSharpZeroSpace, - kSharpBitMark, kSharpGap, data, nbits, 38, true, - 0, // Repeats are handled already. - 33); - // Invert the data per protocol. This is always called twice, so it's - // retured to original upon exiting the inner loop. - data ^= kSharpToggleMask; - } - } -} - -// Encode a (raw) Sharp message from it's components. -// -// Args: -// address: The value of the address to be sent. -// command: The value of the address to be sent. (8 bits) -// expansion: The value of the expansion bit to use. (0 or 1, typically 1) -// check: The value of the check bit to use. (0 or 1, typically 0) -// MSBfirst: Flag indicating MSB first or LSB first order. (Default: false) -// Returns: -// An uint32_t containing the raw Sharp message for sendSharpRaw(). -// -// Status: BETA / Should work okay. -// -// Notes: -// Assumes the standard Sharp bit sizes. -// Historically sendSharp() sends address & command in -// MSB first order. This is actually incorrect. It should be sent in LSB -// order. The behaviour of sendSharp() hasn't been changed to maintain -// backward compatibility. -// -// Ref: -// http://www.sbprojects.com/knowledge/ir/sharp.htm -// http://lirc.sourceforge.net/remotes/sharp/GA538WJSA -// http://www.mwftr.com/ucF08/LEC14%20PIC%20IR.pdf -uint32_t IRsend::encodeSharp(uint16_t address, uint16_t command, - uint16_t expansion, uint16_t check, - bool MSBfirst) { - // Mask any unexpected bits. - address &= ((1 << kSharpAddressBits) - 1); - command &= ((1 << kSharpCommandBits) - 1); - expansion &= 1; - check &= 1; - - if (!MSBfirst) { // Correct bit order if needed. - address = reverseBits(address, kSharpAddressBits); - command = reverseBits(command, kSharpCommandBits); - } - // Concatinate all the bits. - return (address << (kSharpCommandBits + 2)) | (command << 2) | - (expansion << 1) | check; -} - -// Send a Sharp message -// -// Args: -// address: Address value to be sent. -// command: Command value to be sent. -// nbits: Nr. of bits of data to be sent. Typically kSharpBits. -// repeat: Nr. of additional times the message is to be sent. -// -// Status: DEPRICATED / Previously working fine. -// -// Notes: -// This procedure has a non-standard invocation style compared to similar -// sendProtocol() routines. This is due to legacy, compatibility, & historic -// reasons. Normally the calling syntax version is like sendSharpRaw(). -// This procedure transmits the address & command in MSB first order, which is -// incorrect. This behaviour is left as-is to maintain backward -// compatibility with legacy code. -// In short, you should use sendSharpRaw(), encodeSharp(), and the correct -// values of address & command instead of using this, & the wrong values. -// -// Ref: -// http://www.sbprojects.com/knowledge/ir/sharp.htm -// http://lirc.sourceforge.net/remotes/sharp/GA538WJSA -// http://www.mwftr.com/ucF08/LEC14%20PIC%20IR.pdf -void IRsend::sendSharp(uint16_t address, uint16_t command, uint16_t nbits, - uint16_t repeat) { - sendSharpRaw(encodeSharp(address, command, 1, 0, true), nbits, repeat); -} -#endif // (SEND_SHARP || SEND_DENON) - -#if (DECODE_SHARP || DECODE_DENON) -// Decode the supplied Sharp message. -// -// Args: -// results: Ptr to the data to decode and where to store the decode result. -// nbits: Nr. of data bits to expect. Typically kSharpBits. -// strict: Flag indicating if we should perform strict matching. -// expansion: Should we expect the expansion bit to be set. Default is true. -// Returns: -// boolean: True if it can decode it, false if it can't. -// -// Status: STABLE / Working fine. -// -// Note: -// This procedure returns a value suitable for use in sendSharpRaw(). -// TODO(crankyoldgit): Need to ensure capture of the inverted message as it can -// be missed due to the interrupt timeout used to detect an end of message. -// Several compliance checks are disabled until that is resolved. -// Ref: -// http://www.sbprojects.com/knowledge/ir/sharp.php -// http://www.mwftr.com/ucF08/LEC14%20PIC%20IR.pdf -// http://www.hifi-remote.com/johnsfine/DecodeIR.html#Sharp -bool IRrecv::decodeSharp(decode_results *results, uint16_t nbits, bool strict, - bool expansion) { - if (results->rawlen < 2 * nbits + kFooter - 1) - return false; // Not enough entries to be a Sharp message. - // Compliance - if (strict) { - if (nbits != kSharpBits) return false; // Request is out of spec. - // DISABLED - See TODO -#ifdef UNIT_TEST - // An in spec message has the data sent normally, then inverted. So we - // expect twice as many entries than to just get the results. - if (results->rawlen < 2 * (2 * nbits + kFooter)) return false; -#endif - } - - uint64_t data = 0; - uint16_t offset = kStartOffset; - - // No header - // But try to auto-calibrate off the initial mark signal. - if (!matchMark(results->rawbuf[offset], kSharpBitMark, 35)) return false; - // Calculate how long the common tick time is based on the header mark. - uint32_t tick = results->rawbuf[offset] * kRawTick / kSharpBitMarkTicks; - // Data - for (uint16_t i = 0; i < nbits; i++, offset++) { - // Use a higher tolerance value for kSharpBitMark as it is quite small. - if (!matchMark(results->rawbuf[offset++], kSharpBitMarkTicks * tick, 35)) - return false; - if (matchSpace(results->rawbuf[offset], kSharpOneSpaceTicks * tick)) - data = (data << 1) | 1; // 1 - else if (matchSpace(results->rawbuf[offset], kSharpZeroSpaceTicks * tick)) - data <<= 1; // 0 - else - return false; - } - - // Footer - if (!match(results->rawbuf[offset++], kSharpBitMarkTicks * tick)) - return false; - if (offset < results->rawlen && - !matchAtLeast(results->rawbuf[offset], kSharpGapTicks * tick)) - return false; - - // Compliance - if (strict) { - // Check the state of the expansion bit is what we expect. - if ((data & 0b10) >> 1 != expansion) return false; - // The check bit should be cleared in a normal message. - if (data & 0b1) return false; - // DISABLED - See TODO -#ifdef UNIT_TEST - // Grab the second copy of the data (i.e. inverted) - // Header - // i.e. The inter-data/command repeat gap. - if (!matchSpace(results->rawbuf[offset++], kSharpGapTicks * tick)) - return false; - - // Data - uint64_t second_data = 0; - for (uint16_t i = 0; i < nbits; i++, offset++) { - // Use a higher tolerance value for kSharpBitMark as it is quite small. - if (!matchMark(results->rawbuf[offset++], kSharpBitMarkTicks * tick, 35)) - return false; - if (matchSpace(results->rawbuf[offset], kSharpOneSpaceTicks * tick)) - second_data = (second_data << 1) | 1; // 1 - else if (matchSpace(results->rawbuf[offset], kSharpZeroSpaceTicks * tick)) - second_data <<= 1; // 0 - else - return false; - } - // Footer - if (!match(results->rawbuf[offset++], kSharpBitMarkTicks * tick)) - return false; - if (offset < results->rawlen && - !matchAtLeast(results->rawbuf[offset], kSharpGapTicks * tick)) - return false; - - // Check that second_data has been inverted correctly. - if (data != (second_data ^ kSharpToggleMask)) return false; -#endif // UNIT_TEST - } - - // Success - results->decode_type = SHARP; - results->bits = nbits; - results->value = data; - // Address & command are actually transmitted in LSB first order. - results->address = reverseBits(data, nbits) & kSharpAddressMask; - results->command = - reverseBits((data >> 2) & kSharpCommandMask, kSharpCommandBits); - return true; -} -#endif // (DECODE_SHARP || DECODE_DENON) diff --git a/lib/IRremoteESP8266_ID1089/src/ir_Sherwood.cpp b/lib/IRremoteESP8266_ID1089/src/ir_Sherwood.cpp deleted file mode 100644 index 8af7dfb..0000000 --- a/lib/IRremoteESP8266_ID1089/src/ir_Sherwood.cpp +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright 2017 David Conran - -#include -#include "IRsend.h" - -// SSSSS HH HH EEEEEEE RRRRRR WW WW OOOOO OOOOO DDDDD -// SS HH HH EE RR RR WW WW OO OO OO OO DD DD -// SSSSS HHHHHHH EEEEE RRRRRR WW W WW OO OO OO OO DD DD -// SS HH HH EE RR RR WW WWW WW OO OO OO OO DD DD -// SSSSS HH HH EEEEEEE RR RR WW WW OOOO0 OOOO0 DDDDDD - -#if SEND_SHERWOOD -// Send an IR command to a Sherwood device. -// -// Args: -// data: The contents of the command you want to send. -// nbits: The bit size of the command being sent. (kSherwoodBits) -// repeat: The nr. of times you want the command to be repeated. (Default: 1) -// -// Status: STABLE / Known working. -// -// Note: -// Sherwood remote codes appear to be NEC codes with a manditory repeat code. -// i.e. repeat should be >= kSherwoodMinRepeat (1). -void IRsend::sendSherwood(uint64_t data, uint16_t nbits, uint16_t repeat) { - sendNEC(data, nbits, std::max((uint16_t)kSherwoodMinRepeat, repeat)); -} -#endif diff --git a/lib/IRremoteESP8266_ID1089/src/ir_Sony.cpp b/lib/IRremoteESP8266_ID1089/src/ir_Sony.cpp deleted file mode 100644 index efa6e6a..0000000 --- a/lib/IRremoteESP8266_ID1089/src/ir_Sony.cpp +++ /dev/null @@ -1,181 +0,0 @@ -// Copyright 2009 Ken Shirriff -// Copyright 2016 marcosamarinho -// Copyright 2017 David Conran - -#include -#include "IRrecv.h" -#include "IRsend.h" -#include "IRutils.h" - -// 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 - -// Sony originally added from https://github.com/shirriff/Arduino-IRremote/ -// Updates from marcosamarinho - -// Constants -// Ref: -// http://www.sbprojects.com/knowledge/ir/sirc.php -const uint16_t kSonyTick = 200; -const uint16_t kSonyHdrMarkTicks = 12; -const uint16_t kSonyHdrMark = kSonyHdrMarkTicks * kSonyTick; -const uint16_t kSonySpaceTicks = 3; -const uint16_t kSonySpace = kSonySpaceTicks * kSonyTick; -const uint16_t kSonyOneMarkTicks = 6; -const uint16_t kSonyOneMark = kSonyOneMarkTicks * kSonyTick; -const uint16_t kSonyZeroMarkTicks = 3; -const uint16_t kSonyZeroMark = kSonyZeroMarkTicks * kSonyTick; -const uint16_t kSonyRptLengthTicks = 225; -const uint16_t kSonyRptLength = kSonyRptLengthTicks * kSonyTick; -const uint16_t kSonyMinGapTicks = 50; -const uint16_t kSonyMinGap = kSonyMinGapTicks * kSonyTick; - -#if SEND_SONY -// Send a Sony/SIRC(Serial Infra-Red Control) message. -// -// Args: -// data: message to be sent. -// nbits: Nr. of bits of the message to be sent. -// repeat: Nr. of additional times the message is to be sent. (Default: 2) -// -// Status: STABLE / Known working. -// -// Notes: -// sendSony() should typically be called with repeat=2 as Sony devices -// expect the message to be sent at least 3 times. -// -// Ref: -// http://www.sbprojects.com/knowledge/ir/sirc.php -void IRsend::sendSony(uint64_t data, uint16_t nbits, uint16_t repeat) { - sendGeneric(kSonyHdrMark, kSonySpace, kSonyOneMark, kSonySpace, kSonyZeroMark, - kSonySpace, - 0, // No Footer mark. - kSonyMinGap, kSonyRptLength, data, nbits, 40, true, repeat, 33); -} - -// Convert Sony/SIRC command, address, & extended bits into sendSony format. -// Args: -// nbits: Sony protocol bit size. -// command: Sony command bits. -// address: Sony address bits. -// extended: Sony extended bits. -// Returns: -// A sendSony compatible data message. -// -// Status: BETA / Should be working. -uint32_t IRsend::encodeSony(uint16_t nbits, uint16_t command, uint16_t address, - uint16_t extended) { - uint32_t result = 0; - switch (nbits) { - case 12: // 5 address bits. - result = address & 0x1F; - break; - case 15: // 8 address bits. - result = address & 0xFF; - break; - case 20: // 5 address bits, 8 extended bits. - result = address & 0x1F; - result |= (extended & 0xFF) << 5; - break; - default: - return 0; // This is not an expected Sony bit size/protocol. - } - result = (result << 7) | (command & 0x7F); // All sizes have 7 command bits. - return reverseBits(result, nbits); // sendSony uses reverse ordered bits. -} -#endif - -#if DECODE_SONY -// Decode the supplied Sony/SIRC message. -// -// Args: -// results: Ptr to the data to decode and where to store the decode result. -// nbits: The number of data bits to expect. -// strict: Flag indicating if we should perform strict matching. -// Returns: -// boolean: True if it can decode it, false if it can't. -// -// Status: BETA / Should be working. strict mode is ALPHA / Untested. -// -// Notes: -// SONY protocol, SIRC (Serial Infra-Red Control) can be 12,15,20 bits long. -// Ref: -// http://www.sbprojects.com/knowledge/ir/sirc.php -bool IRrecv::decodeSony(decode_results *results, uint16_t nbits, bool strict) { - if (results->rawlen < 2 * nbits + kHeader - 1) - return false; // Message is smaller than we expected. - - // Compliance - if (strict) { - switch (nbits) { // Check we've been called with a correct bit size. - case 12: - case 15: - case 20: - break; - default: - return false; // The request doesn't strictly match the protocol defn. - } - } - - uint64_t data = 0; - uint16_t offset = kStartOffset; - uint16_t actualBits; - uint32_t timeSoFar = 0; // Time in uSecs of the message length. - - // Header - timeSoFar += results->rawbuf[offset] * kRawTick; - if (!matchMark(results->rawbuf[offset], kSonyHdrMark)) return false; - // Calculate how long the common tick time is based on the header mark. - uint32_t tick = results->rawbuf[offset++] * kRawTick / kSonyHdrMarkTicks; - - // Data - for (actualBits = 0; offset < results->rawlen - 1; actualBits++, offset++) { - // The gap after a Sony packet for a repeat should be kSonyMinGap or - // (kSonyRptLength - timeSoFar) according to the spec. - if (matchSpace(results->rawbuf[offset], kSonyMinGapTicks * tick) || - matchAtLeast(results->rawbuf[offset], kSonyRptLength - timeSoFar)) - break; // Found a repeat space. - timeSoFar += results->rawbuf[offset] * kRawTick; - if (!matchSpace(results->rawbuf[offset++], kSonySpaceTicks * tick)) - return false; - timeSoFar += results->rawbuf[offset] * kRawTick; - if (matchMark(results->rawbuf[offset], kSonyOneMarkTicks * tick)) - data = (data << 1) | 1; - else if (matchMark(results->rawbuf[offset], kSonyZeroMarkTicks * tick)) - data <<= 1; - else - return false; - } - // No Footer for Sony. - - // Compliance - if (strict && actualBits != nbits) - return false; // We got the wrong number of bits. - - // Success - results->bits = actualBits; - results->value = data; - results->decode_type = SONY; - // Message comes in LSB first. Convert ot MSB first. - data = reverseBits(data, actualBits); - // Decode the address & command from raw decode value. - switch (actualBits) { - case 12: // 7 command bits, 5 address bits. - case 15: // 7 command bits, 8 address bits. - results->command = data & 0x7F; // Bits 0-6 - results->address = data >> 7; // Bits 7-14 - break; - case 20: // 7 command bits, 5 address bits, 8 extended (command) bits. - results->command = (data & 0x7F) + ((data >> 12) << 7); // Bits 0-6,12-19 - results->address = (data >> 7) & 0x1F; // Bits 7-11 - break; - default: // Shouldn't happen, but just in case. - results->address = 0; - results->command = 0; - } - return true; -} -#endif diff --git a/lib/IRremoteESP8266_ID1089/src/ir_Toshiba.cpp b/lib/IRremoteESP8266_ID1089/src/ir_Toshiba.cpp deleted file mode 100644 index 467745e..0000000 --- a/lib/IRremoteESP8266_ID1089/src/ir_Toshiba.cpp +++ /dev/null @@ -1,340 +0,0 @@ -// Copyright 2017 David Conran - -#include "ir_Toshiba.h" -#include -#ifndef ARDUINO -#include -#endif -#include "IRrecv.h" -#include "IRsend.h" -#include "IRutils.h" - -// TTTTTTT OOOOO SSSSS HH HH IIIII BBBBB AAA -// TTT OO OO SS HH HH III BB B AAAAA -// TTT OO OO SSSSS HHHHHHH III BBBBBB AA AA -// TTT OO OO SS HH HH III BB BB AAAAAAA -// TTT OOOO0 SSSSS HH HH IIIII BBBBBB AA AA - -// Toshiba A/C support added by David Conran -// -// Equipment it seems compatible with: -// * Toshiba RAS-B13N3KV2 / Akita EVO II -// * Toshiba RAS-B13N3KVP-E, RAS 18SKP-ES -// * Toshiba WH-TA04NE, WC-L03SE -// * - -// Constants - -// Toshiba A/C -// Ref: -// https://github.com/r45635/HVAC-IR-Control/blob/master/HVAC_ESP8266/HVAC_ESP8266T.ino#L77 -const uint16_t kToshibaAcHdrMark = 4400; -const uint16_t kToshibaAcHdrSpace = 4300; -const uint16_t kToshibaAcBitMark = 543; -const uint16_t kToshibaAcOneSpace = 1623; -const uint16_t kToshibaAcZeroSpace = 472; -const uint16_t kToshibaAcMinGap = 7048; - -#if SEND_TOSHIBA_AC -// Send a Toshiba A/C message. -// -// Args: -// data: An array of bytes containing the IR command. -// nbytes: Nr. of bytes of data in the array. (>=kToshibaACStateLength) -// repeat: Nr. of times the message is to be repeated. -// (Default = kToshibaACMinRepeat). -// -// Status: StABLE / Working. -// -void IRsend::sendToshibaAC(unsigned char data[], uint16_t nbytes, - uint16_t repeat) { - if (nbytes < kToshibaACStateLength) - return; // Not enough bytes to send a proper message. - sendGeneric(kToshibaAcHdrMark, kToshibaAcHdrSpace, kToshibaAcBitMark, - kToshibaAcOneSpace, kToshibaAcBitMark, kToshibaAcZeroSpace, - kToshibaAcBitMark, kToshibaAcMinGap, data, nbytes, 38, true, - repeat, 50); -} -#endif // SEND_TOSHIBA_AC - -// Code to emulate Toshiba A/C IR remote control unit. -// Inspired and derived from the work done at: -// https://github.com/r45635/HVAC-IR-Control -// -// Status: STABLE / Working. -// -// Initialise the object. -IRToshibaAC::IRToshibaAC(uint16_t pin) : _irsend(pin) { stateReset(); } - -// Reset the state of the remote to a known good state/sequence. -void IRToshibaAC::stateReset() { - // The state of the IR remote in IR code form. - // Known good state obtained from: - // https://github.com/r45635/HVAC-IR-Control/blob/master/HVAC_ESP8266/HVAC_ESP8266T.ino#L103 - // Note: Can't use the following because it requires -std=c++11 - // uint8_t remote_state[kToshibaACStateLength] = { - // 0xF2, 0x0D, 0x03, 0xFC, 0x01, 0x00, 0x00, 0x00, 0x00 }; - remote_state[0] = 0xF2; - remote_state[1] = 0x0D; - remote_state[2] = 0x03; - remote_state[3] = 0xFC; - remote_state[4] = 0x01; - for (uint8_t i = 5; i < kToshibaACStateLength; i++) remote_state[i] = 0; - mode_state = remote_state[6] & 0b00000011; - checksum(); // Calculate the checksum -} - -// Configure the pin for output. -void IRToshibaAC::begin() { _irsend.begin(); } - -#if SEND_TOSHIBA_AC -// Send the current desired state to the IR LED. -void IRToshibaAC::send(const uint16_t repeat) { - checksum(); // Ensure correct checksum before sending. - _irsend.sendToshibaAC(remote_state, kToshibaACStateLength, repeat); -} -#endif // SEND_TOSHIBA_AC - -// Return a pointer to the internal state date of the remote. -uint8_t* IRToshibaAC::getRaw() { - checksum(); - return remote_state; -} - -// Override the internal state with the new state. -void IRToshibaAC::setRaw(uint8_t newState[]) { - for (uint8_t i = 0; i < kToshibaACStateLength; i++) { - remote_state[i] = newState[i]; - } - mode_state = getMode(true); -} - -// Calculate the checksum for a given array. -// Args: -// state: The array to calculate the checksum over. -// length: The size of the array. -// Returns: -// The 8 bit checksum value. -uint8_t IRToshibaAC::calcChecksum(const uint8_t state[], - const uint16_t length) { - uint8_t checksum = 0; - // Only calculate it for valid lengths. - if (length > 1) { - // Checksum is simple XOR of all bytes except the last one. - for (uint8_t i = 0; i < length - 1; i++) checksum ^= state[i]; - } - return checksum; -} - -// Verify the checksum is valid for a given state. -// Args: -// state: The array to verify the checksum of. -// length: The size of the state. -// Returns: -// A boolean. -bool IRToshibaAC::validChecksum(const uint8_t state[], const uint16_t length) { - return (length > 1 && state[length - 1] == calcChecksum(state, length)); -} - -// Calculate & set the checksum for the current internal state of the remote. -void IRToshibaAC::checksum(const uint16_t length) { - // Stored the checksum value in the last byte. - if (length > 1) remote_state[length - 1] = calcChecksum(remote_state, length); -} - -// Set the requested power state of the A/C to off. -void IRToshibaAC::on() { - // state = ON; - remote_state[6] &= ~kToshibaAcPower; - setMode(mode_state); -} - -// Set the requested power state of the A/C to off. -void IRToshibaAC::off() { - // state = OFF; - remote_state[6] |= (kToshibaAcPower | 0b00000011); -} - -// Set the requested power state of the A/C. -void IRToshibaAC::setPower(bool state) { - if (state) - on(); - else - off(); -} - -// Return the requested power state of the A/C. -bool IRToshibaAC::getPower() { - return ((remote_state[6] & kToshibaAcPower) == 0); -} - -// Set the temp. in deg C -void IRToshibaAC::setTemp(uint8_t temp) { - temp = std::max((uint8_t)kToshibaAcMinTemp, temp); - temp = std::min((uint8_t)kToshibaAcMaxTemp, temp); - remote_state[5] = (temp - kToshibaAcMinTemp) << 4; -} - -// Return the set temp. in deg C -uint8_t IRToshibaAC::getTemp() { - return ((remote_state[5] >> 4) + kToshibaAcMinTemp); -} - -// Set the speed of the fan, 0-5. -// 0 is auto, 1-5 is the speed, 5 is Max. -void IRToshibaAC::setFan(uint8_t fan) { - // Bounds check - if (fan > kToshibaAcFanMax) - fan = kToshibaAcFanMax; // Set the fan to maximum if out of range. - if (fan > kToshibaAcFanAuto) fan++; - remote_state[6] &= 0b00011111; // Clear the previous fan state - remote_state[6] |= (fan << 5); -} - -// Return the requested state of the unit's fan. -uint8_t IRToshibaAC::getFan() { - uint8_t fan = remote_state[6] >> 5; - if (fan == kToshibaAcFanAuto) return kToshibaAcFanAuto; - return --fan; -} - -// Get the requested climate operation mode of the a/c unit. -// Args: -// useRaw: Indicate to get the mode from the state array. (Default: false) -// Returns: -// A uint8_t containing the A/C mode. -uint8_t IRToshibaAC::getMode(bool useRaw) { - if (useRaw) - return (remote_state[6] & 0b00000011); - else - return mode_state; -} - -// Set the requested climate operation mode of the a/c unit. -void IRToshibaAC::setMode(uint8_t mode) { - // If we get an unexpected mode, default to AUTO. - switch (mode) { - case kToshibaAcAuto: - break; - case kToshibaAcCool: - break; - case kToshibaAcDry: - break; - case kToshibaAcHeat: - break; - default: - mode = kToshibaAcAuto; - } - mode_state = mode; - // Only adjust the remote_state if we have power set to on. - if (getPower()) { - remote_state[6] &= 0b11111100; // Clear the previous mode. - remote_state[6] |= mode_state; - } -} - -// Convert the internal state into a human readable string. -#ifdef ARDUINO -String IRToshibaAC::toString() { - String result = ""; -#else -std::string IRToshibaAC::toString() { - std::string result = ""; -#endif // ARDUINO - result += "Power: "; - if (getPower()) - result += "On"; - else - result += "Off"; - result += ", Mode: " + uint64ToString(getMode()); - switch (getMode()) { - case kToshibaAcAuto: - result += " (AUTO)"; - break; - case kToshibaAcCool: - result += " (COOL)"; - break; - case kToshibaAcHeat: - result += " (HEAT)"; - break; - case kToshibaAcDry: - result += " (DRY)"; - break; - default: - result += " (UNKNOWN)"; - } - result += ", Temp: " + uint64ToString(getTemp()) + "C"; - result += ", Fan: " + uint64ToString(getFan()); - switch (getFan()) { - case kToshibaAcFanAuto: - result += " (AUTO)"; - break; - case kToshibaAcFanMax: - result += " (MAX)"; - break; - } - return result; -} - -#if DECODE_TOSHIBA_AC -// Decode a Toshiba AC IR message if possible. -// Places successful decode information in the results pointer. -// Args: -// results: Ptr to the data to decode and where to store the decode result. -// nbits: The number of data bits to expect. Typically kToshibaACBits. -// strict: Flag to indicate if we strictly adhere to the specification. -// Returns: -// boolean: True if it can decode it, false if it can't. -// -// Status: STABLE / Working. -// -// Ref: -// -bool IRrecv::decodeToshibaAC(decode_results* results, uint16_t nbits, - bool strict) { - uint16_t offset = kStartOffset; - uint16_t dataBitsSoFar = 0; - - // Have we got enough data to successfully decode? - if (results->rawlen < kToshibaACBits + kHeader + kFooter - 1) - return false; // Can't possibly be a valid message. - - // Compliance - if (strict && nbits != kToshibaACBits) - return false; // Must be called with the correct nr. of bytes. - - // Header - if (!matchMark(results->rawbuf[offset++], kToshibaAcHdrMark)) return false; - if (!matchSpace(results->rawbuf[offset++], kToshibaAcHdrSpace)) return false; - - // Data - for (uint8_t i = 0; i < kToshibaACStateLength; i++) { - // Read a byte's worth of data. - match_result_t data_result = - matchData(&(results->rawbuf[offset]), 8, kToshibaAcBitMark, - kToshibaAcOneSpace, kToshibaAcBitMark, kToshibaAcZeroSpace); - if (data_result.success == false) return false; // Fail - dataBitsSoFar += 8; - results->state[i] = (uint8_t)data_result.data; - offset += data_result.used; - } - - // Footer - if (!matchMark(results->rawbuf[offset++], kToshibaAcBitMark)) return false; - if (!matchSpace(results->rawbuf[offset++], kToshibaAcMinGap)) return false; - - // Compliance - if (strict) { - // Check that the checksum of the message is correct. - if (!IRToshibaAC::validChecksum(results->state)) return false; - } - - // Success - results->decode_type = TOSHIBA_AC; - results->bits = dataBitsSoFar; - // No need to record the state as we stored it as we decoded it. - // As we use result->state, we don't record value, address, or command as it - // is a union data type. - return true; -} -#endif // DECODE_TOSHIBA_AC diff --git a/lib/IRremoteESP8266_ID1089/src/ir_Toshiba.h b/lib/IRremoteESP8266_ID1089/src/ir_Toshiba.h deleted file mode 100644 index a6ea6c6..0000000 --- a/lib/IRremoteESP8266_ID1089/src/ir_Toshiba.h +++ /dev/null @@ -1,85 +0,0 @@ -// Copyright 2017 David Conran -#ifndef IR_TOSHIBA_H_ -#define IR_TOSHIBA_H_ - -#define __STDC_LIMIT_MACROS -#include -#ifdef ARDUINO -#include -#else -#include -#endif -#include "IRremoteESP8266.h" -#include "IRsend.h" - -// TTTTTTT OOOOO SSSSS HH HH IIIII BBBBB AAA -// TTT OO OO SS HH HH III BB B AAAAA -// TTT OO OO SSSSS HHHHHHH III BBBBBB AA AA -// TTT OO OO SS HH HH III BB BB AAAAAAA -// TTT OOOO0 SSSSS HH HH IIIII BBBBBB AA AA - -// Toshiba A/C support added by David Conran - -// Constants -const uint8_t kToshibaAcAuto = 0; -const uint8_t kToshibaAcCool = 1; -const uint8_t kToshibaAcDry = 2; -const uint8_t kToshibaAcHeat = 3; -const uint8_t kToshibaAcPower = 4; -const uint8_t kToshibaAcFanAuto = 0; -const uint8_t kToshibaAcFanMax = 5; -const uint8_t kToshibaAcMinTemp = 17; // 17C -const uint8_t kToshibaAcMaxTemp = 30; // 30C - -// Legacy defines. (Deperecated) -#define TOSHIBA_AC_AUTO kToshibaAcAuto -#define TOSHIBA_AC_COOL kToshibaAcCool -#define TOSHIBA_AC_DRY kToshibaAcDry -#define TOSHIBA_AC_HEAT kToshibaAcHeat -#define TOSHIBA_AC_POWER kToshibaAcPower -#define TOSHIBA_AC_FAN_AUTO kToshibaAcFanAuto -#define TOSHIBA_AC_FAN_MAX kToshibaAcFanMax -#define TOSHIBA_AC_MIN_TEMP kToshibaAcMinTemp -#define TOSHIBA_AC_MAX_TEMP kToshibaAcMaxTemp - -class IRToshibaAC { - public: - explicit IRToshibaAC(uint16_t pin); - - void stateReset(); -#if SEND_TOSHIBA_AC - void send(const uint16_t repeat = kToshibaACMinRepeat); -#endif // SEND_TOSHIBA_AC - void begin(); - void on(); - void off(); - void setPower(bool state); - bool getPower(); - void setTemp(uint8_t temp); - uint8_t getTemp(); - void setFan(uint8_t fan); - uint8_t getFan(); - void setMode(uint8_t mode); - uint8_t getMode(bool useRaw = false); - void setRaw(uint8_t newState[]); - uint8_t* getRaw(); - static bool validChecksum(const uint8_t state[], - const uint16_t length = kToshibaACStateLength); -#ifdef ARDUINO - String toString(); -#else - std::string toString(); -#endif -#ifndef UNIT_TEST - - private: -#endif - uint8_t remote_state[kToshibaACStateLength]; - void checksum(const uint16_t length = kToshibaACStateLength); - static uint8_t calcChecksum(const uint8_t state[], - const uint16_t length = kToshibaACStateLength); - uint8_t mode_state; - IRsend _irsend; -}; - -#endif // IR_TOSHIBA_H_ diff --git a/lib/IRremoteESP8266_ID1089/src/ir_Trotec.cpp b/lib/IRremoteESP8266_ID1089/src/ir_Trotec.cpp deleted file mode 100644 index 77d84ee..0000000 --- a/lib/IRremoteESP8266_ID1089/src/ir_Trotec.cpp +++ /dev/null @@ -1,127 +0,0 @@ -// Copyright 2017 stufisher - -#include "ir_Trotec.h" -#include "IRremoteESP8266.h" -#include "IRutils.h" - -// Constants -const uint16_t kTrotecHdrMark = 5952; -const uint16_t kTrotecHdrSpace = 7364; -const uint16_t kTrotecOneMark = 592; -const uint16_t kTrotecOneSpace = 1560; -const uint16_t kTrotecZeroMark = 592; -const uint16_t kTrotecZeroSpace = 592; -const uint16_t kTrotecGap = 6184; -const uint16_t kTrotecGapEnd = 1500; // made up value - -#if SEND_TROTEC - -void IRsend::sendTrotec(unsigned char data[], uint16_t nbytes, - uint16_t repeat) { - if (nbytes < kTrotecStateLength) return; - - for (uint16_t r = 0; r <= repeat; r++) { - sendGeneric(kTrotecHdrMark, kTrotecHdrSpace, kTrotecOneMark, - kTrotecOneSpace, kTrotecZeroMark, kTrotecZeroSpace, - kTrotecOneMark, kTrotecGap, data, nbytes, 36, false, - 0, // Repeats handled elsewhere - 50); - // More footer - enableIROut(36); - mark(kTrotecOneMark); - space(kTrotecGapEnd); - } -} -#endif // SEND_TROTEC - -IRTrotecESP::IRTrotecESP(uint16_t pin) : _irsend(pin) { stateReset(); } - -void IRTrotecESP::begin() { _irsend.begin(); } - -#if SEND_TROTEC -void IRTrotecESP::send(const uint16_t repeat) { - checksum(); - _irsend.sendTrotec(trotec, kTrotecStateLength, repeat); -} -#endif // SEND_TROTEC - -void IRTrotecESP::checksum() { - uint8_t sum = 0; - uint8_t i; - - for (i = 2; i < 8; i++) sum += trotec[i]; - - trotec[8] = sum & 0xFF; -} - -void IRTrotecESP::stateReset() { - for (uint8_t i = 2; i < kTrotecStateLength; i++) trotec[i] = 0x0; - - trotec[0] = kTrotecIntro1; - trotec[1] = kTrotecIntro2; - - setPower(false); - setTemp(kTrotecDefTemp); - setSpeed(kTrotecFanMed); - setMode(kTrotecAuto); -} - -uint8_t* IRTrotecESP::getRaw() { - checksum(); - return trotec; -} - -void IRTrotecESP::setPower(bool state) { - if (state) - trotec[2] |= (kTrotecOn << 3); - else - trotec[2] &= ~(kTrotecOn << 3); -} - -uint8_t IRTrotecESP::getPower() { return trotec[2] & (kTrotecOn << 3); } - -void IRTrotecESP::setSpeed(uint8_t speed) { - trotec[2] = (trotec[2] & 0xcf) | (speed << 4); -} - -uint8_t IRTrotecESP::getSpeed() { return trotec[2] & 0x30; } - -void IRTrotecESP::setMode(uint8_t mode) { - trotec[2] = (trotec[2] & 0xfc) | mode; -} - -uint8_t IRTrotecESP::getMode() { return trotec[2] & 0x03; } - -void IRTrotecESP::setTemp(uint8_t temp) { - if (temp < kTrotecMinTemp) - temp = kTrotecMinTemp; - else if (temp > kTrotecMaxTemp) - temp = kTrotecMaxTemp; - - trotec[3] = (trotec[3] & 0x80) | (temp - kTrotecMinTemp); -} - -uint8_t IRTrotecESP::getTemp() { return trotec[3] & 0x7f; } - -void IRTrotecESP::setSleep(bool sleep) { - if (sleep) - trotec[3] |= (kTrotecSleepOn << 7); - else - trotec[3] &= ~(kTrotecSleepOn << 7); -} - -bool IRTrotecESP::getSleep(void) { return trotec[3] & (kTrotecSleepOn << 7); } - -void IRTrotecESP::setTimer(uint8_t timer) { - if (timer > kTrotecMaxTimer) timer = kTrotecMaxTimer; - - if (timer) { - trotec[5] |= (kTrotecTimerOn << 6); - trotec[6] = timer; - } else { - trotec[5] &= ~(kTrotecTimerOn << 6); - trotec[6] = 0; - } -} - -uint8_t IRTrotecESP::getTimer() { return trotec[6]; } diff --git a/lib/IRremoteESP8266_ID1089/src/ir_Trotec.h b/lib/IRremoteESP8266_ID1089/src/ir_Trotec.h deleted file mode 100644 index 846381b..0000000 --- a/lib/IRremoteESP8266_ID1089/src/ir_Trotec.h +++ /dev/null @@ -1,92 +0,0 @@ -// Copyright 2017 stufisher - -#ifndef IR_TROTEC_H_ -#define IR_TROTEC_H_ - -#include "IRremoteESP8266.h" -#include "IRsend.h" - -// Constants -// Byte 0 -const uint8_t kTrotecIntro1 = 0x12; - -// Byte 1 -const uint8_t kTrotecIntro2 = 0x34; - -// Byte 2 -const uint8_t kTrotecAuto = 0; -const uint8_t kTrotecCool = 1; -const uint8_t kTrotecDry = 2; -const uint8_t kTrotecFan = 3; - -const uint8_t kTrotecOn = 1; -const uint8_t kTrotecOff = 0; - -const uint8_t kTrotecFanLow = 1; -const uint8_t kTrotecFanMed = 2; -const uint8_t kTrotecFanHigh = 3; - -// Byte 3 -const uint8_t kTrotecMinTemp = 18; -const uint8_t kTrotecDefTemp = 25; -const uint8_t kTrotecMaxTemp = 32; - -const uint8_t kTrotecSleepOn = 1; - -// Byte 5 -const uint8_t kTrotecTimerOn = 1; - -// Byte 6 -const uint8_t kTrotecMinTimer = 0; -const uint8_t kTrotecMaxTimer = 23; - -// Legacy defines. (Deperecated) -#define TROTEC_AUTO kTrotecAuto -#define TROTEC_COOL kTrotecCool -#define TROTEC_DRY kTrotecDry -#define TROTEC_FAN kTrotecFan -#define TROTEC_FAN_LOW kTrotecFanLow -#define TROTEC_FAN_MED kTrotecFanMed -#define TROTEC_FAN_HIGH kTrotecFanHigh -#define TROTEC_MIN_TEMP kTrotecMinTemp -#define TROTEC_MAX_TEMP kTrotecMaxTemp -#define TROTEC_MIN_TIMER kTrotecMinTimer -#define TROTEC_MAX_TIMER kTrotecMaxTimer - -class IRTrotecESP { - public: - explicit IRTrotecESP(uint16_t pin); - -#if SEND_TROTEC - void send(const uint16_t repeat = kTrotecDefaultRepeat); -#endif // SEND_TROTEC - void begin(); - - void setPower(bool state); - uint8_t getPower(); - - void setTemp(uint8_t temp); - uint8_t getTemp(); - - void setSpeed(uint8_t fan); - uint8_t getSpeed(); - - uint8_t getMode(); - void setMode(uint8_t mode); - - bool getSleep(); - void setSleep(bool sleep); - - uint8_t getTimer(); - void setTimer(uint8_t timer); - - uint8_t* getRaw(); - - private: - uint8_t trotec[kTrotecStateLength]; - void stateReset(); - void checksum(); - IRsend _irsend; -}; - -#endif // IR_TROTEC_H_ diff --git a/lib/IRremoteESP8266_ID1089/src/ir_Whirlpool.cpp b/lib/IRremoteESP8266_ID1089/src/ir_Whirlpool.cpp deleted file mode 100644 index bc5c988..0000000 --- a/lib/IRremoteESP8266_ID1089/src/ir_Whirlpool.cpp +++ /dev/null @@ -1,634 +0,0 @@ -// Copyright 2018 David Conran -// -// Code to emulate Whirlpool protocol compatible devices. -// Should be compatible with: -// * SPIS409L, SPIS412L, SPIW409L, SPIW412L, SPIW418L -// Remotes: -// * DG11J1-3A / DG11J1-04 -// * DG11J1-91 -// -// Note: Smart, iFeel, AroundU, PowerSave, & Silent modes are unsupported. -// Advanced 6thSense, Dehumidify, & Sleep modes are not supported. -// FYI: -// Dim == !Light -// Jet == Super == Turbo -// - -#include "ir_Whirlpool.h" -#include -#ifndef ARDUINO -#include -#endif -#include "IRrecv.h" -#include "IRremoteESP8266.h" -#include "IRsend.h" -#include "IRutils.h" - -// WW WW HH HH IIIII RRRRRR LL PPPPPP OOOOO OOOOO LL -// WW WW HH HH III RR RR LL PP PP OO OO OO OO LL -// WW W WW HHHHHHH III RRRRRR LL PPPPPP OO OO OO OO LL -// WW WWW WW HH HH III RR RR LL PP OO OO OO OO LL -// WW WW HH HH IIIII RR RR LLLLLLL PP OOOO0 OOOO0 LLLLLLL - -// Constants -// Ref: https://github.com/markszabo/IRremoteESP8266/issues/509 -const uint16_t kWhirlpoolAcHdrMark = 8950; -const uint16_t kWhirlpoolAcHdrSpace = 4484; -const uint16_t kWhirlpoolAcBitMark = 597; -const uint16_t kWhirlpoolAcOneSpace = 1649; -const uint16_t kWhirlpoolAcZeroSpace = 533; -const uint16_t kWhirlpoolAcGap = 7920; -const uint32_t kWhirlpoolAcMinGap = 100000; // Completely made up value. -const uint8_t kWhirlpoolAcSections = 3; - -#if SEND_WHIRLPOOL_AC -// Send a Whirlpool A/C message. -// -// Args: -// data: An array of bytes containing the IR command. -// nbytes: Nr. of bytes of data in the array. (>=kWhirlpoolAcStateLength) -// repeat: Nr. of times the message is to be repeated. (Default = 0). -// -// Status: ALPHA / Untested. -// -// Ref: -// https://github.com/markszabo/IRremoteESP8266/issues/509 -void IRsend::sendWhirlpoolAC(unsigned char data[], uint16_t nbytes, - uint16_t repeat) { - if (nbytes < kWhirlpoolAcStateLength) - return; // Not enough bytes to send a proper message. - for (uint16_t r = 0; r <= repeat; r++) { - // Section 1 - sendGeneric(kWhirlpoolAcHdrMark, kWhirlpoolAcHdrSpace, kWhirlpoolAcBitMark, - kWhirlpoolAcOneSpace, kWhirlpoolAcBitMark, - kWhirlpoolAcZeroSpace, kWhirlpoolAcBitMark, kWhirlpoolAcGap, - data, 6, // 6 bytes == 48 bits - 38000, // Complete guess of the modulation frequency. - false, 0, 50); - // Section 2 - sendGeneric(0, 0, kWhirlpoolAcBitMark, kWhirlpoolAcOneSpace, - kWhirlpoolAcBitMark, kWhirlpoolAcZeroSpace, kWhirlpoolAcBitMark, - kWhirlpoolAcGap, data + 6, 8, // 8 bytes == 64 bits - 38000, // Complete guess of the modulation frequency. - false, 0, 50); - // Section 3 - sendGeneric(0, 0, kWhirlpoolAcBitMark, kWhirlpoolAcOneSpace, - kWhirlpoolAcBitMark, kWhirlpoolAcZeroSpace, kWhirlpoolAcBitMark, - kWhirlpoolAcMinGap, data + 14, 7, // 7 bytes == 56 bits - 38000, // Complete guess of the modulation frequency. - false, 0, 50); - } -} -#endif // SEND_WHIRLPOOL_AC - -// Class for emulating a Whirlpool A/C remote. -// Decoding help from: -// @redmusicxd, @josh929800, @raducostea - -IRWhirlpoolAc::IRWhirlpoolAc(uint16_t pin) : _irsend(pin) { stateReset(); } - -void IRWhirlpoolAc::stateReset() { - for (uint8_t i = 2; i < kWhirlpoolAcStateLength; i++) remote_state[i] = 0x0; - remote_state[0] = 0x83; - remote_state[1] = 0x06; - remote_state[6] = 0x80; - _setTemp(kWhirlpoolAcAutoTemp); // Default to a sane value. -} - -void IRWhirlpoolAc::begin() { _irsend.begin(); } - -bool IRWhirlpoolAc::validChecksum(uint8_t state[], const uint16_t length) { - if (length > kWhirlpoolAcChecksumByte1 && - state[kWhirlpoolAcChecksumByte1] != - xorBytes(state + 2, kWhirlpoolAcChecksumByte1 - 1 - 2)) { - DPRINTLN("DEBUG: First Whirlpool AC checksum failed."); - return false; - } - if (length > kWhirlpoolAcChecksumByte2 && - state[kWhirlpoolAcChecksumByte2] != - xorBytes(state + kWhirlpoolAcChecksumByte1 + 1, - kWhirlpoolAcChecksumByte2 - kWhirlpoolAcChecksumByte1 - 1)) { - DPRINTLN("DEBUG: Second Whirlpool AC checksum failed."); - return false; - } - // State is too short to have a checksum or everything checked out. - return true; -} - -// Update the checksum for the internal state. -void IRWhirlpoolAc::checksum(uint16_t length) { - if (length >= kWhirlpoolAcChecksumByte1) - remote_state[kWhirlpoolAcChecksumByte1] = - xorBytes(remote_state + 2, kWhirlpoolAcChecksumByte1 - 1 - 2); - if (length >= kWhirlpoolAcChecksumByte2) - remote_state[kWhirlpoolAcChecksumByte2] = - xorBytes(remote_state + kWhirlpoolAcChecksumByte1 + 1, - kWhirlpoolAcChecksumByte2 - kWhirlpoolAcChecksumByte1 - 1); -} - -#if SEND_WHIRLPOOL_AC -void IRWhirlpoolAc::send(const uint16_t repeat, const bool calcchecksum) { - if (calcchecksum) checksum(); - _irsend.sendWhirlpoolAC(remote_state, kWhirlpoolAcStateLength, repeat); -} -#endif // SEND_WHIRLPOOL_AC - -uint8_t *IRWhirlpoolAc::getRaw(const bool calcchecksum) { - if (calcchecksum) checksum(); - return remote_state; -} - -void IRWhirlpoolAc::setRaw(const uint8_t new_code[], const uint16_t length) { - for (uint8_t i = 0; i < length && i < kWhirlpoolAcStateLength; i++) - remote_state[i] = new_code[i]; -} - -whirlpool_ac_remote_model_t IRWhirlpoolAc::getModel() { - if (remote_state[kWhirlpoolAcAltTempPos] & kWhirlpoolAcAltTempMask) - return DG11J191; - else - return DG11J13A; -} - -void IRWhirlpoolAc::setModel(const whirlpool_ac_remote_model_t model) { - switch (model) { - case DG11J191: - remote_state[kWhirlpoolAcAltTempPos] |= kWhirlpoolAcAltTempMask; - break; - case DG11J13A: - // FALL THRU - default: - remote_state[kWhirlpoolAcAltTempPos] &= ~kWhirlpoolAcAltTempMask; - } - _setTemp(_desiredtemp); // Different models have different temp values. -} - -// Return the temp. offset in deg C for the current model. -int8_t IRWhirlpoolAc::getTempOffset() { - switch (getModel()) { - case DG11J191: - return -2; - break; - default: - return 0; - } -} - -// Set the temp. in deg C -void IRWhirlpoolAc::_setTemp(const uint8_t temp, const bool remember) { - if (remember) _desiredtemp = temp; - int8_t offset = getTempOffset(); // Cache the min temp for the model. - uint8_t newtemp = std::max((uint8_t)(kWhirlpoolAcMinTemp + offset), temp); - newtemp = std::min((uint8_t)(kWhirlpoolAcMaxTemp + offset), newtemp); - remote_state[kWhirlpoolAcTempPos] = - (remote_state[kWhirlpoolAcTempPos] & ~kWhirlpoolAcTempMask) | - ((newtemp - (kWhirlpoolAcMinTemp + offset)) << 4); -} - -// Set the temp. in deg C -void IRWhirlpoolAc::setTemp(const uint8_t temp) { - _setTemp(temp); - setSuper(false); // Changing temp cancels Super/Jet mode. - setCommand(kWhirlpoolAcCommandTemp); -} - -// Return the set temp. in deg C -uint8_t IRWhirlpoolAc::getTemp() { - return ((remote_state[kWhirlpoolAcTempPos] & kWhirlpoolAcTempMask) >> 4) + - + kWhirlpoolAcMinTemp + getTempOffset(); -} - -void IRWhirlpoolAc::_setMode(const uint8_t mode) { - switch (mode) { - case kWhirlpoolAcAuto: - setFan(kWhirlpoolAcFanAuto); - _setTemp(kWhirlpoolAcAutoTemp, false); - setSleep(false); // Cancel sleep mode when in auto/6thsense mode. - // FALL THRU - case kWhirlpoolAcHeat: - case kWhirlpoolAcCool: - case kWhirlpoolAcDry: - case kWhirlpoolAcFan: - remote_state[kWhirlpoolAcModePos] &= ~kWhirlpoolAcModeMask; - remote_state[kWhirlpoolAcModePos] |= mode; - setCommand(kWhirlpoolAcCommandMode); - break; - default: - return; - } - if (mode == kWhirlpoolAcAuto) setCommand(kWhirlpoolAcCommand6thSense); -} - -void IRWhirlpoolAc::setMode(const uint8_t mode) { - setSuper(false); // Changing mode cancels Super/Jet mode. - _setMode(mode); -} - -uint8_t IRWhirlpoolAc::getMode() { - return remote_state[kWhirlpoolAcModePos] & kWhirlpoolAcModeMask; -} - -void IRWhirlpoolAc::setFan(const uint8_t speed) { - switch (speed) { - case kWhirlpoolAcFanAuto: - case kWhirlpoolAcFanLow: - case kWhirlpoolAcFanMedium: - case kWhirlpoolAcFanHigh: - remote_state[kWhirlpoolAcFanPos] = - (remote_state[kWhirlpoolAcFanPos] & ~kWhirlpoolAcFanMask) | speed; - setSuper(false); // Changing fan speed cancels Super/Jet mode. - setCommand(kWhirlpoolAcCommandFanSpeed); - break; - } -} - -uint8_t IRWhirlpoolAc::getFan() { - return remote_state[kWhirlpoolAcFanPos] & kWhirlpoolAcFanMask; -} - -void IRWhirlpoolAc::setSwing(const bool on) { - if (on) { - remote_state[kWhirlpoolAcFanPos] |= kWhirlpoolAcSwing1Mask; - remote_state[kWhirlpoolAcOffTimerPos] |= kWhirlpoolAcSwing2Mask; - } else { - remote_state[kWhirlpoolAcFanPos] &= ~kWhirlpoolAcSwing1Mask; - remote_state[kWhirlpoolAcOffTimerPos] &= ~kWhirlpoolAcSwing2Mask; - } - setCommand(kWhirlpoolAcCommandSwing); -} - -bool IRWhirlpoolAc::getSwing() { - return (remote_state[kWhirlpoolAcFanPos] & kWhirlpoolAcSwing1Mask) && - (remote_state[kWhirlpoolAcOffTimerPos] & kWhirlpoolAcSwing2Mask); -} - -void IRWhirlpoolAc::setLight(const bool on) { - if (on) - remote_state[kWhirlpoolAcClockPos] &= ~kWhirlpoolAcLightMask; - else - remote_state[kWhirlpoolAcClockPos] |= kWhirlpoolAcLightMask; -} - -bool IRWhirlpoolAc::getLight() { - return !(remote_state[kWhirlpoolAcClockPos] & kWhirlpoolAcLightMask); -} - -void IRWhirlpoolAc::setTime(const uint16_t pos, - const uint16_t minspastmidnight) { - // Hours - remote_state[pos] &= ~kWhirlpoolAcHourMask; - remote_state[pos] |= (minspastmidnight / 60) % 24; - // Minutes - remote_state[pos + 1] &= ~kWhirlpoolAcMinuteMask; - remote_state[pos + 1] |= minspastmidnight % 60; -} - -uint16_t IRWhirlpoolAc::getTime(const uint16_t pos) { - return (remote_state[pos] & kWhirlpoolAcHourMask) * 60 + - (remote_state[pos + 1] & kWhirlpoolAcMinuteMask); -} - -bool IRWhirlpoolAc::isTimerEnabled(const uint16_t pos) { - return remote_state[pos - 1] & kWhirlpoolAcTimerEnableMask; -} - -void IRWhirlpoolAc::enableTimer(const uint16_t pos, const bool state) { - if (state) - remote_state[pos - 1] |= kWhirlpoolAcTimerEnableMask; - else - remote_state[pos - 1] &= ~kWhirlpoolAcTimerEnableMask; -} - -void IRWhirlpoolAc::setClock(const uint16_t minspastmidnight) { - setTime(kWhirlpoolAcClockPos, minspastmidnight); -} - -uint16_t IRWhirlpoolAc::getClock() { return getTime(kWhirlpoolAcClockPos); } - -void IRWhirlpoolAc::setOffTimer(const uint16_t minspastmidnight) { - setTime(kWhirlpoolAcOffTimerPos, minspastmidnight); -} - -uint16_t IRWhirlpoolAc::getOffTimer() { - return getTime(kWhirlpoolAcOffTimerPos); -} - -bool IRWhirlpoolAc::isOffTimerEnabled() { - return isTimerEnabled(kWhirlpoolAcOffTimerPos); -} - -void IRWhirlpoolAc::enableOffTimer(const bool state) { - enableTimer(kWhirlpoolAcOffTimerPos, state); - setCommand(kWhirlpoolAcCommandOffTimer); -} - -void IRWhirlpoolAc::setOnTimer(const uint16_t minspastmidnight) { - setTime(kWhirlpoolAcOnTimerPos, minspastmidnight); -} - -uint16_t IRWhirlpoolAc::getOnTimer() { return getTime(kWhirlpoolAcOnTimerPos); } - -bool IRWhirlpoolAc::isOnTimerEnabled() { - return isTimerEnabled(kWhirlpoolAcOnTimerPos); -} - -void IRWhirlpoolAc::enableOnTimer(const bool state) { - enableTimer(kWhirlpoolAcOnTimerPos, state); - setCommand(kWhirlpoolAcCommandOnTimer); -} - -void IRWhirlpoolAc::setPowerToggle(const bool on) { - if (on) - remote_state[kWhirlpoolAcPowerTogglePos] |= kWhirlpoolAcPowerToggleMask; - else - remote_state[kWhirlpoolAcPowerTogglePos] &= ~kWhirlpoolAcPowerToggleMask; - setSuper(false); // Changing power cancels Super/Jet mode. - setCommand(kWhirlpoolAcCommandPower); -} - -bool IRWhirlpoolAc::getPowerToggle() { - return remote_state[kWhirlpoolAcPowerTogglePos] & kWhirlpoolAcPowerToggleMask; -} - -uint8_t IRWhirlpoolAc::getCommand() { - return remote_state[kWhirlpoolAcCommandPos]; -} - -void IRWhirlpoolAc::setSleep(const bool on) { - if (on) { - remote_state[kWhirlpoolAcSleepPos] |= kWhirlpoolAcSleepMask; - setFan(kWhirlpoolAcFanLow); - } else { - remote_state[kWhirlpoolAcSleepPos] &= ~kWhirlpoolAcSleepMask; - } - setCommand(kWhirlpoolAcCommandSleep); -} - -bool IRWhirlpoolAc::getSleep() { - return remote_state[kWhirlpoolAcSleepPos] & kWhirlpoolAcSleepMask; -} - -// AKA Jet/Turbo mode. -void IRWhirlpoolAc::setSuper(const bool on) { - if (on) { - setFan(kWhirlpoolAcFanHigh); - switch (getMode()) { - case kWhirlpoolAcHeat: - setTemp(kWhirlpoolAcMaxTemp + getTempOffset()); - break; - case kWhirlpoolAcCool: - default: - setTemp(kWhirlpoolAcMinTemp + getTempOffset()); - setMode(kWhirlpoolAcCool); - break; - } - remote_state[kWhirlpoolAcSuperPos] |= kWhirlpoolAcSuperMask; - } else { - remote_state[kWhirlpoolAcSuperPos] &= ~kWhirlpoolAcSuperMask; - } - setCommand(kWhirlpoolAcCommandSuper); -} - -bool IRWhirlpoolAc::getSuper() { - return remote_state[kWhirlpoolAcSuperPos] & kWhirlpoolAcSuperMask; -} - -void IRWhirlpoolAc::setCommand(const uint8_t code) { - remote_state[kWhirlpoolAcCommandPos] = code; -} - -#ifdef ARDUINO -String IRWhirlpoolAc::timeToString(const uint16_t minspastmidnight) { - String result = ""; -#else -std::string IRWhirlpoolAc::timeToString(const uint16_t minspastmidnight) { - std::string result = ""; -#endif // ARDUINO - uint8_t hours = minspastmidnight / 60; - if (hours < 10) result += "0"; - result += uint64ToString(hours); - result += ":"; - uint8_t mins = minspastmidnight % 60; - if (mins < 10) result += "0"; - result += uint64ToString(mins); - return result; -} - -// Convert the internal state into a human readable string. -#ifdef ARDUINO -String IRWhirlpoolAc::toString() { - String result = ""; -#else -std::string IRWhirlpoolAc::toString() { - std::string result = ""; -#endif // ARDUINO - result += "Model: " + uint64ToString(getModel()); - switch (getModel()) { - case DG11J191: - result += " (DG11J191)"; - break; - case DG11J13A: - result += " (DG11J13A)"; - break; - default: - result += " (UNKNOWN)"; - } - result += ", Power toggle: "; - if (getPowerToggle()) - result += "On"; - else - result += "Off"; - result += ", Mode: " + uint64ToString(getMode()); - switch (getMode()) { - case kWhirlpoolAcHeat: - result += " (HEAT)"; - break; - case kWhirlpoolAcAuto: - result += " (AUTO)"; - break; - case kWhirlpoolAcCool: - result += " (COOL)"; - break; - case kWhirlpoolAcDry: - result += " (DRY)"; - break; - case kWhirlpoolAcFan: - result += " (FAN)"; - break; - default: - result += " (UNKNOWN)"; - } - result += ", Temp: " + uint64ToString(getTemp()) + "C"; - result += ", Fan: " + uint64ToString(getFan()); - switch (getFan()) { - case kWhirlpoolAcFanAuto: - result += " (AUTO)"; - break; - case kWhirlpoolAcFanHigh: - result += " (HIGH)"; - break; - case kWhirlpoolAcFanMedium: - result += " (MEDIUM)"; - break; - case kWhirlpoolAcFanLow: - result += " (LOW)"; - break; - default: - result += " (UNKNOWN)"; - break; - } - result += ", Swing: "; - if (getSwing()) - result += "On"; - else - result += "Off"; - result += ", Light: "; - if (getLight()) - result += "On"; - else - result += "Off"; - result += ", Clock: "; - result += timeToString(getClock()); - result += ", On Timer: "; - if (isOnTimerEnabled()) - result += timeToString(getOnTimer()); - else - result += "Off"; - result += ", Off Timer: "; - if (isOffTimerEnabled()) - result += timeToString(getOffTimer()); - else - result += "Off"; - result += ", Sleep: "; - if (getSleep()) - result += "On"; - else - result += "Off"; - result += ", Super: "; - if (getSuper()) - result += "On"; - else - result += "Off"; - result += ", Command: " + uint64ToString(getCommand()); - switch (getCommand()) { - case kWhirlpoolAcCommandLight: - result += " (LIGHT)"; - break; - case kWhirlpoolAcCommandPower: - result += " (POWER)"; - break; - case kWhirlpoolAcCommandTemp: - result += " (TEMP)"; - break; - case kWhirlpoolAcCommandSleep: - result += " (SLEEP)"; - break; - case kWhirlpoolAcCommandSuper: - result += " (SUPER)"; - break; - case kWhirlpoolAcCommandOnTimer: - result += " (ONTIMER)"; - break; - case kWhirlpoolAcCommandMode: - result += " (MODE)"; - break; - case kWhirlpoolAcCommandSwing: - result += " (SWING)"; - break; - case kWhirlpoolAcCommandIFeel: - result += " (IFEEL)"; - break; - case kWhirlpoolAcCommandFanSpeed: - result += " (FANSPEED)"; - break; - case kWhirlpoolAcCommand6thSense: - result += " (6THSENSE)"; - break; - case kWhirlpoolAcCommandOffTimer: - result += " (OFFTIMER)"; - break; - default: - result += " (UNKNOWN)"; - break; - } - return result; -} - -#if DECODE_WHIRLPOOL_AC -// Decode the supplied Whirlpool A/C message. -// -// Args: -// results: Ptr to the data to decode and where to store the decode result. -// nbits: The number of data bits to expect. Typically kWhirlpoolAcBits -// strict: Flag indicating if we should perform strict matching. -// Returns: -// boolean: True if it can decode it, false if it can't. -// -// Status: STABLE / Working as intended. -// -// -// Ref: -// https://github.com/markszabo/IRremoteESP8266/issues/509 -bool IRrecv::decodeWhirlpoolAC(decode_results *results, uint16_t nbits, - bool strict) { - if (results->rawlen < 2 * nbits + 4 + kHeader + kFooter - 1) - return false; // Can't possibly be a valid Whirlpool A/C message. - if (strict) { - if (nbits != kWhirlpoolAcBits) return false; - } - - uint16_t offset = kStartOffset; - uint16_t dataBitsSoFar = 0; - uint16_t i = 0; - match_result_t data_result; - uint8_t sectionSize[kWhirlpoolAcSections] = {6, 8, 7}; - - // Header - if (!matchMark(results->rawbuf[offset++], kWhirlpoolAcHdrMark)) return false; - if (!matchSpace(results->rawbuf[offset++], kWhirlpoolAcHdrSpace)) - return false; - - // Data Section - // Keep reading bytes until we either run out of section or state to fill. - for (uint8_t section = 0, pos = 0; section < kWhirlpoolAcSections; - section++) { - pos += sectionSize[section]; - for (; offset <= results->rawlen - 16 && i < pos; - i++, dataBitsSoFar += 8, offset += data_result.used) { - data_result = - matchData(&(results->rawbuf[offset]), 8, kWhirlpoolAcBitMark, - kWhirlpoolAcOneSpace, kWhirlpoolAcBitMark, - kWhirlpoolAcZeroSpace, kTolerance, kMarkExcess, false); - if (data_result.success == false) break; // Fail - // Data is in LSB order. We need to reverse it. - results->state[i] = (uint8_t)data_result.data; - } - // Section Footer - if (!matchMark(results->rawbuf[offset++], kWhirlpoolAcBitMark)) - return false; - if (section < kWhirlpoolAcSections - 1) { // Inter-section gaps. - if (!matchSpace(results->rawbuf[offset++], kWhirlpoolAcGap)) return false; - } else { // Last section / End of message gap. - if (offset <= results->rawlen && - !matchAtLeast(results->rawbuf[offset++], kWhirlpoolAcGap)) - return false; - } - } - - // Compliance - if (strict) { - // Re-check we got the correct size/length due to the way we read the data. - if (dataBitsSoFar != kWhirlpoolAcBits) return false; - if (!IRWhirlpoolAc::validChecksum(results->state, dataBitsSoFar / 8)) - return false; - } - - // Success - results->decode_type = WHIRLPOOL_AC; - results->bits = dataBitsSoFar; - // No need to record the state as we stored it as we decoded it. - // As we use result->state, we don't record value, address, or command as it - // is a union data type. - return true; -} -#endif // WHIRLPOOL_AC diff --git a/lib/IRremoteESP8266_ID1089/src/ir_Whirlpool.h b/lib/IRremoteESP8266_ID1089/src/ir_Whirlpool.h deleted file mode 100644 index 211e30f..0000000 --- a/lib/IRremoteESP8266_ID1089/src/ir_Whirlpool.h +++ /dev/null @@ -1,161 +0,0 @@ -// Whirlpool A/C -// -// Copyright 2018 David Conran - -#ifndef IR_WHIRLPOOL_H_ -#define IR_WHIRLPOOL_H_ - -#define __STDC_LIMIT_MACROS -#include -#ifndef UNIT_TEST -#include -#else -#include -#endif -#include "IRremoteESP8266.h" -#include "IRsend.h" - -// WW WW HH HH IIIII RRRRRR LL PPPPPP OOOOO OOOOO LL -// WW WW HH HH III RR RR LL PP PP OO OO OO OO LL -// WW W WW HHHHHHH III RRRRRR LL PPPPPP OO OO OO OO LL -// WW WWW WW HH HH III RR RR LL PP OO OO OO OO LL -// WW WW HH HH IIIII RR RR LLLLLLL PP OOOO0 OOOO0 LLLLLLL - -// Ref: -// https://github.com/markszabo/IRremoteESP8266/issues/509 - -// Constants -const uint8_t kWhirlpoolAcChecksumByte1 = 13; -const uint8_t kWhirlpoolAcChecksumByte2 = kWhirlpoolAcStateLength - 1; -const uint8_t kWhirlpoolAcHeat = 0; -const uint8_t kWhirlpoolAcAuto = 1; -const uint8_t kWhirlpoolAcCool = 2; -const uint8_t kWhirlpoolAcDry = 3; -const uint8_t kWhirlpoolAcFan = 4; -const uint8_t kWhirlpoolAcModeMask = 0b00000111; -const uint8_t kWhirlpoolAcModePos = 3; -const uint8_t kWhirlpoolAcFanAuto = 0; -const uint8_t kWhirlpoolAcFanHigh = 1; -const uint8_t kWhirlpoolAcFanMedium = 2; -const uint8_t kWhirlpoolAcFanLow = 3; -const uint8_t kWhirlpoolAcFanMask = 0b00000011; -const uint8_t kWhirlpoolAcFanPos = 2; -const uint8_t kWhirlpoolAcMinTemp = 18; // 18C (DG11J1-3A), 16C (DG11J1-91) -const uint8_t kWhirlpoolAcMaxTemp = 32; // 32C (DG11J1-3A), 30C (DG11J1-91) -const uint8_t kWhirlpoolAcAutoTemp = 23; // 23C -const uint8_t kWhirlpoolAcTempMask = 0b11110000; -const uint8_t kWhirlpoolAcTempPos = 3; -const uint8_t kWhirlpoolAcSwing1Mask = 0b10000000; -const uint8_t kWhirlpoolAcSwing2Mask = 0b01000000; -const uint8_t kWhirlpoolAcLightMask = 0b00100000; -const uint8_t kWhirlpoolAcPowerToggleMask = 0b00000100; -const uint8_t kWhirlpoolAcPowerTogglePos = 2; -const uint8_t kWhirlpoolAcSleepMask = 0b00001000; -const uint8_t kWhirlpoolAcSleepPos = 2; -const uint8_t kWhirlpoolAcSuperMask = 0b10010000; -const uint8_t kWhirlpoolAcSuperPos = 5; -const uint8_t kWhirlpoolAcHourMask = 0b00011111; -const uint8_t kWhirlpoolAcMinuteMask = 0b00111111; -const uint8_t kWhirlpoolAcTimerEnableMask = 0b10000000; -const uint8_t kWhirlpoolAcClockPos = 6; -const uint8_t kWhirlpoolAcOffTimerPos = 8; -const uint8_t kWhirlpoolAcOnTimerPos = 10; -const uint8_t kWhirlpoolAcCommandPos = 15; -const uint8_t kWhirlpoolAcCommandLight = 0x00; -const uint8_t kWhirlpoolAcCommandPower = 0x01; -const uint8_t kWhirlpoolAcCommandTemp = 0x02; -const uint8_t kWhirlpoolAcCommandSleep = 0x03; -const uint8_t kWhirlpoolAcCommandSuper = 0x04; -const uint8_t kWhirlpoolAcCommandOnTimer = 0x05; -const uint8_t kWhirlpoolAcCommandMode = 0x06; -const uint8_t kWhirlpoolAcCommandSwing = 0x07; -const uint8_t kWhirlpoolAcCommandIFeel = 0x0D; -const uint8_t kWhirlpoolAcCommandFanSpeed = 0x11; -const uint8_t kWhirlpoolAcCommand6thSense = 0x17; -const uint8_t kWhirlpoolAcCommandOffTimer = 0x1D; -const uint8_t kWhirlpoolAcAltTempMask = 0b00001000; -const uint8_t kWhirlpoolAcAltTempPos = 18; - -enum whirlpool_ac_remote_model_t { - DG11J13A = 1, // DG11J1-04 too - DG11J191, -}; - -// Classes -class IRWhirlpoolAc { - public: - explicit IRWhirlpoolAc(uint16_t pin); - - void stateReset(); -#if SEND_WHIRLPOOL_AC - void send(const uint16_t repeat = kWhirlpoolAcDefaultRepeat, - const bool calcchecksum = true); -#endif // SEND_WHIRLPOOL_AC - void begin(); - void on(); - void off(); - void setPowerToggle(const bool on); - bool getPowerToggle(); - void setSleep(const bool on); - bool getSleep(); - void setSuper(const bool on); - bool getSuper(); - void setTemp(const uint8_t temp); - uint8_t getTemp(); - void setFan(const uint8_t speed); - uint8_t getFan(); - void setMode(const uint8_t mode); - uint8_t getMode(); - void setSwing(const bool on); - bool getSwing(); - void setLight(const bool on); - bool getLight(); - uint16_t getClock(); - void setClock(const uint16_t minspastmidnight); - uint16_t getOnTimer(); - void setOnTimer(const uint16_t minspastmidnight); - void enableOnTimer(const bool state); - bool isOnTimerEnabled(); - uint16_t getOffTimer(); - void setOffTimer(const uint16_t minspastmidnight); - void enableOffTimer(const bool state); - bool isOffTimerEnabled(); - void setCommand(const uint8_t code); - uint8_t getCommand(); - whirlpool_ac_remote_model_t getModel(); - void setModel(const whirlpool_ac_remote_model_t model); - uint8_t* getRaw(const bool calcchecksum = true); - void setRaw(const uint8_t new_code[], - const uint16_t length = kWhirlpoolAcStateLength); - static bool validChecksum(uint8_t state[], - const uint16_t length = kWhirlpoolAcStateLength); -#ifdef ARDUINO - String toString(); -#else - std::string toString(); -#endif - -#ifndef UNIT_TEST - - private: -#endif - // The state of the IR remote in IR code form. - uint8_t remote_state[kWhirlpoolAcStateLength]; - IRsend _irsend; - uint8_t _desiredtemp; - void checksum(const uint16_t length = kWhirlpoolAcStateLength); - uint16_t getTime(const uint16_t pos); - void setTime(const uint16_t pos, const uint16_t minspastmidnight); - bool isTimerEnabled(const uint16_t pos); - void enableTimer(const uint16_t pos, const bool state); - void _setTemp(const uint8_t temp, const bool remember = true); - void _setMode(const uint8_t mode); - int8_t getTempOffset(); -#ifdef ARDUINO - String timeToString(uint16_t minspastmidnight); -#else - std::string timeToString(uint16_t minspastmidnight); -#endif -}; - -#endif // IR_WHIRLPOOL_H_ diff --git a/lib/IRremoteESP8266_ID1089/src/ir_Whynter.cpp b/lib/IRremoteESP8266_ID1089/src/ir_Whynter.cpp deleted file mode 100644 index 555c507..0000000 --- a/lib/IRremoteESP8266_ID1089/src/ir_Whynter.cpp +++ /dev/null @@ -1,135 +0,0 @@ -// Copyright 2009 Ken Shirriff -// Copyright 2017 David Conran - -#include -#include "IRrecv.h" -#include "IRsend.h" -#include "IRutils.h" - -// 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 -// Whynter originally added from https://github.com/shirriff/Arduino-IRremote/ - -// Constants - -const uint16_t kWhynterTick = 50; -const uint16_t kWhynterHdrMarkTicks = 57; -const uint16_t kWhynterHdrMark = kWhynterHdrMarkTicks * kWhynterTick; -const uint16_t kWhynterHdrSpaceTicks = 57; -const uint16_t kWhynterHdrSpace = kWhynterHdrSpaceTicks * kWhynterTick; -const uint16_t kWhynterBitMarkTicks = 15; -const uint16_t kWhynterBitMark = kWhynterBitMarkTicks * kWhynterTick; -const uint16_t kWhynterOneSpaceTicks = 43; -const uint16_t kWhynterOneSpace = kWhynterOneSpaceTicks * kWhynterTick; -const uint16_t kWhynterZeroSpaceTicks = 15; -const uint16_t kWhynterZeroSpace = kWhynterZeroSpaceTicks * kWhynterTick; -const uint16_t kWhynterMinCommandLengthTicks = 2160; // Totally made up value. -const uint32_t kWhynterMinCommandLength = - kWhynterMinCommandLengthTicks * kWhynterTick; -const uint16_t kWhynterMinGapTicks = - kWhynterMinCommandLengthTicks - - (2 * (kWhynterBitMarkTicks + kWhynterZeroSpaceTicks) + - kWhynterBits * (kWhynterBitMarkTicks + kWhynterOneSpaceTicks)); -const uint16_t kWhynterMinGap = kWhynterMinGapTicks * kWhynterTick; - -#if SEND_WHYNTER -// Send a Whynter message. -// -// Args: -// data: message to be sent. -// nbits: Nr. of bits of the message to be sent. -// repeat: Nr. of additional times the message is to be sent. -// -// Status: STABLE -// -// Ref: -// https://github.com/z3t0/Arduino-IRremote/blob/master/ir_Whynter.cpp -void IRsend::sendWhynter(uint64_t data, uint16_t nbits, uint16_t repeat) { - // Set IR carrier frequency - enableIROut(38); - - for (uint16_t i = 0; i <= repeat; i++) { - // (Pre-)Header - mark(kWhynterBitMark); - space(kWhynterZeroSpace); - sendGeneric( - kWhynterHdrMark, kWhynterHdrSpace, kWhynterBitMark, kWhynterOneSpace, - kWhynterBitMark, kWhynterZeroSpace, kWhynterBitMark, kWhynterMinGap, - kWhynterMinCommandLength - (kWhynterBitMark + kWhynterZeroSpace), data, - nbits, 38, true, 0, // Repeats are already handled. - 50); - } -} -#endif - -#if DECODE_WHYNTER -// Decode the supplied Whynter message. -// -// Args: -// results: Ptr to the data to decode and where to store the decode result. -// nbits: Nr. of data bits to expect. -// strict: Flag indicating if we should perform strict matching. -// Returns: -// boolean: True if it can decode it, false if it can't. -// -// Status: BETA Strict mode is ALPHA. -// -// Ref: -// https://github.com/z3t0/Arduino-IRremote/blob/master/ir_Whynter.cpp -bool IRrecv::decodeWhynter(decode_results *results, uint16_t nbits, - bool strict) { - if (results->rawlen < 2 * nbits + 2 * kHeader + kFooter - 1) - return false; // We don't have enough entries to possibly match. - - // Compliance - if (strict && nbits != kWhynterBits) - return false; // Incorrect nr. of bits per spec. - - uint16_t offset = kStartOffset; - - // Header - // Sequence begins with a bit mark and a zero space. - // These are typically small, so we'll prefer to do the calibration - // on the much larger header mark & space that are next. - if (!matchMark(results->rawbuf[offset++], kWhynterBitMark)) return false; - if (!matchSpace(results->rawbuf[offset++], kWhynterZeroSpace)) return false; - // Main header mark and space - if (!matchMark(results->rawbuf[offset], kWhynterHdrMark)) return false; - // Calculate how long the common tick time is based on the header mark. - uint32_t m_tick = results->rawbuf[offset++] * kRawTick / kWhynterHdrMarkTicks; - if (!matchSpace(results->rawbuf[offset], kWhynterHdrSpace)) return false; - // Calculate how long the common tick time is based on the header space. - uint32_t s_tick = - results->rawbuf[offset++] * kRawTick / kWhynterHdrSpaceTicks; - - // Data - uint64_t data = 0; - match_result_t data_result = - matchData(&(results->rawbuf[offset]), nbits, - kWhynterBitMarkTicks * m_tick, kWhynterOneSpaceTicks * s_tick, - kWhynterBitMarkTicks * m_tick, kWhynterZeroSpaceTicks * s_tick); - if (data_result.success == false) return false; - data = data_result.data; - offset += data_result.used; - - // Footer - if (!matchMark(results->rawbuf[offset++], kWhynterBitMarkTicks * m_tick)) - return false; - if (offset < results->rawlen && - !matchAtLeast(results->rawbuf[offset], kWhynterMinGapTicks * s_tick)) - return false; - - // Success - results->decode_type = WHYNTER; - results->bits = nbits; - results->value = data; - results->address = 0; - results->command = 0; - return true; -} -#endif diff --git a/lib/rc-switch/RCSwitch.cpp b/lib/rc-switch/RCSwitch.cpp new file mode 100644 index 0000000..cb7a36b --- /dev/null +++ b/lib/rc-switch/RCSwitch.cpp @@ -0,0 +1,779 @@ +/* + RCSwitch - Arduino libary for remote control outlet switches + Copyright (c) 2011 Suat Özgür. All right reserved. + + Contributors: + - Andre Koehler / info(at)tomate-online(dot)de + - Gordeev Andrey Vladimirovich / gordeev(at)openpyro(dot)com + - Skineffect / http://forum.ardumote.com/viewtopic.php?f=2&t=46 + - Dominik Fischer / dom_fischer(at)web(dot)de + - Frank Oltmanns / .(at)gmail(dot)com + - Andreas Steinel / A.(at)gmail(dot)com + - Max Horn / max(at)quendi(dot)de + - Robert ter Vehn / .(at)gmail(dot)com + - Johann Richard / .(at)gmail(dot)com + - Vlad Gheorghe / .(at)gmail(dot)com https://github.com/vgheo + - Matias Cuenca-Acuna + + Project home: https://github.com/sui77/rc-switch/ + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include "RCSwitch.h" +#ifdef RaspberryPi +// PROGMEM and _P functions are for AVR based microprocessors, +// so we must normalize these for the ARM processor: +#define PROGMEM +#define memcpy_P(dest, src, num) memcpy((dest), (src), (num)) +#endif + +#if defined(ESP8266) +// interrupt handler and related code must be in RAM on ESP8266, +// according to issue #46. +#define RECEIVE_ATTR ICACHE_RAM_ATTR +#define VAR_ISR_ATTR +#elif defined(ESP32) +#define RECEIVE_ATTR IRAM_ATTR +#define VAR_ISR_ATTR DRAM_ATTR +#else +#define RECEIVE_ATTR +#define VAR_ISR_ATTR +#endif + +/* Format for protocol definitions: + * {pulselength, Sync bit, "0" bit, "1" bit, invertedSignal} + * + * pulselength: pulse length in microseconds, e.g. 350 + * Sync bit: {1, 31} means 1 high pulse and 31 low pulses + * (perceived as a 31*pulselength long pulse, total length of sync bit is + * 32*pulselength microseconds), i.e: + * _ + * | |_______________________________ (don't count the vertical bars) + * "0" bit: waveform for a data bit of value "0", {1, 3} means 1 high pulse + * and 3 low pulses, total length (1+3)*pulselength, i.e: + * _ + * | |___ + * "1" bit: waveform for a data bit of value "1", e.g. {3,1}: + * ___ + * | |_ + * + * These are combined to form Tri-State bits when sending or receiving codes. + */ +#if defined(ESP8266) || defined(ESP32) +static const VAR_ISR_ATTR RCSwitch::Protocol proto[] = { +#else +static const RCSwitch::Protocol PROGMEM proto[] = { +#endif + {350, {1, 31}, {1, 3}, {3, 1}, false}, // protocol 1 + {650, {1, 10}, {1, 2}, {2, 1}, false}, // protocol 2 + {100, {30, 71}, {4, 11}, {9, 6}, false}, // protocol 3 + {380, {1, 6}, {1, 3}, {3, 1}, false}, // protocol 4 + {500, {6, 14}, {1, 2}, {2, 1}, false}, // protocol 5 + {450, {23, 1}, {1, 2}, {2, 1}, true}, // protocol 6 (HT6P20B) + {150, {2, 62}, {1, 6}, {6, 1}, false}, // protocol 7 (HS2303-PT, i. e. used in AUKEY Remote) + {200, {3, 130}, {7, 16}, {3, 16}, false}, // protocol 8 Conrad RS-200 RX + {200, {130, 7}, {16, 7}, {16, 3}, true}, // protocol 9 Conrad RS-200 TX + {365, {18, 1}, {3, 1}, {1, 3}, true}, // protocol 10 (1ByOne Doorbell) + {270, {36, 1}, {1, 2}, {2, 1}, true}, // protocol 11 (HT12E) + {320, {36, 1}, {1, 2}, {2, 1}, true} // protocol 12 (SM5212) +}; + +enum +{ + numProto = sizeof(proto) / sizeof(proto[0]) +}; + +#if not defined(RCSwitchDisableReceiving) +volatile unsigned long RCSwitch::nReceivedValue = 0; +volatile unsigned int RCSwitch::nReceivedBitlength = 0; +volatile unsigned int RCSwitch::nReceivedDelay = 0; +volatile unsigned int RCSwitch::nReceivedProtocol = 0; +int RCSwitch::nReceiveTolerance = 60; +const unsigned int RCSwitch::nSeparationLimit = 4300; +// separationLimit: minimum microseconds between received codes, closer codes are ignored. +// according to discussion on issue #14 it might be more suitable to set the separation +// limit to the same time as the 'low' part of the sync signal for the current protocol. +unsigned int RCSwitch::timings[RCSWITCH_MAX_CHANGES]; +#endif + +RCSwitch::RCSwitch() +{ + this->nTransmitterPin = -1; + this->setRepeatTransmit(10); + this->setProtocol(1); +#if not defined(RCSwitchDisableReceiving) + this->nReceiverInterrupt = -1; + this->setReceiveTolerance(60); + RCSwitch::nReceivedValue = 0; +#endif +} + +/** + * Sets the protocol to send. + */ +void RCSwitch::setProtocol(Protocol protocol) +{ + this->protocol = protocol; +} + +/** + * Sets the protocol to send, from a list of predefined protocols + */ +void RCSwitch::setProtocol(int nProtocol) +{ + if (nProtocol < 1 || nProtocol > numProto) + { + nProtocol = 1; // TODO: trigger an error, e.g. "bad protocol" ??? + } +#if defined(ESP8266) || defined(ESP32) + this->protocol = proto[nProtocol - 1]; +#else + memcpy_P(&this->protocol, &proto[nProtocol - 1], sizeof(Protocol)); +#endif +} + +/** + * Sets the protocol to send with pulse length in microseconds. + */ +void RCSwitch::setProtocol(int nProtocol, int nPulseLength) +{ + setProtocol(nProtocol); + this->setPulseLength(nPulseLength); +} + +/** + * Sets pulse length in microseconds + */ +void RCSwitch::setPulseLength(int nPulseLength) +{ + this->protocol.pulseLength = nPulseLength; +} + +/** + * Sets Repeat Transmits + */ +void RCSwitch::setRepeatTransmit(int nRepeatTransmit) +{ + this->nRepeatTransmit = nRepeatTransmit; +} + +/** + * Set Receiving Tolerance + */ +#if not defined(RCSwitchDisableReceiving) +void RCSwitch::setReceiveTolerance(int nPercent) +{ + RCSwitch::nReceiveTolerance = nPercent; +} +#endif + +/** + * Enable transmissions + * + * @param nTransmitterPin Arduino Pin to which the sender is connected to + */ +void RCSwitch::enableTransmit(int nTransmitterPin) +{ + this->nTransmitterPin = nTransmitterPin; + pinMode(this->nTransmitterPin, OUTPUT); +} + +/** + * Disable transmissions + */ +void RCSwitch::disableTransmit() +{ + this->nTransmitterPin = -1; +} + +/** + * Switch a remote switch on (Type D REV) + * + * @param sGroup Code of the switch group (A,B,C,D) + * @param nDevice Number of the switch itself (1..3) + */ +void RCSwitch::switchOn(char sGroup, int nDevice) +{ + this->sendTriState(this->getCodeWordD(sGroup, nDevice, true)); +} + +/** + * Switch a remote switch off (Type D REV) + * + * @param sGroup Code of the switch group (A,B,C,D) + * @param nDevice Number of the switch itself (1..3) + */ +void RCSwitch::switchOff(char sGroup, int nDevice) +{ + this->sendTriState(this->getCodeWordD(sGroup, nDevice, false)); +} + +/** + * Switch a remote switch on (Type C Intertechno) + * + * @param sFamily Familycode (a..f) + * @param nGroup Number of group (1..4) + * @param nDevice Number of device (1..4) + */ +void RCSwitch::switchOn(char sFamily, int nGroup, int nDevice) +{ + this->sendTriState(this->getCodeWordC(sFamily, nGroup, nDevice, true)); +} + +/** + * Switch a remote switch off (Type C Intertechno) + * + * @param sFamily Familycode (a..f) + * @param nGroup Number of group (1..4) + * @param nDevice Number of device (1..4) + */ +void RCSwitch::switchOff(char sFamily, int nGroup, int nDevice) +{ + this->sendTriState(this->getCodeWordC(sFamily, nGroup, nDevice, false)); +} + +/** + * Switch a remote switch on (Type B with two rotary/sliding switches) + * + * @param nAddressCode Number of the switch group (1..4) + * @param nChannelCode Number of the switch itself (1..4) + */ +void RCSwitch::switchOn(int nAddressCode, int nChannelCode) +{ + this->sendTriState(this->getCodeWordB(nAddressCode, nChannelCode, true)); +} + +/** + * Switch a remote switch off (Type B with two rotary/sliding switches) + * + * @param nAddressCode Number of the switch group (1..4) + * @param nChannelCode Number of the switch itself (1..4) + */ +void RCSwitch::switchOff(int nAddressCode, int nChannelCode) +{ + this->sendTriState(this->getCodeWordB(nAddressCode, nChannelCode, false)); +} + +/** + * Deprecated, use switchOn(const char* sGroup, const char* sDevice) instead! + * Switch a remote switch on (Type A with 10 pole DIP switches) + * + * @param sGroup Code of the switch group (refers to DIP switches 1..5 where "1" = on and "0" = off, if all DIP switches are on it's "11111") + * @param nChannelCode Number of the switch itself (1..5) + */ +void RCSwitch::switchOn(const char *sGroup, int nChannel) +{ + const char *code[6] = {"00000", "10000", "01000", "00100", "00010", "00001"}; + this->switchOn(sGroup, code[nChannel]); +} + +/** + * Deprecated, use switchOff(const char* sGroup, const char* sDevice) instead! + * Switch a remote switch off (Type A with 10 pole DIP switches) + * + * @param sGroup Code of the switch group (refers to DIP switches 1..5 where "1" = on and "0" = off, if all DIP switches are on it's "11111") + * @param nChannelCode Number of the switch itself (1..5) + */ +void RCSwitch::switchOff(const char *sGroup, int nChannel) +{ + const char *code[6] = {"00000", "10000", "01000", "00100", "00010", "00001"}; + this->switchOff(sGroup, code[nChannel]); +} + +/** + * Switch a remote switch on (Type A with 10 pole DIP switches) + * + * @param sGroup Code of the switch group (refers to DIP switches 1..5 where "1" = on and "0" = off, if all DIP switches are on it's "11111") + * @param sDevice Code of the switch device (refers to DIP switches 6..10 (A..E) where "1" = on and "0" = off, if all DIP switches are on it's "11111") + */ +void RCSwitch::switchOn(const char *sGroup, const char *sDevice) +{ + this->sendTriState(this->getCodeWordA(sGroup, sDevice, true)); +} + +/** + * Switch a remote switch off (Type A with 10 pole DIP switches) + * + * @param sGroup Code of the switch group (refers to DIP switches 1..5 where "1" = on and "0" = off, if all DIP switches are on it's "11111") + * @param sDevice Code of the switch device (refers to DIP switches 6..10 (A..E) where "1" = on and "0" = off, if all DIP switches are on it's "11111") + */ +void RCSwitch::switchOff(const char *sGroup, const char *sDevice) +{ + this->sendTriState(this->getCodeWordA(sGroup, sDevice, false)); +} + +/** + * Returns a char[13], representing the code word to be send. + * + */ +char *RCSwitch::getCodeWordA(const char *sGroup, const char *sDevice, bool bStatus) +{ + static char sReturn[13]; + int nReturnPos = 0; + + for (int i = 0; i < 5; i++) + { + sReturn[nReturnPos++] = (sGroup[i] == '0') ? 'F' : '0'; + } + + for (int i = 0; i < 5; i++) + { + sReturn[nReturnPos++] = (sDevice[i] == '0') ? 'F' : '0'; + } + + sReturn[nReturnPos++] = bStatus ? '0' : 'F'; + sReturn[nReturnPos++] = bStatus ? 'F' : '0'; + + sReturn[nReturnPos] = '\0'; + return sReturn; +} + +/** + * Encoding for type B switches with two rotary/sliding switches. + * + * The code word is a tristate word and with following bit pattern: + * + * +-----------------------------+-----------------------------+----------+------------+ + * | 4 bits address | 4 bits address | 3 bits | 1 bit | + * | switch group | switch number | not used | on / off | + * | 1=0FFF 2=F0FF 3=FF0F 4=FFF0 | 1=0FFF 2=F0FF 3=FF0F 4=FFF0 | FFF | on=F off=0 | + * +-----------------------------+-----------------------------+----------+------------+ + * + * @param nAddressCode Number of the switch group (1..4) + * @param nChannelCode Number of the switch itself (1..4) + * @param bStatus Whether to switch on (true) or off (false) + * + * @return char[13], representing a tristate code word of length 12 + */ +char *RCSwitch::getCodeWordB(int nAddressCode, int nChannelCode, bool bStatus) +{ + static char sReturn[13]; + int nReturnPos = 0; + + if (nAddressCode < 1 || nAddressCode > 4 || nChannelCode < 1 || nChannelCode > 4) + { + return 0; + } + + for (int i = 1; i <= 4; i++) + { + sReturn[nReturnPos++] = (nAddressCode == i) ? '0' : 'F'; + } + + for (int i = 1; i <= 4; i++) + { + sReturn[nReturnPos++] = (nChannelCode == i) ? '0' : 'F'; + } + + sReturn[nReturnPos++] = 'F'; + sReturn[nReturnPos++] = 'F'; + sReturn[nReturnPos++] = 'F'; + + sReturn[nReturnPos++] = bStatus ? 'F' : '0'; + + sReturn[nReturnPos] = '\0'; + return sReturn; +} + +/** + * Like getCodeWord (Type C = Intertechno) + */ +char *RCSwitch::getCodeWordC(char sFamily, int nGroup, int nDevice, bool bStatus) +{ + static char sReturn[13]; + int nReturnPos = 0; + + int nFamily = (int)sFamily - 'a'; + if (nFamily < 0 || nFamily > 15 || nGroup < 1 || nGroup > 4 || nDevice < 1 || nDevice > 4) + { + return 0; + } + + // encode the family into four bits + sReturn[nReturnPos++] = (nFamily & 1) ? 'F' : '0'; + sReturn[nReturnPos++] = (nFamily & 2) ? 'F' : '0'; + sReturn[nReturnPos++] = (nFamily & 4) ? 'F' : '0'; + sReturn[nReturnPos++] = (nFamily & 8) ? 'F' : '0'; + + // encode the device and group + sReturn[nReturnPos++] = ((nDevice - 1) & 1) ? 'F' : '0'; + sReturn[nReturnPos++] = ((nDevice - 1) & 2) ? 'F' : '0'; + sReturn[nReturnPos++] = ((nGroup - 1) & 1) ? 'F' : '0'; + sReturn[nReturnPos++] = ((nGroup - 1) & 2) ? 'F' : '0'; + + // encode the status code + sReturn[nReturnPos++] = '0'; + sReturn[nReturnPos++] = 'F'; + sReturn[nReturnPos++] = 'F'; + sReturn[nReturnPos++] = bStatus ? 'F' : '0'; + + sReturn[nReturnPos] = '\0'; + return sReturn; +} + +/** + * Encoding for the REV Switch Type + * + * The code word is a tristate word and with following bit pattern: + * + * +-----------------------------+-------------------+----------+--------------+ + * | 4 bits address | 3 bits address | 3 bits | 2 bits | + * | switch group | device number | not used | on / off | + * | A=1FFF B=F1FF C=FF1F D=FFF1 | 1=0FF 2=F0F 3=FF0 | 000 | on=10 off=01 | + * +-----------------------------+-------------------+----------+--------------+ + * + * Source: http://www.the-intruder.net/funksteckdosen-von-rev-uber-arduino-ansteuern/ + * + * @param sGroup Name of the switch group (A..D, resp. a..d) + * @param nDevice Number of the switch itself (1..3) + * @param bStatus Whether to switch on (true) or off (false) + * + * @return char[13], representing a tristate code word of length 12 + */ +char *RCSwitch::getCodeWordD(char sGroup, int nDevice, bool bStatus) +{ + static char sReturn[13]; + int nReturnPos = 0; + + // sGroup must be one of the letters in "abcdABCD" + int nGroup = (sGroup >= 'a') ? (int)sGroup - 'a' : (int)sGroup - 'A'; + if (nGroup < 0 || nGroup > 3 || nDevice < 1 || nDevice > 3) + { + return 0; + } + + for (int i = 0; i < 4; i++) + { + sReturn[nReturnPos++] = (nGroup == i) ? '1' : 'F'; + } + + for (int i = 1; i <= 3; i++) + { + sReturn[nReturnPos++] = (nDevice == i) ? '1' : 'F'; + } + + sReturn[nReturnPos++] = '0'; + sReturn[nReturnPos++] = '0'; + sReturn[nReturnPos++] = '0'; + + sReturn[nReturnPos++] = bStatus ? '1' : '0'; + sReturn[nReturnPos++] = bStatus ? '0' : '1'; + + sReturn[nReturnPos] = '\0'; + return sReturn; +} + +/** + * @param sCodeWord a tristate code word consisting of the letter 0, 1, F + */ +void RCSwitch::sendTriState(const char *sCodeWord) +{ + // turn the tristate code word into the corresponding bit pattern, then send it + unsigned long code = 0; + unsigned int length = 0; + for (const char *p = sCodeWord; *p; p++) + { + code <<= 2L; + switch (*p) + { + case '0': + // bit pattern 00 + break; + case 'F': + // bit pattern 01 + code |= 1L; + break; + case '1': + // bit pattern 11 + code |= 3L; + break; + } + length += 2; + } + this->send(code, length); +} + +/** + * @param sCodeWord a binary code word consisting of the letter 0, 1 + */ +void RCSwitch::send(const char *sCodeWord) +{ + // turn the tristate code word into the corresponding bit pattern, then send it + unsigned long code = 0; + unsigned int length = 0; + for (const char *p = sCodeWord; *p; p++) + { + code <<= 1L; + if (*p != '0') + code |= 1L; + length++; + } + this->send(code, length); +} + +/** + * Transmit the first 'length' bits of the integer 'code'. The + * bits are sent from MSB to LSB, i.e., first the bit at position length-1, + * then the bit at position length-2, and so on, till finally the bit at position 0. + */ +void RCSwitch::send(unsigned long code, unsigned int length) +{ + if (this->nTransmitterPin == -1) + return; + +#if not defined(RCSwitchDisableReceiving) + // make sure the receiver is disabled while we transmit + int nReceiverInterrupt_backup = nReceiverInterrupt; + if (nReceiverInterrupt_backup != -1) + { + this->disableReceive(); + } +#endif + + for (int nRepeat = 0; nRepeat < nRepeatTransmit; nRepeat++) + { + for (int i = length - 1; i >= 0; i--) + { + if (code & (1L << i)) + this->transmit(protocol.one); + else + this->transmit(protocol.zero); + } + this->transmit(protocol.syncFactor); + } + + // Disable transmit after sending (i.e., for inverted protocols) + digitalWrite(this->nTransmitterPin, LOW); + +#if not defined(RCSwitchDisableReceiving) + // enable receiver again if we just disabled it + if (nReceiverInterrupt_backup != -1) + { + this->enableReceive(nReceiverInterrupt_backup); + } +#endif +} + +/** + * Transmit a single high-low pulse. + */ +void RCSwitch::transmit(HighLow pulses) +{ + uint8_t firstLogicLevel = (this->protocol.invertedSignal) ? LOW : HIGH; + uint8_t secondLogicLevel = (this->protocol.invertedSignal) ? HIGH : LOW; + + digitalWrite(this->nTransmitterPin, firstLogicLevel); + delayMicroseconds(this->protocol.pulseLength * pulses.high); + digitalWrite(this->nTransmitterPin, secondLogicLevel); + delayMicroseconds(this->protocol.pulseLength * pulses.low); +} + +#if not defined(RCSwitchDisableReceiving) +/** + * Enable receiving data + */ +void RCSwitch::enableReceive(int interrupt) +{ + this->nReceiverInterrupt = interrupt; + this->enableReceive(); +} + +void RCSwitch::enableReceive() +{ + if (this->nReceiverInterrupt != -1) + { + RCSwitch::nReceivedValue = 0; + RCSwitch::nReceivedBitlength = 0; +#if defined(RaspberryPi) // Raspberry Pi + wiringPiISR(this->nReceiverInterrupt, INT_EDGE_BOTH, &handleInterrupt); +#else // Arduino + + attachInterrupt(this->nReceiverInterrupt, handleInterrupt, CHANGE); +#endif + } +} + +/** + * Disable receiving data + */ +void RCSwitch::disableReceive() +{ +#if not defined(RaspberryPi) // Arduino + detachInterrupt(this->nReceiverInterrupt); +#endif // For Raspberry Pi (wiringPi) you can't unregister the ISR + this->nReceiverInterrupt = -1; +} + +bool RCSwitch::available() +{ + return RCSwitch::nReceivedValue != 0; +} + +void RCSwitch::resetAvailable() +{ + RCSwitch::nReceivedValue = 0; +} + +unsigned long RCSwitch::getReceivedValue() +{ + return RCSwitch::nReceivedValue; +} + +unsigned int RCSwitch::getReceivedBitlength() +{ + return RCSwitch::nReceivedBitlength; +} + +unsigned int RCSwitch::getReceivedDelay() +{ + return RCSwitch::nReceivedDelay; +} + +unsigned int RCSwitch::getReceivedProtocol() +{ + return RCSwitch::nReceivedProtocol; +} + +unsigned int *RCSwitch::getReceivedRawdata() +{ + return RCSwitch::timings; +} + +/* helper function for the receiveProtocol method */ +static inline unsigned int diff(int A, int B) +{ + return abs(A - B); +} + +/** + * + */ +bool RECEIVE_ATTR RCSwitch::receiveProtocol(const int p, unsigned int changeCount) +{ +#if defined(ESP8266) || defined(ESP32) + const Protocol &pro = proto[p - 1]; +#else + Protocol pro; + memcpy_P(&pro, &proto[p - 1], sizeof(Protocol)); +#endif + + unsigned long code = 0; + //Assuming the longer pulse length is the pulse captured in timings[0] + const unsigned int syncLengthInPulses = ((pro.syncFactor.low) > (pro.syncFactor.high)) ? (pro.syncFactor.low) : (pro.syncFactor.high); + const unsigned int delay = RCSwitch::timings[0] / syncLengthInPulses; + const unsigned int delayTolerance = delay * RCSwitch::nReceiveTolerance / 100; + + /* For protocols that start low, the sync period looks like + * _________ + * _____________| |XXXXXXXXXXXX| + * + * |--1st dur--|-2nd dur-|-Start data-| + * + * The 3rd saved duration starts the data. + * + * For protocols that start high, the sync period looks like + * + * ______________ + * | |____________|XXXXXXXXXXXXX| + * + * |-filtered out-|--1st dur--|--Start data--| + * + * The 2nd saved duration starts the data + */ + const unsigned int firstDataTiming = (pro.invertedSignal) ? (2) : (1); + + for (unsigned int i = firstDataTiming; i < changeCount - 1; i += 2) + { + code <<= 1; + if (diff(RCSwitch::timings[i], delay * pro.zero.high) < delayTolerance && + diff(RCSwitch::timings[i + 1], delay * pro.zero.low) < delayTolerance) + { + // zero + } + else if (diff(RCSwitch::timings[i], delay * pro.one.high) < delayTolerance && + diff(RCSwitch::timings[i + 1], delay * pro.one.low) < delayTolerance) + { + // one + code |= 1; + } + else + { + // Failed + return false; + } + } + + if (changeCount > 7) + { // ignore very short transmissions: no device sends them, so this must be noise + RCSwitch::nReceivedValue = code; + RCSwitch::nReceivedBitlength = (changeCount - 1) / 2; + RCSwitch::nReceivedDelay = delay; + RCSwitch::nReceivedProtocol = p; + return true; + } + + return false; +} + +void RECEIVE_ATTR RCSwitch::handleInterrupt() +{ + + static unsigned int changeCount = 0; + static unsigned long lastTime = 0; + static unsigned int repeatCount = 0; + + const long time = micros(); + const unsigned int duration = time - lastTime; + + if (duration > RCSwitch::nSeparationLimit) + { + // A long stretch without signal level change occurred. This could + // be the gap between two transmission. + if ((repeatCount == 0) || (diff(duration, RCSwitch::timings[0]) < 200)) + { + // This long signal is close in length to the long signal which + // started the previously recorded timings; this suggests that + // it may indeed by a a gap between two transmissions (we assume + // here that a sender will send the signal multiple times, + // with roughly the same gap between them). + repeatCount++; + if (repeatCount == 2) + { + for (unsigned int i = 1; i <= numProto; i++) + { + if (receiveProtocol(i, changeCount)) + { + // receive succeeded for protocol i + break; + } + } + repeatCount = 0; + } + } + changeCount = 0; + } + + // detect overflow + if (changeCount >= RCSWITCH_MAX_CHANGES) + { + changeCount = 0; + repeatCount = 0; + } + + RCSwitch::timings[changeCount++] = duration; + lastTime = time; +} +#endif diff --git a/lib/rc-switch/RCSwitch.h b/lib/rc-switch/RCSwitch.h new file mode 100644 index 0000000..b7755e0 --- /dev/null +++ b/lib/rc-switch/RCSwitch.h @@ -0,0 +1,184 @@ +/* + RCSwitch - Arduino libary for remote control outlet switches + Copyright (c) 2011 Suat Özgür. All right reserved. + + Contributors: + - Andre Koehler / info(at)tomate-online(dot)de + - Gordeev Andrey Vladimirovich / gordeev(at)openpyro(dot)com + - Skineffect / http://forum.ardumote.com/viewtopic.php?f=2&t=46 + - Dominik Fischer / dom_fischer(at)web(dot)de + - Frank Oltmanns / .(at)gmail(dot)com + - Max Horn / max(at)quendi(dot)de + - Robert ter Vehn / .(at)gmail(dot)com + + Project home: https://github.com/sui77/rc-switch/ + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ +#ifndef _RCSwitch_h +#define _RCSwitch_h + +#if defined(ARDUINO) && ARDUINO >= 100 + #include "Arduino.h" +#elif defined(ENERGIA) // LaunchPad, FraunchPad and StellarPad specific + #include "Energia.h" +#elif defined(RPI) // Raspberry Pi + #define RaspberryPi + + // Include libraries for RPi: + #include /* memcpy */ + #include /* abs */ + #include +#elif defined(SPARK) + #include "application.h" +#else + #include "WProgram.h" +#endif + +#include + + +// At least for the ATTiny X4/X5, receiving has to be disabled due to +// missing libm depencies (udivmodhi4) +#if defined( __AVR_ATtinyX5__ ) or defined ( __AVR_ATtinyX4__ ) +#define RCSwitchDisableReceiving +#endif + +// Number of maximum high/Low changes per packet. +// We can handle up to (unsigned long) => 32 bit * 2 H/L changes per bit + 2 for sync +#define RCSWITCH_MAX_CHANGES 67 + +class RCSwitch { + + public: + RCSwitch(); + + void switchOn(int nGroupNumber, int nSwitchNumber); + void switchOff(int nGroupNumber, int nSwitchNumber); + void switchOn(const char* sGroup, int nSwitchNumber); + void switchOff(const char* sGroup, int nSwitchNumber); + void switchOn(char sFamily, int nGroup, int nDevice); + void switchOff(char sFamily, int nGroup, int nDevice); + void switchOn(const char* sGroup, const char* sDevice); + void switchOff(const char* sGroup, const char* sDevice); + void switchOn(char sGroup, int nDevice); + void switchOff(char sGroup, int nDevice); + + void sendTriState(const char* sCodeWord); + void send(unsigned long code, unsigned int length); + void send(const char* sCodeWord); + + #if not defined( RCSwitchDisableReceiving ) + void enableReceive(int interrupt); + void enableReceive(); + void disableReceive(); + bool available(); + void resetAvailable(); + + unsigned long getReceivedValue(); + unsigned int getReceivedBitlength(); + unsigned int getReceivedDelay(); + unsigned int getReceivedProtocol(); + unsigned int* getReceivedRawdata(); + #endif + + void enableTransmit(int nTransmitterPin); + void disableTransmit(); + void setPulseLength(int nPulseLength); + void setRepeatTransmit(int nRepeatTransmit); + #if not defined( RCSwitchDisableReceiving ) + void setReceiveTolerance(int nPercent); + #endif + + /** + * Description of a single pule, which consists of a high signal + * whose duration is "high" times the base pulse length, followed + * by a low signal lasting "low" times the base pulse length. + * Thus, the pulse overall lasts (high+low)*pulseLength + */ + struct HighLow { + uint8_t high; + uint8_t low; + }; + + /** + * A "protocol" describes how zero and one bits are encoded into high/low + * pulses. + */ + struct Protocol { + /** base pulse length in microseconds, e.g. 350 */ + uint16_t pulseLength; + + HighLow syncFactor; + HighLow zero; + HighLow one; + + /** + * If true, interchange high and low logic levels in all transmissions. + * + * By default, RCSwitch assumes that any signals it sends or receives + * can be broken down into pulses which start with a high signal level, + * followed by a a low signal level. This is e.g. the case for the + * popular PT 2260 encoder chip, and thus many switches out there. + * + * But some devices do it the other way around, and start with a low + * signal level, followed by a high signal level, e.g. the HT6P20B. To + * accommodate this, one can set invertedSignal to true, which causes + * RCSwitch to change how it interprets any HighLow struct FOO: It will + * then assume transmissions start with a low signal lasting + * FOO.high*pulseLength microseconds, followed by a high signal lasting + * FOO.low*pulseLength microseconds. + */ + bool invertedSignal; + }; + + void setProtocol(Protocol protocol); + void setProtocol(int nProtocol); + void setProtocol(int nProtocol, int nPulseLength); + + private: + char* getCodeWordA(const char* sGroup, const char* sDevice, bool bStatus); + char* getCodeWordB(int nGroupNumber, int nSwitchNumber, bool bStatus); + char* getCodeWordC(char sFamily, int nGroup, int nDevice, bool bStatus); + char* getCodeWordD(char group, int nDevice, bool bStatus); + void transmit(HighLow pulses); + + #if not defined( RCSwitchDisableReceiving ) + static void handleInterrupt(); + static bool receiveProtocol(const int p, unsigned int changeCount); + int nReceiverInterrupt; + #endif + int nTransmitterPin; + int nRepeatTransmit; + + Protocol protocol; + + #if not defined( RCSwitchDisableReceiving ) + static int nReceiveTolerance; + volatile static unsigned long nReceivedValue; + volatile static unsigned int nReceivedBitlength; + volatile static unsigned int nReceivedDelay; + volatile static unsigned int nReceivedProtocol; + const static unsigned int nSeparationLimit; + /* + * timings[0] contains sync timing, followed by a number of bits + */ + static unsigned int timings[RCSWITCH_MAX_CHANGES]; + #endif + + +}; + +#endif diff --git a/lib/rc-switch/README.md b/lib/rc-switch/README.md new file mode 100644 index 0000000..f6a679c --- /dev/null +++ b/lib/rc-switch/README.md @@ -0,0 +1,42 @@ +# rc-switch +[![arduino-library-badge](https://www.ardu-badge.com/badge/rc-switch.svg?)](https://www.ardu-badge.com/rc-switch) +[![Build Status](https://travis-ci.org/sui77/rc-switch.svg?branch=master)](https://travis-ci.org/sui77/rc-switch) + +Use your Arduino or [Raspberry Pi](https://github.com/r10r/rcswitch-pi) to operate remote radio controlled devices + +## Download +https://github.com/sui77/rc-switch/releases/latest + +rc-switch is also listed in the arduino library manager. + +## Wiki +https://github.com/sui77/rc-switch/wiki + +## Info +### Send RC codes + +Use your Arduino or Raspberry Pi to operate remote radio controlled devices. +This will most likely work with all popular low cost power outlet sockets. If +yours doesn't work, you might need to adjust the pulse length. + +All you need is a Arduino or Raspberry Pi, a 315/433MHz AM transmitter and one +or more devices with one of the supported chipsets: + + - SC5262 / SC5272 + - HX2262 / HX2272 + - PT2262 / PT2272 + - EV1527 / RT1527 / FP1527 / HS1527 + - Intertechno outlets + - HT6P20X + +### Receive and decode RC codes + +Find out what codes your remote is sending. Use your remote to control your +Arduino. + +All you need is an Arduino, a 315/433MHz AM receiver (altough there is no +instruction yet, yes it is possible to hack an existing device) and a remote +hand set. + +For the Raspberry Pi, clone the https://github.com/ninjablocks/433Utils project to +compile a sniffer tool and transmission commands. diff --git a/lib/rc-switch/library.json b/lib/rc-switch/library.json new file mode 100644 index 0000000..e161d32 --- /dev/null +++ b/lib/rc-switch/library.json @@ -0,0 +1,21 @@ +{ + "name": "rc-switch", + "description": "Use your Arduino or Raspberry Pi to operate remote radio controlled devices", + "keywords": "rf, radio, wireless", + "authors": + { + "name": "Suat Ozgur" + }, + "repository": + { + "type": "git", + "url": "https://github.com/sui77/rc-switch.git" + }, + "version": "2.6.3", + "frameworks": [ + "arduino", + "energia", + "wiringpi" + ], + "platforms": "*" +} diff --git a/lib/rc-switch/library.properties b/lib/rc-switch/library.properties new file mode 100644 index 0000000..2fa4854 --- /dev/null +++ b/lib/rc-switch/library.properties @@ -0,0 +1,10 @@ +name=rc-switch +version=2.6.3 +author=sui77 +maintainer=sui77,fingolfin +sentence=Operate 433/315Mhz devices. +paragraph=Use your Arduino, ESP8266/ESP32 or Raspberry Pi to operate remote radio controlled devices. This will most likely work with all popular low cost power outlet sockets. +category=Device Control +url=https://github.com/sui77/rc-switch +architectures=avr,esp8266,esp32,stm32 +includes=RCSwitch.h diff --git a/platformio.ini b/platformio.ini index b0be6b5..fbbe92f 100644 --- a/platformio.ini +++ b/platformio.ini @@ -1,22 +1,13 @@ -; PlatformIO Project Configuration File -; -; Build options: build flags, source filter -; Upload options: custom upload port, speed and extra flags -; Library options: dependencies, extra library storages -; Advanced options: extra scripting -; -; Please visit documentation for the other options and examples -; https://docs.platformio.org/page/projectconf.html -# 487 KB sketch, 4 KB eeprom, 16 KB reserved, no space for ota +# 871 KB sketch, 4 KB eeprom, 128 KB fs, 16 KB reserved, no space for ota [common] -board_512k = esp01 -ldscript_512k = eagle.flash.512k0m1s.ld +board_1m = esp01 +ldscript_1m = eagle.flash.1m128.ld -# 1019 KB sketch, 16 KB eeprom, 256 KB fs, 16 KB reserved +# 1019 KB sketch, 4 KB eeprom, 256 KB fs, 16 KB reserved, 772 KB empty/ota board_2m = esp_wroom_02 ldscript_2m = eagle.flash.2m256.ld -# 1019 KB sketch, 16 KB eeprom, 992 KB fs, 16 KB reserved, 2048 KB empty/ota +# 1019 KB sketch, 4 KB eeprom, 1000 KB fs, 16 KB reserved, 2052 KB empty/ota board_4m = esp12e ldscript_4m = eagle.flash.4m1m.ld @@ -25,7 +16,7 @@ platform = espressif8266 framework = arduino monitor_speed = 115200 upload_speed = 115200 -board_build.flash_mode = dout +board_build.flash_mode = dout build_flags = -DLOG_DEBUG=1 -DLOG_INFO=1 @@ -37,4 +28,8 @@ board_build.ldscript = ${common.ldscript_4m} [env:esp8266-2m-base] board = ${common.board_2m} -board_build.ldscript = ${common.ldscript_2m} \ No newline at end of file +board_build.ldscript = ${common.ldscript_2m} + +[env:esp8266-1m-base] +board = ${common.board_1m} +board_build.ldscript = ${common.ldscript_1m} \ No newline at end of file diff --git a/src/IRbaby.cpp b/src/IRbaby.cpp index e313a08..238ce6c 100644 --- a/src/IRbaby.cpp +++ b/src/IRbaby.cpp @@ -1,8 +1,26 @@ -/**************************************** - * Auther: Caffreyfans - * Date: 2020-05-17 - * Description: MQTT to IR - ***************************************/ +/** + * + * Copyright (c) 2020 IRbaby + * + * 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. + */ + #include #include #include @@ -14,16 +32,19 @@ #include "defines.h" #include "IRbabyGlobal.h" #include "IRbabyUserSettings.h" +#include "IRbabyRF.h" void registerDevice(); // device info upload to devicehive void ICACHE_RAM_ATTR resetHandle(); // interrupt handle Ticker mqtt_check; // MQTT check timer +Ticker disable_ir; // disable IR receive +Ticker disable_rf; // disable RF receive + void setup() { if (LOG_DEBUG || LOG_ERROR || LOG_INFO) Serial.begin(BAUD_RATE); pinMode(RESET_PIN, INPUT_PULLUP); - digitalWrite(RESET_PIN, HIGH); attachInterrupt(digitalPinToInterrupt(RESET_PIN), resetHandle, ONLOW); INFOLN(); INFOLN( @@ -37,23 +58,33 @@ void setup() " |___/ \n"); wifi_manager.autoConnect(); - settingsLoad(); // 加载配置信息 + settingsLoad(); // load user settings form fs - udpInit(); // udp 初始化 - mqttInit(); // mqtt 初始化 + udpInit(); // udp init + mqttInit(); // mqtt init +#ifdef USE_RF + initRF(); // RF init +#endif + loadIRPin(ConfigData["pin"]["ir_send"], ConfigData["pin"]["ir_receive"]);; // IR init +#ifdef USE_INFO_UPLOAD registerDevice(); - +#endif mqtt_check.attach_scheduled(MQTT_CHECK_INTERVALS, mqttCheck); + disable_ir.attach_scheduled(DISABLE_SIGNAL_INTERVALS, disableIR); + disable_rf.attach_scheduled(DISABLE_SIGNAL_INTERVALS, disableRF); } void loop() { - recvRaw(); - - /* UDP 报文接受处理 */ + /* IR receive */ + recvIR(); +#ifdef USE_RF + /* RF receive */ + recvRF(); +#endif + /* UDP receive and handle */ char *msg = udpRecive(); - if (msg) - { + if (msg) { udp_msg_doc.clear(); DeserializationError error = deserializeJson(udp_msg_doc, msg); if (error) @@ -61,7 +92,7 @@ void loop() msgHandle(&udp_msg_doc, MsgType::udp); } - /* 接收 MQTT 消息 */ + /* mqtt loop */ mqttLoop(); yield(); } @@ -73,14 +104,10 @@ void resetHandle() static unsigned long start_time = millis(); unsigned long end_time = millis(); if (interrupt_time - last_interrupt_time > 10) - { start_time = millis(); - } last_interrupt_time = interrupt_time; if (end_time - start_time > 3000) - { settingsClear(); - } } void registerDevice() diff --git a/src/IRbabyGlobal.cpp b/src/IRbabyGlobal.cpp index d4ae095..81da52c 100644 --- a/src/IRbabyGlobal.cpp +++ b/src/IRbabyGlobal.cpp @@ -1,4 +1,5 @@ #include "IRbabyGlobal.h" +#include "defines.h" StaticJsonDocument<1024> recv_msg_doc; StaticJsonDocument<1024> send_msg_doc; @@ -8,3 +9,12 @@ StaticJsonDocument<1024> mqtt_msg_doc; WiFiManager wifi_manager; WiFiClient wifi_client; +uint8_t ir_send_pin = T_IR; +uint8_t ir_receive_pin = R_IR; + +#ifdef USE_RF +uint8_t rf315_send_pin = T_315; +uint8_t rf315_receive_pin = R_315; +uint8_t rf433_send_pin = T_433; +uint8_t rf433_receive_pin = R_433; +#endif \ No newline at end of file diff --git a/src/IRbabyGlobal.h b/src/IRbabyGlobal.h index 858aa84..0acb5f4 100644 --- a/src/IRbabyGlobal.h +++ b/src/IRbabyGlobal.h @@ -13,4 +13,12 @@ extern StaticJsonDocument<1024> mqtt_msg_doc; extern WiFiManager wifi_manager; extern WiFiClient wifi_client; +extern uint8_t ir_send_pin; +extern uint8_t ir_receive_pin; +#ifdef USE_RF +extern uint8_t rf315_send_pin; +extern uint8_t rf315_receive_pin; +extern uint8_t rf433_send_pin; +extern uint8_t rf433_receive_pin; +#endif #endif // IRBABY_GLOBAL_H \ No newline at end of file diff --git a/src/IRbabyIR.cpp b/src/IRbabyIR.cpp index 36b61cd..c180a6e 100644 --- a/src/IRbabyIR.cpp +++ b/src/IRbabyIR.cpp @@ -5,17 +5,19 @@ #include "IRbabyUserSettings.h" #include #include "IRbabyGlobal.h" +#include "defines.h" #define DOWNLOAD_PREFIX "http://irext-debug.oss-cn-hangzhou.aliyuncs.com/irda_" #define DOWNLOAD_SUFFIX ".bin" #define SAVE_PATH "/bin/" decode_results results; // Somewhere to store the results -IRsend *ir_send = NULL; -IRrecv *ir_recv = NULL; - -void initAC(String); -bool saveAC(String, t_remote_ac_status); +const uint8_t kTimeout = 50; +// As this program is a special purpose capture/decoder, let us use a larger +// than normal buffer so we can handle Air Conditioner remote codes. +const uint16_t kCaptureBufferSize = 1024; +static IRsend * ir_send = nullptr; +static IRrecv * ir_recv = nullptr; bool saveSignal(); void downLoadFile(String file) @@ -24,10 +26,20 @@ void downLoadFile(String file) String download_url = DOWNLOAD_PREFIX + file + DOWNLOAD_SUFFIX; String save_path = SAVE_PATH + file; File cache = LittleFS.open(save_path, "w"); + bool download_flag = false; if (cache) { http_client.begin(wifi_client, download_url); - if (HTTP_CODE_OK == http_client.GET()) + for (int i = 0; i < 5; i++) + { + if (http_client.GET() == HTTP_CODE_OK) + { + download_flag = true; + break; + } + delay(200); + } + if (download_flag) { http_client.writeToStream(&cache); DEBUGF("Download %s success\n", file.c_str()); @@ -35,7 +47,7 @@ void downLoadFile(String file) else { LittleFS.remove(save_path); - DEBUGF("Download %s failed\n", file.c_str()); + ERRORF("Download %s failed\n", file.c_str()); } } else @@ -44,7 +56,7 @@ void downLoadFile(String file) http_client.end(); } -bool sendRaw(String file_name) +bool sendIR(String file_name) { String save_path = SAVE_PATH + file_name; if (LittleFS.exists(save_path)) @@ -62,9 +74,6 @@ bool sendRaw(String file_name) INFOF("file size = %d\n", cache.size()); INFOLN(); cache.readBytes((char *)data_buffer, cache.size()); - // for (size_t i = 0; i < length; i++) - // Serial.printf("%d ", *(data_buffer + i)); - // INFOLN(); ir_recv->disableIRIn(); ir_send->sendRaw(data_buffer, length, 38); ir_recv->enableIRIn(); @@ -111,36 +120,36 @@ void sendStatus(String file, t_remote_ac_status status) ir_close(); free(user_data); free(content); + saveACStatus(file, status); } else ERRORF("Open %s is empty\n", save_path.c_str()); } cache.close(); } - saveAC(file, status); } -void recvRaw() +void recvIR() { if (ir_recv->decode(&results)) { DEBUGF("raw length = %d\n", results.rawlen - 1); String raw_data; - raw_data += "raw length = "; - raw_data += String(results.rawlen - 1) + "\n"; for (int i = 1; i < results.rawlen; i++) raw_data += String(*(results.rawbuf + i) * kRawTick) + " "; ir_recv->resume(); send_msg_doc.clear(); send_msg_doc["cmd"] = "record_rt"; - send_msg_doc["params"] = raw_data.c_str(); + send_msg_doc["params"]["signal"] = "IR"; + send_msg_doc["params"]["length"] = results.rawlen; + send_msg_doc["params"]["value"] = raw_data.c_str(); DEBUGLN(raw_data.c_str()); sendUDP(&send_msg_doc, remote_ip); saveSignal(); } } -bool saveRaw(String file_name) +bool saveIR(String file_name) { String save_path = SAVE_PATH; save_path += file_name; @@ -164,6 +173,7 @@ bool saveSignal() cache.close(); return true; } + void initAC(String file) { ACStatus[file]["power"] = 0; @@ -173,35 +183,70 @@ void initAC(String file) ACStatus[file]["speed"] = 0; } -t_remote_ac_status getACState(String file) +bool sendKey(String file_name, int key) { - if (!ACStatus.containsKey(file)) - initAC(file); - t_remote_ac_status status; - int power = ACStatus[file]["power"]; - int temperature = ACStatus[file]["temperature"]; - int mode = ACStatus[file]["mode"]; - int swing = ACStatus[file]["swing"]; - int wind_speed = ACStatus[file]["speed"]; - status.ac_power = (t_ac_power)power; - status.ac_temp = (t_ac_temperature)temperature; - status.ac_mode = (t_ac_mode)mode; - status.ac_swing = (t_ac_swing)swing; - status.ac_wind_speed = (t_ac_wind_speed)wind_speed; - return status; + String save_path = SAVE_PATH; + save_path += file_name; + if (LittleFS.exists(save_path)) + { + File cache = LittleFS.open(save_path, "r"); + if (cache) + { + UINT16 content_size = cache.size(); + DEBUGF("content size = %d\n", content_size); + + if (content_size != 0) + { + UINT8 *content = (UINT8 *)malloc(content_size * sizeof(UINT8)); + cache.seek(0L, fs::SeekSet); + cache.readBytes((char *)content, content_size); + ir_binary_open(2, 1, content, content_size); + UINT16 *user_data = (UINT16 *)malloc(1024 * sizeof(UINT16)); + UINT16 data_length = ir_decode(0, user_data, NULL, FALSE); + + DEBUGF("data_length = %d\n", data_length); + if (LOG_DEBUG) + { + for (int i = 0; i < data_length; i++) + Serial.printf("%d ", *(user_data + i)); + Serial.println(); + } + ir_recv->disableIRIn(); + ir_send->sendRaw(user_data, data_length, 38); + ir_recv->enableIRIn(); + ir_close(); + free(user_data); + free(content); + } + else + ERRORF("Open %s is empty\n", save_path.c_str()); + } + cache.close(); + } + return true; } -bool saveAC(String file, t_remote_ac_status status) +void loadIRPin(uint8_t send_pin, uint8_t recv_pin) { - bool ret = false; - ACStatus[file]["power"] = status.ac_power; - ACStatus[file]["temperature"] = status.ac_temp; - ACStatus[file]["mode"] = status.ac_mode; - ACStatus[file]["swing"] = status.ac_swing; - ACStatus[file]["speed"] = status.ac_wind_speed; - File cache = LittleFS.open("/acstatus", "w"); - if (cache && (serializeJson(ACStatus, cache) == 0)) - ret = true; - cache.close(); - return ret; + if (ir_send != nullptr) { + delete ir_send; + } + if (ir_recv != nullptr) { + delete ir_recv; + } + ir_send = new IRsend(send_pin); + DEBUGF("load send pin at %d\n", send_pin); + ir_send->begin(); + ir_recv = new IRrecv(recv_pin, kCaptureBufferSize, kTimeout, true); + disableIR(); +} + +void disableIR() +{ + ir_recv->disableIRIn(); } + +void enableIR() +{ + ir_recv->enableIRIn(); +} \ No newline at end of file diff --git a/src/IRbabyIR.h b/src/IRbabyIR.h index e3956e9..d57436c 100644 --- a/src/IRbabyIR.h +++ b/src/IRbabyIR.h @@ -5,12 +5,14 @@ #include #include #include "../lib/Irext/include/ir_decode.h" -void sendStatus(String file_name, t_remote_ac_status status); -bool sendRaw(String file_name); -void recvRaw(); -bool saveRaw(String file_name); -t_remote_ac_status getACState(String file); -extern IRsend* ir_send; -extern IRrecv* ir_recv; +void loadIRPin(uint8_t send_pin, uint8_t recv_pin); +void enableIR(); +void disableIR(); +void sendStatus(String file_name, t_remote_ac_status status); +bool sendKey(String file_name, int key); +bool sendIR(String file_name); +void recvIR(); +bool saveIR(String file_name); +void initAC(String); #endif // IRBABAYIR_H \ No newline at end of file diff --git a/src/IRbabyMQTT.cpp b/src/IRbabyMQTT.cpp index 9e630f0..18b1b97 100644 --- a/src/IRbabyMQTT.cpp +++ b/src/IRbabyMQTT.cpp @@ -36,10 +36,10 @@ bool mqttReconnect() chip_id.toUpperCase(); DEBUGF("Trying to connect %s:%d\n", host, port); if (mqtt_client.connect(chip_id.c_str(), user, - password)) + password)) { - String sub_topic = String("/IRbaby/") + - chip_id + "/set" + String("/#"); + String sub_topic = String("/IRbaby/") + + chip_id + String("/send/#"); DEBUGF("MQTT subscribe %s\n", sub_topic.c_str()); mqtt_client.subscribe(sub_topic.c_str()); flag = true; @@ -59,13 +59,14 @@ void mqttDisconnect() void callback(char *topic, byte *payload, unsigned int length) { mqtt_msg_doc.clear(); + String payload_str = ""; for (uint32_t i = 0; i < length; i++) - { payload_str += (char)payload[i]; - } String topic_str(topic); uint8_t index = 0; + String option; + String func; do { int divsion = topic_str.lastIndexOf("/"); @@ -74,16 +75,25 @@ void callback(char *topic, byte *payload, unsigned int length) switch (index++) { case 0: - mqtt_msg_doc["cmd"] = tmp; + func = tmp; break; case 1: mqtt_msg_doc["params"]["file"] = tmp; break; + case 2: + mqtt_msg_doc["params"]["type"] = tmp; + option = tmp; + break; + case 3: + mqtt_msg_doc["params"]["signal"] = tmp; + break; + case 4: + mqtt_msg_doc["cmd"] = tmp; default: break; } } while (topic_str.lastIndexOf("/") > 0); - mqtt_msg_doc["params"]["status"] = payload_str; + mqtt_msg_doc["params"][option][func] = payload_str; msgHandle(&mqtt_msg_doc, MsgType::mqtt); } diff --git a/src/IRbabyMsgHandler.cpp b/src/IRbabyMsgHandler.cpp index f94088a..33828c8 100644 --- a/src/IRbabyMsgHandler.cpp +++ b/src/IRbabyMsgHandler.cpp @@ -9,8 +9,10 @@ #include "IRbabyOTA.h" #include "defines.h" #include +#include "IRbabyRF.h" +#include "IRbabyGlobal.h" -void sendState(String file, t_remote_ac_status status); +void returnACStatus(String filename, t_remote_ac_status ac_status); bool msgHandle(StaticJsonDocument<1024> *p_recv_msg_doc, MsgType msg_type) { @@ -21,103 +23,13 @@ bool msgHandle(StaticJsonDocument<1024> *p_recv_msg_doc, MsgType msg_type) } JsonObject obj = p_recv_msg_doc->as(); String cmd = obj["cmd"]; - switch (msg_type) - { - case MsgType::mqtt: - { - String file = obj["params"]["file"]; - String var = obj["params"]["status"]; - /* 状态码处理 */ - if (cmd.length() > 0) - { - t_remote_ac_status ac_stauts = getACState(file); - if (cmd.equals("mode")) - { - if (var.equals("off")) - { - ac_stauts.ac_power = AC_POWER_OFF; - } - else - { - ac_stauts.ac_power = AC_POWER_ON; - } - if (var.equals("cool")) - ac_stauts.ac_mode = (t_ac_mode)0; - if (var.equals("heat")) - ac_stauts.ac_mode = (t_ac_mode)1; - if (var.equals("auto")) - ac_stauts.ac_mode = (t_ac_mode)2; - if (var.equals("fan")) - ac_stauts.ac_mode = (t_ac_mode)3; - if (var.equals("dry")) - ac_stauts.ac_mode = (t_ac_mode)4; - } - if (cmd.equals("temperature")) - { - ac_stauts.ac_temp = (t_ac_temperature)(var.toInt() - 16); - } - if (cmd.equals("fan")) - { - if (var.equals("auto")) - ac_stauts.ac_wind_speed = (t_ac_wind_speed)0; - if (var.equals("low")) - ac_stauts.ac_wind_speed = (t_ac_wind_speed)1; - if (var.equals("medium")) - ac_stauts.ac_wind_speed = (t_ac_wind_speed)2; - if (var.equals("high")) - ac_stauts.ac_wind_speed = (t_ac_wind_speed)3; - } - if (cmd.equals("swing")) - { - if (var.equals("on")) - ac_stauts.ac_swing = (t_ac_swing)0; - if (var.equals("off")) - ac_stauts.ac_swing = (t_ac_swing)1; - } - if (cmd.equals("status")) - { - StaticJsonDocument<1024> var_doc; - DeserializationError error = deserializeJson(var_doc, var); - if (error) - { - ERRORLN("Failed to parse var message"); - } - ac_stauts.ac_power = t_ac_power((int)var_doc["power"]); - ac_stauts.ac_temp = t_ac_temperature((int)var_doc["temperature"]); - ac_stauts.ac_mode = t_ac_mode((int)var_doc["mode"]); - ac_stauts.ac_swing = t_ac_swing((int)var_doc["swing"]); - ac_stauts.ac_wind_speed = t_ac_wind_speed((int)var_doc["speed"]); - } - if (cmd.equals("raw")) - { - sendRaw(file); - } - else - { - sendStatus(file, ac_stauts); - } - } - } - break; + JsonObject params = obj["params"]; + send_msg_doc.clear(); - case MsgType::udp: + if (cmd.equalsIgnoreCase("query")) { - send_msg_doc.clear(); - - if (cmd.equals("config")) - { - JsonObject params = obj["params"]; - for (JsonPair kv : params) - { - ConfigData[kv.key()] = kv.value(); - } - send_msg_doc["cmd"] = "return"; - send_msg_doc["params"]["message"] = "set success"; - returnUDP(&send_msg_doc); - settingsSave(); - } - - if (cmd.equals("discovery")) + String type = params["type"]; + if (type.equals("discovery")) { String chip_id = String(ESP.getChipId(), HEX); chip_id.toUpperCase(); @@ -148,57 +60,7 @@ bool msgHandle(StaticJsonDocument<1024> *p_recv_msg_doc, MsgType msg_type) remote_ip.fromString(ip); sendUDP(&send_msg_doc, remote_ip); } - - if (cmd.equals("send")) - { - JsonObject params = obj["params"]; - String file_name = params["file"]; - if (params.containsKey("status")) - { - JsonObject statusJson = params["status"]; - t_remote_ac_status status; - status.ac_power = t_ac_power((int)statusJson["power"]); - status.ac_temp = t_ac_temperature((int)statusJson["temperature"]); - status.ac_mode = t_ac_mode((int)statusJson["mode"]); - status.ac_swing = t_ac_swing((int)statusJson["swing"]); - status.ac_wind_speed = t_ac_wind_speed((int)statusJson["speed"]); - status.ac_display = 1; - status.ac_timer = 0; - status.ac_sleep = 0; - sendStatus(file_name, status); - } - else - { - sendRaw(file_name); - } - } - - if (cmd.equals("update")) - { - JsonObject params = obj["params"]; - String url = params["url"]; - otaUpdate(url); - } - - if (cmd.equals("record")) - { - String ip = obj["params"]; - remote_ip.fromString(ip); - ir_recv->enableIRIn(); - } - - if (cmd.equals("save")) - { - String file_name = obj["params"]["file"]; - saveRaw(file_name); - } - - if (cmd.equals("reset")) - { - settingsClear(); - } - - if (cmd.equals("info")) + else if (type.equals("info")) { String free_mem = String(ESP.getFreeHeap() / 1024) + "KB"; String chip_id = String(ESP.getChipId(), HEX); @@ -228,22 +90,206 @@ bool msgHandle(StaticJsonDocument<1024> *p_recv_msg_doc, MsgType msg_type) returnUDP(&send_msg_doc); } } - break; - default: - break; + if (cmd.equalsIgnoreCase("send")) + { + String signal = params["signal"]; + String type = params["type"]; + + if (signal.equalsIgnoreCase("ir")) + { + if (type.equalsIgnoreCase("status")) + { + String file = params["file"]; + JsonObject statusJson = params[type]; + t_remote_ac_status ac_status; + ac_status.ac_power = t_ac_power((int)statusJson["power"]); + ac_status.ac_temp = t_ac_temperature((int)statusJson["temperature"]); + ac_status.ac_mode = t_ac_mode((int)statusJson["mode"]); + ac_status.ac_swing = t_ac_swing((int)statusJson["swing"]); + ac_status.ac_wind_speed = t_ac_wind_speed((int)statusJson["speed"]); + ac_status.ac_display = 1; + ac_status.ac_timer = 0; + ac_status.ac_sleep = 0; + sendStatus(file, ac_status); + returnACStatus(file, ac_status); + } + else if (type.equalsIgnoreCase("file")) + { + String file = params["file"]; + sendIR(file); + } + else if (type.equalsIgnoreCase("key")) + { + String file = params["file"]; + } + else if (type.equalsIgnoreCase("data")) + { + } + else if (type.equalsIgnoreCase("local")) + { + String file = params["file"]; + JsonObject localobj = params[type]; + if (!ACStatus.containsKey(file)) + initAC(file); + t_remote_ac_status ac_status = getACState(file); + if (localobj.containsKey("mode")) { + String mode = localobj["mode"]; + if (mode.equalsIgnoreCase("off")) + ac_status.ac_power = AC_POWER_OFF; + else + ac_status.ac_power = AC_POWER_ON; + + if (mode.equalsIgnoreCase("cool")) + ac_status.ac_mode = AC_MODE_COOL; + else if (mode.equalsIgnoreCase("heat")) + ac_status.ac_mode = AC_MODE_HEAT; + else if (mode.equalsIgnoreCase("auto")) + ac_status.ac_mode = AC_MODE_AUTO; + else if (mode.equalsIgnoreCase("fan") || mode.equalsIgnoreCase("fan_only")) + ac_status.ac_mode = AC_MODE_FAN; + else if (mode.equalsIgnoreCase("dry")) + ac_status.ac_mode = AC_MODE_DRY; + } else if (localobj.containsKey("temperature")) { + String temperature = localobj["temperature"]; + ac_status.ac_temp = (t_ac_temperature)(temperature.toInt() - 16); + } else if (localobj.containsKey("fan")) { + String fan = localobj["fan"]; + if (fan.equalsIgnoreCase("auto")) + ac_status.ac_wind_speed = AC_WS_AUTO; + else if (fan.equalsIgnoreCase("low")) + ac_status.ac_wind_speed = AC_WS_LOW; + else if (fan.equalsIgnoreCase("medium")) + ac_status.ac_wind_speed = AC_WS_MEDIUM; + else if (fan.equalsIgnoreCase("high")) + ac_status.ac_wind_speed = AC_WS_HIGH; + } else if (localobj.containsKey("swing")) { + String swing = localobj["swing"]; + if (swing.equalsIgnoreCase("on")) + ac_status.ac_swing = AC_SWING_ON; + else if (swing.equalsIgnoreCase("off")) + ac_status.ac_swing = AC_SWING_OFF; + } + sendStatus(file, ac_status); + returnACStatus(file, ac_status); + } + } +#ifdef USE_RF + else if (signal.equalsIgnoreCase("rf315")) + { + RFTYPE rf_type; + + rf_type = RF315; + if (type.equalsIgnoreCase("data")) + { + unsigned long code = params["code"]; + unsigned int length = params["length"]; + sendRFData(code, length, rf_type); + } + else if (type.equalsIgnoreCase("file")) + { + String file = params["file"]; + sendRFFile(file); + } + } + else if (signal.equalsIgnoreCase("rf433")) + { + RFTYPE rf_type; + if (type.equalsIgnoreCase("data")) + { + rf_type = RF433; + unsigned long code = params["code"]; + unsigned int length = params["length"]; + sendRFData(code, length, rf_type); + } + else if (type.equalsIgnoreCase("file")) + { + String file = params["file"]; + sendRFFile(file); + } + } +#endif + } + + if (cmd.equalsIgnoreCase("set")) + { + String type = params["type"]; + if (type.equals("update")) + { + String url = params["url"]; + otaUpdate(url); + } + + else if (type.equals("record")) + { + String ip = params["ip"]; + remote_ip.fromString(ip); + DEBUGLN("start record"); + enableIR(); + enableRF(); + } + + else if (type.equals("disable_record")) + { + DEBUGLN("disable record"); + disableRF(); + disableIR(); + } + + else if (type.equals("save_signal")) + { + String file_name = params["file"]; + String signal = params["signal"]; + if (signal.equals("IR")) + saveIR(file_name); + else + saveRF(file_name); + } + + else if (type.equals("reset")) + settingsClear(); + + else if (type.equals("config")) + { + JsonObject params = obj["params"]; + if (params.containsKey("mqtt")) { + ConfigData["mqtt"]["host"] = params["mqtt"]["host"]; + ConfigData["mqtt"]["port"] = params["mqtt"]["port"]; + ConfigData["mqtt"]["user"] = params["mqtt"]["host"]; + ConfigData["mqtt"]["password"] = params["mqtt"]["host"]; + } + if (params.containsKey("pin")) { + ConfigData["pin"]["ir_send"] = params["pin"]["ir_send"]; + ConfigData["pin"]["ir_receive"] = params["pin"]["ir_receive"]; + } + send_msg_doc["cmd"] = "return"; + send_msg_doc["params"]["message"] = "set success"; + returnUDP(&send_msg_doc); + settingsSave(); + mqttDisconnect(); + mqttReconnect(); + loadIRPin(ConfigData["pin"]["ir_send"], ConfigData["pin"]["ir_receive"]); + } } return true; } -void sendState(String file_name, t_remote_ac_status status) +void returnACStatus(String filename, t_remote_ac_status ac_status) { + send_msg_doc.clear(); String chip_id = String(ESP.getChipId(), HEX); - String topic_head = "/IRbaby/" + chip_id + "/state/" + file_name + "/"; - String topic = topic_head + "mode"; - String payload; - const char *mode[5] = {"auto", "cool", "test", "a", "b"}; - int index = (int)status.ac_mode; - DEBUGLN("mode is"); - DEBUGLN(mode[index]); + chip_id.toUpperCase(); + String topic_head = "/IRbaby/" + chip_id + "/state/" + filename + "/"; + const char* mode[] = {"cool", "heat", "auto", "fan_only", "dry"}; + const char* fan[] = {"auto", "low", "medium", "high", "max"}; + const char* swing[] = {"on","off"}; + if (ac_status.ac_power == AC_POWER_OFF) { + mqttPublish(topic_head + "mode", "off"); + } else { + mqttPublish(topic_head + "mode", mode[(int)ac_status.ac_mode]); + } + mqttPublish(topic_head + "temperature", String((int)ac_status.ac_temp + 16)); + mqttPublish(topic_head + "fan", fan[(int)ac_status.ac_wind_speed]); + mqttPublish(topic_head + "swing", swing[(int)ac_status.ac_swing]); + } \ No newline at end of file diff --git a/src/IRbabyMsgHandler.h b/src/IRbabyMsgHandler.h index 5aee3e4..042c85b 100644 --- a/src/IRbabyMsgHandler.h +++ b/src/IRbabyMsgHandler.h @@ -2,7 +2,6 @@ #define IREASY_MSG_HANDLE_H #include -#include "IRbabyGlobal.h" typedef enum msgtype { diff --git a/src/IRbabyRF.cpp b/src/IRbabyRF.cpp new file mode 100644 index 0000000..4166127 --- /dev/null +++ b/src/IRbabyRF.cpp @@ -0,0 +1,165 @@ +#include "IRbabyRF.h" +#include +#include "IRbabySerial.h" +#include "IRbabyGlobal.h" +#include "IRbabyUDP.h" +#include "defines.h" +#include +RCSwitch rf315; +RCSwitch rf433; + +#define FILE_PRE "/rf/" +unsigned long code_tmp; +unsigned int length_tmp; +RFTYPE rf_type_tmp; + +Ticker change_rf_ticker; // change receiver +RFTYPE rf_receiver; +bool rf_record; + +static void changeReceiver(void) +{ + static bool flag = false; + flag = !flag; + rf_receiver = flag ? RF315 : RF433; +} +void sendRFData(unsigned long code, unsigned int length, RFTYPE type) +{ + if (type == RF315) + { + rf315.send(code, length); + } + else if (type == RF433) + { + rf433.send(code, length); + } +} + +bool sendRFFile(String file_name) +{ + File cache; + unsigned long code; + unsigned int length; + RFTYPE rf_type; + String file_path = FILE_PRE + file_name; + if (file_name.equals("test")) + { + code = code_tmp; + length = length_tmp; + rf_type = rf_type_tmp; + } + else if (LittleFS.exists(file_path)) + { + cache = LittleFS.open(file_path, "r"); + if (!cache) + { + return false; + } + cache.readBytes((char *)&rf_type, sizeof(rf_type_tmp)); + cache.readBytes((char *)&code, sizeof(code)); + cache.readBytes((char *)&length, sizeof(length)); + DEBUGF("type = %d\n", rf_type); + DEBUGF("code = %ld\n", code); + DEBUGF("length = %d\n", length); + cache.close(); + } + else + { + ERRORF("%s file not exists\n", file_name.c_str()); + return false; + } + switch (rf_type) + { + case RF315: + + rf315.send(code, length); + break; + + case RF433: + rf433.send(code, length); + break; + default: + break; + } + return true; +} + +void recvRF(void) +{ + if (rf_record) + { + if (rf_receiver == RF433) + { + rf433.enableReceive(R_433); + rf315.disableReceive(); + } + else + { + rf315.enableReceive(R_315); + rf433.disableReceive(); + } + delay(100); + if (rf_receiver == RF433 && rf433.available()) + { + code_tmp = rf433.getReceivedValue(); + length_tmp = rf433.getReceivedBitlength(); + rf_type_tmp = RF433; + rf433.resetAvailable(); + send_msg_doc.clear(); + send_msg_doc["cmd"] = "record_rt"; + send_msg_doc["params"]["signal"] = "RF433"; + send_msg_doc["params"]["value"] = String(code_tmp); + sendUDP(&send_msg_doc, remote_ip); + serializeJsonPretty(send_msg_doc, Serial); + } + + else if (rf_receiver == RF315 && rf315.available()) + { + code_tmp = rf315.getReceivedValue(); + length_tmp = rf315.getReceivedBitlength(); + rf_type_tmp = RF315; + rf315.resetAvailable(); + send_msg_doc.clear(); + send_msg_doc["cmd"] = "record_rt"; + send_msg_doc["params"]["signal"] = "RF315"; + send_msg_doc["params"]["value"] = String(code_tmp); + sendUDP(&send_msg_doc, remote_ip); + serializeJsonPretty(send_msg_doc, Serial); + } + } +} + +bool saveRF(String file_name) +{ + String save_path = FILE_PRE + file_name; + File cache = LittleFS.open(save_path, "w"); + if (!cache) + { + return false; + } + cache.write((char *)&rf_type_tmp, sizeof(rf_type_tmp)); + cache.write((char *)&code_tmp, sizeof(code_tmp)); + cache.write((char *)&length_tmp, sizeof(length_tmp)); + cache.close(); + return true; +} + +void disableRF(void) +{ + rf_record = false; + rf315.disableReceive(); + rf433.disableReceive(); +} + +void enableRF(void) +{ + rf_record = true; +} + +void initRF(void) +{ + rf_record = false; + rf315.enableTransmit(T_315); + rf433.enableTransmit(T_433); + change_rf_ticker.attach_ms_scheduled(100, changeReceiver); +} \ No newline at end of file diff --git a/src/IRbabyRF.h b/src/IRbabyRF.h new file mode 100644 index 0000000..0a0f327 --- /dev/null +++ b/src/IRbabyRF.h @@ -0,0 +1,22 @@ +#ifndef IRBABYRF_H +#define IRBABYRF_H +#include +#include "RCSwitch.h" + +typedef enum +{ + RF315, + RF433 +} RFTYPE; + +void initRF(void); +bool sendRFFile(String file_name); +void sendRFData(unsigned long code, unsigned int length, RFTYPE type); +void recvRF(void); +void disableRF(void); +void enableRF(void); +bool saveRF(String file_name); + +extern RCSwitch rf315; +extern RCSwitch rf433; +#endif \ No newline at end of file diff --git a/src/IRbabyUserSettings.cpp b/src/IRbabyUserSettings.cpp index 12bad2a..59299e2 100644 --- a/src/IRbabyUserSettings.cpp +++ b/src/IRbabyUserSettings.cpp @@ -9,30 +9,31 @@ StaticJsonDocument<1024> ConfigData; StaticJsonDocument<1024> ACStatus; -void loadPin() { - if (ConfigData.containsKey("send_pin")) { - int pin = ConfigData["send_pin"]; - if (pin > 0) { - if (ir_send != NULL) - delete ir_send; - ir_send = new IRsend(pin); - ir_send->begin(); - INFOF("Init gpio %d as IR send pin\n", pin); - } - } - if (ConfigData.containsKey("receive_pin")) { - int pin = ConfigData["receive_pin"]; - if (pin > 0) { - if (ir_recv != NULL) - delete ir_recv; - const uint8_t kTimeout = 50; - const uint16_t kCaptureBufferSize = 1024; - ir_recv = new IRrecv(pin, kCaptureBufferSize, kTimeout, true); - ir_recv->enableIRIn(); - INFOF("Init gpio %d as IR receive pin\n", pin); - } - } -} +// void loadPin() +// { +// if (ConfigData.containsKey("send_pin")) { +// int pin = ConfigData["send_pin"]; +// if (pin > 0) { +// if (ir_send != NULL) +// delete ir_send; +// ir_send = new IRsend(pin); +// ir_send->begin(); +// INFOF("Init gpio %d as IR send pin\n", pin); +// } +// } +// if (ConfigData.containsKey("receive_pin")) { +// int pin = ConfigData["receive_pin"]; +// if (pin > 0) { +// if (ir_recv != NULL) +// delete ir_recv; +// const uint8_t kTimeout = 50; +// const uint16_t kCaptureBufferSize = 1024; +// ir_recv = new IRrecv(pin, kCaptureBufferSize, kTimeout, true); +// ir_recv->enableIRIn(); +// INFOF("Init gpio %d as IR receive pin\n", pin); +// } +// } +// } bool settingsSave() { @@ -51,9 +52,6 @@ bool settingsSave() return false; } cache.close(); - loadPin(); - mqttDisconnect(); - mqttReconnect(); return true; } @@ -62,15 +60,19 @@ bool settingsLoad() LittleFS.begin(); int ret = false; - if (LittleFS.exists("/config")) { + if (LittleFS.exists("/config")) + { File cache = LittleFS.open("/config", "r"); - if (!cache) { + if (!cache) + { ERRORLN("Failed to read config file"); return ret; } - if (cache.size() > 0) { + if (cache.size() > 0) + { DeserializationError error = deserializeJson(ConfigData, cache); - if (error) { + if (error) + { ERRORLN("Failed to load config settings"); return ret; } @@ -80,24 +82,22 @@ bool settingsLoad() Serial.println(); } cache.close(); - loadPin(); - } - // if (LittleFS.exists("/acstatus")) { - // File cache = LittleFS.open("/acstatus", "r"); - // if (!cache) { - // ERRORLN("Failed to read acstatus file"); - // return ret; - // } - // if (cache.size() > 0) { - // DeserializationError error = deserializeJson(ACStatus, cache); - // if (error) { - // ERRORLN("Failed to load acstatus settings"); - // return ret; - // } - // } - // } + if (LittleFS.exists("/acstatus")) { + File cache = LittleFS.open("/acstatus", "r"); + if (!cache) { + ERRORLN("Failed to read acstatus file"); + return ret; + } + if (cache.size() > 0) { + DeserializationError error = deserializeJson(ACStatus, cache); + if (error) { + ERRORLN("Failed to load acstatus settings"); + return ret; + } + } + } ret = true; return ret; } @@ -108,4 +108,35 @@ void settingsClear() wifi_manager.resetSettings(); LittleFS.format(); ESP.reset(); +} + +bool saveACStatus(String file, t_remote_ac_status status) +{ + bool ret = false; + ACStatus[file]["power"] = status.ac_power; + ACStatus[file]["temperature"] = status.ac_temp; + ACStatus[file]["mode"] = status.ac_mode; + ACStatus[file]["swing"] = status.ac_swing; + ACStatus[file]["speed"] = status.ac_wind_speed; + File cache = LittleFS.open("/acstatus", "w"); + if (cache && (serializeJson(ACStatus, cache) == 0)) + ret = true; + cache.close(); + return ret; +} + +t_remote_ac_status getACState(String file) +{ + t_remote_ac_status status; + int power = ACStatus[file]["power"]; + int temperature = ACStatus[file]["temperature"]; + int mode = ACStatus[file]["mode"]; + int swing = ACStatus[file]["swing"]; + int wind_speed = ACStatus[file]["speed"]; + status.ac_power = (t_ac_power)power; + status.ac_temp = (t_ac_temperature)temperature; + status.ac_mode = (t_ac_mode)mode; + status.ac_swing = (t_ac_swing)swing; + status.ac_wind_speed = (t_ac_wind_speed)wind_speed; + return status; } \ No newline at end of file diff --git a/src/IRbabyUserSettings.h b/src/IRbabyUserSettings.h index c751ffc..cc9cd98 100644 --- a/src/IRbabyUserSettings.h +++ b/src/IRbabyUserSettings.h @@ -1,6 +1,7 @@ #ifndef IRBABY_USER_SETTINGS_H #define IRBABY_USER_SETTINGS_H #include +#include "../lib/Irext/include/ir_ac_control.h" /* 保存配置信息 */ bool settingsSave(); @@ -10,6 +11,9 @@ void settingsClear(); /* 加载配置信息 */ bool settingsLoad(); +bool saveACStatus(String, t_remote_ac_status); +t_remote_ac_status getACState(String file); + extern StaticJsonDocument<1024> ConfigData; extern StaticJsonDocument<1024> ACStatus; #endif // IRBABY_USER_SETTINGS_H \ No newline at end of file diff --git a/src/defines.h b/src/defines.h index b879c08..eba2acc 100644 --- a/src/defines.h +++ b/src/defines.h @@ -1,3 +1,7 @@ +// IRbaby - github.com/Caffreyfans/IRbaby +// Copyright Miao Yi 2019-2020 +// MIT License + #ifndef _DEFINES_H #define _DEFINES_H @@ -7,11 +11,8 @@ typedef unsigned short uint16_t; typedef short int16_t; typedef unsigned char uint8_t; -#define FIRMWARE_VERSION "v0.4" // version name -#define VERSION_CODE 4 // version code - -/* reset settings */ -#define RESET_PIN D7 // reset pin +#define FIRMWARE_VERSION "v0.5" // version name +#define VERSION_CODE 5 // version code /* log settings */ #define BAUD_RATE 115200 @@ -25,7 +26,34 @@ typedef unsigned char uint8_t; #define LOG_ERROR true #endif +/* ----------------- user settings ----------------- */ /* mqtt settings */ -#define MQTT_CHECK_INTERVALS 5 // seconds +#define MQTT_CHECK_INTERVALS 10 // seconds #define MQTT_CONNECT_WAIT_TIME 20000 // MQTT 连接等待时间 -#endif \ No newline at end of file + +/* receive disable */ +#define DISABLE_SIGNAL_INTERVALS 600 // seconds + +// uncomment below to enable RF +// #define USE_RF + +// uncomment below to enable upload board info to remote server +// #define USE_INFO_UPLOAD + +/* ----------------- default pin setting --------------- */ +/* reset pin */ +#define RESET_PIN 2 + +/* 315 RF pin */ +#define T_315 5 +#define R_315 4 + +/* 433 RF pin */ +#define T_433 14 +#define R_433 12 + +/* IR pin */ +#define T_IR 14 +#define R_IR 12 + +#endif // _DEFINES_H