Skip to content

Commit

Permalink
Add basic support for HITACHI_AC184 protocol.
Browse files Browse the repository at this point in the history
* Supports Hitachi PC-LH3B
* Add send & decode routines.
* Update ancillary support routines.
* Add synthetic and real unit test cases.

For #1060
  • Loading branch information
crankyoldgit committed Mar 11, 2020
1 parent 26d30f9 commit 4d32b93
Show file tree
Hide file tree
Showing 10 changed files with 211 additions and 15 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: Sat Mar 7 23:57:26 2020 --->
Last generated: Wed Mar 11 20:20:43 2020 --->
# IR Protocols supported by this library

| Protocol | Brand | Model | A/C Model | Detailed A/C Support |
Expand Down Expand Up @@ -28,7 +28,7 @@
| [Gree](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Gree.cpp) | **[RusClimate](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Gree.h)** | EACS/I-09HAR_X/N3 A/C<BR>YAW1F remote | YAW1F<BR>YBOFB | Yes |
| [Gree](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Gree.cpp) | **[Ultimate](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Gree.h)** | Heat Pump | YAW1F<BR>YBOFB | Yes |
| [Haier](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Haier.cpp) | **[Haier](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Haier.h)** | HSU-09HMC203 A/C<BR>HSU07-HEA03 remote<BR>YR-W02 remote | | Yes |
| [Hitachi](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Hitachi.cpp) | **[Hitachi](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Hitachi.h)** | LT0541-HTA remote<BR>RAR-8P2 remote<BR>RAS-35THA6 remote<BR>RAS-AJ25H A/C<BR>Series VI A/C (Circa 2007) | | Yes |
| [Hitachi](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Hitachi.cpp) | **[Hitachi](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Hitachi.h)** | LT0541-HTA remote<BR>PC-LH3B (HITACHI_AC184)<BR>RAR-8P2 remote<BR>RAS-35THA6 remote<BR>RAS-AJ25H A/C<BR>Series VI A/C (Circa 2007) | | Yes |
| [Inax](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Inax.cpp) | **Lixil** | Inax DT-BA283 Toilet | | - |
| [JVC](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_JVC.cpp) | **Unknown** | | | - |
| [Kelvinator](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Kelvinator.cpp) | **[Green](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/src/ir_Kelvinator.h)** | YAPOF3 remote | | Yes |
Expand Down Expand Up @@ -106,6 +106,7 @@
- HAIER_AC_YRW02
- HITACHI_AC
- HITACHI_AC1
- HITACHI_AC184
- HITACHI_AC2
- HITACHI_AC424
- INAX
Expand Down
10 changes: 8 additions & 2 deletions src/IRrecv.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -641,10 +641,16 @@ bool IRrecv::decode(decode_results *results, irparams_t *save,
if (decodeHaierACYRW02(results, offset)) return true;
#endif
#if DECODE_HITACHI_AC424
// HitachiAc424 should be checked before HitachiAC & HitachiAC2
// HitachiAc424 should be checked before HitachiAC, HitachiAC2,
// & HitachiAC184
DPRINTLN("Attempting Hitachi AC 424 decode");
if (decodeHitachiAc424(results, offset, kHitachiAc424Bits)) return true;
#endif // DECODE_HITACHI_AC2
#endif // DECODE_HITACHI_AC424
#if DECODE_HITACHI_AC184
// HitachiAc184 should be checked before HitachiAC & HitachiAC2
DPRINTLN("Attempting Hitachi AC 184 decode");
if (decodeHitachiAc184(results, offset)) return true;
#endif // DECODE_HITACHI_AC184
#if DECODE_HITACHI_AC2
// HitachiAC2 should be checked before HitachiAC
DPRINTLN("Attempting Hitachi AC2 decode");
Expand Down
6 changes: 6 additions & 0 deletions src/IRrecv.h
Original file line number Diff line number Diff line change
Expand Up @@ -485,6 +485,12 @@ class IRrecv {
const uint16_t nbits = kHitachiAc1Bits,
const bool strict = true);
#endif
#if DECODE_HITACHI_AC184
bool decodeHitachiAc184(decode_results *results,
uint16_t offset = kStartOffset,
const uint16_t nbits = kHitachiAc184Bits,
const bool strict = true);
#endif // DECODE_HITACHI_AC184
#if DECODE_HITACHI_AC424
bool decodeHitachiAc424(decode_results *results,
uint16_t offset = kStartOffset,
Expand Down
28 changes: 19 additions & 9 deletions src/IRremoteESP8266.h
Original file line number Diff line number Diff line change
Expand Up @@ -411,6 +411,20 @@
#define SEND_HITACHI_AC2 _IR_ENABLE_DEFAULT_
#endif // SEND_HITACHI_AC2

#ifndef DECODE_HITACHI_AC184
#define DECODE_HITACHI_AC184 _IR_ENABLE_DEFAULT_
#endif // DECODE_HITACHI_AC184
#ifndef SEND_HITACHI_AC184
#define SEND_HITACHI_AC184 _IR_ENABLE_DEFAULT_
#endif // SEND_HITACHI_AC184

#ifndef DECODE_HITACHI_AC424
#define DECODE_HITACHI_AC424 _IR_ENABLE_DEFAULT_
#endif // DECODE_HITACHI_AC424
#ifndef SEND_HITACHI_AC424
#define SEND_HITACHI_AC424 _IR_ENABLE_DEFAULT_
#endif // SEND_HITACHI_AC424

#ifndef DECODE_GICABLE
#define DECODE_GICABLE _IR_ENABLE_DEFAULT_
#endif // DECODE_GICABLE
Expand Down Expand Up @@ -558,13 +572,6 @@
#define SEND_DAIKIN152 _IR_ENABLE_DEFAULT_
#endif // SEND_DAIKIN152

#ifndef DECODE_HITACHI_AC424
#define DECODE_HITACHI_AC424 _IR_ENABLE_DEFAULT_
#endif // DECODE_HITACHI_AC424
#ifndef SEND_HITACHI_AC424
#define SEND_HITACHI_AC424 _IR_ENABLE_DEFAULT_
#endif // SEND_HITACHI_AC424

#ifndef DECODE_EPSON
#define DECODE_EPSON _IR_ENABLE_DEFAULT_
#endif // DECODE_EPSON
Expand All @@ -589,7 +596,7 @@
DECODE_DAIKIN216 || DECODE_SHARP_AC || DECODE_DAIKIN160 || \
DECODE_NEOCLIMA || DECODE_DAIKIN176 || DECODE_DAIKIN128 || \
DECODE_AMCOR || DECODE_DAIKIN152 || DECODE_MITSUBISHI136 || \
DECODE_MITSUBISHI112 || DECODE_HITACHI_AC424)
DECODE_MITSUBISHI112 || DECODE_HITACHI_AC424 || DECODE_HITACHI_AC184)
#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 @@ -703,8 +710,9 @@ enum decode_type_t {
SONY_38K,
EPSON, // 75
SYMPHONY,
HITACHI_AC184,
// Add new entries before this one, and update it to point to the last entry.
kLastDecodeType = SYMPHONY,
kLastDecodeType = HITACHI_AC184,
};

