Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add basic support for HITACHI_AC3 protocol. #1063

Merged
merged 4 commits into from
Mar 24, 2020
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
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: Tue Mar 17 13:45:51 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_AC3)<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 @@ -107,6 +107,7 @@
- HITACHI_AC
- HITACHI_AC1
- HITACHI_AC2
- HITACHI_AC3
- HITACHI_AC424
- INAX
- JVC
Expand Down
17 changes: 17 additions & 0 deletions examples/IRMQTTServer/IRMQTTServer.ino
Original file line number Diff line number Diff line change
Expand Up @@ -1518,6 +1518,23 @@ bool parseStringAndSendAirCon(IRsend *irsend, const decode_type_t irType,
// Lastly, it should never exceed the maximum "normal" size.
stateSize = std::min(stateSize, kFujitsuAcStateLength);
break;
case HITACHI_AC3:
// HitachiAc3 has two distinct & different size states, so make a best
// guess which one we are being presented with based on the number of
// hexadecimal digits provided. i.e. Zero-pad if you need to to get
// the correct length/byte size.
stateSize = inputLength / 2; // Every two hex chars is a byte.
// Use at least the minimum size.
stateSize = std::max(stateSize,
(uint16_t) (kHitachiAc3MinStateLength));
// If we think it isn't a "short" message.
if (stateSize > kHitachiAc3MinStateLength)
// Then it probably the "normal" size.
stateSize = std::max(stateSize,
(uint16_t) (kHitachiAc3StateLength));
// Lastly, it should never exceed the maximum "normal" size.
stateSize = std::min(stateSize, kHitachiAc3StateLength);
break;
case MWM:
// MWM has variable size states, so make a best guess
// which one we are being presented with based on the number of
Expand Down
26 changes: 20 additions & 6 deletions src/IRrecv.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -641,10 +641,28 @@ 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_MITSUBISHI136
// Needs to happen before HitachiAc3 decode.
DPRINTLN("Attempting Mitsubishi136 decode");
if (decodeMitsubishi136(results, offset)) return true;
#endif // DECODE_MITSUBISHI136
#if DECODE_HITACHI_AC3
// HitachiAc3 should be checked before HitachiAC & HitachiAC2
// Attempt normal before the short version.
DPRINTLN("Attempting Hitachi AC3 decode");
// Order these in decreasing bit size, as it is more optimal.
if (decodeHitachiAc3(results, offset, kHitachiAc3Bits) ||
decodeHitachiAc3(results, offset, kHitachiAc3Bits - 4 * 8) ||
decodeHitachiAc3(results, offset, kHitachiAc3Bits - 6 * 8) ||
decodeHitachiAc3(results, offset, kHitachiAc3MinBits + 2 * 8) ||
decodeHitachiAc3(results, offset, kHitachiAc3MinBits))
return true;
#endif // DECODE_HITACHI_AC3
#if DECODE_HITACHI_AC2
// HitachiAC2 should be checked before HitachiAC
DPRINTLN("Attempting Hitachi AC2 decode");
Expand Down Expand Up @@ -759,10 +777,6 @@ bool IRrecv::decode(decode_results *results, irparams_t *save,
DPRINTLN("Attempting Daikin152 decode");
if (decodeDaikin152(results, offset)) return true;
#endif // DECODE_DAIKIN152
#if DECODE_MITSUBISHI136
DPRINTLN("Attempting Mitsubishi136 decode");
if (decodeMitsubishi136(results, offset)) return true;
#endif // DECODE_MITSUBISHI136
#if DECODE_SYMPHONY
DPRINTLN("Attempting Symphony decode");
if (decodeSymphony(results, offset)) return true;
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_AC3
bool decodeHitachiAc3(decode_results *results,
uint16_t offset = kStartOffset,
const uint16_t nbits = kHitachiAc3Bits,
const bool strict = true);
#endif // DECODE_HITACHI_AC3
#if DECODE_HITACHI_AC424
bool decodeHitachiAc424(decode_results *results,
uint16_t offset = kStartOffset,
Expand Down
30 changes: 21 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_AC3
#define DECODE_HITACHI_AC3 _IR_ENABLE_DEFAULT_
#endif // DECODE_HITACHI_AC3
#ifndef SEND_HITACHI_AC3
#define SEND_HITACHI_AC3 _IR_ENABLE_DEFAULT_
#endif // SEND_HITACHI_AC3

#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_AC3)
#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_AC3,
// Add new entries before this one, and update it to point to the last entry.
kLastDecodeType = SYMPHONY,
kLastDecodeType = HITACHI_AC3,
};

// Message lengths & required repeat values
Expand Down Expand Up @@ -782,6 +790,10 @@ 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 kHitachiAc3StateLength = 27;
const uint16_t kHitachiAc3Bits = kHitachiAc3StateLength * 8;
const uint16_t kHitachiAc3MinStateLength = 15;
const uint16_t kHitachiAc3MinBits = kHitachiAc3MinStateLength * 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_AC3:
return kHitachiAc3Bits;
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_AC3
case HITACHI_AC3:
sendHitachiAc3(state, nbytes);
break;
#endif // SEND_HITACHI_AC3
#if SEND_HITACHI_AC424
case HITACHI_AC424:
sendHitachiAc424(state, nbytes);
Expand Down
6 changes: 6 additions & 0 deletions src/IRsend.h
Original file line number Diff line number Diff line change
Expand Up @@ -471,6 +471,12 @@ class IRsend {
const uint16_t nbytes = kHitachiAc2StateLength,
const uint16_t repeat = kHitachiAcDefaultRepeat);
#endif
#if SEND_HITACHI_AC3
void sendHitachiAc3(const unsigned char data[],
const uint16_t nbytes, // No default as there as so many
// different sizes
const uint16_t repeat = kHitachiAcDefaultRepeat);
#endif // SEND_HITACHI_AC3
#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_AC3"))
return decode_type_t::HITACHI_AC3;
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_AC3:
result = F("HITACHI_AC3");
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_AC3:
case HITACHI_AC424:
case KELVINATOR:
case MITSUBISHI136:
Expand Down
147 changes: 147 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 HitachiAc3 protocol
// Ref: https://github.com/crankyoldgit/IRremoteESP8266/issues/1060
const uint16_t kHitachiAc3HdrMark = 3400; // Header
const uint16_t kHitachiAc3HdrSpace = 1660; // Header
const uint16_t kHitachiAc3BitMark = 460;
const uint16_t kHitachiAc3OneSpace = 1250;
const uint16_t kHitachiAc3ZeroSpace = 410;

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


#if SEND_HITACHI_AC3
// Send HITACHI_AC3 messages
//
// Note: This protocol is almost exactly the same as HitachiAC424 except this
// variant has subtle timing differences.
// There are five(5) typical sizes:
// * kHitachiAc3MinStateLength (Cancel Timer)
// * kHitachiAc3MinStateLength + 2 (Change Temp)
// * kHitachiAc3StateLength - 6 (Change Mode)
// * kHitachiAc3StateLength- 4 (Normal)
// * kHitachiAc3StateLength (Set Timer)
//
// 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.
// repeat: Nr. of times the message is to be repeated.
//
// Status: BETA / Probably working fine.
void IRsend::sendHitachiAc3(const uint8_t data[], const uint16_t nbytes,
const uint16_t repeat) {
// Header + Data + Footer
sendGeneric(kHitachiAc3HdrMark, kHitachiAc3HdrSpace,
kHitachiAc3BitMark, kHitachiAc3OneSpace,
kHitachiAc3BitMark, kHitachiAc3ZeroSpace,
kHitachiAc3BitMark, kHitachiAcMinGap,
data, nbytes, // Bytes
kHitachiAcFreq, false, repeat, kDutyDefault);
}
#endif // SEND_HITACHI_AC3


// Class for handling the remote control on a Hitachi_AC3 53 A/C message
IRHitachiAc3::IRHitachiAc3(const uint16_t pin, const bool inverted,
const bool use_modulation)
: _irsend(pin, inverted, use_modulation) { stateReset(); }

// Reset to auto fan, cooling, 23° Celcius
void IRHitachiAc3::stateReset(void) {
for (uint8_t i = 0; i < kHitachiAc3StateLength; i++)
remote_state[i] = 0x00;
remote_state[0] = 0x01;
remote_state[1] = 0x10;
remote_state[3] = 0x40;
remote_state[5] = 0xFF;
remote_state[7] = 0xE8;
remote_state[9] = 0x89;
remote_state[11] = 0x0B;
remote_state[13] = 0x3F;
remote_state[15] = 0x15;
remote_state[21] = 0x4B;
remote_state[23] = 0x18;
setInvertedStates();
}

void IRHitachiAc3::setInvertedStates(const uint16_t length) {
for (uint8_t i = 3; i < length - 1; i += 2)
remote_state[i + 1] = ~remote_state[i];
}

bool IRHitachiAc3::hasInvertedStates(const uint8_t state[],
const uint16_t length) {
for (uint8_t i = 3; i < length - 1; i += 2)
if ((state[i + 1] ^ state[i]) != 0xFF) return false;
return true;
}

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

uint8_t *IRHitachiAc3::getRaw(void) {
setInvertedStates();
return remote_state;
}

void IRHitachiAc3::setRaw(const uint8_t new_code[], const uint16_t length) {
memcpy(remote_state, new_code, std::min(length, kHitachiAc3StateLength));
}

#if DECODE_HITACHI_AC3
// Decode the supplied HitachiAc3 A/C message.
//
// Note: This protocol is almost exactly the same as HitachiAC424 except this
// variant has subtle timing differences and multiple lengths.
//
// 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 kHitachiAc3Bits.
// 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::decodeHitachiAc3(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) {
// Check the requested bit length.
switch (nbits) {
case kHitachiAc3MinBits: // Cancel Timer (Min Size)
case kHitachiAc3MinBits + 2 * 8: // Change Temp
case kHitachiAc3Bits - 6 * 8: // Change Mode
case kHitachiAc3Bits - 4 * 8: // Normal
case kHitachiAc3Bits: // Set Temp (Max Size)
break;
default: return false;
}
}

// Header + Data + Footer
if (!matchGeneric(results->rawbuf + offset, results->state,
results->rawlen - offset, nbits,
kHitachiAc3HdrMark, kHitachiAc3HdrSpace,
kHitachiAc3BitMark, kHitachiAc3OneSpace,
kHitachiAc3BitMark, kHitachiAc3ZeroSpace,
kHitachiAc3BitMark, kHitachiAcMinGap, true,
kUseDefTol, 0, false))
return false; // We failed to find any data.

// Compliance
if (strict && !IRHitachiAc3::hasInvertedStates(results->state, nbits / 8))
return false;
// Success
results->decode_type = decode_type_t::HITACHI_AC3;
results->bits = nbits;
return true;
}
#endif // DECODE_HITACHI_AC3
Loading