Skip to content

Commit

Permalink
Hitachi344: Experimental detail support.
Browse files Browse the repository at this point in the history
* **[Breaking change]** Moved `state[]` to LSB First ordering.
* Hitachi344 appears to just be a shorter version of Hitachi424, so using class inheritence to reduce code space.
* Update Hitachi424 class to allow virtual methods.
* Add unit test coverage.
* Add common IRac API support.

Fixes #1134 (Hopefully)
  • Loading branch information
crankyoldgit committed May 26, 2020
1 parent 232ec68 commit a1b574c
Show file tree
Hide file tree
Showing 8 changed files with 188 additions and 42 deletions.
52 changes: 52 additions & 0 deletions src/IRac.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,9 @@ bool IRac::isProtocolSupported(const decode_type_t protocol) {
#if SEND_HITACHI_AC1
case decode_type_t::HITACHI_AC1:
#endif
#if SEND_HITACHI_AC344
case decode_type_t::HITACHI_AC344:
#endif
#if SEND_HITACHI_AC424
case decode_type_t::HITACHI_AC424:
#endif
Expand Down Expand Up @@ -778,6 +781,31 @@ void IRac::hitachi1(IRHitachiAc1 *ac, const hitachi_ac1_remote_model_t model,
}
#endif // SEND_HITACHI_AC1

#if SEND_HITACHI_AC344
void IRac::hitachi344(IRHitachiAc344 *ac,
const bool on, const stdAc::opmode_t mode,
const float degrees, const stdAc::fanspeed_t fan,
const stdAc::swingv_t swingv) {
ac->begin();
ac->setMode(ac->convertMode(mode));
ac->setTemp(degrees);
ac->setFan(ac->convertFan(fan));
ac->setPower(on);
// SwingVToggle is special. Needs to be last method called.
ac->setSwingVToggle(swingv != stdAc::swingv_t::kOff);
// No Swing(H) setting available.
// No Quiet setting available.
// No Turbo setting available.
// No Light setting available.
// No Filter setting available.
// No Clean setting available.
// No Beep setting available.
// No Sleep setting available.
// No Clock setting available.
ac->send();
}
#endif // SEND_HITACHI_AC344

#if SEND_HITACHI_AC424
void IRac::hitachi424(IRHitachiAc424 *ac,
const bool on, const stdAc::opmode_t mode,
Expand Down Expand Up @@ -1343,6 +1371,7 @@ stdAc::state_t IRac::handleToggles(const stdAc::state_t desired,
case decode_type_t::ELECTRA_AC:
result.light = desired.light ^ prev->light;
break;
case decode_type_t::HITACHI_AC344:
case decode_type_t::HITACHI_AC424:
case decode_type_t::MIDEA:
case decode_type_t::SHARP_AC:
Expand Down Expand Up @@ -1621,6 +1650,14 @@ bool IRac::sendAc(const stdAc::state_t desired, const stdAc::state_t *prev) {
break;
}
#endif // SEND_HITACHI_AC1
#if SEND_HITACHI_AC344
case HITACHI_AC344:
{
IRHitachiAc344 ac(_pin, _inverted, _modulation);
hitachi344(&ac, send.power, send.mode, degC, send.fanspeed, send.swingv);
break;
}
#endif // SEND_HITACHI_AC344
#if SEND_HITACHI_AC424
case HITACHI_AC424:
{
Expand Down Expand Up @@ -2357,6 +2394,13 @@ namespace IRAcUtils {
return ac.toString();
}
#endif // DECODE_HITACHI_AC1
#if DECODE_HITACHI_AC344
case decode_type_t::HITACHI_AC344: {
IRHitachiAc344 ac(kGpioUnused);
ac.setRaw(result->state);
return ac.toString();
}
#endif // DECODE_HITACHI_AC344
#if DECODE_HITACHI_AC424
case decode_type_t::HITACHI_AC424: {
IRHitachiAc424 ac(0);
Expand Down Expand Up @@ -2593,6 +2637,14 @@ namespace IRAcUtils {
break;
}
#endif // DECODE_HITACHI_AC1
#if DECODE_HITACHI_AC344
case decode_type_t::HITACHI_AC344: {
IRHitachiAc344 ac(kGpioUnused);
ac.setRaw(decode->state);
*result = ac.toCommon();
break;
}
#endif // DECODE_HITACHI_AC344
#if DECODE_HITACHI_AC424
case decode_type_t::HITACHI_AC424: {
IRHitachiAc424 ac(kGpioUnused);
Expand Down
6 changes: 6 additions & 0 deletions src/IRac.h
Original file line number Diff line number Diff line change
Expand Up @@ -249,6 +249,12 @@ void electra(IRElectraAc *ac,
const stdAc::swingv_t swingv, const stdAc::swingh_t swingh,
const bool swing_toggle, const int16_t sleep = -1);
#endif // SEND_HITACHI_AC1
#if SEND_HITACHI_AC344
void hitachi344(IRHitachiAc344 *ac,
const bool on, const stdAc::opmode_t mode,
const float degrees, const stdAc::fanspeed_t fan,
const stdAc::swingv_t swingv);
#endif // SEND_HITACHI_AC344
#if SEND_HITACHI_AC424
void hitachi424(IRHitachiAc424 *ac,
const bool on, const stdAc::opmode_t mode,
Expand Down
3 changes: 2 additions & 1 deletion src/IRrecv.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -666,7 +666,8 @@ bool IRrecv::decode(decode_results *results, irparams_t *save,
#if DECODE_HITACHI_AC344
// HitachiAC344 should be checked before HitachiAC
DPRINTLN("Attempting Hitachi AC344 decode");
if (decodeHitachiAC(results, offset, kHitachiAc344Bits)) return true;
if (decodeHitachiAC(results, offset, kHitachiAc344Bits, true, false))
return true;
#endif // DECODE_HITACHI_AC344
#if DECODE_HITACHI_AC2
// HitachiAC2 should be checked before HitachiAC
Expand Down
4 changes: 2 additions & 2 deletions src/IRrecv.h
Original file line number Diff line number Diff line change
Expand Up @@ -520,10 +520,10 @@ class IRrecv {
const uint16_t nbits = kHaierACYRW02Bits,
const bool strict = true);
#endif
#if (DECODE_HITACHI_AC || DECODE_HITACHI_AC2)
#if (DECODE_HITACHI_AC || DECODE_HITACHI_AC2 || DECODE_HITACHI_AC344)
bool decodeHitachiAC(decode_results *results, uint16_t offset = kStartOffset,
const uint16_t nbits = kHitachiAcBits,
const bool strict = true);
const bool strict = true, const bool MSBfirst = true);
#endif
#if DECODE_HITACHI_AC1
bool decodeHitachiAC1(decode_results *results, uint16_t offset = kStartOffset,
Expand Down
73 changes: 49 additions & 24 deletions src/ir_Hitachi.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -72,9 +72,11 @@ void IRsend::sendHitachiAC(const unsigned char data[], const uint16_t nbytes,
const uint16_t repeat) {
if (nbytes < kHitachiAcStateLength)
return; // Not enough bytes to send a proper message.

const bool MSBfirst = (nbytes == kHitachiAc344StateLength) ? false : true;
sendGeneric(kHitachiAcHdrMark, kHitachiAcHdrSpace, kHitachiAcBitMark,
kHitachiAcOneSpace, kHitachiAcBitMark, kHitachiAcZeroSpace,
kHitachiAcBitMark, kHitachiAcMinGap, data, nbytes, 38, true,
kHitachiAcBitMark, kHitachiAcMinGap, data, nbytes, 38, MSBfirst,
repeat, 50);
}
#endif // (SEND_HITACHI_AC || SEND_HITACHI_AC2 || SEND_HITACHI_AC344)
Expand Down Expand Up @@ -736,31 +738,30 @@ String IRHitachiAc1::toString(void) {

#if (DECODE_HITACHI_AC || DECODE_HITACHI_AC1 || DECODE_HITACHI_AC2 || \
DECODE_HITACHI_AC344)
// Decode the supplied Hitachi A/C message.
//
// 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 kHitachiAcBits, kHitachiAc1Bits, kHitachiAc2Bits,
// kHitachiAc344Bits
// strict: Flag indicating if we should perform strict matching.
// Returns:
// boolean: True if it can decode it, false if it can't.
//
// Status: STABLE / Expected to work.
//
// Supported devices:
// Hitachi A/C Series VI (Circa 2007) / Remote: LT0541-HTA
//
// Ref:
// https://github.com/crankyoldgit/IRremoteESP8266/issues/417
// https://github.com/crankyoldgit/IRremoteESP8266/issues/453
/// Decode the supplied Hitachi A/C message.
/// Status: STABLE / Expected to work.
/// Supported devices:
/// Hitachi A/C Series VI (Circa 2007) / Remote: LT0541-HTA
///
/// Ref:
/// https://github.com/crankyoldgit/IRremoteESP8266/issues/417
/// https://github.com/crankyoldgit/IRremoteESP8266/issues/453
/// @param results Ptr to the data to decode and where to store the result.
/// @param offset The starting index to use when attempting to decode the raw
/// data. Typically/Defaults to kStartOffset.
/// @param nbits The number of data bits to expect.
/// Typically kHitachiAcBits, kHitachiAc1Bits, kHitachiAc2Bits,
/// kHitachiAc344Bits
/// @param strict Flag indicating if we should perform strict matching.
/// @param MSBfirst Is the data per byte stored in MSB First (true) or
/// LSB First order(false)?
/// @return True if it can decode it, false if it can't.
bool IRrecv::decodeHitachiAC(decode_results *results, uint16_t offset,
const uint16_t nbits, const bool strict) {
const uint16_t nbits, const bool strict,
const bool MSBfirst) {
const uint8_t k_tolerance = _tolerance + 5;


if (strict) {
switch (nbits) {
case kHitachiAcBits:
Expand Down Expand Up @@ -788,7 +789,7 @@ bool IRrecv::decodeHitachiAC(decode_results *results, uint16_t offset,
kHitachiAcBitMark, kHitachiAcOneSpace,
kHitachiAcBitMark, kHitachiAcZeroSpace,
kHitachiAcBitMark, kHitachiAcMinGap, true,
k_tolerance)) return false;
k_tolerance, kMarkExcess, MSBfirst)) return false;

// Compliance
if (strict) {
Expand All @@ -798,6 +799,9 @@ bool IRrecv::decodeHitachiAC(decode_results *results, uint16_t offset,
if (nbits / 8 == kHitachiAc1StateLength &&
!IRHitachiAc1::validChecksum(results->state, kHitachiAc1StateLength))
return false;
if (nbits / 8 == kHitachiAc344StateLength &&
!IRHitachiAc3::hasInvertedStates(results->state, nbits / 8))
return false;
}

// Success
Expand Down Expand Up @@ -1329,3 +1333,24 @@ bool IRrecv::decodeHitachiAc3(decode_results *results, uint16_t offset,
return true;
}
#endif // DECODE_HITACHI_AC3

// Class for handling the remote control on a Hitachi_AC344 43 byte A/C message
IRHitachiAc344::IRHitachiAc344(const uint16_t pin, const bool inverted,
const bool use_modulation)
: IRHitachiAc424(pin, inverted, use_modulation) {}

#if SEND_HITACHI_AC344
void IRHitachiAc344::send(const uint16_t repeat) {
_irsend.sendHitachiAc344(getRaw(), kHitachiAc344StateLength, repeat);
}
#endif // SEND_HITACHI_AC344

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

stdAc::state_t IRHitachiAc344::toCommon(void) {
stdAc::state_t result = IRHitachiAc424::toCommon();
result.protocol = decode_type_t::HITACHI_AC344;
return result;
}
25 changes: 19 additions & 6 deletions src/ir_Hitachi.h
Original file line number Diff line number Diff line change
Expand Up @@ -254,13 +254,14 @@ class IRHitachiAc1 {
};

class IRHitachiAc424 {
friend class IRHitachiAc344;
public:
explicit IRHitachiAc424(const uint16_t pin, const bool inverted = false,
const bool use_modulation = true);

void stateReset(void);
#if SEND_HITACHI_AC424
void send(const uint16_t repeat = kHitachiAcDefaultRepeat);
virtual void send(const uint16_t repeat = kHitachiAcDefaultRepeat);
int8_t calibrate(void) { return _irsend.calibrate(); }
#endif // SEND_HITACHI_AC424
void begin(void);
Expand All @@ -279,17 +280,17 @@ class IRHitachiAc424 {
void setMode(const uint8_t mode);
uint8_t getMode(void);
uint8_t* getRaw(void);
void setRaw(const uint8_t new_code[],
const uint16_t length = kHitachiAc424StateLength);
virtual void setRaw(const uint8_t new_code[],
const uint16_t length = kHitachiAc424StateLength);
uint8_t convertMode(const stdAc::opmode_t mode);
uint8_t convertFan(const stdAc::fanspeed_t speed);
static stdAc::opmode_t toCommonMode(const uint8_t mode);
static stdAc::fanspeed_t toCommonFanSpeed(const uint8_t speed);
stdAc::state_t toCommon(void);
virtual stdAc::state_t toCommon(void);
String toString(void);
#ifndef UNIT_TEST

private:
protected:
IRsend _irsend;
#else
IRsendTest _irsend;
Expand All @@ -303,7 +304,7 @@ class IRHitachiAc424 {
class IRHitachiAc3 {
public:
explicit IRHitachiAc3(const uint16_t pin, const bool inverted = false,
const bool use_modulation = true);
const bool use_modulation = true);

void stateReset(void);
#if SEND_HITACHI_AC3
Expand All @@ -328,4 +329,16 @@ class IRHitachiAc3 {
void setInvertedStates(const uint16_t length = kHitachiAc3StateLength);
};

class IRHitachiAc344: public IRHitachiAc424 {
public:
explicit IRHitachiAc344(const uint16_t pin, const bool inverted = false,
const bool use_modulation = true);
void setRaw(const uint8_t new_code[],
const uint16_t length = kHitachiAc344StateLength);
stdAc::state_t toCommon(void);
#if SEND_HITACHI_AC344
void send(const uint16_t repeat = kHitachiAcDefaultRepeat);
#endif // SEND_HITACHI_AC344
};

#endif // IR_HITACHI_H_
45 changes: 45 additions & 0 deletions test/IRac_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -726,6 +726,51 @@ TEST(TestIRac, Hitachi1) {
ASSERT_TRUE(IRAcUtils::decodeToState(&ac._irsend.capture, &r, &p));
}

TEST(TestIRac, Hitachi344) {
IRHitachiAc344 ac(kGpioUnused);
IRac irac(kGpioUnused);
IRrecv capture(kGpioUnused);
char expected[] =
"Power: On, Mode: 6 (Heat), Temp: 25C, Fan: 6 (Max), "
"Swing(V) Toggle: Off, Button: 19 (Power/Mode)";
char expected_swingv[] =
"Power: On, Mode: 3 (Cool), Temp: 26C, Fan: 1 (Min), "
"Swing(V) Toggle: On, Button: 129 (Swing(V))";

ac.begin();
irac.hitachi344(&ac,
true, // Power
stdAc::opmode_t::kHeat, // Mode
25, // Celsius
stdAc::fanspeed_t::kMax, // Fan speed
stdAc::swingv_t::kOff); // Swing(V)

ASSERT_EQ(expected, ac.toString());
ac._irsend.makeDecodeResult();
EXPECT_TRUE(capture.decode(&ac._irsend.capture));
ASSERT_EQ(HITACHI_AC344, ac._irsend.capture.decode_type);
ASSERT_EQ(kHitachiAc344Bits, ac._irsend.capture.bits);
ASSERT_EQ(expected, IRAcUtils::resultAcToString(&ac._irsend.capture));
stdAc::state_t r, p;
ASSERT_TRUE(IRAcUtils::decodeToState(&ac._irsend.capture, &r, &p));

ac._irsend.reset();
irac.hitachi344(&ac,
true, // Power
stdAc::opmode_t::kCool, // Mode
26, // Celsius
stdAc::fanspeed_t::kMin, // Fan speed
stdAc::swingv_t::kAuto); // Swing(V)

ASSERT_EQ(expected_swingv, ac.toString());
ac._irsend.makeDecodeResult();
EXPECT_TRUE(capture.decode(&ac._irsend.capture));
ASSERT_EQ(HITACHI_AC344, ac._irsend.capture.decode_type);
ASSERT_EQ(kHitachiAc344Bits, ac._irsend.capture.bits);
ASSERT_EQ(expected_swingv, IRAcUtils::resultAcToString(&ac._irsend.capture));
ASSERT_TRUE(IRAcUtils::decodeToState(&ac._irsend.capture, &r, &p));
}

TEST(TestIRac, Hitachi424) {
IRHitachiAc424 ac(0);
IRac irac(0);
Expand Down
Loading

0 comments on commit a1b574c

Please sign in to comment.