diff --git a/.github/Contributors.md b/.github/Contributors.md index 380d9893e..934db63f0 100644 --- a/.github/Contributors.md +++ b/.github/Contributors.md @@ -15,6 +15,7 @@ - [Fabien Valthier](https://github.com/hcoohb) - [Ajay Pala](https://github.com/ajaypala/) - [Motea Marius](https://github.com/mariusmotea) +- [Mark Kuchel](https://github.com/kuchel77) All contributors can be found on the [contributors site](https://github.com/crankyoldgit/IRremoteESP8266/graphs/contributors). diff --git a/src/IRac.cpp b/src/IRac.cpp index f5824cb2f..5d477a8be 100644 --- a/src/IRac.cpp +++ b/src/IRac.cpp @@ -148,6 +148,9 @@ bool IRac::isProtocolSupported(const decode_type_t protocol) { #if SEND_MITSUBISHI_AC case decode_type_t::MITSUBISHI_AC: #endif +#if SEND_MITSUBISHI112 + case decode_type_t::MITSUBISHI112: +#endif #if SEND_MITSUBISHI136 case decode_type_t::MITSUBISHI136: #endif @@ -704,6 +707,34 @@ void IRac::mitsubishi(IRMitsubishiAC *ac, } #endif // SEND_MITSUBISHI_AC +#if SEND_MITSUBISHI112 +void IRac::mitsubishi112(IRMitsubishi112 *ac, + const bool on, const stdAc::opmode_t mode, + const float degrees, const stdAc::fanspeed_t fan, + const stdAc::swingv_t swingv, + const stdAc::swingh_t swingh, + const bool quiet) { + ac->begin(); + ac->setPower(on); + ac->setMode(ac->convertMode(mode)); + ac->setTemp(degrees); + ac->setFan(ac->convertFan(fan)); + ac->setSwingV(ac->convertSwingV(swingv)); + ac->setSwingH(ac->convertSwingH(swingh)); + ac->setQuiet(quiet); + // ac->setEcono(econo); + // FIXME - Econo + // No Turbo setting available. + // No Light setting available. + // No Filter setting available. + // No Clean setting available. + // No Beep setting available. + // No Sleep setting available. + // No Clock setting available. + ac->send(); +} +#endif // SEND_MITSUBISHI112 + #if SEND_MITSUBISHI136 void IRac::mitsubishi136(IRMitsubishi136 *ac, const bool on, const stdAc::opmode_t mode, @@ -1336,6 +1367,15 @@ bool IRac::sendAc(const stdAc::state_t desired, const stdAc::state_t *prev) { break; } #endif // SEND_MITSUBISHI_AC +#if SEND_MITSUBISHI112 + case MITSUBISHI112: + { + IRMitsubishi112 ac(_pin, _inverted, _modulation); + mitsubishi112(&ac, on, send.mode, degC, send.fanspeed, send.swingv, + send.swingh, send.quiet); + break; + } +#endif // SEND_MITSUBISHI112 #if SEND_MITSUBISHI136 case MITSUBISHI136: { @@ -1813,6 +1853,13 @@ namespace IRAcUtils { return ac.toString(); } #endif // DECODE_MITSUBISHI_AC +#if DECODE_MITSUBISHI112 + case decode_type_t::MITSUBISHI112: { + IRMitsubishi112 ac(0); + ac.setRaw(result->state); + return ac.toString(); + } +#endif // DECODE_MITSUBISHI112 #if DECODE_MITSUBISHI136 case decode_type_t::MITSUBISHI136: { IRMitsubishi136 ac(0); @@ -2116,6 +2163,14 @@ namespace IRAcUtils { break; } #endif // DECODE_MITSUBISHI_AC +#if DECODE_MITSUBISHI112 + case decode_type_t::MITSUBISHI112: { + IRMitsubishi112 ac(kGpioUnused); + ac.setRaw(decode->state); + *result = ac.toCommon(); + break; + } +#endif // DECODE_MITSUBISHI112 #if DECODE_MITSUBISHI136 case decode_type_t::MITSUBISHI136: { IRMitsubishi136 ac(kGpioUnused); diff --git a/src/IRac.h b/src/IRac.h index dabbc5e64..39722d306 100644 --- a/src/IRac.h +++ b/src/IRac.h @@ -231,6 +231,14 @@ void electra(IRElectraAc *ac, const stdAc::swingh_t swingh, const bool quiet, const int16_t clock = -1); #endif // SEND_MITSUBISHI_AC +#if SEND_MITSUBISHI112 + void mitsubishi112(IRMitsubishi112 *ac, + const bool on, const stdAc::opmode_t mode, + const float degrees, const stdAc::fanspeed_t fan, + const stdAc::swingv_t swingv, + const stdAc::swingh_t swingh, + const bool quiet); +#endif // SEND_MITSUBISHI112 #if SEND_MITSUBISHI136 void mitsubishi136(IRMitsubishi136 *ac, const bool on, const stdAc::opmode_t mode, diff --git a/src/IRrecv.cpp b/src/IRrecv.cpp index 39451c6ac..65b7873ab 100644 --- a/src/IRrecv.cpp +++ b/src/IRrecv.cpp @@ -607,10 +607,11 @@ bool IRrecv::decode(decode_results *results, irparams_t *save) { DPRINTLN("Attempting Vestel AC decode"); if (decodeVestelAc(results)) return true; #endif -#if DECODE_TCL112AC - DPRINTLN("Attempting TCL112AC decode"); - if (decodeTcl112Ac(results)) return true; -#endif +#if DECODE_MITSUBISHI112 || DECODE_TCL112AC + // Mitsubish112 and Tcl112 share the same decoder. + DPRINTLN("Attempting Mitsubishi112/TCL112AC decode"); + if (decodeMitsubishi112(results)) return true; +#endif // DECODE_MITSUBISHI112 || DECODE_TCL112AC #if DECODE_TECO DPRINTLN("Attempting Teco decode"); if (decodeTeco(results)) return true; diff --git a/src/IRrecv.h b/src/IRrecv.h index a543aabfa..671392829 100644 --- a/src/IRrecv.h +++ b/src/IRrecv.h @@ -259,6 +259,11 @@ class IRrecv { const uint16_t nbits = kMitsubishi136Bits, const bool strict = true); #endif +#if DECODE_MITSUBISHI112 + bool decodeMitsubishi112(decode_results *results, + const uint16_t nbits = kMitsubishi112Bits, + const bool strict = true); +#endif #if DECODE_MITSUBISHIHEAVY bool decodeMitsubishiHeavy(decode_results *results, const uint16_t nbits, const bool strict = true); @@ -477,11 +482,6 @@ class IRrecv { const uint16_t nbits = kVestelAcBits, const bool strict = true); #endif -#if DECODE_TCL112AC - bool decodeTcl112Ac(decode_results *results, - const uint16_t nbits = kTcl112AcBits, - const bool strict = true); -#endif #if DECODE_TECO bool decodeTeco(decode_results *results, const uint16_t nbits = kTecoBits, const bool strict = false); diff --git a/src/IRremoteESP8266.h b/src/IRremoteESP8266.h index ac73d796b..6fd59bc4f 100644 --- a/src/IRremoteESP8266.h +++ b/src/IRremoteESP8266.h @@ -36,6 +36,7 @@ * Carrier & Haier AC code by crankyoldgit * Vestel AC code by Erdem U. Altınyurt * Teco AC code by Fabien Valthier (hcoohb) + * Mitsubishi 112 AC Code by kuchel77 * * GPL license, all text above must be included in any redistribution ****************************************************/ @@ -133,6 +134,9 @@ #define DECODE_MITSUBISHI136 true #define SEND_MITSUBISHI136 true +#define DECODE_MITSUBISHI112 true +#define SEND_MITSUBISHI112 true + #define DECODE_FUJITSU_AC true #define SEND_FUJITSU_AC true @@ -265,7 +269,8 @@ DECODE_VESTEL_AC || DECODE_TCL112AC || DECODE_MITSUBISHIHEAVY || \ DECODE_DAIKIN216 || DECODE_SHARP_AC || DECODE_DAIKIN160 || \ DECODE_NEOCLIMA || DECODE_DAIKIN176 || DECODE_DAIKIN128 || \ - DECODE_AMCOR || DECODE_DAIKIN152 || DECODE_MITSUBISHI136) + DECODE_AMCOR || DECODE_DAIKIN152 || DECODE_MITSUBISHI136 || \ + DECODE_MITSUBISHI112) #define DECODE_AC true // We need some common infrastructure for decoding A/Cs. #else #define DECODE_AC false // We don't need that infrastructure. @@ -356,8 +361,9 @@ enum decode_type_t { AMCOR, DAIKIN152, // 70 MITSUBISHI136, + MITSUBISHI112, // Add new entries before this one, and update it to point to the last entry. - kLastDecodeType = MITSUBISHI136, + kLastDecodeType = MITSUBISHI112, }; // Message lengths & required repeat values @@ -458,6 +464,9 @@ const uint16_t kMitsubishiACMinRepeat = kSingleRepeat; const uint16_t kMitsubishi136StateLength = 17; const uint16_t kMitsubishi136Bits = kMitsubishi136StateLength * 8; const uint16_t kMitsubishi136MinRepeat = kNoRepeat; +const uint16_t kMitsubishi112StateLength = 14; +const uint16_t kMitsubishi112Bits = kMitsubishi112StateLength * 8; +const uint16_t kMitsubishi112MinRepeat = kNoRepeat; const uint16_t kMitsubishiHeavy88StateLength = 11; const uint16_t kMitsubishiHeavy88Bits = kMitsubishiHeavy88StateLength * 8; const uint16_t kMitsubishiHeavy88MinRepeat = kNoRepeat; diff --git a/src/IRsend.cpp b/src/IRsend.cpp index 664156d34..c9a097750 100644 --- a/src/IRsend.cpp +++ b/src/IRsend.cpp @@ -614,6 +614,8 @@ uint16_t IRsend::defaultBits(const decode_type_t protocol) { return kMitsubishiACBits; case MITSUBISHI136: return kMitsubishi136Bits; + case MITSUBISHI112: + return kMitsubishi112Bits; case MITSUBISHI_HEAVY_152: return kMitsubishiHeavy152Bits; case MITSUBISHI_HEAVY_88: @@ -947,6 +949,11 @@ bool IRsend::send(const decode_type_t type, const unsigned char *state, sendMitsubishi136(state, nbytes); break; #endif // SEND_MITSUBISHI136 +#if SEND_MITSUBISHI112 + case MITSUBISHI112: + sendMitsubishi112(state, nbytes); + break; +#endif // SEND_MITSUBISHI112 #if SEND_MITSUBISHIHEAVY case MITSUBISHI_HEAVY_88: sendMitsubishiHeavy88(state, nbytes); diff --git a/src/IRsend.h b/src/IRsend.h index 7743ca967..043823391 100644 --- a/src/IRsend.h +++ b/src/IRsend.h @@ -280,6 +280,11 @@ class IRsend { const uint16_t nbytes = kMitsubishi136StateLength, const uint16_t repeat = kMitsubishi136MinRepeat); #endif +#if SEND_MITSUBISHI112 + void sendMitsubishi112(const unsigned char data[], + const uint16_t nbytes = kMitsubishi112StateLength, + const uint16_t repeat = kMitsubishi112MinRepeat); +#endif #if SEND_MITSUBISHI2 void sendMitsubishi2(uint64_t data, uint16_t nbits = kMitsubishiBits, uint16_t repeat = kMitsubishiMinRepeat); diff --git a/src/IRutils.cpp b/src/IRutils.cpp index 28c9af645..f50927df0 100644 --- a/src/IRutils.cpp +++ b/src/IRutils.cpp @@ -171,6 +171,8 @@ decode_type_t strToDecodeType(const char * const str) { return decode_type_t::MITSUBISHI_AC; else if (!strcasecmp(str, "MITSUBISHI136")) return decode_type_t::MITSUBISHI136; + else if (!strcasecmp(str, "MITSUBISHI112")) + return decode_type_t::MITSUBISHI112; else if (!strcasecmp(str, "MITSUBISHI_HEAVY_88")) return decode_type_t::MITSUBISHI_HEAVY_88; else if (!strcasecmp(str, "MITSUBISHI_HEAVY_152")) @@ -374,6 +376,9 @@ String typeToString(const decode_type_t protocol, const bool isRepeat) { case MITSUBISHI136: result = F("MITSUBISHI136"); break; + case MITSUBISHI112: + result = F("MITSUBISHI112"); + break; case MITSUBISHI_HEAVY_88: result = F("MITSUBISHI_HEAVY_88"); break; @@ -501,6 +506,7 @@ bool hasACState(const decode_type_t protocol) { case HITACHI_AC2: case KELVINATOR: case MITSUBISHI136: + case MITSUBISHI112: case MITSUBISHI_AC: case MITSUBISHI_HEAVY_88: case MITSUBISHI_HEAVY_152: diff --git a/src/ir_Mitsubishi.cpp b/src/ir_Mitsubishi.cpp index d23a04522..a8daf18c9 100644 --- a/src/ir_Mitsubishi.cpp +++ b/src/ir_Mitsubishi.cpp @@ -1,5 +1,6 @@ // Copyright 2009 Ken Shirriff // Copyright 2017-2019 David Conran +// Copyright 2019 Mark Kuchel // Copyright 2018 Denes Varga // Mitsubishi @@ -12,6 +13,7 @@ #include "IRrecv.h" #include "IRsend.h" #include "IRutils.h" +#include "ir_Tcl.h" // Mitsubishi (TV) decoding added from https://github.com/z3t0/Arduino-IRremote // Mitsubishi (TV) sending & Mitsubishi A/C support added by David Conran @@ -71,6 +73,20 @@ const uint16_t kMitsubishi136OneSpace = 1137; const uint16_t kMitsubishi136ZeroSpace = 351; const uint32_t kMitsubishi136Gap = kDefaultMessageGap; +// Mitsubishi 112 bit A/C +// Ref: +// https://github.com/kuchel77 + +const uint16_t kMitsubishi112HdrMark = 3450; +const uint16_t kMitsubishi112HdrSpace = 1696; +const uint16_t kMitsubishi112BitMark = 450; +const uint16_t kMitsubishi112OneSpace = 1250; +const uint16_t kMitsubishi112ZeroSpace = 385; +const uint32_t kMitsubishi112Gap = kDefaultMessageGap; +// Total tolerance percentage to use for matching the header mark. +const uint8_t kMitsubishi112HdrMarkTolerance = 5; + + using irutils::addBoolToString; using irutils::addFanToString; using irutils::addIntToString; @@ -1186,3 +1202,471 @@ String IRMitsubishi136::toString(void) { result += addBoolToString(getQuiet(), F("Quiet")); return result; } + + +#if SEND_MITSUBISHI112 +// Send a Mitsubishi112 A/C message. +// +// Args: +// data: An array of bytes containing the IR command. +// nbytes: Nr. of bytes of data in the array. (>=kMitsubishi112StateLength) +// repeat: Nr. of times the message is to be repeated. +// (Default = kMitsubishi112MinRepeat). +// +// Status: Stable / Reported as working. +// +// Ref: +// https://github.com/crankyoldgit/IRremoteESP8266/issues/947 +void IRsend::sendMitsubishi112(const unsigned char data[], + const uint16_t nbytes, + const uint16_t repeat) { + if (nbytes < kMitsubishi112StateLength) + return; // Not enough bytes to send a proper message. + + sendGeneric(kMitsubishi112HdrMark, kMitsubishi112HdrSpace, + kMitsubishi112BitMark, kMitsubishi112OneSpace, + kMitsubishi112BitMark, kMitsubishi112ZeroSpace, + kMitsubishi112BitMark, kMitsubishi112Gap, + data, nbytes, 38, false, repeat, 50); +} +#endif // SEND_MITSUBISHI112 + +#if DECODE_MITSUBISHI112 || DECODE_TCL112AC +// Decode the supplied Mitsubishi112 / Tcl112Ac 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: STABLE / Reported as working. +// +// Note: Mitsubishi112 & Tcl112Ac are basically the same protocol. +// The only significant difference I can see is Mitsubishi112 has a +// slightly longer header mark. We will use that to determine which +// varient it should be. The other differences require full decoding and +// only only with certain settings. +// There are some other timing differences too, but the tolerances will +// overlap. +// Ref: +// https://github.com/crankyoldgit/IRremoteESP8266/issues/619 +// https://github.com/crankyoldgit/IRremoteESP8266/issues/947 +bool IRrecv::decodeMitsubishi112(decode_results *results, const uint16_t nbits, + const bool strict) { + if (results->rawlen < ((2 * nbits) + kHeader + kFooter - 1)) return false; + if (nbits % 8 != 0) return false; // Not a multiple of an 8 bit byte. + if (strict) { // Do checks to see if it matches the spec. + if (nbits != kMitsubishi112Bits && nbits != kTcl112AcBits) return false; + } + uint16_t offset = kStartOffset; + decode_type_t typeguess = decode_type_t::UNKNOWN; + uint16_t hdrspace; + uint16_t bitmark; + uint16_t onespace; + uint16_t zerospace; + uint32_t gap; + uint8_t tolerance = _tolerance; + + // Header +#if DECODE_MITSUBISHI112 + if (matchMark(results->rawbuf[offset], kMitsubishi112HdrMark, + kMitsubishi112HdrMarkTolerance, 0)) { + typeguess = decode_type_t::MITSUBISHI112; + hdrspace = kMitsubishi112HdrSpace; + bitmark = kMitsubishi112BitMark; + onespace = kMitsubishi112OneSpace; + zerospace = kMitsubishi112ZeroSpace; + gap = kMitsubishi112Gap; + } +#endif // DECODE_MITSUBISHI112 +#if DECODE_TCL112AC + if (typeguess == decode_type_t::UNKNOWN && // We didn't match Mitsubishi112 + matchMark(results->rawbuf[offset], kTcl112AcHdrMark, + kTcl112AcHdrMarkTolerance, 0)) { + typeguess = decode_type_t::TCL112AC; + hdrspace = kTcl112AcHdrSpace; + bitmark = kTcl112AcBitMark; + onespace = kTcl112AcOneSpace; + zerospace = kTcl112AcZeroSpace; + gap = kTcl112AcGap; + tolerance += kTcl112AcTolerance; + } +#endif // DECODE_TCL112AC + if (typeguess == decode_type_t::UNKNOWN) return false; // No header matched. + offset++; + + uint16_t used = matchGeneric(results->rawbuf + offset, results->state, + results->rawlen - offset, nbits, + 0, // Skip the header as we matched it earlier. + hdrspace, bitmark, onespace, bitmark, zerospace, + bitmark, gap, + true, tolerance, 0, false); + if (!used) return false; + if (strict) { + // Header validation: Codes start with 0x23CB26 + if (results->state[0] != 0x23 || results->state[1] != 0xCB || + results->state[2] != 0x26) return false; + // TCL112 and MITSUBISHI112 share the exact same checksum. + if (!IRTcl112Ac::validChecksum(results->state, nbits / 8)) return false; + } + // Success + results->decode_type = typeguess; + results->bits = nbits; + // 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_MITSUBISHI112 || DECODE_TCL112AC + +// Code to emulate Mitsubishi 112bit A/C IR remote control unit. +// +// Equipment it seems compatible with: +// Brand: Mitsubishi Electric, Model: MSH-A24WV / MUH-A24WV A/C +// Brand: Mitsubishi Electric, Model: KPOA remote + +// Initialise the object. +IRMitsubishi112::IRMitsubishi112(const uint16_t pin, const bool inverted, + const bool use_modulation) + : _irsend(pin, inverted, use_modulation) { this->stateReset(); } + +// Reset the state of the remote to a known good state/sequence. +void IRMitsubishi112::stateReset(void) { + // The state of the IR remote in IR code form. + remote_state[0] = 0x23; + remote_state[1] = 0xCB; + remote_state[2] = 0x26; + remote_state[3] = 0x01; + remote_state[4] = 0x00; + remote_state[5] = 0x24; + remote_state[6] = 0x03; + remote_state[7] = 0x0B; + remote_state[8] = 0x10; + remote_state[9] = 0x00; + remote_state[10] = 0x00; + remote_state[11] = 0x00; + remote_state[12] = 0x30; +} + +// Calculate the checksum for the current internal state of the remote. +void IRMitsubishi112::checksum(void) { + remote_state[kMitsubishi112StateLength - 1] = IRTcl112Ac::calcChecksum( + remote_state, kMitsubishi112StateLength); +} + +// Configure the pin for output. +void IRMitsubishi112::begin(void) { _irsend.begin(); } + +#if SEND_MITSUBISHI112 +// Send the current desired state to the IR LED. +void IRMitsubishi112::send(const uint16_t repeat) { + checksum(); // Ensure correct checksum before sending. + _irsend.sendMitsubishi112(remote_state, kMitsubishi112StateLength, repeat); +} +#endif // SEND_MITSUBISHI112 + +// Return a pointer to the internal state date of the remote. +uint8_t *IRMitsubishi112::getRaw(void) { + checksum(); + return remote_state; +} + +void IRMitsubishi112::setRaw(const uint8_t *data) { + for (uint8_t i = 0; i < (kMitsubishi112StateLength - 1); i++) { + remote_state[i] = data[i]; + } +} + +// Set the requested power state of the A/C to off. +void IRMitsubishi112::on(void) { setPower(true); } + +// Set the requested power state of the A/C to off. +void IRMitsubishi112::off(void) { setPower(false); } + +// Set the requested power state of the A/C. +void IRMitsubishi112::setPower(bool on) { + if (on) + remote_state[kMitsubishi112PowerByte] |= kMitsubishi112PowerBit; + else + remote_state[kMitsubishi112PowerByte] &= ~kMitsubishi112PowerBit; +} + +// Return the requested power state of the A/C. +bool IRMitsubishi112::getPower(void) { + return remote_state[kMitsubishi112PowerByte] & kMitsubishi112PowerBit; +} + +// Set the temp. in deg C +void IRMitsubishi112::setTemp(const uint8_t degrees) { + uint8_t temp = std::max((uint8_t)kMitsubishi112MinTemp, degrees); + temp = std::min((uint8_t)kMitsubishi112MaxTemp, temp); + remote_state[kMitsubishi112TempByte] = kMitsubishiAcMaxTemp - temp; +} + +// Return the set temp. in deg C +uint8_t IRMitsubishi112::getTemp(void) { + return (kMitsubishiAcMaxTemp -remote_state[kMitsubishi112TempByte]); +} + +void IRMitsubishi112::setFan(const uint8_t speed) { + switch (speed) { + case kMitsubishi112FanMin: + case kMitsubishi112FanLow: + case kMitsubishi112FanMed: + case kMitsubishi112FanMax: + remote_state[kMitsubishi112FanByte] &= kMitsubishi112FanMask; + remote_state[kMitsubishi112FanByte] |= speed; + break; + default: + setFan(kMitsubishi112FanMax); + } +} + +// Return the requested state of the unit's fan. +uint8_t IRMitsubishi112::getFan(void) { + return (remote_state[kMitsubishi112FanByte] & kMitsubishi112FanGetMask); +} + +// Return the requested climate operation mode of the a/c unit. +uint8_t IRMitsubishi112::getMode(void) { + return remote_state[kMitsubishi112ModeByte] & kMitsubishi112ModeMask; +} + +// Set the requested climate operation mode of the a/c unit. +void IRMitsubishi112::setMode(const uint8_t mode) { + // If we get an unexpected mode, default to AUTO. + switch (mode) { + case kMitsubishi112Cool: + case kMitsubishi112Heat: + case kMitsubishi112Auto: + case kMitsubishi112Dry: + remote_state[kMitsubishi112ModeByte] &= ~kMitsubishi112ModeMask; + remote_state[kMitsubishi112ModeByte] |= mode; + break; + default: + setMode(kMitsubishi112Auto); + } +} + +// Set the requested vane operation mode of the a/c unit. +void IRMitsubishi112::setSwingV(const uint8_t position) { + // If we get an unexpected mode, default to auto. + switch (position) { + case kMitsubishi112SwingVLowest: + case kMitsubishi112SwingVLow: + case kMitsubishi112SwingVMiddle: + case kMitsubishi112SwingVHigh: + case kMitsubishi112SwingVHighest: + case kMitsubishi112SwingVAuto: + remote_state[kMitsubishi112SwingVByte] &= ~kMitsubishi112SwingVMask; + remote_state[kMitsubishi112SwingVByte] |= position; + break; + default: + setMode(kMitsubishi112SwingVAuto); + } +} + +// Return the requested vane operation mode of the a/c unit. +uint8_t IRMitsubishi112::getSwingV(void) { + return remote_state[kMitsubishi112SwingVByte] & kMitsubishi112SwingVMask; +} + +// Set the requested vane operation mode of the a/c unit. +void IRMitsubishi112::setSwingH(const uint8_t position) { + // If we get an unexpected mode, default to auto. + switch (position) { + case kMitsubishi112SwingHLeftMax: + case kMitsubishi112SwingHLeft: + case kMitsubishi112SwingHMiddle: + case kMitsubishi112SwingHRight: + case kMitsubishi112SwingHRightMax: + case kMitsubishi112SwingHWide: + case kMitsubishi112SwingHAuto: + remote_state[kMitsubishi112SwingHByte] &= ~kMitsubishi112SwingHMask; + remote_state[kMitsubishi112SwingHByte] |= position; + break; + default: + remote_state[kMitsubishi112SwingHByte] &= ~kMitsubishi112SwingHMask; + remote_state[kMitsubishi112SwingHByte] |= kMitsubishi112SwingHAuto; + break; + } +} + +// Return the requested vane operation mode of the a/c unit. +uint8_t IRMitsubishi112::getSwingH(void) { + return (remote_state[kMitsubishi112SwingHByte] & kMitsubishi112SwingHMask); +} + +// Emulate a quiet setting. There is no true quiet setting on this a/c +void IRMitsubishi112::setQuiet(bool on) { + if (on) + setFan(kMitsubishi112FanQuiet); + else if (getQuiet()) setFan(kMitsubishi112FanLow); +} + +// Return the requested power state of the A/C. +bool IRMitsubishi112::getQuiet(void) { + return getFan() == kMitsubishi112FanQuiet; +} + + +// Convert a standard A/C mode into its native mode. +uint8_t IRMitsubishi112::convertMode(const stdAc::opmode_t mode) { + switch (mode) { + case stdAc::opmode_t::kCool: return kMitsubishi112Cool; + case stdAc::opmode_t::kHeat: return kMitsubishi112Heat; + case stdAc::opmode_t::kDry: return kMitsubishi112Dry; + default: return kMitsubishi112Auto; + } +} + +// Convert a standard A/C Fan speed into its native fan speed. +uint8_t IRMitsubishi112::convertFan(const stdAc::fanspeed_t speed) { + switch (speed) { + case stdAc::fanspeed_t::kMin: return kMitsubishi112FanMin; + case stdAc::fanspeed_t::kLow: return kMitsubishi112FanLow; + case stdAc::fanspeed_t::kMedium: return kMitsubishi112FanMed; + case stdAc::fanspeed_t::kHigh: + case stdAc::fanspeed_t::kMax: return kMitsubishi112FanMax; + default: return kMitsubishi112FanMed; + } +} + +// Convert a standard A/C vertical swing into its native setting. +uint8_t IRMitsubishi112::convertSwingV(const stdAc::swingv_t position) { + switch (position) { + case stdAc::swingv_t::kHighest: return kMitsubishi112SwingVHighest; + case stdAc::swingv_t::kHigh: return kMitsubishi112SwingVHigh; + case stdAc::swingv_t::kMiddle: return kMitsubishi112SwingVMiddle; + case stdAc::swingv_t::kLow: return kMitsubishi112SwingVLow; + case stdAc::swingv_t::kLowest: return kMitsubishi112SwingVLowest; + default: return kMitsubishi112SwingVAuto; + } +} + +// Convert a standard A/C vertical swing into its native setting. +uint8_t IRMitsubishi112::convertSwingH(const stdAc::swingh_t position) { + switch (position) { + case stdAc::swingh_t::kLeftMax: return kMitsubishi112SwingHLeftMax; + case stdAc::swingh_t::kLeft: return kMitsubishi112SwingHLeft; + case stdAc::swingh_t::kMiddle: return kMitsubishi112SwingHMiddle; + case stdAc::swingh_t::kRight: return kMitsubishi112SwingHRight; + case stdAc::swingh_t::kRightMax: return kMitsubishi112SwingHRightMax; + case stdAc::swingh_t::kWide: return kMitsubishi112SwingHWide; + case stdAc::swingh_t::kAuto: return kMitsubishi112SwingHAuto; + default: return kMitsubishi112SwingHAuto; + } +} + +// Convert a native mode to it's common equivalent. +stdAc::opmode_t IRMitsubishi112::toCommonMode(const uint8_t mode) { + switch (mode) { + case kMitsubishi112Cool: return stdAc::opmode_t::kCool; + case kMitsubishi112Heat: return stdAc::opmode_t::kHeat; + case kMitsubishi112Dry: return stdAc::opmode_t::kDry; + default: return stdAc::opmode_t::kAuto; + } +} + +// Convert a native fan speed to it's common equivalent. +stdAc::fanspeed_t IRMitsubishi112::toCommonFanSpeed(const uint8_t speed) { + switch (speed) { + case kMitsubishi112FanMax: return stdAc::fanspeed_t::kMax; + case kMitsubishi112FanMed: return stdAc::fanspeed_t::kMedium; + case kMitsubishi112FanLow: return stdAc::fanspeed_t::kLow; + case kMitsubishi112FanMin: return stdAc::fanspeed_t::kMin; + default: return stdAc::fanspeed_t::kMedium; + } +} + +// Convert a native vertical swing to it's common equivalent. +stdAc::swingv_t IRMitsubishi112::toCommonSwingV(const uint8_t pos) { + switch (pos) { + case kMitsubishi112SwingVHighest: return stdAc::swingv_t::kHighest; + case kMitsubishi112SwingVHigh: return stdAc::swingv_t::kHigh; + case kMitsubishi112SwingVMiddle: return stdAc::swingv_t::kMiddle; + case kMitsubishi112SwingVLow: return stdAc::swingv_t::kLow; + case kMitsubishi112SwingVLowest: return stdAc::swingv_t::kLowest; + default: return stdAc::swingv_t::kAuto; + } +} + +// Convert a native vertical swing to it's common equivalent. +stdAc::swingh_t IRMitsubishi112::toCommonSwingH(const uint8_t pos) { + switch (pos) { + case kMitsubishi112SwingHLeftMax: return stdAc::swingh_t::kLeftMax; + case kMitsubishi112SwingHLeft: return stdAc::swingh_t::kLeft; + case kMitsubishi112SwingHMiddle: return stdAc::swingh_t::kMiddle; + case kMitsubishi112SwingHRight: return stdAc::swingh_t::kRight; + case kMitsubishi112SwingHRightMax: return stdAc::swingh_t::kRightMax; + case kMitsubishi112SwingHWide: return stdAc::swingh_t::kWide; + case kMitsubishi112SwingHAuto: return stdAc::swingh_t::kAuto; + default: return stdAc::swingh_t::kAuto; + } +} + + +// Convert the A/C state to it's common equivalent. +stdAc::state_t IRMitsubishi112::toCommon(void) { + stdAc::state_t result; + result.protocol = decode_type_t::MITSUBISHI112; + result.model = -1; // No models used. + result.power = this->getPower(); + result.mode = this->toCommonMode(this->getMode()); + result.celsius = true; + result.degrees = this->getTemp(); + result.fanspeed = this->toCommonFanSpeed(this->getFan()); + result.swingv = this->toCommonSwingV(this->getSwingV()); + result.quiet = this->getQuiet(); + result.swingh = this->toCommonSwingH(this->getSwingH());; + // Not supported. + result.econo = false; // Need to figure this part from stdAc + result.clock = -1; + result.sleep = -1; + result.turbo = false; + result.clean = false; + result.filter = false; + result.light = false; + result.beep = false; + + + return result; +} + +// Convert the internal state into a human readable string. +String IRMitsubishi112::toString(void) { + String result = ""; + result.reserve(80); // Reserve some heap for the string to reduce fragging. + result += addBoolToString(getPower(), F("Power"), false); + result += addModeToString(getMode(), kMitsubishi112Auto, kMitsubishi112Cool, + kMitsubishi112Heat, kMitsubishi112Dry, -1); + result += addTempToString(getTemp()); + result += addFanToString(getFan(), kMitsubishi112FanMax, + kMitsubishi112FanLow, kMitsubishi112FanMax, + kMitsubishi112FanQuiet, kMitsubishi112FanMed); + result += addIntToString(getSwingV(), F("Swing(V)")); + switch (getSwingV()) { + case kMitsubishi112SwingVHighest: result += F(" (Highest)"); break; + case kMitsubishi112SwingVHigh: result += F(" (High)"); break; + case kMitsubishi112SwingVMiddle: result += F(" (Middle)"); break; + case kMitsubishi112SwingVLow: result += F(" (Low)"); break; + case kMitsubishi112SwingVLowest: result += F(" (Lowest)"); break; + case kMitsubishi112SwingVAuto: result += F(" (Auto)"); break; + default: result += F(" (UNKNOWN)"); + } + result += addIntToString(getSwingH(), F("Swing(H)")); + switch (getSwingH()) { + case kMitsubishi112SwingHLeftMax: result += F(" (Left Max)"); break; + case kMitsubishi112SwingHLeft: result += F(" (Left)"); break; + case kMitsubishi112SwingHMiddle: result += F(" (Middle)"); break; + case kMitsubishi112SwingHRight: result += F(" (Right)"); break; + case kMitsubishi112SwingHRightMax: result += F(" (Right Max)"); break; + case kMitsubishi112SwingHWide: result += F(" (Wide)"); break; + case kMitsubishi112SwingHAuto: result += F(" (Auto)"); break; + default: result += F(" (UNKNOWN)"); + } + result += addBoolToString(getQuiet(), F("Quiet")); + return result; +} diff --git a/src/ir_Mitsubishi.h b/src/ir_Mitsubishi.h index db0973bd0..cff699887 100644 --- a/src/ir_Mitsubishi.h +++ b/src/ir_Mitsubishi.h @@ -1,5 +1,6 @@ // Copyright 2009 Ken Shirriff // Copyright 2017-2019 David Conran +// Copyright 2019 Mark Kuchel // Mitsubishi @@ -10,6 +11,8 @@ // Brand: Mitsubishi, Model: KM14A 0179213 remote // Brand: Mitsubishi Electric, Model: PEAD-RP71JAA Ducted A/C // Brand: Mitsubishi Electric, Model: 001CP T7WE10714 remote +// Brand: Mitsubishi Electric, Model: MSH-A24WV / MUH-A24WV A/C +// Brand: Mitsubishi Electric, Model: KPOA remote #ifndef IR_MITSUBISHI_H_ #define IR_MITSUBISHI_H_ @@ -77,6 +80,51 @@ const uint8_t kMitsubishi136FanMed = 0b10; const uint8_t kMitsubishi136FanMax = 0b11; const uint8_t kMitsubishi136FanQuiet = kMitsubishi136FanMin; +// Mitsubishi112 + +// remote_state[5] +const uint8_t kMitsubishi112PowerByte = 5; +const uint8_t kMitsubishi112PowerBit = 0b00000100; +// remote_state[6] +const uint8_t kMitsubishi112ModeByte = 6; +const uint8_t kMitsubishi112ModeMask = 0b00000111; +const uint8_t kMitsubishi112Cool = 0b011; +const uint8_t kMitsubishi112Heat = 0b001; +const uint8_t kMitsubishi112Auto = 0b111; +const uint8_t kMitsubishi112Dry = 0b010; +// remote_state[7] +const uint8_t kMitsubishi112TempByte = 7; +const uint8_t kMitsubishi112TempMask = 0b00001111; +const uint8_t kMitsubishi112MinTemp = 16; // 16C +const uint8_t kMitsubishi112MaxTemp = 31; // 31C +// remote_state[8] +const uint8_t kMitsubishi112FanByte = 8; +const uint8_t kMitsubishi112FanMask = 0b11111000; +const uint8_t kMitsubishi112FanGetMask = 0b111; +const uint8_t kMitsubishi112FanMin = 0b010; +const uint8_t kMitsubishi112FanLow = 0b011; +const uint8_t kMitsubishi112FanMed = 0b101; +const uint8_t kMitsubishi112FanMax = 0b000; +const uint8_t kMitsubishi112FanQuiet = kMitsubishi112FanMin; +const uint8_t kMitsubishi112SwingVByte = kMitsubishi112FanByte; +const uint8_t kMitsubishi112SwingVMask = 0b00111000; +const uint8_t kMitsubishi112SwingVLowest = 0b00101000; +const uint8_t kMitsubishi112SwingVLow = 0b00100000; +const uint8_t kMitsubishi112SwingVMiddle = 0b00011000; +const uint8_t kMitsubishi112SwingVHigh = 0b00010000; +const uint8_t kMitsubishi112SwingVHighest = 0b00001000; +const uint8_t kMitsubishi112SwingVAuto = 0b00111000; +// remote_state[12] +const uint8_t kMitsubishi112SwingHByte = 12; +const uint8_t kMitsubishi112SwingHMask = 0b00111100; +const uint8_t kMitsubishi112SwingHLeftMax = 0b00000100; +const uint8_t kMitsubishi112SwingHLeft = 0b00001000; +const uint8_t kMitsubishi112SwingHMiddle = 0b00001100; +const uint8_t kMitsubishi112SwingHRight = 0b00010000; +const uint8_t kMitsubishi112SwingHRightMax = 0b00010100; +const uint8_t kMitsubishi112SwingHWide = 0b00100000; +const uint8_t kMitsubishi112SwingHAuto = 0b00110000; + // Legacy defines (Deprecated) #define MITSUBISHI_AC_VANE_AUTO_MOVE kMitsubishiAcVaneAutoMove #define MITSUBISHI_AC_VANE_AUTO kMitsubishiAcVaneAuto @@ -199,4 +247,56 @@ class IRMitsubishi136 { void checksum(void); }; + +class IRMitsubishi112 { + public: + explicit IRMitsubishi112(const uint16_t pin, const bool inverted = false, + const bool use_modulation = true); + + + void stateReset(void); +#if SEND_MITSUBISHI112 + void send(const uint16_t repeat = kMitsubishi112MinRepeat); + uint8_t calibrate(void) { return _irsend.calibrate(); } +#endif // SEND_MITSUBISHI112 + void begin(void); + void on(void); + void off(void); + void setPower(const bool on); + bool getPower(void); + void setTemp(const uint8_t degrees); + uint8_t getTemp(void); + void setFan(const uint8_t speed); + uint8_t getFan(void); + void setMode(const uint8_t mode); + uint8_t getMode(void); + void setSwingV(const uint8_t position); + uint8_t getSwingV(void); + void setSwingH(const uint8_t position); + uint8_t getSwingH(void); + void setQuiet(const bool on); + bool getQuiet(void); + uint8_t* getRaw(void); + void setRaw(const uint8_t* data); + static uint8_t convertMode(const stdAc::opmode_t mode); + static uint8_t convertFan(const stdAc::fanspeed_t speed); + static uint8_t convertSwingV(const stdAc::swingv_t position); + static uint8_t convertSwingH(const stdAc::swingh_t position); + static stdAc::opmode_t toCommonMode(const uint8_t mode); + static stdAc::fanspeed_t toCommonFanSpeed(const uint8_t speed); + static stdAc::swingv_t toCommonSwingV(const uint8_t pos); + static stdAc::swingh_t toCommonSwingH(const uint8_t pos); + stdAc::state_t toCommon(void); + String toString(void); +#ifndef UNIT_TEST + + private: + IRsend _irsend; +#else + IRsendTest _irsend; +#endif + uint8_t remote_state[kMitsubishi112StateLength]; + void checksum(void); +}; + #endif // IR_MITSUBISHI_H_ diff --git a/src/ir_Tcl.cpp b/src/ir_Tcl.cpp index b961e315a..6c17a8837 100644 --- a/src/ir_Tcl.cpp +++ b/src/ir_Tcl.cpp @@ -370,41 +370,7 @@ String IRTcl112Ac::toString(void) { } #if DECODE_TCL112AC -// Decode the supplied TCL112AC 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 kTcl112AcBits. -// 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/crankyoldgit/IRremoteESP8266/issues/619 -bool IRrecv::decodeTcl112Ac(decode_results *results, const uint16_t nbits, - const bool strict) { - if (strict && nbits != kTcl112AcBits) return false; - - uint16_t offset = kStartOffset; - // Match Header + Data + Footer - if (!matchGeneric(results->rawbuf + offset, results->state, - results->rawlen - offset, nbits, - kTcl112AcHdrMark, kTcl112AcHdrSpace, - kTcl112AcBitMark, kTcl112AcOneSpace, - kTcl112AcBitMark, kTcl112AcZeroSpace, - kTcl112AcBitMark, kTcl112AcGap, true, - _tolerance + kTcl112AcTolerance, 0, false)) return false; - // Compliance - // Verify we got a valid checksum. - if (strict && !IRTcl112Ac::validChecksum(results->state)) return false; - // Success - results->decode_type = TCL112AC; - results->bits = nbits; - // 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; -} +// NOTE: There is no `decodedecodeTcl112Ac()`. +// It's the same as `decodeMitsubishi112()`. A shared routine is used. +// You can find it in: ir_Mitsubishi.cpp #endif // DECODE_TCL112AC diff --git a/src/ir_Tcl.h b/src/ir_Tcl.h index 1a1bc1d6b..061b255d9 100644 --- a/src/ir_Tcl.h +++ b/src/ir_Tcl.h @@ -23,7 +23,9 @@ const uint16_t kTcl112AcBitMark = 500; const uint16_t kTcl112AcOneSpace = 1050; const uint16_t kTcl112AcZeroSpace = 325; const uint32_t kTcl112AcGap = kDefaultMessageGap; // Just a guess. -const uint8_t kTcl112AcTolerance = 5; // Extra Percent +// Total tolerance percentage to use for matching the header mark. +const uint8_t kTcl112AcHdrMarkTolerance = 6; +const uint8_t kTcl112AcTolerance = 5; // Extra Percentage for the rest. const uint8_t kTcl112AcHeat = 1; const uint8_t kTcl112AcDry = 2; diff --git a/test/ir_Mitsubishi_test.cpp b/test/ir_Mitsubishi_test.cpp index e8db4f58d..e02093c12 100644 --- a/test/ir_Mitsubishi_test.cpp +++ b/test/ir_Mitsubishi_test.cpp @@ -1,4 +1,5 @@ // Copyright 2017-2019 David Conran +// Copyright 2019 kuchel77 // Copyright 2018 denxhun #include "ir_Mitsubishi.h" @@ -1500,3 +1501,227 @@ TEST(TestDecodeMitsubishiAC, Issue891) { "Time: 00:00, On timer: 00:00, Off timer: 00:00, Timer: -", ac.toString()); } + + +// Tests for IRMitsubishi112 class. + +TEST(TestMitsubishi112Class, Power) { + IRMitsubishi112 ac(0); + ac.begin(); + + ac.on(); + EXPECT_TRUE(ac.getPower()); + + ac.off(); + EXPECT_FALSE(ac.getPower()); + + ac.setPower(true); + EXPECT_TRUE(ac.getPower()); + + ac.setPower(false); + EXPECT_FALSE(ac.getPower()); +} + +TEST(TestMitsubishi112Class, Temperature) { + IRMitsubishi112 ac(0); + ac.begin(); + + ac.setTemp(0); + EXPECT_EQ(kMitsubishi112MinTemp, ac.getTemp()); + + ac.setTemp(255); + EXPECT_EQ(kMitsubishi112MaxTemp, ac.getTemp()); + + ac.setTemp(kMitsubishi112MinTemp); + EXPECT_EQ(kMitsubishi112MinTemp, ac.getTemp()); + + ac.setTemp(kMitsubishi112MaxTemp); + EXPECT_EQ(kMitsubishi112MaxTemp, ac.getTemp()); + + ac.setTemp(kMitsubishi112MinTemp - 1); + EXPECT_EQ(kMitsubishi112MinTemp, ac.getTemp()); + + ac.setTemp(kMitsubishi112MaxTemp + 1); + EXPECT_EQ(kMitsubishi112MaxTemp, ac.getTemp()); + + ac.setTemp(19); + EXPECT_EQ(19, ac.getTemp()); + + ac.setTemp(21); + EXPECT_EQ(21, ac.getTemp()); + + ac.setTemp(25); + EXPECT_EQ(25, ac.getTemp()); + + ac.setTemp(29); + EXPECT_EQ(29, ac.getTemp()); +} + +TEST(TestMitsubishi112Class, OperatingMode) { + IRMitsubishi112 ac(0); + ac.begin(); + + ac.setMode(kMitsubishi112Auto); + EXPECT_EQ(kMitsubishi112Auto, ac.getMode()); + + ac.setMode(kMitsubishi112Cool); + EXPECT_EQ(kMitsubishi112Cool, ac.getMode()); + + ac.setMode(kMitsubishi112Heat); + EXPECT_EQ(kMitsubishi112Heat, ac.getMode()); + + ac.setMode(kMitsubishi112Dry); + EXPECT_EQ(kMitsubishi112Dry, ac.getMode()); +} + +TEST(TestMitsubishi112Class, FanSpeed) { + IRMitsubishi112 ac(0); + ac.begin(); + + ac.setFan(kMitsubishi112FanMax); + EXPECT_EQ(kMitsubishi112FanMax, ac.getFan()); + + ac.setFan(kMitsubishi112FanMin); + EXPECT_EQ(kMitsubishi112FanMin, ac.getFan()); + + ac.setFan(255); + EXPECT_EQ(kMitsubishi112FanMax, ac.getFan()); + + ac.setFan(kMitsubishi112FanMed); + EXPECT_EQ(kMitsubishi112FanMed, ac.getFan()); + + ac.setFan(kMitsubishi112FanLow); + EXPECT_EQ(kMitsubishi112FanLow, ac.getFan()); + + ac.setFan(kMitsubishi112FanQuiet); + EXPECT_EQ(kMitsubishi112FanQuiet, ac.getFan()); +} + + +TEST(TestMitsubishi112Class, SwingV) { + IRMitsubishi112 ac(0); + ac.begin(); + + ac.setSwingV(kMitsubishi112SwingVAuto); + EXPECT_EQ(kMitsubishi112SwingVAuto, ac.getSwingV()); + + ac.setSwingV(kMitsubishi112SwingVAuto + 1); + EXPECT_EQ(kMitsubishi112SwingVAuto, ac.getSwingV()); + + ac.setSwingV(kMitsubishi112SwingVLowest); + EXPECT_EQ(kMitsubishi112SwingVLowest, ac.getSwingV()); + + ac.setSwingV(kMitsubishi112SwingVLow); + EXPECT_EQ(kMitsubishi112SwingVLow, ac.getSwingV()); + + ac.setSwingV(kMitsubishi112SwingVHighest); + EXPECT_EQ(kMitsubishi112SwingVHighest, ac.getSwingV()); + + ac.setSwingV(kMitsubishi112SwingVHigh); + EXPECT_EQ(kMitsubishi112SwingVHigh, ac.getSwingV()); +} + +TEST(TestMitsubishi112Class, toCommon) { + IRMitsubishi112 ac(0); + ac.setPower(true); + ac.setMode(kMitsubishi112Dry); + ac.setTemp(22); + ac.setFan(kMitsubishi112FanQuiet); + ac.setSwingV(kMitsubishi112SwingVAuto); + ac.setSwingH(kMitsubishi112SwingHAuto); + // Now test it. + ASSERT_EQ(decode_type_t::MITSUBISHI112, ac.toCommon().protocol); + ASSERT_EQ(-1, ac.toCommon().model); + ASSERT_TRUE(ac.toCommon().power); + ASSERT_TRUE(ac.toCommon().celsius); + ASSERT_EQ(22, ac.toCommon().degrees); + ASSERT_EQ(stdAc::opmode_t::kDry, ac.toCommon().mode); + ASSERT_EQ(stdAc::fanspeed_t::kMin, ac.toCommon().fanspeed); + ASSERT_EQ(stdAc::swingv_t::kAuto, ac.toCommon().swingv); + ASSERT_EQ(stdAc::swingh_t::kAuto, ac.toCommon().swingh); + ASSERT_TRUE(ac.toCommon().quiet); + // Unsupported. + ASSERT_FALSE(ac.toCommon().turbo); + ASSERT_FALSE(ac.toCommon().clean); + ASSERT_FALSE(ac.toCommon().light); + ASSERT_FALSE(ac.toCommon().econo); + ASSERT_FALSE(ac.toCommon().filter); + ASSERT_FALSE(ac.toCommon().beep); + ASSERT_EQ(-1, ac.toCommon().sleep); + ASSERT_EQ(-1, ac.toCommon().clock); +} + +TEST(TestMitsubishi112Class, toCommonMode) { + ASSERT_EQ(stdAc::opmode_t::kCool, + IRMitsubishi112::toCommonMode(kMitsubishi112Cool)); + ASSERT_EQ(kMitsubishi112Cool, + IRMitsubishi112::convertMode(stdAc::opmode_t::kCool)); + ASSERT_EQ(stdAc::opmode_t::kDry, + IRMitsubishi112::toCommonMode(kMitsubishi112Dry)); + ASSERT_EQ(kMitsubishi112Dry, + IRMitsubishi112::convertMode(stdAc::opmode_t::kDry)); +} + + + +// Decode a 'real' example. +TEST(TestDecodeMitsubishi112, DecodeRealExample) { + IRsendTest irsend(0); + IRrecv irrecv(0); + irsend.begin(); + + irsend.reset(); + uint16_t rawData[227] = {3468, 1694, 464, 1248, 464, 1248, 466, 376, 468, + 422, 470, 380, 488, 1246, 464, 382, 462, 422, 470, 1248, 466, 1246, + 442, 420, 472, 1246, 466, 380, 488, 382, 486, 1248, 442, 1272, 440, + 422, 472, 1246, 464, 1248, 466, 378, 490, 382, 464, 1270, 466, 380, + 464, 420, 448, 1270, 440, 422, 472, 380, 488, 380, 490, 378, 490, + 376, 466, 424, 468, 386, 486, 380, 488, 382, 462, 422, 448, 422, + 448, 422, 472, 378, 464, 422, 472, 380, 488, 380, 490, 380, 486, + 1248, 466, 380, 488, 380, 490, 1246, 440, 424, 468, 382, 488, 1246, + 466, 1246, 466, 380, 490, 380, 464, 422, 472, 380, 464, 422, 472, + 380, 464, 424, 472, 376, 466, 422, 448, 1270, 444, 420, 448, 420, + 470, 384, 486, 380, 490, 380, 486, 1248, 442, 422, 474, 1244, 464, + 1246, 466, 1246, 466, 384, 462, 422, 472, 378, 490, 382, 486, 384, + 462, 422, 470, 382, 488, 380, 464, 420, 448, 420, 448, 422, 470, + 380, 466, 420, 472, 378, 490, 380, 490, 378, 490, 378, 466, 422, + 446, 422, 446, 422, 446, 422, 472, 378, 488, 380, 490, 380, 466, + 420, 472, 380, 490, 380, 486, 384, 462, 422, 472, 378, 466, 1270, + 466, 1246, 442, 422, 470, 380, 488, 384, 486, 1246, 466, 1246, 442, + 1270, 440, 420, 472, 1248, 466, 384, 462, 1270, 440}; // MITSUBISHI112 + + irsend.sendRaw(rawData, 227, 38); + irsend.makeDecodeResult(); + + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + ASSERT_EQ(MITSUBISHI112, irsend.capture.decode_type); + EXPECT_EQ(kMitsubishi112Bits, irsend.capture.bits); + uint8_t expected[kMitsubishi112StateLength] = { + 0x23, 0xCB, 0x26, 0x01, 0x00, 0x24, 0x03, 0x08, 0x3A, 0x00, + 0x00, 0x00, 0x30, 0xAE}; + EXPECT_STATE_EQ(expected, irsend.capture.state, kMitsubishi112Bits); + EXPECT_EQ( + "Power: On, Mode: 3 (Cool), Temp: 23C, Fan: 2 (Quiet), " + "Swing(V): 56 (Auto), Swing(H): 48 (Auto), Quiet: On", + IRAcUtils::resultAcToString(&irsend.capture)); +} + +// Self decode a synthetic example. +TEST(TestDecodeMitsubishi112, SyntheticExample) { + IRsendTest irsend(0); + IRrecv irrecv(0); + irsend.begin(); + + irsend.reset(); + uint8_t expected[kMitsubishi112StateLength] = { + 0x23, 0xCB, 0x26, 0x01, 0x00, 0x24, 0x03, 0x08, 0x3A, + 0x00, 0x00, 0x00, 0x30, 0xAE}; + + irsend.sendMitsubishi112(expected); + irsend.makeDecodeResult(); + + ASSERT_TRUE(irrecv.decode(&irsend.capture)); + ASSERT_EQ(MITSUBISHI112, irsend.capture.decode_type); + EXPECT_EQ(kMitsubishi112Bits, irsend.capture.bits); + EXPECT_STATE_EQ(expected, irsend.capture.state, kMitsubishi112Bits); +}