Skip to content

Commit

Permalink
Initial code to handle Hitachi A/C 13 byte codes.
Browse files Browse the repository at this point in the history
HITACHI_AC1 is basically the same as HITACHI_AC except only 13 bytes long
and a Header Space that is the same length as the Header Mark.

Ref: #453
  • Loading branch information
crankyoldgit committed May 14, 2018
1 parent 3c11f65 commit 71f5601
Show file tree
Hide file tree
Showing 8 changed files with 196 additions and 3 deletions.
11 changes: 11 additions & 0 deletions examples/IRMQTTServer/IRMQTTServer.ino
Original file line number Diff line number Diff line change
Expand Up @@ -386,6 +386,8 @@ void handleRoot() {
"<option value='33'>Fujitsu</option>"
"<option value='24'>Gree</option>"
"<option value='38'>Haier</option>"
"<option value='40'>Hitachi (28 bytes)</option>"
"<option value='41'>Hitachi1 (13 bytes)</option>"
"<option selected='selected' value='18'>Kelvinator</option>" // Default
"<option value='20'>Mitsubishi</option>"
"<option value='32'>Toshiba</option>"
Expand Down Expand Up @@ -488,6 +490,9 @@ void parseStringAndSendAirCon(const uint16_t irType, const String str) {
case HITACHI_AC:
stateSize = HITACHI_AC_STATE_LENGTH;
break;
case HITACHI_AC1:
stateSize = HITACHI_AC1_STATE_LENGTH;
break;
default: // Not a protocol we expected. Abort.
debug("Unexpected AirCon protocol detected. Ignoring.");
return;
Expand Down Expand Up @@ -572,6 +577,11 @@ void parseStringAndSendAirCon(const uint16_t irType, const String str) {
case HITACHI_AC:
irsend.sendHitachiAC(reinterpret_cast<uint8_t *>(state));
break;
#endif
#if SEND_HITACHI_AC1
case HITACHI_AC1:
irsend.sendHitachiAC1(reinterpret_cast<uint8_t *>(state));
break;
#endif
}
}
Expand Down Expand Up @@ -1091,6 +1101,7 @@ void sendIRCode(int const ir_type, uint64_t const code, char const * code_str,
case FUJITSU_AC: // 33
case HAIER_AC: // 38
case HITACHI_AC: // 40
case HITACHI_AC1: // 41
parseStringAndSendAirCon(ir_type, code_str);
break;
#if SEND_DENON
Expand Down
5 changes: 5 additions & 0 deletions src/IRrecv.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -464,6 +464,11 @@ bool IRrecv::decode(decode_results *results, irparams_t *save) {
if (decodeHitachiAC(results))
return true;
#endif
#if DECODE_HITACHI_AC1
DPRINTLN("Attempting Hitachi AC1 decode");
if (decodeHitachiAC1(results))
return true;
#endif
#if DECODE_HASH
// decodeHash returns a hash on any input.
// Thus, it needs to be last in the list.
Expand Down
6 changes: 5 additions & 1 deletion src/IRrecv.h
Original file line number Diff line number Diff line change
Expand Up @@ -274,10 +274,14 @@ class IRrecv {
bool decodeHaierAC(decode_results *results,
uint16_t nbits = HAIER_AC_BITS, bool strict = true);
#endif
#if DECODE_HAIER_AC
#if DECODE_HITACHI_AC
bool decodeHitachiAC(decode_results *results,
uint16_t nbits = HITACHI_AC_BITS, bool strict = true);
#endif
#if DECODE_HITACHI_AC1
bool decodeHitachiAC1(decode_results *results,
uint16_t nbits = HITACHI_AC1_BITS, bool strict = true);
#endif
};

#endif // IRRECV_H_
11 changes: 9 additions & 2 deletions src/IRremoteESP8266.h
Original file line number Diff line number Diff line change
Expand Up @@ -166,9 +166,13 @@
#define DECODE_HITACHI_AC true
#define SEND_HITACHI_AC true

#define DECODE_HITACHI_AC1 true
#define SEND_HITACHI_AC1 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_TROTEC || DECODE_HAIER_AC || DECODE_HITACHI_AC || \
DECODE_HITACHI_AC1)
#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 @@ -227,7 +231,8 @@ enum decode_type_t {
CARRIER_AC,
HAIER_AC,
MITSUBISHI2,
HITACHI_AC
HITACHI_AC,
HITACHI_AC1
};

// Message lengths & required repeat values
Expand All @@ -251,6 +256,8 @@ enum decode_type_t {
#define HAIER_AC_BITS (HAIER_AC_STATE_LENGTH * 8)
#define HITACHI_AC_STATE_LENGTH 28U
#define HITACHI_AC_BITS (HITACHI_AC_STATE_LENGTH * 8)
#define HITACHI_AC1_STATE_LENGTH 13U
#define HITACHI_AC1_BITS (HITACHI_AC1_STATE_LENGTH * 8)
#define JVC_BITS 16U
#define KELVINATOR_STATE_LENGTH 16U
#define KELVINATOR_BITS (KELVINATOR_STATE_LENGTH * 8)
Expand Down
5 changes: 5 additions & 0 deletions src/IRsend.h
Original file line number Diff line number Diff line change
Expand Up @@ -249,6 +249,11 @@ void send(uint16_t type, uint64_t data, uint16_t nbits);
uint16_t nbytes = HITACHI_AC_STATE_LENGTH,
uint16_t repeat = 0);
#endif
#if SEND_HITACHI_AC1
void sendHitachiAC1(unsigned char data[],
uint16_t nbytes = HITACHI_AC1_STATE_LENGTH,
uint16_t repeat = 0);
#endif

protected:
#ifdef UNIT_TEST
Expand Down
2 changes: 2 additions & 0 deletions src/IRutils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,7 @@ std::string typeToString(const decode_type_t protocol,
case GREE: result = "GREE"; break;
case HAIER_AC: result = "HAIER_AC"; break;
case HITACHI_AC: result = "HITACHI_AC"; break;
case HITACHI_AC1: result = "HITACHI_AC1"; break;
case JVC: result = "JVC"; break;
case KELVINATOR: result = "KELVINATOR"; break;
case LG: result = "LG"; break;
Expand Down Expand Up @@ -153,6 +154,7 @@ bool hasACState(const decode_type_t protocol) {
case GREE:
case HAIER_AC:
case HITACHI_AC:
case HITACHI_AC1:
case KELVINATOR:
case MITSUBISHI_AC:
case TOSHIBA_AC:
Expand Down
92 changes: 92 additions & 0 deletions src/ir_Hitachi.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@
// Ref: https://github.com/markszabo/IRremoteESP8266/issues/417
#define HITACHI_AC_HDR_MARK 3300U
#define HITACHI_AC_HDR_SPACE 1700U
#define HITACHI_AC1_HDR_MARK 3400U
#define HITACHI_AC1_HDR_SPACE 3400U
#define HITACHI_AC_BIT_MARK 400U
#define HITACHI_AC_ONE_SPACE 1250U
#define HITACHI_AC_ZERO_SPACE 500U
Expand Down Expand Up @@ -53,6 +55,31 @@ void IRsend::sendHitachiAC(unsigned char data[], uint16_t nbytes,
}
#endif // SEND_HITACHI_AC

#if SEND_HITACHI_AC1
// Send a Hitachi A/C 13-byte message.
//
// Args:
// data: An array of bytes containing the IR command.
// nbytes: Nr. of bytes of data in the array. (>=HITACHI_AC1_STATE_LENGTH)
// repeat: Nr. of times the message is to be repeated. (Default = 0).
//
// Status: BETA / Appears to work.
//
// Ref:
// https://github.com/markszabo/IRremoteESP8266/issues/453
// Basically the same as sendHitatchiAC() except different size and header.
void IRsend::sendHitachiAC1(unsigned char data[], uint16_t nbytes,
uint16_t repeat) {
if (nbytes < HITACHI_AC1_STATE_LENGTH)
return; // Not enough bytes to send a proper message.
sendGeneric(HITACHI_AC1_HDR_MARK, HITACHI_AC1_HDR_SPACE,
HITACHI_AC_BIT_MARK, HITACHI_AC_ONE_SPACE,
HITACHI_AC_BIT_MARK, HITACHI_AC_ZERO_SPACE,
HITACHI_AC_BIT_MARK, HITACHI_AC_MIN_GAP,
data, nbytes, 38, true, repeat, 50);
}
#endif // SEND_HITACHI_AC1

#if DECODE_HITACHI_AC
// Decode the supplied Hitachi A/C message.
//
Expand Down Expand Up @@ -117,3 +144,68 @@ bool IRrecv::decodeHitachiAC(decode_results *results, uint16_t nbits,
return true;
}
#endif // DECODE_HITACHI_AC

#if DECODE_HITACHI_AC1
// Decode the supplied Hitachi A/C 13-byte message.
//
// Args:
// results: Ptr to the data to decode and where to store the decode result.
// nbits: The number of data bits to expect. Typically HITACHI_AC1_BITS.
// strict: Flag indicating if we should perform strict matching.
// Returns:
// boolean: True if it can decode it, false if it can't.
//
// Status: ALPHA / Untested.
bool IRrecv::decodeHitachiAC1(decode_results *results, uint16_t nbits,
bool strict) {
if (results->rawlen < 2 * nbits + HEADER + FOOTER - 1)
return false; // Can't possibly be a valid HitachiAC1 message.
if (strict && nbits != HITACHI_AC1_BITS)
return false; // Not strictly a HitachiAC1 message.

uint16_t offset = OFFSET_START;
uint16_t dataBitsSoFar = 0;
match_result_t data_result;

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

// Data
// Keep reading bytes until we either run out of message or state to fill.
for (uint16_t i = 0;
offset <= results->rawlen - 16 && i < HITACHI_AC1_STATE_LENGTH;
i++, dataBitsSoFar += 8, offset += data_result.used) {
data_result = matchData(&(results->rawbuf[offset]), 8,
HITACHI_AC_BIT_MARK,
HITACHI_AC_ONE_SPACE,
HITACHI_AC_BIT_MARK,
HITACHI_AC_ZERO_SPACE);
if (data_result.success == false) break; // Fail
results->state[i] = (uint8_t) data_result.data;
}

// Footer.
if (!matchMark(results->rawbuf[offset++], HITACHI_AC_BIT_MARK))
return false;
if (offset <= results->rawlen &&
!matchAtLeast(results->rawbuf[offset], HITACHI_AC_MIN_GAP))
return false;

// Compliance
if (strict) {
// Correct size/length)
if (dataBitsSoFar / 8 != HITACHI_AC1_STATE_LENGTH) return false;
}

// Success
results->decode_type = HITACHI_AC1;
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_HITACHI_AC1
67 changes: 67 additions & 0 deletions test/ir_Hitachi_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -314,3 +314,70 @@ TEST(TestDecodeHitachiAC, NormalRealExample2) {
ASSERT_EQ(HITACHI_AC_BITS, irsend.capture.bits);
EXPECT_STATE_EQ(hitachi_code, irsend.capture.state, HITACHI_AC_BITS);
}

// Tests for sendHitachiAC1().

// Test sending typical data only.
TEST(TestSendHitachiAC1, SendData) {
IRsendTest irsend(0);
irsend.begin();

uint8_t hitachi_code[HITACHI_AC1_STATE_LENGTH] = {
0xB2, 0xAE, 0x4D, 0x51, 0xF0, 0x61, 0x84, 0x00, 0x00, 0x00, 0x00, 0x30,
0xB8};
irsend.reset();
irsend.sendHitachiAC1(hitachi_code);
EXPECT_EQ(
"m3400s3400"
"m400s1250m400s500m400s1250m400s1250m400s500m400s500m400s1250m400s500"
"m400s1250m400s500m400s1250m400s500m400s1250m400s1250m400s1250m400s500"
"m400s500m400s1250m400s500m400s500m400s1250m400s1250m400s500m400s1250"
"m400s500m400s1250m400s500m400s1250m400s500m400s500m400s500m400s1250"
"m400s1250m400s1250m400s1250m400s1250m400s500m400s500m400s500m400s500"
"m400s500m400s1250m400s1250m400s500m400s500m400s500m400s500m400s1250"
"m400s1250m400s500m400s500m400s500m400s500m400s1250m400s500m400s500"
"m400s500m400s500m400s500m400s500m400s500m400s500m400s500m400s500"
"m400s500m400s500m400s500m400s500m400s500m400s500m400s500m400s500"
"m400s500m400s500m400s500m400s500m400s500m400s500m400s500m400s500"
"m400s500m400s500m400s500m400s500m400s500m400s500m400s500m400s500"
"m400s500m400s500m400s1250m400s1250m400s500m400s500m400s500m400s500"
"m400s1250m400s500m400s1250m400s1250m400s1250m400s500m400s500m400s500"
"m400s100000", irsend.outputStr());
}

// Decode a 'real' HitachiAC1 message.
TEST(TestDecodeHitachiAC1, NormalRealExample) {
IRsendTest irsend(0);
IRrecv irrecv(0);
irsend.begin();

uint8_t hitachi_code[HITACHI_AC1_STATE_LENGTH] = {
0xB2, 0xAE, 0x4D, 0x51, 0xF0, 0x61, 0x84, 0x00, 0x00, 0x00, 0x00, 0x10,
0x98};

// Ref: https://github.com/markszabo/IRremoteESP8266/issues/453
uint16_t rawData[211] = {
3400, 3350, 450, 1250, 450, 400, 400, 1300, 400, 1300, 400, 400, 450, 400,
400, 1300, 400, 400, 400, 1300, 400, 400, 450, 1250, 400, 450, 400, 1300,
400, 1250, 450, 1250, 450, 400, 400, 450, 400, 1250, 450, 400, 400, 400,
400, 1300, 400, 1300, 400, 400, 450, 1250, 450, 400, 400, 1300, 400, 400,
450, 1250, 400, 400, 450, 400, 400, 400, 450, 1250, 400, 1300, 450, 1250,
450, 1250, 400, 1300, 400, 400, 450, 400, 400, 450, 350, 450, 400, 400, 400,
1300, 400, 1300, 400, 400, 450, 400, 400, 400, 450, 400, 400, 1300, 400,
1250, 450, 400, 400, 400, 450, 400, 400, 400, 450, 1250, 450, 400, 400, 400,
450, 400, 400, 400, 450, 400, 400, 400, 450, 400, 400, 400, 450, 400, 400,
400, 400, 450, 400, 400, 400, 400, 450, 400, 400, 400, 450, 400, 400, 450,
400, 400, 400, 400, 450, 400, 400, 400, 450, 400, 400, 450, 400, 400, 400,
400, 400, 450, 400, 400, 400, 400, 450, 400, 400, 400, 450, 400, 400, 400,
450, 400, 400, 400, 450, 400, 400, 400, 450, 400, 400, 1300, 400, 400, 450,
400, 400, 400, 400, 400, 450, 1250, 450, 400, 400, 400, 450, 1250, 450,
1250, 450, 400, 400, 400, 450, 400, 400}; // UNKNOWN 828A89E1

irsend.reset();
irsend.sendRaw(rawData, 211, 38000);
irsend.makeDecodeResult();
EXPECT_TRUE(irrecv.decode(&irsend.capture));
EXPECT_EQ(HITACHI_AC1, irsend.capture.decode_type);
ASSERT_EQ(HITACHI_AC1_BITS, irsend.capture.bits);
EXPECT_STATE_EQ(hitachi_code, irsend.capture.state, HITACHI_AC1_BITS);
}

0 comments on commit 71f5601

Please sign in to comment.