Skip to content

Commit f63b9be

Browse files
authored
HITACHI_AC296: Add IRac class support & tests. (#1776)
* Fix max temp issue. * Set special temp for auto operation mode. * Merge into the `IRac` class so it is supported fully. * Add `.toString()` output. * Add real & synthetic decoding examples. * General code style cleanup(s). * Add supporting Unit Tests. Ref: #1758 Fixes #1757
1 parent 434f65e commit f63b9be

File tree

6 files changed

+423
-56
lines changed

6 files changed

+423
-56
lines changed

src/IRac.cpp

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -230,6 +230,9 @@ bool IRac::isProtocolSupported(const decode_type_t protocol) {
230230
#if SEND_HITACHI_AC264
231231
case decode_type_t::HITACHI_AC264:
232232
#endif
233+
#if SEND_HITACHI_AC296
234+
case decode_type_t::HITACHI_AC296:
235+
#endif
233236
#if SEND_HITACHI_AC344
234237
case decode_type_t::HITACHI_AC344:
235238
#endif
@@ -1341,6 +1344,35 @@ void IRac::hitachi264(IRHitachiAc264 *ac,
13411344
}
13421345
#endif // SEND_HITACHI_AC264
13431346

1347+
#if SEND_HITACHI_AC296
1348+
/// Send a Hitachi 296-bit A/C message with the supplied settings.
1349+
/// @param[in, out] ac A Ptr to an IRHitachiAc296 object to use.
1350+
/// @param[in] on The power setting.
1351+
/// @param[in] mode The operation mode setting.
1352+
/// @param[in] degrees The temperature setting in degrees.
1353+
/// @param[in] fan The speed setting for the fan.
1354+
void IRac::hitachi296(IRHitachiAc296 *ac,
1355+
const bool on, const stdAc::opmode_t mode,
1356+
const float degrees, const stdAc::fanspeed_t fan) {
1357+
ac->begin();
1358+
ac->setMode(ac->convertMode(mode));
1359+
ac->setTemp(degrees);
1360+
ac->setFan(ac->convertFan(fan));
1361+
ac->setPower(on);
1362+
// No Swing(V) setting available.
1363+
// No Swing(H) setting available.
1364+
// No Quiet setting available.
1365+
// No Turbo setting available.
1366+
// No Light setting available.
1367+
// No Filter setting available.
1368+
// No Clean setting available.
1369+
// No Beep setting available.
1370+
// No Sleep setting available.
1371+
// No Clock setting available.
1372+
ac->send();
1373+
}
1374+
#endif // SEND_HITACHI_AC296
1375+
13441376
#if SEND_HITACHI_AC344
13451377
/// Send a Hitachi 344-bit A/C message with the supplied settings.
13461378
/// @param[in, out] ac A Ptr to an IRHitachiAc344 object to use.
@@ -2946,6 +2978,14 @@ bool IRac::sendAc(const stdAc::state_t desired, const stdAc::state_t *prev) {
29462978
break;
29472979
}
29482980
#endif // SEND_HITACHI_AC264
2981+
#if SEND_HITACHI_AC296
2982+
case HITACHI_AC296:
2983+
{
2984+
IRHitachiAc296 ac(_pin, _inverted, _modulation);
2985+
hitachi296(&ac, send.power, send.mode, degC, send.fanspeed);
2986+
break;
2987+
}
2988+
#endif // SEND_HITACHI_AC296
29492989
#if SEND_HITACHI_AC344
29502990
case HITACHI_AC344:
29512991
{
@@ -3808,6 +3848,13 @@ namespace IRAcUtils {
38083848
return ac.toString();
38093849
}
38103850
#endif // DECODE_HITACHI_AC264
3851+
#if DECODE_HITACHI_AC296
3852+
case decode_type_t::HITACHI_AC296: {
3853+
IRHitachiAc296 ac(kGpioUnused);
3854+
ac.setRaw(result->state);
3855+
return ac.toString();
3856+
}
3857+
#endif // DECODE_HITACHI_AC296
38113858
#if DECODE_HITACHI_AC344
38123859
case decode_type_t::HITACHI_AC344: {
38133860
IRHitachiAc344 ac(kGpioUnused);
@@ -4273,6 +4320,14 @@ namespace IRAcUtils {
42734320
break;
42744321
}
42754322
#endif // DECODE_HITACHI_AC264
4323+
#if DECODE_HITACHI_AC296
4324+
case decode_type_t::HITACHI_AC296: {
4325+
IRHitachiAc296 ac(kGpioUnused);
4326+
ac.setRaw(decode->state);
4327+
*result = ac.toCommon();
4328+
break;
4329+
}
4330+
#endif // DECODE_HITACHI_AC296
42764331
#if DECODE_HITACHI_AC344
42774332
case decode_type_t::HITACHI_AC344: {
42784333
IRHitachiAc344 ac(kGpioUnused);

src/IRac.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -305,6 +305,11 @@ void electra(IRElectraAc *ac,
305305
const bool on, const stdAc::opmode_t mode,
306306
const float degrees, const stdAc::fanspeed_t fan);
307307
#endif // SEND_HITACHI_AC264
308+
#if SEND_HITACHI_AC296
309+
void hitachi296(IRHitachiAc296 *ac,
310+
const bool on, const stdAc::opmode_t mode,
311+
const float degrees, const stdAc::fanspeed_t fan);
312+
#endif // SEND_HITACHI_AC296
308313
#if SEND_HITACHI_AC344
309314
void hitachi344(IRHitachiAc344 *ac,
310315
const bool on, const stdAc::opmode_t mode,

src/ir_Hitachi.cpp

Lines changed: 129 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1723,10 +1723,15 @@ void IRsend::sendHitachiAc296(const unsigned char data[],
17231723
}
17241724
#endif // SEND_HITACHIAC296
17251725

1726+
// Class constructor for handling detailed Hitachi_AC296 37 byte A/C messages.
1727+
/// @param[in] pin GPIO to be used when sending.
1728+
/// @param[in] inverted Is the output signal to be inverted?
1729+
/// @param[in] use_modulation Is frequency modulation to be used?
17261730
IRHitachiAc296::IRHitachiAc296(const uint16_t pin, const bool inverted,
17271731
const bool use_modulation)
17281732
: _irsend(pin, inverted, use_modulation) { stateReset(); }
17291733

1734+
/// Reset the internal state to auto fan, heating, & 24° Celsius
17301735
void IRHitachiAc296::stateReset(void) {
17311736
// Header
17321737
_.raw[0] = 0x01;
@@ -1785,18 +1790,13 @@ void IRHitachiAc296::send(const uint16_t repeat) {
17851790
}
17861791
#endif // SEND_HITACHI_AC296
17871792

1788-
17891793
/// Get the value of the current power setting.
17901794
/// @return true, the setting is on. false, the setting is off.
1791-
bool IRHitachiAc296::getPower(void) const {
1792-
return _.Power;
1793-
}
1795+
bool IRHitachiAc296::getPower(void) const { return _.Power; }
17941796

17951797
/// Change the power setting.
17961798
/// @param[in] on true, the setting is on. false, the setting is off.
1797-
void IRHitachiAc296::setPower(const bool on) {
1798-
_.Power = on;
1799-
}
1799+
void IRHitachiAc296::setPower(const bool on) { _.Power = on; }
18001800

18011801
/// Change the power setting to On.
18021802
void IRHitachiAc296::on(void) { setPower(true); }
@@ -1806,52 +1806,106 @@ void IRHitachiAc296::off(void) { setPower(false); }
18061806

18071807
/// Get the operating mode setting of the A/C.
18081808
/// @return The current operating mode setting.
1809-
uint8_t IRHitachiAc296::getMode(void) const {
1810-
return _.Mode;
1811-
}
1809+
uint8_t IRHitachiAc296::getMode(void) const { return _.Mode; }
18121810

18131811
/// Set the operating mode of the A/C.
18141812
/// @param[in] mode The desired operating mode.
18151813
void IRHitachiAc296::setMode(const uint8_t mode) {
1816-
uint8_t newMode = mode;
18171814
switch (mode) {
18181815
case kHitachiAc296Heat:
18191816
case kHitachiAc296Cool:
1820-
case kHitachiAc296Auto: break;
1821-
default: newMode = kHitachiAc296Auto;
1817+
case kHitachiAc296Dehumidify:
1818+
case kHitachiAc296AutoDehumidifying:
1819+
case kHitachiAc296Auto:
1820+
_.Mode = mode;
1821+
setTemp(getTemp()); // Reset the temp to handle "Auto"'s special temp.
1822+
break;
1823+
default:
1824+
setMode(kHitachiAc296Auto);
18221825
}
1826+
}
18231827

1824-
_.Mode = newMode;
1828+
/// Convert a stdAc::opmode_t enum into its native mode.
1829+
/// @param[in] mode The enum to be converted.
1830+
/// @return The native equivalent of the enum.
1831+
uint8_t IRHitachiAc296::convertMode(const stdAc::opmode_t mode) {
1832+
switch (mode) {
1833+
case stdAc::opmode_t::kCool: return kHitachiAc296Cool;
1834+
case stdAc::opmode_t::kHeat: return kHitachiAc296Heat;
1835+
case stdAc::opmode_t::kDry: return kHitachiAc296Dehumidify;
1836+
default: return kHitachiAc296Auto;
1837+
}
1838+
}
1839+
1840+
/// Convert a native mode into its stdAc equivalent.
1841+
/// @param[in] mode The native setting to be converted.
1842+
/// @return The stdAc equivalent of the native setting.
1843+
stdAc::opmode_t IRHitachiAc296::toCommonMode(const uint8_t mode) {
1844+
switch (mode) {
1845+
case kHitachiAc296DryCool:
1846+
case kHitachiAc296Cool: return stdAc::opmode_t::kCool;
1847+
case kHitachiAc296Heat: return stdAc::opmode_t::kHeat;
1848+
case kHitachiAc296AutoDehumidifying:
1849+
case kHitachiAc296Dehumidify: return stdAc::opmode_t::kDry;
1850+
default: return stdAc::opmode_t::kAuto;
1851+
}
18251852
}
18261853

18271854
/// Get the current temperature setting.
18281855
/// @return The current setting for temp. in degrees celsius.
1829-
uint8_t IRHitachiAc296::getTemp(void) const {
1830-
return _.Temp;
1831-
}
1856+
uint8_t IRHitachiAc296::getTemp(void) const { return _.Temp; }
18321857

18331858
/// Set the temperature.
18341859
/// @param[in] celsius The temperature in degrees celsius.
18351860
void IRHitachiAc296::setTemp(const uint8_t celsius) {
1836-
uint8_t temp;
1837-
temp = std::min(celsius, kHitachiAc296MaxTemp);
1838-
_.Temp = std::max(temp, kHitachiAc296MinTemp);
1861+
uint8_t temp = celsius;
1862+
if (getMode() == kHitachiAc296Auto) { // Special temp for auto mode
1863+
temp = kHitachiAc296TempAuto;
1864+
} else { // Normal temp setting.
1865+
temp = std::min(temp, kHitachiAc296MaxTemp);
1866+
temp = std::max(temp, kHitachiAc296MinTemp);
1867+
}
1868+
_.Temp = temp;
18391869
}
18401870

18411871
/// Get the current fan speed setting.
18421872
/// @return The current fan speed.
1843-
uint8_t IRHitachiAc296::getFan(void) const {
1844-
return _.Fan;
1845-
}
1873+
uint8_t IRHitachiAc296::getFan(void) const { return _.Fan; }
18461874

18471875
/// Set the speed of the fan.
18481876
/// @param[in] speed The desired setting.
18491877
void IRHitachiAc296::setFan(const uint8_t speed) {
1850-
uint8_t newSpeed = speed;
1851-
newSpeed = std::max(newSpeed, kHitachiAc296FanSilent);
1878+
uint8_t newSpeed = std::max(speed, kHitachiAc296FanSilent);
18521879
_.Fan = std::min(newSpeed, kHitachiAc296FanAuto);
18531880
}
18541881

1882+
/// Convert a stdAc::fanspeed_t enum into it's native speed.
1883+
/// @param[in] speed The enum to be converted.
1884+
/// @return The native equivalent of the enum.
1885+
uint8_t IRHitachiAc296::convertFan(const stdAc::fanspeed_t speed) {
1886+
switch (speed) {
1887+
case stdAc::fanspeed_t::kMin: return kHitachiAc296FanSilent;
1888+
case stdAc::fanspeed_t::kLow: return kHitachiAc296FanLow;
1889+
case stdAc::fanspeed_t::kMedium: return kHitachiAc296FanMedium;
1890+
case stdAc::fanspeed_t::kHigh:
1891+
case stdAc::fanspeed_t::kMax: return kHitachiAc296FanHigh;
1892+
default: return kHitachiAc296FanAuto;
1893+
}
1894+
}
1895+
1896+
/// Convert a native fan speed into its stdAc equivalent.
1897+
/// @param[in] speed The native setting to be converted.
1898+
/// @return The stdAc equivalent of the native setting.
1899+
stdAc::fanspeed_t IRHitachiAc296::toCommonFanSpeed(const uint8_t speed) {
1900+
switch (speed) {
1901+
case kHitachiAc296FanHigh: return stdAc::fanspeed_t::kHigh;
1902+
case kHitachiAc296FanMedium: return stdAc::fanspeed_t::kMedium;
1903+
case kHitachiAc296FanLow: return stdAc::fanspeed_t::kLow;
1904+
case kHitachiAc296FanSilent: return stdAc::fanspeed_t::kMin;
1905+
default: return stdAc::fanspeed_t::kAuto;
1906+
}
1907+
}
1908+
18551909
/// Get a PTR to the internal state/code for this protocol.
18561910
/// @return PTR to a code for this protocol based on the current internal state.
18571911
uint8_t *IRHitachiAc296::getRaw(void) {
@@ -1866,6 +1920,49 @@ void IRHitachiAc296::setRaw(const uint8_t new_code[], const uint16_t length) {
18661920
memcpy(_.raw, new_code, std::min(length, kHitachiAc296StateLength));
18671921
}
18681922

1923+
1924+
/// Convert the current internal state into its stdAc::state_t equivalent.
1925+
/// @return The stdAc equivalent of the native settings.
1926+
stdAc::state_t IRHitachiAc296::toCommon(void) const {
1927+
stdAc::state_t result{};
1928+
result.protocol = decode_type_t::HITACHI_AC296;
1929+
result.model = -1; // No models used.
1930+
result.power = getPower();
1931+
result.mode = toCommonMode(_.Mode);
1932+
result.celsius = true;
1933+
result.degrees = _.Temp;
1934+
result.fanspeed = toCommonFanSpeed(_.Fan);
1935+
result.quiet = _.Fan == kHitachiAc296FanSilent;
1936+
// Not supported.
1937+
result.swingv = stdAc::swingv_t::kOff;
1938+
result.swingh = stdAc::swingh_t::kOff;
1939+
result.turbo = false;
1940+
result.clean = false;
1941+
result.econo = false;
1942+
result.filter = false;
1943+
result.light = false;
1944+
result.beep = false;
1945+
result.sleep = -1;
1946+
result.clock = -1;
1947+
return result;
1948+
}
1949+
1950+
/// Convert the current internal state into a human readable string.
1951+
/// @return A human readable string.
1952+
String IRHitachiAc296::toString(void) const {
1953+
String result = "";
1954+
result.reserve(70); // Reserve some heap for the string to reduce fragging.
1955+
result += addBoolToString(_.Power, kPowerStr, false);
1956+
result += addModeToString(_.Mode, kHitachiAc296Auto, kHitachiAc296Cool,
1957+
kHitachiAc296Heat, kHitachiAc1Dry,
1958+
kHitachiAc296Auto);
1959+
result += addTempToString(getTemp());
1960+
result += addFanToString(_.Fan, kHitachiAc296FanHigh, kHitachiAc296FanLow,
1961+
kHitachiAc296FanAuto, kHitachiAc296FanSilent,
1962+
kHitachiAc296FanMedium);
1963+
return result;
1964+
}
1965+
18691966
#if DECODE_HITACHI_AC296
18701967
/// Decode the supplied Hitachi 37-byte A/C message.
18711968
/// Status: STABLE / Working on a real device.
@@ -1879,14 +1976,13 @@ void IRHitachiAc296::setRaw(const uint8_t new_code[], const uint16_t length) {
18791976
bool IRrecv::decodeHitachiAc296(decode_results *results, uint16_t offset,
18801977
const uint16_t nbits,
18811978
const bool strict) {
1882-
uint16_t used = matchGeneric(results->rawbuf + offset, results->state,
1883-
results->rawlen - offset, nbits,
1884-
kHitachiAcHdrMark, kHitachiAcHdrSpace,
1885-
kHitachiAcBitMark, kHitachiAcOneSpace,
1886-
kHitachiAcBitMark, kHitachiAcZeroSpace,
1887-
kHitachiAcBitMark, kHitachiAcMinGap, true,
1888-
kUseDefTol, 0, false);
1889-
if (used == 0) return false;
1979+
if (!matchGeneric(results->rawbuf + offset, results->state,
1980+
results->rawlen - offset, nbits,
1981+
kHitachiAcHdrMark, kHitachiAcHdrSpace,
1982+
kHitachiAcBitMark, kHitachiAcOneSpace,
1983+
kHitachiAcBitMark, kHitachiAcZeroSpace,
1984+
kHitachiAcBitMark, kHitachiAcMinGap, true,
1985+
kUseDefTol, 0, false)) return false;
18901986

18911987
// Compliance
18921988
if (strict && !IRHitachiAc296::hasInvertedStates(results->state, nbits / 8))

src/ir_Hitachi.h

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -358,9 +358,9 @@ const uint8_t kHitachiAc296FanMedium = 0b011;
358358
const uint8_t kHitachiAc296FanHigh = 0b100;
359359
const uint8_t kHitachiAc296FanAuto = 0b101;
360360

361-
const uint8_t kHitachiAc296TempSize = 5;
361+
const uint8_t kHitachiAc296TempAuto = 1; // Special value for "Auto" op mode.
362362
const uint8_t kHitachiAc296MinTemp = 16;
363-
const uint8_t kHitachiAc296MaxTemp = 32;
363+
const uint8_t kHitachiAc296MaxTemp = 31; // Max value you can store in 5 bits.
364364

365365
const uint8_t kHitachiAc296PowerOn = 1;
366366
const uint8_t kHitachiAc296PowerOff = 0;
@@ -629,7 +629,7 @@ class IRHitachiAc296 {
629629

630630
#if SEND_HITACHI_AC296
631631
void send(const uint16_t repeat = kHitachiAcDefaultRepeat);
632-
#endif
632+
#endif // SEND_HITACHI_AC296
633633
void begin(void);
634634
void on(void);
635635
void off(void);
@@ -645,7 +645,12 @@ class IRHitachiAc296 {
645645
uint8_t* getRaw(void);
646646
void setRaw(const uint8_t new_code[],
647647
const uint16_t length = kHitachiAc296StateLength);
648-
648+
static uint8_t convertMode(const stdAc::opmode_t mode);
649+
static uint8_t convertFan(const stdAc::fanspeed_t speed);
650+
static stdAc::opmode_t toCommonMode(const uint8_t mode);
651+
static stdAc::fanspeed_t toCommonFanSpeed(const uint8_t speed);
652+
stdAc::state_t toCommon(void) const;
653+
String toString(void) const;
649654
#ifndef UNIT_TEST
650655

651656
private:

0 commit comments

Comments
 (0)