Skip to content

Commit

Permalink
Basic support for Daikin FFN-C A/C
Browse files Browse the repository at this point in the history
* `sendDaikin64()` & `decodeDaikin64()`
* Unit test coverage for both routines.
* Update support routines.

For #1064
  • Loading branch information
crankyoldgit committed Mar 25, 2020
1 parent a31c6bd commit c4942cc
Show file tree
Hide file tree
Showing 10 changed files with 188 additions and 3 deletions.
5 changes: 3 additions & 2 deletions SupportedProtocols.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<!--- WARNING: Do NOT edit this file directly.
It is generated by './tools/scrape_supported_devices.py'.
Last generated: Tue Mar 17 13:45:51 2020 --->
Last generated: Wed Mar 25 11:38:37 2020 --->
# IR Protocols supported by this library

| Protocol | Brand | Model | A/C Model | Detailed A/C Support |
Expand All @@ -12,7 +12,7 @@
| [Coolix](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Coolix.cpp) | **[Beko](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Coolix.h)** | BINR 070/071 split-type A/C<BR>BINR 070/071 split-type A/C<BR>RG57K7(B)/BGEF Remote<BR>RG57K7(B)/BGEF Remote | | Yes |
| [Coolix](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Coolix.cpp) | **[Midea](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Coolix.h)** | MS12FU-10HRDN1-QRD0GW(B) A/C<BR>MS12FU-10HRDN1-QRD0GW(B) A/C<BR>MSABAU-07HRFN1-QRD0GW A/C (circa 2016)<BR>MSABAU-07HRFN1-QRD0GW A/C (circa 2016)<BR>RG52D/BGE Remote<BR>RG52D/BGE Remote | | Yes |
| [Coolix](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Coolix.cpp) | **[Tokio](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Coolix.h)** | AATOEMF17-12CHR1SW split-type RG51\|50/BGE Remote<BR>AATOEMF17-12CHR1SW split-type RG51\|50/BGE Remote | | Yes |
| [Daikin](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Daikin.cpp) | **[Daikin](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Daikin.h)** | 17 Series A/C (DAIKIN128)<BR>ARC423A5 remote<BR>ARC433** remote<BR>ARC433B69 remote<BR>ARC477A1 remote<BR>ARC480A5 remote (DAIKIN152)<BR>BRC4C153 remote<BR>BRC52B63 remote (DAIKIN128)<BR>FTE12HV2S A/C<BR>FTXB09AXVJU A/C (DAIKIN128)<BR>FTXB12AXVJU A/C (DAIKIN128)<BR>FTXZ25NV1B A/C<BR>FTXZ35NV1B A/C<BR>FTXZ50NV1B A/C | | Yes |
| [Daikin](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Daikin.cpp) | **[Daikin](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Daikin.h)** | 17 Series A/C (DAIKIN128)<BR>ARC423A5 remote<BR>ARC433** remote<BR>ARC433B69 remote<BR>ARC477A1 remote<BR>ARC480A5 remote (DAIKIN152)<BR>BRC4C153 remote<BR>BRC52B63 remote (DAIKIN128)<BR>DGS01 remote (DAIKIN64)<BR>FFN-C/FCN-F Series A/C (DAIKIN64)<BR>FTE12HV2S A/C<BR>FTXB09AXVJU A/C (DAIKIN128)<BR>FTXB12AXVJU A/C (DAIKIN128)<BR>FTXZ25NV1B A/C<BR>FTXZ35NV1B A/C<BR>FTXZ50NV1B A/C | | Yes |
| [Denon](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Denon.cpp) | **Unknown** | | | - |
| [Dish](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Dish.cpp) | **DISH NETWORK** | echostar 301 | | - |
| [Electra](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Electra.cpp) | **[AUX](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Electra.h)** | KFR-35GW/BpNFW=3 A/C<BR>YKR-T/011 remote | | Yes |
Expand Down Expand Up @@ -94,6 +94,7 @@
- DAIKIN176
- DAIKIN2
- DAIKIN216
- DAIKIN64
- DENON
- DISH
- ELECTRA_AC
Expand Down
4 changes: 4 additions & 0 deletions src/IRrecv.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -781,6 +781,10 @@ bool IRrecv::decode(decode_results *results, irparams_t *save,
DPRINTLN("Attempting Symphony decode");
if (decodeSymphony(results, offset)) return true;
#endif // DECODE_SYMPHONY
#if DECODE_DAIKIN64
DPRINTLN("Attempting Daikin64 decode");
if (decodeDaikin64(results, offset)) return true;
#endif // DECODE_DAIKIN64
// Typically new protocols are added above this line.
}
#if DECODE_HASH
Expand Down
5 changes: 5 additions & 0 deletions src/IRrecv.h
Original file line number Diff line number Diff line change
Expand Up @@ -393,6 +393,11 @@ class IRrecv {
const uint16_t nbits = kDaikinBits,
const bool strict = true);
#endif
#if DECODE_DAIKIN64
bool decodeDaikin64(decode_results *results, uint16_t offset = kStartOffset,
const uint16_t nbits = kDaikin64Bits,
const bool strict = true);
#endif // DECODE_DAIKIN64
#if DECODE_DAIKIN128
bool decodeDaikin128(decode_results *results, uint16_t offset = kStartOffset,
const uint16_t nbits = kDaikin128Bits,
Expand Down
12 changes: 11 additions & 1 deletion src/IRremoteESP8266.h
Original file line number Diff line number Diff line change
Expand Up @@ -586,6 +586,13 @@
#define SEND_SYMPHONY _IR_ENABLE_DEFAULT_
#endif // SEND_SYMPHONY

#ifndef DECODE_DAIKIN64
#define DECODE_DAIKIN64 _IR_ENABLE_DEFAULT_
#endif // DECODE_DAIKIN64
#ifndef SEND_DAIKIN64
#define SEND_DAIKIN64 _IR_ENABLE_DEFAULT_
#endif // SEND_DAIKIN64

#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 || \
Expand Down Expand Up @@ -711,8 +718,9 @@ enum decode_type_t {
EPSON, // 75
SYMPHONY,
HITACHI_AC3,
DAIKIN64,
// Add new entries before this one, and update it to point to the last entry.
kLastDecodeType = HITACHI_AC3,
kLastDecodeType = DAIKIN64,
};

// Message lengths & required repeat values
Expand Down Expand Up @@ -740,6 +748,8 @@ const uint16_t kDaikinDefaultRepeat = kNoRepeat;
const uint16_t kDaikin2StateLength = 39;
const uint16_t kDaikin2Bits = kDaikin2StateLength * 8;
const uint16_t kDaikin2DefaultRepeat = kNoRepeat;
const uint16_t kDaikin64Bits = 64;
const uint16_t kDaikin64DefaultRepeat = kNoRepeat;
const uint16_t kDaikin160StateLength = 20;
const uint16_t kDaikin160Bits = kDaikin160StateLength * 8;
const uint16_t kDaikin160DefaultRepeat = kNoRepeat;
Expand Down
7 changes: 7 additions & 0 deletions src/IRsend.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -604,6 +604,8 @@ uint16_t IRsend::defaultBits(const decode_type_t protocol) {
return kDaikin2Bits;
case DAIKIN216:
return kDaikin216Bits;
case DAIKIN64:
return kDaikin64Bits;
case ELECTRA_AC:
return kElectraAcBits;
case GREE:
Expand Down Expand Up @@ -686,6 +688,11 @@ bool IRsend::send(const decode_type_t type, const uint64_t data,
sendCOOLIX(data, nbits, min_repeat);
break;
#endif
#if SEND_DAIKIN64
case DAIKIN64:
sendDaikin64(data, nbits, min_repeat);
break;
#endif
#if SEND_DENON
case DENON:
sendDenon(data, nbits, min_repeat);
Expand Down
4 changes: 4 additions & 0 deletions src/IRsend.h
Original file line number Diff line number Diff line change
Expand Up @@ -362,6 +362,10 @@ class IRsend {
const uint16_t nbytes = kDaikinStateLength,
const uint16_t repeat = kDaikinDefaultRepeat);
#endif
#if SEND_DAIKIN64
void sendDaikin64(const uint64_t data, const uint16_t nbits = kDaikin64Bits,
const uint16_t repeat = kDaikin64DefaultRepeat);
#endif // SEND_DAIKIN64
#if SEND_DAIKIN128
void sendDaikin128(const unsigned char data[],
const uint16_t nbytes = kDaikin128StateLength,
Expand Down
5 changes: 5 additions & 0 deletions src/IRutils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,8 @@ decode_type_t strToDecodeType(const char * const str) {
return decode_type_t::DAIKIN2;
else if (!strcasecmp(str, "DAIKIN216"))
return decode_type_t::DAIKIN216;
else if (!strcasecmp(str, "DAIKIN64"))
return decode_type_t::DAIKIN64;
else if (!strcasecmp(str, "DENON"))
return decode_type_t::DENON;
else if (!strcasecmp(str, "DISH"))
Expand Down Expand Up @@ -307,6 +309,9 @@ String typeToString(const decode_type_t protocol, const bool isRepeat) {
case DAIKIN216:
result = F("DAIKIN216");
break;
case DAIKIN64:
result = F("DAIKIN64");
break;
case DENON:
result = F("DENON");
break;
Expand Down
89 changes: 89 additions & 0 deletions src/ir_Daikin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3187,3 +3187,92 @@ String IRDaikin152::toString(void) {
result += addBoolToString(getComfort(), kComfortStr);
return result;
}

#if SEND_DAIKIN64
// Send a Daikin 64 bit A/C message.
//
// Args:
// data: A uint64_t containing the IR command/code.
//
// Supported devices:
// - Daikin FFN-C.
//
// Status: Beta / Probably Working.
//
// Ref: https://github.com/crankyoldgit/IRremoteESP8266/issues/1064
void IRsend::sendDaikin64(const uint64_t data, const uint16_t nbits,
const uint16_t repeat) {
enableIROut(kDaikin64Freq);
for (uint16_t r = 0; r <= repeat; r++) {
for (uint8_t i = 0; i < 2; i++) {
// Leader
mark(kDaikin64LdrMark);
space(kDaikin64LdrSpace);
}
// Header + Data + Footer #1
sendGeneric(kDaikin64HdrMark, kDaikin64HdrSpace,
kDaikin64BitMark, kDaikin64OneSpace,
kDaikin64BitMark, kDaikin64ZeroSpace,
kDaikin64BitMark, kDaikin64Gap,
data, nbits, kDaikin64Freq, false, 0, 50);
// Footer #2
mark(kDaikin64HdrMark);
space(kDefaultMessageGap); // A guess of the gap between messages.
}
}
#endif // SEND_DAIKIN64

#if DECODE_DAIKIN64
// Decode the supplied Daikin 64 bit A/C message.
// Args:
// results: Ptr to the data to decode and where to store the decode result.
// offset: The starting index to use when attempting to decode the raw data.
// Typically/Defaults to kStartOffset.
// nbits: Nr. of bits to expect in the data portion. (kDaikin64Bits)
// 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 FFN-C.
//
// Status: Beta / Probably Working.
//
// Ref: https://github.com/crankyoldgit/IRremoteESP8266/issues/1064
bool IRrecv::decodeDaikin64(decode_results *results, uint16_t offset,
const uint16_t nbits, const bool strict) {
if (results->rawlen < 2 * nbits + kDaikin64Overhead - offset)
return false; // Too short a message to match.
// Compliance
if (strict && nbits != kDaikin64Bits)
return false;

// Leader
for (uint8_t i = 0; i < 2; i++) {
if (!matchMark(results->rawbuf[offset++], kDaikin64LdrMark))
return false;
if (!matchSpace(results->rawbuf[offset++], kDaikin64LdrSpace))
return false;
}
// Header + Data + Footer #1
uint16_t used = matchGeneric(results->rawbuf + offset, &results->value,
results->rawlen - offset, nbits,
kDaikin64HdrMark, kDaikin64HdrSpace,
kDaikin64BitMark, kDaikin64OneSpace,
kDaikin64BitMark, kDaikin64ZeroSpace,
kDaikin64BitMark, kDaikin64Gap,
false, _tolerance, kMarkExcess, false);
if (used == 0) return false;
offset += used;
// Footer #2
if (!matchMark(results->rawbuf[offset++], kDaikin64HdrMark))
return false;

// Success
results->decode_type = decode_type_t::DAIKIN64;
results->bits = nbits;
results->command = 0;
results->address = 0;
return true;
}
#endif // DAIKIN64
13 changes: 13 additions & 0 deletions src/ir_Daikin.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@
// Brand: Daikin, Model: FTXB09AXVJU A/C (DAIKIN128)
// Brand: Daikin, Model: BRC52B63 remote (DAIKIN128)
// Brand: Daikin, Model: ARC480A5 remote (DAIKIN152)
// Brand: Daikin, Model: FFN-C/FCN-F Series A/C (DAIKIN64)
// Brand: Daikin, Model: DGS01 remote (DAIKIN64)

#ifndef IR_DAIKIN_H_
#define IR_DAIKIN_H_
Expand Down Expand Up @@ -430,6 +432,17 @@ const uint8_t kDaikin152ComfortOffset = 1; // Mask 0b00000010
const uint8_t kDaikin152SensorByte = kDaikin152EconoByte; // Mask 0b00001000
const uint8_t kDaikin152SensorOffset = 3; // Mask 0b00001000

const uint16_t kDaikin64HdrMark = kDaikin128HdrMark;
const uint16_t kDaikin64BitMark = kDaikin128BitMark;
const uint16_t kDaikin64HdrSpace = kDaikin128HdrSpace;
const uint16_t kDaikin64OneSpace = kDaikin128OneSpace;
const uint16_t kDaikin64ZeroSpace = kDaikin128ZeroSpace;
const uint16_t kDaikin64LdrMark = kDaikin128LeaderMark;
const uint16_t kDaikin64Gap = kDaikin128Gap;
const uint16_t kDaikin64LdrSpace = kDaikin128LeaderSpace;
const uint16_t kDaikin64Freq = kDaikin128Freq; // Hz.
const uint16_t kDaikin64Overhead = 9;


// Legacy defines.
#define DAIKIN_COOL kDaikinCool
Expand Down
47 changes: 47 additions & 0 deletions test/ir_Daikin_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1545,6 +1545,11 @@ TEST(TestUtils, Housekeeping) {
ASSERT_EQ(decode_type_t::DAIKIN216, strToDecodeType("DAIKIN216"));
ASSERT_TRUE(hasACState(decode_type_t::DAIKIN216));
ASSERT_TRUE(IRac::isProtocolSupported(decode_type_t::DAIKIN216));

ASSERT_EQ("DAIKIN64", typeToString(decode_type_t::DAIKIN64));
ASSERT_EQ(decode_type_t::DAIKIN64, strToDecodeType("DAIKIN64"));
ASSERT_FALSE(hasACState(decode_type_t::DAIKIN64));
ASSERT_FALSE(IRac::isProtocolSupported(decode_type_t::DAIKIN64));
}

// https://github.com/crankyoldgit/IRremoteESP8266/issues/582#issuecomment-453863879
Expand Down Expand Up @@ -3400,3 +3405,45 @@ TEST(TestDaikin2Class, Issue1035) {
ac.toString());
ASSERT_FALSE(ac.toCommon().power);
}

// Ref: https://github.com/crankyoldgit/IRremoteESP8266/issues/1064
// Data from:
// https://docs.google.com/spreadsheets/d/1sxjLQCRLMFM1FQpttBXsye2JG5hHIe2BrKnKDuPV9Bw/edit#gid=726071135&range=A1
TEST(TestDecodeDaikin64, RealExample) {
IRsendTest irsend(kGpioUnused);
IRrecv irrecv(kGpioUnused);
uint16_t rawData[137] = {
9864, 9778, 9810, 9728, 4666, 2482, 384, 342, 390, 922, 386, 928, 388,
348, 388, 920, 386, 348, 390, 342, 384, 330, 414, 342, 390, 922, 386, 348,
390, 342, 382, 352, 384, 352, 382, 924, 386, 356, 382, 344, 384, 350, 388,
346, 390, 342, 386, 348, 386, 928, 388, 348, 388, 354, 388, 888, 412, 928,
390, 896, 416, 348, 386, 348, 388, 346, 388, 342, 384, 358, 388, 340, 384,
926, 384, 932, 386, 346, 388, 922, 384, 350, 384, 348, 384, 358, 382, 338,
394, 922, 388, 928, 386, 350, 384, 926, 390, 344, 388, 342, 386, 356, 388,
338, 382, 928, 382, 932, 390, 344, 386, 924, 390, 344, 388, 314, 414, 356,
384, 344, 386, 346, 390, 926, 386, 924, 388, 924, 388, 926, 386, 924, 388,
350, 388, 20258, 4670};

irsend.begin();
irsend.reset();
irsend.sendRaw(rawData, 137, kDaikin64Freq);
irsend.makeDecodeResult();
ASSERT_TRUE(irrecv.decode(&irsend.capture));
ASSERT_EQ(decode_type_t::DAIKIN64, irsend.capture.decode_type);
ASSERT_EQ(kDaikin64Bits, irsend.capture.bits);
EXPECT_EQ(0x7C16161607204216, irsend.capture.value);
}

TEST(TestDecodeDaikin64, SyntheticExample) {
IRsendTest irsend(kGpioUnused);
IRrecv irrecv(kGpioUnused);

irsend.begin();
irsend.reset();
irsend.sendDaikin64(0x7C16161607204216);
irsend.makeDecodeResult();
ASSERT_TRUE(irrecv.decode(&irsend.capture));
ASSERT_EQ(decode_type_t::DAIKIN64, irsend.capture.decode_type);
ASSERT_EQ(kDaikin64Bits, irsend.capture.bits);
EXPECT_EQ(0x7C16161607204216, irsend.capture.value);
}

0 comments on commit c4942cc

Please sign in to comment.