Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 12 additions & 2 deletions examples/IRMQTTServer/IRMQTTServer.ino
Original file line number Diff line number Diff line change
Expand Up @@ -694,8 +694,9 @@ void handleRoot(void) {
"Type: "
"<select name='type'>"
"<option value='27'>Argo</option>"
"<option value='16'>Daikin</option>"
"<option value='53'>Daikin2</option>"
"<option value='16'>Daikin (35 bytes)</option>"
"<option value='65'>Daikin160 (20 bytes)</option>"
"<option value='53'>Daikin2 (39 bytes)</option>"
"<option value='61'>Daikin216 (27 bytes)</option>"
"<option value='48'>Electra</option>"
"<option value='33'>Fujitsu</option>"
Expand Down Expand Up @@ -1419,6 +1420,9 @@ bool parseStringAndSendAirCon(IRsend *irsend, const uint16_t irType,
// Lastly, it should never exceed the "normal" size.
stateSize = std::min(stateSize, kDaikinStateLength);
break;
case DAIKIN160:
stateSize = kDaikin160StateLength;
break;
case DAIKIN2:
stateSize = kDaikin2StateLength;
break;
Expand Down Expand Up @@ -1567,6 +1571,11 @@ bool parseStringAndSendAirCon(IRsend *irsend, const uint16_t irType,
irsend->sendDaikin(reinterpret_cast<uint8_t *>(state));
break;
#endif
#if SEND_DAIKIN160
case DAIKIN160: // 65
irsend->sendDaikin160(reinterpret_cast<uint8_t *>(state));
break;
#endif // SEND_DAIKIN160
#if SEND_DAIKIN2
case DAIKIN2:
irsend->sendDaikin2(reinterpret_cast<uint8_t *>(state));
Expand Down Expand Up @@ -2871,6 +2880,7 @@ bool sendIRCode(IRsend *irsend, int const ir_type,
break;
#endif
case DAIKIN: // 16
case DAIKIN160: // 65
case DAIKIN2: // 53
case DAIKIN216: // 61
case KELVINATOR: // 18
Expand Down
4 changes: 4 additions & 0 deletions src/IRrecv.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -543,6 +543,10 @@ bool IRrecv::decode(decode_results *results, irparams_t *save) {
DPRINTLN("Attempting Trotec decode");
if (decodeTrotec(results)) return true;
#endif // DECODE_TROTEC
#if DECODE_DAIKIN160
DPRINTLN("Attempting Daikin160 decode");
if (decodeDaikin160(results)) return true;
#endif // DECODE_DAIKIN160
#if DECODE_HASH
// decodeHash returns a hash on any input.
// Thus, it needs to be last in the list.
Expand Down
5 changes: 5 additions & 0 deletions src/IRrecv.h
Original file line number Diff line number Diff line change
Expand Up @@ -285,6 +285,11 @@ class IRrecv {
bool decodeDaikin(decode_results *results, const uint16_t nbits = kDaikinBits,
const bool strict = true);
#endif
#if DECODE_DAIKIN160
bool decodeDaikin160(decode_results *results,
const uint16_t nbits = kDaikin160Bits,
const bool strict = true);
#endif // DECODE_DAIKIN160
#if DECODE_DAIKIN2
bool decodeDaikin2(decode_results *results, uint16_t nbits = kDaikin2Bits,
bool strict = true);
Expand Down
11 changes: 9 additions & 2 deletions src/IRremoteESP8266.h
Original file line number Diff line number Diff line change
Expand Up @@ -234,14 +234,17 @@
#define DECODE_DAIKIN216 true
#define SEND_DAIKIN216 true

#define DECODE_DAIKIN160 true
#define SEND_DAIKIN160 true

#if (DECODE_ARGO || DECODE_DAIKIN || DECODE_FUJITSU_AC || DECODE_GREE || \
DECODE_KELVINATOR || DECODE_MITSUBISHI_AC || DECODE_TOSHIBA_AC || \
DECODE_TROTEC || DECODE_HAIER_AC || DECODE_HITACHI_AC || \
DECODE_HITACHI_AC1 || DECODE_HITACHI_AC2 || DECODE_HAIER_AC_YRW02 || \
DECODE_WHIRLPOOL_AC || DECODE_SAMSUNG_AC || DECODE_ELECTRA_AC || \
DECODE_PANASONIC_AC || DECODE_MWM || DECODE_DAIKIN2 || \
DECODE_VESTEL_AC || DECODE_TCL112AC || DECODE_MITSUBISHIHEAVY || \
DECODE_DAIKIN216 || DECODE_SHARP_AC)
DECODE_DAIKIN216 || DECODE_SHARP_AC || DECODE_DAIKIN160)
#define DECODE_AC true // We need some common infrastructure for decoding A/Cs.
#else
#define DECODE_AC false // We don't need that infrastructure.
Expand Down Expand Up @@ -325,8 +328,9 @@ enum decode_type_t {
SHARP_AC,
GOODWEATHER,
INAX,
DAIKIN160, // 65
// Add new entries before this one, and update it to point to the last entry.
kLastDecodeType = INAX,
kLastDecodeType = DAIKIN160,
};

// Message lengths & required repeat values
Expand All @@ -350,6 +354,9 @@ const uint16_t kDaikinDefaultRepeat = kNoRepeat;
const uint16_t kDaikin2StateLength = 39;
const uint16_t kDaikin2Bits = kDaikin2StateLength * 8;
const uint16_t kDaikin2DefaultRepeat = kNoRepeat;
const uint16_t kDaikin160StateLength = 20;
const uint16_t kDaikin160Bits = kDaikin160StateLength * 8;
const uint16_t kDaikin160DefaultRepeat = kNoRepeat;
const uint16_t kDaikin216StateLength = 27;
const uint16_t kDaikin216Bits = kDaikin216StateLength * 8;
const uint16_t kDaikin216DefaultRepeat = kNoRepeat;
Expand Down
5 changes: 5 additions & 0 deletions src/IRsend.h
Original file line number Diff line number Diff line change
Expand Up @@ -298,6 +298,11 @@ class IRsend {
const uint16_t nbytes = kDaikinStateLength,
const uint16_t repeat = kDaikinDefaultRepeat);
#endif
#if SEND_DAIKIN160
void sendDaikin160(const unsigned char data[],
const uint16_t nbytes = kDaikin160StateLength,
const uint16_t repeat = kDaikin160DefaultRepeat);
#endif // SEND_DAIKIN160
#if SEND_DAIKIN2
void sendDaikin2(unsigned char data[], uint16_t nbytes = kDaikin2StateLength,
uint16_t repeat = kDaikin2DefaultRepeat);
Expand Down
6 changes: 6 additions & 0 deletions src/IRutils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,8 @@ decode_type_t strToDecodeType(const char * const str) {
return decode_type_t::COOLIX;
else if (!strcmp(str, "DAIKIN"))
return decode_type_t::DAIKIN;
else if (!strcmp(str, "DAIKIN160"))
return decode_type_t::DAIKIN160;
else if (!strcmp(str, "DAIKIN2"))
return decode_type_t::DAIKIN2;
else if (!strcmp(str, "DAIKIN216"))
Expand Down Expand Up @@ -329,6 +331,9 @@ std::string typeToString(const decode_type_t protocol, const bool isRepeat) {
case DAIKIN:
result = F("DAIKIN");
break;
case DAIKIN160:
result = F("DAIKIN160");
break;
case DAIKIN2:
result = F("DAIKIN2");
break;
Expand Down Expand Up @@ -520,6 +525,7 @@ bool hasACState(const decode_type_t protocol) {
switch (protocol) {
case ARGO:
case DAIKIN:
case DAIKIN160:
case DAIKIN2:
case DAIKIN216:
case ELECTRA_AC:
Expand Down
190 changes: 190 additions & 0 deletions src/ir_Daikin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1885,3 +1885,193 @@ bool IRrecv::decodeDaikin216(decode_results *results, const uint16_t nbits,
return true;
}
#endif // DECODE_DAIKIN216

#if SEND_DAIKIN160
// Send a Daikin 160 bit A/C message.
//
// Args:
// data: An array of kDaikin160StateLength bytes containing the IR command.
//
// Status: Alpha/Untested on a real device.
//
// Supported devices:
// - Daikin ARC423A5 remote.
//
// Ref:
// https://github.com/markszabo/IRremoteESP8266/issues/731
void IRsend::sendDaikin160(const unsigned char data[], const uint16_t nbytes,
const uint16_t repeat) {
if (nbytes < kDaikin160Section1Length)
return; // Not enough bytes to send a partial message.

for (uint16_t r = 0; r <= repeat; r++) {
// Section #1
sendGeneric(kDaikin160HdrMark, kDaikin160HdrSpace, kDaikin160BitMark,
kDaikin160OneSpace, kDaikin160BitMark, kDaikin160ZeroSpace,
kDaikin160BitMark, kDaikin160Gap, data,
kDaikin160Section1Length,
kDaikin160Freq, false, 0, kDutyDefault);
// Section #2
sendGeneric(kDaikin160HdrMark, kDaikin160HdrSpace, kDaikin160BitMark,
kDaikin160OneSpace, kDaikin160BitMark, kDaikin160ZeroSpace,
kDaikin160BitMark, kDaikin160Gap,
data + kDaikin160Section1Length,
nbytes - kDaikin160Section1Length,
kDaikin160Freq, false, 0, kDutyDefault);
}
}
#endif // SEND_DAIKIN160

// Class for handling Daikin 160 bit / 20 byte A/C messages.
//
// Code by crankyoldgit.
//
// Supported Remotes: Daikin ARC423A5 remote
//
// Ref:
// https://github.com/markszabo/IRremoteESP8266/issues/731
IRDaikin160::IRDaikin160(uint16_t pin) : _irsend(pin) { stateReset(); }

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

#if SEND_DAIKIN160
void IRDaikin160::send(const uint16_t repeat) {
checksum();
_irsend.sendDaikin160(remote_state, kDaikin160StateLength, repeat);
}
#endif // SEND_DAIKIN160

// 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 IRDaikin160::validChecksum(uint8_t state[], const uint16_t length) {
// Validate the checksum of section #1.
if (length <= kDaikin160Section1Length - 1 ||
state[kDaikin160Section1Length - 1] != sumBytes(
state, kDaikin160Section1Length - 1))
return false;
// Validate the checksum of section #2 (a.k.a. the rest)
if (length <= kDaikin160Section1Length + 1 ||
state[length - 1] != sumBytes(state + kDaikin160Section1Length,
length - kDaikin160Section1Length - 1))
return false;
return true;
}

// Calculate and set the checksum values for the internal state.
void IRDaikin160::checksum() {
remote_state[kDaikin160Section1Length - 1] = sumBytes(
remote_state, kDaikin160Section1Length - 1);
remote_state[kDaikin160StateLength - 1] = sumBytes(
remote_state + kDaikin160Section1Length, kDaikin160Section2Length - 1);
}

void IRDaikin160::stateReset() {
for (uint8_t i = 0; i < kDaikin160StateLength; i++) remote_state[i] = 0x00;
remote_state[0] = 0x11;
remote_state[1] = 0xDA;
remote_state[2] = 0x27;
remote_state[3] = 0xF0;
// remote_state[6] is a checksum byte, it will be set by checksum().
remote_state[7] = 0x11;
remote_state[8] = 0xDA;
remote_state[9] = 0x27;
// remote_state[19] is a checksum byte, it will be set by checksum().
}

uint8_t *IRDaikin160::getRaw() {
checksum(); // Ensure correct settings before sending.
return remote_state;
}

void IRDaikin160::setRaw(const uint8_t new_code[]) {
for (uint8_t i = 0; i < kDaikin160StateLength; i++)
remote_state[i] = new_code[i];
}

#if DECODE_DAIKIN160
// Decode the supplied Daikin 160 bit A/C message.
// Args:
// results: Ptr to the data to decode and where to store the decode result.
// nbits: Nr. of bits to expect in the data portion. (kDaikin160Bits)
// strict: Flag to indicate if we strictly adhere to the specification.
// Returns:
// boolean: True if it can decode it, false if it can't.
//
// Supported devices:
// - Daikin ARC423A5 remote.
//
// Status: BETA / Probably works.
//
// Ref:
// https://github.com/markszabo/IRremoteESP8266/issues/731
bool IRrecv::decodeDaikin160(decode_results *results, const uint16_t nbits,
const bool strict) {
if (results->rawlen < 2 * (nbits + kHeader + kFooter) - 1)
return false;

// Compliance
if (strict && nbits != kDaikin160Bits) return false;

uint16_t offset = kStartOffset;
uint16_t dataBitsSoFar = 0;
uint16_t i = 0;
match_result_t data_result;
uint8_t sectionSize[kDaikin160Sections] = {kDaikin160Section1Length,
kDaikin160Section2Length};

// Sections
// Keep reading bytes until we either run out of section or state to fill.
for (uint8_t section = 0, pos = 0; section < kDaikin160Sections;
section++) {
pos += sectionSize[section];

// Section Header
if (!matchMark(results->rawbuf[offset++], kDaikin160HdrMark)) return false;
if (!matchSpace(results->rawbuf[offset++], kDaikin160HdrSpace))
return false;

// Section Data
for (; offset <= results->rawlen - 16 && i < pos;
i++, dataBitsSoFar += 8, offset += data_result.used) {
// Read in a byte at a time.
data_result =
matchData(&(results->rawbuf[offset]), 8, kDaikin160BitMark,
kDaikin160OneSpace, kDaikin160BitMark,
kDaikin160ZeroSpace, kDaikinTolerance, kDaikinMarkExcess,
false);
if (data_result.success == false) break; // Fail
results->state[i] = (uint8_t)data_result.data;
}

// Section Footer
if (!matchMark(results->rawbuf[offset++], kDaikin160BitMark,
kDaikinTolerance, kDaikinMarkExcess)) return false;
if (section < kDaikin160Sections - 1) { // Inter-section gaps.
if (!matchSpace(results->rawbuf[offset++], kDaikin160Gap)) return false;
} else { // Last section / End of message gap.
if (offset <= results->rawlen &&
!matchAtLeast(results->rawbuf[offset++], kDaikin160Gap)) return false;
}
}

// Compliance
if (strict) {
// Re-check we got the correct size/length due to the way we read the data.
if (dataBitsSoFar != kDaikin160Bits) return false;
// Validate the checksum.
if (!IRDaikin160::validChecksum(results->state)) return false;
}

// Success
results->decode_type = decode_type_t::DAIKIN160;
results->bits = dataBitsSoFar;
// 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_DAIKIN160
41 changes: 40 additions & 1 deletion src/ir_Daikin.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
// Brand: Daikin, Model: FTXZ35NV1B A/C
// Brand: Daikin, Model: FTXZ50NV1B A/C
// Brand: Daikin, Model: ARC433B69 remote
// Brand: Daikin, Model: ARC423A5 remote

#ifndef IR_DAIKIN_H_
#define IR_DAIKIN_H_
Expand Down Expand Up @@ -216,7 +217,18 @@ const uint8_t kDaikin216ByteSwingH = 17;
const uint8_t kDaikin216MaskSwingH = kDaikin216MaskSwingV;
const uint8_t kDaikin216BytePowerful = 21;


// Another variant of the protocol for the Daikin ARC423A5 remote.
const uint16_t kDaikin160Freq = 38000; // Modulation Frequency in Hz.
const uint16_t kDaikin160HdrMark = 5000;
const uint16_t kDaikin160HdrSpace = 2145;
const uint16_t kDaikin160BitMark = 342;
const uint16_t kDaikin160OneSpace = 1786;
const uint16_t kDaikin160ZeroSpace = 700;
const uint16_t kDaikin160Gap = 29650;
const uint16_t kDaikin160Sections = 2;
const uint16_t kDaikin160Section1Length = 7;
const uint16_t kDaikin160Section2Length = kDaikin160StateLength -
kDaikin160Section1Length;
// Legacy defines.
#define DAIKIN_COOL kDaikinCool
#define DAIKIN_HEAT kDaikinHeat
Expand Down Expand Up @@ -462,4 +474,31 @@ class IRDaikin216 {
void checksum();
};

// Class to emulate a Daikin ARC423A5 remote.
class IRDaikin160 {
public:
explicit IRDaikin160(uint16_t pin);

#if SEND_DAIKIN160
void send(const uint16_t repeat = kDaikin160DefaultRepeat);
uint8_t calibrate(void) { return _irsend.calibrate(); }
#endif
void begin();
uint8_t* getRaw();
void setRaw(const uint8_t new_code[]);
static bool validChecksum(uint8_t state[],
const uint16_t length = kDaikin160StateLength);
#ifndef UNIT_TEST

private:
IRsend _irsend;
#else
IRsendTest _irsend;
#endif
// # of bytes per command
uint8_t remote_state[kDaikin160StateLength];
void stateReset();
void checksum();
};

#endif // IR_DAIKIN_H_
Loading