From 61a738934f92a25fe0abb0d278e05fb55518d220 Mon Sep 17 00:00:00 2001 From: Jeff Knaggs Date: Sun, 13 Feb 2022 11:21:41 +1300 Subject: [PATCH 01/15] Initial additions for Hitachi 296 protocol (#1757) As per https://github.com/crankyoldgit/IRremoteESP8266/wiki/Adding-support-for-a-new-IR-protocol --- src/IRremoteESP8266.h | 10 +++++++++- src/IRsend.cpp | 7 +++++++ src/IRsend.h | 5 +++++ src/IRtext.cpp | 1 + src/IRutils.cpp | 1 + src/locale/defaults.h | 3 +++ 6 files changed, 26 insertions(+), 1 deletion(-) diff --git a/src/IRremoteESP8266.h b/src/IRremoteESP8266.h index 94027ff39..471cd7ce5 100644 --- a/src/IRremoteESP8266.h +++ b/src/IRremoteESP8266.h @@ -497,6 +497,13 @@ #define SEND_HITACHI_AC264 _IR_ENABLE_DEFAULT_ #endif // SEND_HITACHI_AC264 +#ifndef DECODE_HITACHI_AC296 +#define DECODE_HITACHI_AC296 _IR_ENABLE_DEFAULT_ +#endif // DECODE_HITACHI_AC296 +#ifndef SEND_HITACHI_AC296 +#define SEND_HITACHI_AC296 _IR_ENABLE_DEFAULT_ +#endif // SEND_HITACHI_AC296 + #ifndef DECODE_HITACHI_AC344 #define DECODE_HITACHI_AC344 _IR_ENABLE_DEFAULT_ #endif // DECODE_HITACHI_AC344 @@ -1022,8 +1029,9 @@ enum decode_type_t { COOLIX48, // 110 HITACHI_AC264, KELON168, + HITACHI_AC296, // Add new entries before this one, and update it to point to the last entry. - kLastDecodeType = KELON168, + kLastDecodeType = HITACHI_AC296, }; // Message lengths & required repeat values diff --git a/src/IRsend.cpp b/src/IRsend.cpp index 6df1aea96..d80b4bbf6 100644 --- a/src/IRsend.cpp +++ b/src/IRsend.cpp @@ -720,6 +720,8 @@ uint16_t IRsend::defaultBits(const decode_type_t protocol) { return kHitachiAc3Bits; case HITACHI_AC264: return kHitachiAc264Bits; + case HITACHI_AC296: + return kHitachiAc296Bits; case HITACHI_AC344: return kHitachiAc344Bits; case HITACHI_AC424: @@ -1219,6 +1221,11 @@ bool IRsend::send(const decode_type_t type, const uint8_t *state, sendHitachiAc264(state, nbytes); break; #endif // SEND_HITACHI_AC264 +#if SEND_HITACHI_AC296 + case HITACHI_AC296: + sendHitachiAc296(state, nbytes); + break; +#endif // SEND_HITACHI_AC296 #if SEND_HITACHI_AC344 case HITACHI_AC344: sendHitachiAc344(state, nbytes); diff --git a/src/IRsend.h b/src/IRsend.h index e51a214b0..7b77c3bc1 100644 --- a/src/IRsend.h +++ b/src/IRsend.h @@ -597,6 +597,11 @@ class IRsend { const uint16_t nbytes = kHitachiAc264StateLength, const uint16_t repeat = kHitachiAcDefaultRepeat); #endif // SEND_HITACHI_AC264 +#if SEND_HITACHI_AC296 + void sendHitachiAc296(const unsigned char data[], + const uint16_t nbytes = kHitachiAc296StateLength, + const uint16_t repeat = kHitachiAcDefaultRepeat); +#endif // SEND_HITACHI_AC296 #if SEND_HITACHI_AC344 void sendHitachiAc344(const unsigned char data[], const uint16_t nbytes = kHitachiAc344StateLength, diff --git a/src/IRtext.cpp b/src/IRtext.cpp index bd65ffd98..edd042b52 100644 --- a/src/IRtext.cpp +++ b/src/IRtext.cpp @@ -393,6 +393,7 @@ IRTEXT_CONST_BLOB_DECL(kAllProtocolNamesStr) { D_STR_COOLIX48 "\x0" D_STR_HITACHI_AC264 "\x0" D_STR_KELON168 "\x0" + D_STR_HITACHI_AC296 "\x0" ///< New protocol strings should be added just above this line. "\x0" ///< This string requires double null termination. }; diff --git a/src/IRutils.cpp b/src/IRutils.cpp index 166813035..0ede6ef13 100644 --- a/src/IRutils.cpp +++ b/src/IRutils.cpp @@ -191,6 +191,7 @@ bool hasACState(const decode_type_t protocol) { case HITACHI_AC2: case HITACHI_AC3: case HITACHI_AC264: + case HITACHI_AC296: case HITACHI_AC344: case HITACHI_AC424: case KELON168: diff --git a/src/locale/defaults.h b/src/locale/defaults.h index 4fb188f6c..cade71219 100644 --- a/src/locale/defaults.h +++ b/src/locale/defaults.h @@ -811,6 +811,9 @@ D_STR_INDIRECT " " D_STR_MODE #ifndef D_STR_HITACHI_AC264 #define D_STR_HITACHI_AC264 D_STR_HITACHI_AC "264" #endif // D_STR_HITACHI_AC264 +#ifndef D_STR_HITACHI_AC296 +#define D_STR_HITACHI_AC296 D_STR_HITACHI_AC "296" +#endif // D_STR_HITACHI_AC296 #ifndef D_STR_HITACHI_AC344 #define D_STR_HITACHI_AC344 D_STR_HITACHI_AC "344" #endif // D_STR_HITACHI_AC344 From 9e0c4a482c0762ca2f115be61bea5adcf23543e6 Mon Sep 17 00:00:00 2001 From: Jeff Knaggs Date: Sun, 13 Feb 2022 14:08:40 +1300 Subject: [PATCH 02/15] Start adding generic implementation (#1757) --- src/IRremoteESP8266.h | 2 + src/ir_Hitachi.cpp | 109 +++++++++++++++++++++++++++++++++++++++++- src/ir_Hitachi.h | 3 ++ 3 files changed, 113 insertions(+), 1 deletion(-) diff --git a/src/IRremoteESP8266.h b/src/IRremoteESP8266.h index 471cd7ce5..4750905d7 100644 --- a/src/IRremoteESP8266.h +++ b/src/IRremoteESP8266.h @@ -1143,6 +1143,8 @@ const uint16_t kHitachiAc3MinStateLength = 15; const uint16_t kHitachiAc3MinBits = kHitachiAc3MinStateLength * 8; const uint16_t kHitachiAc264StateLength = 33; const uint16_t kHitachiAc264Bits = kHitachiAc264StateLength * 8; +const uint16_t kHitachiAc296StateLength = 37; +const uint16_t kHitachiAc296Bits = kHitachiAc296StateLength * 8; const uint16_t kHitachiAc344StateLength = 43; const uint16_t kHitachiAc344Bits = kHitachiAc344StateLength * 8; const uint16_t kHitachiAc424StateLength = 53; diff --git a/src/ir_Hitachi.cpp b/src/ir_Hitachi.cpp index a90ad6bee..266ea92b2 100644 --- a/src/ir_Hitachi.cpp +++ b/src/ir_Hitachi.cpp @@ -7,6 +7,7 @@ /// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1056 /// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1060 /// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1134 +/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1757 #include "ir_Hitachi.h" #include @@ -45,6 +46,9 @@ const uint16_t kHitachiAc3BitMark = 460; const uint16_t kHitachiAc3OneSpace = 1250; const uint16_t kHitachiAc3ZeroSpace = 410; +// Support for HitachiAc296 protocol +const uint16_t kHitachiAc296HdrSpace = 1714; + using irutils::addBoolToString; using irutils::addIntToString; using irutils::addLabeledString; @@ -57,7 +61,7 @@ using irutils::invertBytePairs; using irutils::minsToString; #if (SEND_HITACHI_AC || SEND_HITACHI_AC2 || SEND_HITACHI_AC264 || \ - SEND_HITACHI_AC344) + SEND_HITACHI_AC344 || SEND_HITACHI_AC296) /// Send a Hitachi 28-byte/224-bit A/C formatted message. (HITACHI_AC) /// Status: STABLE / Working. /// @param[in] data The message to be sent. @@ -1704,3 +1708,106 @@ String IRHitachiAc264::toString(void) const { #if DECODE_HITACHI_AC264 // For Decoding HITACHI_AC264, see `decodeHitachiAC` #endif // DECODE_HITACHI_AC264 + + +#if SEND_HITACHIAC296 +/// Send a HitachiAc 37-byte/296-bit A/C message (HITACHI_AC296) +/// Status: ALPHA / Untested. +/// @param[in] data containing the IR command. +/// @param[in] nbits Nr. of bits to send. usually kHitachiAc296Bits +/// @param[in] repeat Nr. of times the message is to be repeated. +void IRsend::sendHitachiAc296(const uint64_t data, const uint16_t nbits, const uint16_t repeat) { + if (nbytes < kHitachiAc264StateLength) + return; // Not enough bytes to send a proper message. + sendHitachiAC(data, nbytes, repeat); +} +#endif // SEND_HITACHIAC296 + +#if DECODE_HITACHIAC296 +// Function should be safe up to 64 bits. +/// Decode the supplied HitachiAc296 message. +/// Status: ALPHA / Untested. +/// @param[in,out] results Ptr to the data to decode & where to store the decode +/// @param[in] offset The starting index to use when attempting to decode the +/// raw data. Typically/Defaults to kStartOffset. +/// @param[in] nbits The number of data bits to expect. +/// @param[in] strict Flag indicating if we should perform strict matching. +/// @return A boolean. True if it can decode it, false if it can't. +bool IRrecv::decodeHitachiAc296(decode_results *results, uint16_t offset, const uint16_t nbits, const bool strict) { + if (results->rawlen < 2 * nbits + kHitachiAc296Overhead - offset) + return false; // Too short a message to match. + if (strict && nbits != kHitachiAc296Bits) + return false; + + uint64_t data = 0; + match_result_t data_result; + + // Header + if (!matchMark(results->rawbuf[offset++], kHitachiAc296HdrMark)) + return false; + if (!matchSpace(results->rawbuf[offset++], kHitachiAc296HdrSpace)) + return false; + + // Data Section #1 + // e.g. data_result.data = 0x80080002FDFF0033CC49B622DD06F900FF00FF00FF00FF00FF6A958F7000FF00FF00FFC03F, nbits = 296 + data_result = matchData(&(results->rawbuf[offset]), 296, + kHitachiAc296BitMark, kHitachiAc296OneSpace, + kHitachiAc296BitMark, kHitachiAc296ZeroSpace); + offset += data_result.used; + if (data_result.success == false) return false; // Fail + data <<= 296; // Make room for the new bits of data. + data |= data_result.data; + + // Footer + if (!matchMark(results->rawbuf[offset++], kHitachiAc296BitMark)) + return false; + + // Success + results->decode_type = decode_type_t::HITACHIAC296; + results->bits = nbits; + results->value = data; + results->command = 0; + results->address = 0; + return true; +} +#endif // DECODE_HITACHIAC296 + +#if DECODE_HITACHIAC296 +// Function should be safe over 64 bits. +/// Decode the supplied HitachiAc296 message. +/// Status: ALPHA / Untested. +/// @param[in,out] results Ptr to the data to decode & where to store the decode +/// @param[in] offset The starting index to use when attempting to decode the +/// raw data. Typically/Defaults to kStartOffset. +/// @param[in] nbits The number of data bits to expect. +/// @param[in] strict Flag indicating if we should perform strict matching. +/// @return A boolean. True if it can decode it, false if it can't. +bool IRrecv::decodeHitachiAc296(decode_results *results, uint16_t offset, const uint16_t nbits, const bool strict) { + if (results->rawlen < 2 * nbits + kHitachiAc296Overhead - offset) + return false; // Too short a message to match. + if (strict && nbits != kHitachiAc296Bits) + return false; + + uint16_t pos = 0; + uint16_t used = 0; + + // Data Section #1 + // e.g. + // bits = 296; bytes = 37; + // *(results->state + pos) = {0x80, 0x08, 0x00, 0x02, 0xFD, 0xFF, 0x00, 0x33, 0xCC, 0x49, 0xB6, 0x22, 0xDD, 0x06, 0xF9, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x6A, 0x95, 0x8F, 0x70, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0xC0, 0x3F}; + used = matchGeneric(results->rawbuf + offset, results->state + pos, + results->rawlen - offset, 296, + kHitachiAc296HdrMark, kHitachiAc296HdrSpace, + kHitachiAc296BitMark, kHitachiAc296OneSpace, + kHitachiAc296BitMark, kHitachiAc296ZeroSpace, + kHitachiAc296BitMark, kDefaultMessageGap, true); + if (used == 0) return false; // We failed to find any data. + offset += used; // Adjust for how much of the message we read. + pos += 37; // Adjust by how many bytes of data we read + + // Success + results->decode_type = decode_type_t::HITACHIAC296; + results->bits = nbits; + return true; +} +#endif // DECODE_HITACHIAC296 diff --git a/src/ir_Hitachi.h b/src/ir_Hitachi.h index 5b9e93f14..50511b6b1 100644 --- a/src/ir_Hitachi.h +++ b/src/ir_Hitachi.h @@ -8,6 +8,7 @@ /// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1060 /// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1134 /// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1729 +/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1757 // Supports: // Brand: Hitachi, Model: RAS-35THA6 remote @@ -22,6 +23,8 @@ // Brand: Hitachi, Model: RF11T1 remote (HITACHI_AC344) // Brand: Hitachi, Model: RAR-2P2 remote (HITACHI_AC264) // Brand: Hitachi, Model: RAK-25NH5 A/C (HITACHI_AC264) +// Brand: Hitachi, Model: RAR-3U3 remote (HITACHI_AC296) +// Brand: Hitachi, Model: RAS-70YHA3 A/C (HITACHI_AC296) #ifndef IR_HITACHI_H_ #define IR_HITACHI_H_ From 5b935081a711bf033fdaca5fe5420e49130d766d Mon Sep 17 00:00:00 2001 From: Jeff Knaggs Date: Sun, 13 Feb 2022 14:33:31 +1300 Subject: [PATCH 03/15] Trying to get decode working (#1757) --- src/IRrecv.cpp | 6 +++ src/IRrecv.h | 2 +- src/ir_Hitachi.cpp | 93 ++++++---------------------------------------- src/ir_Hitachi.h | 78 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 97 insertions(+), 82 deletions(-) diff --git a/src/IRrecv.cpp b/src/IRrecv.cpp index 5626db2c7..e44355355 100644 --- a/src/IRrecv.cpp +++ b/src/IRrecv.cpp @@ -836,6 +836,12 @@ bool IRrecv::decode(decode_results *results, irparams_t *save, if (decodeHitachiAC(results, offset, kHitachiAc264Bits, true, false)) return true; #endif // DECODE_HITACHI_AC264 +#if DECODE_HITACHI_AC296 + // HitachiAC296 should be checked before HitachiAC + DPRINTLN("Attempting Hitachi AC296 decode"); + if (decodeHitachiAC(results, offset, kHitachiAc296Bits, true, false)) + return true; +#endif // DECODE_HITACHI_AC296 #if DECODE_HITACHI_AC2 // HitachiAC2 should be checked before HitachiAC DPRINTLN("Attempting Hitachi AC2 decode"); diff --git a/src/IRrecv.h b/src/IRrecv.h index f9ff4b0ef..804926b9c 100644 --- a/src/IRrecv.h +++ b/src/IRrecv.h @@ -591,7 +591,7 @@ class IRrecv { const bool strict = true); #endif // DECODE_HAIER_AC176 #if (DECODE_HITACHI_AC || DECODE_HITACHI_AC2 || DECODE_HITACHI_AC264 || \ - DECODE_HITACHI_AC344) + DECODE_HITACHI_AC296 || DECODE_HITACHI_AC344) bool decodeHitachiAC(decode_results *results, uint16_t offset = kStartOffset, const uint16_t nbits = kHitachiAcBits, const bool strict = true, const bool MSBfirst = true); diff --git a/src/ir_Hitachi.cpp b/src/ir_Hitachi.cpp index 266ea92b2..6266f32da 100644 --- a/src/ir_Hitachi.cpp +++ b/src/ir_Hitachi.cpp @@ -1724,89 +1724,20 @@ void IRsend::sendHitachiAc296(const uint64_t data, const uint16_t nbits, const u #endif // SEND_HITACHIAC296 #if DECODE_HITACHIAC296 -// Function should be safe up to 64 bits. -/// Decode the supplied HitachiAc296 message. -/// Status: ALPHA / Untested. -/// @param[in,out] results Ptr to the data to decode & where to store the decode -/// @param[in] offset The starting index to use when attempting to decode the -/// raw data. Typically/Defaults to kStartOffset. -/// @param[in] nbits The number of data bits to expect. -/// @param[in] strict Flag indicating if we should perform strict matching. -/// @return A boolean. True if it can decode it, false if it can't. -bool IRrecv::decodeHitachiAc296(decode_results *results, uint16_t offset, const uint16_t nbits, const bool strict) { - if (results->rawlen < 2 * nbits + kHitachiAc296Overhead - offset) - return false; // Too short a message to match. - if (strict && nbits != kHitachiAc296Bits) - return false; - - uint64_t data = 0; - match_result_t data_result; - - // Header - if (!matchMark(results->rawbuf[offset++], kHitachiAc296HdrMark)) - return false; - if (!matchSpace(results->rawbuf[offset++], kHitachiAc296HdrSpace)) - return false; - - // Data Section #1 - // e.g. data_result.data = 0x80080002FDFF0033CC49B622DD06F900FF00FF00FF00FF00FF6A958F7000FF00FF00FFC03F, nbits = 296 - data_result = matchData(&(results->rawbuf[offset]), 296, - kHitachiAc296BitMark, kHitachiAc296OneSpace, - kHitachiAc296BitMark, kHitachiAc296ZeroSpace); - offset += data_result.used; - if (data_result.success == false) return false; // Fail - data <<= 296; // Make room for the new bits of data. - data |= data_result.data; - - // Footer - if (!matchMark(results->rawbuf[offset++], kHitachiAc296BitMark)) - return false; - - // Success - results->decode_type = decode_type_t::HITACHIAC296; - results->bits = nbits; - results->value = data; - results->command = 0; - results->address = 0; - return true; -} -#endif // DECODE_HITACHIAC296 - -#if DECODE_HITACHIAC296 -// Function should be safe over 64 bits. -/// Decode the supplied HitachiAc296 message. -/// Status: ALPHA / Untested. -/// @param[in,out] results Ptr to the data to decode & where to store the decode -/// @param[in] offset The starting index to use when attempting to decode the -/// raw data. Typically/Defaults to kStartOffset. -/// @param[in] nbits The number of data bits to expect. -/// @param[in] strict Flag indicating if we should perform strict matching. -/// @return A boolean. True if it can decode it, false if it can't. -bool IRrecv::decodeHitachiAc296(decode_results *results, uint16_t offset, const uint16_t nbits, const bool strict) { - if (results->rawlen < 2 * nbits + kHitachiAc296Overhead - offset) - return false; // Too short a message to match. - if (strict && nbits != kHitachiAc296Bits) - return false; +bool IRrecv::decodeHitachiAc296(decode_results *results, uint16_t offset, + const uint16_t nbits, + const bool strict) { - uint16_t pos = 0; - uint16_t used = 0; - - // Data Section #1 - // e.g. - // bits = 296; bytes = 37; - // *(results->state + pos) = {0x80, 0x08, 0x00, 0x02, 0xFD, 0xFF, 0x00, 0x33, 0xCC, 0x49, 0xB6, 0x22, 0xDD, 0x06, 0xF9, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x6A, 0x95, 0x8F, 0x70, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0xC0, 0x3F}; - used = matchGeneric(results->rawbuf + offset, results->state + pos, - results->rawlen - offset, 296, - kHitachiAc296HdrMark, kHitachiAc296HdrSpace, - kHitachiAc296BitMark, kHitachiAc296OneSpace, - kHitachiAc296BitMark, kHitachiAc296ZeroSpace, - kHitachiAc296BitMark, kDefaultMessageGap, true); - if (used == 0) return false; // We failed to find any data. - offset += used; // Adjust for how much of the message we read. - pos += 37; // Adjust by how many bytes of data we read + uint16_t used = matchGeneric(results->rawbuf + offset, results->state, + results->rawlen - offset, nbits, + kHitachiAc296HdrMark, kHitachiAc296HdrSpace, + kHitachiAc296BitMark, kHitachiAc296OneSpace, + kHitachiAc296BitMark, kHitachiAc296ZeroSpace, + kHitachiAc296BitMark, kHitachiAcMinGap, true, + kUseDefTol, 0, false); + if (used == 0) return false; - // Success - results->decode_type = decode_type_t::HITACHIAC296; + results->decode_type = decode_type_t::HITACHI_AC296; results->bits = nbits; return true; } diff --git a/src/ir_Hitachi.h b/src/ir_Hitachi.h index 50511b6b1..2c73ff448 100644 --- a/src/ir_Hitachi.h +++ b/src/ir_Hitachi.h @@ -296,6 +296,84 @@ const uint8_t kHitachiAc264FanMedium = kHitachiAc424FanMedium; const uint8_t kHitachiAc264FanHigh = kHitachiAc424FanHigh; const uint8_t kHitachiAc264FanAuto = kHitachiAc424FanAuto; +// HitachiAc296 +union HitachiAC296Protocol{ + uint8_t raw[kHitachiAc296StateLength]; + struct { + // Byte 1~13 + uint8_t pad0[13]; + // Byte 14&15 + uint8_t :2; + uint8_t Temp :5; // storedf in LSB order. + uint8_t :1; + uint8_t :2; + uint8_t TempParity :5; // stored in LSB order. + uint8_t :1; + // Byte 16~17 + uint8_t :8; + uint8_t :8; + // Byte 18~21 + uint8_t :4; + uint8_t OffTimerLow :4; // LSB + uint8_t OffTimerLowParity :8; + uint8_t OffTimerHigh :8; + uint8_t OffTimerHighParity :8; + // Byte 22~25 + uint8_t OnTimerLow :8; // LSB + uint8_t OnTimerLowParity :8; + uint8_t OnTimerHigh :4; + uint8_t OffTimerActive :1; + uint8_t OnTimerActive :1; + uint8_t :2; + uint8_t OnTimerHighParity :6; + uint8_t :2; + // Byte 26~27 + uint8_t Mode :4; + uint8_t Fan :3; + uint8_t :1; + uint8_t ModeParity :4; + uint8_t FanParity :3; + uint8_t :1; + // Byte 28~29 + uint8_t :4; + uint8_t Power :1; + uint8_t :2; + uint8_t TimerActive :1; + uint8_t :4; + uint8_t PowerParity :1; + uint8_t :2; + uint8_t TimerActiveParity :1; + // Byte 30~35 + uint8_t pad1[6]; + // Byte 36~37 + uint8_t :4; + uint8_t Humidity :4; + uint8_t :4; + uint8_t HumidityParity :4; + }; +}; + +// Mode & Fan +const uint8_t kHitachiAc296Cool = 0b1100; // 3 +const uint8_t kHitachiAc296Dry = 0b0010; // 4 +const uint8_t kHitachiAc296Heat = 0b0110; // 6 +const uint8_t kHitachiAc296Auto = 0b1110; // 7 +const uint8_t kHitachiAc296CondensationControl = 0b1110; // 12 + +const uint8_t kHitachiAc296FanSilent = 0b100; // 1 +const uint8_t kHitachiAc296FanLow = 0b010; // 2 +const uint8_t kHitachiAc296FanMedium = 0b110; // 3 +const uint8_t kHitachiAc296FanHigh = 0b001; // 4 +const uint8_t kHitachiAc296FanAuto = 0b101; // 5 + +const uint8_t kHitachiAc296TempSize = 5; +const uint8_t kHitachiAc296MinTemp = 16; +const uint8_t kHitachiAc296MaxTemp = 32; + +const uint8_t kHitachiAc296PowerOn = 1; +const uint8_t kHitachiAc296PowerOff = 0; + + // Classes /// Class for handling detailed Hitachi 224-bit A/C messages. /// @see https://github.com/ToniA/arduino-heatpumpir/blob/master/HitachiHeatpumpIR.cpp From 8d82aef9c491960dca9ff4c5b0e80216e21f57ba Mon Sep 17 00:00:00 2001 From: Jeff Knaggs Date: Sun, 13 Feb 2022 15:01:23 +1300 Subject: [PATCH 04/15] Fix a few typos (#1757) --- src/IRrecv.cpp | 2 +- src/IRrecv.h | 8 +++++++- src/ir_Hitachi.cpp | 19 +++++++++---------- 3 files changed, 17 insertions(+), 12 deletions(-) diff --git a/src/IRrecv.cpp b/src/IRrecv.cpp index e44355355..019297213 100644 --- a/src/IRrecv.cpp +++ b/src/IRrecv.cpp @@ -839,7 +839,7 @@ bool IRrecv::decode(decode_results *results, irparams_t *save, #if DECODE_HITACHI_AC296 // HitachiAC296 should be checked before HitachiAC DPRINTLN("Attempting Hitachi AC296 decode"); - if (decodeHitachiAC(results, offset, kHitachiAc296Bits, true, false)) + if (decodeHitachiAc296(results, offset, kHitachiAc296Bits, true)) return true; #endif // DECODE_HITACHI_AC296 #if DECODE_HITACHI_AC2 diff --git a/src/IRrecv.h b/src/IRrecv.h index 804926b9c..f6df07057 100644 --- a/src/IRrecv.h +++ b/src/IRrecv.h @@ -591,7 +591,7 @@ class IRrecv { const bool strict = true); #endif // DECODE_HAIER_AC176 #if (DECODE_HITACHI_AC || DECODE_HITACHI_AC2 || DECODE_HITACHI_AC264 || \ - DECODE_HITACHI_AC296 || DECODE_HITACHI_AC344) + DECODE_HITACHI_AC344) bool decodeHitachiAC(decode_results *results, uint16_t offset = kStartOffset, const uint16_t nbits = kHitachiAcBits, const bool strict = true, const bool MSBfirst = true); @@ -608,6 +608,12 @@ class IRrecv { const uint16_t nbits = kHitachiAc3Bits, const bool strict = true); #endif // DECODE_HITACHI_AC3 +#if DECODE_HITACHI_AC296 + bool decodeHitachiAc296(decode_results *results, + uint16_t offset = kStartOffset, + const uint16_t nbits = kHitachiAc296Bits, + const bool strict = true); +#endif // DECODE_HITACHI_AC296 #if DECODE_HITACHI_AC424 bool decodeHitachiAc424(decode_results *results, uint16_t offset = kStartOffset, diff --git a/src/ir_Hitachi.cpp b/src/ir_Hitachi.cpp index 6266f32da..ee388517b 100644 --- a/src/ir_Hitachi.cpp +++ b/src/ir_Hitachi.cpp @@ -47,7 +47,6 @@ const uint16_t kHitachiAc3OneSpace = 1250; const uint16_t kHitachiAc3ZeroSpace = 410; // Support for HitachiAc296 protocol -const uint16_t kHitachiAc296HdrSpace = 1714; using irutils::addBoolToString; using irutils::addIntToString; @@ -61,7 +60,7 @@ using irutils::invertBytePairs; using irutils::minsToString; #if (SEND_HITACHI_AC || SEND_HITACHI_AC2 || SEND_HITACHI_AC264 || \ - SEND_HITACHI_AC344 || SEND_HITACHI_AC296) + SEND_HITACHI_AC344) /// Send a Hitachi 28-byte/224-bit A/C formatted message. (HITACHI_AC) /// Status: STABLE / Working. /// @param[in] data The message to be sent. @@ -1710,30 +1709,30 @@ String IRHitachiAc264::toString(void) const { #endif // DECODE_HITACHI_AC264 -#if SEND_HITACHIAC296 +#if SEND_HITACHI_AC296 /// Send a HitachiAc 37-byte/296-bit A/C message (HITACHI_AC296) /// Status: ALPHA / Untested. /// @param[in] data containing the IR command. /// @param[in] nbits Nr. of bits to send. usually kHitachiAc296Bits /// @param[in] repeat Nr. of times the message is to be repeated. -void IRsend::sendHitachiAc296(const uint64_t data, const uint16_t nbits, const uint16_t repeat) { +void IRsend::sendHitachiAc296(const unsigned char data[], const uint16_t nbytes, const uint16_t repeat) { if (nbytes < kHitachiAc264StateLength) return; // Not enough bytes to send a proper message. sendHitachiAC(data, nbytes, repeat); } #endif // SEND_HITACHIAC296 -#if DECODE_HITACHIAC296 +#if DECODE_HITACHI_AC296 bool IRrecv::decodeHitachiAc296(decode_results *results, uint16_t offset, const uint16_t nbits, const bool strict) { uint16_t used = matchGeneric(results->rawbuf + offset, results->state, results->rawlen - offset, nbits, - kHitachiAc296HdrMark, kHitachiAc296HdrSpace, - kHitachiAc296BitMark, kHitachiAc296OneSpace, - kHitachiAc296BitMark, kHitachiAc296ZeroSpace, - kHitachiAc296BitMark, kHitachiAcMinGap, true, + kHitachiAcHdrMark, kHitachiAcHdrSpace, + kHitachiAcBitMark, kHitachiAcOneSpace, + kHitachiAcBitMark, kHitachiAcZeroSpace, + kHitachiAcBitMark, kHitachiAcMinGap, true, kUseDefTol, 0, false); if (used == 0) return false; @@ -1741,4 +1740,4 @@ bool IRrecv::decodeHitachiAc296(decode_results *results, uint16_t offset, results->bits = nbits; return true; } -#endif // DECODE_HITACHIAC296 +#endif // DECODE_HITACHI_AC296 From 2ef4a0dc7e46c297bb56d47a3880c2256982c245 Mon Sep 17 00:00:00 2001 From: Jeff Knaggs Date: Mon, 14 Feb 2022 00:23:49 +1300 Subject: [PATCH 05/15] Implementing some class methods (#1757) --- src/ir_Hitachi.cpp | 125 +++++++++++++++++++++++++++++++++++++++++++++ src/ir_Hitachi.h | 92 ++++++++++++++++++++++----------- 2 files changed, 186 insertions(+), 31 deletions(-) diff --git a/src/ir_Hitachi.cpp b/src/ir_Hitachi.cpp index ee388517b..f1e8f16b9 100644 --- a/src/ir_Hitachi.cpp +++ b/src/ir_Hitachi.cpp @@ -1722,6 +1722,131 @@ void IRsend::sendHitachiAc296(const unsigned char data[], const uint16_t nbytes, } #endif // SEND_HITACHIAC296 +IRHitachiAc296::IRHitachiAc296(const uint16_t pin, const bool inverted, + const bool use_modulation) + : _irsend(pin, inverted, use_modulation) { stateReset(); } + +void IRHitachiAc296::stateReset(void) { + // Header + _.raw[0] = 0x01; + _.raw[1] = 0x10; + _.raw[2] = 0x00; + + // Every next byte is a parity byte + _.raw[3] = 0x40; + _.raw[5] = 0xFF; + _.raw[7] = 0xCC; + _.raw[9] = 0x92; + _.raw[11] = 0x44; + // 13-14 is Temperature and parity + _.raw[15] = 0x00; + _.raw[17] = 0x00; // Off timer LSB + _.raw[19] = 0x00; // Off timer cont + _.raw[21] = 0x00; // On timer LSB + _.raw[23] = 0x00; // On timer cont + // 25-26 is Mode and fan + // 27-28 is Power + _.raw[29] = 0x00; + _.raw[31] = 0x00; + _.raw[33] = 0x00; + _.raw[35] = 0x03; // Humidity + + setTemp(23); + setPower(true); + setMode(kHitachiAc296Cool); + setFan(kHitachiAc296FanAuto); + + setInvertedStates(); +} + +/// Update the internal consistency check for the protocol. +void IRHitachiAc296::setInvertedStates(void) { + invertBytePairs(_.raw + 3, kHitachiAc296StateLength - 3); +} + +/// Get the value of the current power setting. +/// @return true, the setting is on. false, the setting is off. +bool IRHitachiAc296::getPower(void) const { + return _.Power; +} + +/// Change the power setting. +/// @param[in] on true, the setting is on. false, the setting is off. +void IRHitachiAc296::setPower(const bool on) { + _.Power = on; +} + +/// Change the power setting to On. +void IRHitachiAc296::on(void) { setPower(true); } + +/// Change the power setting to Off. +void IRHitachiAc296::off(void) { setPower(false); } + +/// Get the operating mode setting of the A/C. +/// @return The current operating mode setting. +uint8_t IRHitachiAc296::getMode(void) const { + return _.Mode; +} + +/// Set the operating mode of the A/C. +/// @param[in] mode The desired operating mode. +void IRHitachiAc296::setMode(const uint8_t mode) { + uint8_t newMode = mode; + switch (mode) { + case kHitachiAc296Heat: + case kHitachiAc296Cool: + case kHitachiAc296Auto: break; + default: newMode = kHitachiAc296Auto; + } + + _.Mode = newMode; +} + +/// Get the current temperature setting. +/// @return The current setting for temp. in degrees celsius. +uint8_t IRHitachiAc296::getTemp(void) const { + return reverseBits(_.Temp, kHitachiAc296TempSize) << 1; +} + +/// Set the temperature. +/// @param[in] celsius The temperature in degrees celsius. +void IRHitachiAc296::setTemp(const uint8_t celsius) { + uint8_t temp; + temp = std::min(celsius, kHitachiAc296MaxTemp); + temp = std::max(temp, kHitachiAc296MinTemp); + _.Temp = reverseBits(temp << 1, kHitachiAc296TempSize); +} + +/// Get the current fan speed setting. +/// @return The current fan speed. +uint8_t IRHitachiAc296::getFan(void) const { + return _.Fan; +} + +/// Set the speed of the fan. +/// @param[in] speed The desired setting. +void IRHitachiAc296::setFan(const uint8_t speed) { + uint8_t newSpeed = speed; + newSpeed = std::max(newSpeed, kHitachiAc296FanSilent); + newSpeed = std::min(newSpeed, kHitachiAc296FanAuto); + + _.Fan = newSpeed; +} + +/// Get a PTR to the internal state/code for this protocol. +/// @return PTR to a code for this protocol based on the current internal state. +uint8_t *IRHitachiAc296::getRaw(void) { + setInvertedStates(); + return _.raw; +} + +/// Set the internal state from a valid code for this protocol. +/// @param[in] new_code A valid code for this protocol. +/// @param[in] length Size (in bytes) of the code for this protocol. +void IRHitachiAc296::setRaw(const uint8_t new_code[], const uint16_t length) { + memcpy(_.raw, new_code, std::min(length, kHitachiAc264StateLength)); +} + #if DECODE_HITACHI_AC296 bool IRrecv::decodeHitachiAc296(decode_results *results, uint16_t offset, const uint16_t nbits, diff --git a/src/ir_Hitachi.h b/src/ir_Hitachi.h index 2c73ff448..b3d516ddd 100644 --- a/src/ir_Hitachi.h +++ b/src/ir_Hitachi.h @@ -300,56 +300,44 @@ const uint8_t kHitachiAc264FanAuto = kHitachiAc424FanAuto; union HitachiAC296Protocol{ uint8_t raw[kHitachiAc296StateLength]; struct { - // Byte 1~13 + // Byte 0~12 uint8_t pad0[13]; - // Byte 14&15 - uint8_t :2; - uint8_t Temp :5; // storedf in LSB order. - uint8_t :1; + // Byte 13 uint8_t :2; - uint8_t TempParity :5; // stored in LSB order. + uint8_t Temp :5; // stored in LSB order. uint8_t :1; - // Byte 16~17 uint8_t :8; + // Byte 15~16 uint8_t :8; - // Byte 18~21 - uint8_t :4; - uint8_t OffTimerLow :4; // LSB - uint8_t OffTimerLowParity :8; + uint8_t :8; + // Byte 17~24 + uint8_t OffTimerLow :8; // LSB + uint8_t /* Parity */ :8; uint8_t OffTimerHigh :8; - uint8_t OffTimerHighParity :8; - // Byte 22~25 + uint8_t /* Parity */ :8; uint8_t OnTimerLow :8; // LSB - uint8_t OnTimerLowParity :8; + uint8_t /* Parity */ :8; uint8_t OnTimerHigh :4; uint8_t OffTimerActive :1; uint8_t OnTimerActive :1; uint8_t :2; - uint8_t OnTimerHighParity :6; - uint8_t :2; - // Byte 26~27 + uint8_t /* Parity */ :8; + // Byte 25~26 uint8_t Mode :4; uint8_t Fan :3; uint8_t :1; - uint8_t ModeParity :4; - uint8_t FanParity :3; - uint8_t :1; - // Byte 28~29 + uint8_t :8; + // Byte 27~28 uint8_t :4; uint8_t Power :1; uint8_t :2; uint8_t TimerActive :1; - uint8_t :4; - uint8_t PowerParity :1; - uint8_t :2; - uint8_t TimerActiveParity :1; - // Byte 30~35 + uint8_t :8; + // Byte 29~34 uint8_t pad1[6]; - // Byte 36~37 - uint8_t :4; - uint8_t Humidity :4; - uint8_t :4; - uint8_t HumidityParity :4; + // Byte 35~36 + uint8_t Humidity :8; // LSB + uint8_t :8; }; }; @@ -628,4 +616,46 @@ class IRHitachiAc264: public IRHitachiAc424 { #endif // SEND_HITACHI_AC264 String toString(void) const override; }; + +class IRHitachiAc296 { + public: + explicit IRHitachiAc296(const uint16_t pin, const bool inverted = false, + const bool use_modulation = true); + void stateReset(void); + +#if SEND_HITACHI_AC296 + void send(const uint16_t repeat = kHitachiAcDefaultRepeat); +#endif + void begin(void); + void on(void); + void off(void); + void setPower(const bool on); + bool getPower(void) const; + void setTemp(const uint8_t temp); + uint8_t getTemp(void) const; + void setFan(const uint8_t speed); + uint8_t getFan(void) const; + void setMode(const uint8_t mode); + uint8_t getMode(void) const; + /* void setSwingVertical(const bool on); */ + /* bool getSwingVertical(void) const; */ + /* void setSwingHorizontal(const bool on); */ + /* bool getSwingHorizontal(void) const; */ + uint8_t* getRaw(void); + void setRaw(const uint8_t new_code[], + const uint16_t length = kHitachiAc296StateLength); + +#ifndef UNIT_TEST + + private: + IRsend _irsend; ///< Instance of the IR send class +#else // UNIT_TEST + /// @cond IGNORE + IRsendTest _irsend; ///< Instance of the testing IR send class + /// @endcond +#endif // UNIT_TEST + + HitachiAC296Protocol _; + void setInvertedStates(void); +}; #endif // IR_HITACHI_H_ From ddb5961a26f533f3f4f37182155ef046c54d5197 Mon Sep 17 00:00:00 2001 From: Jeff Knaggs Date: Sun, 20 Feb 2022 16:28:32 +1300 Subject: [PATCH 06/15] Fix setTemp (#1757) --- src/IRremoteESP8266.h | 2 ++ src/ir_Hitachi.cpp | 31 ++++++++++++++++++++++--------- src/ir_Hitachi.h | 38 +++++++++++++++++++++----------------- 3 files changed, 45 insertions(+), 26 deletions(-) diff --git a/src/IRremoteESP8266.h b/src/IRremoteESP8266.h index 4750905d7..f103b6161 100644 --- a/src/IRremoteESP8266.h +++ b/src/IRremoteESP8266.h @@ -1314,6 +1314,8 @@ const uint16_t kRhossDefaultRepeat = 0; #define HITACHI_AC1_BITS kHitachiAc1Bits #define HITACHI_AC2_STATE_LENGTH kHitachiAc2StateLength #define HITACHI_AC2_BITS kHitachiAc2Bits +#define HITACHI_AC296_STATE_LENGTH kHitachiAc296StateLength +#define HITACHI_AC296_BITS kHitachiAc296Bits #define JVC_BITS kJvcBits #define KELVINATOR_STATE_LENGTH kKelvinatorStateLength #define LASERTAG_BITS kLasertagBits diff --git a/src/ir_Hitachi.cpp b/src/ir_Hitachi.cpp index f1e8f16b9..94abe63f4 100644 --- a/src/ir_Hitachi.cpp +++ b/src/ir_Hitachi.cpp @@ -1716,7 +1716,7 @@ String IRHitachiAc264::toString(void) const { /// @param[in] nbits Nr. of bits to send. usually kHitachiAc296Bits /// @param[in] repeat Nr. of times the message is to be repeated. void IRsend::sendHitachiAc296(const unsigned char data[], const uint16_t nbytes, const uint16_t repeat) { - if (nbytes < kHitachiAc264StateLength) + if (nbytes < kHitachiAc296StateLength) return; // Not enough bytes to send a proper message. sendHitachiAC(data, nbytes, repeat); } @@ -1737,7 +1737,7 @@ void IRHitachiAc296::stateReset(void) { _.raw[5] = 0xFF; _.raw[7] = 0xCC; _.raw[9] = 0x92; - _.raw[11] = 0x44; + _.raw[11] = 0x43; // 13-14 is Temperature and parity _.raw[15] = 0x00; _.raw[17] = 0x00; // Off timer LSB @@ -1745,15 +1745,14 @@ void IRHitachiAc296::stateReset(void) { _.raw[21] = 0x00; // On timer LSB _.raw[23] = 0x00; // On timer cont // 25-26 is Mode and fan - // 27-28 is Power + _.raw[27] = 0xF1; // Power on _.raw[29] = 0x00; _.raw[31] = 0x00; _.raw[33] = 0x00; _.raw[35] = 0x03; // Humidity - setTemp(23); - setPower(true); - setMode(kHitachiAc296Cool); + setTemp(24); + setMode(kHitachiAc296Heat); setFan(kHitachiAc296FanAuto); setInvertedStates(); @@ -1764,6 +1763,19 @@ void IRHitachiAc296::setInvertedStates(void) { invertBytePairs(_.raw + 3, kHitachiAc296StateLength - 3); } +/// Set up hardware to be able to send a message. +void IRHitachiAc296::begin(void) { _irsend.begin(); } + +#if SEND_HITACHI_AC296 +/// Send the current internal state as an IR message. +/// @param[in] repeat Nr. of times the message will be repeated. +void IRHitachiAc296::send(const uint16_t repeat) { + _irsend.sendHitachiAc296(getRaw(), kHitachiAc296StateLength, repeat); +} +#endif // SEND_HITACHI_AC296 + +//begin + /// Get the value of the current power setting. /// @return true, the setting is on. false, the setting is off. bool IRHitachiAc296::getPower(void) const { @@ -1805,7 +1817,7 @@ void IRHitachiAc296::setMode(const uint8_t mode) { /// Get the current temperature setting. /// @return The current setting for temp. in degrees celsius. uint8_t IRHitachiAc296::getTemp(void) const { - return reverseBits(_.Temp, kHitachiAc296TempSize) << 1; + return reverseBits(_.Temp, kHitachiAc296TempSize) >> 2; } /// Set the temperature. @@ -1814,7 +1826,8 @@ void IRHitachiAc296::setTemp(const uint8_t celsius) { uint8_t temp; temp = std::min(celsius, kHitachiAc296MaxTemp); temp = std::max(temp, kHitachiAc296MinTemp); - _.Temp = reverseBits(temp << 1, kHitachiAc296TempSize); + temp = reverseBits(temp << 2, kHitachiAc296TempSize); + _.Temp = temp; } /// Get the current fan speed setting. @@ -1844,7 +1857,7 @@ uint8_t *IRHitachiAc296::getRaw(void) { /// @param[in] new_code A valid code for this protocol. /// @param[in] length Size (in bytes) of the code for this protocol. void IRHitachiAc296::setRaw(const uint8_t new_code[], const uint16_t length) { - memcpy(_.raw, new_code, std::min(length, kHitachiAc264StateLength)); + memcpy(_.raw, new_code, std::min(length, kHitachiAc296StateLength)); } #if DECODE_HITACHI_AC296 diff --git a/src/ir_Hitachi.h b/src/ir_Hitachi.h index b3d516ddd..17705d12e 100644 --- a/src/ir_Hitachi.h +++ b/src/ir_Hitachi.h @@ -304,14 +304,14 @@ union HitachiAC296Protocol{ uint8_t pad0[13]; // Byte 13 uint8_t :2; - uint8_t Temp :5; // stored in LSB order. + uint8_t Temp :5; // LSB uint8_t :1; uint8_t :8; // Byte 15~16 uint8_t :8; uint8_t :8; // Byte 17~24 - uint8_t OffTimerLow :8; // LSB + uint8_t OffTimerLow :8; // LSB uint8_t /* Parity */ :8; uint8_t OffTimerHigh :8; uint8_t /* Parity */ :8; @@ -336,29 +336,33 @@ union HitachiAC296Protocol{ // Byte 29~34 uint8_t pad1[6]; // Byte 35~36 - uint8_t Humidity :8; // LSB + uint8_t :4; + uint8_t Humidity :4; // LSB uint8_t :8; }; }; // Mode & Fan -const uint8_t kHitachiAc296Cool = 0b1100; // 3 -const uint8_t kHitachiAc296Dry = 0b0010; // 4 -const uint8_t kHitachiAc296Heat = 0b0110; // 6 -const uint8_t kHitachiAc296Auto = 0b1110; // 7 -const uint8_t kHitachiAc296CondensationControl = 0b1110; // 12 - -const uint8_t kHitachiAc296FanSilent = 0b100; // 1 -const uint8_t kHitachiAc296FanLow = 0b010; // 2 -const uint8_t kHitachiAc296FanMedium = 0b110; // 3 -const uint8_t kHitachiAc296FanHigh = 0b001; // 4 -const uint8_t kHitachiAc296FanAuto = 0b101; // 5 +const uint8_t kHitachiAc296Cool = 0b0011; +const uint8_t kHitachiAc296DryCool = 0b0100; +const uint8_t kHitachiAc296Dehumidify = 0b0101; +const uint8_t kHitachiAc296Heat = 0b0110; +const uint8_t kHitachiAc296Auto = 0b0111; +const uint8_t kHitachiAc296AutoDehumidifying = 0b1001; +const uint8_t kHitachiAc296QuickLaundry = 0b1010; +const uint8_t kHitachiAc296CondensationControl = 0b1100; + +const uint8_t kHitachiAc296FanSilent = 0b001; +const uint8_t kHitachiAc296FanLow = 0b010; +const uint8_t kHitachiAc296FanMedium = 0b011; +const uint8_t kHitachiAc296FanHigh = 0b100; +const uint8_t kHitachiAc296FanAuto = 0b101; const uint8_t kHitachiAc296TempSize = 5; -const uint8_t kHitachiAc296MinTemp = 16; -const uint8_t kHitachiAc296MaxTemp = 32; +const uint8_t kHitachiAc296MinTemp = 16; +const uint8_t kHitachiAc296MaxTemp = 32; -const uint8_t kHitachiAc296PowerOn = 1; +const uint8_t kHitachiAc296PowerOn = 1; const uint8_t kHitachiAc296PowerOff = 0; From 9118a86bd36c58b620ee66248b77d148c3693597 Mon Sep 17 00:00:00 2001 From: Jeff Knaggs Date: Mon, 21 Feb 2022 09:25:21 +1300 Subject: [PATCH 07/15] Fix some linting issues (#1757) --- src/ir_Hitachi.cpp | 20 +++++++++----------- src/ir_Hitachi.h | 18 +++++++++--------- 2 files changed, 18 insertions(+), 20 deletions(-) diff --git a/src/ir_Hitachi.cpp b/src/ir_Hitachi.cpp index 94abe63f4..4e00eeb79 100644 --- a/src/ir_Hitachi.cpp +++ b/src/ir_Hitachi.cpp @@ -46,8 +46,6 @@ const uint16_t kHitachiAc3BitMark = 460; const uint16_t kHitachiAc3OneSpace = 1250; const uint16_t kHitachiAc3ZeroSpace = 410; -// Support for HitachiAc296 protocol - using irutils::addBoolToString; using irutils::addIntToString; using irutils::addLabeledString; @@ -1715,7 +1713,9 @@ String IRHitachiAc264::toString(void) const { /// @param[in] data containing the IR command. /// @param[in] nbits Nr. of bits to send. usually kHitachiAc296Bits /// @param[in] repeat Nr. of times the message is to be repeated. -void IRsend::sendHitachiAc296(const unsigned char data[], const uint16_t nbytes, const uint16_t repeat) { +void IRsend::sendHitachiAc296(const unsigned char data[], + const uint16_t nbytes, + const uint16_t repeat) { if (nbytes < kHitachiAc296StateLength) return; // Not enough bytes to send a proper message. sendHitachiAC(data, nbytes, repeat); @@ -1740,16 +1740,16 @@ void IRHitachiAc296::stateReset(void) { _.raw[11] = 0x43; // 13-14 is Temperature and parity _.raw[15] = 0x00; - _.raw[17] = 0x00; // Off timer LSB - _.raw[19] = 0x00; // Off timer cont - _.raw[21] = 0x00; // On timer LSB - _.raw[23] = 0x00; // On timer cont + _.raw[17] = 0x00; // Off timer LSB + _.raw[19] = 0x00; // Off timer cont + _.raw[21] = 0x00; // On timer LSB + _.raw[23] = 0x00; // On timer cont // 25-26 is Mode and fan - _.raw[27] = 0xF1; // Power on + _.raw[27] = 0xF1; // Power on _.raw[29] = 0x00; _.raw[31] = 0x00; _.raw[33] = 0x00; - _.raw[35] = 0x03; // Humidity + _.raw[35] = 0x03; // Humidity setTemp(24); setMode(kHitachiAc296Heat); @@ -1774,7 +1774,6 @@ void IRHitachiAc296::send(const uint16_t repeat) { } #endif // SEND_HITACHI_AC296 -//begin /// Get the value of the current power setting. /// @return true, the setting is on. false, the setting is off. @@ -1864,7 +1863,6 @@ void IRHitachiAc296::setRaw(const uint8_t new_code[], const uint16_t length) { bool IRrecv::decodeHitachiAc296(decode_results *results, uint16_t offset, const uint16_t nbits, const bool strict) { - uint16_t used = matchGeneric(results->rawbuf + offset, results->state, results->rawlen - offset, nbits, kHitachiAcHdrMark, kHitachiAcHdrSpace, diff --git a/src/ir_Hitachi.h b/src/ir_Hitachi.h index 17705d12e..20e96edd8 100644 --- a/src/ir_Hitachi.h +++ b/src/ir_Hitachi.h @@ -296,7 +296,7 @@ const uint8_t kHitachiAc264FanMedium = kHitachiAc424FanMedium; const uint8_t kHitachiAc264FanHigh = kHitachiAc424FanHigh; const uint8_t kHitachiAc264FanAuto = kHitachiAc424FanAuto; -// HitachiAc296 +// HitachiAc296 union HitachiAC296Protocol{ uint8_t raw[kHitachiAc296StateLength]; struct { @@ -304,25 +304,25 @@ union HitachiAC296Protocol{ uint8_t pad0[13]; // Byte 13 uint8_t :2; - uint8_t Temp :5; // LSB + uint8_t Temp :5; // LSB uint8_t :1; uint8_t :8; // Byte 15~16 uint8_t :8; uint8_t :8; // Byte 17~24 - uint8_t OffTimerLow :8; // LSB + uint8_t OffTimerLow :8; // LSB uint8_t /* Parity */ :8; uint8_t OffTimerHigh :8; uint8_t /* Parity */ :8; - uint8_t OnTimerLow :8; // LSB + uint8_t OnTimerLow :8; // LSB uint8_t /* Parity */ :8; uint8_t OnTimerHigh :4; uint8_t OffTimerActive :1; uint8_t OnTimerActive :1; uint8_t :2; uint8_t /* Parity */ :8; - // Byte 25~26 + // Byte 25~26 uint8_t Mode :4; uint8_t Fan :3; uint8_t :1; @@ -337,15 +337,15 @@ union HitachiAC296Protocol{ uint8_t pad1[6]; // Byte 35~36 uint8_t :4; - uint8_t Humidity :4; // LSB + uint8_t Humidity :4; // LSB uint8_t :8; }; }; // Mode & Fan -const uint8_t kHitachiAc296Cool = 0b0011; -const uint8_t kHitachiAc296DryCool = 0b0100; -const uint8_t kHitachiAc296Dehumidify = 0b0101; +const uint8_t kHitachiAc296Cool = 0b0011; +const uint8_t kHitachiAc296DryCool = 0b0100; +const uint8_t kHitachiAc296Dehumidify = 0b0101; const uint8_t kHitachiAc296Heat = 0b0110; const uint8_t kHitachiAc296Auto = 0b0111; const uint8_t kHitachiAc296AutoDehumidifying = 0b1001; From 62297a15258091c6709349262725d9368a3ef18c Mon Sep 17 00:00:00 2001 From: Jeff Knaggs Date: Mon, 21 Feb 2022 11:08:00 +1300 Subject: [PATCH 08/15] No need to reverse the temp bits (#1757) --- src/ir_Hitachi.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/ir_Hitachi.cpp b/src/ir_Hitachi.cpp index 4e00eeb79..fdd5477b4 100644 --- a/src/ir_Hitachi.cpp +++ b/src/ir_Hitachi.cpp @@ -1816,7 +1816,7 @@ void IRHitachiAc296::setMode(const uint8_t mode) { /// Get the current temperature setting. /// @return The current setting for temp. in degrees celsius. uint8_t IRHitachiAc296::getTemp(void) const { - return reverseBits(_.Temp, kHitachiAc296TempSize) >> 2; + return _.Temp; } /// Set the temperature. @@ -1825,7 +1825,6 @@ void IRHitachiAc296::setTemp(const uint8_t celsius) { uint8_t temp; temp = std::min(celsius, kHitachiAc296MaxTemp); temp = std::max(temp, kHitachiAc296MinTemp); - temp = reverseBits(temp << 2, kHitachiAc296TempSize); _.Temp = temp; } From eb81caaa7a42ab12d7f89523b6cb81816000a300 Mon Sep 17 00:00:00 2001 From: Jeff Knaggs Date: Mon, 21 Feb 2022 13:05:02 +1300 Subject: [PATCH 09/15] Needs to be sent LSB (#1757) --- src/ir_Hitachi.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/ir_Hitachi.cpp b/src/ir_Hitachi.cpp index fdd5477b4..d8adce003 100644 --- a/src/ir_Hitachi.cpp +++ b/src/ir_Hitachi.cpp @@ -73,6 +73,7 @@ void IRsend::sendHitachiAC(const unsigned char data[], const uint16_t nbytes, bool MSBfirst = true; switch (nbytes) { case kHitachiAc264StateLength: + case kHitachiAc296StateLength: case kHitachiAc344StateLength: MSBfirst = false; } From d466dd4939cd5969c23d980e38b61f2df3df2c6f Mon Sep 17 00:00:00 2001 From: Jeff Knaggs Date: Tue, 22 Feb 2022 08:54:05 +1300 Subject: [PATCH 10/15] Lint and error fixes (#1757) --- src/ir_Hitachi.cpp | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/src/ir_Hitachi.cpp b/src/ir_Hitachi.cpp index d8adce003..5f4f7d9b8 100644 --- a/src/ir_Hitachi.cpp +++ b/src/ir_Hitachi.cpp @@ -1712,7 +1712,7 @@ String IRHitachiAc264::toString(void) const { /// Send a HitachiAc 37-byte/296-bit A/C message (HITACHI_AC296) /// Status: ALPHA / Untested. /// @param[in] data containing the IR command. -/// @param[in] nbits Nr. of bits to send. usually kHitachiAc296Bits +/// @param[in] nbytes Nr. of bytes to send. usually kHitachiAc296StateLength /// @param[in] repeat Nr. of times the message is to be repeated. void IRsend::sendHitachiAc296(const unsigned char data[], const uint16_t nbytes, @@ -1825,8 +1825,7 @@ uint8_t IRHitachiAc296::getTemp(void) const { void IRHitachiAc296::setTemp(const uint8_t celsius) { uint8_t temp; temp = std::min(celsius, kHitachiAc296MaxTemp); - temp = std::max(temp, kHitachiAc296MinTemp); - _.Temp = temp; + _.Temp = std::max(temp, kHitachiAc296MinTemp); } /// Get the current fan speed setting. @@ -1840,9 +1839,7 @@ uint8_t IRHitachiAc296::getFan(void) const { void IRHitachiAc296::setFan(const uint8_t speed) { uint8_t newSpeed = speed; newSpeed = std::max(newSpeed, kHitachiAc296FanSilent); - newSpeed = std::min(newSpeed, kHitachiAc296FanAuto); - - _.Fan = newSpeed; + _.Fan = std::min(newSpeed, kHitachiAc296FanAuto); } /// Get a PTR to the internal state/code for this protocol. From 916c45090e3b5d67b191fc60b220583863ee7e26 Mon Sep 17 00:00:00 2001 From: Jeff Knaggs Date: Tue, 22 Feb 2022 09:13:22 +1300 Subject: [PATCH 11/15] Check for inverted byte pairs (#1757) --- src/ir_Hitachi.cpp | 15 +++++++++++++++ src/ir_Hitachi.h | 5 +---- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/src/ir_Hitachi.cpp b/src/ir_Hitachi.cpp index 5f4f7d9b8..3fc5163e1 100644 --- a/src/ir_Hitachi.cpp +++ b/src/ir_Hitachi.cpp @@ -1764,6 +1764,16 @@ void IRHitachiAc296::setInvertedStates(void) { invertBytePairs(_.raw + 3, kHitachiAc296StateLength - 3); } +/// Check if every second byte of the state, after the fixed header +/// is inverted to the previous byte. +/// @param[in] state The state array to be checked. +/// @param[in] length The size of the state array. +/// @note This is this protocols integrity check. +bool IRHitachiAc296::hasInvertedStates(const uint8_t state[], + const uint16_t length) { + return (length <= 3 || checkInvertedBytePairs(state + 3, length - 3)); +} + /// Set up hardware to be able to send a message. void IRHitachiAc296::begin(void) { _irsend.begin(); } @@ -1869,6 +1879,11 @@ bool IRrecv::decodeHitachiAc296(decode_results *results, uint16_t offset, kUseDefTol, 0, false); if (used == 0) return false; + // Compliance + if (strict && !IRHitachiAc296::hasInvertedStates(results->state, nbits / 8)) + return false; + + // Success results->decode_type = decode_type_t::HITACHI_AC296; results->bits = nbits; return true; diff --git a/src/ir_Hitachi.h b/src/ir_Hitachi.h index 20e96edd8..8c58b2974 100644 --- a/src/ir_Hitachi.h +++ b/src/ir_Hitachi.h @@ -641,10 +641,7 @@ class IRHitachiAc296 { uint8_t getFan(void) const; void setMode(const uint8_t mode); uint8_t getMode(void) const; - /* void setSwingVertical(const bool on); */ - /* bool getSwingVertical(void) const; */ - /* void setSwingHorizontal(const bool on); */ - /* bool getSwingHorizontal(void) const; */ + static bool hasInvertedStates(const uint8_t state[], const uint16_t length); uint8_t* getRaw(void); void setRaw(const uint8_t new_code[], const uint16_t length = kHitachiAc296StateLength); From 03dde2b1b42406aacc69732dadd2fc426f85121a Mon Sep 17 00:00:00 2001 From: Jeff Knaggs Date: Tue, 8 Mar 2022 14:13:46 +1300 Subject: [PATCH 12/15] Update protocol status to STABLE (#1757) --- src/ir_Hitachi.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ir_Hitachi.cpp b/src/ir_Hitachi.cpp index 3fc5163e1..c27640a76 100644 --- a/src/ir_Hitachi.cpp +++ b/src/ir_Hitachi.cpp @@ -1710,7 +1710,7 @@ String IRHitachiAc264::toString(void) const { #if SEND_HITACHI_AC296 /// Send a HitachiAc 37-byte/296-bit A/C message (HITACHI_AC296) -/// Status: ALPHA / Untested. +/// Status: STABLE / Working on a real device. /// @param[in] data containing the IR command. /// @param[in] nbytes Nr. of bytes to send. usually kHitachiAc296StateLength /// @param[in] repeat Nr. of times the message is to be repeated. From 823b764b7f9770ec143f6c2640a5f15fa7dd9328 Mon Sep 17 00:00:00 2001 From: Jeff Knaggs Date: Tue, 8 Mar 2022 14:14:06 +1300 Subject: [PATCH 13/15] Include AC296 in decode list (#1757) --- src/IRremoteESP8266.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/IRremoteESP8266.h b/src/IRremoteESP8266.h index f103b6161..59cb4034e 100644 --- a/src/IRremoteESP8266.h +++ b/src/IRremoteESP8266.h @@ -876,7 +876,7 @@ DECODE_VOLTAS || DECODE_MIRAGE || DECODE_HAIER_AC176 || \ DECODE_TEKNOPOINT || DECODE_KELON || DECODE_TROTEC_3550 || \ DECODE_SANYO_AC88 || DECODE_RHOSS || DECODE_HITACHI_AC264 || \ - DECODE_KELON168 || \ + DECODE_KELON168 || DECODE_HITACHI_AC296 || \ false) // Add any DECODE to the above if it uses result->state (see kStateSizeMax) // you might also want to add the protocol to hasACState function From a9e9b18487b50fe2cc0fe7d968f2377fd05c697e Mon Sep 17 00:00:00 2001 From: Jeff Knaggs Date: Tue, 8 Mar 2022 14:16:49 +1300 Subject: [PATCH 14/15] Reduce duplicate code (#1757) --- src/ir_Hitachi.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ir_Hitachi.cpp b/src/ir_Hitachi.cpp index c27640a76..84e4280af 100644 --- a/src/ir_Hitachi.cpp +++ b/src/ir_Hitachi.cpp @@ -1771,7 +1771,7 @@ void IRHitachiAc296::setInvertedStates(void) { /// @note This is this protocols integrity check. bool IRHitachiAc296::hasInvertedStates(const uint8_t state[], const uint16_t length) { - return (length <= 3 || checkInvertedBytePairs(state + 3, length - 3)); + return IRHitachiAc3::hasInvertedStates(state, length); } /// Set up hardware to be able to send a message. From 893ee95a806264e0451b80520ef695690ff8ac74 Mon Sep 17 00:00:00 2001 From: Jeff Knaggs Date: Tue, 8 Mar 2022 14:21:38 +1300 Subject: [PATCH 15/15] Add doxygen comment for decode (#1757) --- src/ir_Hitachi.cpp | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/ir_Hitachi.cpp b/src/ir_Hitachi.cpp index 84e4280af..b80430424 100644 --- a/src/ir_Hitachi.cpp +++ b/src/ir_Hitachi.cpp @@ -1867,6 +1867,15 @@ void IRHitachiAc296::setRaw(const uint8_t new_code[], const uint16_t length) { } #if DECODE_HITACHI_AC296 +/// Decode the supplied Hitachi 37-byte A/C message. +/// Status: STABLE / Working on a real device. +/// @param[in,out] results Ptr to the data to decode & where to store the result +/// @param[in] offset The starting index to use when attempting to decode the +/// raw data. Typically/Defaults to kStartOffset. +/// @param[in] nbits The number of data bits to expect. +/// @param[in] strict Flag indicating if we should perform strict matching. +/// @return True if it can decode it, false if it can't. +/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1757 bool IRrecv::decodeHitachiAc296(decode_results *results, uint16_t offset, const uint16_t nbits, const bool strict) {