Skip to content

Commit

Permalink
Initial code to handle Hitachi A/C 13 & 53 byte codes. (#461)
Browse files Browse the repository at this point in the history
* Add basic support for 13 & 53 byte Hitachi A/C protocol.
  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 authored May 20, 2018
1 parent 822bcf3 commit 7f54c54
Show file tree
Hide file tree
Showing 8 changed files with 452 additions and 30 deletions.
23 changes: 22 additions & 1 deletion examples/IRMQTTServer/IRMQTTServer.ino
Original file line number Diff line number Diff line change
Expand Up @@ -387,6 +387,9 @@ 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 value='42'>Hitachi2 (53 bytes)</option>"
"<option selected='selected' value='18'>Kelvinator</option>" // Default
"<option value='20'>Mitsubishi</option>"
"<option value='32'>Toshiba</option>"
Expand Down Expand Up @@ -489,6 +492,12 @@ 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;
case HITACHI_AC2:
stateSize = HITACHI_AC2_STATE_LENGTH;
break;
default: // Not a protocol we expected. Abort.
debug("Unexpected AirCon protocol detected. Ignoring.");
return;
Expand Down Expand Up @@ -573,6 +582,16 @@ 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
#if SEND_HITACHI_AC2
case HITACHI_AC2:
irsend.sendHitachiAC2(reinterpret_cast<uint8_t *>(state));
break;
#endif
}
}
Expand Down Expand Up @@ -1092,6 +1111,8 @@ 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
case HITACHI_AC2: // 42
parseStringAndSendAirCon(ir_type, code_str);
break;
#if SEND_DENON
Expand Down Expand Up @@ -1189,7 +1210,7 @@ void sendIRCode(int const ir_type, uint64_t const code, char const * code_str,
break;
#endif
#if SEND_GICABLE
case GICABLE: // 41
case GICABLE: // 43
if (bits == 0)
bits = GICABLE_BITS;
repeat = std::max(repeat, (uint16_t) GICABLE_BITS);
Expand Down
13 changes: 12 additions & 1 deletion src/IRrecv.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -466,9 +466,20 @@ bool IRrecv::decode(decode_results *results, irparams_t *save) {
if (decodeHaierAC(results))
return true;
#endif
#if DECODE_HITACHI_AC2
// HitachiAC2 should be checked before HitachiAC
DPRINTLN("Attempting Hitachi AC2 decode");
if (decodeHitachiAC(results, HITACHI_AC2_BITS))
return true;
#endif
#if DECODE_HITACHI_AC
DPRINTLN("Attempting Hitachi AC decode");
if (decodeHitachiAC(results))
if (decodeHitachiAC(results, HITACHI_AC_BITS))
return true;
#endif
#if DECODE_HITACHI_AC1
DPRINTLN("Attempting Hitachi AC1 decode");
if (decodeHitachiAC(results, HITACHI_AC1_BITS))
return true;
#endif
#if DECODE_HASH
Expand Down
18 changes: 12 additions & 6 deletions src/IRrecv.h
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,8 @@
#define FNV_PRIME_32 16777619UL
#define FNV_BASIS_32 2166136261UL

// Hitachi is the current largest state size.
#define STATE_SIZE_MAX HITACHI_AC_STATE_LENGTH
// Hitachi AC is the current largest state size.
#define STATE_SIZE_MAX HITACHI_AC2_STATE_LENGTH

// Types
// information for the interrupt handler
Expand Down Expand Up @@ -114,11 +114,13 @@ class IRrecv {
void setUnknownThreshold(uint16_t length);
#endif
static bool match(uint32_t measured, uint32_t desired,
uint8_t tolerance = TOLERANCE, uint16_t delta = 0);
uint8_t tolerance = TOLERANCE, uint16_t delta = 0);
static bool matchMark(uint32_t measured, uint32_t desired,
uint8_t tolerance = TOLERANCE, int16_t excess = MARK_EXCESS);
uint8_t tolerance = TOLERANCE,
int16_t excess = MARK_EXCESS);
static bool matchSpace(uint32_t measured, uint32_t desired,
uint8_t tolerance = TOLERANCE, int16_t excess = MARK_EXCESS);
uint8_t tolerance = TOLERANCE,
int16_t excess = MARK_EXCESS);
#ifndef UNIT_TEST

