Skip to content

Commit

Permalink
Add clock & timers to DAIKIN64
Browse files Browse the repository at this point in the history
* Add support for clock, On time, & off timer etc.

That should be everything that has been reverse engineered on the 
spreadsheet by @chongkk

Fixes #1064
  • Loading branch information
crankyoldgit committed Mar 25, 2020
1 parent 1e9bd6e commit 4c45e74
Show file tree
Hide file tree
Showing 6 changed files with 140 additions and 14 deletions.
6 changes: 4 additions & 2 deletions src/IRac.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -469,7 +469,8 @@ void IRac::daikin64(IRDaikin64 *ac,
const bool on, const stdAc::opmode_t mode,
const float degrees, const stdAc::fanspeed_t fan,
const stdAc::swingv_t swingv,
const bool quiet, const bool turbo, const int16_t sleep) {
const bool quiet, const bool turbo,
const int16_t sleep, const int16_t clock) {
ac->begin();
ac->setPowerToggle(on);
ac->setMode(ac->convertMode(mode));
Expand All @@ -479,6 +480,7 @@ void IRac::daikin64(IRDaikin64 *ac,
ac->setTurbo(turbo);
ac->setQuiet(quiet);
ac->setSleep(sleep >= 0);
ac->setClock(clock);
ac->send();
}
#endif // SEND_DAIKIN64
Expand Down Expand Up @@ -1418,7 +1420,7 @@ bool IRac::sendAc(const stdAc::state_t desired, const stdAc::state_t *prev) {
{
IRDaikin64 ac(_pin, _inverted, _modulation);
daikin64(&ac, send.power, send.mode, degC, send.fanspeed, send.swingv,
send.quiet, send.turbo, send.sleep);
send.quiet, send.turbo, send.sleep, send.clock);
break;
}
#endif // SEND_DAIKIN64
Expand Down
3 changes: 2 additions & 1 deletion src/IRac.h
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,8 @@ void daikin216(IRDaikin216 *ac,
const bool on, const stdAc::opmode_t mode,
const float degrees, const stdAc::fanspeed_t fan,
const stdAc::swingv_t swingv,
const bool quiet, const bool turbo, const int16_t sleep = -1);
const bool quiet, const bool turbo,
const int16_t sleep = -1, const int16_t clock = -1);
#endif // SEND_DAIKIN64
#if SEND_ELECTRA_AC
void electra(IRElectraAc *ac,
Expand Down
72 changes: 71 additions & 1 deletion src/ir_Daikin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3483,6 +3483,69 @@ bool IRDaikin64::getSleep(void) {
return GETBIT64(remote_state, kDaikin64SleepBit);
}

void IRDaikin64::setClock(const uint16_t mins_since_midnight) {
uint16_t mins = mins_since_midnight;
if (mins_since_midnight >= 24 * 60) mins = 0; // Bounds check.
setBits(&remote_state, kDaikin64ClockOffset, kDaikin64ClockMinsSize,
uint8ToBcd(mins % 60)); // Mins
setBits(&remote_state, kDaikin64ClockOffset + kDaikin64ClockMinsSize,
kDaikin64ClockHoursSize, uint8ToBcd(mins / 60)); // Hours
}

uint16_t IRDaikin64::getClock(void) {
return bcdToUint8(GETBITS64(remote_state,
kDaikin64ClockOffset + kDaikin64ClockMinsSize,
kDaikin64ClockHoursSize)) * 60 +
bcdToUint8(GETBITS64(remote_state, kDaikin64ClockOffset,
kDaikin64ClockMinsSize));
}

void IRDaikin64::setOnTimeEnabled(const bool on) {
setBit(&remote_state, kDaikin64OnTimeEnableBit, on);
}

bool IRDaikin64::getOnTimeEnabled(void) {
return GETBIT64(remote_state, kDaikin64OnTimeEnableBit);
}

uint16_t IRDaikin64::getOnTime(void) {
return bcdToUint8(GETBITS64(remote_state, kDaikin64OnTimeOffset,
kDaikin64OnTimeSize)) * 60 +
(GETBIT64(remote_state, kDaikin64OnTimeHalfHourBit) ? 30 : 0);
}

void IRDaikin64::setOnTime(const uint16_t mins_since_midnight) {
uint16_t halfhours = mins_since_midnight / 30;
if (mins_since_midnight >= 24 * 60) halfhours = 0; // Bounds check.
setBits(&remote_state, kDaikin64OnTimeOffset, kDaikin64OnTimeSize,
uint8ToBcd(halfhours / 2)); // Hours
// Half Hour
setBit(&remote_state, kDaikin64OnTimeHalfHourBit, halfhours % 2);
}

void IRDaikin64::setOffTimeEnabled(const bool on) {
setBit(&remote_state, kDaikin64OffTimeEnableBit, on);
}

bool IRDaikin64::getOffTimeEnabled(void) {
return GETBIT64(remote_state, kDaikin64OffTimeEnableBit);
}

uint16_t IRDaikin64::getOffTime(void) {
return bcdToUint8(GETBITS64(remote_state, kDaikin64OffTimeOffset,
kDaikin64OffTimeSize)) * 60 +
(GETBIT64(remote_state, kDaikin64OffTimeHalfHourBit) ? 30 : 0);
}

void IRDaikin64::setOffTime(const uint16_t mins_since_midnight) {
uint16_t halfhours = mins_since_midnight / 30;
if (mins_since_midnight >= 24 * 60) halfhours = 0; // Bounds check.
setBits(&remote_state, kDaikin64OffTimeOffset, kDaikin64OffTimeSize,
uint8ToBcd(halfhours / 2)); // Hours
// Half Hour
setBit(&remote_state, kDaikin64OffTimeHalfHourBit, halfhours % 2);
}

// Convert the internal state into a human readable string.
String IRDaikin64::toString(void) {
String result = "";
Expand All @@ -3505,6 +3568,13 @@ String IRDaikin64::toString(void) {
result += addBoolToString(getQuiet(), kQuietStr);
result += addBoolToString(getSwingVertical(), kSwingVStr);
result += addBoolToString(getSleep(), kSleepStr);
result += addLabeledString(minsToString(getClock()), kClockStr);
result += addLabeledString(getOnTimeEnabled()
? minsToString(getOnTime()) : kOffStr,
kOnTimerStr);
result += addLabeledString(getOffTimeEnabled()
? minsToString(getOffTime()) : kOffStr,
kOffTimerStr);
return result;
}

Expand All @@ -3524,13 +3594,13 @@ stdAc::state_t IRDaikin64::toCommon(const stdAc::state_t *prev) {
result.turbo = getTurbo();
result.quiet = getQuiet();
result.sleep = getSleep() ? 0 : -1;
result.clock = getClock();
// Not supported.
result.swingh = stdAc::swingh_t::kOff;
result.clean = false;
result.filter = false;
result.beep = false;
result.econo = false;
result.light = false;
result.clock = -1;
return result;
}
32 changes: 29 additions & 3 deletions src/ir_Daikin.h
Original file line number Diff line number Diff line change
Expand Up @@ -446,6 +446,9 @@ const uint16_t kDaikin64Overhead = 9;
const uint64_t kDaikin64KnownGoodState = 0x7C16161607204216;
const uint8_t kDaikin64ModeOffset = 8;
const uint8_t kDaikin64ModeSize = 4; // Mask 0b111100000000
const uint8_t kDaikin64Dry = 0b001;
const uint8_t kDaikin64Cool = 0b010;
const uint8_t kDaikin64Fan = 0b100;
const uint8_t kDaikin64FanOffset = kDaikin64ModeOffset + kDaikin64ModeSize;
const uint8_t kDaikin64FanSize = 4; // Mask 0b1111000000000000
const uint8_t kDaikin64FanAuto = 0b0001;
Expand All @@ -454,9 +457,22 @@ const uint8_t kDaikin64FanMed = 0b0100;
const uint8_t kDaikin64FanHigh = 0b0010;
const uint8_t kDaikin64FanQuiet = 0b1001;
const uint8_t kDaikin64FanTurbo = 0b0011;
const uint8_t kDaikin64Dry = 0b001;
const uint8_t kDaikin64Cool = 0b010;
const uint8_t kDaikin64Fan = 0b100;
const uint8_t kDaikin64ClockOffset = kDaikin64FanOffset + kDaikin64FanSize;
const uint8_t kDaikin64ClockMinsSize = 8;
const uint8_t kDaikin64ClockHoursSize = 8;
const uint8_t kDaikin64ClockSize = kDaikin64ClockMinsSize +
kDaikin64ClockHoursSize; // Mask 0b1111111111111111 << 15
const uint8_t kDaikin64OnTimeOffset = kDaikin64ClockOffset +
kDaikin64ClockSize;
const uint8_t kDaikin64OnTimeSize = 6;
const uint8_t kDaikin64OnTimeHalfHourBit = kDaikin64OnTimeOffset +
kDaikin64OnTimeSize;
const uint8_t kDaikin64OnTimeEnableBit = kDaikin64OnTimeHalfHourBit + 1;
const uint8_t kDaikin64OffTimeOffset = kDaikin64OnTimeEnableBit + 1;
const uint8_t kDaikin64OffTimeSize = 6;
const uint8_t kDaikin64OffTimeHalfHourBit = kDaikin64OffTimeOffset +
kDaikin64OffTimeSize;
const uint8_t kDaikin64OffTimeEnableBit = kDaikin64OffTimeHalfHourBit + 1;
const uint8_t kDaikin64TempOffset = 48;
const uint8_t kDaikin64TempSize = 8; // Mask 0b11111111 << 47
const uint8_t kDaikin64MinTemp = 16; // Celsius
Expand Down Expand Up @@ -949,6 +965,16 @@ class IRDaikin64 {
void setQuiet(const bool on);
bool getTurbo(void);
void setTurbo(const bool on);
void setClock(const uint16_t mins_since_midnight);
uint16_t getClock(void);
void setOnTimeEnabled(const bool on);
bool getOnTimeEnabled(void);
void setOnTime(const uint16_t mins_since_midnight);
uint16_t getOnTime(void);
void setOffTimeEnabled(const bool on);
bool getOffTimeEnabled(void);
void setOffTime(const uint16_t mins_since_midnight);
uint16_t getOffTime(void);
static uint8_t convertMode(const stdAc::opmode_t mode);
static uint8_t convertFan(const stdAc::fanspeed_t speed);
static stdAc::opmode_t toCommonMode(const uint8_t mode);
Expand Down
6 changes: 4 additions & 2 deletions test/IRac_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -337,7 +337,8 @@ TEST(TestIRac, Daikin64) {
IRrecv capture(kGpioUnused);
char expected[] =
"Power Toggle: On, Mode: 2 (Cool), Temp: 27C, Fan: 8 (Low), "
"Turbo: Off, Quiet: Off, Swing(V): On, Sleep: On";
"Turbo: Off, Quiet: Off, Swing(V): On, Sleep: On, "
"Clock: 17:59, On Timer: Off, Off Timer: Off";

ac.begin();
irac.daikin64(&ac,
Expand All @@ -348,7 +349,8 @@ TEST(TestIRac, Daikin64) {
stdAc::swingv_t::kAuto, // Veritcal swing
false, // Quiet
false, // Turbo
360); // Sleep
360, // Sleep
17 * 60 + 59); // Clock
ASSERT_EQ(expected, ac.toString());
ac._irsend.makeDecodeResult();
EXPECT_TRUE(capture.decode(&ac._irsend.capture));
Expand Down
35 changes: 30 additions & 5 deletions test/ir_Daikin_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3434,7 +3434,8 @@ TEST(TestDecodeDaikin64, RealExample) {
EXPECT_EQ(0x7C16161607204216, irsend.capture.value);
EXPECT_EQ(
"Power Toggle: On, Mode: 2 (Cool), Temp: 16C, Fan: 4 (Medium), "
"Turbo: Off, Quiet: Off, Swing(V): Off, Sleep: Off",
"Turbo: Off, Quiet: Off, Swing(V): Off, Sleep: Off, "
"Clock: 07:20, On Timer: Off, Off Timer: Off",
IRAcUtils::resultAcToString(&irsend.capture));
}

Expand Down Expand Up @@ -3629,12 +3630,27 @@ TEST(TestDaikin64Class, SwingVertical) {
EXPECT_TRUE(ac.getSwingVertical());
}

TEST(TestDaikin64Class, Clock) {
IRDaikin64 ac(kGpioUnused);
ac.begin();

ac.setClock(0);
EXPECT_EQ(0, ac.getClock());
ac.setClock(23 * 60 + 59);
EXPECT_EQ(23 * 60 + 59, ac.getClock());
ac.setClock(23 * 60 + 59 + 1);
EXPECT_EQ(0, ac.getClock());
ac.setClock(24 * 60 + 99);
EXPECT_EQ(0, ac.getClock());
}

// Test human readable output.
TEST(TestDaikin64Class, HumanReadable) {
IRDaikin64 ac(kGpioUnused);
EXPECT_EQ(
"Power Toggle: On, Mode: 2 (Cool), Temp: 16C, Fan: 4 (Medium), "
"Turbo: Off, Quiet: Off, Swing(V): Off, Sleep: Off",
"Turbo: Off, Quiet: Off, Swing(V): Off, Sleep: Off, "
"Clock: 07:20, On Timer: Off, Off Timer: Off",
ac.toString());
ac.setPowerToggle(false);
ac.setMode(kDaikin64Fan);
Expand All @@ -3643,17 +3659,26 @@ TEST(TestDaikin64Class, HumanReadable) {
ac.setSwingVertical(true);
EXPECT_EQ(
"Power Toggle: Off, Mode: 4 (Fan), Temp: 30C, Fan: 1 (Auto), "
"Turbo: Off, Quiet: Off, Swing(V): On, Sleep: Off",
"Turbo: Off, Quiet: Off, Swing(V): On, Sleep: Off, "
"Clock: 07:20, On Timer: Off, Off Timer: Off",
ac.toString());
ac.setTurbo(true);
ac.setOffTimeEnabled(true);
ac.setOffTime(23 * 60 + 30);
EXPECT_EQ(
"Power Toggle: Off, Mode: 4 (Fan), Temp: 30C, Fan: 3 (Turbo), "
"Turbo: On, Quiet: Off, Swing(V): On, Sleep: Off",
"Turbo: On, Quiet: Off, Swing(V): On, Sleep: Off, "
"Clock: 07:20, On Timer: Off, Off Timer: 23:30",
ac.toString());
ac.setQuiet(true);
ac.setSleep(true);
ac.setClock(12 * 60 + 31);
ac.setOnTimeEnabled(true);
ac.setOnTime(8 * 60 + 59);
ac.setOffTimeEnabled(false);
EXPECT_EQ(
"Power Toggle: Off, Mode: 4 (Fan), Temp: 30C, Fan: 9 (Quiet), "
"Turbo: Off, Quiet: On, Swing(V): On, Sleep: On",
"Turbo: Off, Quiet: On, Swing(V): On, Sleep: On, "
"Clock: 12:31, On Timer: 08:30, Off Timer: Off",
ac.toString());
}

0 comments on commit 4c45e74

Please sign in to comment.