Skip to content

Commit

Permalink
Support for Hitachi RAS-70YHA3 (remote RAR-3U3) (#1758)
Browse files Browse the repository at this point in the history
Add support for Hitachi RAS-70YHA3 (remote RAR-3U3). Fixes #1757

This PR provides some initial support for the RAR-3U3 remote. It works of initial research [found here](https://perhof.wordpress.com/2015/03/29/reverse-engineering-hitachi-air-conditioner-infrared-remote-commands/).
  • Loading branch information
jeef3 authored Mar 13, 2022
1 parent 8f75109 commit 434f65e
Show file tree
Hide file tree
Showing 10 changed files with 349 additions and 2 deletions.
6 changes: 6 additions & 0 deletions src/IRrecv.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -850,6 +850,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 (decodeHitachiAc296(results, offset, kHitachiAc296Bits, true))
return true;
#endif // DECODE_HITACHI_AC296
#if DECODE_HITACHI_AC2
// HitachiAC2 should be checked before HitachiAC
DPRINTLN("Attempting Hitachi AC2 decode");
Expand Down
6 changes: 6 additions & 0 deletions src/IRrecv.h
Original file line number Diff line number Diff line change
Expand Up @@ -615,6 +615,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,
Expand Down
16 changes: 14 additions & 2 deletions src/IRremoteESP8266.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -869,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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -1135,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;
Expand Down Expand Up @@ -1304,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
Expand Down
7 changes: 7 additions & 0 deletions src/IRsend.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down Expand Up @@ -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);
Expand Down
5 changes: 5 additions & 0 deletions src/IRsend.h
Original file line number Diff line number Diff line change
Expand Up @@ -598,6 +598,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,
Expand Down
1 change: 1 addition & 0 deletions src/IRtext.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -395,6 +395,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.
};
Expand Down
1 change: 1 addition & 0 deletions src/IRutils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
194 changes: 194 additions & 0 deletions src/ir_Hitachi.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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 <algorithm>
Expand Down Expand Up @@ -72,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;
}
Expand Down Expand Up @@ -1704,3 +1706,195 @@ String IRHitachiAc264::toString(void) const {
#if DECODE_HITACHI_AC264
// For Decoding HITACHI_AC264, see `decodeHitachiAC`
#endif // DECODE_HITACHI_AC264


#if SEND_HITACHI_AC296
/// Send a HitachiAc 37-byte/296-bit A/C message (HITACHI_AC296)
/// 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.
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);
}
#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] = 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
// 25-26 is Mode and fan
_.raw[27] = 0xF1; // Power on
_.raw[29] = 0x00;
_.raw[31] = 0x00;
_.raw[33] = 0x00;
_.raw[35] = 0x03; // Humidity

setTemp(24);
setMode(kHitachiAc296Heat);
setFan(kHitachiAc296FanAuto);

setInvertedStates();
}

/// Update the internal consistency check for the protocol.
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 IRHitachiAc3::hasInvertedStates(state, length);
}

/// 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


/// 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 _.Temp;
}

/// 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);
}

/// 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);
_.Fan = std::min(newSpeed, kHitachiAc296FanAuto);
}

/// 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, kHitachiAc296StateLength));
}

#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) {
uint16_t used = matchGeneric(results->rawbuf + offset, results->state,
results->rawlen - offset, nbits,
kHitachiAcHdrMark, kHitachiAcHdrSpace,
kHitachiAcBitMark, kHitachiAcOneSpace,
kHitachiAcBitMark, kHitachiAcZeroSpace,
kHitachiAcBitMark, kHitachiAcMinGap, true,
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;
}
#endif // DECODE_HITACHI_AC296
Loading

0 comments on commit 434f65e

Please sign in to comment.