private:
Expand Down Expand Up @@ -274,10 +276,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 || DECODE_HITACHI_AC2)
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
#if DECODE_GICABLE
bool decodeGICable(decode_results *results, uint16_t nbits = GICABLE_BITS,
bool strict = true);
Expand Down
15 changes: 14 additions & 1 deletion src/IRremoteESP8266.h
Original file line number Diff line number Diff line change
Expand Up @@ -166,12 +166,19 @@
#define DECODE_HITACHI_AC true
#define SEND_HITACHI_AC true

#define DECODE_HITACHI_AC1 true
#define SEND_HITACHI_AC1 true

#define DECODE_HITACHI_AC2 true
#define SEND_HITACHI_AC2 true

#define DECODE_GICABLE true
#define SEND_GICABLE 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 || DECODE_HITACHI_AC2)
#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 @@ -231,6 +238,8 @@ enum decode_type_t {
HAIER_AC,
MITSUBISHI2,
HITACHI_AC,
HITACHI_AC1,
HITACHI_AC2,
GICABLE
};

Expand All @@ -257,6 +266,10 @@ 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 HITACHI_AC2_STATE_LENGTH 53U
#define HITACHI_AC2_BITS (HITACHI_AC2_STATE_LENGTH * 8)
#define JVC_BITS 16U
#define KELVINATOR_STATE_LENGTH 16U
#define KELVINATOR_BITS (KELVINATOR_STATE_LENGTH * 8)
Expand Down
10 changes: 10 additions & 0 deletions src/IRsend.h
Original file line number Diff line number Diff line change
Expand Up @@ -249,6 +249,16 @@ 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
#if SEND_HITACHI_AC2
void sendHitachiAC2(unsigned char data[],
uint16_t nbytes = HITACHI_AC2_STATE_LENGTH,
uint16_t repeat = 0);
#endif
#if SEND_GICABLE
void sendGICable(uint64_t data, uint16_t nbits = GICABLE_BITS,
uint16_t repeat = GICABLE_MIN_REPEAT);
Expand Down
4 changes: 4 additions & 0 deletions src/IRutils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,8 @@ 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 HITACHI_AC2: result = "HITACHI_AC2"; break;
case JVC: result = "JVC"; break;
case KELVINATOR: result = "KELVINATOR"; break;
case LG: result = "LG"; break;
Expand Down Expand Up @@ -154,6 +156,8 @@ bool hasACState(const decode_type_t protocol) {
case GREE:
case HAIER_AC:
case HITACHI_AC:
case HITACHI_AC1:
case HITACHI_AC2:
case KELVINATOR:
case MITSUBISHI_AC:
case TOSHIBA_AC:
Expand Down
138 changes: 117 additions & 21 deletions src/ir_Hitachi.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,14 @@
// 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
#define HITACHI_AC_MIN_GAP 100000U // Completely made up value.

#if SEND_HITACHI_AC
#if (SEND_HITACHI_AC || SEND_HITACHI_AC2)
// Send a Hitachi A/C message.
//
// Args:
Expand All @@ -51,69 +53,163 @@ void IRsend::sendHitachiAC(unsigned char data[], uint16_t nbytes,
HITACHI_AC_BIT_MARK, HITACHI_AC_MIN_GAP,
data, nbytes, 38, true, repeat, 50);
}
#endif // SEND_HITACHI_AC
#endif // (SEND_HITACHI_AC || SEND_HITACHI_AC2)

