diff --git a/src/ir_Electrolux.cpp b/src/ir_Electrolux.cpp index a7a8406c2..e477db831 100644 --- a/src/ir_Electrolux.cpp +++ b/src/ir_Electrolux.cpp @@ -6,7 +6,6 @@ // Brand: Electrolux, Model: Electrolux EACM EZ/N3 #include "ir_Electrolux.h" -#include #include #include "IRac.h" #include "IRrecv.h" @@ -20,7 +19,7 @@ const uint16_t kElectroluxAcBitMark = 752; const uint16_t kElectroluxAcHdrSpace = 2700; const uint16_t kElectroluxAcOneSpace = 2149; const uint16_t kElectroluxAcZeroSpace = 756; -const uint16_t kElectroluxAcFreq = 38000; // Hz. (Guessing the most common frequency.) +const uint16_t kElectroluxAcFreq = 38000; // Hz. (Guessing the most common frequency.) const uint16_t kElectroluxAcOverhead = 3; #if SEND_ELECTROLUX_AC @@ -30,9 +29,11 @@ const uint16_t kElectroluxAcOverhead = 3; /// @param[in] data containing the IR command. /// @param[in] nbits Nr. of bits to send. usually kElectroluxBits /// @param[in] repeat Nr. of times the message is to be repeated. -void IRsend::sendElectroluxAc(const uint64_t data, const uint16_t nbits, const uint16_t repeat) { +void IRsend::sendElectroluxAc(const uint64_t data, const uint16_t nbits, const uint16_t repeat) +{ enableIROut(kElectroluxAcFreq); - for (uint16_t r = 0; r <= repeat; r++) { + for (uint16_t r = 0; r <= repeat; r++) + { uint64_t send_data = data; // Header mark(kElectroluxAcHdrMark); @@ -42,10 +43,10 @@ void IRsend::sendElectroluxAc(const uint64_t data, const uint16_t nbits, const u send_data >>= 32; // Footer mark(kElectroluxAcBitMark); - space(kDefaultMessageGap); // A 100% made up guess of the gap between messages. + space(kDefaultMessageGap); // A 100% made up guess of the gap between messages. } } -#endif // SEND_ELECTROLUX +#endif // SEND_ELECTROLUX #if DECODE_ELECTROLUX_AC // Function should be safe up to 64 bits. @@ -57,10 +58,11 @@ void IRsend::sendElectroluxAc(const uint64_t data, const uint16_t nbits, const u /// @param[in] nbits The number of data bits to expect. /// @param[in] strict Flag indicating if we should perform strict matching. /// @return A boolean. True if it can decode it, false if it can't. -bool IRrecv::decodeElectroluxAc(decode_results *results, uint16_t offset, const uint16_t nbits, const bool strict) { - - if (results->rawlen < 2 * nbits + kElectroluxAcOverhead - offset) //rawlen = 68, nbits = 104 - return false; // Too short a message to match. +bool IRrecv::decodeElectroluxAc(decode_results *results, uint16_t offset, const uint16_t nbits, const bool strict) +{ + + if (results->rawlen < 2 * nbits + kElectroluxAcOverhead - offset) // rawlen = 68, nbits = 104 + return false; // Too short a message to match. if (strict && nbits != kElectroluxAcBits) return false; @@ -73,16 +75,15 @@ bool IRrecv::decodeElectroluxAc(decode_results *results, uint16_t offset, const if (!matchSpace(results->rawbuf[offset++], kElectroluxAcHdrSpace)) return false; - - // Data Section #1 // e.g. data_result.data = 0xED000004, nbits = 32 data_result = matchData(&(results->rawbuf[offset]), 32, kElectroluxAcBitMark, kElectroluxAcOneSpace, kElectroluxAcBitMark, kElectroluxAcZeroSpace); offset += data_result.used; - if (data_result.success == false) return false; // Fail - data <<= 32; // Make room for the new bits of data. + if (data_result.success == false) + return false; // Fail + data <<= 32; // Make room for the new bits of data. data |= data_result.data; // Footer @@ -97,15 +98,15 @@ bool IRrecv::decodeElectroluxAc(decode_results *results, uint16_t offset, const results->address = 0; return true; } -#endif // DECODE_ELECTROLUX +#endif // DECODE_ELECTROLUX /// Class constructor /// @param[in] pin GPIO to be used when sending. /// @param[in] inverted Is the output signal to be inverted? /// @param[in] use_modulation Is frequency modulation to be used? IRElectroluxAc::IRElectroluxAc(const uint16_t pin, const bool inverted, - const bool use_modulation) - : _irsend(pin, inverted, use_modulation) { stateReset(); } + const bool use_modulation) + : _irsend(pin, inverted, use_modulation) { stateReset(); } /// Reset the internal state to a fixed known good state. /// @note The state is powered off. @@ -114,17 +115,18 @@ void IRElectroluxAc::stateReset(void) { _.raw = 0xF3008005; } #if SEND_ELECTROLUX_AC /// Send the current internal state as an IR message. /// @param[in] repeat Nr. of times the message will be repeated. -void IRElectroluxAc::send(const uint16_t repeat) { +void IRElectroluxAc::send(const uint16_t repeat) +{ _irsend.sendElectroluxAc(getRaw(), kElectroluxAcBits, repeat); } -#endif // SEND_ELECTROLUX_AC +#endif // SEND_ELECTROLUX_AC /// Set up hardware to be able to send a message. void IRElectroluxAc::begin(void) { _irsend.begin(); } /// Turn on/off the Power Airwell setting. /// @param[in] on The desired setting state. -void IRElectroluxAc::setPower(const bool on) { _.Power = on; } +void IRElectroluxAc::setPower(const bool on) { _.Power = on; } /// Get the power toggle setting from the internal state. /// @return A boolean indicating the setting. @@ -140,13 +142,16 @@ bool IRElectroluxAc::getTempModeFahrenheit(void) const { return _.TempModeFahren /// Set the temperature. /// @param[in] degrees The temperature in celsius or fahrenheit. -void IRElectroluxAc::setTemp(const uint8_t degrees) { - if(getTempModeFahrenheit()) { +void IRElectroluxAc::setTemp(const uint8_t degrees) +{ + if (getTempModeFahrenheit()) + { uint8_t temp = max(kElectroluxAcMinFTemp, degrees); temp = min(kElectroluxAcMaxFTemp, temp); _.Temp = (temp - kElectroluxAcMinFTemp); } - else { + else + { uint8_t temp = max(kElectroluxAcMinTemp, degrees); temp = min(kElectroluxAcMaxTemp, temp); temp = map(temp, kElectroluxAcMinTemp, kElectroluxAcMaxTemp, kElectroluxAcMinFTemp, kElectroluxAcMaxFTemp); @@ -156,11 +161,14 @@ void IRElectroluxAc::setTemp(const uint8_t degrees) { /// Get the current temperature from the internal state. /// @return The current temperature in Celsius. -uint8_t IRElectroluxAc::getTemp(void) const { - if(getTempModeFahrenheit()) { +uint8_t IRElectroluxAc::getTemp(void) const +{ + if (getTempModeFahrenheit()) + { return _.Temp + kElectroluxAcMinFTemp; } - else { + else + { uint8_t temp = map(_.Temp + kElectroluxAcMinFTemp, kElectroluxAcMinFTemp, kElectroluxAcMaxFTemp, kElectroluxAcMinTemp, kElectroluxAcMaxTemp); return temp; } @@ -169,9 +177,10 @@ uint8_t IRElectroluxAc::getTemp(void) const { /// Set the speed of the fan. /// @param[in] speed The desired setting. /// @note The speed is locked to Low when in Dry mode. -void IRElectroluxAc::setFan(const uint8_t speed) { +void IRElectroluxAc::setFan(const uint8_t speed) +{ _.Fan = (_.Mode == kElectroluxModeAuto) ? kElectroluxFanAuto - : std::min(speed, kElectroluxFanAuto); + : std::min(speed, kElectroluxFanAuto); } /// Get the current fan speed setting. @@ -180,16 +189,18 @@ uint8_t IRElectroluxAc::getFan(void) const { return _.Fan; } /// Set the desired operation mode. /// @param[in] mode The desired operation mode. -void IRElectroluxAc::setMode(const uint8_t mode) { - switch (mode) { - case kElectroluxModeCool: - case kElectroluxModeDry: - case kElectroluxModeFan: - case kElectroluxModeAuto: - _.Mode = mode; - break; - default: - _.Mode = kElectroluxModeAuto; +void IRElectroluxAc::setMode(const uint8_t mode) +{ + switch (mode) + { + case kElectroluxModeCool: + case kElectroluxModeDry: + case kElectroluxModeFan: + case kElectroluxModeAuto: + _.Mode = mode; + break; + default: + _.Mode = kElectroluxModeAuto; } } @@ -201,17 +212,19 @@ uint8_t IRElectroluxAc::getMode(void) const { return _.Mode; } /// @param[in] nr_of_mins Number of minutes to set the timer to. /// (< 60 is disable). /// @note The A/C protocol only supports one hour increments. -void IRElectroluxAc::setOnOffTimer(const uint16_t nr_of_hours) { - uint8_t hours = std::min((uint8_t)(nr_of_hours / 60), kElectroluxTimerMax); +void IRElectroluxAc::setOnOffTimer(const uint16_t nr_of_mins) +{ + uint8_t hours = std::min((uint8_t)(nr_of_mins / 60), kElectroluxTimerMax); // The time can be changed in sleep mode, but doesn't set the flag. _.TimerEnable = hours > 0; - _.Timer = std::max(kElectroluxTimerMin, hours); // Hours + _.Timer = std::max(kElectroluxTimerMin, hours); // Hours } /// Get the current On/Off Timer time. /// @return The number of minutes it is set for. 0 means it's off. /// @note The A/C protocol only supports one hour increments. -uint16_t IRElectroluxAc::getOnOffTimer(void) const { +uint16_t IRElectroluxAc::getOnOffTimer(void) const +{ if (_.TimerEnable) return _.Timer * 60; else @@ -228,8 +241,9 @@ bool IRElectroluxAc::getQuiet(void) const { return _.Quiet; } /// Get a copy of the internal state as a valid code for this protocol. /// @return A valid code for this protocol based on the current internal state. -uint64_t IRElectroluxAc::getRaw(void) { - checksum(); // Ensure correct settings before sending. +uint64_t IRElectroluxAc::getRaw(void) +{ + checksum(); // Ensure correct settings before sending. return _.raw; } @@ -240,10 +254,11 @@ void IRElectroluxAc::setRaw(const uint64_t state) { _.raw = state; } /// Calculate the checksum for a given state. /// @param[in] state The value to calc the checksum of. /// @return The 4-bit checksum stored in a uint_8. -uint8_t IRElectroluxAc::calcChecksum(const uint64_t state) { +uint8_t IRElectroluxAc::calcChecksum(const uint64_t state) +{ uint32_t data = GETBITS64(state, kElectroluxAcChecksumSize + kElectroluxAcChecksumOffset, kElectroluxAcBits - 4); uint8_t result = 0; - for (; data; data >>= 4) // Add each nibble together. + for (; data; data >>= 4) // Add each nibble together. result += GETBITS8(data, 0, 4); return (result ^ 0xF) & 0xF; } @@ -251,76 +266,101 @@ uint8_t IRElectroluxAc::calcChecksum(const uint64_t state) { /// Verify the checksum is valid for a given state. /// @param[in] state The array to verify the checksum of. /// @return true, if the state has a valid checksum. Otherwise, false. -bool IRElectroluxAc::validChecksum(const uint64_t state) { +bool IRElectroluxAc::validChecksum(const uint64_t state) +{ // Validate the checksum of the given state. return (GETBITS8(state, kElectroluxAcChecksumOffset, - kElectroluxAcChecksumSize) == calcChecksum(state)); + kElectroluxAcChecksumSize) == calcChecksum(state)); } /// Convert a stdAc::opmode_t enum into its native mode. /// @param[in] mode The enum to be converted. /// @return The native equivalent of the enum. -uint8_t IRElectroluxAc::convertMode(const stdAc::opmode_t mode) { - switch (mode) { - case stdAc::opmode_t::kCool: return kElectroluxModeCool; - case stdAc::opmode_t::kDry: return kElectroluxModeDry; - case stdAc::opmode_t::kFan: return kElectroluxModeFan; - default: return kElectroluxModeAuto; +uint8_t IRElectroluxAc::convertMode(const stdAc::opmode_t mode) +{ + switch (mode) + { + case stdAc::opmode_t::kCool: + return kElectroluxModeCool; + case stdAc::opmode_t::kDry: + return kElectroluxModeDry; + case stdAc::opmode_t::kFan: + return kElectroluxModeFan; + default: + return kElectroluxModeAuto; } } /// Convert a stdAc::fanspeed_t enum into it's native speed. /// @param[in] speed The enum to be converted. /// @return The native equivalent of the enum. -uint8_t IRElectroluxAc::convertFan(const stdAc::fanspeed_t speed) { - switch (speed) { - case stdAc::fanspeed_t::kMin: - case stdAc::fanspeed_t::kLow: - return kElectroluxFanLow; - case stdAc::fanspeed_t::kMedium: - case stdAc::fanspeed_t::kMediumHigh: - return kElectroluxFanMedium; - case stdAc::fanspeed_t::kHigh: - case stdAc::fanspeed_t::kMax: - return kElectroluxFanHigh; - default: - return kElectroluxFanAuto; +uint8_t IRElectroluxAc::convertFan(const stdAc::fanspeed_t speed) +{ + switch (speed) + { + case stdAc::fanspeed_t::kMin: + case stdAc::fanspeed_t::kLow: + return kElectroluxFanLow; + case stdAc::fanspeed_t::kMedium: + case stdAc::fanspeed_t::kMediumHigh: + return kElectroluxFanMedium; + case stdAc::fanspeed_t::kHigh: + case stdAc::fanspeed_t::kMax: + return kElectroluxFanHigh; + default: + return kElectroluxFanAuto; } } /// Convert a native mode into its stdAc equivalent. /// @param[in] mode The native setting to be converted. /// @return The stdAc equivalent of the native setting. -stdAc::opmode_t IRElectroluxAc::toCommonMode(const uint8_t mode) { - switch (mode) { - case kElectroluxModeCool: return stdAc::opmode_t::kCool; - case kElectroluxModeDry: return stdAc::opmode_t::kDry; - case kElectroluxModeFan: return stdAc::opmode_t::kFan; - default: return stdAc::opmode_t::kAuto; +stdAc::opmode_t IRElectroluxAc::toCommonMode(const uint8_t mode) +{ + switch (mode) + { + case kElectroluxModeCool: + return stdAc::opmode_t::kCool; + case kElectroluxModeDry: + return stdAc::opmode_t::kDry; + case kElectroluxModeFan: + return stdAc::opmode_t::kFan; + default: + return stdAc::opmode_t::kAuto; } } /// Convert a native fan speed into its stdAc equivalent. /// @param[in] speed The native setting to be converted. /// @return The stdAc equivalent of the native setting. -stdAc::fanspeed_t IRElectroluxAc::toCommonFanSpeed(const uint8_t speed) { - switch (speed) { - case kElectroluxFanHigh: return stdAc::fanspeed_t::kMax; - case kElectroluxFanMedium: return stdAc::fanspeed_t::kMedium; - case kElectroluxFanLow: return stdAc::fanspeed_t::kMin; - default: return stdAc::fanspeed_t::kAuto; +stdAc::fanspeed_t IRElectroluxAc::toCommonFanSpeed(const uint8_t speed) +{ + switch (speed) + { + case kElectroluxFanHigh: + return stdAc::fanspeed_t::kMax; + case kElectroluxFanMedium: + return stdAc::fanspeed_t::kMedium; + case kElectroluxFanLow: + return stdAc::fanspeed_t::kMin; + default: + return stdAc::fanspeed_t::kAuto; } } /// Convert the current internal state into its stdAc::state_t equivalent. /// @param[in] prev Ptr to the previous state if required. /// @return The stdAc equivalent of the native settings. -stdAc::state_t IRElectroluxAc::toCommon(const stdAc::state_t *prev) const { +stdAc::state_t IRElectroluxAc::toCommon(const stdAc::state_t *prev) const +{ stdAc::state_t result{}; // Start with the previous state if given it. - if (prev != NULL) { + if (prev != NULL) + { result = *prev; - } else { + } + else + { // Set defaults for non-zero values that are not implicitly set for when // there is no previous state. // e.g. Any setting that toggles should probably go here. @@ -350,9 +390,10 @@ stdAc::state_t IRElectroluxAc::toCommon(const stdAc::state_t *prev) const { /// Convert the internal state into a human readable string. /// @return The current internal state expressed as a human readable String. -String IRElectroluxAc::toString(void) const { +String IRElectroluxAc::toString(void) const +{ String result = ""; - result.reserve(120); // Reserve some heap for the string to reduce fragging. + result.reserve(120); // Reserve some heap for the string to reduce fragging. result += addBoolToString(_.Power, kPowerStr, false); result += addModeToString(_.Mode, kElectroluxModeAuto, kElectroluxModeCool, 0xFF, kElectroluxModeDry, kElectroluxModeFan); @@ -363,17 +404,20 @@ String IRElectroluxAc::toString(void) const { result += addBoolToString(getQuiet(), kQuietStr); - if(getPower()) { + if (getPower()) + { result += irutils::addLabeledString(irutils::minsToString(getOnOffTimer()), kOffTimerStr); } - else { + else + { result += irutils::addLabeledString(irutils::minsToString(getOnOffTimer()), kOnTimerStr); } return result; } /// Calculate and set the checksum values for the internal state. -void IRElectroluxAc::checksum(void) { +void IRElectroluxAc::checksum(void) +{ _.Sum = calcChecksum(_.raw); } diff --git a/src/ir_Electrolux.h b/src/ir_Electrolux.h index d3f7ee92e..7e98111b2 100644 --- a/src/ir_Electrolux.h +++ b/src/ir_Electrolux.h @@ -20,51 +20,53 @@ #include "IRsend_test.h" #endif -union ElectroluxAcProtocol{ - uint64_t raw; // The state of the IR remote in native IR code form. - struct { - uint8_t Sum :4; - uint8_t :4; - uint8_t :5; - uint8_t TempModeFahrenheit :1; - uint8_t :1; - uint8_t Quiet :1; - uint8_t Timer :4; - uint8_t TimerEnable :1; - uint8_t Mode :3; - uint8_t Temp :5; - uint8_t Fan :2; - uint8_t Power :1; - uint64_t :0; - }; +union ElectroluxAcProtocol +{ + uint64_t raw; // The state of the IR remote in native IR code form. + struct + { + uint8_t Sum : 4; + uint8_t : 4; + uint8_t : 5; + uint8_t TempModeFahrenheit : 1; + uint8_t : 1; + uint8_t Quiet : 1; + uint8_t Timer : 4; + uint8_t TimerEnable : 1; + uint8_t Mode : 3; + uint8_t Temp : 5; + uint8_t Fan : 2; + uint8_t Power : 1; + uint64_t : 0; + }; }; // Constants -const uint8_t kElectroluxAcMinTemp = 16; // 16C -const uint8_t kElectroluxAcMaxTemp = 32; // 32C -const uint8_t kElectroluxAcMinFTemp = 60; // 60F -const uint8_t kElectroluxAcMaxFTemp = 90; // 90F -const uint8_t kElectroluxTimerMax = 12; // 12H -const uint8_t kElectroluxTimerMin = 1; // 1H +const uint8_t kElectroluxAcMinTemp = 16; // 16C +const uint8_t kElectroluxAcMaxTemp = 32; // 32C +const uint8_t kElectroluxAcMinFTemp = 60; // 60F +const uint8_t kElectroluxAcMaxFTemp = 90; // 90F +const uint8_t kElectroluxTimerMax = 12; // 12H +const uint8_t kElectroluxTimerMin = 1; // 1H const uint64_t kElectroluxAcKnownGoodState = 0xF3008005; const uint8_t kElectroluxAcChecksumOffset = 0; const uint8_t kElectroluxAcChecksumSize = 4; // Fan -const uint8_t kElectroluxFanLow = 2; // 0b11 -const uint8_t kElectroluxFanMedium = 1; // 0b01 -const uint8_t kElectroluxFanHigh = 0; // 0b00 -const uint8_t kElectroluxFanAuto = 3; // 0b11 +const uint8_t kElectroluxFanLow = 2; // 0b11 +const uint8_t kElectroluxFanMedium = 1; // 0b01 +const uint8_t kElectroluxFanHigh = 0; // 0b00 +const uint8_t kElectroluxFanAuto = 3; // 0b11 // Modes -const uint8_t kElectroluxModeCool = 0; // 0b000 -const uint8_t kElectroluxModeDry = 1; // 0b001 -const uint8_t kElectroluxModeFan = 2; // 0b010 -const uint8_t kElectroluxModeAuto = 4; // 0b100 +const uint8_t kElectroluxModeCool = 0; // 0b000 +const uint8_t kElectroluxModeDry = 1; // 0b001 +const uint8_t kElectroluxModeFan = 2; // 0b010 +const uint8_t kElectroluxModeAuto = 4; // 0b100 - -class IRElectroluxAc { - public: +class IRElectroluxAc +{ +public: explicit IRElectroluxAc(const uint16_t pin, const bool inverted = false, const bool use_modulation = true); void stateReset(); @@ -75,7 +77,7 @@ class IRElectroluxAc { /// @note This will produce a 65ms IR signal pulse at 38kHz. /// Only ever needs to be run once per object instantiation, if at all. int8_t calibrate(void) { return _irsend.calibrate(); } -#endif // SEND_ELECTROLUX_AC +#endif // SEND_ELECTROLUX_AC void begin(); void on(void); void off(void); @@ -104,17 +106,16 @@ class IRElectroluxAc { stdAc::state_t toCommon(const stdAc::state_t *prev = NULL) const; String toString(void) const; - #ifndef UNIT_TEST - private: - IRsend _irsend; ///< Instance of the IR send class -#else // UNIT_TEST +private: + IRsend _irsend; ///< Instance of the IR send class +#else // UNIT_TEST /// @cond IGNORE - IRsendTest _irsend; ///< Instance of the testing IR send class + IRsendTest _irsend; ///< Instance of the testing IR send class /// @endcond -#endif // UNIT_TEST +#endif // UNIT_TEST ElectroluxAcProtocol _; void checksum(void); }; -#endif // IR_ELECTROLUX_AC_H_ \ No newline at end of file +#endif // IR_ELECTROLUX_AC_H_ \ No newline at end of file