Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Coolix improvements #944

Merged
merged 15 commits into from
Oct 22, 2019
Merged
10 changes: 8 additions & 2 deletions src/IRac.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -251,6 +251,13 @@ void IRac::coolix(IRCoolixAC *ac,
const bool turbo, const bool light, const bool clean,
const int16_t sleep) {
ac->begin();
ac->setPower(on);
if (!on) {
// after turn off AC no more commands should
// be accepted
ac->send();
return;
}
ac->setMode(ac->convertMode(mode));
ac->setTemp(degrees);
ac->setFan(ac->convertFan(fan));
Expand Down Expand Up @@ -284,8 +291,6 @@ void IRac::coolix(IRCoolixAC *ac,
ac->setClean();
ac->send();
}
// Power gets done last, as off has a special command.
ac->setPower(on);
ac->send();
}
#endif // SEND_COOLIX
Expand Down Expand Up @@ -2030,6 +2035,7 @@ namespace IRAcUtils {
#if DECODE_COOLIX
case decode_type_t::COOLIX: {
IRCoolixAC ac(0);
ac.on();
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this statement required? setRaw() should (in theory) generate the correct internal state for setPower() from result->value.
As I understand it (I don't have access to a "Coolix" compatible A/C system), every IR message that is NOT an "off" command is considered an "on" message. Is that a correct assumption?

ac.setRaw(result->value); // Coolix uses value instead of state.
return ac.toString();
}
Expand Down
155 changes: 107 additions & 48 deletions src/ir_Coolix.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
// Brand: Midea, Model: RG52D/BGE Remote
// Brand: Midea, Model: MS12FU-10HRDN1-QRD0GW(B) A/C
// Brand: Midea, Model: MSABAU-07HRFN1-QRD0GW A/C (circa 2016)
// Brand: Tokio, Model: AATOEMF17-12CHR1SW split-type RG51|50/BGE Remote
// Ref:
// https://github.com/crankyoldgit/IRremoteESP8266/issues/484

Expand Down Expand Up @@ -103,21 +104,43 @@ IRCoolixAC::IRCoolixAC(const uint16_t pin, const bool inverted,
const bool use_modulation)
: _irsend(pin, inverted, use_modulation) { stateReset(); }

void IRCoolixAC::stateReset() { setRaw(kCoolixDefaultState); }
void IRCoolixAC::stateReset() {
setRaw(kCoolixDefaultState);
clearSensorTemp();
powerFlag = false;
turboFlag = false;
ledFlag = false;
cleanFlag = false;
sleepFlag = false;
swingFlag = false;
swingHFlag = false;
swingVFlag = false;
}

void IRCoolixAC::begin() { _irsend.begin(); }

#if SEND_COOLIX
void IRCoolixAC::send(const uint16_t repeat) {
_irsend.sendCOOLIX(remote_state, kCoolixBits, repeat);
// make sure to remove special state from remote_state
// after command has being transmitted.
recoverSavedState();
ribeirodanielf marked this conversation as resolved.
Show resolved Hide resolved
}
#endif // SEND_COOLIX

uint32_t IRCoolixAC::getRaw() { return remote_state; }

void IRCoolixAC::setRaw(const uint32_t new_code) {
remote_state = new_code;
saved_state = new_code;
if (!handleSpecialState(new_code)) {
// it isn`t special so might afect Temp|mode|Fan
if (new_code == kCoolixCmdFan) {
setMode(kCoolixFan);
} else {
// must be a command changing Temp|Mode|Fan
// it is safe to just copy to remote var
remote_state = new_code;
}
}
}

// Return true if the current state is a special state.
Expand All @@ -133,6 +156,36 @@ bool IRCoolixAC::isSpecialState(void) {
}
}

// Special state means commands that are not
// affecting Temperature/Mode/Fan
bool IRCoolixAC::handleSpecialState(const uint32_t data) {
switch (data) {
case kCoolixClean:
cleanFlag = !cleanFlag;
break;
case kCoolixLed:
ledFlag = !ledFlag;
break;
case kCoolixOff:
powerFlag = false;
break;
case kCoolixSwing:
swingFlag = !swingFlag;
break;
case kCoolixSleep:
sleepFlag = !sleepFlag;
break;
case kCoolixTurbo:
turboFlag = !turboFlag;
break;
default:
return false;
}
return true;
}

// must be called before every special state
// to make sure the remote_state is safe
void IRCoolixAC::updateSavedState(void) {
if (!isSpecialState()) saved_state = remote_state;
}
Expand All @@ -145,17 +198,12 @@ void IRCoolixAC::recoverSavedState(void) {
if (isSpecialState()) stateReset();
}

uint32_t IRCoolixAC::getNormalState(void) {
return isSpecialState() ? saved_state : remote_state;
}

void IRCoolixAC::setTempRaw(const uint8_t code) {
recoverSavedState();
setBits(&remote_state, kCoolixTempOffset, kCoolixTempSize, code);
}

uint8_t IRCoolixAC::getTempRaw() {
return GETBITS32(getNormalState(), kCoolixTempOffset, kCoolixTempSize);
return GETBITS32(remote_state, kCoolixTempOffset, kCoolixTempSize);
}

void IRCoolixAC::setTemp(const uint8_t desired) {
Expand All @@ -169,11 +217,10 @@ uint8_t IRCoolixAC::getTemp() {
const uint8_t code = getTempRaw();
for (uint8_t i = 0; i < kCoolixTempRange; i++)
if (kCoolixTempMap[i] == code) return kCoolixTempMin + i;
return kCoolixUnknown; // Not a temp we expected.
return kCoolixTempMax; // Not a temp we expected.
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Might this hide when we are getting a value we don't (yet) understand, and perhaps should?

}

void IRCoolixAC::setSensorTempRaw(const uint8_t code) {
recoverSavedState();
setBits(&remote_state, kCoolixSensorTempOffset, kCoolixSensorTempSize, code);
}

Expand All @@ -186,77 +233,89 @@ void IRCoolixAC::setSensorTemp(const uint8_t desired) {
}

uint8_t IRCoolixAC::getSensorTemp() {
return GETBITS32(getNormalState(), kCoolixSensorTempOffset,
return GETBITS32(remote_state, kCoolixSensorTempOffset,
kCoolixSensorTempSize) + kCoolixSensorTempMin;
}

// There is only an off state. Everything else is "on".
bool IRCoolixAC::getPower() { return remote_state != kCoolixOff; }
bool IRCoolixAC::getPower() {
// There is only an off state. Everything else is "on".
return powerFlag;
}

void IRCoolixAC::setPower(const bool on) {
if (on) {
// There really is no distinct "on" setting, just ensure it a normal state.
recoverSavedState();
if (powerFlag) {
if (!on) {
updateSavedState();
remote_state = kCoolixOff;
}
} else {
updateSavedState();
remote_state = kCoolixOff;
if (on) {
// at this point remote_state must be ready
// to be transmitted
recoverSavedState();
}
}
powerFlag = on;
}

void IRCoolixAC::on(void) { this->setPower(true); }

void IRCoolixAC::off(void) { this->setPower(false); }

bool IRCoolixAC::getSwing() { return remote_state == kCoolixSwing; }
bool IRCoolixAC::getSwing() { return swingFlag; }

void IRCoolixAC::setSwing() {
// Assumes that repeated sending "swing" toggles the action on the device.
updateSavedState();
remote_state = kCoolixSwing;
swingFlag = !swingFlag;
}

bool IRCoolixAC::getSleep() { return remote_state == kCoolixSleep; }
bool IRCoolixAC::getSleep() { return sleepFlag; }

void IRCoolixAC::setSleep() {
updateSavedState();
remote_state = kCoolixSleep;
sleepFlag = !sleepFlag;
}

bool IRCoolixAC::getTurbo() { return remote_state == kCoolixTurbo; }
bool IRCoolixAC::getTurbo() { return turboFlag; }

void IRCoolixAC::setTurbo() {
// Assumes that repeated sending "turbo" toggles the action on the device.
updateSavedState();
remote_state = kCoolixTurbo;
turboFlag = !turboFlag;
}

bool IRCoolixAC::getLed() { return remote_state == kCoolixLed; }
bool IRCoolixAC::getLed() { return ledFlag; }

void IRCoolixAC::setLed() {
// Assumes that repeated sending "Led" toggles the action on the device.
updateSavedState();
remote_state = kCoolixLed;
ledFlag = !ledFlag;
}

bool IRCoolixAC::getClean() { return remote_state == kCoolixClean; }
bool IRCoolixAC::getClean() { return cleanFlag; }

void IRCoolixAC::setClean() {
updateSavedState();
remote_state = kCoolixClean;
cleanFlag = !cleanFlag;
}

bool IRCoolixAC::getZoneFollow() {
return GETBIT32(getNormalState(), kCoolixZoneFollowMaskOffset);
return zoneFollowFlag;
}

// Internal use only.
void IRCoolixAC::setZoneFollow(bool on) {
recoverSavedState();
zoneFollowFlag = on;
setBit(&remote_state, kCoolixZoneFollowMaskOffset, on);
}

void IRCoolixAC::clearSensorTemp() {
recoverSavedState();
setZoneFollow(false);
setSensorTempRaw(kCoolixSensorTempIgnoreCode);
}
Expand All @@ -266,51 +325,49 @@ void IRCoolixAC::setMode(const uint8_t mode) {
switch (actualmode) {
case kCoolixAuto:
case kCoolixDry:
if (this->getFan() == kCoolixFanAuto)
// No kCoolixFanAuto in Dry/Auto mode.
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this comment/assumption incorrect? IIRC when I added this, the person said there was no "auto" fan speed when in dry/auto operation modes.
Can you confirm that isn't the case? If you can have fan set to auto in dry/auto operation mode, then this is obviously fine as is.

this->setFan(kCoolixFanAuto0, false);
setFan(kCoolixFanAuto0, false);
break;
case kCoolixCool:
case kCoolixHeat:
case kCoolixFan:
if (this->getFan() == kCoolixFanAuto0)
// kCoolixFanAuto0 only in Dry/Auto mode.
this->setFan(kCoolixFanAuto, false);
setFan(kCoolixFanAuto, false);
break;
default: // Anything else, go with Auto mode.
this->setMode(kCoolixAuto);
setMode(kCoolixAuto);
setFan(kCoolixFanAuto0, false);
return;
}
setTemp(getTemp());
// Fan mode is a special case of Dry.
if (mode == kCoolixFan) actualmode = kCoolixDry;
recoverSavedState();
if (mode == kCoolixFan) {
actualmode = kCoolixDry;
setTempRaw(kCoolixFanTempCode);
}
setBits(&remote_state, kCoolixModeOffset, kCoolixModeSize, actualmode);
// Force the temp into a known-good state.
setTemp(getTemp());
if (mode == kCoolixFan) setTempRaw(kCoolixFanTempCode);
}

uint8_t IRCoolixAC::getMode() {
uint8_t mode = GETBITS32(getNormalState(), kCoolixModeOffset,
uint8_t mode = GETBITS32(remote_state, kCoolixModeOffset,
kCoolixModeSize);
if (mode == kCoolixDry && getTempRaw() == kCoolixFanTempCode)
return kCoolixFan;
if (mode == kCoolixDry)
if (getTempRaw() == kCoolixFanTempCode) return kCoolixFan;
return mode;
}

uint8_t IRCoolixAC::getFan() {
return GETBITS32(getNormalState(), kCoolixFanOffset, kCoolixFanSize);
return GETBITS32(remote_state, kCoolixFanOffset, kCoolixFanSize);
}

void IRCoolixAC::setFan(const uint8_t speed, const bool modecheck) {
recoverSavedState();
uint8_t newspeed = speed;
switch (speed) {
case kCoolixFanAuto: // Dry & Auto mode can't have this speed.
if (modecheck) {
switch (this->getMode()) {
case kCoolixAuto:
case kCoolixDry: newspeed = kCoolixFanAuto0;
case kCoolixDry:
newspeed = kCoolixFanAuto0;
break;
}
}
break;
Expand All @@ -327,9 +384,11 @@ void IRCoolixAC::setFan(const uint8_t speed, const bool modecheck) {
case kCoolixFanMed:
case kCoolixFanMax:
case kCoolixFanZoneFollow:
case kCoolixFanFixed: break;
// Unknown speed requested.
default: newspeed = kCoolixFanAuto;
case kCoolixFanFixed:
break;
default: // Unknown speed requested.
newspeed = kCoolixFanAuto;
break;
crankyoldgit marked this conversation as resolved.
Show resolved Hide resolved
}
setBits(&remote_state, kCoolixFanOffset, kCoolixFanSize, newspeed);
}
Expand Down
28 changes: 22 additions & 6 deletions src/ir_Coolix.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
// Brand: Midea, Model: RG52D/BGE Remote
// Brand: Midea, Model: MS12FU-10HRDN1-QRD0GW(B) A/C
// Brand: Midea, Model: MSABAU-07HRFN1-QRD0GW A/C (circa 2016)
// Brand: Tokio, Model: AATOEMF17-12CHR1SW split-type RG51|50/BGE Remote
// Ref:
// https://github.com/crankyoldgit/IRremoteESP8266/issues/484
// Kudos:
Expand Down Expand Up @@ -83,12 +84,15 @@ const uint8_t kCoolixSensorTempSize = 4;
// Fixed states/messages.
const uint8_t kCoolixPrefix = 0b1011; // 0xB
const uint8_t kCoolixUnknown = 0xFF;
const uint32_t kCoolixOff = 0b101100100111101111100000; // 0xB27BE0
const uint32_t kCoolixSwing = 0b101100100110101111100000; // 0xB26BE0
const uint32_t kCoolixSleep = 0b101100101110000000000011; // 0xB2E003
const uint32_t kCoolixTurbo = 0b101101011111010110100010; // 0xB5F5A2
const uint32_t kCoolixLed = 0b101101011111010110100101; // 0xB5F5A5
const uint32_t kCoolixClean = 0b101101011111010110101010; // 0xB5F5AA
const uint32_t kCoolixOff = 0b101100100111101111100000; // 0xB27BE0
const uint32_t kCoolixSwing = 0b101100100110101111100000; // 0xB26BE0
const uint32_t kCoolixSwingH = 0b101100101111010110100010; // 0xB5F5A2
const uint32_t kCoolixSwingV = 0b101100100000111111100000; // 0xB20FE0
const uint32_t kCoolixSleep = 0b101100101110000000000011; // 0xB2E003
const uint32_t kCoolixTurbo = 0b101101011111010110100010; // 0xB5F5A2
const uint32_t kCoolixLed = 0b101101011111010110100101; // 0xB5F5A5
const uint32_t kCoolixClean = 0b101101011111010110101010; // 0xB5F5AA
const uint32_t kCoolixCmdFan = 0b101100101011111111100100; // 0xB2BFE4
// On, 25C, Mode: Auto, Fan: Auto, Zone Follow: Off, Sensor Temp: Ignore.
const uint32_t kCoolixDefaultState = 0b101100100001111111001000; // 0xB21FC8

Expand Down Expand Up @@ -143,13 +147,25 @@ class IRCoolixAC {
#else
IRsendTest _irsend;
#endif
// internal state
bool powerFlag;
bool turboFlag;
bool ledFlag;
bool cleanFlag;
bool sleepFlag;
bool zoneFollowFlag;
bool swingFlag;
bool swingHFlag;
bool swingVFlag;

uint32_t remote_state; // The state of the IR remote in IR code form.
uint32_t saved_state; // Copy of the state if we required a special mode.
void setTempRaw(const uint8_t code);
uint8_t getTempRaw();
void setSensorTempRaw(const uint8_t code);
void setZoneFollow(const bool on);
bool isSpecialState(void);
bool handleSpecialState(const uint32_t data);
void updateSavedState(void);
void recoverSavedState(void);
uint32_t getNormalState(void);
Expand Down
Loading