// Message lengths & required repeat values
Expand Down Expand Up @@ -782,6 +790,8 @@ const uint16_t kHitachiAc1StateLength = 13;
const uint16_t kHitachiAc1Bits = kHitachiAc1StateLength * 8;
const uint16_t kHitachiAc2StateLength = 53;
const uint16_t kHitachiAc2Bits = kHitachiAc2StateLength * 8;
const uint16_t kHitachiAc184StateLength = 23;
const uint16_t kHitachiAc184Bits = kHitachiAc184StateLength * 8;
const uint16_t kHitachiAc424StateLength = 53;
const uint16_t kHitachiAc424Bits = kHitachiAc424StateLength * 8;
const uint16_t kInaxBits = 24;
Expand Down
7 changes: 7 additions & 0 deletions src/IRsend.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -618,6 +618,8 @@ uint16_t IRsend::defaultBits(const decode_type_t protocol) {
return kHitachiAc1Bits;
case HITACHI_AC2:
return kHitachiAc2Bits;
case HITACHI_AC184:
return kHitachiAc184Bits;
case HITACHI_AC424:
return kHitachiAc424Bits;
case KELVINATOR:
Expand Down Expand Up @@ -959,6 +961,11 @@ bool IRsend::send(const decode_type_t type, const unsigned char *state,
sendHitachiAC2(state, nbytes);
break;
#endif // SEND_HITACHI_AC2
#if SEND_HITACHI_AC184
case HITACHI_AC184:
sendHitachiAc184(state, nbytes);
break;
#endif // SEND_HITACHI_AC184
#if SEND_HITACHI_AC424
case HITACHI_AC424:
sendHitachiAc424(state, nbytes);
Expand Down
5 changes: 5 additions & 0 deletions src/IRsend.h
Original file line number Diff line number Diff line change
Expand Up @@ -471,6 +471,11 @@ class IRsend {
const uint16_t nbytes = kHitachiAc2StateLength,
const uint16_t repeat = kHitachiAcDefaultRepeat);
#endif
#if SEND_HITACHI_AC184
void sendHitachiAc184(const unsigned char data[],
const uint16_t nbytes = kHitachiAc184StateLength,
const uint16_t repeat = kHitachiAcDefaultRepeat);
#endif // SEND_HITACHI_AC184
#if SEND_HITACHI_AC424
void sendHitachiAc424(const unsigned char data[],
const uint16_t nbytes = kHitachiAc424StateLength,
Expand Down
6 changes: 6 additions & 0 deletions src/IRutils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,8 @@ decode_type_t strToDecodeType(const char * const str) {
return decode_type_t::HITACHI_AC1;
else if (!strcasecmp(str, "HITACHI_AC2"))
return decode_type_t::HITACHI_AC2;
else if (!strcasecmp(str, "HITACHI_AC184"))
return decode_type_t::HITACHI_AC184;
else if (!strcasecmp(str, "HITACHI_AC424"))
return decode_type_t::HITACHI_AC424;
else if (!strcasecmp(str, "INAX"))
Expand Down Expand Up @@ -347,6 +349,9 @@ String typeToString(const decode_type_t protocol, const bool isRepeat) {
case HITACHI_AC2:
result = F("HITACHI_AC2");
break;
case HITACHI_AC184:
result = F("HITACHI_AC184");
break;
case HITACHI_AC424:
result = F("HITACHI_AC424");
break;
Expand Down Expand Up @@ -530,6 +535,7 @@ bool hasACState(const decode_type_t protocol) {
case HITACHI_AC:
case HITACHI_AC1:
case HITACHI_AC2:
case HITACHI_AC184:
case HITACHI_AC424:
case KELVINATOR:
case MITSUBISHI136:
Expand Down
81 changes: 81 additions & 0 deletions src/ir_Hitachi.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,14 @@ const uint16_t kHitachiAc424BitMark = 463;
const uint16_t kHitachiAc424OneSpace = 1208;
const uint16_t kHitachiAc424ZeroSpace = 372;

// Support for HitachiAc184 protocol
// Ref: https://github.com/crankyoldgit/IRremoteESP8266/issues/1060
const uint16_t kHitachiAc184HdrMark = 3400; // Header
const uint16_t kHitachiAc184HdrSpace = 1660; // Header
const uint16_t kHitachiAc184BitMark = 460;
const uint16_t kHitachiAc184OneSpace = 1250;
const uint16_t kHitachiAc184ZeroSpace = 410;

using irutils::addBoolToString;
using irutils::addIntToString;
using irutils::addLabeledString;
Expand Down Expand Up @@ -800,3 +808,76 @@ String IRHitachiAc424::toString(void) {
result += ')';
return result;
}


#if SEND_HITACHI_AC184
// Send HITACHI_AC184 messages
//
// Note: This protocol is almost exactly the same as HitachiAC424 except this
// variant has subtle timing differences.
//
// Args:
// data: An array of bytes containing the IR command.
// It is assumed to be in LSBF order for this code.
// nbytes: Nr. of bytes of data in the array. (>=kHitachiAc184StateLength)
// repeat: Nr. of times the message is to be repeated.
//
// Status: BETA / Probably working fine.
void IRsend::sendHitachiAc184(const uint8_t data[], const uint16_t nbytes,
const uint16_t repeat) {
// Header + Data + Footer
sendGeneric(kHitachiAc184HdrMark, kHitachiAc184HdrSpace,
kHitachiAc184BitMark, kHitachiAc184OneSpace,
kHitachiAc184BitMark, kHitachiAc184ZeroSpace,
kHitachiAc184BitMark, kHitachiAcMinGap,
data, nbytes, // Bytes
kHitachiAcFreq, false, repeat, kDutyDefault);
}
#endif // SEND_HITACHI_AC184

#if DECODE_HITACHI_AC184
// Decode the supplied Hitachi 184 bit A/C message.
//
// Note: This protocol is almost exactly the same as HitachiAC424 except this
// variant has subtle timing differences.
//
// 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: The number of data bits to expect. Typically kHitachiAc184Bits.
// strict: Flag indicating if we should perform strict matching.
// Returns:
// boolean: True if it can decode it, false if it can't.
//
// Status: BETA / Probably works fine.
//
// Supported devices:
// Hitachi PC-LH3B
//
// Ref:
// https://github.com/crankyoldgit/IRremoteESP8266/issues/1060
bool IRrecv::decodeHitachiAc184(decode_results *results, uint16_t offset,
const uint16_t nbits,
const bool strict) {
if (results->rawlen < 2 * nbits + kHeader + kFooter - 1 + offset)
return false; // Too short a message to match.
if (strict && nbits != kHitachiAc184Bits)
return false;

// Header + Data + Footer
if (!matchGeneric(results->rawbuf + offset, results->state,
results->rawlen - offset, nbits,
kHitachiAc184HdrMark, kHitachiAc184HdrSpace,
kHitachiAc184BitMark, kHitachiAc184OneSpace,
kHitachiAc184BitMark, kHitachiAc184ZeroSpace,
kHitachiAc184BitMark, kHitachiAcMinGap, true,
kUseDefTol, 0, false))
return false; // We failed to find any data.

// Success
results->decode_type = decode_type_t::HITACHI_AC184;
results->bits = nbits;
return true;
}
#endif // DECODE_HITACHI_AC184
3 changes: 2 additions & 1 deletion src/ir_Hitachi.h
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
// Hitachi A/C
//
// Copyright 2018-2019 David Conran
// Copyright 2018-2020 David Conran

// Supports:
// Brand: Hitachi, Model: RAS-35THA6 remote
// Brand: Hitachi, Model: LT0541-HTA remote
// Brand: Hitachi, Model: Series VI A/C (Circa 2007)
// Brand: Hitachi, Model: RAR-8P2 remote
// Brand: Hitachi, Model: RAS-AJ25H A/C
// Brand: Hitachi, Model: PC-LH3B (HITACHI_AC184)

#ifndef IR_HITACHI_H_
#define IR_HITACHI_H_
Expand Down
75 changes: 74 additions & 1 deletion test/ir_Hitachi_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -816,6 +816,11 @@ TEST(TestUtils, Housekeeping) {
ASSERT_TRUE(hasACState(decode_type_t::HITACHI_AC2));
ASSERT_FALSE(IRac::isProtocolSupported(decode_type_t::HITACHI_AC2));

ASSERT_EQ("HITACHI_AC184", typeToString(decode_type_t::HITACHI_AC184));
ASSERT_EQ(decode_type_t::HITACHI_AC184, strToDecodeType("HITACHI_AC184"));
ASSERT_TRUE(hasACState(decode_type_t::HITACHI_AC184));
ASSERT_FALSE(IRac::isProtocolSupported(decode_type_t::HITACHI_AC184));

ASSERT_EQ("HITACHI_AC424", typeToString(decode_type_t::HITACHI_AC424));
ASSERT_EQ(decode_type_t::HITACHI_AC424, strToDecodeType("HITACHI_AC424"));
ASSERT_TRUE(hasACState(decode_type_t::HITACHI_AC424));
Expand Down Expand Up @@ -1113,7 +1118,7 @@ TEST(TestIRHitachiAc424Class, HumanReadable) {
}

TEST(TestIRHitachiAc424Class, toCommon) {
IRHitachiAc424 ac(0);
IRHitachiAc424 ac(kGpioUnused);
ac.setPower(true);
ac.setMode(kHitachiAc424Cool);
ac.setTemp(20);
Expand All @@ -1139,3 +1144,71 @@ TEST(TestIRHitachiAc424Class, toCommon) {
ASSERT_EQ(-1, ac.toCommon().sleep);
ASSERT_EQ(-1, ac.toCommon().clock);
}

TEST(TestDecodeHitachiAc184, SyntheticExample) {
IRsendTest irsend(kGpioUnused);
IRrecv irrecv(kGpioUnused);
irsend.begin();

uint8_t expected[kHitachiAc184StateLength] = {
0x01, 0x10, 0x00, 0x40, 0xBF, 0xFF, 0x00, 0xE6, 0x19, 0x89, 0x76, 0x01,
0xFE, 0x3F, 0xC0, 0x2F, 0xD0, 0x18, 0xE7, 0x00, 0xFF, 0xA0, 0x5F};

irsend.reset();
irsend.sendHitachiAc184(expected);
irsend.makeDecodeResult();
EXPECT_TRUE(irrecv.decode(&irsend.capture));
ASSERT_EQ(HITACHI_AC184, irsend.capture.decode_type);
ASSERT_EQ(kHitachiAc184Bits, irsend.capture.bits);
EXPECT_STATE_EQ(expected, irsend.capture.state, irsend.capture.bits);
}

// Decode a 'real' HitachiAc184 message.
TEST(TestDecodeHitachiAc184, RealExample) {
IRsendTest irsend(kGpioUnused);
IRrecv irrecv(kGpioUnused);
irsend.begin();

uint8_t expected[kHitachiAc184StateLength] = {
0x01, 0x10, 0x00, 0x40, 0xBF, 0xFF, 0x00, 0xE6, 0x19, 0x89, 0x76, 0x01,
0xFE, 0x3F, 0xC0, 0x2F, 0xD0, 0x18, 0xE7, 0x00, 0xFF, 0xA0, 0x5F};

// Ref: https://github.com/crankyoldgit/IRremoteESP8266/issues/1060#issuecomment-597519432
uint16_t rawData[371] = {
// Power Off
3422, 1660, 464, 1264, 438, 426, 438, 402, 486, 426, 440, 402, 462, 402,
488, 428, 438, 402, 460, 404, 488, 426, 440, 424, 436, 428, 462, 1240,
466, 398, 462, 404, 486, 426, 438, 402, 464, 400, 490, 400, 464, 426, 438,
402, 488, 428, 436, 428, 462, 376, 486, 404, 474, 416, 438, 400, 488, 402,
462, 426, 438, 426, 458, 1246, 464, 426, 436, 1244, 488, 1266, 434, 1268,
436, 1242, 486, 1242, 462, 1240, 464, 426, 462, 1266, 438, 1242, 462,
1240, 514, 1214, 466, 1236, 462, 1266, 464, 1264, 438, 1266, 438, 1240,
488, 400, 466, 424, 440, 402, 486, 404, 462, 402, 464, 402, 488, 426, 438,
402, 462, 402, 488, 1264, 438, 1242, 460, 428, 462, 428, 436, 1264, 440,
1240, 488, 1240, 464, 1240, 460, 402, 490, 424, 440, 1264, 438, 1242, 512,
402, 438, 426, 466, 398, 464, 1266, 440, 400, 462, 402, 488, 1264, 438,
402, 462, 402, 482, 432, 438, 1264, 436, 404, 488, 1240, 462, 1264, 438,
402, 486, 1246, 456, 1266, 438, 1238, 488, 402, 462, 1240, 462, 402, 512,
402, 438, 428, 438, 426, 462, 430, 434, 428, 438, 402, 512, 376, 462,
1240, 464, 1264, 458, 1270, 436, 1242, 462, 1240, 486, 1242, 460, 1242,
462, 1242, 486, 1240, 464, 1240, 464, 1238, 492, 1264, 436, 1266, 440,
400, 488, 426, 436, 430, 434, 430, 460, 404, 462, 426, 438, 402, 488, 426,
436, 1264, 466, 1238, 462, 1242, 462, 1266, 438, 1238, 490, 1264, 438,
426, 438, 1240, 486, 406, 462, 402, 462, 400, 488, 428, 436, 426, 438,
402, 484, 1268, 438, 426, 436, 1242, 488, 1266, 436, 402, 462, 402, 502,
386, 464, 1240, 464, 1240, 488, 426, 438, 426, 438, 402, 488, 1240, 462,
1266, 438, 1242, 486, 428, 440, 424, 438, 1266, 460, 1268, 438, 1264, 438,
404, 486, 428, 438, 426, 438, 402, 490, 400, 462, 426, 436, 402, 490, 426,
438, 1240, 462, 1268, 460, 1268, 434, 1266, 436, 1240, 514, 1238, 438,
1240, 488, 1240, 460, 406, 464, 426, 436, 402, 488, 402, 462, 402, 462,
1264, 462, 404, 462, 1266, 434, 1268, 464, 1264, 438, 1242, 464, 1238,
488, 1266, 438, 402, 462, 1268, 458, 430, 410};

irsend.reset();
irsend.sendRaw(rawData, 371, kHitachiAcFreq);
irsend.makeDecodeResult();
EXPECT_TRUE(irrecv.decode(&irsend.capture));
EXPECT_EQ(HITACHI_AC184, irsend.capture.decode_type);
ASSERT_EQ(kHitachiAc184Bits, irsend.capture.bits);
EXPECT_STATE_EQ(expected, irsend.capture.state, kHitachiAc184Bits);
}

0 comments on commit 4d32b93

Please sign in to comment.