#if DECODE_HITACHI_AC
#if SEND_HITACHI_AC1
// Send a Hitachi A/C 13-byte message.
//
// For devices:
// Hitachi A/C Series VI (Circa 2007) / Remote: LT0541-HTA
//
// 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 SEND_HITACHI_AC2
// Send a Hitachi A/C 53-byte message.
//
// For devices:
// Hitachi A/C Series VI (Circa 2007) / Remote: LT0541-HTA
//
// Args:
// data: An array of bytes containing the IR command.
// nbytes: Nr. of bytes of data in the array. (>=HITACHI_AC2_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/417
// Basically the same as sendHitatchiAC() except different size.
void IRsend::sendHitachiAC2(unsigned char data[], uint16_t nbytes,
uint16_t repeat) {
if (nbytes < HITACHI_AC2_STATE_LENGTH)
return; // Not enough bytes to send a proper message.
sendHitachiAC(data, nbytes, repeat);
}
#endif // SEND_HITACHI_AC2

#if (DECODE_HITACHI_AC || DECODE_HITACHI_AC1 || DECODE_HITACHI_AC2)
// Decode the supplied Hitachi A/C 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_AC_BITS.
// nbits: The number of data bits to expect.
// Typically HITACHI_AC_BITS, HITACHI_AC1_BITS, HITACHI_AC2_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.
//
// Supported devices:
// Hitachi A/C Series VI (Circa 2007) / Remote: LT0541-HTA
//
// Ref:
// https://github.com/markszabo/IRremoteESP8266/issues/417
// https://github.com/markszabo/IRremoteESP8266/issues/453
bool IRrecv::decodeHitachiAC(decode_results *results, uint16_t nbits,
bool strict) {
const uint8_t kTolerance = 30;
if (results->rawlen < 2 * nbits + HEADER + FOOTER - 1)
return false; // Can't possibly be a valid HitachiAC message.
if (strict && nbits != HITACHI_AC_BITS)
return false; // Not strictly a HitachiAC message.

if (strict) {
switch (nbits) {
case HITACHI_AC_BITS:
case HITACHI_AC1_BITS:
case HITACHI_AC2_BITS:
break; // Okay to continue.
default:
return false; // Not strictly a Hitachi message.
}
}
uint16_t offset = OFFSET_START;
uint16_t dataBitsSoFar = 0;
match_result_t data_result;

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

if (nbits == HITACHI_AC1_BITS) {
if (!matchMark(results->rawbuf[offset++], HITACHI_AC1_HDR_MARK, kTolerance))
return false;
if (!matchSpace(results->rawbuf[offset++], HITACHI_AC1_HDR_SPACE,
kTolerance))
return false;
} else { // Everything else.
if (!matchMark(results->rawbuf[offset++], HITACHI_AC_HDR_MARK, kTolerance))
return false;
if (!matchSpace(results->rawbuf[offset++], HITACHI_AC_HDR_SPACE,
kTolerance))
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_AC_STATE_LENGTH;
offset <= results->rawlen - 16 && i < nbits / 8;
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);
HITACHI_AC_ZERO_SPACE,
kTolerance);
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))
// Footer
if (!matchMark(results->rawbuf[offset++], HITACHI_AC_BIT_MARK, kTolerance))
return false;
if (offset <= results->rawlen &&
!matchAtLeast(results->rawbuf[offset], HITACHI_AC_MIN_GAP))
!matchAtLeast(results->rawbuf[offset], HITACHI_AC_MIN_GAP, kTolerance))
return false;

// Compliance
if (strict) {
// Correct size/length)
if (dataBitsSoFar / 8 != HITACHI_AC_STATE_LENGTH) return false;
// Re-check we got the correct size/length due to the way we read the data.
switch (dataBitsSoFar / 8) {
case HITACHI_AC_STATE_LENGTH:
case HITACHI_AC1_STATE_LENGTH:
case HITACHI_AC2_STATE_LENGTH:
break; // Continue
default:
return false;
}
}

// Success
results->decode_type = HITACHI_AC;
switch (dataBitsSoFar) {
case HITACHI_AC1_BITS:
results->decode_type = HITACHI_AC1;
break;
case HITACHI_AC2_BITS:
results->decode_type = HITACHI_AC2;
break;
case HITACHI_AC_BITS:
default:
results->decode_type = HITACHI_AC;
}
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_AC
#endif // (DECODE_HITACHI_AC || DECODE_HITACHI_AC1 || DECODE_HITACHI_AC2)
Loading

0 comments on commit 7f54c54

Please sign in to comment.