Merge pull request #8992 from s-hadinger/ir_pioneer_samsung

Change IRRemoteESP8266 IR lib to pre-2.7.9, fixing Samsung and Pioneer protocols (#8938)
This commit is contained in:
Theo Arends 2020-07-29 14:37:24 +02:00 committed by GitHub
commit d9427c5c5b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
48 changed files with 2991 additions and 862 deletions

View File

@ -38,7 +38,7 @@ void printState() {
// Display the encoded IR sequence.
unsigned char* ir_code = ac.getRaw();
Serial.print("IR Code: 0x");
for (uint8_t i = 0; i < kToshibaACStateLength; i++)
for (uint8_t i = 0; i < ac.getStateLength(); i++)
Serial.printf("%02X", ir_code[i]);
Serial.println();
}

View File

@ -16,6 +16,7 @@
#include "IRremoteESP8266.h"
#include "IRtext.h"
#include "IRutils.h"
#include "ir_Airwell.h"
#include "ir_Amcor.h"
#include "ir_Argo.h"
#include "ir_Carrier.h"
@ -34,6 +35,7 @@
#include "ir_Neoclima.h"
#include "ir_Panasonic.h"
#include "ir_Samsung.h"
#include "ir_Sanyo.h"
#include "ir_Sharp.h"
#include "ir_Tcl.h"
#include "ir_Teco.h"
@ -132,6 +134,9 @@ stdAc::state_t IRac::getStatePrev(void) { return _prev; }
/// @return true if the protocol is supported by this class, otherwise false.
bool IRac::isProtocolSupported(const decode_type_t protocol) {
switch (protocol) {
#if SEND_AIRWELL
case decode_type_t::AIRWELL:
#endif
#if SEND_AMCOR
case decode_type_t::AMCOR:
#endif
@ -236,6 +241,9 @@ bool IRac::isProtocolSupported(const decode_type_t protocol) {
#if SEND_SAMSUNG_AC
case decode_type_t::SAMSUNG_AC:
#endif
#if SEND_SANYO_AC
case decode_type_t::SANYO_AC:
#endif
#if SEND_SHARP_AC
case decode_type_t::SHARP_AC:
#endif
@ -269,6 +277,34 @@ bool IRac::isProtocolSupported(const decode_type_t protocol) {
}
}
#if SEND_AIRWELL
/// Send an Airwell A/C message with the supplied settings.
/// @param[in, out] ac A Ptr to an IRAirwellAc object to use.
/// @param[in] on The power setting.
/// @param[in] mode The operation mode setting.
/// @param[in] degrees The temperature setting in degrees.
/// @param[in] fan The speed setting for the fan.
void IRac::airwell(IRAirwellAc *ac,
const bool on, const stdAc::opmode_t mode,
const float degrees, const stdAc::fanspeed_t fan) {
ac->begin();
ac->setPowerToggle(on);
ac->setMode(ac->convertMode(mode));
ac->setTemp(degrees);
ac->setFan(ac->convertFan(fan));
// No Swing setting available.
// No Quiet setting available.
// No Light setting available.
// No Filter setting available.
// No Turbo setting available.
// No Economy setting available.
// No Clean setting available.
// No Beep setting available.
// No Sleep setting available.
ac->send();
}
#endif // SEND_AIRWELL
#if SEND_AMCOR
/// Send an Amcor A/C message with the supplied settings.
/// @param[in, out] ac A Ptr to an IRAmcorAc object to use.
@ -1231,11 +1267,14 @@ void IRac::lg(IRLgAc *ac, const lg_ac_remote_model_t model,
/// @param[in] degrees The temperature setting in degrees.
/// @param[in] fan The speed setting for the fan.
/// @param[in] swingv The vertical swing setting.
/// @param[in] econo Run the device in economical mode.
/// @param[in] sleep Nr. of minutes for sleep mode. -1 is Off, >= 0 is on.
/// @note On Danby A/C units, swingv controls the Ion Filter instead.
void IRac::midea(IRMideaAC *ac,
const bool on, const stdAc::opmode_t mode, const bool celsius,
const float degrees, const stdAc::fanspeed_t fan,
const stdAc::swingv_t swingv, const int16_t sleep) {
const stdAc::swingv_t swingv, const bool econo,
const int16_t sleep) {
ac->begin();
ac->setPower(on);
ac->setMode(ac->convertMode(mode));
@ -1246,6 +1285,7 @@ void IRac::midea(IRMideaAC *ac,
// No Horizontal swing setting available.
// No Quiet setting available.
// No Turbo setting available.
ac->setEconoToggle(econo);
// No Light setting available.
// No Filter setting available.
// No Clean setting available.
@ -1572,6 +1612,45 @@ void IRac::samsung(IRSamsungAc *ac,
}
#endif // SEND_SAMSUNG_AC
#if SEND_SANYO_AC
/// Send a Toshiba A/C message with the supplied settings.
/// @param[in, out] ac A Ptr to an IRSanyoAc object to use.
/// @param[in] on The power setting.
/// @param[in] mode The operation mode setting.
/// @param[in] degrees The temperature setting in degrees.
/// @param[in] fan The speed setting for the fan.
/// @param[in] swingv The vertical swing setting.
/// @param[in] beep Enable/Disable beeps when receiving IR messages.
/// @param[in] sleep Nr. of minutes for sleep mode. -1 is Off, > 0 is on.
void IRac::sanyo(IRSanyoAc *ac,
const bool on, const stdAc::opmode_t mode,
const float degrees, const stdAc::fanspeed_t fan,
const stdAc::swingv_t swingv, const bool beep,
const int16_t sleep) {
ac->begin();
ac->setPower(on);
ac->setMode(ac->convertMode(mode));
ac->setTemp(degrees);
ac->setFan(ac->convertFan(fan));
ac->setSwingV(ac->convertSwingV(swingv));
// No Horizontal swing setting available.
// No Quiet setting available.
// No Turbo setting available.
// No Econo setting available.
// No Light setting available.
// No Filter setting available.
// No Clean setting available.
ac->setBeep(beep);
ac->setSleep(sleep >= 0); // Sleep is either on/off, so convert to boolean.
// No Clock setting available.
// Extra
ac->setSensor(true); // Set the A/C to use the temp sensor in the Unit/Wall.
ac->setSensorTemp(degrees); // Set the sensor temp to the desired temp.
ac->send();
}
#endif // SEND_SANYO_AC
#if SEND_SHARP_AC
/// Send a Sharp A/C message with the supplied settings.
/// @note Multiple IR messages may be generated & sent.
@ -1703,18 +1782,26 @@ void IRac::teco(IRTecoAc *ac,
/// @param[in] mode The operation mode setting.
/// @param[in] degrees The temperature setting in degrees.
/// @param[in] fan The speed setting for the fan.
/// @param[in] swingv The vertical swing setting.
/// @param[in] turbo Run the device in turbo/powerful mode.
/// @param[in] econo Run the device in economical mode.
void IRac::toshiba(IRToshibaAC *ac,
const bool on, const stdAc::opmode_t mode,
const float degrees, const stdAc::fanspeed_t fan) {
const float degrees, const stdAc::fanspeed_t fan,
const stdAc::swingv_t swingv,
const bool turbo, const bool econo) {
ac->begin();
ac->setPower(on);
ac->setMode(ac->convertMode(mode));
ac->setTemp(degrees);
ac->setFan(ac->convertFan(fan));
// No Vertical swing setting available.
// The API has no "step" option, so off is off, anything else is on.
ac->setSwing((swingv == stdAc::swingv_t::kOff) ? kToshibaAcSwingOff
: kToshibaAcSwingOn);
// No Horizontal swing setting available.
// No Quiet setting available.
// No Turbo setting available.
ac->setTurbo(turbo);
ac->setEcono(econo);
// No Light setting available.
// No Filter setting available.
// No Clean setting available.
@ -1881,10 +1968,12 @@ stdAc::state_t IRac::handleToggles(const stdAc::state_t desired,
case decode_type_t::ELECTRA_AC:
result.light = desired.light ^ prev->light;
break;
case decode_type_t::MIDEA:
result.econo = desired.econo ^ prev->econo;
// FALL THRU
case decode_type_t::CORONA_AC:
case decode_type_t::HITACHI_AC344:
case decode_type_t::HITACHI_AC424:
case decode_type_t::MIDEA:
case decode_type_t::SHARP_AC:
if ((desired.swingv == stdAc::swingv_t::kOff) ^
(prev->swingv == stdAc::swingv_t::kOff)) // It changed, so toggle.
@ -1892,6 +1981,7 @@ stdAc::state_t IRac::handleToggles(const stdAc::state_t desired,
else
result.swingv = stdAc::swingv_t::kOff; // No change, so no toggle.
break;
case decode_type_t::AIRWELL:
case decode_type_t::DAIKIN64:
case decode_type_t::WHIRLPOOL_AC:
result.power = desired.power ^ prev->power;
@ -1959,6 +2049,14 @@ bool IRac::sendAc(const stdAc::state_t desired, const stdAc::state_t *prev) {
stdAc::state_t send = this->handleToggles(this->cleanState(desired), prev);
// Per vendor settings & setup.
switch (send.protocol) {
#if SEND_AIRWELL
case AIRWELL:
{
IRAirwellAc ac(_pin, _inverted, _modulation);
airwell(&ac, send.power, send.mode, degC, send.fanspeed);
break;
}
#endif // SEND_AIRWELL
#if SEND_AMCOR
case AMCOR:
{
@ -2210,7 +2308,7 @@ bool IRac::sendAc(const stdAc::state_t desired, const stdAc::state_t *prev) {
{
IRMideaAC ac(_pin, _inverted, _modulation);
midea(&ac, send.power, send.mode, send.celsius, send.degrees,
send.fanspeed, send.swingv, send.sleep);
send.fanspeed, send.swingv, send.econo, send.sleep);
break;
}
#endif // SEND_MIDEA
@ -2288,6 +2386,15 @@ bool IRac::sendAc(const stdAc::state_t desired, const stdAc::state_t *prev) {
break;
}
#endif // SEND_SAMSUNG_AC
#if SEND_SANYO_AC
case SANYO_AC:
{
IRSanyoAc ac(_pin, _inverted, _modulation);
sanyo(&ac, send.power, send.mode, degC, send.fanspeed, send.swingv,
send.beep, send.sleep);
break;
}
#endif // SEND_SANYO_AC
#if SEND_SHARP_AC
case SHARP_AC:
{
@ -2321,7 +2428,8 @@ bool IRac::sendAc(const stdAc::state_t desired, const stdAc::state_t *prev) {
case TOSHIBA_AC:
{
IRToshibaAC ac(_pin, _inverted, _modulation);
toshiba(&ac, send.power, send.mode, degC, send.fanspeed);
toshiba(&ac, send.power, send.mode, degC, send.fanspeed, send.swingv,
send.turbo, send.econo);
break;
}
#endif // SEND_TOSHIBA_AC
@ -2715,16 +2823,23 @@ namespace IRAcUtils {
/// An empty string if we can't.
String resultAcToString(const decode_results * const result) {
switch (result->decode_type) {
#if DECODE_AIRWELL
case decode_type_t::AIRWELL: {
IRAirwellAc ac(kGpioUnused);
ac.setRaw(result->value); // AIRWELL uses value instead of state.
return ac.toString();
}
#endif // DECODE_AIRWELL
#if DECODE_AMCOR
case decode_type_t::AMCOR: {
IRAmcorAc ac(0);
IRAmcorAc ac(kGpioUnused);
ac.setRaw(result->state);
return ac.toString();
}
#endif // DECODE_AMCOR
#if DECODE_ARGO
case decode_type_t::ARGO: {
IRArgoAC ac(0);
IRArgoAC ac(kGpioUnused);
ac.setRaw(result->state);
return ac.toString();
}
@ -2738,49 +2853,49 @@ namespace IRAcUtils {
#endif // DECODE_CARRIER_AC64
#if DECODE_DAIKIN
case decode_type_t::DAIKIN: {
IRDaikinESP ac(0);
IRDaikinESP ac(kGpioUnused);
ac.setRaw(result->state);
return ac.toString();
}
#endif // DECODE_DAIKIN
#if DECODE_DAIKIN128
case decode_type_t::DAIKIN128: {
IRDaikin128 ac(0);
IRDaikin128 ac(kGpioUnused);
ac.setRaw(result->state);
return ac.toString();
}
#endif // DECODE_DAIKIN128
#if DECODE_DAIKIN152
case decode_type_t::DAIKIN152: {
IRDaikin152 ac(0);
IRDaikin152 ac(kGpioUnused);
ac.setRaw(result->state);
return ac.toString();
}
#endif // DECODE_DAIKIN152
#if DECODE_DAIKIN160
case decode_type_t::DAIKIN160: {
IRDaikin160 ac(0);
IRDaikin160 ac(kGpioUnused);
ac.setRaw(result->state);
return ac.toString();
}
#endif // DECODE_DAIKIN160
#if DECODE_DAIKIN176
case decode_type_t::DAIKIN176: {
IRDaikin176 ac(0);
IRDaikin176 ac(kGpioUnused);
ac.setRaw(result->state);
return ac.toString();
}
#endif // DECODE_DAIKIN160
#if DECODE_DAIKIN2
case decode_type_t::DAIKIN2: {
IRDaikin2 ac(0);
IRDaikin2 ac(kGpioUnused);
ac.setRaw(result->state);
return ac.toString();
}
#endif // DECODE_DAIKIN2
#if DECODE_DAIKIN216
case decode_type_t::DAIKIN216: {
IRDaikin216 ac(0);
IRDaikin216 ac(kGpioUnused);
ac.setRaw(result->state);
return ac.toString();
}
@ -2801,131 +2916,138 @@ namespace IRAcUtils {
#endif // DECODE_DELONGHI_AC
#if DECODE_ELECTRA_AC
case decode_type_t::ELECTRA_AC: {
IRElectraAc ac(0);
IRElectraAc ac(kGpioUnused);
ac.setRaw(result->state);
return ac.toString();
}
#endif // DECODE_ELECTRA_AC
#if DECODE_FUJITSU_AC
case decode_type_t::FUJITSU_AC: {
IRFujitsuAC ac(0);
IRFujitsuAC ac(kGpioUnused);
ac.setRaw(result->state, result->bits / 8);
return ac.toString();
}
#endif // DECODE_FUJITSU_AC
#if DECODE_KELVINATOR
case decode_type_t::KELVINATOR: {
IRKelvinatorAC ac(0);
IRKelvinatorAC ac(kGpioUnused);
ac.setRaw(result->state);
return ac.toString();
}
#endif // DECODE_KELVINATOR
#if DECODE_MITSUBISHI_AC
case decode_type_t::MITSUBISHI_AC: {
IRMitsubishiAC ac(0);
IRMitsubishiAC ac(kGpioUnused);
ac.setRaw(result->state);
return ac.toString();
}
#endif // DECODE_MITSUBISHI_AC
#if DECODE_MITSUBISHI112
case decode_type_t::MITSUBISHI112: {
IRMitsubishi112 ac(0);
IRMitsubishi112 ac(kGpioUnused);
ac.setRaw(result->state);
return ac.toString();
}
#endif // DECODE_MITSUBISHI112
#if DECODE_MITSUBISHI136
case decode_type_t::MITSUBISHI136: {
IRMitsubishi136 ac(0);
IRMitsubishi136 ac(kGpioUnused);
ac.setRaw(result->state);
return ac.toString();
}
#endif // DECODE_MITSUBISHI136
#if DECODE_MITSUBISHIHEAVY
case decode_type_t::MITSUBISHI_HEAVY_88: {
IRMitsubishiHeavy88Ac ac(0);
IRMitsubishiHeavy88Ac ac(kGpioUnused);
ac.setRaw(result->state);
return ac.toString();
}
case decode_type_t::MITSUBISHI_HEAVY_152: {
IRMitsubishiHeavy152Ac ac(0);
IRMitsubishiHeavy152Ac ac(kGpioUnused);
ac.setRaw(result->state);
return ac.toString();
}
#endif // DECODE_MITSUBISHIHEAVY
#if DECODE_NEOCLIMA
case decode_type_t::NEOCLIMA: {
IRNeoclimaAc ac(0);
IRNeoclimaAc ac(kGpioUnused);
ac.setRaw(result->state);
return ac.toString();
}
#endif // DECODE_NEOCLIMA
#if DECODE_TOSHIBA_AC
case decode_type_t::TOSHIBA_AC: {
IRToshibaAC ac(0);
IRToshibaAC ac(kGpioUnused);
ac.setRaw(result->state);
return ac.toString();
}
#endif // DECODE_TOSHIBA_AC
#if DECODE_TROTEC
case decode_type_t::TROTEC: {
IRTrotecESP ac(0);
IRTrotecESP ac(kGpioUnused);
ac.setRaw(result->state);
return ac.toString();
}
#endif // DECODE_TROTEC
#if DECODE_GOODWEATHER
case decode_type_t::GOODWEATHER: {
IRGoodweatherAc ac(0);
IRGoodweatherAc ac(kGpioUnused);
ac.setRaw(result->value); // Goodweather uses value instead of state.
return ac.toString();
}
#endif // DECODE_GOODWEATHER
#if DECODE_GREE
case decode_type_t::GREE: {
IRGreeAC ac(0);
IRGreeAC ac(kGpioUnused);
ac.setRaw(result->state);
return ac.toString();
}
#endif // DECODE_GREE
#if DECODE_MIDEA
case decode_type_t::MIDEA: {
IRMideaAC ac(0);
IRMideaAC ac(kGpioUnused);
ac.setRaw(result->value); // Midea uses value instead of state.
return ac.toString();
}
#endif // DECODE_MIDEA
#if DECODE_HAIER_AC
case decode_type_t::HAIER_AC: {
IRHaierAC ac(0);
IRHaierAC ac(kGpioUnused);
ac.setRaw(result->state);
return ac.toString();
}
#endif // DECODE_HAIER_AC
#if DECODE_HAIER_AC_YRW02
case decode_type_t::HAIER_AC_YRW02: {
IRHaierACYRW02 ac(0);
IRHaierACYRW02 ac(kGpioUnused);
ac.setRaw(result->state);
return ac.toString();
}
#endif // DECODE_HAIER_AC_YRW02
#if DECODE_SAMSUNG_AC
case decode_type_t::SAMSUNG_AC: {
IRSamsungAc ac(0);
IRSamsungAc ac(kGpioUnused);
ac.setRaw(result->state, result->bits / 8);
return ac.toString();
}
#endif // DECODE_SAMSUNG_AC
#if DECODE_SANYO_AC
case decode_type_t::SANYO_AC: {
IRSanyoAc ac(kGpioUnused);
ac.setRaw(result->state);
return ac.toString();
}
#endif // DECODE_SANYO_AC
#if DECODE_SHARP_AC
case decode_type_t::SHARP_AC: {
IRSharpAc ac(0);
IRSharpAc ac(kGpioUnused);
ac.setRaw(result->state);
return ac.toString();
}
#endif // DECODE_SHARP_AC
#if DECODE_COOLIX
case decode_type_t::COOLIX: {
IRCoolixAC ac(0);
IRCoolixAC ac(kGpioUnused);
ac.on();
ac.setRaw(result->value); // Coolix uses value instead of state.
return ac.toString();
@ -2941,7 +3063,7 @@ namespace IRAcUtils {
#if DECODE_PANASONIC_AC
case decode_type_t::PANASONIC_AC: {
if (result->bits > kPanasonicAcShortBits) {
IRPanasonicAc ac(0);
IRPanasonicAc ac(kGpioUnused);
ac.setRaw(result->state);
return ac.toString();
}
@ -2950,7 +3072,7 @@ namespace IRAcUtils {
#endif // DECODE_PANASONIC_AC
#if DECODE_HITACHI_AC
case decode_type_t::HITACHI_AC: {
IRHitachiAc ac(0);
IRHitachiAc ac(kGpioUnused);
ac.setRaw(result->state);
return ac.toString();
}
@ -2971,35 +3093,35 @@ namespace IRAcUtils {
#endif // DECODE_HITACHI_AC344
#if DECODE_HITACHI_AC424
case decode_type_t::HITACHI_AC424: {
IRHitachiAc424 ac(0);
IRHitachiAc424 ac(kGpioUnused);
ac.setRaw(result->state);
return ac.toString();
}
#endif // DECODE_HITACHI_AC424
#if DECODE_WHIRLPOOL_AC
case decode_type_t::WHIRLPOOL_AC: {
IRWhirlpoolAc ac(0);
IRWhirlpoolAc ac(kGpioUnused);
ac.setRaw(result->state);
return ac.toString();
}
#endif // DECODE_WHIRLPOOL_AC
#if DECODE_VESTEL_AC
case decode_type_t::VESTEL_AC: {
IRVestelAc ac(0);
IRVestelAc ac(kGpioUnused);
ac.setRaw(result->value); // Like Coolix, use value instead of state.
return ac.toString();
}
#endif // DECODE_VESTEL_AC
#if DECODE_TECO
case decode_type_t::TECO: {
IRTecoAc ac(0);
IRTecoAc ac(kGpioUnused);
ac.setRaw(result->value); // Like Coolix, use value instead of state.
return ac.toString();
}
#endif // DECODE_TECO
#if DECODE_TCL112AC
case decode_type_t::TCL112AC: {
IRTcl112Ac ac(0);
IRTcl112Ac ac(kGpioUnused);
ac.setRaw(result->state);
return ac.toString();
}
@ -3007,7 +3129,7 @@ namespace IRAcUtils {
#if DECODE_LG
case decode_type_t::LG:
case decode_type_t::LG2: {
IRLgAc ac(0);
IRLgAc ac(kGpioUnused);
ac.setRaw(result->value); // Like Coolix, use value instead of state.
switch (result->decode_type) {
case decode_type_t::LG2:
@ -3040,6 +3162,14 @@ namespace IRAcUtils {
) {
if (decode == NULL || result == NULL) return false; // Safety check.
switch (decode->decode_type) {
#if DECODE_AIRWELL
case decode_type_t::AIRWELL: {
IRAirwellAc ac(kGpioUnused);
ac.setRaw(decode->value); // Uses value instead of state.
*result = ac.toCommon();
break;
}
#endif // DECODE_AIRWELL
#if DECODE_AMCOR
case decode_type_t::AMCOR: {
IRAmcorAc ac(kGpioUnused);
@ -3090,7 +3220,7 @@ namespace IRAcUtils {
#endif // DECODE_DAIKIN
#if DECODE_DAIKIN128
case decode_type_t::DAIKIN128: {
IRDaikin128 ac(0);
IRDaikin128 ac(kGpioUnused);
ac.setRaw(decode->state);
*result = ac.toCommon();
break;
@ -3098,7 +3228,7 @@ namespace IRAcUtils {
#endif // DECODE_DAIKIN128
#if DECODE_DAIKIN152
case decode_type_t::DAIKIN152: {
IRDaikin152 ac(0);
IRDaikin152 ac(kGpioUnused);
ac.setRaw(decode->state);
*result = ac.toCommon();
break;
@ -3327,6 +3457,14 @@ namespace IRAcUtils {
break;
}
#endif // DECODE_SAMSUNG_AC
#if DECODE_SANYO_AC
case decode_type_t::SANYO_AC: {
IRSanyoAc ac(kGpioUnused);
ac.setRaw(decode->state);
*result = ac.toCommon();
break;
}
#endif // DECODE_SANYO_AC
#if DECODE_SHARP_AC
case decode_type_t::SHARP_AC: {
IRSharpAc ac(kGpioUnused);

View File

@ -7,6 +7,7 @@
#include <Arduino.h>
#endif
#include "IRremoteESP8266.h"
#include "ir_Airwell.h"
#include "ir_Amcor.h"
#include "ir_Argo.h"
#include "ir_Carrier.h"
@ -28,6 +29,7 @@
#include "ir_Neoclima.h"
#include "ir_Panasonic.h"
#include "ir_Samsung.h"
#include "ir_Sanyo.h"
#include "ir_Sharp.h"
#include "ir_Tcl.h"
#include "ir_Teco.h"
@ -37,9 +39,10 @@
#include "ir_Whirlpool.h"
// Constants
const int8_t kGpioUnused = -1;
const int8_t kGpioUnused = -1; ///< A placeholder for not using an actual GPIO.
// Class
/// A universal/common/generic interface for controling supported A/Cs.
class IRac {
public:
explicit IRac(const uint16_t pin, const bool inverted = false,
@ -93,10 +96,15 @@ class IRac {
private:
#endif
uint16_t _pin;
bool _inverted;
bool _modulation;
stdAc::state_t _prev; // The state we expect the device to currently be in.
uint16_t _pin; ///< The GPIO to use to transmit messages from.
bool _inverted; ///< IR LED is lit when GPIO is LOW (true) or HIGH (false)?
bool _modulation; ///< Is frequency modulation to be used?
stdAc::state_t _prev; ///< The state we expect the device to currently be in.
#if SEND_AIRWELL
void airwell(IRAirwellAc *ac,
const bool on, const stdAc::opmode_t mode, const float degrees,
const stdAc::fanspeed_t fan);
#endif // SEND_AIRWELL
#if SEND_AMCOR
void amcor(IRAmcorAc *ac,
const bool on, const stdAc::opmode_t mode, const float degrees,
@ -286,7 +294,8 @@ void electra(IRElectraAc *ac,
void midea(IRMideaAC *ac,
const bool on, const stdAc::opmode_t mode, const bool celsius,
const float degrees, const stdAc::fanspeed_t fan,
const stdAc::swingv_t swingv, const int16_t sleep = -1);
const stdAc::swingv_t swingv, const bool econo,
const int16_t sleep = -1);
#endif // SEND_MIDEA
#if SEND_MITSUBISHI_AC
void mitsubishi(IRMitsubishiAC *ac,
@ -350,6 +359,12 @@ void electra(IRElectraAc *ac,
const bool beep, const bool prevpower = true,
const bool forcepower = true);
#endif // SEND_SAMSUNG_AC
#if SEND_SANYO_AC
void sanyo(IRSanyoAc *ac,
const bool on, const stdAc::opmode_t mode, const float degrees,
const stdAc::fanspeed_t fan, const stdAc::swingv_t swingv,
const bool beep, const int16_t sleep = -1);
#endif // SEND_SANYO_AC
#if SEND_SHARP_AC
void sharp(IRSharpAc *ac,
const bool on, const bool prev_power, const stdAc::opmode_t mode,
@ -374,7 +389,8 @@ void electra(IRElectraAc *ac,
#if SEND_TOSHIBA_AC
void toshiba(IRToshibaAC *ac,
const bool on, const stdAc::opmode_t mode, const float degrees,
const stdAc::fanspeed_t fan);
const stdAc::fanspeed_t fan, const stdAc::swingv_t swingv,
const bool turbo, const bool econo);
#endif // SEND_TOSHIBA_AC
#if SEND_TROTEC
void trotec(IRTrotecESP *ac,
@ -401,6 +417,7 @@ static stdAc::state_t handleToggles(const stdAc::state_t desired,
const stdAc::state_t *prev = NULL);
}; // IRac class
/// Common functions for use with all A/Cs supported by the IRac class.
namespace IRAcUtils {
String resultAcToString(const decode_results * const results);
bool decodeToState(const decode_results *decode, stdAc::state_t *result,

View File

@ -614,8 +614,12 @@ bool IRrecv::decode(decode_results *results, irparams_t *save,
if (decodeDaikin216(results, offset)) return true;
#endif
#if DECODE_TOSHIBA_AC
DPRINTLN("Attempting Toshiba AC decode");
DPRINTLN("Attempting Toshiba AC 72bit decode");
if (decodeToshibaAC(results, offset)) return true;
DPRINTLN("Attempting Toshiba AC 80bit decode");
if (decodeToshibaAC(results, offset, kToshibaACBitsLong)) return true;
DPRINTLN("Attempting Toshiba AC 56bit decode");
if (decodeToshibaAC(results, offset, kToshibaACBitsShort)) return true;
#endif
#if DECODE_MIDEA
DPRINTLN("Attempting Midea decode");
@ -852,6 +856,10 @@ bool IRrecv::decode(decode_results *results, irparams_t *save,
DPRINTLN("Attempting Zepeal decode");
if (decodeZepeal(results, offset)) return true;
#endif // DECODE_ZEPEAL
#if DECODE_SANYO_AC
DPRINTLN("Attempting Sanyo AC decode");
if (decodeSanyoAc(results, offset)) return true;
#endif // DECODE_SANYO_AC
// Typically new protocols are added above this line.
}
#if DECODE_HASH

View File

@ -283,8 +283,14 @@ class IRrecv {
bool decodeSanyoLC7461(decode_results *results,
uint16_t offset = kStartOffset,
const uint16_t nbits = kSanyoLC7461Bits,
bool strict = true);
const bool strict = true);
#endif
#if DECODE_SANYO_AC
bool decodeSanyoAc(decode_results *results,
uint16_t offset = kStartOffset,
const uint16_t nbits = kSanyoAcBits,
const bool strict = true);
#endif // DECODE_SANYO_AC
#if DECODE_MITSUBISHI
bool decodeMitsubishi(decode_results *results, uint16_t offset = kStartOffset,
const uint16_t nbits = kMitsubishiBits,
@ -469,7 +475,7 @@ class IRrecv {
#endif
#if DECODE_TOSHIBA_AC
bool decodeToshibaAC(decode_results *results, uint16_t offset = kStartOffset,
const uint16_t nbytes = kToshibaACBits,
const uint16_t nbits = kToshibaACBits,
const bool strict = true);
#endif
#if DECODE_TROTEC

View File

@ -201,6 +201,13 @@
#define SEND_SANYO _IR_ENABLE_DEFAULT_
#endif // SEND_SANYO
#ifndef DECODE_SANYO_AC
#define DECODE_SANYO_AC _IR_ENABLE_DEFAULT_
#endif // DECODE_SANYO_AC
#ifndef SEND_SANYO_AC
#define SEND_SANYO_AC _IR_ENABLE_DEFAULT_
#endif // SEND_SANYO_AC
#ifndef DECODE_MITSUBISHI
#define DECODE_MITSUBISHI _IR_ENABLE_DEFAULT_
#endif // DECODE_MITSUBISHI
@ -674,7 +681,7 @@
DECODE_NEOCLIMA || DECODE_DAIKIN176 || DECODE_DAIKIN128 || \
DECODE_AMCOR || DECODE_DAIKIN152 || DECODE_MITSUBISHI136 || \
DECODE_MITSUBISHI112 || DECODE_HITACHI_AC424 || DECODE_HITACHI_AC3 || \
DECODE_HITACHI_AC344 || DECODE_CORONA_AC)
DECODE_HITACHI_AC344 || DECODE_CORONA_AC || DECODE_SANYO_AC)
// Add any DECODE to the above if it uses result->state (see kStateSizeMax)
// you might also want to add the protocol to hasACState function
#define DECODE_AC true // We need some common infrastructure for decoding A/Cs.
@ -802,8 +809,9 @@ enum decode_type_t {
CORONA_AC,
MIDEA24,
ZEPEAL,
SANYO_AC,
// Add new entries before this one, and update it to point to the last entry.
kLastDecodeType = ZEPEAL,
kLastDecodeType = SANYO_AC,
};
// Message lengths & required repeat values
@ -971,6 +979,8 @@ const uint16_t kSamsungAcBits = kSamsungAcStateLength * 8;
const uint16_t kSamsungAcExtendedStateLength = 21;
const uint16_t kSamsungAcExtendedBits = kSamsungAcExtendedStateLength * 8;
const uint16_t kSamsungAcDefaultRepeat = kNoRepeat;
const uint16_t kSanyoAcStateLength = 9;
const uint16_t kSanyoAcBits = kSanyoAcStateLength * 8;
const uint16_t kSanyoSA8650BBits = 12;
const uint16_t kSanyoLC7461AddressBits = 13;
const uint16_t kSanyoLC7461CommandBits = 8;
@ -999,6 +1009,10 @@ const uint16_t kTecoDefaultRepeat = kNoRepeat;
const uint16_t kToshibaACStateLength = 9;
const uint16_t kToshibaACBits = kToshibaACStateLength * 8;
const uint16_t kToshibaACMinRepeat = kSingleRepeat;
const uint16_t kToshibaACStateLengthShort = kToshibaACStateLength - 2;
const uint16_t kToshibaACBitsShort = kToshibaACStateLengthShort * 8;
const uint16_t kToshibaACStateLengthLong = kToshibaACStateLength + 1;
const uint16_t kToshibaACBitsLong = kToshibaACStateLengthLong * 8;
const uint16_t kTrotecStateLength = 9;
const uint16_t kTrotecBits = kTrotecStateLength * 8;
const uint16_t kTrotecDefaultRepeat = kNoRepeat;

View File

@ -721,6 +721,8 @@ uint16_t IRsend::defaultBits(const decode_type_t protocol) {
return kNeoclimaBits;
case SAMSUNG_AC:
return kSamsungAcBits;
case SANYO_AC:
return kSanyoAcBits;
case SHARP_AC:
return kSharpAcBits;
case TCL112AC:
@ -1152,6 +1154,11 @@ bool IRsend::send(const decode_type_t type, const uint8_t *state,
sendSamsungAC(state, nbytes);
break;
#endif // SEND_SAMSUNG_AC
#if SEND_SANYO_AC
case SANYO_AC:
sendSanyoAc(state, nbytes);
break;
#endif // SEND_SANYO_AC
#if SEND_SHARP_AC
case SHARP_AC:
sendSharpAc(state, nbytes);

View File

@ -294,6 +294,11 @@ class IRsend {
const uint16_t nbits = kSanyoLC7461Bits,
const uint16_t repeat = kNoRepeat);
#endif
#if SEND_SANYO_AC
void sendSanyoAc(const uint8_t *data,
const uint16_t nbytes = kSanyoAcStateLength,
const uint16_t repeat = kNoRepeat);
#endif // SEND_SANYO_AC
#if SEND_DISH
// sendDISH() should typically be called with repeat=3 as DISH devices
// expect the code to be sent at least 4 times. (code + 3 repeats = 4 codes)
@ -461,7 +466,7 @@ class IRsend {
uint16_t repeat = kNoRepeat);
#endif
#if SEND_TOSHIBA_AC
void sendToshibaAC(const unsigned char data[],
void sendToshibaAC(const uint8_t data[],
const uint16_t nbytes = kToshibaACStateLength,
const uint16_t repeat = kToshibaACMinRepeat);
#endif

View File

@ -130,6 +130,7 @@ const PROGMEM char* kTopStr = D_STR_TOP; ///< "Top"
const PROGMEM char* kBottomStr = D_STR_BOTTOM; ///< "Bottom"
// Compound words/phrases/descriptions from pre-defined words.
const PROGMEM char* kEconoToggleStr = D_STR_ECONOTOGGLE; ///< "Econo Toggle"
const PROGMEM char* kEyeAutoStr = D_STR_EYEAUTO; ///< "Eye Auto"
const PROGMEM char* kLightToggleStr = D_STR_LIGHTTOGGLE; ///< "Light Toggle"
const PROGMEM char* kOutsideQuietStr = D_STR_OUTSIDEQUIET; ///< "Outside Quiet"
@ -264,5 +265,6 @@ const PROGMEM char *kAllProtocolNamesStr =
D_STR_CORONA_AC "\x0"
D_STR_MIDEA24 "\x0"
D_STR_ZEPEAL "\x0"
D_STR_SANYO_AC "\x0"
///< New protocol strings should be added just above this line.
"\x0"; ///< This string requires double null termination.

View File

@ -45,6 +45,7 @@ extern const char* kDisplayTempStr;
extern const char* kDownStr;
extern const char* kDryStr;
extern const char* kEconoStr;
extern const char* kEconoToggleStr;
extern const char* kEyeAutoStr;
extern const char* kEyeStr;
extern const char* kFalseStr;
@ -104,10 +105,10 @@ extern const char* kOnStr;
extern const char* kOnTimerStr;
extern const char* kOutsideQuietStr;
extern const char* kOutsideStr;
extern const char* kPowerButtonStr;
extern const char* kPowerfulStr;
extern const char* kPowerStr;
extern const char* kPowerToggleStr;
extern const char* kPowerButtonStr;
extern const char* kPreviousPowerStr;
extern const char* kProtocolStr;
extern const char* kPurifyStr;

View File

@ -14,7 +14,7 @@ uint32_t _TimerMs_unittest_now = 0;
/// Class constructor.
IRtimer::IRtimer() { reset(); }
/// Resets the IRtimer object.
/// Resets the IRtimer object. I.e. The counter starts again from now.
void IRtimer::reset() {
#ifndef UNIT_TEST
start = micros();
@ -47,7 +47,7 @@ void IRtimer::add(uint32_t usecs) { _IRtimer_unittest_now += usecs; }
/// Class constructor.
TimerMs::TimerMs() { reset(); }
/// Resets the TimerMs object.
/// Resets the TimerMs object. I.e. The counter starts again from now.
void TimerMs::reset() {
#ifndef UNIT_TEST
start = millis();

View File

@ -8,7 +8,7 @@
// Classes
/// This class performs a simple timer in useconds since instantiated.
/// This class offers a simple counter in micro-seconds since instantiated.
/// @note Handles when the system timer wraps around (once).
class IRtimer {
public:
@ -20,10 +20,10 @@ class IRtimer {
#endif // UNIT_TEST
private:
uint32_t start;
uint32_t start; ///< Time in uSeconds when the class was instantiated/reset.
};
/// This class performs a simple timer in milli-seoncds since instantiated.
/// This class offers a simple counter in milli-seconds since instantiated.
/// @note Handles when the system timer wraps around (once).
class TimerMs {
public:
@ -35,6 +35,6 @@ class TimerMs {
#endif // UNIT_TEST
private:
uint32_t start;
uint32_t start; ///< Time in mSeconds when the class was instantiated/reset.
};
#endif // IRTIMER_H_

View File

@ -161,6 +161,7 @@ bool hasACState(const decode_type_t protocol) {
case NEOCLIMA:
case PANASONIC_AC:
case SAMSUNG_AC:
case SANYO_AC:
case SHARP_AC:
case TCL112AC:
case TOSHIBA_AC:
@ -915,4 +916,35 @@ namespace irutils {
// Merge in the data.
*dst |= ((data & mask) << offset);
}
/// Create byte pairs where the second byte of the pair is a bit
/// inverted/flipped copy of the first/previous byte of the pair.
/// @param[in,out] ptr A pointer to the start of array to modify.
/// @param[in] length The byte size of the array.
/// @note A length of `<= 1` will do nothing.
/// @return A ptr to the modified array.
uint8_t * invertBytePairs(uint8_t *ptr, const uint16_t length) {
for (uint16_t i = 1; i < length; i += 2) {
// Code done this way to avoid a compiler warning bug.
uint8_t inv = ~*(ptr + i - 1);
*(ptr + i) = inv;
}
return ptr;
}
/// Check an array to see if every second byte of a pair is a bit
/// inverted/flipped copy of the first/previous byte of the pair.
/// @param[in] ptr A pointer to the start of array to check.
/// @param[in] length The byte size of the array.
/// @note A length of `<= 1` will always return true.
/// @return true, if every second byte is inverted. Otherwise false.
bool checkInvertedBytePairs(const uint8_t * const ptr,
const uint16_t length) {
for (uint16_t i = 1; i < length; i += 2) {
// Code done this way to avoid a compiler warning bug.
uint8_t inv = ~*(ptr + i - 1);
if (*(ptr + i) != inv) return false;
}
return true;
}
} // namespace irutils

View File

@ -107,5 +107,7 @@ namespace irutils {
const uint32_t data);
void setBits(uint64_t * const dst, const uint8_t offset, const uint8_t nbits,
const uint64_t data);
uint8_t * invertBytePairs(uint8_t *ptr, const uint16_t length);
bool checkInvertedBytePairs(const uint8_t * const ptr, const uint16_t length);
} // namespace irutils
#endif // IRUTILS_H_

View File

@ -1,23 +1,28 @@
// Copyright 2020 David Conran
#include "ir_Airwell.h"
#include <algorithm>
#include "IRrecv.h"
#include "IRsend.h"
#include "IRtext.h"
#include "IRutils.h"
/// @file
/// @brief Airwell "Manchester code" based protocol.
/// Some other Airwell products use the COOLIX protocol.
// Supports:
// Brand: Airwell, Model: RC08W remote
// Brand: Airwell, Model: RC04 remote
// Brand: Airwell, Model: DLS 21 DCI R410 AW A/C
const uint8_t kAirwellOverhead = 4;
const uint16_t kAirwellHalfClockPeriod = 950; // uSeconds
const uint16_t kAirwellHdrMark = 3 * kAirwellHalfClockPeriod; // uSeconds
const uint16_t kAirwellHdrSpace = 3 * kAirwellHalfClockPeriod; // uSeconds
const uint16_t kAirwellFooterMark = 5 * kAirwellHalfClockPeriod; // uSeconds
using irutils::addBoolToString;
using irutils::addModeToString;
using irutils::addFanToString;
using irutils::addTempToString;
using irutils::setBit;
using irutils::setBits;
#if SEND_AIRWELL
/// Send an Airwell Manchester Code formatted message.
/// Status: BETA / Appears to be working.
@ -74,3 +79,203 @@ bool IRrecv::decodeAirwell(decode_results *results, uint16_t offset,
return true;
}
#endif
/// 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?
IRAirwellAc::IRAirwellAc(const uint16_t pin, const bool inverted,
const bool use_modulation)
: _irsend(pin, inverted, use_modulation) { stateReset(); }
/// Set up hardware to be able to send a message.
void IRAirwellAc::begin(void) { _irsend.begin(); }
/// Get the raw state of the object, suitable to be sent with the appropriate
/// IRsend object method.
/// @return A copy of the internal state.
uint64_t IRAirwellAc::getRaw(void) {
return remote_state;
}
/// Set the raw state of the object.
/// @param[in] state The raw state from the native IR message.
void IRAirwellAc::setRaw(const uint64_t state) {
remote_state = state;
}
#if SEND_AIRWELL
/// Send the current internal state as an IR message.
/// @param[in] repeat Nr. of times the message will be repeated.
void IRAirwellAc::send(const uint16_t repeat) {
_irsend.sendAirwell(getRaw(), kAirwellBits, repeat);
}
#endif // SEND_AIRWELL
/// Reset the internals of the object to a known good state.
void IRAirwellAc::stateReset(void) {
remote_state = kAirwellKnownGoodState;
}
/// Turn on/off the Power Airwell setting.
/// @param[in] on The desired setting state.
void IRAirwellAc::setPowerToggle(const bool on) {
setBit(&remote_state, kAirwellPowerToggleBit, on);
}
/// Get the power toggle setting from the internal state.
/// @return A boolean indicating the setting.
bool IRAirwellAc::getPowerToggle(void) {
return GETBIT64(remote_state, kAirwellPowerToggleBit);
}
/// Get the current operation mode setting.
/// @return The current operation mode.
uint8_t IRAirwellAc::getMode(void) {
return GETBITS64(remote_state, kAirwellModeOffset, kAirwellModeSize);
}
/// Set the desired operation mode.
/// @param[in] mode The desired operation mode.
void IRAirwellAc::setMode(const uint8_t mode) {
switch (mode) {
case kAirwellFan:
case kAirwellCool:
case kAirwellHeat:
case kAirwellDry:
case kAirwellAuto:
setBits(&remote_state, kAirwellModeOffset, kAirwellModeSize, mode);
break;
default:
setMode(kAirwellAuto);
}
setFan(getFan()); // Ensure the fan is at the correct speed for the new mode.
}
/// Convert a stdAc::opmode_t enum into its native mode.
/// @param[in] mode The enum to be converted.
/// @return The native equivilant of the enum.
uint8_t IRAirwellAc::convertMode(const stdAc::opmode_t mode) {
switch (mode) {
case stdAc::opmode_t::kCool: return kAirwellCool;
case stdAc::opmode_t::kHeat: return kAirwellHeat;
case stdAc::opmode_t::kDry: return kAirwellDry;
case stdAc::opmode_t::kFan: return kAirwellFan;
default: return kAirwellAuto;
}
}
/// Convert a native mode into its stdAc equivilant.
/// @param[in] mode The native setting to be converted.
/// @return The stdAc equivilant of the native setting.
stdAc::opmode_t IRAirwellAc::toCommonMode(const uint8_t mode) {
switch (mode) {
case kAirwellCool: return stdAc::opmode_t::kCool;
case kAirwellHeat: return stdAc::opmode_t::kHeat;
case kAirwellDry: return stdAc::opmode_t::kDry;
case kAirwellFan: return stdAc::opmode_t::kFan;
default: return stdAc::opmode_t::kAuto;
}
}
/// Set the speed of the fan.
/// @param[in] speed The desired setting.
/// @note The speed is locked to Low when in Dry mode.
void IRAirwellAc::setFan(const uint8_t speed) {
setBits(&remote_state, kAirwellFanOffset, kAirwellFanSize,
(getMode() == kAirwellDry) ? kAirwellFanLow
: std::min(speed, kAirwellFanAuto));
}
/// Get the current fan speed setting.
/// @return The current fan speed.
uint8_t IRAirwellAc::getFan(void) {
return GETBITS64(remote_state, kAirwellFanOffset, kAirwellFanSize);
}
/// Convert a stdAc::fanspeed_t enum into it's native speed.
/// @param[in] speed The enum to be converted.
/// @return The native equivilant of the enum.
uint8_t IRAirwellAc::convertFan(const stdAc::fanspeed_t speed) {
switch (speed) {
case stdAc::fanspeed_t::kMin:
case stdAc::fanspeed_t::kLow:
return kAirwellFanLow;
case stdAc::fanspeed_t::kMedium:
return kAirwellFanMedium;
case stdAc::fanspeed_t::kHigh:
case stdAc::fanspeed_t::kMax:
return kAirwellFanHigh;
default:
return kAirwellFanAuto;
}
}
/// Convert a native fan speed into its stdAc equivilant.
/// @param[in] speed The native setting to be converted.
/// @return The stdAc equivilant of the native setting.
stdAc::fanspeed_t IRAirwellAc::toCommonFanSpeed(const uint8_t speed) {
switch (speed) {
case kAirwellFanHigh: return stdAc::fanspeed_t::kMax;
case kAirwellFanMedium: return stdAc::fanspeed_t::kMedium;
case kAirwellFanLow: return stdAc::fanspeed_t::kMin;
default: return stdAc::fanspeed_t::kAuto;
}
}
/// Set the temperature.
/// @param[in] degrees The temperature in degrees celsius.
void IRAirwellAc::setTemp(const uint8_t degrees) {
uint8_t temp = std::max(kAirwellMinTemp, degrees);
temp = std::min(kAirwellMaxTemp, temp);
setBits(&remote_state, kAirwellTempOffset, kAirwellTempSize,
temp - kAirwellMinTemp + 1);
}
/// Get the current temperature setting.
/// @return Get current setting for temp. in degrees celsius.
uint8_t IRAirwellAc::getTemp(void) {
return GETBITS64(remote_state, kAirwellTempOffset,
kAirwellTempSize) + kAirwellMinTemp - 1;
}
/// Convert the current internal state into its stdAc::state_t equivilant.
/// @return The stdAc equivilant of the native settings.
stdAc::state_t IRAirwellAc::toCommon(void) {
stdAc::state_t result;
result.protocol = decode_type_t::AIRWELL;
result.power = getPowerToggle();
result.mode = toCommonMode(getMode());
result.celsius = true;
result.degrees = getTemp();
result.fanspeed = toCommonFanSpeed(getFan());
// Not supported.
result.model = -1;
result.turbo = false;
result.swingv = stdAc::swingv_t::kOff;
result.swingh = stdAc::swingh_t::kOff;
result.light = false;
result.filter = false;
result.econo = false;
result.quiet = false;
result.clean = false;
result.beep = false;
result.sleep = -1;
result.clock = -1;
return result;
}
/// Convert the current internal state into a human readable string.
/// @return A human readable string.
String IRAirwellAc::toString(void) {
String result = "";
result.reserve(70); // Reserve some heap for the string to reduce fragging.
result += addBoolToString(getPowerToggle(), kPowerToggleStr, false);
result += addModeToString(getMode(), kAirwellAuto, kAirwellCool,
kAirwellHeat, kAirwellDry, kAirwellFan);
result += addFanToString(getFan(), kAirwellFanHigh, kAirwellFanLow,
kAirwellFanAuto, kAirwellFanAuto,
kAirwellFanMedium);
result += addTempToString(getTemp());
return result;
}

View File

@ -0,0 +1,97 @@
// Copyright 2020 David Conran
/// @file
/// @brief Airwell "Manchester code" based protocol.
/// Some other Airwell products use the COOLIX protocol.
// Supports:
// Brand: Airwell, Model: RC08W remote
// Brand: Airwell, Model: RC04 remote
// Brand: Airwell, Model: DLS 21 DCI R410 AW A/C
#ifndef IR_AIRWELL_H_
#define IR_AIRWELL_H_
#define __STDC_LIMIT_MACROS
#include <stdint.h>
#ifndef UNIT_TEST
#include <Arduino.h>
#endif
#include "IRremoteESP8266.h"
#include "IRsend.h"
#ifdef UNIT_TEST
#include "IRsend_test.h"
#endif
// Constants
const uint64_t kAirwellKnownGoodState = 0x140500002; // Mode Fan, Speed 1, 25C
// Temperature
const uint8_t kAirwellMinTemp = 16; // Celsius
const uint8_t kAirwellMaxTemp = 30; // Celsius
const uint8_t kAirwellTempSize = 4; // Bits
const uint8_t kAirwellTempOffset = 19; // 0b1111 << 19
// Fan
const uint8_t kAirwellFanSize = 2; // Bits
const uint8_t kAirwellFanOffset = 28; // 0b11 << 28
const uint8_t kAirwellFanLow = 0; // 0b00
const uint8_t kAirwellFanMedium = 1; // 0b01
const uint8_t kAirwellFanHigh = 2; // 0b10
const uint8_t kAirwellFanAuto = 3; // 0b11
// Modes
const uint8_t kAirwellModeSize = 3; // Bits
const uint8_t kAirwellModeOffset = 30; // 0b111 << 30
const uint8_t kAirwellCool = 1; // 0b001
const uint8_t kAirwellHeat = 2; // 0b010
const uint8_t kAirwellAuto = 3; // 0b011
const uint8_t kAirwellDry = 4; // 0b100
const uint8_t kAirwellFan = 5; // 0b101
// Power
const uint8_t kAirwellPowerToggleBit = 33; // 0b1 << 33
// Classes
/// Class for handling detailed Airwell A/C messages.
class IRAirwellAc {
public:
explicit IRAirwellAc(const uint16_t pin, const bool inverted = false,
const bool use_modulation = true);
void stateReset();
#if SEND_AIRWELL
void send(const uint16_t repeat = kAirwellMinRepeats);
/// Run the calibration to calculate uSec timing offsets for this platform.
/// @return The uSec timing offset needed per modulation of the IR Led.
/// @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_AIRWELL
void begin();
void setPowerToggle(const bool on);
bool getPowerToggle();
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();
uint64_t getRaw();
void setRaw(const uint64_t state);
uint8_t convertMode(const stdAc::opmode_t mode);
uint8_t convertFan(const stdAc::fanspeed_t speed);
static stdAc::opmode_t toCommonMode(const uint8_t mode);
static stdAc::fanspeed_t toCommonFanSpeed(const uint8_t speed);
stdAc::state_t toCommon(void);
String toString();
#ifndef UNIT_TEST
private:
IRsend _irsend; ///< Instance of the IR send class
#else
/// @cond IGNORE
IRsendTest _irsend; ///< Instance of the testing IR send class
/// @endcond
#endif
uint64_t remote_state; // The state of the IR remote in native IR code form.
void checksum(void);
};
#endif // IR_AIRWELL_H_

View File

@ -598,7 +598,7 @@ class IRDaikinESP {
};
/// Class for handling detailed Daikin 312-bit A/C messages.
/// Code by crankyoldgit, Reverse engineering analysis by sheppy99
/// @note Code by crankyoldgit, Reverse engineering analysis by sheppy99
class IRDaikin2 {
public:
explicit IRDaikin2(const uint16_t pin, const bool inverted = false,
@ -859,8 +859,7 @@ class IRDaikin176 {
};
/// Class for handling detailed Daikin 128-bit A/C messages.
/// Code by crankyoldgit.
/// Analysis by Daniel Vena
/// @note Code by crankyoldgit. Analysis by Daniel Vena
class IRDaikin128 {
public:
explicit IRDaikin128(const uint16_t pin, const bool inverted = false,

View File

@ -52,6 +52,8 @@ using irutils::addModeToString;
using irutils::addModelToString;
using irutils::addFanToString;
using irutils::addTempToString;
using irutils::checkInvertedBytePairs;
using irutils::invertBytePairs;
using irutils::minsToString;
using irutils::setBit;
using irutils::setBits;
@ -1047,8 +1049,7 @@ void IRHitachiAc424::stateReset(void) {
/// Update the internal consistency check for the protocol.
void IRHitachiAc424::setInvertedStates(void) {
for (uint8_t i = 3; i < kHitachiAc424StateLength - 1; i += 2)
remote_state[i + 1] = ~remote_state[i];
invertBytePairs(remote_state + 3, kHitachiAc424StateLength - 3);
}
/// Set up hardware to be able to send a message.
@ -1402,8 +1403,7 @@ void IRHitachiAc3::stateReset(void) {
/// @param[in] length The size of the state array.
/// @note This is this protocols integrity check.
void IRHitachiAc3::setInvertedStates(const uint16_t length) {
for (uint8_t i = 3; i < length - 1; i += 2)
remote_state[i + 1] = ~remote_state[i];
if (length > 3) invertBytePairs(remote_state + 3, length - 3);
}
/// Check if every second byte of the state, after the fixed header
@ -1413,9 +1413,7 @@ void IRHitachiAc3::setInvertedStates(const uint16_t length) {
/// @note This is this protocols integrity check.
bool IRHitachiAc3::hasInvertedStates(const uint8_t state[],
const uint16_t length) {
for (uint8_t i = 3; i < length - 1; i += 2)
if ((state[i + 1] ^ state[i]) != 0xFF) return false;
return true;
return (length <= 3 || checkInvertedBytePairs(state + 3, length - 3));
}
/// Set up hardware to be able to send a message.

View File

@ -400,6 +400,7 @@ uint8_t IRLgAc::getTemp(void) {
void IRLgAc::setFan(const uint8_t speed) {
switch (speed) {
case kLgAcFanAuto:
case kLgAcFanLowest:
case kLgAcFanLow:
case kLgAcFanMedium:
case kLgAcFanHigh:
@ -469,12 +470,12 @@ stdAc::opmode_t IRLgAc::toCommonMode(const uint8_t mode) {
/// @return The native equivilant of the enum.
uint8_t IRLgAc::convertFan(const stdAc::fanspeed_t speed) {
switch (speed) {
case stdAc::fanspeed_t::kMin:
case stdAc::fanspeed_t::kMin: return kLgAcFanLowest;
case stdAc::fanspeed_t::kLow: return kLgAcFanLow;
case stdAc::fanspeed_t::kMedium: return kLgAcFanMedium;
case stdAc::fanspeed_t::kHigh:
case stdAc::fanspeed_t::kMax: return kHitachiAcFanHigh;
default: return kHitachiAcFanAuto;
case stdAc::fanspeed_t::kMax: return kLgAcFanHigh;
default: return kLgAcFanAuto;
}
}
@ -486,6 +487,7 @@ stdAc::fanspeed_t IRLgAc::toCommonFanSpeed(const uint8_t speed) {
case kLgAcFanHigh: return stdAc::fanspeed_t::kMax;
case kLgAcFanMedium: return stdAc::fanspeed_t::kMedium;
case kLgAcFanLow: return stdAc::fanspeed_t::kLow;
case kLgAcFanLowest: return stdAc::fanspeed_t::kMin;
default: return stdAc::fanspeed_t::kAuto;
}
}
@ -528,7 +530,7 @@ String IRLgAc::toString(void) {
kLgAcHeat, kLgAcDry, kLgAcFan);
result += addTempToString(getTemp());
result += addFanToString(getFan(), kLgAcFanHigh, kLgAcFanLow,
kLgAcFanAuto, kLgAcFanAuto, kLgAcFanMedium);
kLgAcFanAuto, kLgAcFanLowest, kLgAcFanMedium);
}
return result;
}

View File

@ -32,7 +32,8 @@ const uint8_t kLgAcChecksumOffset = 0; // Nr. of bits
const uint8_t kLgAcChecksumSize = kNibbleSize; // Nr. of bits
const uint8_t kLgAcFanOffset = 4; // Nr. of bits
const uint8_t kLgAcFanSize = 3; // Nr. of bits
const uint8_t kLgAcFanLow = 0; // 0b000
const uint8_t kLgAcFanLowest = 0; // 0b000
const uint8_t kLgAcFanLow = 1; // 0b001
const uint8_t kLgAcFanMedium = 2; // 0b010
const uint8_t kLgAcFanHigh = 4; // 0b100
const uint8_t kLgAcFanAuto = 5; // 0b101

View File

@ -3,7 +3,9 @@
/// @brief Support for Midea protocols.
/// Midea added by crankyoldgit & bwze.
/// send: bwze/crankyoldgit, decode: crankyoldgit
/// @note SwingV has the function of an Ion Filter on Danby A/C units.
/// @see https://docs.google.com/spreadsheets/d/1TZh4jWrx4h9zzpYUI9aYXMl1fYOiqu-xVuOOMqagxrs/edit?usp=sharing
/// @see https://github.com/crankyoldgit/IRremoteESP8266/pull/1213
#include "ir_Midea.h"
#include "ir_NEC.h"
@ -99,6 +101,7 @@ void IRMideaAC::stateReset(void) {
// Power On, Mode Auto, Fan Auto, Temp = 25C/77F
remote_state = 0xA1826FFFFF62;
_SwingVToggle = false;
_EconoToggle = false;
}
/// Set up hardware to be able to send a message.
@ -110,11 +113,14 @@ void IRMideaAC::begin(void) { _irsend.begin(); }
void IRMideaAC::send(const uint16_t repeat) {
this->checksum(); // Ensure correct checksum before sending.
_irsend.sendMidea(remote_state, kMideaBits, repeat);
// Handle toggling the swing if we need to.
if (_SwingVToggle && !isSwingVToggle()) {
// Handle toggling the swing & econo mode if we need to.
if (_SwingVToggle && !isSwingVToggle())
_irsend.sendMidea(kMideaACToggleSwingV, kMideaBits, repeat);
}
_SwingVToggle = false; // The toggle message has been sent, so reset.
if (_EconoToggle && !isEconoToggle())
_irsend.sendMidea(kMideaACToggleEcono, kMideaBits, repeat);
// The toggle messages has been sent, so reset.
_SwingVToggle = false;
_EconoToggle = false;
}
#endif // SEND_MIDEA
@ -246,22 +252,42 @@ bool IRMideaAC::getSleep(void) {
}
/// Set the A/C to toggle the vertical swing toggle for the next send.
/// @note On Danby A/C units, this is associated with the Ion Filter instead.
/// @param[in] on true, the setting is on. false, the setting is off.
void IRMideaAC::setSwingVToggle(const bool on) { _SwingVToggle = on; }
/// Is the current state a vertical swing toggle message?
/// @note On Danby A/C units, this is associated with the Ion Filter instead.
/// @return true, it is. false, it isn't.
bool IRMideaAC::isSwingVToggle(void) {
return remote_state == kMideaACToggleSwingV;
}
// Get the vertical swing toggle state of the A/C.
/// @note On Danby A/C units, this is associated with the Ion Filter instead.
/// @return true, the setting is on. false, the setting is off.
bool IRMideaAC::getSwingVToggle(void) {
_SwingVToggle |= isSwingVToggle();
return _SwingVToggle;
}
/// Set the A/C to toggle the Econo (energy saver) mode for the next send.
/// @param[in] on true, the setting is on. false, the setting is off.
void IRMideaAC::setEconoToggle(const bool on) { _EconoToggle = on; }
/// Is the current state an Econo (energy saver) toggle message?
/// @return true, it is. false, it isn't.
bool IRMideaAC::isEconoToggle(void) {
return remote_state == kMideaACToggleEcono;
}
// Get the Econo (energy saver) toggle state of the A/C.
/// @return true, the setting is on. false, the setting is off.
bool IRMideaAC::getEconoToggle(void) {
_EconoToggle |= isEconoToggle();
return _EconoToggle;
}
/// Calculate the checksum for a given state.
/// @param[in] state The value to calc the checksum of.
/// @return The calculated checksum value.
@ -376,6 +402,7 @@ stdAc::state_t IRMideaAC::toCommon(const stdAc::state_t *prev) {
result.degrees = this->getTemp(result.celsius);
result.fanspeed = this->toCommonFanSpeed(this->getFan());
result.sleep = this->getSleep() ? 0 : -1;
result.econo = this->getEconoToggle();
return result;
}
@ -384,7 +411,8 @@ stdAc::state_t IRMideaAC::toCommon(const stdAc::state_t *prev) {
String IRMideaAC::toString(void) {
String result = "";
result.reserve(100); // Reserve some heap for the string to reduce fragging.
if (!isSwingVToggle()) {
bool needComma = false;
if (!isSwingVToggle() && !isEconoToggle()) {
result += addBoolToString(getPower(), kPowerStr, false);
result += addModeToString(getMode(), kMideaACAuto, kMideaACCool,
kMideaACHeat, kMideaACDry, kMideaACFan);
@ -396,9 +424,10 @@ String IRMideaAC::toString(void) {
result += addFanToString(getFan(), kMideaACFanHigh, kMideaACFanLow,
kMideaACFanAuto, kMideaACFanAuto, kMideaACFanMed);
result += addBoolToString(getSleep(), kSleepStr);
needComma = true;
}
result += addBoolToString(getSwingVToggle(), kSwingVToggleStr,
!isSwingVToggle());
result += addBoolToString(getSwingVToggle(), kSwingVToggleStr, needComma);
result += addBoolToString(getEconoToggle(), kEconoToggleStr);
return result;
}

View File

@ -11,6 +11,10 @@
// Brand: Comfee, Model: MPD1-12CRN7 A/C (MIDEA)
// Brand: Keystone, Model: RG57H4(B)BGEF remote (MIDEA)
// Brand: Midea, Model: FS40-7AR Stand Fan (MIDEA24)
// Brand: Danby, Model: DAC080BGUWDB (MIDEA)
// Brand: Danby, Model: DAC100BGUWDB (MIDEA)
// Brand: Danby, Model: DAC120BGUWDB (MIDEA)
// Brand: Danby, Model: R09C/BCGE remote (MIDEA)
#ifndef IR_MIDEA_H_
#define IR_MIDEA_H_
@ -26,6 +30,10 @@
#include "IRsend_test.h"
#endif
#if DANBY_DAC
kSwingVToggleStr = kIonStr;
#endif
// Constants
const uint8_t kMideaACTempOffset = 24;
const uint8_t kMideaACTempSize = 5; // Bits
@ -49,6 +57,9 @@ const uint8_t kMideaACFanHigh = 3; // 0b11
const uint8_t kMideaACSleepOffset = 38;
const uint8_t kMideaACPowerOffset = 39;
const uint64_t kMideaACToggleSwingV = 0x0000A201FFFFFF7C;
// For Danby DAC unit, the Ionizer toggle is the same as ToggleSwingV
// const uint64_t kMideaACToggleIonizer = 0x0000A201FFFFFF7C;
const uint64_t kMideaACToggleEcono = 0x0000A202FFFFFF7E;
// Legacy defines. (Deprecated)
#define MIDEA_AC_COOL kMideaACCool
@ -104,6 +115,9 @@ class IRMideaAC {
bool isSwingVToggle(void);
void setSwingVToggle(const bool on);
bool getSwingVToggle(void);
bool isEconoToggle(void);
void setEconoToggle(const bool on);
bool getEconoToggle(void);
uint8_t convertMode(const stdAc::opmode_t mode);
uint8_t convertFan(const stdAc::fanspeed_t speed);
static stdAc::opmode_t toCommonMode(const uint8_t mode);
@ -121,6 +135,7 @@ class IRMideaAC {
#endif // UNIT_TEST
uint64_t remote_state; ///< The state of the IR remote in IR code form.
bool _SwingVToggle;
bool _EconoToggle;
void checksum(void);
static uint8_t calcChecksum(const uint64_t state);
};

View File

@ -163,7 +163,7 @@ const uint8_t kMitsubishi112SwingHAuto = 0b1100;
/// Class for handling detailed Mitsubishi 144-bit A/C messages.
/// Inspired and derived from the work done at: https://github.com/r45635/HVAC-IR-Control
/// @note 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.
class IRMitsubishiAC {
public:
@ -282,7 +282,7 @@ class IRMitsubishi136 {
void checksum(void);
};
/// Class for handling detailed Mitsubishi 122-bit A/C messages.
class IRMitsubishi112 {
public:
explicit IRMitsubishi112(const uint16_t pin, const bool inverted = false,

View File

@ -33,6 +33,8 @@ using irutils::addIntToString;
using irutils::addLabeledString;
using irutils::addModeToString;
using irutils::addTempToString;
using irutils::checkInvertedBytePairs;
using irutils::invertBytePairs;
using irutils::setBit;
using irutils::setBits;
@ -321,32 +323,24 @@ bool IRMitsubishiHeavy152Ac::checkZmsSig(const uint8_t *state) {
}
/// Calculate the checksum for the current internal state of the remote.
/// Note: Technically it has no checksum, but does has inverted byte pairs.
/// Note: Technically it has no checksum, but does have inverted byte pairs.
void IRMitsubishiHeavy152Ac::checksum(void) {
for (uint8_t i = kMitsubishiHeavySigLength - 2;
i < kMitsubishiHeavy152StateLength;
i += 2) {
remote_state[i + 1] = ~remote_state[i];
}
const uint8_t kOffset = kMitsubishiHeavySigLength - 2;
invertBytePairs(remote_state + kOffset,
kMitsubishiHeavy152StateLength - kOffset);
}
/// Verify the checksum is valid for a given state.
/// @param[in] state The array to verify the checksum of.
/// @param[in] length The length/size of the state array.
/// @return true, if the state has a valid checksum. Otherwise, false.
/// Note: Technically it has no checksum, but does has inverted byte pairs.
/// Note: Technically it has no checksum, but does have inverted byte pairs.
bool IRMitsubishiHeavy152Ac::validChecksum(const uint8_t *state,
const uint16_t length) {
// Assume anything too short is fine.
if (length < kMitsubishiHeavySigLength) return true;
// Check all the byte pairs.
for (uint16_t i = kMitsubishiHeavySigLength - 2;
i < length;
i += 2) {
// XOR of a byte and it's self inverted should be 0xFF;
if ((state[i] ^ state[i + 1]) != 0xFF) return false;
}
return true;
const uint8_t kOffset = kMitsubishiHeavySigLength - 2;
return checkInvertedBytePairs(state + kOffset, length - kOffset);
}
/// Convert a stdAc::opmode_t enum into its native mode.
@ -856,20 +850,18 @@ bool IRMitsubishiHeavy88Ac::checkZjsSig(const uint8_t *state) {
}
/// Calculate the checksum for the current internal state of the remote.
/// Note: Technically it has no checksum, but does has inverted byte pairs.
/// Note: Technically it has no checksum, but does have inverted byte pairs.
void IRMitsubishiHeavy88Ac::checksum(void) {
for (uint8_t i = kMitsubishiHeavySigLength - 2;
i < kMitsubishiHeavy88StateLength;
i += 2) {
remote_state[i + 1] = ~remote_state[i];
}
const uint8_t kOffset = kMitsubishiHeavySigLength - 2;
invertBytePairs(remote_state + kOffset,
kMitsubishiHeavy88StateLength - kOffset);
}
/// Verify the checksum is valid for a given state.
/// @param[in] state The array to verify the checksum of.
/// @param[in] length The length/size of the state array.
/// @return true, if the state has a valid checksum. Otherwise, false.
/// Note: Technically it has no checksum, but does has inverted byte pairs.
/// Note: Technically it has no checksum, but does have inverted byte pairs.
bool IRMitsubishiHeavy88Ac::validChecksum(const uint8_t *state,
const uint16_t length) {
return IRMitsubishiHeavy152Ac::validChecksum(state, length);

View File

@ -8,9 +8,12 @@
/// @see http://www.adrian-kingston.com/IRFormatPioneer.htm
/// @see https://github.com/crankyoldgit/IRremoteESP8266/pull/547
/// @see https://www.pioneerelectronics.com/PUSA/Support/Home-Entertainment-Custom-Install/IR+Codes/A+V+Receivers
/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1220
// Supports:
// Brand: Pioneer, Model: AV Receivers
// Brand: Pioneer, Model: VSX-324 AV Receiver
// Brand: Pioneer, Model: AXD7690 Remote
#define __STDC_LIMIT_MACROS
#include <stdint.h>
@ -20,22 +23,15 @@
#include "IRutils.h"
// Constants
const uint16_t kPioneerTick = 534;
const uint16_t kPioneerHdrMarkTicks = 16;
const uint16_t kPioneerHdrMark = kPioneerHdrMarkTicks * kPioneerTick;
const uint16_t kPioneerHdrSpaceTicks = 8;
const uint16_t kPioneerHdrSpace = kPioneerHdrSpaceTicks * kPioneerTick;
const uint16_t kPioneerBitMarkTicks = 1;
const uint16_t kPioneerBitMark = kPioneerBitMarkTicks * kPioneerTick;
const uint16_t kPioneerOneSpaceTicks = 3;
const uint16_t kPioneerOneSpace = kPioneerOneSpaceTicks * kPioneerTick;
const uint16_t kPioneerZeroSpaceTicks = 1;
const uint16_t kPioneerZeroSpace = kPioneerZeroSpaceTicks * kPioneerTick;
const uint16_t kPioneerMinCommandLengthTicks = 159;
const uint32_t kPioneerMinCommandLength = kPioneerMinCommandLengthTicks *
kPioneerTick;
const uint16_t kPioneerMinGapTicks = 47;
const uint32_t kPioneerMinGap = kPioneerMinGapTicks * kPioneerTick;
// Ref: https://github.com/crankyoldgit/IRremoteESP8266/issues/1220
const uint16_t kPioneerTick = 534; ///< uSeconds.
const uint16_t kPioneerHdrMark = 8506; ///< uSeconds.
const uint16_t kPioneerHdrSpace = 4191; ///< uSeconds.
const uint16_t kPioneerBitMark = 568; ///< uSeconds.
const uint16_t kPioneerOneSpace = 1542; ///< uSeconds.
const uint16_t kPioneerZeroSpace = 487; ///< uSeconds.
const uint32_t kPioneerMinCommandLength = 84906; ///< uSeconds.
const uint32_t kPioneerMinGap = 25181; ///< uSeconds.
#if SEND_PIONEER
/// Send a raw Pioneer formatted message.

View File

@ -53,6 +53,14 @@ const uint16_t kSamsungAcBitMark = 586;
const uint16_t kSamsungAcOneSpace = 1432;
const uint16_t kSamsungAcZeroSpace = 436;
// Data from https://github.com/crankyoldgit/IRremoteESP8266/issues/1220
// Values calculated based on the average of ten messages.
const uint16_t kSamsung36HdrMark = 4515; /// < uSeconds
const uint16_t kSamsung36HdrSpace = 4438; /// < uSeconds
const uint16_t kSamsung36BitMark = 512; /// < uSeconds
const uint16_t kSamsung36OneSpace = 1468; /// < uSeconds
const uint16_t kSamsung36ZeroSpace = 490; /// < uSeconds
using irutils::addBoolToString;
using irutils::addFanToString;
using irutils::addIntToString;
@ -146,7 +154,7 @@ bool IRrecv::decodeSAMSUNG(decode_results *results, uint16_t offset,
#if SEND_SAMSUNG36
/// Send a Samsung 36-bit formatted message.
/// Status: Alpha / Experimental.
/// Status: STABLE / Works on real devices.
/// @param[in] data The message to be sent.
/// @param[in] nbits The number of bits of message to be sent.
/// @param[in] repeat The number of times the command is to be repeated.
@ -156,16 +164,16 @@ void IRsend::sendSamsung36(const uint64_t data, const uint16_t nbits,
if (nbits < 16) return; // To small to send.
for (uint16_t r = 0; r <= repeat; r++) {
// Block #1 (16 bits)
sendGeneric(kSamsungHdrMark, kSamsungHdrSpace,
kSamsungBitMark, kSamsungOneSpace,
kSamsungBitMark, kSamsungZeroSpace,
kSamsungBitMark, kSamsungHdrSpace,
sendGeneric(kSamsung36HdrMark, kSamsung36HdrSpace,
kSamsung36BitMark, kSamsung36OneSpace,
kSamsung36BitMark, kSamsung36ZeroSpace,
kSamsung36BitMark, kSamsung36HdrSpace,
data >> (nbits - 16), 16, 38, true, 0, kDutyDefault);
// Block #2 (The rest, typically 20 bits)
sendGeneric(0, 0, // No header
kSamsungBitMark, kSamsungOneSpace,
kSamsungBitMark, kSamsungZeroSpace,
kSamsungBitMark, kSamsungMinGap, // Gap is just a guess.
kSamsung36BitMark, kSamsung36OneSpace,
kSamsung36BitMark, kSamsung36ZeroSpace,
kSamsung36BitMark, kSamsungMinGap, // Gap is just a guess.
// Mask off the rest of the bits.
data & ((1ULL << (nbits - 16)) - 1),
nbits - 16, 38, true, 0, kDutyDefault);
@ -175,7 +183,7 @@ void IRsend::sendSamsung36(const uint64_t data, const uint16_t nbits,
#if DECODE_SAMSUNG36
/// Decode the supplied Samsung36 message.
/// Status: Alpha / Experimental
/// Status: STABLE / Expected to work.
/// @param[in,out] results Ptr to the data to decode & where to store the result
/// @param[in] offset The starting index to use when attempting to decode the
/// raw data. Typically/Defaults to kStartOffset.
@ -198,10 +206,10 @@ bool IRrecv::decodeSamsung36(decode_results *results, uint16_t offset,
uint16_t used;
used = matchGeneric(results->rawbuf + offset, &data,
results->rawlen - offset, 16,
kSamsungHdrMark, kSamsungHdrSpace,
kSamsungBitMark, kSamsungOneSpace,
kSamsungBitMark, kSamsungZeroSpace,
kSamsungBitMark, kSamsungHdrSpace, false);
kSamsung36HdrMark, kSamsung36HdrSpace,
kSamsung36BitMark, kSamsung36OneSpace,
kSamsung36BitMark, kSamsung36ZeroSpace,
kSamsung36BitMark, kSamsung36HdrSpace, false);
if (!used) return false;
offset += used;
// Data (Block #2)
@ -209,9 +217,9 @@ bool IRrecv::decodeSamsung36(decode_results *results, uint16_t offset,
if (!matchGeneric(results->rawbuf + offset, &data2,
results->rawlen - offset, nbits - 16,
0, 0,
kSamsungBitMark, kSamsungOneSpace,
kSamsungBitMark, kSamsungZeroSpace,
kSamsungBitMark, kSamsungMinGap, true)) return false;
kSamsung36BitMark, kSamsung36OneSpace,
kSamsung36BitMark, kSamsung36ZeroSpace,
kSamsung36BitMark, kSamsungMinGap, true)) return false;
data <<= (nbits - 16);
data += data2;

View File

@ -14,6 +14,8 @@
// Brand: Samsung, Model: DB93-16761C remote
// Brand: Samsung, Model: IEC-R03 remote
// Brand: Samsung, Model: AK59-00167A Bluray remote (SAMSUNG36)
// Brand: Samsung, Model: AH59-02692E Soundbar remote (SAMSUNG36)
// Brand: Samsung, Model: HW-J551 Soundbar (SAMSUNG36)
// Brand: Samsung, Model: AR09FSSDAWKNFA A/C (SAMSUNG_AC)
// Brand: Samsung, Model: AR12KSFPEWQNET A/C (SAMSUNG_AC)
// Brand: Samsung, Model: AR12HSSDBWKNEU A/C (SAMSUNG_AC)

View File

@ -1,6 +1,6 @@
// Copyright 2009 Ken Shirriff
// Copyright 2016 marcosamarinho
// Copyright 2017 David Conran
// Copyright 2017-2020 David Conran
/// @file
/// @brief Support for Sanyo protocols.
@ -11,15 +11,27 @@
/// @see http://pdf.datasheetcatalog.com/datasheet/sanyo/LC7461.pdf
/// @see https://github.com/marcosamarinho/IRremoteESP8266/blob/master/ir_Sanyo.cpp
/// @see http://slydiman.narod.ru/scr/kb/sanyo.htm
/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1211
/// @see https://docs.google.com/spreadsheets/d/1dYfLsnYvpjV-SgO8pdinpfuBIpSzm8Q1R5SabrLeskw/edit?usp=sharing
// Supports:
// Brand: Sanyo, Model: SA 8650B - disabled
// Brand: Sanyo, Model: LC7461 transmitter IC (SANYO_LC7461)
#include "ir_Sanyo.h"
#include <algorithm>
#include <cstring>
#include "IRrecv.h"
#include "IRsend.h"
#include "IRtext.h"
#include "IRutils.h"
using irutils::addBoolToString;
using irutils::addFanToString;
using irutils::addIntToString;
using irutils::addLabeledString;
using irutils::addModeToString;
using irutils::addTempToString;
using irutils::minsToString;
using irutils::sumNibbles;
using irutils::setBit;
using irutils::setBits;
// Constants
// Sanyo SA 8650B
@ -48,6 +60,14 @@ const uint16_t kSanyoLc7461MinGap =
(kSanyoLc7461OneSpace + kSanyoLc7461ZeroSpace) / 2) +
kSanyoLc7461BitMark);
const uint16_t kSanyoAcHdrMark = 8500; ///< uSeconds
const uint16_t kSanyoAcHdrSpace = 4200; ///< uSeconds
const uint16_t kSanyoAcBitMark = 500; ///< uSeconds
const uint16_t kSanyoAcOneSpace = 1600; ///< uSeconds
const uint16_t kSanyoAcZeroSpace = 550; ///< uSeconds
const uint32_t kSanyoAcGap = kDefaultMessageGap; ///< uSeconds (Guess only)
const uint16_t kSanyoAcFreq = 38000; ///< Hz. (Guess only)
#if SEND_SANYO
/// Construct a Sanyo LC7461 message.
/// @param[in] address The 13 bit value of the address(Custom) portion of the
@ -221,3 +241,453 @@ bool IRrecv::decodeSanyo(decode_results *results, uint16_t nbits, bool strict) {
}
*/
#endif // DECODE_SANYO
#if SEND_SANYO_AC
/// Send a SanyoAc formatted message.
/// Status: STABLE / Reported as working.
/// @param[in] data An array of bytes containing the IR command.
/// @param[in] nbytes Nr. of bytes of data in the array.
/// @param[in] repeat Nr. of times the message is to be repeated.
/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1211
void IRsend::sendSanyoAc(const uint8_t data[], const uint16_t nbytes,
const uint16_t repeat) {
// Header + Data + Footer
sendGeneric(kSanyoAcHdrMark, kSanyoAcHdrSpace,
kSanyoAcBitMark, kSanyoAcOneSpace,
kSanyoAcBitMark, kSanyoAcZeroSpace,
kSanyoAcBitMark, kSanyoAcGap,
data, nbytes, kSanyoAcFreq, false, repeat, kDutyDefault);
}
#endif // SEND_SANYO_AC
#if DECODE_SANYO_AC
/// Decode the supplied SanyoAc message.
/// Status: STABLE / Reported as working.
/// @param[in,out] results Ptr to the data to decode & where to store the decode
/// @param[in] offset The starting index to use when attempting to decode the
/// raw data. Typically/Defaults to kStartOffset.
/// @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.
/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1211
bool IRrecv::decodeSanyoAc(decode_results *results, uint16_t offset,
const uint16_t nbits, const bool strict) {
if (strict && nbits != kSanyoAcBits)
return false;
// Header + Data + Footer
if (!matchGeneric(results->rawbuf + offset, results->state,
results->rawlen - offset, nbits,
kSanyoAcHdrMark, kSanyoAcHdrSpace,
kSanyoAcBitMark, kSanyoAcOneSpace,
kSanyoAcBitMark, kSanyoAcZeroSpace,
kSanyoAcBitMark, kSanyoAcGap,
true, kUseDefTol, kMarkExcess, false)) return false;
// Compliance
if (strict)
if (!IRSanyoAc::validChecksum(results->state, nbits / 8)) return false;
// Success
results->decode_type = decode_type_t::SANYO_AC;
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_SANYO_AC
/// 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?
IRSanyoAc::IRSanyoAc(const uint16_t pin, const bool inverted,
const bool use_modulation)
: _irsend(pin, inverted, use_modulation) { stateReset(); }
/// Reset the state of the remote to a known good state/sequence.
/// @see https://docs.google.com/spreadsheets/d/1dYfLsnYvpjV-SgO8pdinpfuBIpSzm8Q1R5SabrLeskw/edit?ts=5f0190a5#gid=1050142776&range=A2:B2
void IRSanyoAc::stateReset(void) {
static const uint8_t kReset[kSanyoAcStateLength] = {
0x6A, 0x6D, 0x51, 0x00, 0x10, 0x45, 0x00, 0x00, 0x33};
memcpy(remote_state, kReset, kSanyoAcStateLength);
}
/// Set up hardware to be able to send a message.
void IRSanyoAc::begin(void) { _irsend.begin(); }
#if SEND_SANYO_AC
/// Send the current internal state as IR messages.
/// @param[in] repeat Nr. of times the message will be repeated.
void IRSanyoAc::send(const uint16_t repeat) {
_irsend.sendSanyoAc(getRaw(), kSanyoAcStateLength, repeat);
}
#endif // SEND_SANYO_AC
/// Get a PTR to the internal state/code for this protocol with all integrity
/// checks passing.
/// @return PTR to a code for this protocol based on the current internal state.
uint8_t* IRSanyoAc::getRaw(void) {
checksum();
return remote_state;
}
/// Set the internal state from a valid code for this protocol.
/// @param[in] newState A valid code for this protocol.
void IRSanyoAc::setRaw(const uint8_t newState[]) {
memcpy(remote_state, newState, kSanyoAcStateLength);
}
/// Calculate the checksum for a given state.
/// @param[in] state The array to calc the checksum of.
/// @param[in] length The length/size of the array.
/// @return The calculated checksum value.
uint8_t IRSanyoAc::calcChecksum(const uint8_t state[],
const uint16_t length) {
return length ? sumNibbles(state, length - 1) : 0;
}
/// Verify the checksum is valid for a given state.
/// @param[in] state The array to verify the checksum of.
/// @param[in] length The length/size of the array.
/// @return true, if the state has a valid checksum. Otherwise, false.
bool IRSanyoAc::validChecksum(const uint8_t state[], const uint16_t length) {
return length && state[length - 1] == IRSanyoAc::calcChecksum(state, length);
}
/// Calculate & set the checksum for the current internal state of the remote.
void IRSanyoAc::checksum(void) {
// Stored the checksum value in the last byte.
remote_state[kSanyoAcStateLength - 1] = calcChecksum(remote_state);
}
/// Set the requested power state of the A/C to on.
void IRSanyoAc::on(void) { setPower(true); }
/// Set the requested power state of the A/C to off.
void IRSanyoAc::off(void) { setPower(false); }
/// Change the power setting.
/// @param[in] on true, the setting is on. false, the setting is off.
void IRSanyoAc::setPower(const bool on) {
setBits(&remote_state[kSanyoAcPowerByte], kSanyoAcPowerOffset,
kSanyoAcPowerSize, on ? kSanyoAcPowerOn : kSanyoAcPowerOff);
}
/// Get the value of the current power setting.
/// @return true, the setting is on. false, the setting is off.
bool IRSanyoAc::getPower(void) {
return GETBITS8(remote_state[kSanyoAcPowerByte], kSanyoAcPowerOffset,
kSanyoAcPowerSize) == kSanyoAcPowerOn;
}
/// Get the operating mode setting of the A/C.
/// @return The current operating mode setting.
uint8_t IRSanyoAc::getMode(void) {
return GETBITS8(remote_state[kSanyoAcModeByte], kSanyoAcModeOffset,
kSanyoAcModeSize);
}
/// Set the operating mode of the A/C.
/// @param[in] mode The desired operating mode.
/// @note If we get an unexpected mode, default to AUTO.
void IRSanyoAc::setMode(const uint8_t mode) {
switch (mode) {
case kSanyoAcAuto:
case kSanyoAcCool:
case kSanyoAcDry:
case kSanyoAcHeat:
setBits(&remote_state[kSanyoAcModeByte], kSanyoAcModeOffset,
kSanyoAcModeSize, mode);
break;
default: setMode(kSanyoAcAuto);
}
}
/// Convert a stdAc::opmode_t enum into its native mode.
/// @param[in] mode The enum to be converted.
/// @return The native equivilant of the enum.
uint8_t IRSanyoAc::convertMode(const stdAc::opmode_t mode) {
switch (mode) {
case stdAc::opmode_t::kCool: return kSanyoAcCool;
case stdAc::opmode_t::kHeat: return kSanyoAcHeat;
case stdAc::opmode_t::kDry: return kSanyoAcDry;
default: return kSanyoAcAuto;
}
}
/// Convert a native mode into its stdAc equivilant.
/// @param[in] mode The native setting to be converted.
/// @return The stdAc equivilant of the native setting.
stdAc::opmode_t IRSanyoAc::toCommonMode(const uint8_t mode) {
switch (mode) {
case kSanyoAcCool: return stdAc::opmode_t::kCool;
case kSanyoAcHeat: return stdAc::opmode_t::kHeat;
case kSanyoAcDry: return stdAc::opmode_t::kDry;
default: return stdAc::opmode_t::kAuto;
}
}
/// Set the temperature at a given location.
/// @param[out] ptr A pointer to a temperature byte.
/// @param[in] degrees The temperature in degrees celsius.
void IRSanyoAc::_setTemp(uint8_t *ptr, const uint8_t degrees) {
uint8_t temp = std::max((uint8_t)kSanyoAcTempMin, degrees);
temp = std::min((uint8_t)kSanyoAcTempMax, temp);
setBits(ptr, kSanyoAcTempOffset, kSanyoAcTempSize, temp - kSanyoAcTempDelta);
}
/// Get the temperature from a given location.
/// @param[in] ptr A pointer to a temperature byte.
/// @return The current setting for temp. in degrees celsius.
uint8_t IRSanyoAc::_getTemp(uint8_t *ptr) {
return GETBITS8(*ptr, kSanyoAcTempOffset, kSanyoAcTempSize) +
kSanyoAcTempDelta;
}
/// Set the desired temperature.
/// @param[in] degrees The temperature in degrees celsius.
void IRSanyoAc::setTemp(const uint8_t degrees) {
_setTemp(&remote_state[kSanyoAcTempByte], degrees);
}
/// Get the current desired temperature setting.
/// @return The current setting for temp. in degrees celsius.
uint8_t IRSanyoAc::getTemp(void) {
return _getTemp(&remote_state[kSanyoAcTempByte]);
}
/// Set the sensor temperature.
/// @param[in] degrees The temperature in degrees celsius.
void IRSanyoAc::setSensorTemp(const uint8_t degrees) {
_setTemp(&remote_state[kSanyoAcSensorByte], degrees);
}
/// Get the current sensor temperature setting.
/// @return The current setting for temp. in degrees celsius.
uint8_t IRSanyoAc::getSensorTemp(void) {
return _getTemp(&remote_state[kSanyoAcSensorByte]);
}
/// Set the speed of the fan.
/// @param[in] speed The desired setting.
void IRSanyoAc::setFan(const uint8_t speed) {
setBits(&remote_state[kSanyoAcModeByte], kSanyoAcFanOffset, kSanyoAcFanSize,
speed);
}
/// Get the current fan speed setting.
/// @return The current fan speed/mode.
uint8_t IRSanyoAc::getFan(void) {
return GETBITS8(remote_state[kSanyoAcModeByte], kSanyoAcFanOffset,
kSanyoAcFanSize);
}
/// Convert a stdAc::fanspeed_t enum into it's native speed.
/// @param[in] speed The enum to be converted.
/// @return The native equivilant of the enum.
uint8_t IRSanyoAc::convertFan(const stdAc::fanspeed_t speed) {
switch (speed) {
case stdAc::fanspeed_t::kMin:
case stdAc::fanspeed_t::kLow: return kSanyoAcFanLow;
case stdAc::fanspeed_t::kMedium: return kSanyoAcFanMedium;
case stdAc::fanspeed_t::kHigh:
case stdAc::fanspeed_t::kMax: return kSanyoAcFanHigh;
default: return kSanyoAcFanAuto;
}
}
/// Convert a native fan speed into its stdAc equivilant.
/// @param[in] spd The native setting to be converted.
/// @return The stdAc equivilant of the native setting.
stdAc::fanspeed_t IRSanyoAc::toCommonFanSpeed(const uint8_t spd) {
switch (spd) {
case kSanyoAcFanHigh: return stdAc::fanspeed_t::kHigh;
case kSanyoAcFanMedium: return stdAc::fanspeed_t::kMedium;
case kSanyoAcFanLow: return stdAc::fanspeed_t::kLow;
default: return stdAc::fanspeed_t::kAuto;
}
}
/// Get the vertical swing setting of the A/C.
/// @return The current swing mode setting.
uint8_t IRSanyoAc::getSwingV(void) {
return GETBITS8(remote_state[kSanyoAcPowerByte], kSanyoAcSwingVOffset,
kSanyoAcSwingVSize);
}
/// Set the vertical swing setting of the A/C.
/// @param[in] setting The value of the desired setting.
void IRSanyoAc::setSwingV(const uint8_t setting) {
if (setting == kSanyoAcSwingVAuto ||
(setting >= kSanyoAcSwingVLowest && setting <= kSanyoAcSwingVHighest))
setBits(&remote_state[kSanyoAcPowerByte], kSanyoAcSwingVOffset,
kSanyoAcSwingVSize, setting);
else
setSwingV(kSanyoAcSwingVAuto);
}
/// Convert a stdAc::swingv_t enum into it's native setting.
/// @param[in] position The enum to be converted.
/// @return The native equivilant of the enum.
uint8_t IRSanyoAc::convertSwingV(const stdAc::swingv_t position) {
switch (position) {
case stdAc::swingv_t::kHighest: return kSanyoAcSwingVHighest;
case stdAc::swingv_t::kHigh: return kSanyoAcSwingVHigh;
case stdAc::swingv_t::kMiddle: return kSanyoAcSwingVUpperMiddle;
case stdAc::swingv_t::kLow: return kSanyoAcSwingVLow;
case stdAc::swingv_t::kLowest: return kSanyoAcSwingVLowest;
default: return kSanyoAcSwingVAuto;
}
}
/// Convert a native vertical swing postion to it's common equivalent.
/// @param[in] setting A native position to convert.
/// @return The common vertical swing position.
stdAc::swingv_t IRSanyoAc::toCommonSwingV(const uint8_t setting) {
switch (setting) {
case kSanyoAcSwingVHighest: return stdAc::swingv_t::kHighest;
case kSanyoAcSwingVHigh: return stdAc::swingv_t::kHigh;
case kSanyoAcSwingVUpperMiddle:
case kSanyoAcSwingVLowerMiddle: return stdAc::swingv_t::kMiddle;
case kSanyoAcSwingVLow: return stdAc::swingv_t::kLow;
case kSanyoAcSwingVLowest: return stdAc::swingv_t::kLowest;
default: return stdAc::swingv_t::kAuto;
}
}
/// Set the Sleep (Night Setback) setting of the A/C.
/// @param[in] on true, the setting is on. false, the setting is off.
void IRSanyoAc::setSleep(const bool on) {
setBit(&remote_state[kSanyoAcSleepByte], kSanyoAcSleepBit, on);
}
/// Get the Sleep (Night Setback) setting of the A/C.
/// @return true, the setting is on. false, the setting is off.
bool IRSanyoAc::getSleep(void) {
return GETBIT8(remote_state[kSanyoAcSleepByte], kSanyoAcSleepBit);
}
/// Set the Sensor Location setting of the A/C.
/// i.e. Where the ambient temperature is measured.
/// @param[in] location true is Unit/Wall, false is Remote/Room.
void IRSanyoAc::setSensor(const bool location) {
setBit(&remote_state[kSanyoAcSensorByte], kSanyoAcSensorBit, location);
}
/// Get the Sensor Location setting of the A/C.
/// i.e. Where the ambient temperature is measured.
/// @return true is Unit/Wall, false is Remote/Room.
bool IRSanyoAc::getSensor(void) {
return GETBIT8(remote_state[kSanyoAcSensorByte], kSanyoAcSensorBit);
}
/// Set the Beep setting of the A/C.
/// @param[in] on true, the setting is on. false, the setting is off.
void IRSanyoAc::setBeep(const bool on) {
setBit(&remote_state[kSanyoAcSensorByte], kSanyoAcBeepBit, on);
}
/// Get the Beep setting of the A/C.
/// @return true, the setting is on. false, the setting is off.
bool IRSanyoAc::getBeep(void) {
return GETBIT8(remote_state[kSanyoAcSensorByte], kSanyoAcBeepBit);
}
/// Get the nr of minutes the Off Timer is set to.
/// @return The timer time expressed as the number of minutes.
/// A value of 0 means the Off Timer is off/disabled.
/// @note The internal precission has a resolution of 1 hour.
uint16_t IRSanyoAc::getOffTimer(void) {
if (GETBIT8(remote_state[kSanyoAcModeByte], kSanyoAcOffTimerEnableBit))
return GETBITS8(remote_state[kSanyoAcOffHourByte], kSanyoAcOffHourOffset,
kSanyoAcOffHourSize) * 60;
else
return 0;
}
/// Set the nr of minutes for the Off Timer.
/// @param[in] mins The timer time expressed as nr. of minutes.
/// A value of 0 means the Off Timer is off/disabled.
/// @note The internal precission has a resolution of 1 hour.
void IRSanyoAc::setOffTimer(const uint16_t mins) {
const uint8_t hours = std::min((uint8_t)(mins / 60), kSanyoAcHourMax);
setBit(&remote_state[kSanyoAcModeByte], kSanyoAcOffTimerEnableBit, hours > 0);
setBits(&remote_state[kSanyoAcOffHourByte], kSanyoAcOffHourOffset,
kSanyoAcOffHourSize, hours);
}
/// Convert the current internal state into its stdAc::state_t equivilant.
/// @return The stdAc equivilant of the native settings.
stdAc::state_t IRSanyoAc::toCommon(void) {
stdAc::state_t result;
result.protocol = decode_type_t::SANYO_AC;
result.model = -1; // Not supported.
result.power = getPower();
result.mode = toCommonMode(getMode());
result.celsius = true;
result.degrees = getTemp();
result.fanspeed = toCommonFanSpeed(getFan());
result.sleep = getSleep() ? 0 : -1;
result.swingv = toCommonSwingV(getSwingV());
result.beep = getBeep();
// Not supported.
result.swingh = stdAc::swingh_t::kOff;
result.turbo = false;
result.econo = false;
result.light = false;
result.filter = false;
result.quiet = false;
result.clean = false;
result.clock = -1;
return result;
}
/// Convert the current internal state into a human readable string.
/// @return A human readable string.
String IRSanyoAc::toString(void) {
String result = "";
result.reserve(140);
result += addBoolToString(getPower(), kPowerStr, false);
result += addModeToString(getMode(), kSanyoAcAuto, kSanyoAcCool,
kSanyoAcHeat, kSanyoAcDry, kSanyoAcAuto);
result += addTempToString(getTemp());
result += addFanToString(getFan(), kSanyoAcFanHigh, kSanyoAcFanLow,
kSanyoAcFanAuto, kSanyoAcFanAuto,
kSanyoAcFanMedium);
result += addIntToString(getSwingV(), kSwingVStr);
result += kSpaceLBraceStr;
switch (getSwingV()) {
case kSanyoAcSwingVHighest: result += kHighestStr; break;
case kSanyoAcSwingVHigh: result += kHighStr; break;
case kSanyoAcSwingVUpperMiddle:
result += kUpperStr;
result += ' ';
result += kMiddleStr;
break;
case kSanyoAcSwingVLowerMiddle:
result += kLowerStr;
result += ' ';
result += kMiddleStr;
break;
case kSanyoAcSwingVLow: result += kLowStr; break;
case kSanyoAcSwingVLowest: result += kLowestStr; break;
case kSanyoAcSwingVAuto: result += kAutoStr; break;
default: result += kUnknownStr;
}
result += ')';
result += addBoolToString(getSleep(), kSleepStr);
result += addBoolToString(getBeep(), kBeepStr);
result += addLabeledString(getSensor() ? kRoomStr : kWallStr, kSensorStr);
result += kCommaSpaceStr;
result += kSensorStr;
result += ' ';
result += addTempToString(getSensorTemp(), true, false);
const uint16_t offtime = getOffTimer();
result += addLabeledString(offtime ? minsToString(offtime) : kOffStr,
kOffTimerStr);
return result;
}

View File

@ -0,0 +1,163 @@
// Copyright 2020 David Conran
/// @file
/// @brief Support for Sanyo protocols.
/// Sanyo LC7461 support originally by marcosamarinho
/// Sanyo SA 8650B originally added from
/// https://github.com/shirriff/Arduino-IRremote/
/// @see https://github.com/z3t0/Arduino-IRremote/blob/master/ir_Sanyo.cpp
/// @see http://pdf.datasheetcatalog.com/datasheet/sanyo/LC7461.pdf
/// @see https://github.com/marcosamarinho/IRremoteESP8266/blob/master/ir_Sanyo.cpp
/// @see http://slydiman.narod.ru/scr/kb/sanyo.htm
/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1211
/// @see https://docs.google.com/spreadsheets/d/1dYfLsnYvpjV-SgO8pdinpfuBIpSzm8Q1R5SabrLeskw/edit?usp=sharing
// Supports:
// Brand: Sanyo, Model: SA 8650B - disabled
// Brand: Sanyo, Model: LC7461 transmitter IC (SANYO_LC7461)
// Brand: Sanyo, Model: SAP-K121AHA A/C (SANYO_AC)
// Brand: Sanyo, Model: RCS-2HS4E remote (SANYO_AC)
// Brand: Sanyo, Model: SAP-K242AH A/C (SANYO_AC)
// Brand: Sanyo, Model: RCS-2S4E remote (SANYO_AC)
#ifndef IR_SANYO_H_
#define IR_SANYO_H_
#define __STDC_LIMIT_MACROS
#include <stdint.h>
#ifdef ARDUINO
#include <Arduino.h>
#endif
#include "IRremoteESP8266.h"
#include "IRsend.h"
#ifdef UNIT_TEST
#include "IRsend_test.h"
#endif
// Constants
// Sanyo A/C
// Ref: https://docs.google.com/spreadsheets/d/1dYfLsnYvpjV-SgO8pdinpfuBIpSzm8Q1R5SabrLeskw/edit?usp=sharing
// Byte[0] - 0x6A (Fixed?)
// Byte[1] - Address + Temperature
const uint8_t kSanyoAcTempByte = 1; ///< Index
const uint8_t kSanyoAcTempOffset = 0; ///< Mask 0b000xxxxx
const uint8_t kSanyoAcTempSize = 5; ///< Mask 0b000xxxxx
const uint8_t kSanyoAcTempMin = 16; ///< Celsius
const uint8_t kSanyoAcTempMax = 30; ///< Celsius
const uint8_t kSanyoAcTempDelta = 4; ///< Celsius to Native Temp difference.
// Byte[2] - Ambient Temp + Sensor
const uint8_t kSanyoAcSensorByte = 2; ///< Index
const uint8_t kSanyoAcSensorBit = 2; ///< Mask 0b00x00000
// Ambient Temp Mask 0b000xxxxx
const uint8_t kSanyoAcBeepBit = 6; ///< Mask 0b0x000000
// Byte[3] - Off Hour
const uint8_t kSanyoAcOffHourByte = 3; ///< Index
const uint8_t kSanyoAcOffHourOffset = 0; ///< Mask 0b0000xxxx
const uint8_t kSanyoAcOffHourSize = 4; ///< Mask 0b0000xxxx
const uint8_t kSanyoAcHourMax = 15; ///< 0b1111
// Byte[4] - Mode + Fan + Timer Enables
const uint8_t kSanyoAcModeByte = 4; ///< Index
const uint8_t kSanyoAcModeOffset = 4; ///< Mask 0b0xxx0000
const uint8_t kSanyoAcModeSize = 3; ///< Mask 0b0xxx0000
const uint8_t kSanyoAcHeat = 1; ///< 0b001
const uint8_t kSanyoAcCool = 2; ///< 0b010
const uint8_t kSanyoAcDry = 3; ///< 0b011
const uint8_t kSanyoAcAuto = 4; ///< 0b100
const uint8_t kSanyoAcOffTimerEnableBit = 2; ///< Mask 0b00000x00
const uint8_t kSanyoAcFanOffset = 0; ///< Mask 0b000000xx
const uint8_t kSanyoAcFanSize = 2; ///< Mask 0b000000xx
const uint8_t kSanyoAcFanAuto = 0; ///< 0b00
const uint8_t kSanyoAcFanHigh = 1; ///< 0b01
const uint8_t kSanyoAcFanLow = 2; ///< 0b10
const uint8_t kSanyoAcFanMedium = 3; ///< 0b11
// Byte[5] - Power + SwingV
const uint8_t kSanyoAcPowerByte = 5; ///< Index
const uint8_t kSanyoAcPowerOffset = 6; ///< Mask 0bxx000000
const uint8_t kSanyoAcPowerSize = 2; ///< Mask 0bxx000000
// const uint8_t kSanyoAcPowerStandby = 0b00; ///< Standby?
const uint8_t kSanyoAcPowerOff = 0b01; ///< Off
const uint8_t kSanyoAcPowerOn = 0b10; ///< On
const uint8_t kSanyoAcSwingVOffset = 0; ///< Mask 0b00000xxx
const uint8_t kSanyoAcSwingVSize = 3; ///< Mask 0b00000xxx
const uint8_t kSanyoAcSwingVAuto = 0; ///< 0b000
const uint8_t kSanyoAcSwingVLowest = 2; ///< 0b010
const uint8_t kSanyoAcSwingVLow = 3; ///< 0b011
const uint8_t kSanyoAcSwingVLowerMiddle = 4; ///< 0b100
const uint8_t kSanyoAcSwingVUpperMiddle = 5; ///< 0b101
const uint8_t kSanyoAcSwingVHigh = 6; ///< 0b110
const uint8_t kSanyoAcSwingVHighest = 7; ///< 0b111
// Byte[6] - Sleep
const uint8_t kSanyoAcSleepByte = 6; ///< Index
const uint8_t kSanyoAcSleepBit = 3; ///< Mask 0b0000x000
// Byte[8] - Checksum (8-bit Sum of all preceeding nibbles)
// Classes
/// Class for handling detailed Sanyo A/C messages.
class IRSanyoAc {
public:
explicit IRSanyoAc(const uint16_t pin, const bool inverted = false,
const bool use_modulation = true);
void stateReset(void);
#if SEND_SANYO_AC
void send(const uint16_t repeat = kNoRepeat);
/// Run the calibration to calculate uSec timing offsets for this platform.
/// @return The uSec timing offset needed per modulation of the IR Led.
/// @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_SANYO_AC
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 setSensorTemp(const uint8_t degrees);
uint8_t getSensorTemp(void);
void setFan(const uint8_t speed);
uint8_t getFan(void);
void setMode(const uint8_t mode);
uint8_t getMode(void);
void setSleep(const bool on);
bool getSleep(void);
void setSensor(const bool location);
bool getSensor(void);
void setBeep(const bool on);
bool getBeep(void);
void setSwingV(const uint8_t setting);
uint8_t getSwingV(void);
void setRaw(const uint8_t newState[]);
uint8_t* getRaw(void);
uint16_t getOffTimer(void);
void setOffTimer(const uint16_t mins);
static bool validChecksum(const uint8_t state[],
const uint16_t length = kSanyoAcStateLength);
uint8_t convertMode(const stdAc::opmode_t mode);
uint8_t convertFan(const stdAc::fanspeed_t speed);
uint8_t convertSwingV(const stdAc::swingv_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 setting);
stdAc::state_t toCommon(void);
String toString(void);
#ifndef 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
/// @endcond
#endif // UNIT_TEST
uint8_t remote_state[kSanyoAcStateLength]; ///< The state in IR code form.
void checksum(void);
static uint8_t calcChecksum(const uint8_t state[],
const uint16_t length = kSanyoAcStateLength);
void _setTemp(uint8_t *ptr, const uint8_t degrees);
uint8_t _getTemp(uint8_t *ptr);
};
#endif // IR_SANYO_H_

View File

@ -4,6 +4,9 @@
/// @brief Support for Toshiba protocols.
/// @see https://github.com/r45635/HVAC-IR-Control
/// @see https://github.com/r45635/HVAC-IR-Control/blob/master/HVAC_ESP8266/HVAC_ESP8266T.ino#L77
/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1205
/// @see https://www.toshiba-carrier.co.jp/global/about/index.htm
/// @see http://www.toshiba-carrier.co.th/AboutUs/Pages/CompanyProfile.aspx
#include "ir_Toshiba.h"
#include <algorithm>
@ -21,10 +24,10 @@
// Toshiba A/C
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;
const uint16_t kToshibaAcBitMark = 580;
const uint16_t kToshibaAcOneSpace = 1600;
const uint16_t kToshibaAcZeroSpace = 490;
const uint16_t kToshibaAcMinGap = 7400;
using irutils::addBoolToString;
using irutils::addFanToString;
@ -32,6 +35,8 @@ using irutils::addIntToString;
using irutils::addLabeledString;
using irutils::addModeToString;
using irutils::addTempToString;
using irutils::checkInvertedBytePairs;
using irutils::invertBytePairs;
using irutils::setBit;
using irutils::setBits;
@ -41,10 +46,8 @@ using irutils::setBits;
/// @param[in] data The message to be sent.
/// @param[in] nbytes The number of bytes of message to be sent.
/// @param[in] repeat The number of times the command is to be repeated.
void IRsend::sendToshibaAC(const unsigned char data[], const uint16_t nbytes,
void IRsend::sendToshibaAC(const uint8_t data[], const uint16_t nbytes,
const 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,
@ -58,7 +61,7 @@ void IRsend::sendToshibaAC(const unsigned char data[], const uint16_t nbytes,
/// @param[in] use_modulation Is frequency modulation to be used?
IRToshibaAC::IRToshibaAC(const uint16_t pin, const bool inverted,
const bool use_modulation)
: _irsend(pin, inverted, use_modulation) { this->stateReset(); }
: _irsend(pin, inverted, use_modulation) { stateReset(); }
/// Reset the state of the remote to a known good state/sequence.
/// @see https://github.com/r45635/HVAC-IR-Control/blob/master/HVAC_ESP8266/HVAC_ESP8266T.ino#L103
@ -66,24 +69,72 @@ void IRToshibaAC::stateReset(void) {
static const uint8_t kReset[kToshibaACStateLength] = {
0xF2, 0x0D, 0x03, 0xFC, 0x01};
memcpy(remote_state, kReset, kToshibaACStateLength);
mode_state = getMode(true);
setTemp(22); // Remote defaults to 22C after factory reset. So do the same.
setSwing(kToshibaAcSwingOff);
prev_mode = getMode();
}
/// Set up hardware to be able to send a message.
void IRToshibaAC::begin(void) { _irsend.begin(); }
#if SEND_TOSHIBA_AC
/// Send the current internal state as an IR message.
/// Send the current internal state as IR messages.
/// @param[in] repeat Nr. of times the message will be repeated.
void IRToshibaAC::send(const uint16_t repeat) {
_irsend.sendToshibaAC(getRaw(), kToshibaACStateLength, repeat);
_backupState();
_irsend.sendToshibaAC(getRaw(), getStateLength(), repeat);
if (_send_swing && (getStateLength() != kToshibaACStateLengthShort)) {
setStateLength(kToshibaACStateLengthShort);
// Swing settings expect the min temp to be set.
// Ref: https://github.com/crankyoldgit/IRremoteESP8266/issues/1205#issuecomment-653922374
setTemp(kToshibaAcMinTemp);
setSwing(_swing_mode);
_irsend.sendToshibaAC(getRaw(), getStateLength(), repeat);
_restoreState();
}
_send_swing = false;
}
#endif // SEND_TOSHIBA_AC
/// Get a PTR to the internal state/code for this protocol.
/// Get the length of the supplied Toshiba state per it's protocol structure.
/// @param[in] state The array to get the built-in length from.
/// @param[in] size The physical size of the state array.
/// @return Nr. of bytes in use for the provided state message.
uint16_t IRToshibaAC::getInternalStateLength(const uint8_t state[],
const uint16_t size) {
if (size < kToshibaAcLengthByte) return 0;
return std::min((uint16_t)(state[kToshibaAcLengthByte] + kToshibaAcMinLength),
kToshibaACStateLengthLong);
}
/// Get the length of the current internal state per the protocol structure.
/// @return Nr. of bytes in use for the current internal state message.
uint16_t IRToshibaAC::getStateLength(void) {
return getInternalStateLength(remote_state, kToshibaACStateLengthLong);
}
/// Set the internal length of the current internal state per the protocol.
/// @param[in] size Nr. of bytes in use for the current internal state message.
void IRToshibaAC::setStateLength(const uint16_t size) {
if (size < kToshibaAcMinLength) return;
remote_state[kToshibaAcLengthByte] = size - kToshibaAcMinLength;
}
/// Make a copy of the internal code-form A/C state.
void IRToshibaAC::_backupState(void) {
memcpy(backup, remote_state, kToshibaACStateLengthLong);
}
/// Recover the internal code-form A/C state from the backup.
void IRToshibaAC::_restoreState(void) {
memcpy(remote_state, backup, kToshibaACStateLengthLong);
}
/// Get a PTR to the internal state/code for this protocol with all integrity
/// checks passing.
/// @return PTR to a code for this protocol based on the current internal state.
uint8_t* IRToshibaAC::getRaw(void) {
this->checksum();
checksum(getStateLength());
return remote_state;
}
@ -91,7 +142,8 @@ uint8_t* IRToshibaAC::getRaw(void) {
/// @param[in] newState A valid code for this protocol.
void IRToshibaAC::setRaw(const uint8_t newState[]) {
memcpy(remote_state, newState, kToshibaACStateLength);
mode_state = this->getMode(true);
prev_mode = getMode();
_send_swing = true;
}
/// Calculate the checksum for a given state.
@ -100,13 +152,7 @@ void IRToshibaAC::setRaw(const uint8_t newState[]) {
/// @return The calculated 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;
return length ? xorBytes(state, length - 1) : 0;
}
/// Verify the checksum is valid for a given state.
@ -114,17 +160,27 @@ uint8_t IRToshibaAC::calcChecksum(const uint8_t state[],
/// @param[in] length The length/size of the array.
/// @return true, if the state has a valid checksum. Otherwise, false.
bool IRToshibaAC::validChecksum(const uint8_t state[], const uint16_t length) {
return (length > 1 && state[length - 1] == IRToshibaAC::calcChecksum(state,
length));
return length >= kToshibaAcMinLength &&
state[length - 1] == IRToshibaAC::calcChecksum(state, length) &&
checkInvertedBytePairs(state, kToshibaAcInvertedLength) &&
IRToshibaAC::getInternalStateLength(state, length) == length;
}
/// Calculate & set the checksum for the current internal state of the remote.
/// @param[in] length The length/size of the internal array to checksum.
void IRToshibaAC::checksum(const uint16_t length) {
// Stored the checksum value in the last byte.
if (length > 1) remote_state[length - 1] = this->calcChecksum(remote_state,
length);
if (length >= kToshibaAcMinLength) {
// Set/clear the short msg bit.
setBit(&remote_state[4], kToshibaAcShortMsgBit,
getStateLength() == kToshibaACStateLengthShort);
// Set/clear the long msg bit.
setBit(&remote_state[4], kToshibaAcLongMsgBit,
getStateLength() == kToshibaACStateLengthLong);
invertBytePairs(remote_state, kToshibaAcInvertedLength);
// Always do the Xor checksum LAST!
remote_state[length - 1] = calcChecksum(remote_state, length);
}
}
/// Set the requested power state of the A/C to on.
@ -136,19 +192,18 @@ void IRToshibaAC::off(void) { setPower(false); }
/// Change the power setting.
/// @param[in] on true, the setting is on. false, the setting is off.
void IRToshibaAC::setPower(const bool on) {
setBit(&remote_state[6], kToshibaAcPowerOffset, !on); // Cleared when on.
if (on)
setMode(mode_state);
else
setBits(&remote_state[6], kToshibaAcModeOffset, kToshibaAcModeSize,
kToshibaAcHeat);
if (on) { // On
// If not already on, pick the last non-off mode used
if (!getPower()) setMode(prev_mode);
} else { // Off
setMode(kToshibaAcOff);
}
}
/// Get the value of the current power setting.
/// @return true, the setting is on. false, the setting is off.
bool IRToshibaAC::getPower(void) {
return !GETBIT8(remote_state[6], kToshibaAcPowerOffset);
return getMode(true) != kToshibaAcOff;
}
/// Set the temperature.
@ -187,32 +242,104 @@ uint8_t IRToshibaAC::getFan(void) {
return --fan;
}
/// Get the swing setting of the A/C.
/// @param[in] raw Calculate the answer from just the state data.
/// @return The current swing mode setting.
uint8_t IRToshibaAC::getSwing(const bool raw) {
return raw ? GETBITS8(remote_state[5], kToshibaAcSwingOffset,
kToshibaAcSwingSize) : _swing_mode;
}
/// Set the swing setting of the A/C.
/// @param[in] setting The value of the desired setting.
void IRToshibaAC::setSwing(const uint8_t setting) {
switch (setting) {
case kToshibaAcSwingStep:
case kToshibaAcSwingOn:
case kToshibaAcSwingOff:
_send_swing = true;
_swing_mode = setting;
if (getStateLength() == kToshibaACStateLengthShort)
setBits(&remote_state[5], kToshibaAcSwingOffset, kToshibaAcSwingSize,
setting);
}
}
/// Get the operating mode setting of the A/C.
/// @param[in] useRaw Indicate to get the mode from the internal state array.
/// @param[in] raw Get the value without any intelligent processing.
/// @return The current operating mode setting.
uint8_t IRToshibaAC::getMode(const bool useRaw) {
if (useRaw)
return GETBITS8(remote_state[6], kToshibaAcModeOffset, kToshibaAcModeSize);
else
return mode_state;
uint8_t IRToshibaAC::getMode(const bool raw) {
const uint8_t mode = GETBITS8(remote_state[6], kToshibaAcModeOffset,
kToshibaAcModeSize);
if (raw) return mode;
switch (mode) {
case kToshibaAcOff: return prev_mode;
default: return mode;
}
}
/// Set the operating mode of the A/C.
/// @param[in] mode The desired operating mode.
/// @note If we get an unexpected mode, default to AUTO.
/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1205#issuecomment-654446771
void IRToshibaAC::setMode(const uint8_t mode) {
if (mode != prev_mode)
// Changing mode or power turns Econo & Turbo to off on a real remote.
// Setting the internal message length to "normal" will do that.
setStateLength(kToshibaACStateLength);
switch (mode) {
case kToshibaAcAuto:
case kToshibaAcCool:
case kToshibaAcDry:
case kToshibaAcHeat:
mode_state = mode;
// Only adjust the remote_state if we have power set to on.
if (getPower())
setBits(&remote_state[6], kToshibaAcModeOffset, kToshibaAcModeSize,
mode_state);
return;
default: this->setMode(kToshibaAcAuto); // There is no Fan mode.
case kToshibaAcFan:
prev_mode = mode;
// FALL-THRU
case kToshibaAcOff:
setBits(&remote_state[6], kToshibaAcModeOffset, kToshibaAcModeSize,
mode);
break;
default: setMode(kToshibaAcAuto);
}
}
/// Get the Turbo (Powerful) setting of the A/C.
/// @return true, if the current setting is on. Otherwise, false.
bool IRToshibaAC::getTurbo(void) {
if (getStateLength() == kToshibaACStateLengthLong)
return remote_state[8] == kToshibaAcTurboOn;
return false;
}
/// Set the Turbo (Powerful) setting of the A/C.
/// @param[in] on true, the setting is on. false, the setting is off.
/// Note: Turbo mode is mutually exclusive with Economy mode.
void IRToshibaAC::setTurbo(const bool on) {
if (on) {
remote_state[8] = kToshibaAcTurboOn;
setStateLength(kToshibaACStateLengthLong);
} else {
if (!getEcono()) setStateLength(kToshibaACStateLength);
}
}
/// Get the Economy mode setting of the A/C.
/// @return true, if the current setting is on. Otherwise, false.
bool IRToshibaAC::getEcono(void) {
if (getStateLength() == kToshibaACStateLengthLong)
return remote_state[8] == kToshibaAcEconoOn;
return false;
}
/// Set the Economy mode setting of the A/C.
/// @param[in] on true, the setting is on. false, the setting is off.
/// Note: Economy mode is mutually exclusive with Turbo mode.
void IRToshibaAC::setEcono(const bool on) {
if (on) {
remote_state[8] = kToshibaAcEconoOn;
setStateLength(kToshibaACStateLengthLong);
} else {
if (!getTurbo()) setStateLength(kToshibaACStateLength);
}
}
@ -224,7 +351,8 @@ uint8_t IRToshibaAC::convertMode(const stdAc::opmode_t mode) {
case stdAc::opmode_t::kCool: return kToshibaAcCool;
case stdAc::opmode_t::kHeat: return kToshibaAcHeat;
case stdAc::opmode_t::kDry: return kToshibaAcDry;
// No Fan mode.
case stdAc::opmode_t::kFan: return kToshibaAcFan;
case stdAc::opmode_t::kOff: return kToshibaAcOff;
default: return kToshibaAcAuto;
}
}
@ -251,6 +379,8 @@ stdAc::opmode_t IRToshibaAC::toCommonMode(const uint8_t mode) {
case kToshibaAcCool: return stdAc::opmode_t::kCool;
case kToshibaAcHeat: return stdAc::opmode_t::kHeat;
case kToshibaAcDry: return stdAc::opmode_t::kDry;
case kToshibaAcFan: return stdAc::opmode_t::kFan;
case kToshibaAcOff: return stdAc::opmode_t::kOff;
default: return stdAc::opmode_t::kAuto;
}
}
@ -280,12 +410,13 @@ stdAc::state_t IRToshibaAC::toCommon(void) {
result.celsius = true;
result.degrees = this->getTemp();
result.fanspeed = this->toCommonFanSpeed(this->getFan());
result.swingv = (getSwing() == kToshibaAcSwingOn) ? stdAc::swingv_t::kAuto
: stdAc::swingv_t::kOff;
result.turbo = getTurbo();
result.econo = getEcono();
// Not supported.
result.turbo = false;
result.light = false;
result.filter = false;
result.econo = false;
result.swingv = stdAc::swingv_t::kOff;
result.swingh = stdAc::swingh_t::kOff;
result.quiet = false;
result.clean = false;
@ -299,14 +430,33 @@ stdAc::state_t IRToshibaAC::toCommon(void) {
/// @return A human readable string.
String IRToshibaAC::toString(void) {
String result = "";
result.reserve(40);
result += addBoolToString(getPower(), kPowerStr, false);
result += addModeToString(getMode(), kToshibaAcAuto, kToshibaAcCool,
kToshibaAcHeat, kToshibaAcDry, kToshibaAcAuto);
result += addTempToString(getTemp());
result += addFanToString(getFan(), kToshibaAcFanMax, kToshibaAcFanMin,
kToshibaAcFanAuto, kToshibaAcFanAuto,
kToshibaAcFanMed);
result.reserve(80);
result += addTempToString(getTemp(), true, false);
switch (getStateLength()) {
case kToshibaACStateLengthShort:
result += addIntToString(getSwing(true), kSwingVStr);
result += kSpaceLBraceStr;
switch (getSwing(true)) {
case kToshibaAcSwingOff: result += kOffStr; break;
case kToshibaAcSwingOn: result += kOnStr; break;
case kToshibaAcSwingStep: result += kStepStr; break;
default: result += kUnknownStr;
}
result += ')';
break;
case kToshibaACStateLengthLong:
case kToshibaACStateLength:
default:
result += addBoolToString(getPower(), kPowerStr);
if (getPower())
result += addModeToString(getMode(), kToshibaAcAuto, kToshibaAcCool,
kToshibaAcHeat, kToshibaAcDry, kToshibaAcFan);
result += addFanToString(getFan(), kToshibaAcFanMax, kToshibaAcFanMin,
kToshibaAcFanAuto, kToshibaAcFanAuto,
kToshibaAcFanMed);
result += addBoolToString(getTurbo(), kTurboStr);
result += addBoolToString(getEcono(), kEconoStr);
}
return result;
}
@ -322,8 +472,16 @@ String IRToshibaAC::toString(void) {
bool IRrecv::decodeToshibaAC(decode_results* results, uint16_t offset,
const uint16_t nbits, const bool strict) {
// Compliance
if (strict && nbits != kToshibaACBits)
return false; // Must be called with the correct nr. of bytes.
if (strict) {
switch (nbits) { // Must be called with the correct nr. of bits.
case kToshibaACBits:
case kToshibaACBitsShort:
case kToshibaACBitsLong:
break;
default:
return false;
}
}
// Match Header + Data + Footer
if (!matchGeneric(results->rawbuf + offset, results->state,
@ -336,7 +494,7 @@ bool IRrecv::decodeToshibaAC(decode_results* results, uint16_t offset,
// Compliance
if (strict) {
// Check that the checksum of the message is correct.
if (!IRToshibaAC::validChecksum(results->state)) return false;
if (!IRToshibaAC::validChecksum(results->state, nbits / 8)) return false;
}
// Success

View File

@ -4,6 +4,10 @@
/// @brief Support for Toshiba protocols.
/// @see https://github.com/r45635/HVAC-IR-Control
/// @see https://github.com/r45635/HVAC-IR-Control/blob/master/HVAC_ESP8266/HVAC_ESP8266T.ino#L77
/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1205
/// @see https://docs.google.com/spreadsheets/d/1yidE2fvaO9kpCHfKafIdH31q4uaskYR1OwwrkyOxbp0/edit?usp=drivesdk
/// @see https://www.toshiba-carrier.co.jp/global/about/index.htm
/// @see http://www.toshiba-carrier.co.th/AboutUs/Pages/CompanyProfile.aspx
// Supports:
// Brand: Toshiba, Model: RAS-B13N3KV2
@ -12,6 +16,10 @@
// Brand: Toshiba, Model: RAS 18SKP-ES
// Brand: Toshiba, Model: WH-TA04NE
// Brand: Toshiba, Model: WC-L03SE
// Brand: Carrier, Model: 42NQV060M2 / 38NYV060M2 A/C
// Brand: Carrier, Model: 42NQV050M2 / 38NYV050M2 A/C
// Brand: Carrier, Model: 42NQV035M2 / 38NYV035M2 A/C
// Brand: Carrier, Model: 42NQV025M2 / 38NYV025M2 A/C
#ifndef IR_TOSHIBA_H_
#define IR_TOSHIBA_H_
@ -28,23 +36,53 @@
#endif
// Constants
// Byte[0] - 0xF2
// Byte[1] - 0x0D (inverted previous byte's value)
// Byte[2] - The expected payload length (in bytes) past the Byte[4].
const uint8_t kToshibaAcLengthByte = 2; ///< Byte pos of the "length" attribute
const uint8_t kToshibaAcMinLength = 6; ///< Min Nr. of bytes in a message.
///< Known lengths are:
///< 1 (56 bit message)
///< 3 (72 bit message)
///< 4 (80 bit message)
// Byte[3] - The bit-inverted value of the "length" byte.
const uint16_t kToshibaAcInvertedLength = 4; ///< Nr. of leading bytes in
///< inverted pairs.
// Byte[4]
const uint8_t kToshibaAcShortMsgBit = 5; ///< Mask 0b00x00000
const uint8_t kToshibaAcLongMsgBit = 3; ///< Mask 0b00001000
// Byte[5]
const uint8_t kToshibaAcSwingOffset = 0; ///< Bit offset.
const uint8_t kToshibaAcSwingSize = 2; ///< Mask 0b000000xx
const uint8_t kToshibaAcSwingStep = 0; ///< 0b00
const uint8_t kToshibaAcSwingOn = 1; ///< 0b01
const uint8_t kToshibaAcSwingOff = 2; ///< 0b10
const uint8_t kToshibaAcTempOffset = 4; ///< Bit offset.
const uint8_t kToshibaAcTempSize = 4; ///< Mask 0bxxxx0000
const uint8_t kToshibaAcMinTemp = 17; ///< 17C
const uint8_t kToshibaAcMaxTemp = 30; ///< 30C
// Byte[6]
const uint8_t kToshibaAcModeOffset = 0;
const uint8_t kToshibaAcModeSize = 2; // Nr. of bits
const uint8_t kToshibaAcAuto = 0;
const uint8_t kToshibaAcCool = 1;
const uint8_t kToshibaAcDry = 2;
const uint8_t kToshibaAcHeat = 3;
const uint8_t kToshibaAcPowerOffset = 2;
const uint8_t kToshibaAcModeSize = 3; // Mask 0b00000xxx
const uint8_t kToshibaAcAuto = 0; // 0b000
const uint8_t kToshibaAcCool = 1; // 0b001
const uint8_t kToshibaAcDry = 2; // 0b010
const uint8_t kToshibaAcHeat = 3; // 0b011
const uint8_t kToshibaAcFan = 4; // 0b100
const uint8_t kToshibaAcOff = 7; // 0b111
const uint8_t kToshibaAcFanOffset = 5;
const uint8_t kToshibaAcFanSize = 3; // Nr. of bits
const uint8_t kToshibaAcFanAuto = 0b000;
const uint8_t kToshibaAcFanMin = 0b001;
const uint8_t kToshibaAcFanMed = 0b011;
const uint8_t kToshibaAcFanMax = 0b101;
const uint8_t kToshibaAcTempOffset = 4;
const uint8_t kToshibaAcTempSize = 4; // Nr. of bits
const uint8_t kToshibaAcMinTemp = 17; // 17C
const uint8_t kToshibaAcMaxTemp = 30; // 30C
const uint8_t kToshibaAcFanSize = 3; // Mask 0bxxx00000
const uint8_t kToshibaAcFanAuto = 0; // 0b000
const uint8_t kToshibaAcFanMin = 1; // 0b001
const uint8_t kToshibaAcFanMed = 3; // 0b011
const uint8_t kToshibaAcFanMax = 5; // 0b101
// Byte[8] (Checksum for 72 bit messages, Eco/Turbo for long 80 bit messages)
const uint8_t kToshibaAcEcoTurboOffset = 0;
const uint8_t kToshibaAcEcoTurboSize = 2; // Mask 0b000000xx
const uint8_t kToshibaAcTurboOn = 1; // 0b01
const uint8_t kToshibaAcEconoOn = 3; // 0b11
// Byte[last] - Checksum (xor)
// Legacy defines. (Deperecated)
#define TOSHIBA_AC_AUTO kToshibaAcAuto
@ -81,12 +119,21 @@ class IRToshibaAC {
uint8_t getTemp(void);
void setFan(const uint8_t speed);
uint8_t getFan(void);
void setTurbo(const bool on);
bool getTurbo(void);
void setEcono(const bool on);
bool getEcono(void);
void setMode(const uint8_t mode);
uint8_t getMode(const bool useRaw = false);
uint8_t getMode(const bool raw = false);
void setRaw(const uint8_t newState[]);
uint8_t* getRaw(void);
static uint16_t getInternalStateLength(const uint8_t state[],
const uint16_t size);
uint16_t getStateLength(void);
static bool validChecksum(const uint8_t state[],
const uint16_t length = kToshibaACStateLength);
uint8_t getSwing(const bool raw = true);
void setSwing(const uint8_t setting);
uint8_t convertMode(const stdAc::opmode_t mode);
uint8_t convertFan(const stdAc::fanspeed_t speed);
static stdAc::opmode_t toCommonMode(const uint8_t mode);
@ -102,11 +149,17 @@ class IRToshibaAC {
IRsendTest _irsend; ///< Instance of the testing IR send class
/// @endcond
#endif // UNIT_TEST
uint8_t remote_state[kToshibaACStateLength]; ///< The state in IR code form.
uint8_t remote_state[kToshibaACStateLengthLong]; ///< The state in code form.
uint8_t backup[kToshibaACStateLengthLong]; ///< A backup copy of the state.
uint8_t prev_mode; ///< Store of the previously set mode.
bool _send_swing; ///< Flag indicating if we need to send a swing message.
uint8_t _swing_mode; ///< The saved swing state/mode/command.
void checksum(const uint16_t length = kToshibaACStateLength);
static uint8_t calcChecksum(const uint8_t state[],
const uint16_t length = kToshibaACStateLength);
uint8_t mode_state;
void setStateLength(const uint16_t size);
void _backupState(void);
void _restoreState(void);
};
#endif // IR_TOSHIBA_H_

View File

@ -360,6 +360,9 @@
// Compound words/phrases/descriptions from pre-defined words.
// Note: Obviously these need to be defined *after* their component words.
#ifndef D_STR_ECONOTOGGLE
#define D_STR_ECONOTOGGLE D_STR_ECONO " " D_STR_TOGGLE
#endif // D_STR_ECONOTOGGLE
#ifndef D_STR_EYEAUTO
#define D_STR_EYEAUTO D_STR_EYE " " D_STR_AUTO
#endif // D_STR_EYEAUTO
@ -685,6 +688,9 @@
#ifndef D_STR_SANYO
#define D_STR_SANYO "SANYO"
#endif // D_STR_SANYO
#ifndef D_STR_SANYO_AC
#define D_STR_SANYO_AC "SANYO_AC"
#endif // D_STR_SANYO_AC
#ifndef D_STR_SANYO_LC7461
#define D_STR_SANYO_LC7461 "SANYO_LC7461"
#endif // D_STR_SANYO_LC7461

View File

@ -1,6 +1,7 @@
// Copyright 2019 David Conran
#include <string>
#include "ir_Airwell.h"
#include "ir_Amcor.h"
#include "ir_Argo.h"
#include "ir_Carrier.h"
@ -39,10 +40,33 @@
// Tests for IRac class.
TEST(TestIRac, Airwell) {
IRAirwellAc ac(kGpioUnused);
IRac irac(kGpioUnused);
IRrecv capture(kGpioUnused);
char expected[] =
"Power Toggle: On, Mode: 3 (Auto), Fan: 1 (Medium), Temp: 18C";
ac.begin();
irac.airwell(&ac,
true, // Power
stdAc::opmode_t::kAuto, // Mode
18, // Celsius
stdAc::fanspeed_t::kMedium); // Fan speed
ASSERT_EQ(expected, ac.toString());
ac._irsend.makeDecodeResult();
EXPECT_TRUE(capture.decode(&ac._irsend.capture));
ASSERT_EQ(AIRWELL, ac._irsend.capture.decode_type);
ASSERT_EQ(kAirwellBits, ac._irsend.capture.bits);
ASSERT_EQ(expected, IRAcUtils::resultAcToString(&ac._irsend.capture));
stdAc::state_t r, p;
ASSERT_TRUE(IRAcUtils::decodeToState(&ac._irsend.capture, &r, &p));
}
TEST(TestIRac, Amcor) {
IRAmcorAc ac(0);
IRac irac(0);
IRrecv capture(0);
IRAmcorAc ac(kGpioUnused);
IRac irac(kGpioUnused);
IRrecv capture(kGpioUnused);
char expected[] =
"Power: On, Mode: 5 (Auto), Fan: 3 (High), Temp: 19C, Max: Off";
@ -63,8 +87,8 @@ TEST(TestIRac, Amcor) {
}
TEST(TestIRac, Argo) {
IRArgoAC ac(0);
IRac irac(0);
IRArgoAC ac(kGpioUnused);
IRac irac(kGpioUnused);
ac.begin();
irac.argo(&ac,
@ -118,9 +142,9 @@ TEST(TestIRac, Carrier64) {
}
TEST(TestIRac, Coolix) {
IRCoolixAC ac(0);
IRac irac(0);
IRrecv capture(0);
IRCoolixAC ac(kGpioUnused);
IRac irac(kGpioUnused);
IRrecv capture(kGpioUnused);
char expected[] =
"Power: On, Mode: 3 (Heat), Fan: 1 (Max), Temp: 21C, Zone Follow: Off, "
"Sensor Temp: Off";
@ -221,9 +245,9 @@ TEST(TestIRac, Corona) {
}
TEST(TestIRac, Daikin) {
IRDaikinESP ac(0);
IRac irac(0);
IRrecv capture(0);
IRDaikinESP ac(kGpioUnused);
IRac irac(kGpioUnused);
IRrecv capture(kGpioUnused);
char expected[] =
"Power: On, Mode: 3 (Cool), Temp: 19C, Fan: 5 (High), Powerful: Off, "
"Quiet: Off, Sensor: Off, Mould: On, Comfort: Off, "
@ -254,9 +278,9 @@ TEST(TestIRac, Daikin) {
}
TEST(TestIRac, Daikin128) {
IRDaikin128 ac(0);
IRac irac(0);
IRrecv capture(0);
IRDaikin128 ac(kGpioUnused);
IRac irac(kGpioUnused);
IRrecv capture(kGpioUnused);
char expected[] =
"Power Toggle: On, Mode: 8 (Heat), Temp: 27C, Fan: 9 (Quiet), "
"Powerful: Off, Quiet: On, Swing(V): On, Sleep: On, "
@ -287,9 +311,9 @@ TEST(TestIRac, Daikin128) {
}
TEST(TestIRac, Daikin152) {
IRDaikin152 ac(0);
IRac irac(0);
IRrecv capture(0);
IRDaikin152 ac(kGpioUnused);
IRac irac(kGpioUnused);
IRrecv capture(kGpioUnused);
char expected[] =
"Power: On, Mode: 3 (Cool), Temp: 27C, Fan: 3 (Medium), Swing(V): On, "
"Powerful: Off, Quiet: Off, Econo: On, Sensor: Off, Comfort: Off";
@ -315,9 +339,9 @@ TEST(TestIRac, Daikin152) {
}
TEST(TestIRac, Daikin160) {
IRDaikin160 ac(0);
IRac irac(0);
IRrecv capture(0);
IRDaikin160 ac(kGpioUnused);
IRac irac(kGpioUnused);
IRrecv capture(kGpioUnused);
char expected[] =
"Power: On, Mode: 2 (Dry), Temp: 23C, Fan: 1 (Low), "
"Swing(V): 3 (Middle)";
@ -340,9 +364,9 @@ TEST(TestIRac, Daikin160) {
}
TEST(TestIRac, Daikin176) {
IRDaikin176 ac(0);
IRac irac(0);
IRrecv capture(0);
IRDaikin176 ac(kGpioUnused);
IRac irac(kGpioUnused);
IRrecv capture(kGpioUnused);
char expected[] =
"Power: On, Mode: 7 (Cool), Temp: 26C, Fan: 1 (Low), Swing(H): 5 (Auto)";
@ -364,9 +388,9 @@ TEST(TestIRac, Daikin176) {
}
TEST(TestIRac, Daikin2) {
IRDaikin2 ac(0);
IRac irac(0);
IRrecv capture(0);
IRDaikin2 ac(kGpioUnused);
IRac irac(kGpioUnused);
IRrecv capture(kGpioUnused);
char expected[] =
"Power: On, Mode: 3 (Cool), Temp: 19C, Fan: 1 (Low), "
"Swing(V): 14 (Auto), Swing(H): 170 (UNKNOWN), Clock: 00:00, "
@ -402,9 +426,9 @@ TEST(TestIRac, Daikin2) {
}
TEST(TestIRac, Daikin216) {
IRDaikin216 ac(0);
IRac irac(0);
IRrecv capture(0);
IRDaikin216 ac(kGpioUnused);
IRac irac(kGpioUnused);
IRrecv capture(kGpioUnused);
char expected[] =
"Power: On, Mode: 4 (Heat), Temp: 31C, Fan: 11 (Quiet), "
"Swing(H): On, Swing(V): On, Quiet: On, Powerful: Off";
@ -511,9 +535,9 @@ TEST(TestIRac, Electra) {
}
TEST(TestIRac, Fujitsu) {
IRFujitsuAC ac(0);
IRac irac(0);
IRrecv capture(0);
IRFujitsuAC ac(kGpioUnused);
IRac irac(kGpioUnused);
IRrecv capture(kGpioUnused);
std::string ardb1_expected =
"Model: 2 (ARDB1), Power: On, Mode: 1 (Cool), Temp: 19C, "
"Fan: 2 (Medium), Command: N/A";
@ -591,9 +615,9 @@ TEST(TestIRac, Fujitsu) {
}
TEST(TestIRac, Goodweather) {
IRGoodweatherAc ac(0);
IRac irac(0);
IRrecv capture(0);
IRGoodweatherAc ac(kGpioUnused);
IRac irac(kGpioUnused);
IRrecv capture(kGpioUnused);
char expected[] =
"Power: On, Mode: 1 (Cool), Temp: 19C, Fan: 2 (Medium), Turbo: Toggle, "
"Light: Toggle, Sleep: Toggle, Swing: 1 (Slow), Command: 0 (Power)";
@ -619,9 +643,9 @@ TEST(TestIRac, Goodweather) {
}
TEST(TestIRac, Gree) {
IRGreeAC ac(0);
IRac irac(0);
IRrecv capture(0);
IRGreeAC ac(kGpioUnused);
IRac irac(kGpioUnused);
IRrecv capture(kGpioUnused);
char expected[] =
"Model: 1 (YAW1F), Power: On, Mode: 1 (Cool), Temp: 71F, "
"Fan: 2 (Medium), Turbo: Off, IFeel: Off, WiFi: Off, XFan: On, "
@ -652,9 +676,9 @@ TEST(TestIRac, Gree) {
}
TEST(TestIRac, Haier) {
IRHaierAC ac(0);
IRac irac(0);
IRrecv capture(0);
IRHaierAC ac(kGpioUnused);
IRac irac(kGpioUnused);
IRrecv capture(kGpioUnused);
char expected[] =
"Command: 1 (On), Mode: 1 (Cool), Temp: 24C, Fan: 2 (Medium), "
"Swing: 1 (Up), Sleep: On, Health: On, Clock: 13:45, "
@ -682,9 +706,9 @@ TEST(TestIRac, Haier) {
TEST(TestIRac, HaierYrwo2) {
IRHaierACYRW02 ac(0);
IRac irac(0);
IRrecv capture(0);
IRHaierACYRW02 ac(kGpioUnused);
IRac irac(kGpioUnused);
IRrecv capture(kGpioUnused);
char expected[] =
"Power: On, Button: 5 (Power), Mode: 1 (Cool), Temp: 23C, "
"Fan: 2 (Medium), Turbo: 1 (High), Swing: 1 (Highest), Sleep: On, "
@ -711,9 +735,9 @@ TEST(TestIRac, HaierYrwo2) {
}
TEST(TestIRac, Hitachi) {
IRHitachiAc ac(0);
IRac irac(0);
IRrecv capture(0);
IRHitachiAc ac(kGpioUnused);
IRac irac(kGpioUnused);
IRrecv capture(kGpioUnused);
char expected[] =
"Power: On, Mode: 2 (Auto), Temp: 22C, Fan: 3 (Medium), "
"Swing(V): Off, Swing(H): On";
@ -821,9 +845,9 @@ TEST(TestIRac, Hitachi344) {
}
TEST(TestIRac, Hitachi424) {
IRHitachiAc424 ac(0);
IRac irac(0);
IRrecv capture(0);
IRHitachiAc424 ac(kGpioUnused);
IRac irac(kGpioUnused);
IRrecv capture(kGpioUnused);
char expected[] =
"Power: On, Mode: 6 (Heat), Temp: 25C, Fan: 6 (Max), "
"Button: 19 (Power/Mode), Swing(V) Toggle: Off";
@ -866,9 +890,9 @@ TEST(TestIRac, Hitachi424) {
}
TEST(TestIRac, Kelvinator) {
IRKelvinatorAC ac(0);
IRac irac(0);
IRrecv capture(0);
IRKelvinatorAC ac(kGpioUnused);
IRac irac(kGpioUnused);
IRrecv capture(kGpioUnused);
char expected[] =
"Power: On, Mode: 1 (Cool), Temp: 19C, Fan: 3 (Medium), Turbo: Off, "
"Quiet: Off, XFan: On, Ion: On, Light: On, "
@ -899,9 +923,9 @@ TEST(TestIRac, Kelvinator) {
}
TEST(TestIRac, LG) {
IRLgAc ac(0);
IRac irac(0);
IRrecv capture(0);
IRLgAc ac(kGpioUnused);
IRac irac(kGpioUnused);
IRrecv capture(kGpioUnused);
char expected[] =
"Model: 1 (GE6711AR2853M), "
"Power: On, Mode: 1 (Dry), Temp: 27C, Fan: 2 (Medium)";
@ -925,12 +949,12 @@ TEST(TestIRac, LG) {
}
TEST(TestIRac, Midea) {
IRMideaAC ac(0);
IRac irac(0);
IRrecv capture(0);
IRMideaAC ac(kGpioUnused);
IRac irac(kGpioUnused);
IRrecv capture(kGpioUnused);
char expected[] =
"Power: On, Mode: 1 (Dry), Celsius: On, Temp: 27C/80F, Fan: 2 (Medium), "
"Sleep: On, Swing(V) Toggle: Off";
"Sleep: On, Swing(V) Toggle: Off, Econo Toggle: Off";
ac.begin();
irac.midea(&ac,
@ -940,6 +964,7 @@ TEST(TestIRac, Midea) {
27, // Degrees
stdAc::fanspeed_t::kMedium, // Fan speed
stdAc::swingv_t::kOff, // Swing(V)
false, // Econo
8 * 60 + 0); // Sleep time
ASSERT_EQ(expected, ac.toString());
@ -953,9 +978,9 @@ TEST(TestIRac, Midea) {
}
TEST(TestIRac, Mitsubishi) {
IRMitsubishiAC ac(0);
IRac irac(0);
IRrecv capture(0);
IRMitsubishiAC ac(kGpioUnused);
IRac irac(kGpioUnused);
IRrecv capture(kGpioUnused);
char expected[] =
"Power: On, Mode: 3 (Cool), Temp: 20C, Fan: 2 (Medium), "
"Swing(V): 0 (Auto), Swing(H): 3 (UNKNOWN), "
@ -982,9 +1007,9 @@ TEST(TestIRac, Mitsubishi) {
}
TEST(TestIRac, Mitsubishi136) {
IRMitsubishi136 ac(0);
IRac irac(0);
IRrecv capture(0);
IRMitsubishi136 ac(kGpioUnused);
IRac irac(kGpioUnused);
IRrecv capture(kGpioUnused);
char expected[] =
"Power: On, Mode: 5 (Dry), Temp: 22C, Fan: 3 (High), "
"Swing(V): 3 (Highest), Quiet: Off";
@ -1008,9 +1033,9 @@ TEST(TestIRac, Mitsubishi136) {
}
TEST(TestIRac, MitsubishiHeavy88) {
IRMitsubishiHeavy88Ac ac(0);
IRac irac(0);
IRrecv capture(0);
IRMitsubishiHeavy88Ac ac(kGpioUnused);
IRac irac(kGpioUnused);
IRrecv capture(kGpioUnused);
char expected[] =
"Power: On, Mode: 1 (Cool), Temp: 21C, Fan: 3 (Med), "
"Swing(V): 4 (Auto), Swing(H): 0 (Off), Turbo: Off, Econo: Off, "
@ -1038,9 +1063,9 @@ TEST(TestIRac, MitsubishiHeavy88) {
}
TEST(TestIRac, MitsubishiHeavy152) {
IRMitsubishiHeavy152Ac ac(0);
IRac irac(0);
IRrecv capture(0);
IRMitsubishiHeavy152Ac ac(kGpioUnused);
IRac irac(kGpioUnused);
IRrecv capture(kGpioUnused);
char expected[] =
"Power: On, Mode: 1 (Cool), Temp: 20C, Fan: 6 (Econo), "
"Swing(V): 6 (Off), Swing(H): 0 (Auto), Silent: On, Turbo: Off, "
@ -1071,9 +1096,9 @@ TEST(TestIRac, MitsubishiHeavy152) {
}
TEST(TestIRac, Neoclima) {
IRNeoclimaAc ac(0);
IRac irac(0);
IRrecv capture(0);
IRNeoclimaAc ac(kGpioUnused);
IRac irac(kGpioUnused);
IRrecv capture(kGpioUnused);
char expected[] =
"Power: On, Mode: 1 (Cool), Temp: 20C, Fan: 3 (Low), "
"Swing(V): Off, Swing(H): On, Sleep: On, Turbo: Off, Hold: Off, Ion: On, "
@ -1103,9 +1128,9 @@ TEST(TestIRac, Neoclima) {
}
TEST(TestIRac, Panasonic) {
IRPanasonicAc ac(0);
IRac irac(0);
IRrecv capture(0);
IRPanasonicAc ac(kGpioUnused);
IRac irac(kGpioUnused);
IRrecv capture(kGpioUnused);
char expected_nke[] =
"Model: 2 (NKE), Power: On, Mode: 4 (Heat), Temp: 28C, Fan: 2 (Medium), "
"Swing(V): 15 (Auto), Swing(H): 6 (Middle), Quiet: On, "
@ -1161,9 +1186,9 @@ TEST(TestIRac, Panasonic) {
}
TEST(TestIRac, Samsung) {
IRSamsungAc ac(0);
IRac irac(0);
IRrecv capture(0);
IRSamsungAc ac(kGpioUnused);
IRac irac(kGpioUnused);
IRrecv capture(kGpioUnused);
char expected[] =
"Power: On, Mode: 0 (Auto), Temp: 28C, Fan: 6 (Auto), Swing: On, "
"Beep: On, Clean: On, Quiet: On, Powerful: Off, Breeze: Off, "
@ -1223,10 +1248,38 @@ TEST(TestIRac, Samsung) {
ASSERT_TRUE(IRAcUtils::decodeToState(&ac._irsend.capture, &r, &p));
}
TEST(TestIRac, Sanyo) {
IRSanyoAc ac(kGpioUnused);
IRac irac(kGpioUnused);
IRrecv capture(kGpioUnused);
char expected[] =
"Power: On, Mode: 2 (Cool), Temp: 28C, Fan: 3 (Medium), "
"Swing(V): 7 (Highest), Sleep: On, Beep: On, "
"Sensor: Wall, Sensor Temp: 28C, Off Timer: Off";
ac.begin();
irac.sanyo(&ac,
true, // Power
stdAc::opmode_t::kCool, // Mode
28, // Celsius
stdAc::fanspeed_t::kMedium, // Fan speed
stdAc::swingv_t::kHighest, // Vertical Swing
true, // Beep
17); // Sleep
ASSERT_EQ(expected, ac.toString());
ac._irsend.makeDecodeResult();
EXPECT_TRUE(capture.decode(&ac._irsend.capture));
ASSERT_EQ(SANYO_AC, ac._irsend.capture.decode_type);
ASSERT_EQ(kSanyoAcBits, ac._irsend.capture.bits);
ASSERT_EQ(expected, IRAcUtils::resultAcToString(&ac._irsend.capture));
stdAc::state_t r, p;
ASSERT_TRUE(IRAcUtils::decodeToState(&ac._irsend.capture, &r, &p));
}
TEST(TestIRac, Sharp) {
IRSharpAc ac(0);
IRac irac(0);
IRrecv capture(0);
IRSharpAc ac(kGpioUnused);
IRac irac(kGpioUnused);
IRrecv capture(kGpioUnused);
char expected[] =
"Power: On, Mode: 2 (Cool), Temp: 28C, Fan: 3 (Medium), "
"Turbo: Off, Swing(V) Toggle: On, Ion: On, Econo: -, Clean: Off";
@ -1253,9 +1306,9 @@ TEST(TestIRac, Sharp) {
}
TEST(TestIRac, Tcl112) {
IRTcl112Ac ac(0);
IRac irac(0);
IRrecv capture(0);
IRTcl112Ac ac(kGpioUnused);
IRac irac(kGpioUnused);
IRrecv capture(kGpioUnused);
char expected[] =
"Power: On, Mode: 3 (Cool), Temp: 20C, Fan: 3 (Medium), Econo: On, "
"Health: On, Light: On, Turbo: Off, Swing(H): On, Swing(V): Off";
@ -1283,9 +1336,9 @@ TEST(TestIRac, Tcl112) {
}
TEST(TestIRac, Teco) {
IRTecoAc ac(0);
IRac irac(0);
IRrecv capture(0);
IRTecoAc ac(kGpioUnused);
IRac irac(kGpioUnused);
IRrecv capture(kGpioUnused);
char expected[] =
"Power: On, Mode: 0 (Auto), Temp: 21C, Fan: 2 (Medium), Sleep: On, "
"Swing: On, Light: On, Humid: Off, Save: Off, Timer: Off";
@ -1310,31 +1363,82 @@ TEST(TestIRac, Teco) {
}
TEST(TestIRac, Toshiba) {
IRToshibaAC ac(0);
IRac irac(0);
IRrecv capture(0);
char expected[] = "Power: On, Mode: 2 (Dry), Temp: 29C, Fan: 2 (UNKNOWN)";
IRToshibaAC ac(kGpioUnused);
IRac irac(kGpioUnused);
IRrecv capture(kGpioUnused);
char expected[] =
"Temp: 29C, Power: On, Mode: 2 (Dry), Fan: 2 (UNKNOWN), "
"Turbo: Off, Econo: On";
ac.begin();
irac.toshiba(&ac,
true, // Power
stdAc::opmode_t::kDry, // Mode
29, // Celsius
stdAc::fanspeed_t::kLow); // Fan speed
stdAc::fanspeed_t::kLow, // Fan speed
stdAc::swingv_t::kOff, // Vertical Swing
false, // Turbo
true); // Econo
ASSERT_EQ(expected, ac.toString());
ASSERT_EQ(kToshibaACStateLengthLong, ac.getStateLength());
ac._irsend.makeDecodeResult();
EXPECT_TRUE(capture.decode(&ac._irsend.capture));
ASSERT_EQ(TOSHIBA_AC, ac._irsend.capture.decode_type);
ASSERT_EQ(kToshibaACBits, ac._irsend.capture.bits);
ASSERT_EQ(kToshibaACBitsLong, ac._irsend.capture.bits);
ASSERT_EQ(expected, IRAcUtils::resultAcToString(&ac._irsend.capture));
stdAc::state_t r, p;
ASSERT_TRUE(IRAcUtils::decodeToState(&ac._irsend.capture, &r, &p));
EXPECT_EQ(
"f38000d50"
"m4400s4300"
"m580s1600m580s1600m580s1600m580s1600m580s490m580s490m580s1600m580s490"
"m580s490m580s490m580s490m580s490m580s1600m580s1600m580s490m580s1600"
"m580s490m580s490m580s490m580s490m580s490m580s1600m580s490m580s490"
"m580s1600m580s1600m580s1600m580s1600m580s1600m580s490m580s1600m580s1600"
"m580s490m580s490m580s490m580s490m580s1600m580s490m580s490m580s1600"
"m580s1600m580s1600m580s490m580s490m580s490m580s490m580s490m580s490"
"m580s490m580s1600m580s1600m580s490m580s490m580s490m580s1600m580s490"
"m580s490m580s490m580s490m580s490m580s490m580s490m580s490m580s490"
"m580s490m580s490m580s490m580s490m580s490m580s490m580s1600m580s1600"
"m580s1600m580s490m580s1600m580s490m580s1600m580s490m580s490m580s490"
"m580s7400"
"m4400s4300"
"m580s1600m580s1600m580s1600m580s1600m580s490m580s490m580s1600m580s490"
"m580s490m580s490m580s490m580s490m580s1600m580s1600m580s490m580s1600"
"m580s490m580s490m580s490m580s490m580s490m580s1600m580s490m580s490"
"m580s1600m580s1600m580s1600m580s1600m580s1600m580s490m580s1600m580s1600"
"m580s490m580s490m580s490m580s490m580s1600m580s490m580s490m580s1600"
"m580s1600m580s1600m580s490m580s490m580s490m580s490m580s490m580s490"
"m580s490m580s1600m580s1600m580s490m580s490m580s490m580s1600m580s490"
"m580s490m580s490m580s490m580s490m580s490m580s490m580s490m580s490"
"m580s490m580s490m580s490m580s490m580s490m580s490m580s1600m580s1600"
"m580s1600m580s490m580s1600m580s490m580s1600m580s490m580s490m580s490"
"m580s7400"
"m4400s4300"
"m580s1600m580s1600m580s1600m580s1600m580s490m580s490m580s1600m580s490"
"m580s490m580s490m580s490m580s490m580s1600m580s1600m580s490m580s1600"
"m580s490m580s490m580s490m580s490m580s490m580s490m580s490m580s1600"
"m580s1600m580s1600m580s1600m580s1600m580s1600m580s1600m580s1600m580s490"
"m580s490m580s490m580s1600m580s490m580s490m580s490m580s490m580s1600"
"m580s490m580s490m580s490m580s490m580s490m580s490m580s1600m580s490"
"m580s490m580s490m580s1600m580s490m580s490m580s490m580s1600m580s1600"
"m580s7400"
"m4400s4300"
"m580s1600m580s1600m580s1600m580s1600m580s490m580s490m580s1600m580s490"
"m580s490m580s490m580s490m580s490m580s1600m580s1600m580s490m580s1600"
"m580s490m580s490m580s490m580s490m580s490m580s490m580s490m580s1600"
"m580s1600m580s1600m580s1600m580s1600m580s1600m580s1600m580s1600m580s490"
"m580s490m580s490m580s1600m580s490m580s490m580s490m580s490m580s1600"
"m580s490m580s490m580s490m580s490m580s490m580s490m580s1600m580s490"
"m580s490m580s490m580s1600m580s490m580s490m580s490m580s1600m580s1600"
"m580s7400",
ac._irsend.outputStr());
}
TEST(TestIRac, Trotec) {
IRTrotecESP ac(0);
IRac irac(0);
IRrecv capture(0);
IRTrotecESP ac(kGpioUnused);
IRac irac(kGpioUnused);
IRrecv capture(kGpioUnused);
char expected[] =
"Power: On, Mode: 1 (Cool), Temp: 18C, Fan: 3 (High), Sleep: On";
@ -1361,9 +1465,9 @@ TEST(TestIRac, Trotec) {
}
TEST(TestIRac, Vestel) {
IRVestelAc ac(0);
IRac irac(0);
IRrecv capture(0);
IRVestelAc ac(kGpioUnused);
IRac irac(kGpioUnused);
IRrecv capture(kGpioUnused);
char expected[] =
"Power: On, Mode: 0 (Auto), Temp: 22C, Fan: 5 (Low), Sleep: On, "
"Turbo: Off, Ion: On, Swing: On";
@ -1452,9 +1556,9 @@ TEST(TestIRac, Vestel) {
TEST(TestIRac, Whirlpool) {
IRWhirlpoolAc ac(0);
IRac irac(0);
IRrecv capture(0);
IRWhirlpoolAc ac(kGpioUnused);
IRac irac(kGpioUnused);
IRrecv capture(kGpioUnused);
char expected[] =
"Model: 1 (DG11J13A), Power Toggle: On, Mode: 1 (Auto), Temp: 21C, "
"Fan: 3 (Low), Swing: On, Light: On, Clock: 23:58, On Timer: Off, "
@ -1755,9 +1859,9 @@ TEST(TestIRac, Issue821) {
next = prev;
next.light = true;
IRac irac(0);
IRrecv capture(0);
IRCoolixAC ac(0);
IRac irac(kGpioUnused);
IRrecv capture(kGpioUnused);
IRCoolixAC ac(kGpioUnused);
ac.begin();
result = irac.handleToggles(next, &prev);
@ -1844,9 +1948,9 @@ TEST(TestIRac, Issue1001) {
desired = prev;
desired.power = false;
IRac irac(0);
IRrecv capture(0);
IRWhirlpoolAc ac(0);
IRac irac(kGpioUnused);
IRrecv capture(kGpioUnused);
IRWhirlpoolAc ac(kGpioUnused);
ac.begin();
ASSERT_TRUE(prev.power);

View File

@ -220,32 +220,32 @@ TEST(TestResultToSourceCode, ComplexProtocols) {
ASSERT_EQ(TOSHIBA_AC, irsend.capture.decode_type);
ASSERT_EQ(kToshibaACBits, irsend.capture.bits);
EXPECT_EQ(
"uint16_t rawData[296] = {4400, 4300, 542, 1622, 542, 1622, "
"542, 1622, 542, 1622, 542, 472, 542, 472, 542, 1622, 542, 472, "
"542, 472, 542, 472, 542, 472, 542, 472, 542, 1622, 542, 1622, "
"542, 472, 542, 1622, 542, 472, 542, 472, 542, 472, 542, 472, "
"542, 472, 542, 472, 542, 1622, 542, 1622, 542, 1622, 542, 1622, "
"542, 1622, 542, 1622, 542, 1622, 542, 1622, 542, 472, 542, 472, "
"542, 472, 542, 472, 542, 472, 542, 472, 542, 472, 542, 472, "
"542, 472, 542, 1622, 542, 472, 542, 472, 542, 472, 542, 472, "
"542, 472, 542, 472, 542, 472, 542, 472, 542, 472, 542, 472, "
"542, 472, 542, 472, 542, 472, 542, 472, 542, 472, 542, 472, "
"542, 472, 542, 472, 542, 472, 542, 472, 542, 472, 542, 472, "
"542, 472, 542, 472, 542, 472, 542, 472, 542, 472, 542, 472, "
"542, 472, 542, 472, 542, 472, 542, 1622, 542, 7048, 4400, 4300, "
"542, 1622, 542, 1622, 542, 1622, 542, 1622, 542, 472, 542, 472, "
"542, 1622, 542, 472, 542, 472, 542, 472, 542, 472, 542, 472, "
"542, 1622, 542, 1622, 542, 472, 542, 1622, 542, 472, 542, 472, "
"542, 472, 542, 472, 542, 472, 542, 472, 542, 1622, 542, 1622, "
"542, 1622, 542, 1622, 542, 1622, 542, 1622, 542, 1622, 542, 1622, "
"542, 472, 542, 472, 542, 472, 542, 472, 542, 472, 542, 472, "
"542, 472, 542, 472, 542, 472, 542, 1622, 542, 472, 542, 472, "
"542, 472, 542, 472, 542, 472, 542, 472, 542, 472, 542, 472, "
"542, 472, 542, 472, 542, 472, 542, 472, 542, 472, 542, 472, "
"542, 472, 542, 472, 542, 472, 542, 472, 542, 472, 542, 472, "
"542, 472, 542, 472, 542, 472, 542, 472, 542, 472, 542, 472, "
"542, 472, 542, 472, 542, 472, 542, 472, 542, 472, 542, 1622, "
"542, 7048 }; // TOSHIBA_AC\n"
"uint16_t rawData[296] = {4400, 4300, 580, 1600, 580, 1600, "
"580, 1600, 580, 1600, 580, 490, 580, 490, 580, 1600, 580, 490, "
"580, 490, 580, 490, 580, 490, 580, 490, 580, 1600, 580, 1600, "
"580, 490, 580, 1600, 580, 490, 580, 490, 580, 490, 580, 490, "
"580, 490, 580, 490, 580, 1600, 580, 1600, 580, 1600, 580, 1600, "
"580, 1600, 580, 1600, 580, 1600, 580, 1600, 580, 490, 580, 490, "
"580, 490, 580, 490, 580, 490, 580, 490, 580, 490, 580, 490, "
"580, 490, 580, 1600, 580, 490, 580, 490, 580, 490, 580, 490, "
"580, 490, 580, 490, 580, 490, 580, 490, 580, 490, 580, 490, "
"580, 490, 580, 490, 580, 490, 580, 490, 580, 490, 580, 490, "
"580, 490, 580, 490, 580, 490, 580, 490, 580, 490, 580, 490, "
"580, 490, 580, 490, 580, 490, 580, 490, 580, 490, 580, 490, "
"580, 490, 580, 490, 580, 490, 580, 1600, 580, 7400, 4400, 4300, "
"580, 1600, 580, 1600, 580, 1600, 580, 1600, 580, 490, 580, 490, "
"580, 1600, 580, 490, 580, 490, 580, 490, 580, 490, 580, 490, "
"580, 1600, 580, 1600, 580, 490, 580, 1600, 580, 490, 580, 490, "
"580, 490, 580, 490, 580, 490, 580, 490, 580, 1600, 580, 1600, "
"580, 1600, 580, 1600, 580, 1600, 580, 1600, 580, 1600, 580, 1600, "
"580, 490, 580, 490, 580, 490, 580, 490, 580, 490, 580, 490, "
"580, 490, 580, 490, 580, 490, 580, 1600, 580, 490, 580, 490, "
"580, 490, 580, 490, 580, 490, 580, 490, 580, 490, 580, 490, "
"580, 490, 580, 490, 580, 490, 580, 490, 580, 490, 580, 490, "
"580, 490, 580, 490, 580, 490, 580, 490, 580, 490, 580, 490, "
"580, 490, 580, 490, 580, 490, 580, 490, 580, 490, 580, 490, "
"580, 490, 580, 490, 580, 490, 580, 490, 580, 490, 580, 1600, "
"580, 7400 }; // TOSHIBA_AC\n"
"uint8_t state[9] = {0xF2, 0x0D, 0x03, 0xFC, 0x01, 0x00, 0x00, 0x00, "
"0x01};\n",
resultToSourceCode(&irsend.capture));
@ -744,3 +744,35 @@ TEST(TestUtils, setBits64Bit) {
irutils::setBits(&data, 32, 4, 0b1001);
EXPECT_EQ(0x4000000900000013, data);
}
TEST(TestUtils, InvertedBytePairs) {
const uint8_t correct[] = {0x00, 0xFF, 0x01, 0xFE, 0xAA, 0x55};
uint8_t wrong[] = {0x00, 0xFF, 0x01, 0xFD, 0xAA, 0x55};
ASSERT_TRUE(irutils::checkInvertedBytePairs(correct, 6));
ASSERT_TRUE(irutils::checkInvertedBytePairs(correct, 5));
ASSERT_TRUE(irutils::checkInvertedBytePairs(correct, 4));
ASSERT_TRUE(irutils::checkInvertedBytePairs(correct, 3));
ASSERT_TRUE(irutils::checkInvertedBytePairs(correct, 2));
ASSERT_TRUE(irutils::checkInvertedBytePairs(correct, 1));
ASSERT_TRUE(irutils::checkInvertedBytePairs(correct, 0));
ASSERT_FALSE(irutils::checkInvertedBytePairs(wrong, 6));
ASSERT_FALSE(irutils::checkInvertedBytePairs(wrong, 5));
ASSERT_FALSE(irutils::checkInvertedBytePairs(wrong, 4));
ASSERT_TRUE(irutils::checkInvertedBytePairs(wrong, 3));
ASSERT_TRUE(irutils::checkInvertedBytePairs(wrong, 2));
ASSERT_TRUE(irutils::checkInvertedBytePairs(wrong, 1));
ASSERT_TRUE(irutils::checkInvertedBytePairs(wrong, 0));
irutils::invertBytePairs(wrong, 0);
ASSERT_FALSE(irutils::checkInvertedBytePairs(wrong, 6));
irutils::invertBytePairs(wrong, 1);
ASSERT_FALSE(irutils::checkInvertedBytePairs(wrong, 6));
irutils::invertBytePairs(wrong, 2);
ASSERT_FALSE(irutils::checkInvertedBytePairs(wrong, 6));
irutils::invertBytePairs(wrong, 6);
ASSERT_TRUE(irutils::checkInvertedBytePairs(wrong, 6));
EXPECT_STATE_EQ(correct, wrong, 6 * 8);
}

View File

@ -1,5 +1,6 @@
// Copyright 2020 David Conran
#include "ir_Airwell.h"
#include "IRac.h"
#include "IRrecv.h"
#include "IRrecv_test.h"
@ -44,6 +45,9 @@ TEST(TestDecodeAirwell, RealExample) {
EXPECT_EQ(0x2B0D0181B, irsend.capture.value);
EXPECT_EQ(0x0, irsend.capture.address);
EXPECT_EQ(0x0, irsend.capture.command);
EXPECT_EQ(
"Power Toggle: On, Mode: 2 (Heat), Fan: 3 (Auto), Temp: 25C",
IRAcUtils::resultAcToString(&irsend.capture));
const uint16_t rawData_2[175] = {
2862, 3892,
@ -76,6 +80,9 @@ TEST(TestDecodeAirwell, RealExample) {
EXPECT_EQ(0x270F8181B, irsend.capture.value);
EXPECT_EQ(0x0, irsend.capture.address);
EXPECT_EQ(0x0, irsend.capture.command);
EXPECT_EQ(
"Power Toggle: On, Mode: 1 (Cool), Fan: 3 (Auto), Temp: 30C",
IRAcUtils::resultAcToString(&irsend.capture));
}
TEST(TestDecodeAirwell, SyntheticExample) {
@ -192,6 +199,9 @@ TEST(TestDecodeAirwell, RealExample2) {
EXPECT_EQ(0xB0C0181B, irsend.capture.value);
EXPECT_EQ(0x0, irsend.capture.address);
EXPECT_EQ(0x0, irsend.capture.command);
EXPECT_EQ(
"Power Toggle: Off, Mode: 2 (Heat), Fan: 3 (Auto), Temp: 23C",
IRAcUtils::resultAcToString(&irsend.capture));
// Resend it as a synthetic to see if it decodes to the same value.
irsend.reset();
@ -210,7 +220,7 @@ TEST(TestUtils, Housekeeping) {
ASSERT_EQ("AIRWELL", typeToString(decode_type_t::AIRWELL));
ASSERT_EQ(decode_type_t::AIRWELL, strToDecodeType("AIRWELL"));
ASSERT_FALSE(hasACState(decode_type_t::AIRWELL));
ASSERT_FALSE(IRac::isProtocolSupported(decode_type_t::AIRWELL));
ASSERT_TRUE(IRac::isProtocolSupported(decode_type_t::AIRWELL));
ASSERT_EQ(kAirwellBits, IRsend::defaultBits(decode_type_t::AIRWELL));
ASSERT_EQ(kAirwellMinRepeats, IRsend::minRepeats(decode_type_t::AIRWELL));
}
@ -250,4 +260,142 @@ TEST(TestDecodeAirwell, RealExample3) {
EXPECT_EQ(0x60080002, irsend.capture.value);
EXPECT_EQ(0x0, irsend.capture.address);
EXPECT_EQ(0x0, irsend.capture.command);
EXPECT_EQ(
"Power Toggle: Off, Mode: 1 (Cool), Fan: 2 (High), Temp: 16C",
IRAcUtils::resultAcToString(&irsend.capture));
}
// Tests for IRAirwellAc class.
TEST(TestAirwellAcClass, PowerToggle) {
IRAirwellAc ac(kGpioUnused);
ac.begin();
ac.setPowerToggle(true);
EXPECT_TRUE(ac.getPowerToggle());
ac.setPowerToggle(false);
EXPECT_FALSE(ac.getPowerToggle());
ac.setPowerToggle(true);
EXPECT_TRUE(ac.getPowerToggle());
}
TEST(TestAirwellAcClass, Temperature) {
IRAirwellAc ac(kGpioUnused);
ac.begin();
ac.setTemp(0);
EXPECT_EQ(kAirwellMinTemp, ac.getTemp());
ac.setTemp(255);
EXPECT_EQ(kAirwellMaxTemp, ac.getTemp());
ac.setTemp(kAirwellMinTemp);
EXPECT_EQ(kAirwellMinTemp, ac.getTemp());
ac.setTemp(kAirwellMaxTemp);
EXPECT_EQ(kAirwellMaxTemp, ac.getTemp());
ac.setTemp(kAirwellMinTemp - 1);
EXPECT_EQ(kAirwellMinTemp, ac.getTemp());
ac.setTemp(kAirwellMaxTemp + 1);
EXPECT_EQ(kAirwellMaxTemp, ac.getTemp());
ac.setTemp(17);
EXPECT_EQ(17, 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(TestAirwellAcClass, OperatingMode) {
IRAirwellAc ac(kGpioUnused);
ac.begin();
ac.setMode(kAirwellAuto);
EXPECT_EQ(kAirwellAuto, ac.getMode());
ac.setMode(kAirwellCool);
EXPECT_EQ(kAirwellCool, ac.getMode());
ac.setMode(kAirwellHeat);
EXPECT_EQ(kAirwellHeat, ac.getMode());
ac.setMode(kAirwellDry);
EXPECT_EQ(kAirwellDry, ac.getMode());
ac.setMode(kAirwellFan);
EXPECT_EQ(kAirwellFan, ac.getMode());
ac.setMode(kAirwellFan + 1);
EXPECT_EQ(kAirwellAuto, ac.getMode());
ac.setMode(255);
EXPECT_EQ(kAirwellAuto, ac.getMode());
}
TEST(TestAirwellAcClass, FanSpeed) {
IRAirwellAc ac(0);
ac.begin();
ac.setFan(0);
EXPECT_EQ(kAirwellFanLow, ac.getFan());
ac.setFan(255);
EXPECT_EQ(kAirwellFanAuto, ac.getFan());
ac.setFan(kAirwellFanHigh);
EXPECT_EQ(kAirwellFanHigh, ac.getFan());
ac.setFan(kAirwellFanHigh + 2);
EXPECT_EQ(kAirwellFanAuto, ac.getFan());
ac.setFan(kAirwellFanHigh - 1);
EXPECT_EQ(kAirwellFanHigh - 1, ac.getFan());
ac.setFan(1);
EXPECT_EQ(1, ac.getFan());
ac.setFan(1);
EXPECT_EQ(1, ac.getFan());
ac.setFan(3);
EXPECT_EQ(3, ac.getFan());
}
// Test human readable output.
TEST(TestAirwellAcClass, HumanReadable) {
IRAirwellAc ac(kGpioUnused);
EXPECT_EQ(
"Power Toggle: Off, Mode: 5 (Fan), Fan: 0 (Low), Temp: 25C",
ac.toString());
ac.setPowerToggle(true);
ac.setMode(kAirwellHeat);
ac.setTemp(30);
ac.setFan(kAirwellFanAuto);
EXPECT_EQ(
"Power Toggle: On, Mode: 2 (Heat), Fan: 3 (Auto), Temp: 30C",
ac.toString());
}
TEST(TestAirwellAcClass, ReconstructKnownState) {
IRAirwellAc ac(kGpioUnused);
const uint64_t expected = 0x240380002;
ac.begin();
ac.stateReset();
ASSERT_NE(expected, ac.getRaw());
ac.setPowerToggle(true);
ac.setMode(kAirwellCool);
ac.setTemp(22);
ac.setFan(kAirwellFanLow);
EXPECT_EQ(expected, ac.getRaw());
EXPECT_EQ(
"Power Toggle: On, Mode: 1 (Cool), Fan: 0 (Low), Temp: 22C",
ac.toString());
}

View File

@ -448,7 +448,7 @@ TEST(TestDecodeLG, Issue620) {
// Resend the same code as the report is a sent code doesn't decode
// to the same message code.
IRLgAc ac(0);
IRLgAc ac(kGpioUnused);
irsend.sendLG(0x8808721);
irsend.makeDecodeResult();
ASSERT_TRUE(irrecv.decode(&irsend.capture));
@ -477,7 +477,7 @@ TEST(TestDecodeLG, Issue620) {
}
TEST(TestIRLgAcClass, SetAndGetPower) {
IRLgAc ac(0);
IRLgAc ac(kGpioUnused);
ac.on();
EXPECT_TRUE(ac.getPower());
ac.off();
@ -489,7 +489,7 @@ TEST(TestIRLgAcClass, SetAndGetPower) {
}
TEST(TestIRLgAcClass, SetAndGetTemp) {
IRLgAc ac(0);
IRLgAc ac(kGpioUnused);
ac.setTemp(25);
EXPECT_EQ(25, ac.getTemp());
ac.setTemp(kLgAcMinTemp);
@ -503,7 +503,7 @@ TEST(TestIRLgAcClass, SetAndGetTemp) {
}
TEST(TestIRLgAcClass, SetAndGetMode) {
IRLgAc ac(0);
IRLgAc ac(kGpioUnused);
ac.setMode(kLgAcCool);
ac.setFan(kLgAcFanAuto);
ac.setTemp(25);
@ -517,22 +517,22 @@ TEST(TestIRLgAcClass, SetAndGetMode) {
}
TEST(TestIRLgAcClass, SetAndGetFan) {
IRLgAc ac(0);
IRLgAc ac(kGpioUnused);
ac.setMode(kLgAcCool);
ac.setFan(kLgAcFanAuto);
EXPECT_EQ(kLgAcFanAuto, ac.getFan());
ac.setFan(kLgAcFanLow);
EXPECT_EQ(kLgAcFanLow, ac.getFan());
ac.setFan(kLgAcFanLowest);
EXPECT_EQ(kLgAcFanLowest, ac.getFan());
ac.setFan(kLgAcFanHigh);
EXPECT_EQ(kLgAcFanHigh, ac.getFan());
ac.setFan(kLgAcFanAuto + 1);
EXPECT_EQ(kLgAcFanAuto, ac.getFan());
ac.setFan(kLgAcFanLow - 1);
ac.setFan(kLgAcFanLowest - 1);
EXPECT_EQ(kLgAcFanAuto, ac.getFan());
}
TEST(TestIRLgAcClass, toCommon) {
IRLgAc ac(0);
IRLgAc ac(kGpioUnused);
ac.setPower(true);
ac.setMode(kLgAcCool);
ac.setTemp(20);
@ -564,7 +564,7 @@ TEST(TestIRLgAcClass, toCommon) {
}
TEST(TestIRLgAcClass, HumanReadable) {
IRLgAc ac(0);
IRLgAc ac(kGpioUnused);
EXPECT_EQ(
"Model: 1 (GE6711AR2853M), "
@ -583,17 +583,17 @@ TEST(TestIRLgAcClass, HumanReadable) {
ac.setTemp(kLgAcMinTemp);
EXPECT_EQ(
"Model: 1 (GE6711AR2853M), "
"Power: On, Mode: 0 (Cool), Temp: 16C, Fan: 0 (Low)",
"Power: On, Mode: 0 (Cool), Temp: 16C, Fan: 1 (Low)",
ac.toString());
ac.setTemp(ac.getTemp() + 1);
EXPECT_EQ(
"Model: 1 (GE6711AR2853M), "
"Power: On, Mode: 0 (Cool), Temp: 17C, Fan: 0 (Low)",
"Power: On, Mode: 0 (Cool), Temp: 17C, Fan: 1 (Low)",
ac.toString());
ac.setTemp(ac.getTemp() - 1);
EXPECT_EQ(
"Model: 1 (GE6711AR2853M), "
"Power: On, Mode: 0 (Cool), Temp: 16C, Fan: 0 (Low)",
"Power: On, Mode: 0 (Cool), Temp: 16C, Fan: 1 (Low)",
ac.toString());
ac.setPower(false);
EXPECT_EQ(
@ -603,7 +603,7 @@ TEST(TestIRLgAcClass, HumanReadable) {
}
TEST(TestIRLgAcClass, SetAndGetRaw) {
IRLgAc ac(0);
IRLgAc ac(kGpioUnused);
ac.setRaw(0x8800A4E);
ASSERT_EQ(0x8800A4E, ac.getRaw());
@ -621,7 +621,7 @@ TEST(TestIRLgAcClass, SetAndGetRaw) {
}
TEST(TestIRLgAcClass, MessageConstruction) {
IRLgAc ac(0);
IRLgAc ac(kGpioUnused);
ac.on();
ac.setMode(kLgAcCool);
@ -635,7 +635,7 @@ TEST(TestIRLgAcClass, MessageConstruction) {
}
TEST(TestIRLgAcClass, isValidLgAc) {
IRLgAc ac(0);
IRLgAc ac(kGpioUnused);
ac.setRaw(0x8800A4E);
ASSERT_TRUE(ac.isValidLgAc());
@ -670,7 +670,7 @@ TEST(TestUtils, Housekeeping) {
}
TEST(TestIRLgAcClass, KnownExamples) {
IRLgAc ac(0);
IRLgAc ac(kGpioUnused);
// Ref:
// https://github.com/crankyoldgit/IRremoteESP8266/issues/1008#issuecomment-570646648
@ -694,7 +694,7 @@ TEST(TestIRLgAcClass, KnownExamples) {
ASSERT_TRUE(ac.isValidLgAc());
EXPECT_EQ(
"Model: 1 (GE6711AR2853M), "
"Power: On, Mode: 1 (Dry), Temp: 21C, Fan: 0 (Low)",
"Power: On, Mode: 1 (Dry), Temp: 21C, Fan: 0 (Quiet)",
ac.toString());
ac.setRaw(0x880C758);
@ -716,7 +716,7 @@ TEST(TestIRLgAcClass, KnownExamples) {
ASSERT_TRUE(ac.isValidLgAc());
EXPECT_EQ(
"Model: 1 (GE6711AR2853M), "
"Power: On, Mode: 0 (Cool), Temp: 22C, Fan: 0 (Low)",
"Power: On, Mode: 0 (Cool), Temp: 22C, Fan: 0 (Quiet)",
ac.toString());
ac.setRaw(0x8808721);
@ -816,7 +816,7 @@ TEST(TestDecodeLG2, Issue1008) {
EXPECT_EQ(0x8800347, irsend.capture.value);
irsend.reset();
IRLgAc ac(0);
IRLgAc ac(kGpioUnused);
ac.setRaw(0x8800347);
ac.setModel(lg_ac_remote_model_t::AKB75215403); // aka. 2
ac.send();
@ -835,7 +835,7 @@ TEST(TestDecodeLG2, Issue1008) {
}
TEST(TestIRLgAcClass, DifferentModels) {
IRLgAc ac(0);
IRLgAc ac(kGpioUnused);
IRrecv capture(0);
ac.setRaw(0x8800347);
@ -872,3 +872,12 @@ TEST(TestIRLgAcClass, DifferentModels) {
ASSERT_EQ(expected2, IRAcUtils::resultAcToString(&ac._irsend.capture));
ASSERT_TRUE(IRAcUtils::decodeToState(&ac._irsend.capture, &r, &p));
}
TEST(TestIRLgAcClass, FanSpeedIssue1214) {
EXPECT_EQ(kLgAcFanLowest, IRLgAc::convertFan(stdAc::fanspeed_t::kMin));
EXPECT_EQ(kLgAcFanLow, IRLgAc::convertFan(stdAc::fanspeed_t::kLow));
EXPECT_EQ(kLgAcFanMedium, IRLgAc::convertFan(stdAc::fanspeed_t::kMedium));
EXPECT_EQ(kLgAcFanHigh, IRLgAc::convertFan(stdAc::fanspeed_t::kHigh));
EXPECT_EQ(kLgAcFanHigh, IRLgAc::convertFan(stdAc::fanspeed_t::kMax));
EXPECT_EQ(kLgAcFanAuto, IRLgAc::convertFan(stdAc::fanspeed_t::kAuto));
}

View File

@ -441,13 +441,13 @@ TEST(TestMideaACClass, Sleep) {
}
TEST(TestMideaACClass, HumanReadableOutput) {
IRMideaAC ac(0);
IRMideaAC ac(kGpioUnused);
ac.begin();
ac.setRaw(0xA1826FFFFF62);
EXPECT_EQ(
"Power: On, Mode: 2 (Auto), Celsius: Off, Temp: 25C/77F, Fan: 0 (Auto), "
"Sleep: Off, Swing(V) Toggle: Off", ac.toString());
"Sleep: Off, Swing(V) Toggle: Off, Econo Toggle: Off", ac.toString());
ac.off();
ac.setTemp(25, true);
ac.setFan(kMideaACFanHigh);
@ -455,16 +455,16 @@ TEST(TestMideaACClass, HumanReadableOutput) {
ac.setSleep(true);
EXPECT_EQ(
"Power: Off, Mode: 1 (Dry), Celsius: Off, Temp: 25C/77F, Fan: 3 (High), "
"Sleep: On, Swing(V) Toggle: Off", ac.toString());
"Sleep: On, Swing(V) Toggle: Off, Econo Toggle: Off", ac.toString());
ac.setUseCelsius(true);
EXPECT_EQ(
"Power: Off, Mode: 1 (Dry), Celsius: On, Temp: 25C/77F, Fan: 3 (High), "
"Sleep: On, Swing(V) Toggle: Off", ac.toString());
"Sleep: On, Swing(V) Toggle: Off, Econo Toggle: Off", ac.toString());
ac.setRaw(0xA19867FFFF7E);
EXPECT_EQ(
"Power: On, Mode: 0 (Cool), Celsius: Off, Temp: 21C/69F, Fan: 3 (High), "
"Sleep: Off, Swing(V) Toggle: Off", ac.toString());
"Sleep: Off, Swing(V) Toggle: Off, Econo Toggle: Off", ac.toString());
}
// Tests for decodeMidea().
@ -672,14 +672,14 @@ TEST(TestDecodeMidea, DecodeRealExample) {
EXPECT_EQ(0xA18263FFFF6E, irsend.capture.value);
EXPECT_EQ(
"Power: On, Mode: 2 (Auto), Celsius: Off, Temp: 18C/65F, Fan: 0 (Auto), "
"Sleep: Off, Swing(V) Toggle: Off",
"Sleep: Off, Swing(V) Toggle: Off, Econo Toggle: Off",
IRAcUtils::resultAcToString(&irsend.capture));
stdAc::state_t r, p;
ASSERT_TRUE(IRAcUtils::decodeToState(&irsend.capture, &r, &p));
}
TEST(TestMideaACClass, toCommon) {
IRMideaAC ac(0);
IRMideaAC ac(kGpioUnused);
ac.setPower(true);
ac.setMode(kMideaACCool);
ac.setUseCelsius(true);
@ -709,7 +709,7 @@ TEST(TestMideaACClass, toCommon) {
// https://github.com/crankyoldgit/IRremoteESP8266/issues/819
TEST(TestMideaACClass, CelsiusRemoteTemp) {
IRMideaAC ac(0);
IRMideaAC ac(kGpioUnused);
uint64_t on_cool_low_17c = 0xA18840FFFF56;
uint64_t on_cool_low_30c = 0xA1884DFFFF5D;
ac.on();
@ -722,13 +722,13 @@ TEST(TestMideaACClass, CelsiusRemoteTemp) {
EXPECT_EQ(on_cool_low_17c, ac.getRaw());
EXPECT_EQ(
"Power: On, Mode: 0 (Cool), Celsius: On, Temp: 17C/62F, Fan: 1 (Low), "
"Sleep: Off, Swing(V) Toggle: Off", ac.toString());
"Sleep: Off, Swing(V) Toggle: Off, Econo Toggle: Off", ac.toString());
ac.setRaw(on_cool_low_17c);
EXPECT_EQ(17, ac.getTemp(true));
EXPECT_EQ(62, ac.getTemp(false));
EXPECT_EQ(
"Power: On, Mode: 0 (Cool), Celsius: On, Temp: 17C/62F, Fan: 1 (Low), "
"Sleep: Off, Swing(V) Toggle: Off", ac.toString());
"Sleep: Off, Swing(V) Toggle: Off, Econo Toggle: Off", ac.toString());
ac.setTemp(17, true);
EXPECT_EQ(17, ac.getTemp(true));
EXPECT_EQ(62, ac.getTemp(false));
@ -737,26 +737,45 @@ TEST(TestMideaACClass, CelsiusRemoteTemp) {
ac.setRaw(on_cool_low_30c);
EXPECT_EQ(
"Power: On, Mode: 0 (Cool), Celsius: On, Temp: 30C/86F, Fan: 1 (Low), "
"Sleep: Off, Swing(V) Toggle: Off", ac.toString());
"Sleep: Off, Swing(V) Toggle: Off, Econo Toggle: Off", ac.toString());
}
// https://github.com/crankyoldgit/IRremoteESP8266/issues/819
TEST(TestMideaACClass, SwingV) {
IRMideaAC ac(0);
IRMideaAC ac(kGpioUnused);
ac.setSwingVToggle(false);
ASSERT_FALSE(ac.getSwingVToggle());
ac.setSwingVToggle(true);
ASSERT_TRUE(ac.getSwingVToggle());
EXPECT_EQ(
"Power: On, Mode: 2 (Auto), Celsius: Off, Temp: 25C/77F, Fan: 0 (Auto), "
"Sleep: Off, Swing(V) Toggle: On", ac.toString());
"Sleep: Off, Swing(V) Toggle: On, Econo Toggle: Off", ac.toString());
ac.setSwingVToggle(false);
ASSERT_FALSE(ac.getSwingVToggle());
EXPECT_EQ(
"Power: On, Mode: 2 (Auto), Celsius: Off, Temp: 25C/77F, Fan: 0 (Auto), "
"Sleep: Off, Swing(V) Toggle: Off", ac.toString());
"Sleep: Off, Swing(V) Toggle: Off, Econo Toggle: Off", ac.toString());
ac.setRaw(kMideaACToggleSwingV);
EXPECT_EQ("Swing(V) Toggle: On", ac.toString());
EXPECT_EQ("Swing(V) Toggle: On, Econo Toggle: Off", ac.toString());
}
// https://github.com/crankyoldgit/IRremoteESP8266/pull/1213
TEST(TestMideaACClass, Econo) {
IRMideaAC ac(kGpioUnused);
ac.setEconoToggle(false);
ASSERT_FALSE(ac.getEconoToggle());
ac.setEconoToggle(true);
ASSERT_TRUE(ac.getEconoToggle());
EXPECT_EQ(
"Power: On, Mode: 2 (Auto), Celsius: Off, Temp: 25C/77F, Fan: 0 (Auto), "
"Sleep: Off, Swing(V) Toggle: Off, Econo Toggle: On", ac.toString());
ac.setEconoToggle(false);
ASSERT_FALSE(ac.getEconoToggle());
EXPECT_EQ(
"Power: On, Mode: 2 (Auto), Celsius: Off, Temp: 25C/77F, Fan: 0 (Auto), "
"Sleep: Off, Swing(V) Toggle: Off, Econo Toggle: Off", ac.toString());
ac.setRaw(kMideaACToggleEcono);
EXPECT_EQ("Swing(V) Toggle: Off, Econo Toggle: On", ac.toString());
}
// Test abusing the protocol for sending 6 arbitary bytes.

View File

@ -14,34 +14,34 @@ TEST(TestSendPioneer, SendDataOnly) {
irsend.sendPioneer(0);
EXPECT_EQ(
"f40000d33"
"m8544s4272"
"m534s534m534s534m534s534m534s534m534s534m534s534m534s534m534s534"
"m534s534m534s534m534s534m534s534m534s534m534s534m534s534m534s534"
"m534s534m534s534m534s534m534s534m534s534m534s534m534s534m534s534"
"m534s534m534s534m534s534m534s534m534s534m534s534m534s534m534s534"
"m534s37380"
"m8544s4272"
"m534s534m534s534m534s534m534s534m534s534m534s534m534s534m534s534"
"m534s534m534s534m534s534m534s534m534s534m534s534m534s534m534s534"
"m534s534m534s534m534s534m534s534m534s534m534s534m534s534m534s534"
"m534s534m534s534m534s534m534s534m534s534m534s534m534s534m534s534"
"m534s37380",
"m8506s4191"
"m568s487m568s487m568s487m568s487m568s487m568s487m568s487m568s487"
"m568s487m568s487m568s487m568s487m568s487m568s487m568s487m568s487"
"m568s487m568s487m568s487m568s487m568s487m568s487m568s487m568s487"
"m568s487m568s487m568s487m568s487m568s487m568s487m568s487m568s487"
"m568s37881"
"m8506s4191"
"m568s487m568s487m568s487m568s487m568s487m568s487m568s487m568s487"
"m568s487m568s487m568s487m568s487m568s487m568s487m568s487m568s487"
"m568s487m568s487m568s487m568s487m568s487m568s487m568s487m568s487"
"m568s487m568s487m568s487m568s487m568s487m568s487m568s487m568s487"
"m568s37881",
irsend.outputStr());
irsend.sendPioneer(0x55FF00AAAA00FF55);
EXPECT_EQ(
"f40000d33"
"m8544s4272"
"m534s534m534s1602m534s534m534s1602m534s534m534s1602m534s534m534s1602"
"m534s1602m534s1602m534s1602m534s1602m534s1602m534s1602m534s1602m534s1602"
"m534s534m534s534m534s534m534s534m534s534m534s534m534s534m534s534"
"m534s1602m534s534m534s1602m534s534m534s1602m534s534m534s1602m534s534"
"m534s25098"
"m8544s4272"
"m534s1602m534s534m534s1602m534s534m534s1602m534s534m534s1602m534s534"
"m534s534m534s534m534s534m534s534m534s534m534s534m534s534m534s534"
"m534s1602m534s1602m534s1602m534s1602m534s1602m534s1602m534s1602m534s1602"
"m534s534m534s1602m534s534m534s1602m534s534m534s1602m534s534m534s1602"
"m534s25098",
"m8506s4191"
"m568s487m568s1542m568s487m568s1542m568s487m568s1542m568s487m568s1542"
"m568s1542m568s1542m568s1542m568s1542m568s1542m568s1542m568s1542m568s1542"
"m568s487m568s487m568s487m568s487m568s487m568s487m568s487m568s487"
"m568s1542m568s487m568s1542m568s487m568s1542m568s487m568s1542m568s487"
"m568s25181"
"m8506s4191"
"m568s1542m568s487m568s1542m568s487m568s1542m568s487m568s1542m568s487"
"m568s487m568s487m568s487m568s487m568s487m568s487m568s487m568s487"
"m568s1542m568s1542m568s1542m568s1542m568s1542m568s1542m568s1542m568s1542"
"m568s487m568s1542m568s487m568s1542m568s487m568s1542m568s487m568s1542"
"m568s25181",
irsend.outputStr());
}
@ -139,17 +139,91 @@ TEST(TestDecodePioneer, SyntheticPioneerMessage) {
irsend.sendPioneer(0x659A857AF50A3DC2, 64, 0);
EXPECT_EQ(
"f40000d33"
"m8544s4272"
"m534s534m534s1602m534s1602m534s534m534s534m534s1602m534s534m534s1602"
"m534s1602m534s534m534s534m534s1602m534s1602m534s534m534s1602m534s534"
"m534s1602m534s534m534s534m534s534m534s534m534s1602m534s534m534s1602"
"m534s534m534s1602m534s1602m534s1602m534s1602m534s534m534s1602m534s534"
"m534s25098"
"m8544s4272"
"m534s1602m534s1602m534s1602m534s1602m534s534m534s1602m534s534m534s1602"
"m534s534m534s534m534s534m534s534m534s1602m534s534m534s1602m534s534"
"m534s534m534s534m534s1602m534s1602m534s1602m534s1602m534s534m534s1602"
"m534s1602m534s1602m534s534m534s534m534s534m534s534m534s1602m534s534"
"m534s25098",
"m8506s4191"
"m568s487m568s1542m568s1542m568s487m568s487m568s1542m568s487m568s1542"
"m568s1542m568s487m568s487m568s1542m568s1542m568s487m568s1542m568s487"
"m568s1542m568s487m568s487m568s487m568s487m568s1542m568s487m568s1542"
"m568s487m568s1542m568s1542m568s1542m568s1542m568s487m568s1542m568s487"
"m568s25181"
"m8506s4191"
"m568s1542m568s1542m568s1542m568s1542m568s487m568s1542m568s487m568s1542"
"m568s487m568s487m568s487m568s487m568s1542m568s487m568s1542m568s487"
"m568s487m568s487m568s1542m568s1542m568s1542m568s1542m568s487m568s1542"
"m568s1542m568s1542m568s487m568s487m568s487m568s487m568s1542m568s487"
"m568s25181",
irsend.outputStr());
}
// Ref: https://github.com/crankyoldgit/IRremoteESP8266/issues/1220#issuecomment-661598412
TEST(TestDecodePioneer, Issue1220) {
IRsendTest irsend(0);
IRrecv irrecv(0);
irsend.begin();
irsend.reset();
// Pwr Toggle {"IrReceived":{"Protocol":"PIONEER","Bits":64,
// "Data":"0xA55A38C7A55A38C7"}}
const uint16_t rawPowerToggle[203] = {
8510, 4188, 580, 1544, 556, 470, 582, 1566, 556, 494, 560, 488, 556, 1542,
580, 470, 584, 1540, 582, 492, 552, 1522, 578, 496, 558, 1564, 558, 1542,
580, 470, 586, 1564, 536, 512, 532, 494, 560, 464, 582, 1542, 580, 1544,
578, 1544, 578, 498, 558, 492, 562, 488, 556, 1518, 582, 1542, 582, 492,
552, 498, 556, 494, 550, 1572, 560, 1538, 562, 1536, 586, 25188,
8512, 4186, 584, 1540, 584, 468, 576, 1572, 528, 522, 532, 492, 552, 1546,
584, 492, 550, 1548, 582, 494, 560, 1538, 580, 468, 586, 1514, 586, 1538,
584, 490, 554, 1546, 586, 488, 554, 470, 584, 490, 556, 1542, 556, 1542,
580, 1544, 578, 496, 560, 466, 578, 496, 560, 1538, 584, 1516, 584, 490,
554, 472, 582, 492, 552, 1546, 586, 1536, 586, 1538, 586, 25162,
8514, 4184, 582, 1542, 580, 496, 562, 1536, 584, 490, 554, 496, 560, 1538,
562, 514, 530, 1542, 580, 496, 560, 1538, 584, 466, 578, 1546, 576, 1524,
578, 496, 558, 1542, 586, 488, 554, 496, 558, 466, 576, 1546, 576, 1548,
586, 1512, 578, 498, 558, 492, 552, 496, 558, 1540, 580, 1542, 578, 498,
556, 494, 550, 498, 556, 1516, 584, 1540, 584, 1540, 580};
irsend.sendRaw(rawPowerToggle, 203, 40); // Pioneer uses 40kHz
irsend.makeDecodeResult();
EXPECT_TRUE(irrecv.decode(&irsend.capture));
EXPECT_EQ(PIONEER, irsend.capture.decode_type);
EXPECT_EQ(kPioneerBits, irsend.capture.bits);
EXPECT_EQ(0xA55A38C7A55A38C7, irsend.capture.value);
EXPECT_EQ(0xA51C, irsend.capture.address);
EXPECT_EQ(0xA51C, irsend.capture.command);
irsend.reset();
irsend.sendPioneer(0xA55A38C7A55A38C7, 64, 1);
irsend.makeDecodeResult();
EXPECT_TRUE(irrecv.decode(&irsend.capture));
EXPECT_EQ(PIONEER, irsend.capture.decode_type);
EXPECT_EQ(kPioneerBits, irsend.capture.bits);
EXPECT_EQ(0xA55A38C7A55A38C7, irsend.capture.value);
EXPECT_EQ(0xA51C, irsend.capture.address);
EXPECT_EQ(0xA51C, irsend.capture.command);
EXPECT_EQ(
"f40000d33"
"m8506s4191"
"m568s1542m568s487m568s1542m568s487m568s487m568s1542m568s487m568s1542"
"m568s487m568s1542m568s487m568s1542m568s1542m568s487m568s1542m568s487"
"m568s487m568s487m568s1542m568s1542m568s1542m568s487m568s487m568s487"
"m568s1542m568s1542m568s487m568s487m568s487m568s1542m568s1542m568s1542"
"m568s25181"
"m8506s4191"
"m568s1542m568s487m568s1542m568s487m568s487m568s1542m568s487m568s1542"
"m568s487m568s1542m568s487m568s1542m568s1542m568s487m568s1542m568s487"
"m568s487m568s487m568s1542m568s1542m568s1542m568s487m568s487m568s487"
"m568s1542m568s1542m568s487m568s487m568s487m568s1542m568s1542m568s1542"
"m568s25181"
"m8506s4191"
"m568s1542m568s487m568s1542m568s487m568s487m568s1542m568s487m568s1542"
"m568s487m568s1542m568s487m568s1542m568s1542m568s487m568s1542m568s487"
"m568s487m568s487m568s1542m568s1542m568s1542m568s487m568s487m568s487"
"m568s1542m568s1542m568s487m568s487m568s487m568s1542m568s1542m568s1542"
"m568s25181"
"m8506s4191"
"m568s1542m568s487m568s1542m568s487m568s487m568s1542m568s487m568s1542"
"m568s487m568s1542m568s487m568s1542m568s1542m568s487m568s1542m568s487"
"m568s487m568s487m568s1542m568s1542m568s1542m568s487m568s487m568s487"
"m568s1542m568s1542m568s487m568s487m568s487m568s1542m568s1542m568s1542"
"m568s25181",
irsend.outputStr());
EXPECT_EQ(0xA55A38C7A55A38C7, irsend.encodePioneer(0xA51C, 0xA51C));
}

View File

@ -12,8 +12,10 @@
// General housekeeping
TEST(TestSamsung, Housekeeping) {
ASSERT_EQ("SAMSUNG", typeToString(SAMSUNG));
ASSERT_FALSE(hasACState(SAMSUNG));
ASSERT_EQ("SAMSUNG", typeToString(decode_type_t::SAMSUNG));
ASSERT_EQ(decode_type_t::SAMSUNG, strToDecodeType("SAMSUNG"));
ASSERT_FALSE(hasACState(decode_type_t::SAMSUNG));
ASSERT_EQ(kSamsungBits, IRsend::defaultBits(decode_type_t::SAMSUNG));
}
// Tests for sendSAMSUNG().
@ -307,8 +309,12 @@ TEST(TestDecodeSamsung, FailToDecodeNonSamsungExample) {
// General housekeeping
TEST(TestSamsungAC, Housekeeping) {
ASSERT_EQ("SAMSUNG_AC", typeToString(SAMSUNG_AC));
ASSERT_TRUE(hasACState(SAMSUNG_AC));
ASSERT_EQ("SAMSUNG_AC", typeToString(decode_type_t::SAMSUNG_AC));
ASSERT_EQ(decode_type_t::SAMSUNG_AC, strToDecodeType("SAMSUNG_AC"));
ASSERT_TRUE(hasACState(decode_type_t::SAMSUNG_AC));
ASSERT_TRUE(IRac::isProtocolSupported(decode_type_t::SAMSUNG_AC));
ASSERT_EQ(kSamsungAcBits, IRsend::defaultBits(decode_type_t::SAMSUNG_AC));
ASSERT_EQ(kNoRepeat, IRsend::minRepeats(decode_type_t::SAMSUNG_AC));
}
// Tests for sendSamsungAC().
@ -1143,34 +1149,36 @@ TEST(TestSendSamsung36, SendDataOnly) {
irsend.sendSamsung36(0);
EXPECT_EQ(
"f38000d50"
"m4480s4480"
"m560s560m560s560m560s560m560s560m560s560m560s560m560s560m560s560"
"m560s560m560s560m560s560m560s560m560s560m560s560m560s560m560s560"
"m560s4480"
"m560s560m560s560m560s560m560s560m560s560m560s560m560s560m560s560"
"m560s560m560s560m560s560m560s560m560s560m560s560m560s560m560s560"
"m560s560m560s560m560s560m560s560"
"m560s26880",
"m4515s4438"
"m512s490m512s490m512s490m512s490m512s490m512s490m512s490m512s490"
"m512s490m512s490m512s490m512s490m512s490m512s490m512s490m512s490"
"m512s4438"
"m512s490m512s490m512s490m512s490m512s490m512s490m512s490m512s490"
"m512s490m512s490m512s490m512s490m512s490m512s490m512s490m512s490"
"m512s490m512s490m512s490m512s490"
"m512s26880",
irsend.outputStr());
irsend.sendSamsung36(0x400E00FF);
EXPECT_EQ(
"f38000d50"
"m4480s4480"
"m560s560m560s560m560s560m560s560m560s560m560s1680m560s560m560s560"
"m560s560m560s560m560s560m560s560m560s560m560s560m560s560m560s560"
"m560s4480"
"m560s1680m560s1680m560s1680m560s560m560s560m560s560m560s560m560s560"
"m560s560m560s560m560s560m560s560m560s1680m560s1680m560s1680m560s1680"
"m560s1680m560s1680m560s1680m560s1680"
"m560s26880",
"m4515s4438"
"m512s490m512s490m512s490m512s490m512s490m512s1468m512s490m512s490"
"m512s490m512s490m512s490m512s490m512s490m512s490m512s490m512s490"
"m512s4438"
"m512s1468m512s1468m512s1468m512s490m512s490m512s490m512s490m512s490"
"m512s490m512s490m512s490m512s490m512s1468m512s1468m512s1468m512s1468"
"m512s1468m512s1468m512s1468m512s1468"
"m512s26880",
irsend.outputStr());
irsend.reset();
}
// General housekeeping
TEST(TestSamsung36, Housekeeping) {
ASSERT_EQ("SAMSUNG36", typeToString(SAMSUNG36));
ASSERT_FALSE(hasACState(SAMSUNG36));
ASSERT_EQ("SAMSUNG36", typeToString(decode_type_t::SAMSUNG36));
ASSERT_EQ(decode_type_t::SAMSUNG36, strToDecodeType("SAMSUNG36"));
ASSERT_FALSE(hasACState(decode_type_t::SAMSUNG36));
ASSERT_EQ(kSamsung36Bits, IRsend::defaultBits(decode_type_t::SAMSUNG36));
}
// Test sending with different repeats.
@ -1182,50 +1190,50 @@ TEST(TestSendSamsung36, SendWithRepeats) {
irsend.sendSamsung36(0x400E00FF, kSamsung36Bits, 1); // 1 repeat.
EXPECT_EQ(
"f38000d50"
"m4480s4480"
"m560s560m560s560m560s560m560s560m560s560m560s1680m560s560m560s560"
"m560s560m560s560m560s560m560s560m560s560m560s560m560s560m560s560"
"m560s4480"
"m560s1680m560s1680m560s1680m560s560m560s560m560s560m560s560m560s560"
"m560s560m560s560m560s560m560s560m560s1680m560s1680m560s1680m560s1680"
"m560s1680m560s1680m560s1680m560s1680"
"m560s26880"
"m4480s4480"
"m560s560m560s560m560s560m560s560m560s560m560s1680m560s560m560s560"
"m560s560m560s560m560s560m560s560m560s560m560s560m560s560m560s560"
"m560s4480"
"m560s1680m560s1680m560s1680m560s560m560s560m560s560m560s560m560s560"
"m560s560m560s560m560s560m560s560m560s1680m560s1680m560s1680m560s1680"
"m560s1680m560s1680m560s1680m560s1680"
"m560s26880",
"m4515s4438"
"m512s490m512s490m512s490m512s490m512s490m512s1468m512s490m512s490"
"m512s490m512s490m512s490m512s490m512s490m512s490m512s490m512s490"
"m512s4438"
"m512s1468m512s1468m512s1468m512s490m512s490m512s490m512s490m512s490"
"m512s490m512s490m512s490m512s490m512s1468m512s1468m512s1468m512s1468"
"m512s1468m512s1468m512s1468m512s1468"
"m512s26880"
"m4515s4438"
"m512s490m512s490m512s490m512s490m512s490m512s1468m512s490m512s490"
"m512s490m512s490m512s490m512s490m512s490m512s490m512s490m512s490"
"m512s4438"
"m512s1468m512s1468m512s1468m512s490m512s490m512s490m512s490m512s490"
"m512s490m512s490m512s490m512s490m512s1468m512s1468m512s1468m512s1468"
"m512s1468m512s1468m512s1468m512s1468"
"m512s26880",
irsend.outputStr());
irsend.sendSamsung36(0x400E00FF, kSamsung36Bits, 2); // 2 repeats.
EXPECT_EQ(
"f38000d50"
"m4480s4480"
"m560s560m560s560m560s560m560s560m560s560m560s1680m560s560m560s560"
"m560s560m560s560m560s560m560s560m560s560m560s560m560s560m560s560"
"m560s4480"
"m560s1680m560s1680m560s1680m560s560m560s560m560s560m560s560m560s560"
"m560s560m560s560m560s560m560s560m560s1680m560s1680m560s1680m560s1680"
"m560s1680m560s1680m560s1680m560s1680"
"m560s26880"
"m4480s4480"
"m560s560m560s560m560s560m560s560m560s560m560s1680m560s560m560s560"
"m560s560m560s560m560s560m560s560m560s560m560s560m560s560m560s560"
"m560s4480"
"m560s1680m560s1680m560s1680m560s560m560s560m560s560m560s560m560s560"
"m560s560m560s560m560s560m560s560m560s1680m560s1680m560s1680m560s1680"
"m560s1680m560s1680m560s1680m560s1680"
"m560s26880"
"m4480s4480"
"m560s560m560s560m560s560m560s560m560s560m560s1680m560s560m560s560"
"m560s560m560s560m560s560m560s560m560s560m560s560m560s560m560s560"
"m560s4480"
"m560s1680m560s1680m560s1680m560s560m560s560m560s560m560s560m560s560"
"m560s560m560s560m560s560m560s560m560s1680m560s1680m560s1680m560s1680"
"m560s1680m560s1680m560s1680m560s1680"
"m560s26880",
"m4515s4438"
"m512s490m512s490m512s490m512s490m512s490m512s1468m512s490m512s490"
"m512s490m512s490m512s490m512s490m512s490m512s490m512s490m512s490"
"m512s4438"
"m512s1468m512s1468m512s1468m512s490m512s490m512s490m512s490m512s490"
"m512s490m512s490m512s490m512s490m512s1468m512s1468m512s1468m512s1468"
"m512s1468m512s1468m512s1468m512s1468"
"m512s26880"
"m4515s4438"
"m512s490m512s490m512s490m512s490m512s490m512s1468m512s490m512s490"
"m512s490m512s490m512s490m512s490m512s490m512s490m512s490m512s490"
"m512s4438"
"m512s1468m512s1468m512s1468m512s490m512s490m512s490m512s490m512s490"
"m512s490m512s490m512s490m512s490m512s1468m512s1468m512s1468m512s1468"
"m512s1468m512s1468m512s1468m512s1468"
"m512s26880"
"m4515s4438"
"m512s490m512s490m512s490m512s490m512s490m512s1468m512s490m512s490"
"m512s490m512s490m512s490m512s490m512s490m512s490m512s490m512s490"
"m512s4438"
"m512s1468m512s1468m512s1468m512s490m512s490m512s490m512s490m512s490"
"m512s490m512s490m512s490m512s490m512s1468m512s1468m512s1468m512s1468"
"m512s1468m512s1468m512s1468m512s1468"
"m512s26880",
irsend.outputStr());
}

View File

@ -1,5 +1,9 @@
// Copyright 2017 David Conran
// Copyright 2017-2020 David Conran
#include "ir_Sanyo.h"
#include "IRac.h"
#include "IRrecv.h"
#include "IRrecv_test.h"
#include "IRsend.h"
#include "IRsend_test.h"
#include "gtest/gtest.h"
@ -257,3 +261,272 @@ TEST(TestDecodeSanyoLC7461, FailToDecodeNonSanyoLC7461Example) {
irrecv.decodeSanyoLC7461(&irsend.capture, kStartOffset, kSanyoLC7461Bits,
false));
}
TEST(TestUtils, Housekeeping) {
// Sanyo LC7461
ASSERT_EQ("SANYO_LC7461", typeToString(decode_type_t::SANYO_LC7461));
ASSERT_EQ(decode_type_t::SANYO_LC7461, strToDecodeType("SANYO_LC7461"));
ASSERT_FALSE(hasACState(decode_type_t::SANYO_LC7461));
ASSERT_FALSE(IRac::isProtocolSupported(decode_type_t::SANYO_LC7461));
ASSERT_EQ(kSanyoLC7461Bits, IRsend::defaultBits(decode_type_t::SANYO_LC7461));
ASSERT_EQ(kNoRepeat, IRsend::minRepeats(decode_type_t::SANYO_LC7461));
// Sanyo A/C
ASSERT_EQ("SANYO_AC", typeToString(decode_type_t::SANYO_AC));
ASSERT_EQ(decode_type_t::SANYO_AC, strToDecodeType("SANYO_AC"));
ASSERT_TRUE(hasACState(decode_type_t::SANYO_AC));
ASSERT_TRUE(IRac::isProtocolSupported(decode_type_t::SANYO_AC));
ASSERT_EQ(kSanyoAcBits, IRsend::defaultBits(decode_type_t::SANYO_AC));
ASSERT_EQ(kNoRepeat, IRsend::minRepeats(decode_type_t::SANYO_AC));
}
TEST(TestDecodeSanyoAc, DecodeRealExamples) {
IRsendTest irsend(kGpioUnused);
IRrecv irrecv(kGpioUnused);
// Ref: "On" from https://github.com/crankyoldgit/IRremoteESP8266/issues/1211#issue-650997449
const uint16_t rawData[148] = {
8456, 4192,
624, 448, 584, 1508, 608, 452, 580, 1512, 628, 452, 604, 1468, 560, 1552,
600, 472, 584, 1532, 580, 472, 528, 516, 512, 540, 576, 1516, 628, 1472,
612, 1508, 612, 452, 580, 1512, 628, 1468, 612, 1496, 632, 444, 580, 480,
580, 476, 580, 1496, 564, 508, 576, 480, 576, 480, 580, 476, 584, 472,
584, 468, 584, 480, 520, 512, 580, 480, 576, 480, 580, 476, 584, 472,
584, 472, 528, 508, 524, 1568, 600, 480, 576, 480, 584, 1492, 560, 512,
580, 1536, 576, 480, 580, 476, 580, 476, 528, 528, 524, 1568, 580, 476,
584, 476, 580, 476, 580, 472, 528, 512, 520, 536, 576, 480, 580, 480,
576, 480, 576, 476, 532, 528, 520, 512, 576, 480, 584, 476, 580, 476,
580, 480, 576, 472, 528, 1548, 600, 480, 576, 480, 576, 1520, 592, 1496,
600, 476, 580, 480, 576};
const uint8_t expectedState[kSanyoAcStateLength] = {
0x6A, 0x71, 0x47, 0x00, 0x20, 0x85, 0x00, 0x00, 0x32};
irsend.begin();
irsend.reset();
irsend.sendRaw(rawData, 148, 38000);
irsend.makeDecodeResult();
ASSERT_TRUE(irrecv.decode(&irsend.capture));
EXPECT_EQ(SANYO_AC, irsend.capture.decode_type);
EXPECT_EQ(kSanyoAcBits, irsend.capture.bits);
EXPECT_STATE_EQ(expectedState, irsend.capture.state, irsend.capture.bits);
EXPECT_FALSE(irsend.capture.repeat);
EXPECT_EQ(
"Power: On, Mode: 2 (Cool), Temp: 21C, Fan: 0 (Auto), "
"Swing(V): 5 (Upper Middle), Sleep: Off, Beep: On, Sensor: Room, "
"Sensor Temp: 11C, Off Timer: Off",
IRAcUtils::resultAcToString(&irsend.capture));
}
TEST(TestDecodeSanyoAc, SyntheticSelfDecode) {
IRsendTest irsend(kGpioUnused);
IRrecv irrecv(kGpioUnused);
const uint8_t expectedState[kSanyoAcStateLength] = {
0x6A, 0x71, 0x47, 0x00, 0x20, 0x85, 0x00, 0x00, 0x32};
irsend.begin();
irsend.reset();
irsend.sendSanyoAc(expectedState);
irsend.makeDecodeResult();
ASSERT_TRUE(irrecv.decode(&irsend.capture));
EXPECT_EQ(SANYO_AC, irsend.capture.decode_type);
EXPECT_EQ(kSanyoAcBits, irsend.capture.bits);
EXPECT_STATE_EQ(expectedState, irsend.capture.state, irsend.capture.bits);
EXPECT_FALSE(irsend.capture.repeat);
EXPECT_EQ(
"Power: On, Mode: 2 (Cool), Temp: 21C, Fan: 0 (Auto), "
"Swing(V): 5 (Upper Middle), Sleep: Off, Beep: On, Sensor: Room, "
"Sensor Temp: 11C, Off Timer: Off",
IRAcUtils::resultAcToString(&irsend.capture));
EXPECT_EQ(
"f38000d50"
"m8500s4200"
"m500s550m500s1600m500s550m500s1600m500s550m500s1600m500s1600m500s550"
"m500s1600m500s550m500s550m500s550m500s1600m500s1600m500s1600m500s550"
"m500s1600m500s1600m500s1600m500s550m500s550m500s550m500s1600m500s550"
"m500s550m500s550m500s550m500s550m500s550m500s550m500s550m500s550m500s550"
"m500s550m500s550m500s550m500s550m500s1600m500s550m500s550m500s1600"
"m500s550m500s1600m500s550m500s550m500s550m500s550m500s1600m500s550"
"m500s550m500s550m500s550m500s550m500s550m500s550m500s550m500s550m500s550"
"m500s550m500s550m500s550m500s550m500s550m500s550m500s550m500s1600"
"m500s550m500s550m500s1600m500s1600m500s550m500s550"
"m500s100000",
irsend.outputStr());
}
// Tests for IRSanyoAc class.
TEST(TestSanyoAcClass, Power) {
IRSanyoAc ac(kGpioUnused);
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(TestSanyoAcClass, Temperature) {
IRSanyoAc ac(kGpioUnused);
ac.begin();
ac.setTemp(0);
EXPECT_EQ(kSanyoAcTempMin, ac.getTemp());
ac.setTemp(255);
EXPECT_EQ(kSanyoAcTempMax, ac.getTemp());
ac.setTemp(kSanyoAcTempMin);
EXPECT_EQ(kSanyoAcTempMin, ac.getTemp());
ac.setTemp(kSanyoAcTempMax);
EXPECT_EQ(kSanyoAcTempMax, ac.getTemp());
ac.setTemp(kSanyoAcTempMin - 1);
EXPECT_EQ(kSanyoAcTempMin, ac.getTemp());
ac.setTemp(kSanyoAcTempMax + 1);
EXPECT_EQ(kSanyoAcTempMax, ac.getTemp());
ac.setTemp(17);
EXPECT_EQ(17, ac.getTemp());
ac.setTemp(21);
EXPECT_EQ(21, ac.getTemp());
ac.setTemp(25);
EXPECT_EQ(25, ac.getTemp());
ac.setTemp(30);
EXPECT_EQ(30, ac.getTemp());
}
TEST(TestSanyoAcClass, OperatingMode) {
IRSanyoAc ac(kGpioUnused);
ac.begin();
ac.setMode(kSanyoAcAuto);
EXPECT_EQ(kSanyoAcAuto, ac.getMode());
ac.setMode(kSanyoAcCool);
EXPECT_EQ(kSanyoAcCool, ac.getMode());
ac.setMode(kSanyoAcHeat);
EXPECT_EQ(kSanyoAcHeat, ac.getMode());
ac.setMode(kSanyoAcDry);
EXPECT_EQ(kSanyoAcDry, ac.getMode());
ac.setMode(kSanyoAcAuto + 1);
EXPECT_EQ(kSanyoAcAuto, ac.getMode());
ac.setMode(0);
EXPECT_EQ(kSanyoAcAuto, ac.getMode());
ac.setMode(255);
EXPECT_EQ(kSanyoAcAuto, ac.getMode());
}
TEST(TestSanyoAcClass, FanSpeed) {
IRSanyoAc ac(kGpioUnused);
ac.begin();
ac.setFan(kSanyoAcFanAuto);
EXPECT_EQ(kSanyoAcFanAuto, ac.getFan());
ac.setFan(kSanyoAcFanHigh);
EXPECT_EQ(kSanyoAcFanHigh, ac.getFan());
ac.setFan(kSanyoAcFanLow);
EXPECT_EQ(kSanyoAcFanLow, ac.getFan());
ac.setFan(kSanyoAcFanMedium);
EXPECT_EQ(kSanyoAcFanMedium, ac.getFan());
}
TEST(TestSanyoAcClass, Sleep) {
IRSanyoAc ac(kGpioUnused);
ac.begin();
ac.setSleep(true);
EXPECT_TRUE(ac.getSleep());
ac.setSleep(false);
EXPECT_FALSE(ac.getSleep());
ac.setSleep(true);
EXPECT_TRUE(ac.getSleep());
}
TEST(TestSanyoAcClass, SwingV) {
IRSanyoAc ac(kGpioUnused);
ac.begin();
ac.setSwingV(kSanyoAcSwingVAuto);
EXPECT_EQ(kSanyoAcSwingVAuto, ac.getSwingV());
ac.setSwingV(kSanyoAcSwingVHigh);
EXPECT_EQ(kSanyoAcSwingVHigh, ac.getSwingV());
ac.setSwingV(kSanyoAcSwingVLow);
EXPECT_EQ(kSanyoAcSwingVLow, ac.getSwingV());
ac.setSwingV(kSanyoAcSwingVUpperMiddle);
EXPECT_EQ(kSanyoAcSwingVUpperMiddle, ac.getSwingV());
ac.setSwingV(0);
EXPECT_EQ(kSanyoAcSwingVAuto, ac.getSwingV());
ac.setSwingV(255);
EXPECT_EQ(kSanyoAcSwingVAuto, ac.getSwingV());
}
TEST(TestSanyoAcClass, Timers) {
IRSanyoAc ac(kGpioUnused);
ac.begin();
ac.setOffTimer(0);
EXPECT_EQ(0, ac.getOffTimer());
ac.setOffTimer(59);
EXPECT_EQ(0, ac.getOffTimer());
ac.setOffTimer(60);
EXPECT_EQ(60, ac.getOffTimer());
ac.setOffTimer(61);
EXPECT_EQ(60, ac.getOffTimer());
ac.setOffTimer(15 * 60 + 59);
EXPECT_EQ(15 * 60, ac.getOffTimer());
ac.setOffTimer(16 * 60);
EXPECT_EQ(15 * 60, ac.getOffTimer());
const uint8_t offTimer2Hr[kSanyoAcStateLength] = {
0x6A, 0x6D, 0x4F, 0x02, 0x14, 0x85, 0x00, 0x00, 0x4A};
ac.setRaw(offTimer2Hr);
EXPECT_EQ(2 * 60, ac.getOffTimer());
EXPECT_EQ(
"Power: On, Mode: 1 (Heat), Temp: 17C, Fan: 0 (Auto), "
"Swing(V): 5 (Upper Middle), Sleep: Off, Beep: On, "
"Sensor: Room, Sensor Temp: 19C, Off Timer: 02:00",
ac.toString());
}
TEST(TestSanyoAcClass, Beep) {
IRSanyoAc ac(kGpioUnused);
ac.begin();
ac.setBeep(true);
EXPECT_TRUE(ac.getBeep());
ac.setBeep(false);
EXPECT_FALSE(ac.getBeep());
ac.setBeep(true);
EXPECT_TRUE(ac.getBeep());
const uint8_t beep_off[kSanyoAcStateLength] = {
0x6A, 0x6D, 0x11, 0x00, 0x10, 0x85, 0x00, 0x00, 0x33};
ac.setRaw(beep_off);
EXPECT_FALSE(ac.getBeep());
const uint8_t beep_on[kSanyoAcStateLength] = {
0x6A, 0x6E, 0x54, 0x00, 0x10, 0x83, 0x00, 0x00, 0x39};
ac.setRaw(beep_on);
EXPECT_TRUE(ac.getBeep());
}

View File

@ -11,7 +11,7 @@
// Test sending typical data only.
TEST(TestSendToshibaAC, SendDataOnly) {
IRsendTest irsend(4);
IRsendTest irsend(kGpioUnused);
irsend.begin();
uint8_t toshiba_code[kToshibaACStateLength] = {0xF2, 0x0D, 0x03, 0xFC, 0x01,
@ -21,33 +21,33 @@ TEST(TestSendToshibaAC, SendDataOnly) {
EXPECT_EQ(
"f38000d50"
"m4400s4300"
"m543s1623m543s1623m543s1623m543s1623m543s472m543s472m543s1623m543s472"
"m543s472m543s472m543s472m543s472m543s1623m543s1623m543s472m543s1623"
"m543s472m543s472m543s472m543s472m543s472m543s472m543s1623m543s1623"
"m543s1623m543s1623m543s1623m543s1623m543s1623m543s1623m543s472m543s472"
"m543s472m543s472m543s472m543s472m543s472m543s472m543s472m543s1623"
"m543s472m543s472m543s472m543s472m543s472m543s472m543s472m543s472"
"m543s472m543s472m543s472m543s472m543s472m543s472m543s472m543s472"
"m543s472m543s472m543s472m543s472m543s472m543s472m543s472m543s472"
"m543s472m543s472m543s472m543s472m543s472m543s472m543s472m543s472"
"m543s7048"
"m580s1600m580s1600m580s1600m580s1600m580s490m580s490m580s1600m580s490"
"m580s490m580s490m580s490m580s490m580s1600m580s1600m580s490m580s1600"
"m580s490m580s490m580s490m580s490m580s490m580s490m580s1600m580s1600"
"m580s1600m580s1600m580s1600m580s1600m580s1600m580s1600m580s490m580s490"
"m580s490m580s490m580s490m580s490m580s490m580s490m580s490m580s1600"
"m580s490m580s490m580s490m580s490m580s490m580s490m580s490m580s490"
"m580s490m580s490m580s490m580s490m580s490m580s490m580s490m580s490"
"m580s490m580s490m580s490m580s490m580s490m580s490m580s490m580s490"
"m580s490m580s490m580s490m580s490m580s490m580s490m580s490m580s490"
"m580s7400"
"m4400s4300"
"m543s1623m543s1623m543s1623m543s1623m543s472m543s472m543s1623m543s472"
"m543s472m543s472m543s472m543s472m543s1623m543s1623m543s472m543s1623"
"m543s472m543s472m543s472m543s472m543s472m543s472m543s1623m543s1623"
"m543s1623m543s1623m543s1623m543s1623m543s1623m543s1623m543s472m543s472"
"m543s472m543s472m543s472m543s472m543s472m543s472m543s472m543s1623"
"m543s472m543s472m543s472m543s472m543s472m543s472m543s472m543s472"
"m543s472m543s472m543s472m543s472m543s472m543s472m543s472m543s472"
"m543s472m543s472m543s472m543s472m543s472m543s472m543s472m543s472"
"m543s472m543s472m543s472m543s472m543s472m543s472m543s472m543s472"
"m543s7048",
"m580s1600m580s1600m580s1600m580s1600m580s490m580s490m580s1600m580s490"
"m580s490m580s490m580s490m580s490m580s1600m580s1600m580s490m580s1600"
"m580s490m580s490m580s490m580s490m580s490m580s490m580s1600m580s1600"
"m580s1600m580s1600m580s1600m580s1600m580s1600m580s1600m580s490m580s490"
"m580s490m580s490m580s490m580s490m580s490m580s490m580s490m580s1600"
"m580s490m580s490m580s490m580s490m580s490m580s490m580s490m580s490"
"m580s490m580s490m580s490m580s490m580s490m580s490m580s490m580s490"
"m580s490m580s490m580s490m580s490m580s490m580s490m580s490m580s490"
"m580s490m580s490m580s490m580s490m580s490m580s490m580s490m580s490"
"m580s7400",
irsend.outputStr());
}
// Test sending with repeats.
TEST(TestSendToshibaAC, SendWithRepeats) {
IRsendTest irsend(4);
IRsendTest irsend(kGpioUnused);
irsend.begin();
irsend.reset();
@ -58,16 +58,16 @@ TEST(TestSendToshibaAC, SendWithRepeats) {
EXPECT_EQ(
"f38000d50"
"m4400s4300"
"m543s1623m543s1623m543s1623m543s1623m543s472m543s472m543s1623m543s472"
"m543s472m543s472m543s472m543s472m543s1623m543s1623m543s472m543s1623"
"m543s472m543s472m543s472m543s472m543s472m543s472m543s1623m543s1623"
"m543s1623m543s1623m543s1623m543s1623m543s1623m543s1623m543s472m543s472"
"m543s472m543s472m543s472m543s472m543s472m543s472m543s472m543s1623"
"m543s472m543s472m543s472m543s472m543s472m543s472m543s472m543s472"
"m543s472m543s472m543s472m543s472m543s472m543s472m543s472m543s472"
"m543s472m543s472m543s472m543s472m543s472m543s472m543s472m543s472"
"m543s472m543s472m543s472m543s472m543s472m543s472m543s472m543s472"
"m543s7048",
"m580s1600m580s1600m580s1600m580s1600m580s490m580s490m580s1600m580s490"
"m580s490m580s490m580s490m580s490m580s1600m580s1600m580s490m580s1600"
"m580s490m580s490m580s490m580s490m580s490m580s490m580s1600m580s1600"
"m580s1600m580s1600m580s1600m580s1600m580s1600m580s1600m580s490m580s490"
"m580s490m580s490m580s490m580s490m580s490m580s490m580s490m580s1600"
"m580s490m580s490m580s490m580s490m580s490m580s490m580s490m580s490"
"m580s490m580s490m580s490m580s490m580s490m580s490m580s490m580s490"
"m580s490m580s490m580s490m580s490m580s490m580s490m580s490m580s490"
"m580s490m580s490m580s490m580s490m580s490m580s490m580s490m580s490"
"m580s7400",
irsend.outputStr());
irsend.reset();
@ -75,242 +75,200 @@ TEST(TestSendToshibaAC, SendWithRepeats) {
EXPECT_EQ(
"f38000d50"
"m4400s4300"
"m543s1623m543s1623m543s1623m543s1623m543s472m543s472m543s1623m543s472"
"m543s472m543s472m543s472m543s472m543s1623m543s1623m543s472m543s1623"
"m543s472m543s472m543s472m543s472m543s472m543s472m543s1623m543s1623"
"m543s1623m543s1623m543s1623m543s1623m543s1623m543s1623m543s472m543s472"
"m543s472m543s472m543s472m543s472m543s472m543s472m543s472m543s1623"
"m543s472m543s472m543s472m543s472m543s472m543s472m543s472m543s472"
"m543s472m543s472m543s472m543s472m543s472m543s472m543s472m543s472"
"m543s472m543s472m543s472m543s472m543s472m543s472m543s472m543s472"
"m543s472m543s472m543s472m543s472m543s472m543s472m543s472m543s472"
"m543s7048"
"m580s1600m580s1600m580s1600m580s1600m580s490m580s490m580s1600m580s490"
"m580s490m580s490m580s490m580s490m580s1600m580s1600m580s490m580s1600"
"m580s490m580s490m580s490m580s490m580s490m580s490m580s1600m580s1600"
"m580s1600m580s1600m580s1600m580s1600m580s1600m580s1600m580s490m580s490"
"m580s490m580s490m580s490m580s490m580s490m580s490m580s490m580s1600"
"m580s490m580s490m580s490m580s490m580s490m580s490m580s490m580s490"
"m580s490m580s490m580s490m580s490m580s490m580s490m580s490m580s490"
"m580s490m580s490m580s490m580s490m580s490m580s490m580s490m580s490"
"m580s490m580s490m580s490m580s490m580s490m580s490m580s490m580s490"
"m580s7400"
"m4400s4300"
"m543s1623m543s1623m543s1623m543s1623m543s472m543s472m543s1623m543s472"
"m543s472m543s472m543s472m543s472m543s1623m543s1623m543s472m543s1623"
"m543s472m543s472m543s472m543s472m543s472m543s472m543s1623m543s1623"
"m543s1623m543s1623m543s1623m543s1623m543s1623m543s1623m543s472m543s472"
"m543s472m543s472m543s472m543s472m543s472m543s472m543s472m543s1623"
"m543s472m543s472m543s472m543s472m543s472m543s472m543s472m543s472"
"m543s472m543s472m543s472m543s472m543s472m543s472m543s472m543s472"
"m543s472m543s472m543s472m543s472m543s472m543s472m543s472m543s472"
"m543s472m543s472m543s472m543s472m543s472m543s472m543s472m543s472"
"m543s7048"
"m580s1600m580s1600m580s1600m580s1600m580s490m580s490m580s1600m580s490"
"m580s490m580s490m580s490m580s490m580s1600m580s1600m580s490m580s1600"
"m580s490m580s490m580s490m580s490m580s490m580s490m580s1600m580s1600"
"m580s1600m580s1600m580s1600m580s1600m580s1600m580s1600m580s490m580s490"
"m580s490m580s490m580s490m580s490m580s490m580s490m580s490m580s1600"
"m580s490m580s490m580s490m580s490m580s490m580s490m580s490m580s490"
"m580s490m580s490m580s490m580s490m580s490m580s490m580s490m580s490"
"m580s490m580s490m580s490m580s490m580s490m580s490m580s490m580s490"
"m580s490m580s490m580s490m580s490m580s490m580s490m580s490m580s490"
"m580s7400"
"m4400s4300"
"m543s1623m543s1623m543s1623m543s1623m543s472m543s472m543s1623m543s472"
"m543s472m543s472m543s472m543s472m543s1623m543s1623m543s472m543s1623"
"m543s472m543s472m543s472m543s472m543s472m543s472m543s1623m543s1623"
"m543s1623m543s1623m543s1623m543s1623m543s1623m543s1623m543s472m543s472"
"m543s472m543s472m543s472m543s472m543s472m543s472m543s472m543s1623"
"m543s472m543s472m543s472m543s472m543s472m543s472m543s472m543s472"
"m543s472m543s472m543s472m543s472m543s472m543s472m543s472m543s472"
"m543s472m543s472m543s472m543s472m543s472m543s472m543s472m543s472"
"m543s472m543s472m543s472m543s472m543s472m543s472m543s472m543s472"
"m543s7048",
irsend.outputStr());
}
// Test sending atypical sizes.
TEST(TestSendToshibaAC, SendUnexpectedSizes) {
IRsendTest irsend(4);
irsend.begin();
uint8_t toshiba_short_code[8] = {0x01, 0x02, 0x03, 0x04,
0x05, 0x06, 0x07, 0x08};
uint8_t toshiba_long_code[10] = {0x01, 0x02, 0x03, 0x04, 0x05,
0x06, 0x07, 0x08, 0x09, 0x0A};
irsend.reset();
irsend.sendToshibaAC(toshiba_short_code, kToshibaACStateLength - 1);
ASSERT_EQ("", irsend.outputStr());
irsend.reset();
irsend.sendToshibaAC(toshiba_long_code, kToshibaACStateLength + 1);
ASSERT_EQ(
"f38000d50"
"m4400s4300"
"m543s472m543s472m543s472m543s472m543s472m543s472m543s472m543s1623"
"m543s472m543s472m543s472m543s472m543s472m543s472m543s1623m543s472"
"m543s472m543s472m543s472m543s472m543s472m543s472m543s1623m543s1623"
"m543s472m543s472m543s472m543s472m543s472m543s1623m543s472m543s472"
"m543s472m543s472m543s472m543s472m543s472m543s1623m543s472m543s1623"
"m543s472m543s472m543s472m543s472m543s472m543s1623m543s1623m543s472"
"m543s472m543s472m543s472m543s472m543s472m543s1623m543s1623m543s1623"
"m543s472m543s472m543s472m543s472m543s1623m543s472m543s472m543s472"
"m543s472m543s472m543s472m543s472m543s1623m543s472m543s472m543s1623"
"m543s472m543s472m543s472m543s472m543s1623m543s472m543s1623m543s472"
"m543s7048"
"m4400s4300"
"m543s472m543s472m543s472m543s472m543s472m543s472m543s472m543s1623"
"m543s472m543s472m543s472m543s472m543s472m543s472m543s1623m543s472"
"m543s472m543s472m543s472m543s472m543s472m543s472m543s1623m543s1623"
"m543s472m543s472m543s472m543s472m543s472m543s1623m543s472m543s472"
"m543s472m543s472m543s472m543s472m543s472m543s1623m543s472m543s1623"
"m543s472m543s472m543s472m543s472m543s472m543s1623m543s1623m543s472"
"m543s472m543s472m543s472m543s472m543s472m543s1623m543s1623m543s1623"
"m543s472m543s472m543s472m543s472m543s1623m543s472m543s472m543s472"
"m543s472m543s472m543s472m543s472m543s1623m543s472m543s472m543s1623"
"m543s472m543s472m543s472m543s472m543s1623m543s472m543s1623m543s472"
"m543s7048",
"m580s1600m580s1600m580s1600m580s1600m580s490m580s490m580s1600m580s490"
"m580s490m580s490m580s490m580s490m580s1600m580s1600m580s490m580s1600"
"m580s490m580s490m580s490m580s490m580s490m580s490m580s1600m580s1600"
"m580s1600m580s1600m580s1600m580s1600m580s1600m580s1600m580s490m580s490"
"m580s490m580s490m580s490m580s490m580s490m580s490m580s490m580s1600"
"m580s490m580s490m580s490m580s490m580s490m580s490m580s490m580s490"
"m580s490m580s490m580s490m580s490m580s490m580s490m580s490m580s490"
"m580s490m580s490m580s490m580s490m580s490m580s490m580s490m580s490"
"m580s490m580s490m580s490m580s490m580s490m580s490m580s490m580s490"
"m580s7400",
irsend.outputStr());
}
// Tests for IRToshibaAC class.
TEST(TestToshibaACClass, Power) {
IRToshibaAC toshiba(0);
toshiba.begin();
IRToshibaAC ac(kGpioUnused);
ac.begin();
toshiba.on();
EXPECT_TRUE(toshiba.getPower());
ac.on();
EXPECT_TRUE(ac.getPower());
toshiba.off();
EXPECT_FALSE(toshiba.getPower());
ac.off();
EXPECT_FALSE(ac.getPower());
toshiba.setPower(true);
EXPECT_TRUE(toshiba.getPower());
ac.setPower(true);
EXPECT_TRUE(ac.getPower());
toshiba.setPower(false);
EXPECT_FALSE(toshiba.getPower());
ac.setPower(false);
EXPECT_FALSE(ac.getPower());
}
TEST(TestToshibaACClass, Temperature) {
IRToshibaAC toshiba(0);
toshiba.begin();
IRToshibaAC ac(kGpioUnused);
ac.begin();
toshiba.setTemp(0);
EXPECT_EQ(kToshibaAcMinTemp, toshiba.getTemp());
ac.setTemp(0);
EXPECT_EQ(kToshibaAcMinTemp, ac.getTemp());
toshiba.setTemp(255);
EXPECT_EQ(kToshibaAcMaxTemp, toshiba.getTemp());
ac.setTemp(255);
EXPECT_EQ(kToshibaAcMaxTemp, ac.getTemp());
toshiba.setTemp(kToshibaAcMinTemp);
EXPECT_EQ(kToshibaAcMinTemp, toshiba.getTemp());
ac.setTemp(kToshibaAcMinTemp);
EXPECT_EQ(kToshibaAcMinTemp, ac.getTemp());
toshiba.setTemp(kToshibaAcMaxTemp);
EXPECT_EQ(kToshibaAcMaxTemp, toshiba.getTemp());
ac.setTemp(kToshibaAcMaxTemp);
EXPECT_EQ(kToshibaAcMaxTemp, ac.getTemp());
toshiba.setTemp(kToshibaAcMinTemp - 1);
EXPECT_EQ(kToshibaAcMinTemp, toshiba.getTemp());
ac.setTemp(kToshibaAcMinTemp - 1);
EXPECT_EQ(kToshibaAcMinTemp, ac.getTemp());
toshiba.setTemp(kToshibaAcMaxTemp + 1);
EXPECT_EQ(kToshibaAcMaxTemp, toshiba.getTemp());
ac.setTemp(kToshibaAcMaxTemp + 1);
EXPECT_EQ(kToshibaAcMaxTemp, ac.getTemp());
toshiba.setTemp(17);
EXPECT_EQ(17, toshiba.getTemp());
ac.setTemp(17);
EXPECT_EQ(17, ac.getTemp());
toshiba.setTemp(21);
EXPECT_EQ(21, toshiba.getTemp());
ac.setTemp(21);
EXPECT_EQ(21, ac.getTemp());
toshiba.setTemp(25);
EXPECT_EQ(25, toshiba.getTemp());
ac.setTemp(25);
EXPECT_EQ(25, ac.getTemp());
toshiba.setTemp(30);
EXPECT_EQ(30, toshiba.getTemp());
ac.setTemp(30);
EXPECT_EQ(30, ac.getTemp());
}
TEST(TestToshibaACClass, OperatingMode) {
IRToshibaAC toshiba(0);
toshiba.begin();
IRToshibaAC ac(kGpioUnused);
ac.begin();
toshiba.setMode(kToshibaAcAuto);
EXPECT_EQ(kToshibaAcAuto, toshiba.getMode());
ac.setMode(kToshibaAcAuto);
EXPECT_EQ(kToshibaAcAuto, ac.getMode());
toshiba.setMode(kToshibaAcCool);
EXPECT_EQ(kToshibaAcCool, toshiba.getMode());
ac.setMode(kToshibaAcCool);
EXPECT_EQ(kToshibaAcCool, ac.getMode());
toshiba.setMode(kToshibaAcHeat);
EXPECT_EQ(kToshibaAcHeat, toshiba.getMode());
ac.setMode(kToshibaAcHeat);
EXPECT_EQ(kToshibaAcHeat, ac.getMode());
toshiba.setMode(kToshibaAcDry);
EXPECT_EQ(kToshibaAcDry, toshiba.getMode());
ac.setMode(kToshibaAcDry);
EXPECT_EQ(kToshibaAcDry, ac.getMode());
toshiba.setMode(kToshibaAcHeat + 1);
EXPECT_EQ(kToshibaAcAuto, toshiba.getMode());
ac.setMode(kToshibaAcFan);
EXPECT_EQ(kToshibaAcFan, ac.getMode());
toshiba.setMode(255);
EXPECT_EQ(kToshibaAcAuto, toshiba.getMode());
ac.setMode(kToshibaAcFan + 1);
EXPECT_EQ(kToshibaAcAuto, ac.getMode());
// Setting the power off changes the underlying mode in the state to heat.
toshiba.setPower(true);
toshiba.setMode(kToshibaAcCool);
EXPECT_EQ(kToshibaAcCool, toshiba.getMode());
EXPECT_EQ(kToshibaAcCool, toshiba.getMode(true));
toshiba.setPower(false);
EXPECT_EQ(kToshibaAcCool, toshiba.getMode());
EXPECT_EQ(kToshibaAcHeat, toshiba.getMode(true));
ac.setMode(255);
EXPECT_EQ(kToshibaAcAuto, ac.getMode());
// Setting the power off changes the underlying mode in the state to a special
// off mode.
ac.setPower(true);
ac.setMode(kToshibaAcCool);
EXPECT_EQ(kToshibaAcCool, ac.getMode());
ac.setPower(false);
EXPECT_EQ(kToshibaAcCool, ac.getMode());
}
TEST(TestToshibaACClass, FanSpeed) {
IRToshibaAC toshiba(0);
toshiba.begin();
IRToshibaAC ac(kGpioUnused);
ac.begin();
toshiba.setFan(kToshibaAcFanAuto);
EXPECT_EQ(kToshibaAcFanAuto, toshiba.getFan());
ac.setFan(kToshibaAcFanAuto);
EXPECT_EQ(kToshibaAcFanAuto, ac.getFan());
toshiba.setFan(255);
EXPECT_EQ(kToshibaAcFanMax, toshiba.getFan());
ac.setFan(255);
EXPECT_EQ(kToshibaAcFanMax, ac.getFan());
toshiba.setFan(kToshibaAcFanMax);
EXPECT_EQ(kToshibaAcFanMax, toshiba.getFan());
ac.setFan(kToshibaAcFanMax);
EXPECT_EQ(kToshibaAcFanMax, ac.getFan());
toshiba.setFan(kToshibaAcFanMax - 1);
EXPECT_EQ(kToshibaAcFanMax - 1, toshiba.getFan());
ac.setFan(kToshibaAcFanMax - 1);
EXPECT_EQ(kToshibaAcFanMax - 1, ac.getFan());
toshiba.setFan(1);
EXPECT_EQ(1, toshiba.getFan());
ac.setFan(1);
EXPECT_EQ(1, ac.getFan());
toshiba.setFan(2);
EXPECT_EQ(2, toshiba.getFan());
ac.setFan(2);
EXPECT_EQ(2, ac.getFan());
toshiba.setFan(3);
EXPECT_EQ(3, toshiba.getFan());
ac.setFan(3);
EXPECT_EQ(3, ac.getFan());
toshiba.setFan(4);
EXPECT_EQ(4, toshiba.getFan());
ac.setFan(4);
EXPECT_EQ(4, ac.getFan());
toshiba.setFan(kToshibaAcFanMax + 1);
EXPECT_EQ(kToshibaAcFanMax, toshiba.getFan());
ac.setFan(kToshibaAcFanMax + 1);
EXPECT_EQ(kToshibaAcFanMax, ac.getFan());
}
TEST(TestToshibaACClass, RawState) {
IRToshibaAC toshiba(0);
toshiba.begin();
IRToshibaAC ac(kGpioUnused);
ac.begin();
uint8_t initial_state[kToshibaACStateLength] = {0xF2, 0x0D, 0x03, 0xFC, 0x01,
0x00, 0x00, 0x00, 0x01};
0x50, 0x00, 0x00, 0x51};
uint8_t modified_state[kToshibaACStateLength] = {0xF2, 0x0D, 0x03, 0xFC, 0x01,
0x00, 0xC1, 0x00, 0xC0};
// Verify the starting state.
EXPECT_STATE_EQ(initial_state, toshiba.getRaw(), kToshibaACBits);
EXPECT_TRUE(toshiba.getPower());
EXPECT_EQ(kToshibaAcAuto, toshiba.getMode());
EXPECT_EQ(kToshibaAcFanAuto, toshiba.getFan());
EXPECT_STATE_EQ(initial_state, ac.getRaw(), kToshibaACBits);
EXPECT_TRUE(ac.getPower());
EXPECT_EQ(kToshibaAcAuto, ac.getMode());
EXPECT_EQ(kToshibaAcFanAuto, ac.getFan());
// Change some settings.
toshiba.setMode(kToshibaAcCool);
toshiba.setFan(kToshibaAcFanMax);
toshiba.setTemp(kToshibaAcMinTemp);
ac.setMode(kToshibaAcCool);
ac.setFan(kToshibaAcFanMax);
ac.setTemp(kToshibaAcMinTemp);
// Verify those were set.
EXPECT_EQ(kToshibaAcCool, toshiba.getMode());
EXPECT_EQ(kToshibaAcFanMax, toshiba.getFan());
EXPECT_EQ(kToshibaAcMinTemp, toshiba.getTemp());
EXPECT_EQ(kToshibaAcCool, ac.getMode());
EXPECT_EQ(kToshibaAcFanMax, ac.getFan());
EXPECT_EQ(kToshibaAcMinTemp, ac.getTemp());
// Retrieve the modified state.
EXPECT_STATE_EQ(modified_state, toshiba.getRaw(), kToshibaACBits);
EXPECT_STATE_EQ(modified_state, ac.getRaw(), kToshibaACBits);
// Set it back to the initial state.
toshiba.setRaw(initial_state);
ac.setRaw(initial_state);
// Check the new state was set correctly.
EXPECT_TRUE(toshiba.getPower());
EXPECT_EQ(kToshibaAcAuto, toshiba.getMode());
EXPECT_EQ(kToshibaAcFanAuto, toshiba.getFan());
EXPECT_STATE_EQ(initial_state, toshiba.getRaw(), kToshibaACBits);
EXPECT_TRUE(ac.getPower());
EXPECT_EQ(kToshibaAcAuto, ac.getMode());
EXPECT_EQ(kToshibaAcFanAuto, ac.getFan());
EXPECT_STATE_EQ(initial_state, ac.getRaw(), kToshibaACBits);
}
TEST(TestToshibaACClass, Checksums) {
IRToshibaAC toshiba(0);
toshiba.begin();
IRToshibaAC ac(kGpioUnused);
ac.begin();
uint8_t initial_state[kToshibaACStateLength] = {0xF2, 0x0D, 0x03, 0xFC, 0x01,
0x00, 0x00, 0x00, 0x01};
@ -319,8 +277,8 @@ TEST(TestToshibaACClass, Checksums) {
uint8_t invalid_state[kToshibaACStateLength] = {0xF2, 0x0D, 0x03, 0xFC, 0x01,
0x00, 0x00, 0x00, 0x00};
EXPECT_EQ(0x01, toshiba.calcChecksum(initial_state));
EXPECT_EQ(0xC0, toshiba.calcChecksum(modified_state));
EXPECT_EQ(0x01, ac.calcChecksum(initial_state));
EXPECT_EQ(0xC0, ac.calcChecksum(modified_state));
// Check we can call it without instantiating the object.
EXPECT_EQ(0x01, IRToshibaAC::calcChecksum(initial_state));
// Use different lengths.
@ -344,150 +302,69 @@ TEST(TestToshibaACClass, Checksums) {
}
TEST(TestToshibaACClass, HumanReadableOutput) {
IRToshibaAC toshiba(0);
toshiba.begin();
IRToshibaAC ac(kGpioUnused);
ac.begin();
uint8_t initial_state[kToshibaACStateLength] = {0xF2, 0x0D, 0x03, 0xFC, 0x01,
0x00, 0x00, 0x00, 0x01};
uint8_t modified_state[kToshibaACStateLength] = {0xF2, 0x0D, 0x03, 0xFC, 0x01,
0x00, 0xC1, 0x00, 0xC0};
toshiba.setRaw(initial_state);
EXPECT_EQ("Power: On, Mode: 0 (Auto), Temp: 17C, Fan: 0 (Auto)",
toshiba.toString());
toshiba.setRaw(modified_state);
EXPECT_EQ("Power: On, Mode: 1 (Cool), Temp: 17C, Fan: 5 (High)",
toshiba.toString());
toshiba.off();
toshiba.setTemp(25);
toshiba.setFan(3);
toshiba.setMode(kToshibaAcDry);
EXPECT_EQ("Power: Off, Mode: 2 (Dry), Temp: 25C, Fan: 3 (Medium)",
toshiba.toString());
ac.setRaw(initial_state);
EXPECT_EQ("Temp: 17C, Power: On, Mode: 0 (Auto), Fan: 0 (Auto), "
"Turbo: Off, Econo: Off",
ac.toString());
ac.setRaw(modified_state);
EXPECT_EQ("Temp: 17C, Power: On, Mode: 1 (Cool), Fan: 5 (High), "
"Turbo: Off, Econo: Off",
ac.toString());
ac.setTemp(25);
ac.setFan(3);
ac.setMode(kToshibaAcDry);
EXPECT_EQ("Temp: 25C, Power: On, Mode: 2 (Dry), Fan: 3 (Medium), "
"Turbo: Off, Econo: Off",
ac.toString());
ac.off();
EXPECT_EQ("Temp: 25C, Power: Off, Fan: 3 (Medium), Turbo: Off, Econo: Off",
ac.toString());
}
TEST(TestToshibaACClass, MessageConstuction) {
IRToshibaAC toshiba(0);
IRsendTest irsend(4);
toshiba.begin();
irsend.begin();
IRToshibaAC ac(kGpioUnused);
ac.begin();
toshiba.setFan(1);
toshiba.setMode(kToshibaAcCool);
toshiba.setTemp(27);
toshiba.on();
ac.on();
ac.setFan(1);
ac.setMode(kToshibaAcCool);
ac.setTemp(27);
// Check everything for kicks.
EXPECT_EQ(1, toshiba.getFan());
EXPECT_EQ(kToshibaAcCool, toshiba.getMode());
EXPECT_EQ(27, toshiba.getTemp());
EXPECT_TRUE(toshiba.getPower());
irsend.reset();
irsend.sendToshibaAC(toshiba.getRaw());
EXPECT_EQ(
"f38000d50"
"m4400s4300"
"m543s1623m543s1623m543s1623m543s1623m543s472m543s472m543s1623m543s472"
"m543s472m543s472m543s472m543s472m543s1623m543s1623m543s472m543s1623"
"m543s472m543s472m543s472m543s472m543s472m543s472m543s1623m543s1623"
"m543s1623m543s1623m543s1623m543s1623m543s1623m543s1623m543s472m543s472"
"m543s472m543s472m543s472m543s472m543s472m543s472m543s472m543s1623"
"m543s1623m543s472m543s1623m543s472m543s472m543s472m543s472m543s472"
"m543s472m543s1623m543s472m543s472m543s472m543s472m543s472m543s1623"
"m543s472m543s472m543s472m543s472m543s472m543s472m543s472m543s472"
"m543s1623m543s1623m543s1623m543s472m543s472m543s472m543s472m543s472"
"m543s7048"
"m4400s4300"
"m543s1623m543s1623m543s1623m543s1623m543s472m543s472m543s1623m543s472"
"m543s472m543s472m543s472m543s472m543s1623m543s1623m543s472m543s1623"
"m543s472m543s472m543s472m543s472m543s472m543s472m543s1623m543s1623"
"m543s1623m543s1623m543s1623m543s1623m543s1623m543s1623m543s472m543s472"
"m543s472m543s472m543s472m543s472m543s472m543s472m543s472m543s1623"
"m543s1623m543s472m543s1623m543s472m543s472m543s472m543s472m543s472"
"m543s472m543s1623m543s472m543s472m543s472m543s472m543s472m543s1623"
"m543s472m543s472m543s472m543s472m543s472m543s472m543s472m543s472"
"m543s1623m543s1623m543s1623m543s472m543s472m543s472m543s472m543s472"
"m543s7048",
irsend.outputStr());
EXPECT_EQ(1, ac.getFan());
EXPECT_EQ(kToshibaAcCool, ac.getMode());
EXPECT_EQ(27, ac.getTemp());
EXPECT_TRUE(ac.getPower());
// Turn off the power and re-check.
toshiba.setPower(false);
ac.setPower(false);
// Check everything for kicks.
EXPECT_EQ(1, toshiba.getFan());
EXPECT_EQ(kToshibaAcCool, toshiba.getMode());
EXPECT_EQ(27, toshiba.getTemp());
EXPECT_FALSE(toshiba.getPower());
irsend.reset();
irsend.sendToshibaAC(toshiba.getRaw());
EXPECT_EQ(
"f38000d50"
"m4400s4300"
"m543s1623m543s1623m543s1623m543s1623m543s472m543s472m543s1623m543s472"
"m543s472m543s472m543s472m543s472m543s1623m543s1623m543s472m543s1623"
"m543s472m543s472m543s472m543s472m543s472m543s472m543s1623m543s1623"
"m543s1623m543s1623m543s1623m543s1623m543s1623m543s1623m543s472m543s472"
"m543s472m543s472m543s472m543s472m543s472m543s472m543s472m543s1623"
"m543s1623m543s472m543s1623m543s472m543s472m543s472m543s472m543s472"
"m543s472m543s1623m543s472m543s472m543s472m543s1623m543s1623m543s1623"
"m543s472m543s472m543s472m543s472m543s472m543s472m543s472m543s472"
"m543s1623m543s1623m543s1623m543s472m543s472m543s1623m543s1623m543s472"
"m543s7048"
"m4400s4300"
"m543s1623m543s1623m543s1623m543s1623m543s472m543s472m543s1623m543s472"
"m543s472m543s472m543s472m543s472m543s1623m543s1623m543s472m543s1623"
"m543s472m543s472m543s472m543s472m543s472m543s472m543s1623m543s1623"
"m543s1623m543s1623m543s1623m543s1623m543s1623m543s1623m543s472m543s472"
"m543s472m543s472m543s472m543s472m543s472m543s472m543s472m543s1623"
"m543s1623m543s472m543s1623m543s472m543s472m543s472m543s472m543s472"
"m543s472m543s1623m543s472m543s472m543s472m543s1623m543s1623m543s1623"
"m543s472m543s472m543s472m543s472m543s472m543s472m543s472m543s472"
"m543s1623m543s1623m543s1623m543s472m543s472m543s1623m543s1623m543s472"
"m543s7048",
irsend.outputStr());
EXPECT_EQ(1, ac.getFan());
EXPECT_EQ(kToshibaAcCool, ac.getMode());
EXPECT_EQ(27, ac.getTemp());
EXPECT_FALSE(ac.getPower());
// Turn the power back on, and check nothing changed.
toshiba.on();
ac.on();
EXPECT_EQ(1, toshiba.getFan());
EXPECT_EQ(kToshibaAcCool, toshiba.getMode());
EXPECT_EQ(27, toshiba.getTemp());
EXPECT_TRUE(toshiba.getPower());
irsend.reset();
irsend.sendToshibaAC(toshiba.getRaw());
EXPECT_EQ(
"f38000d50"
"m4400s4300"
"m543s1623m543s1623m543s1623m543s1623m543s472m543s472m543s1623m543s472"
"m543s472m543s472m543s472m543s472m543s1623m543s1623m543s472m543s1623"
"m543s472m543s472m543s472m543s472m543s472m543s472m543s1623m543s1623"
"m543s1623m543s1623m543s1623m543s1623m543s1623m543s1623m543s472m543s472"
"m543s472m543s472m543s472m543s472m543s472m543s472m543s472m543s1623"
"m543s1623m543s472m543s1623m543s472m543s472m543s472m543s472m543s472"
"m543s472m543s1623m543s472m543s472m543s472m543s472m543s472m543s1623"
"m543s472m543s472m543s472m543s472m543s472m543s472m543s472m543s472"
"m543s1623m543s1623m543s1623m543s472m543s472m543s472m543s472m543s472"
"m543s7048"
"m4400s4300"
"m543s1623m543s1623m543s1623m543s1623m543s472m543s472m543s1623m543s472"
"m543s472m543s472m543s472m543s472m543s1623m543s1623m543s472m543s1623"
"m543s472m543s472m543s472m543s472m543s472m543s472m543s1623m543s1623"
"m543s1623m543s1623m543s1623m543s1623m543s1623m543s1623m543s472m543s472"
"m543s472m543s472m543s472m543s472m543s472m543s472m543s472m543s1623"
"m543s1623m543s472m543s1623m543s472m543s472m543s472m543s472m543s472"
"m543s472m543s1623m543s472m543s472m543s472m543s472m543s472m543s1623"
"m543s472m543s472m543s472m543s472m543s472m543s472m543s472m543s472"
"m543s1623m543s1623m543s1623m543s472m543s472m543s472m543s472m543s472"
"m543s7048",
irsend.outputStr());
EXPECT_EQ(1, ac.getFan());
EXPECT_EQ(kToshibaAcCool, ac.getMode());
EXPECT_EQ(27, ac.getTemp());
EXPECT_TRUE(ac.getPower());
}
// Decoding a message we entirely constructed based solely on a given state.
TEST(TestDecodeToshibaAC, SyntheticExample) {
IRsendTest irsend(4);
IRrecv irrecv(4);
IRsendTest irsend(kGpioUnused);
IRrecv irrecv(kGpioUnused);
irsend.begin();
uint8_t expectedState[kToshibaACStateLength] = {0xF2, 0x0D, 0x03, 0xFC, 0x01,
@ -501,7 +378,8 @@ TEST(TestDecodeToshibaAC, SyntheticExample) {
ASSERT_EQ(kToshibaACBits, irsend.capture.bits);
EXPECT_STATE_EQ(expectedState, irsend.capture.state, irsend.capture.bits);
EXPECT_EQ(
"Power: On, Mode: 0 (Auto), Temp: 17C, Fan: 0 (Auto)",
"Temp: 17C, Power: On, Mode: 0 (Auto), Fan: 0 (Auto), Turbo: Off, "
"Econo: Off",
IRAcUtils::resultAcToString(&irsend.capture));
stdAc::state_t r, p;
ASSERT_TRUE(IRAcUtils::decodeToState(&irsend.capture, &r, &p));
@ -510,9 +388,9 @@ TEST(TestDecodeToshibaAC, SyntheticExample) {
// Test decoding against captures from a real Toshiba A/C remote.
// Recorded by @mwildbolz
TEST(TestDecodeToshibaAC, RealExamples) {
IRToshibaAC toshiba(0);
IRsendTest irsend(4);
IRrecv irrecv(4);
IRToshibaAC ac(kGpioUnused);
IRsendTest irsend(kGpioUnused);
IRrecv irrecv(kGpioUnused);
irsend.begin();
uint16_t rawData1[295] = {
@ -548,11 +426,11 @@ TEST(TestDecodeToshibaAC, RealExamples) {
EXPECT_TRUE(irrecv.decode(&irsend.capture));
ASSERT_EQ(TOSHIBA_AC, irsend.capture.decode_type);
ASSERT_EQ(kToshibaACBits, irsend.capture.bits);
toshiba.setRaw(irsend.capture.state);
EXPECT_TRUE(toshiba.getPower());
EXPECT_EQ(23, toshiba.getTemp());
EXPECT_EQ(kToshibaAcFanAuto, toshiba.getFan());
EXPECT_EQ(kToshibaAcAuto, toshiba.getMode());
ac.setRaw(irsend.capture.state);
EXPECT_TRUE(ac.getPower());
EXPECT_EQ(23, ac.getTemp());
EXPECT_EQ(kToshibaAcFanAuto, ac.getFan());
EXPECT_EQ(kToshibaAcAuto, ac.getMode());
uint16_t rawData2[295] = {
4500, 4236, 636, 1520, 642, 1520, 640, 1520, 664, 1492, 642, 440,
@ -587,11 +465,11 @@ TEST(TestDecodeToshibaAC, RealExamples) {
EXPECT_TRUE(irrecv.decode(&irsend.capture));
ASSERT_EQ(TOSHIBA_AC, irsend.capture.decode_type);
ASSERT_EQ(kToshibaACBits, irsend.capture.bits);
toshiba.setRaw(irsend.capture.state);
EXPECT_TRUE(toshiba.getPower());
EXPECT_EQ(17, toshiba.getTemp());
EXPECT_EQ(3, toshiba.getFan());
EXPECT_EQ(kToshibaAcCool, toshiba.getMode());
ac.setRaw(irsend.capture.state);
EXPECT_TRUE(ac.getPower());
EXPECT_EQ(17, ac.getTemp());
EXPECT_EQ(3, ac.getFan());
EXPECT_EQ(kToshibaAcCool, ac.getMode());
uint16_t rawData3[295] = {
4474, 4262, 642, 1514, 642, 1520, 642, 1520, 642, 1514, 642, 438,
@ -626,11 +504,11 @@ TEST(TestDecodeToshibaAC, RealExamples) {
EXPECT_TRUE(irrecv.decode(&irsend.capture));
ASSERT_EQ(TOSHIBA_AC, irsend.capture.decode_type);
ASSERT_EQ(kToshibaACBits, irsend.capture.bits);
toshiba.setRaw(irsend.capture.state);
EXPECT_TRUE(toshiba.getPower());
EXPECT_EQ(24, toshiba.getTemp());
EXPECT_EQ(kToshibaAcFanMax, toshiba.getFan());
EXPECT_EQ(kToshibaAcHeat, toshiba.getMode());
ac.setRaw(irsend.capture.state);
EXPECT_TRUE(ac.getPower());
EXPECT_EQ(24, ac.getTemp());
EXPECT_EQ(kToshibaAcFanMax, ac.getFan());
EXPECT_EQ(kToshibaAcHeat, ac.getMode());
uint16_t rawData4[295] = {
4474, 4262, 636, 1520, 640, 1520, 640, 1520, 638, 1518, 642, 438,
@ -665,16 +543,10 @@ TEST(TestDecodeToshibaAC, RealExamples) {
EXPECT_TRUE(irrecv.decode(&irsend.capture));
ASSERT_EQ(TOSHIBA_AC, irsend.capture.decode_type);
ASSERT_EQ(kToshibaACBits, irsend.capture.bits);
toshiba.setRaw(irsend.capture.state);
EXPECT_FALSE(toshiba.getPower());
EXPECT_EQ(22, toshiba.getTemp());
EXPECT_EQ(4, toshiba.getFan());
// Confirming the quirky behaviour that the 'Power OFF' signal
// sets the mode to heat.
// The previous state the remote was in was 'AUTO' just prior to
// sending the power off message.
EXPECT_EQ(kToshibaAcHeat, toshiba.getMode());
ac.setRaw(irsend.capture.state);
EXPECT_FALSE(ac.getPower());
EXPECT_EQ(22, ac.getTemp());
EXPECT_EQ(4, ac.getFan());
}
TEST(TestToshibaACClass, toCommon) {
@ -704,3 +576,166 @@ TEST(TestToshibaACClass, toCommon) {
ASSERT_EQ(-1, ac.toCommon().sleep);
ASSERT_EQ(-1, ac.toCommon().clock);
}
// Tests for CarrierAc2
/// Decode a "real" long example message.
TEST(TestDecodeToshibaAC, RealLongExample) {
IRsendTest irsend(kGpioUnused);
IRrecv irrecv(kGpioUnused);
irsend.begin();
irsend.reset();
// Data from:
// https://github.com/crankyoldgit/IRremoteESP8266/issues/1205#issuecomment-650475434
const uint16_t high_power_on[327] = {
4424, 4320,
582, 1574, 588, 1578, 582, 1574, 586, 1578, 586, 496, 582, 492, 586, 1576,
586, 492, 586, 492, 588, 496, 584, 496, 584, 496, 584, 1626, 534, 1626,
534, 494, 586, 1578, 582, 494, 586, 494, 586, 494, 588, 492, 586, 492,
586, 1576, 586, 494, 588, 492, 588, 1574, 588, 1576, 584, 1578, 584, 1574,
588, 1574, 588, 492, 588, 1572, 590, 1570, 590, 492, 588, 492, 590, 488,
590, 494, 584, 1570, 592, 492, 586, 490, 590, 1572, 590, 490, 590, 1570,
590, 490, 590, 1570, 590, 492, 588, 490, 588, 492, 588, 492, 590, 490,
590, 494, 586, 490, 590, 490, 588, 490, 590, 490, 588, 492, 590, 490,
588, 492, 590, 490, 590, 490, 590, 494, 584, 490, 590, 490, 590, 490,
590, 490, 588, 490, 588, 492, 588, 492, 586, 492, 588, 490, 588, 492,
588, 490, 590, 1572, 588, 494, 586, 1574, 588, 492, 588, 1572, 590, 1572,
588, 492, 588, 492, 586, 494, 588,
7422,
4424, 4320,
586, 1572, 588, 1572, 588, 1576, 584, 1574, 588, 494, 586, 492, 588, 1572,
588, 492, 588, 492, 588, 492, 588, 494, 586, 496, 584, 1574, 586, 1578,
582, 494, 586, 1578, 584, 494, 586, 492, 588, 492, 586, 496, 584, 494,
586, 1578, 584, 494, 586, 494, 584, 1574, 588, 1572, 586, 1574, 588, 1574,
588, 1572, 588, 494, 590, 1572, 588, 1574, 588, 492, 588, 492, 588, 492,
586, 492, 588, 1572, 588, 498, 582, 492, 588, 1576, 586, 492, 588, 1572,
588, 494, 588, 1572, 588, 492, 586, 492, 588, 492, 590, 490, 588, 492,
586, 492, 588, 492, 590, 490, 588, 490, 588, 492, 590, 490, 588, 492,
590, 490, 588, 490, 590, 490, 590, 490, 592, 488, 592, 494, 584, 494,
586, 490, 590, 494, 586, 494, 588, 488, 592, 490, 588, 492, 586, 490,
592, 490, 588, 1576, 584, 494, 586, 1570, 590, 494, 586, 1576, 582, 1572,
590, 490, 590, 490, 588, 490, 590}; // UNKNOWN 54926187
const uint8_t expectedState[kToshibaACStateLengthLong] = {
0xF2, 0x0D, 0x04, 0xFB, 0x09, 0x50, 0x00, 0x00, 0x01, 0x58};
irsend.sendRaw(high_power_on, 327, 38000);
irsend.makeDecodeResult();
ASSERT_TRUE(irrecv.decode(&irsend.capture));
EXPECT_EQ(decode_type_t::TOSHIBA_AC, irsend.capture.decode_type);
EXPECT_EQ(kToshibaACBitsLong, irsend.capture.bits);
EXPECT_STATE_EQ(expectedState, irsend.capture.state, irsend.capture.bits);
EXPECT_EQ(
"Temp: 22C, Power: On, Mode: 0 (Auto), Fan: 0 (Auto), Turbo: On, "
"Econo: Off",
IRAcUtils::resultAcToString(&irsend.capture));
}
/// Decode a synthetic long message.
TEST(TestDecodeToshibaAC, SyntheticLongExample) {
IRsendTest irsend(0);
IRrecv irrecv(0);
const uint8_t expectedState[kToshibaACStateLengthLong] = {
0xF2, 0x0D, 0x04, 0xFB, 0x09, 0x50, 0x00, 0x00, 0x01, 0x58};
irsend.begin();
irsend.reset();
irsend.sendToshibaAC(expectedState, kToshibaACStateLengthLong);
irsend.makeDecodeResult();
ASSERT_TRUE(irrecv.decode(&irsend.capture));
EXPECT_EQ(decode_type_t::TOSHIBA_AC, irsend.capture.decode_type);
EXPECT_EQ(kToshibaACBitsLong, irsend.capture.bits);
EXPECT_STATE_EQ(expectedState, irsend.capture.state, irsend.capture.bits);
EXPECT_EQ(
"f38000d50"
// 4424 4320
"m4400s4300"
// 582 1574 588 1578 582 1574 586 1578 586 496 582 492 586 1576 586 492
"m580s1600m580s1600m580s1600m580s1600m580s490m580s490m580s1600m580s490"
// 586 492 588 496 584 496 584 496 584 1626 534 1626 534 494 586 1578
"m580s490m580s490m580s490m580s490m580s1600m580s1600m580s490m580s1600"
// 582 494 586 494 586 494 588 492 586 492 586 1576 586 494 588 492
"m580s490m580s490m580s490m580s490m580s490m580s1600m580s490m580s490"
// 588 1574 588 1576 584 1578 584 1574 588 1574 588 492 588 1572 590 1570
"m580s1600m580s1600m580s1600m580s1600m580s1600m580s490m580s1600m580s1600"
// 590 492 588 492 590 488 590 494 584 1570 592 492 586 490 590 1572
"m580s490m580s490m580s490m580s490m580s1600m580s490m580s490m580s1600"
// 590 490 590 1570 590 490 590 1570 590 492 588 490 588 492 588 492
"m580s490m580s1600m580s490m580s1600m580s490m580s490m580s490m580s490"
// 590 490 590 494 586 490 590 490 588 490 590 490 588 492 590 490
"m580s490m580s490m580s490m580s490m580s490m580s490m580s490m580s490"
// 588 492 590 490 590 490 590 494 584 490 590 490 590 490 590 490
"m580s490m580s490m580s490m580s490m580s490m580s490m580s490m580s490"
// 588 490 588 492 588 492 586 492 588 490 588 492 588 490 590 1572
"m580s490m580s490m580s490m580s490m580s490m580s490m580s490m580s1600"
// 588 494 586 1574 588 492 588 1572 590 1572 588 492 588 492 586 494
"m580s490m580s1600m580s490m580s1600m580s1600m580s490m580s490m580s490"
// 588 7422
"m580s7400"
"m4400s4300"
"m580s1600m580s1600m580s1600m580s1600m580s490m580s490m580s1600m580s490"
"m580s490m580s490m580s490m580s490m580s1600m580s1600m580s490m580s1600"
"m580s490m580s490m580s490m580s490m580s490m580s1600m580s490m580s490"
"m580s1600m580s1600m580s1600m580s1600m580s1600m580s490m580s1600m580s1600"
"m580s490m580s490m580s490m580s490m580s1600m580s490m580s490m580s1600"
"m580s490m580s1600m580s490m580s1600m580s490m580s490m580s490m580s490"
"m580s490m580s490m580s490m580s490m580s490m580s490m580s490m580s490"
"m580s490m580s490m580s490m580s490m580s490m580s490m580s490m580s490"
"m580s490m580s490m580s490m580s490m580s490m580s490m580s490m580s1600"
"m580s490m580s1600m580s490m580s1600m580s1600m580s490m580s490m580s490"
"m580s7400",
irsend.outputStr());
}
/// Decode a "real" short example message.
TEST(TestDecodeToshibaAC, RealShortExample) {
IRsendTest irsend(kGpioUnused);
IRrecv irrecv(kGpioUnused);
irsend.begin();
irsend.reset();
// Data from:
// https://github.com/crankyoldgit/IRremoteESP8266/issues/1205#issuecomment-650475096
const uint16_t air_direction[115] = {
4424, 4318,
588, 1574, 588, 1572, 588, 1574, 586, 1572, 590, 490, 586, 494, 586, 1574,
588, 492, 586, 494, 586, 494, 586, 496, 584, 492, 588, 1572, 588, 1572,
590, 492, 588, 1572, 590, 490, 588, 492, 586, 494, 588, 492, 588, 492,
588, 494, 584, 492, 588, 1572, 588, 1574, 588, 1574, 588, 1572, 588, 1572,
588, 1572, 588, 1574, 590, 1570, 588, 494, 586, 496, 584, 494, 588, 1572,
588, 492, 588, 492, 588, 490, 588, 492, 590, 1572, 588, 492, 588, 496,
586, 492, 588, 492, 586, 492, 588, 492, 588, 490, 590, 490, 588, 492,
588, 490, 588, 1572, 588, 492, 588, 492, 588, 490, 588, 492, 588, 1572,
586}; // UNKNOWN DEB8845C
const uint8_t expectedState[kToshibaACStateLengthShort] = {
0xF2, 0x0D, 0x01, 0xFE, 0x21, 0x00, 0x21};
irsend.sendRaw(air_direction, 115, 38000);
irsend.makeDecodeResult();
ASSERT_TRUE(irrecv.decode(&irsend.capture));
EXPECT_EQ(decode_type_t::TOSHIBA_AC, irsend.capture.decode_type);
EXPECT_EQ(kToshibaACBitsShort, irsend.capture.bits);
EXPECT_STATE_EQ(expectedState, irsend.capture.state, irsend.capture.bits);
EXPECT_EQ(
"Temp: 17C, Swing(V): 0 (Step)",
IRAcUtils::resultAcToString(&irsend.capture));
}
TEST(TestToshibaACClass, ConstructLongState) {
IRToshibaAC ac(kGpioUnused);
ac.setPower(true);
ac.setMode(kToshibaAcDry);
ac.setTemp(29);
ac.setFan(2);
ac.setSwing(false);
ac.setTurbo(false);
ac.setEcono(true);
EXPECT_EQ(
"Temp: 29C, Power: On, Mode: 2 (Dry), Fan: 2 (UNKNOWN), "
"Turbo: Off, Econo: On",
ac.toString());
EXPECT_EQ(kToshibaACStateLengthLong, ac.getStateLength());
const uint8_t expectedState[kToshibaACStateLengthLong] = {
0xF2, 0x0D, 0x04, 0xFB, 0x09, 0xC0, 0x62, 0x00, 0x03, 0xA8};
EXPECT_STATE_EQ(expectedState, ac.getRaw(), kToshibaACBitsLong);
EXPECT_EQ(kToshibaACStateLengthLong, ac.getStateLength());
}

0
lib/IRremoteESP8266-2.7.8/tools/RawToGlobalCache.sh Normal file → Executable file
View File

View File

View File

0
lib/IRremoteESP8266-2.7.8/tools/generate_irtext_h.sh Normal file → Executable file
View File

0
lib/IRremoteESP8266-2.7.8/tools/mkkeywords Normal file → Executable file
View File

View File

View File

@ -13,6 +13,7 @@
- Add command ``SetOption101 0/1`` to add the Zigbee source endpoint as suffix to attributes, ex `Power3` instead of `Power` if sent from endpoint 3
- Add command (``S``)``SerialSend6`` \<comma seperated values\> (#8937)
- Add support for Sonoff Zigbee Bridge as module 75 (#8583)
- Change IRRemoteESP8266 IR lib to pre-2.7.9, fixing Samsung and Pioneer protocols (#8938)
### 8.3.1.6 20200617