Skip to content

Commit

Permalink
Progress Check-in.
Browse files Browse the repository at this point in the history
* Add checksum support.
* Add Power Toggle, Mode, & Temp control.
* Partial common AC api support.
* Unit tests for setting controls.

For #1064
  • Loading branch information
crankyoldgit committed Mar 20, 2020
1 parent 83e60b8 commit 9a51749
Show file tree
Hide file tree
Showing 5 changed files with 376 additions and 1 deletion.
30 changes: 30 additions & 0 deletions src/IRac.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,9 @@ bool IRac::isProtocolSupported(const decode_type_t protocol) {
#if SEND_DAIKIN216
case decode_type_t::DAIKIN216:
#endif
#if SEND_DAIKIN64
case decode_type_t::DAIKIN64:
#endif
#if SEND_ELECTRA_AC
case decode_type_t::ELECTRA_AC:
#endif
Expand Down Expand Up @@ -461,6 +464,18 @@ void IRac::daikin216(IRDaikin216 *ac,
}
#endif // SEND_DAIKIN216

#if SEND_DAIKIN64
void IRac::daikin64(IRDaikin64 *ac,
const bool on, const stdAc::opmode_t mode,
const float degrees) {
ac->begin();
ac->setPowerToggle(on);
ac->setMode(ac->convertMode(mode));
ac->setTemp(degrees);
ac->send();
}
#endif // SEND_DAIKIN128

#if SEND_ELECTRA_AC
void IRac::electra(IRElectraAc *ac,
const bool on, const stdAc::opmode_t mode,
Expand Down Expand Up @@ -1390,6 +1405,14 @@ bool IRac::sendAc(const stdAc::state_t desired, const stdAc::state_t *prev) {
break;
}
#endif // SEND_DAIKIN216
#if SEND_DAIKIN64
case DAIKIN64:
{
IRDaikin64 ac(_pin, _inverted, _modulation);
daikin64(&ac, send.power, send.mode, degC);
break;
}
#endif // SEND_DAIKIN64
#if SEND_ELECTRA_AC
case ELECTRA_AC:
{
Expand Down Expand Up @@ -2008,6 +2031,13 @@ namespace IRAcUtils {
return ac.toString();
}
#endif // DECODE_DAIKIN216
#if DECODE_DAIKIN64
case decode_type_t::DAIKIN64: {
IRDaikin64 ac(kGpioUnused);
ac.setRaw(result->value); // Daikin64 uses value instead of state.
return ac.toString();
}
#endif // DECODE_DAIKIN216
#if DECODE_ELECTRA_AC
case decode_type_t::ELECTRA_AC: {
IRElectraAc ac(0);
Expand Down
5 changes: 5 additions & 0 deletions src/IRac.h
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,11 @@ void daikin216(IRDaikin216 *ac,
const stdAc::swingv_t swingv, const stdAc::swingh_t swingh,
const bool quiet, const bool turbo);
#endif // SEND_DAIKIN216
#if SEND_DAIKIN64
void daikin64(IRDaikin64 *ac,
const bool on, const stdAc::opmode_t mode,
const float degrees);
#endif // SEND_DAIKIN64
#if SEND_ELECTRA_AC
void electra(IRElectraAc *ac,
const bool on, const stdAc::opmode_t mode,
Expand Down
164 changes: 164 additions & 0 deletions src/ir_Daikin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3268,6 +3268,8 @@ bool IRrecv::decodeDaikin64(decode_results *results, uint16_t offset,
if (!matchMark(results->rawbuf[offset++], kDaikin64HdrMark))
return false;

// Compliance
if (strict && !IRDaikin64::validChecksum(results->value)) return false;
// Success
results->decode_type = decode_type_t::DAIKIN64;
results->bits = nbits;
Expand All @@ -3276,3 +3278,165 @@ bool IRrecv::decodeDaikin64(decode_results *results, uint16_t offset,
return true;
}
#endif // DAIKIN64

// Class for handling Daikin 64 bit / 19 byte A/C messages.
//
// Code by crankyoldgit.
//
// Supported Remotes: Daikin ARC480A5 remote
//
// Ref:
// https://github.com/crankyoldgit/IRremoteESP8266/issues/873
// https://github.com/ToniA/arduino-heatpumpir/blob/master/DaikinHeatpumpARC480A14IR.cpp
// https://github.com/ToniA/arduino-heatpumpir/blob/master/DaikinHeatpumpARC480A14IR.h
IRDaikin64::IRDaikin64(const uint16_t pin, const bool inverted,
const bool use_modulation)
: _irsend(pin, inverted, use_modulation) { stateReset(); }

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

#if SEND_DAIKIN64
void IRDaikin64::send(const uint16_t repeat) {
_irsend.sendDaikin64(getRaw(), kDaikin64Bits, repeat);
}
#endif // SEND_DAIKIN64

// Calc the checksum for a given state.
// Args:
// state: The value to calc the checksum of.
// Returns:
// A 4-bit checksum stored in a uint_8.
uint8_t IRDaikin64::calcChecksum(const uint64_t state) {
uint64_t data = GETBITS64(state, 0, kDaikin64ChecksumOffset);
uint8_t result = 0;
for (; data; data >>= 4) // Add each nibble together.
result += GETBITS64(data, 0, 4);
return result & 0xF;
}

// Verify the checksum is valid for a given state.
// Args:
// state: The array to verify the checksum of.
// length: The size of the state.
// Returns:
// A boolean.
bool IRDaikin64::validChecksum(const uint64_t state) {
// Validate the checksum of the given state.
return (GETBITS64(state, kDaikin64ChecksumOffset,
kDaikin64ChecksumSize) == calcChecksum(state));
}

// Calculate and set the checksum values for the internal state.
void IRDaikin64::checksum(void) {
setBits(&remote_state, kDaikin64ChecksumOffset, kDaikin64ChecksumSize,
calcChecksum(remote_state));
}

void IRDaikin64::stateReset(void) {
remote_state = kDaikin64KnownGoodState;
}

uint64_t IRDaikin64::getRaw(void) {
checksum(); // Ensure correct settings before sending.
return remote_state;
}

void IRDaikin64::setRaw(const uint64_t new_state) { remote_state = new_state; }

void IRDaikin64::setPowerToggle(const bool on) {
setBit(&remote_state, kDaikin64PowerToggleBit, on);
}

bool IRDaikin64::getPowerToggle(void) {
return GETBIT64(remote_state, kDaikin64PowerToggleBit);
}

// Set the temp in deg C
void IRDaikin64::setTemp(const uint8_t temp) {
uint8_t degrees = std::max(temp, kDaikin64MinTemp);
degrees = std::min(degrees, kDaikin64MaxTemp);
setBits(&remote_state, kDaikin64TempOffset,
kDaikin64TempSize, uint8ToBcd(degrees));
}

uint8_t IRDaikin64::getTemp(void) {
return bcdToUint8(GETBITS64(remote_state, kDaikin64TempOffset,
kDaikin64TempSize));
}

uint8_t IRDaikin64::getMode(void) {
return GETBITS64(remote_state, kDaikin64ModeOffset, kDaikin64ModeSize);
}

void IRDaikin64::setMode(const uint8_t mode) {
switch (mode) {
case kDaikin64Fan:
case kDaikin64Dry:
case kDaikin64Cool:
break;
default:
this->setMode(kDaikin64Cool);
return;
}
setBits(&remote_state, kDaikin64ModeOffset, kDaikin64ModeSize, mode);
}

// Convert a standard A/C mode into its native mode.
uint8_t IRDaikin64::convertMode(const stdAc::opmode_t mode) {
switch (mode) {
case stdAc::opmode_t::kDry: return kDaikin64Dry;
case stdAc::opmode_t::kFan: return kDaikin64Fan;
default: return kDaikinCool;
}
}

// Convert a native mode to it's common equivalent.
stdAc::opmode_t IRDaikin64::toCommonMode(const uint8_t mode) {
switch (mode) {
case kDaikin64Cool: return stdAc::opmode_t::kCool;
case kDaikin64Dry: return stdAc::opmode_t::kDry;
case kDaikin64Fan: return stdAc::opmode_t::kFan;
default: return stdAc::opmode_t::kAuto;
}
}

// Convert the internal state into a human readable string.
String IRDaikin64::toString(void) {
String result = "";
result.reserve(80); // Reserve some heap for the string to reduce fragging.
result += addBoolToString(getPowerToggle(), kPowerToggleStr, false);
result += addModeToString(getMode(), 0xFF, kDaikin64Cool,
0xFF, kDaikin64Dry, kDaikin64Fan);
result += addTempToString(getTemp());
// result += addFanToString(getFan(), kDaikin128FanHigh, kDaikin128FanLow,
// kDaikin128FanAuto, kDaikin128FanQuiet,
// kDaikin128FanMed);
return result;
}

// Convert the A/C state to it's common equivalent.
stdAc::state_t IRDaikin64::toCommon(const stdAc::state_t *prev) {
stdAc::state_t result;
if (prev != NULL) result = *prev;
result.protocol = decode_type_t::DAIKIN64;
result.model = -1; // No models used.
result.power ^= getPowerToggle();
result.mode = toCommonMode(getMode());
result.celsius = true;
result.degrees = getTemp();
// result.fanspeed = toCommonFanSpeed(getFan());
// result.swingv = getSwingVertical() ? stdAc::swingv_t::kAuto
// : stdAc::swingv_t::kOff;
// Not supported.
result.swingh = stdAc::swingh_t::kOff;
result.clean = false;
result.filter = false;
result.beep = false;
result.quiet = false;
result.turbo = false;
result.econo = false;
result.light = false;
result.sleep = -1;
result.clock = -1;
return result;
}
66 changes: 66 additions & 0 deletions src/ir_Daikin.h
Original file line number Diff line number Diff line change
Expand Up @@ -443,6 +443,19 @@ const uint16_t kDaikin64LdrSpace = kDaikin128LeaderSpace;
const uint16_t kDaikin64Freq = kDaikin128Freq; // Hz.
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 kDaikin64TempOffset = 48;
const uint8_t kDaikin64TempSize = 8; // Mask 0b11111111 << 47
const uint8_t kDaikin64MinTemp = 16; // Celsius
const uint8_t kDaikin64MaxTemp = 30; // Celsius
const uint8_t kDaikin64PowerToggleBit = 59;
const uint8_t kDaikin64ChecksumOffset = 60;
const uint8_t kDaikin64ChecksumSize = 4; // Mask 0b1111 << 59

// Legacy defines.
#define DAIKIN_COOL kDaikinCool
Expand Down Expand Up @@ -894,4 +907,57 @@ class IRDaikin152 {
void stateReset();
void checksum();
};

// Class to emulate a Daikin DGS01 remote.
class IRDaikin64 {
public:
explicit IRDaikin64(const uint16_t pin, const bool inverted = false,
const bool use_modulation = true);

#if SEND_DAIKIN64
void send(const uint16_t repeat = kDaikin64DefaultRepeat);
uint8_t calibrate(void) { return _irsend.calibrate(); }
#endif // SEND_DAIKIN64
void begin();
uint64_t getRaw();
void setRaw(const uint64_t new_state);
static uint8_t calcChecksum(const uint64_t state);
static bool validChecksum(const uint64_t state);
void setPowerToggle(const bool on);
bool getPowerToggle(void);
void setTemp(const uint8_t temp);
uint8_t getTemp();
void setFan(const uint8_t fan);
uint8_t getFan(void);
void setMode(const uint8_t mode);
uint8_t getMode(void);
void setSwingV(const bool on);
bool getSwingV(void);
bool getQuiet(void);
void setQuiet(const bool on);
bool getPowerful(void);
void setPowerful(const bool on);
void setSensor(const bool on);
bool getSensor(void);
void setEcono(const bool on);
bool getEcono(void);
void setComfort(const bool on);
bool getComfort(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);
static stdAc::fanspeed_t toCommonFanSpeed(const uint8_t speed);
stdAc::state_t toCommon(const stdAc::state_t *prev = NULL);
String toString(void);
#ifndef UNIT_TEST

private:
IRsend _irsend;
#else
IRsendTest _irsend;
#endif
uint64_t remote_state;
void stateReset();
void checksum();
};
#endif // IR_DAIKIN_H_
Loading

0 comments on commit 9a51749

Please sign in to comment.