Add Fahrenheit support for the BOSCH144 protocol

Issue #2224
This commit is contained in:
Ben Gruver
2025-11-27 20:29:31 -06:00
committed by David Conran
parent 95b87cccd2
commit 8fb411b6a1
5 changed files with 194 additions and 70 deletions

View File

@@ -648,13 +648,14 @@ void IRac::argoWrem3_SetTimer(IRArgoAC_WREM3 *ac, bool on,
/// @param[in] on The power setting.
/// @param[in] mode The operation mode setting.
/// @param[in] degrees The temperature setting in degrees.
/// @param[in] celsius Temperature units. True is Celsius, False is Fahrenheit.
/// @param[in] fan The speed setting for the fan.
/// @param[in] quiet Run the device in quiet/silent mode.
/// @note -1 is Off, >= 0 is on.
void IRac::bosch144(IRBosch144AC *ac,
const bool on, const stdAc::opmode_t mode,
const float degrees, const stdAc::fanspeed_t fan,
const bool quiet) {
const float degrees, const bool celsius,
const stdAc::fanspeed_t fan, const bool quiet) {
ac->begin();
ac->setPower(on);
if (!on) {
@@ -663,7 +664,7 @@ void IRac::bosch144(IRBosch144AC *ac,
ac->send();
return;
}
ac->setTemp(degrees);
ac->setTemp(degrees, !celsius);
ac->setFan(ac->convertFan(fan));
ac->setMode(ac->convertMode(mode));
ac->setQuiet(quiet);
@@ -3136,7 +3137,8 @@ bool IRac::sendAc(const stdAc::state_t desired, const stdAc::state_t *prev) {
case BOSCH144:
{
IRBosch144AC ac(_pin, _inverted, _modulation);
bosch144(&ac, send.power, send.mode, degC, send.fanspeed, send.quiet);
bosch144(&ac, send.power, send.mode, send.degrees, send.celsius,
send.fanspeed, send.quiet);
break;
}
#endif // SEND_BOSCH144

View File

@@ -163,7 +163,7 @@ class IRac {
#if SEND_BOSCH144
void bosch144(IRBosch144AC *ac,
const bool on, const stdAc::opmode_t mode, const float degrees,
const stdAc::fanspeed_t fan,
const bool celsius, const stdAc::fanspeed_t fan,
const bool quiet);
#endif // SEND_BOSCH144
#if SEND_CARRIER_AC64

View File

@@ -95,27 +95,55 @@ bool IRBosch144AC::getPower(void) const {
}
void IRBosch144AC::setTempRaw(const uint8_t code) {
_.TempS1 = _.TempS2 = code >> 1; // save 4 bits in S1 and S2
_.TempS3 = code & 1; // save 1 bit in Section3
_.TempS1 = _.TempS2 = code >> 2; // save bits 3-6 in S1 and S2
_.TempS3 = code >> 1; // save bit 2 in Section3
_.TempS4 = code; // save bit 1 in Section3
}
/// Set the temperature.
/// @param[in] degrees The temperature in degrees celsius.
void IRBosch144AC::setTemp(const uint8_t degrees) {
uint8_t temp = max(kBosch144TempMin, degrees);
temp = min(kBosch144TempMax, temp);
setTempRaw(kBosch144TempMap[temp - kBosch144TempMin]);
/// Set the temp. in degrees
/// @param[in] temp Desired temperature in Degrees.
/// @param[in] fahrenheit Use units of Fahrenheit and set that as units used.
/// false is Celsius (Default), true is Fahrenheit.
void IRBosch144AC::setTemp(const uint8_t temp, const bool fahrenheit) {
if (fahrenheit) {
uint8_t constrainedTemp = max(kBosch144FahrenheitMin, temp);
constrainedTemp = min(kBosch144FahrenheitMax, constrainedTemp);
setTempRaw(
kBosch144FahrenheitMap[constrainedTemp - kBosch144FahrenheitMin]);
setUseFahrenheit(true);
} else {
uint8_t constrainedTemp = max(kBosch144CelsiusMin, temp);
constrainedTemp = min(kBosch144CelsiusMax, constrainedTemp);
setTempRaw(kBosch144CelsiusMap[constrainedTemp - kBosch144CelsiusMin]);
setUseFahrenheit(false);
}
}
uint8_t IRBosch144AC::getTemp(void) const {
uint8_t temp = (_.TempS1 << 1) + _.TempS3;
uint8_t retemp = 25;
for (uint8_t i = 0; i < kBosch144TempRange; i++) {
if (temp == kBosch144TempMap[i]) {
retemp = kBosch144TempMin + i;
uint8_t temp = (_.TempS1 << 2) + (_.TempS3 << 1) + _.TempS4;
if (getUseFahrenheit()) {
for (uint8_t i = 0; i < sizeof(kBosch144FahrenheitMap); i++) {
if (temp == kBosch144FahrenheitMap[i]) {
return kBosch144FahrenheitMin + i;
}
}
return 77;
} else {
for (uint8_t i = 0; i < sizeof(kBosch144CelsiusMap); i++) {
if (temp == kBosch144CelsiusMap[i]) {
return kBosch144CelsiusMin + i;
}
}
return 25;
}
return retemp;
}
void IRBosch144AC::setUseFahrenheit(const bool on) {
_.UseFahrenheit = on;
}
bool IRBosch144AC::getUseFahrenheit(void) const {
return _.UseFahrenheit;
}
/// Set the speed of the fan.
@@ -227,7 +255,7 @@ stdAc::state_t IRBosch144AC::toCommon(void) const {
result.protocol = decode_type_t::BOSCH144;
result.power = getPower();
result.mode = toCommonMode(getMode());
result.celsius = true;
result.celsius = !getUseFahrenheit();
result.degrees = getTemp();
result.fanspeed = toCommonFanSpeed(getFan());
result.quiet = getQuiet();
@@ -261,7 +289,7 @@ String IRBosch144AC::toString(void) const {
static_cast<int>(stdAc::fanspeed_t::kAuto),
static_cast<int>(stdAc::fanspeed_t::kAuto),
static_cast<int>(stdAc::fanspeed_t::kMedium));
result += addTempToString(getTemp());
result += addTempToString(getTemp(), !getUseFahrenheit());
result += addBoolToString(_.Quiet, kQuietStr);
return result;
}

View File

@@ -6,6 +6,7 @@
// Supports:
// Brand: Bosch, Model: CL3000i-Set 26 E A/C
// Brand: Bosch, Model: RG10A(G2S)BGEF remote
// Brand: Durastar, Model: RG10R(M2S)/BGEFU1 remote
#ifndef IR_BOSCH_H_
@@ -66,25 +67,62 @@ const uint16_t kBosch144FanAuto = 0b101110011;
const uint16_t kBosch144FanAuto0 = 0b000110011;
// Temperature
const uint8_t kBosch144TempMin = 16; // Celsius
const uint8_t kBosch144TempMax = 30; // Celsius
const uint8_t kBosch144TempRange = kBosch144TempMax - kBosch144TempMin + 1;
const uint8_t kBosch144TempMap[kBosch144TempRange] = {
0b00001, // 16C // Bit[0] to Section 3 Bit[1-4] to Section 1
0b00000, // 17C // TempS3 TempS1
0b00010, // 18c
0b00110, // 19C
0b00100, // 20C
0b01100, // 21C
0b01110, // 22C
0b01010, // 23C
0b01000, // 24C
0b11000, // 25C
0b11010, // 26C
0b10010, // 27C
0b10000, // 28C
0b10100, // 29C
0b10110 // 30C
const uint8_t kBosch144CelsiusMin = 16;
const uint8_t kBosch144CelsiusMax = 30;
const uint8_t kBosch144CelsiusMap[] = {
// Bit[0] to Section 3: TempS4 (the "half-degree" bit)
// Bit[1] to Section 3: TempS3
// Bit[1-4] to Section 1: TempS1
0b000010, // 16C
0b000000, // 17C
0b000100, // 18C
0b001100, // 19C
0b001000, // 20C
0b011000, // 21C
0b011100, // 22C
0b010100, // 23C
0b010000, // 24C
0b110000, // 25C
0b110100, // 26C
0b100100, // 27C
0b100000, // 28C
0b101000, // 29C
0b101100 // 30C
};
const uint8_t kBosch144FahrenheitMin = 60;
const uint8_t kBosch144FahrenheitMax = 86;
const uint8_t kBosch144FahrenheitMap[] = {
// Bit[0] to Section 3: TempS4
// Bit[1] to Section 3: TempS3
// Bit[1-4] to Section 1: TempS1
0b000010, // 60F
0b000011, // 61F
0b000000, // 62F
0b000001, // 63F
0b000100, // 64F
0b000101, // 65F
0b001100, // 66F
0b001101, // 67F
0b001000, // 68F
0b001001, // 69F
0b011000, // 70F
0b011001, // 71F
0b011100, // 72F
0b010100, // 73F
0b010101, // 74F
0b010000, // 75F
0b010001, // 76F
0b110000, // 77F
0b110001, // 78F
0b110100, // 79F
0b110101, // 80F
0b100100, // 81F
0b100000, // 82F
0b100001, // 83F
0b101000, // 84F
0b101001, // 85F
0b101100 // 86F
};
// "OFF" is a 96bit-message the same as Coolix protocol
@@ -100,37 +138,40 @@ const uint8_t kBosch144DefaultState[kBosch144StateLength] = {
union Bosch144Protocol {
uint8_t raw[kBosch144StateLength]; ///< The state in IR code form.
struct {
uint8_t :8; // Fixed value 0b10110010 / 0xB2. ############
uint8_t InnvertS1_1:8; // Invert byte 0b01001101 / 0x4D #
uint8_t :5; // not used (without timer use) #
uint8_t FanS1 :3; // Fan speed bits in Section 1 #
uint8_t InnvertS1_2:8; // Invert byte # Section 1 =
uint8_t :2; // not used (without timer use) # Sektion 2
uint8_t ModeS1 :2; // Operation mode bits S1 #
uint8_t TempS1 :4; // Desired temperature (Celsius) S2 #
uint8_t InnvertS1_3:8; // Invert byte (without timer use) ############
uint8_t :8; // Fixed value 0b10110010 / 0xB2. ############
uint8_t InnvertS1_1 :8; // Invert byte 0b01001101 / 0x4D #
uint8_t :5; // not used (without timer use) #
uint8_t FanS1 :3; // Fan speed bits in Section 1 #
uint8_t InnvertS1_2 :8; // Invert byte # Section 1
uint8_t :2; // not used (without timer use) # =
uint8_t ModeS1 :2; // Operation mode bits S1 # Section 2
uint8_t TempS1 :4; // Desired temperature (Celsius) S2 #
uint8_t InnvertS1_3 :8; // Invert byte (without timer use) ############
uint8_t :8; // Fixed value 0b10110010 / 0xB2. ############
uint8_t InnvertS2_1:8; // Invert byte 0b01001101 / 0x4D #
uint8_t :5; // not used (without timer use) #
uint8_t FanS2 :3; // Fan speed bits in Section 2 #
uint8_t InnvertS2_2:8; // Invert byte # Section 2 =
uint8_t :2; // not used (without timer use) # Sektion 1
uint8_t ModeS2 :2; // Operation mode bits S2 #
uint8_t TempS2 :4; // Desired temperature (Celsius) S2 #
uint8_t InnvertS2_3:8; // Invert byte (without timer use) ###########
uint8_t :8; // Fixed value 0b10110010 / 0xB2. ############
uint8_t InnvertS2_1 :8; // Invert byte 0b01001101 / 0x4D #
uint8_t :5; // not used (without timer use) #
uint8_t FanS2 :3; // Fan speed bits in Section 2 #
uint8_t InnvertS2_2 :8; // Invert byte # Section 2
uint8_t :2; // not used (without timer use) # =
uint8_t ModeS2 :2; // Operation mode bits S2 # Section 1
uint8_t TempS2 :4; // Desired temperature (Celsius) S2 #
uint8_t InnvertS2_3 :8; // Invert byte (without timer use) ###########
uint8_t :8; // Fixed value 0b11010101 / 0xD5 ###########
uint8_t ModeS3 :1; // ModeBit in Section 3 #
uint8_t FanS3 :6; // Fan speed bits in Section 3 #
uint8_t :1; // Unknown #
uint8_t :7; // Unknown #
uint8_t Quiet :1; // Silent-Mode # Section 3
uint8_t :4; // Unknown #
uint8_t TempS3 :1; // Desired temp. Bit in Section3 #
uint8_t :3; // Unknown #
uint8_t :8; // Unknown #
uint8_t ChecksumS3 :8; // Checksum from byte 13-17 ###########
uint8_t :8; // Fixed value 0b11010101 / 0xD5 ###########
uint8_t ModeS3 :1; // ModeBit in Section 3 #
uint8_t FanS3 :6; // Fan speed bits in Section 3 #
uint8_t :1; // Unknown #
uint8_t :5; // Unknown #
uint8_t TempS4 :1; // Desired temp #
uint8_t :1; // Unknown #
uint8_t Quiet :1; // Silent-Mode # Section 3
uint8_t UseFahrenheit :1; // Fahrenheit or Celcius #
uint8_t :3; // Unknown #
uint8_t TempS3 :1; // Desired temp. Bit in Section3 #
uint8_t :3; // Unknown #
uint8_t :8; // Unknown #
uint8_t ChecksumS3 :8; // Checksum from byte 13-17 ###########
};
};
@@ -153,8 +194,10 @@ class IRBosch144AC {
void begin();
void setPower(const bool state);
bool getPower(void) const;
void setTemp(const uint8_t temp);
void setTemp(const uint8_t temp, const bool fahrenheit = false);
uint8_t getTemp(void) const;
void setUseFahrenheit(const bool on);
bool getUseFahrenheit(void) const;
void setFan(const uint16_t speed);
uint16_t getFan(void) const;
void setMode(const uint8_t mode);

View File

@@ -75,6 +75,57 @@ TEST(TestDecodeBosch144, RealExample) {
ASSERT_TRUE(IRAcUtils::decodeToState(&irsend.capture, &result, &prev));
}
TEST(TestDecodeBosch144, DurastarExample) {
IRsendTest irsend(kGpioUnused);
IRrecv irrecv(kGpioUnused);
// Mode: Heat; Fan: auto ; Temp: 73°F
uint16_t rawData[299] = {
4382, 4414, 522, 1630, 522, 552, 550, 1600, 538, 1616, 540, 532, 548,
526, 550, 1602, 548, 528, 548, 528, 550, 1604, 522, 552, 548, 528, 548,
1604, 522, 1630, 546, 528, 548, 1604, 522, 1628, 524, 552, 548, 1604,
546, 1606, 548, 1604, 522, 1630, 520, 1632, 546, 1604, 522, 554, 522,
1630, 520, 554, 522, 552, 522, 552, 524, 552, 546, 530, 546, 528, 548,
528, 548, 1604, 546, 528, 522, 1630, 522, 1630, 522, 1630, 546, 528, 524,
552, 522, 1630, 524, 552, 524, 1630, 520, 554, 522, 554, 522, 554, 548,
1606, 520, 1630, 522, 5224, 4404, 4390, 522, 1630, 522, 554, 520, 1632,
520, 1632, 520, 554, 520, 556, 520, 1632, 520, 556, 520, 556, 520, 1632,
518, 558, 518, 556, 518, 1634, 518, 1634, 518, 558, 518, 1634, 518, 1634,
518, 558, 516, 1636, 516, 1636, 516, 1636, 514, 1638, 514, 1638, 514,
1638, 514, 582, 494, 1658, 492, 582, 492, 584, 492, 584, 490, 584, 492,
584, 490, 586, 488, 586, 488, 1664, 488, 588, 486, 1666, 484, 1666, 486,
1666, 484, 590, 484, 592, 484, 1668, 484, 592, 484, 1666, 486, 590, 484,
592, 484, 592, 484, 1668, 484, 1666, 486, 5262, 4344, 4450, 484, 1668,
484, 1668, 482, 592, 484, 1668, 482, 592, 484, 1680, 472, 594, 482, 1668,
482, 616, 460, 1692, 460, 1692, 458, 616, 460, 616, 460, 1692, 460, 1692,
458, 616, 460, 616, 458, 618, 458, 616, 460, 616, 458, 616, 460, 614,
460, 616, 460, 616, 458, 616, 460, 616, 460, 616, 460, 616, 460, 616,
460, 616, 460, 616, 458, 1692, 458, 616, 460, 616, 460, 616, 460, 616,
444, 632, 438, 638, 434, 642, 434, 642, 434, 640, 434, 640, 434, 1718,
434, 1718, 434, 1718, 434, 1718, 432, 644, 432, 642, 434
}; // DURASTAR DRAW09F2A
uint8_t expectedState[18] = {
0xB2, 0x4D, 0xBF, 0x40, 0x5C, 0xA3,
0xB2, 0x4D, 0xBF, 0x40, 0x5C, 0xA3,
0xD5, 0x66, 0x00, 0x01, 0x00, 0x3C};
irsend.begin();
irsend.reset();
irsend.sendRaw(rawData, 299, 38000);
irsend.makeDecodeResult();
ASSERT_TRUE(irrecv.decode(&irsend.capture));
EXPECT_EQ(decode_type_t::BOSCH144, irsend.capture.decode_type);
EXPECT_EQ(kBosch144Bits, irsend.capture.bits);
EXPECT_STATE_EQ(expectedState, irsend.capture.state, irsend.capture.bits);
EXPECT_EQ(
"Power: On, Mode: 6 (Heat), Fan: 0 (Auto), Temp: 73F, Quiet: Off",
IRAcUtils::resultAcToString(&irsend.capture));
stdAc::state_t result, prev;
ASSERT_TRUE(IRAcUtils::decodeToState(&irsend.capture, &result, &prev));
}
TEST(TestDecodeBosch144, SyntheticSelfDecode) {
IRsendTest irsend(kGpioUnused);
IRrecv irrecv(kGpioUnused);