Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions src/IRrecv.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -830,8 +830,7 @@ bool IRrecv::decode(decode_results *results, irparams_t *save,
DPRINTLN("Attempting Samsung AC (extended) decode");
// Check the extended size first, as it should fail fast due to longer
// length.
if (decodeSamsungAC(results, offset, kSamsungAcExtendedBits, false))
return true;
if (decodeSamsungAC(results, offset, kSamsungAcExtendedBits)) return true;
// Now check for the more common length.
DPRINTLN("Attempting Samsung AC decode");
if (decodeSamsungAC(results, offset, kSamsungAcBits)) return true;
Expand Down
46 changes: 14 additions & 32 deletions src/ir_Samsung.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -372,9 +372,9 @@ void IRSamsungAc::send(const uint16_t repeat, const bool calcchecksum) {
/// Samsung A/C requires an extended length message when you want to
/// change the power operating mode of the A/C unit.
void IRSamsungAc::sendExtended(const uint16_t repeat, const bool calcchecksum) {
_lastsentpowerstate = getPower(); // Remember the last power state sent.
static const uint8_t extended_middle_section[kSamsungAcSectionLength] = {
0x01, 0xD2, 0x0F, 0x00, 0x00, 0x00, 0x00};
if (calcchecksum) checksum();
// Copy/convert the internal state to an extended state by
// copying the second section to the third section, and inserting the extended
// middle (second) section.
Expand All @@ -383,13 +383,13 @@ void IRSamsungAc::sendExtended(const uint16_t repeat, const bool calcchecksum) {
kSamsungAcSectionLength);
std::memcpy(_.raw + kSamsungAcSectionLength, extended_middle_section,
kSamsungAcSectionLength);
if (calcchecksum) checksum();
// Send it.
_irsend.sendSamsungAC(_.raw, kSamsungAcExtendedStateLength, repeat);
// Now revert it by copying the third section over the second section.
std::memcpy(_.raw + kSamsungAcSectionLength,
_.raw + 2* kSamsungAcSectionLength,
_.raw + 2 * kSamsungAcSectionLength,
kSamsungAcSectionLength);
_lastsentpowerstate = getPower(); // Remember the last power state sent.
}

/// Send the special extended "On" message as the library can't seem to
Expand Down Expand Up @@ -448,14 +448,13 @@ void IRSamsungAc::off(void) { setPower(false); }
/// Change the power setting.
/// @param[in] on true, the setting is on. false, the setting is off.
void IRSamsungAc::setPower(const bool on) {
_.Power1 = !on; // Cleared when on.
_.Power6 = (on ? 0b11 : 0b00);
_.Power1 = _.Power2 = (on ? 0b11 : 0b00);
}

/// Get the value of the current power setting.
/// @return true, the setting is on. false, the setting is off.
bool IRSamsungAc::getPower(void) const {
return (_.Power6 == 0b11) && !_.Power1;
return _.Power1 == 0b11 && _.Power2 == 0b11;
}

/// Set the temperature.
Expand Down Expand Up @@ -565,15 +564,12 @@ void IRSamsungAc::setClean(const bool on) {

/// Get the Quiet setting of the A/C.
/// @return true, the setting is on. false, the setting is off.
bool IRSamsungAc::getQuiet(void) const {
return !_.Quiet1 && _.Quiet5;
}
bool IRSamsungAc::getQuiet(void) const { return _.Quiet; }

/// Set the Quiet setting of the A/C.
/// @param[in] on true, the setting is on. false, the setting is off.
void IRSamsungAc::setQuiet(const bool on) {
_.Quiet1 = !on; // Cleared when on.
_.Quiet5 = on;
_.Quiet = on;
if (on) {
// Quiet mode seems to set fan speed to auto.
setFan(kSamsungAcFanAuto);
Expand All @@ -584,23 +580,20 @@ void IRSamsungAc::setQuiet(const bool on) {
/// Get the Powerful (Turbo) setting of the A/C.
/// @return true, the setting is on. false, the setting is off.
bool IRSamsungAc::getPowerful(void) const {
return !(_.Powerful8 & kSamsungAcPowerfulMask8) &&
(_.Powerful10 == kSamsungAcPowerful10On) &&
return (_.Powerful == kSamsungAcPowerfulOn) &&
(_.Fan == kSamsungAcFanTurbo);
}

/// Set the Powerful (Turbo) setting of the A/C.
/// @param[in] on true, the setting is on. false, the setting is off.
void IRSamsungAc::setPowerful(const bool on) {
uint8_t off_value = getBreeze() ? kSamsungAcBreezeOn : 0b000;
_.Powerful10 = (on ? kSamsungAcPowerful10On : off_value);
_.Powerful = (on ? kSamsungAcPowerfulOn : off_value);
if (on) {
_.Powerful8 &= ~kSamsungAcPowerfulMask8; // Bit needs to be cleared.
// Powerful mode sets fan speed to Turbo.
setFan(kSamsungAcFanTurbo);
setQuiet(false); // Powerful 'on' is mutually exclusive to Quiet.
} else {
_.Powerful8 |= kSamsungAcPowerfulMask8; // Bit needs to be set.
// Turning off Powerful mode sets fan speed to Auto if we were in Turbo mode
if (_.Fan == kSamsungAcFanTurbo) setFan(kSamsungAcFanAuto);
}
Expand All @@ -618,7 +611,7 @@ bool IRSamsungAc::getBreeze(void) const {
/// @param[in] on true, the setting is on. false, the setting is off.
/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1062
void IRSamsungAc::setBreeze(const bool on) {
uint8_t off_value = getPowerful() ? kSamsungAcPowerful10On : 0b000;
const uint8_t off_value = getPowerful() ? kSamsungAcPowerfulOn : 0b000;
_.Breeze = (on ? kSamsungAcBreezeOn : off_value);
if (on) {
setFan(kSamsungAcFanAuto);
Expand All @@ -628,27 +621,19 @@ void IRSamsungAc::setBreeze(const bool on) {

/// Get the Display (Light/LED) setting of the A/C.
/// @return true, the setting is on. false, the setting is off.
bool IRSamsungAc::getDisplay(void) const {
return _.Display;
}
bool IRSamsungAc::getDisplay(void) const { return _.Display; }

/// Set the Display (Light/LED) setting of the A/C.
/// @param[in] on true, the setting is on. false, the setting is off.
void IRSamsungAc::setDisplay(const bool on) {
_.Display = on;
}
void IRSamsungAc::setDisplay(const bool on) { _.Display = on; }

/// Get the Ion (Filter) setting of the A/C.
/// @return true, the setting is on. false, the setting is off.
bool IRSamsungAc::getIon(void) const {
return _.Ion;
}
bool IRSamsungAc::getIon(void) const { return _.Ion; }

/// Set the Ion (Filter) setting of the A/C.
/// @param[in] on true, the setting is on. false, the setting is off.
void IRSamsungAc::setIon(const bool on) {
_.Ion = on;
}
void IRSamsungAc::setIon(const bool on) { _.Ion = on; }

/// Convert a stdAc::opmode_t enum into its native mode.
/// @param[in] mode The enum to be converted.
Expand Down Expand Up @@ -811,9 +796,6 @@ bool IRrecv::decodeSamsungAC(decode_results *results, uint16_t offset,
offset += used;
}
// Compliance
// Is the signature correct?
DPRINTLN("DEBUG: Checking signature.");
if (results->state[0] != 0x02 || results->state[2] != 0x0F) return false;
if (strict) {
// Is the checksum valid?
if (!IRSamsungAc::validChecksum(results->state, nbits / 8)) {
Expand Down
51 changes: 28 additions & 23 deletions src/ir_Samsung.h
Original file line number Diff line number Diff line change
Expand Up @@ -43,36 +43,40 @@ union SamsungProtocol{
uint8_t raw[kSamsungAcExtendedStateLength]; ///< State in code form.
struct {
// Byte 0
uint8_t :8;
uint8_t :8;
// Byte 1
uint8_t :4;
uint8_t Quiet1 :1;
uint8_t Power1 :1;
uint8_t :2;
// Byte 2~4
uint8_t pad0[3];
uint8_t :4; // Sum1Lower
// Byte 2
uint8_t :4; // Sum1Upper
uint8_t :4;
// Byte 3
uint8_t :8;
// Byte 4
uint8_t :8;
// Byte 5
uint8_t :5;
uint8_t Quiet5 :1;
uint8_t Quiet :1;
uint8_t :2;
// Byte 6
uint8_t :4;
uint8_t Power6 :2;
uint8_t Power1 :2;
uint8_t :2;
// Byte 7
uint8_t :8;
uint8_t :8;
// Byte 8
uint8_t Powerful8 :8;
uint8_t :4;
uint8_t :4; // Sum2Lower
// Byte 9
uint8_t :4;
uint8_t Swing :3;
uint8_t :1;
uint8_t :4; // Sum1Upper
uint8_t Swing :3;
uint8_t :1;
// Byte 10
uint8_t :1;
uint8_t Powerful10 :3;
uint8_t Display :1;
uint8_t :2;
uint8_t Clean10 :1;
uint8_t :1;
uint8_t Powerful :3;
uint8_t Display :1;
uint8_t :2;
uint8_t Clean10 :1;
// Byte 11
uint8_t Ion :1;
uint8_t Clean11 :1;
Expand All @@ -84,9 +88,11 @@ union SamsungProtocol{
uint8_t Mode :3;
uint8_t :1;
// Byte 13
uint8_t :1;
uint8_t Beep :1;
uint8_t :6;
uint8_t :1;
uint8_t Beep :1;
uint8_t :2;
uint8_t Power2 :2;
uint8_t :2;
};
struct {
// 1st Section
Expand Down Expand Up @@ -146,10 +152,9 @@ union SamsungProtocol{
};

// Constants
const uint8_t kSamsungAcPowerfulMask8 = 0b01010000;
const uint8_t kSamsungAcSwingMove = 0b010;
const uint8_t kSamsungAcSwingStop = 0b111;
const uint8_t kSamsungAcPowerful10On = 0b011;
const uint8_t kSamsungAcPowerfulOn = 0b011;
const uint8_t kSamsungAcBreezeOn = 0b101;
const uint8_t kSamsungAcMinTemp = 16; // C Mask 0b11110000
const uint8_t kSamsungAcMaxTemp = 30; // C Mask 0b11110000
Expand Down
133 changes: 133 additions & 0 deletions test/ir_Samsung_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1671,3 +1671,136 @@ TEST(TestIRSamsungAcClass, SectionChecksums) {
EXPECT_EQ(IRSamsungAc::getSectionChecksum(extended_off + 14),
IRSamsungAc::calcSectionChecksum(extended_off + 14));
}

TEST(TestIRSamsungAcClass, Issue1648) {
IRSamsungAc ac(kGpioUnused);
IRrecv irrecv(kGpioUnused);
const uint8_t onState[kSamsungAcExtendedStateLength] = {
0x02, 0x92, 0x0F, 0x00, 0x00, 0x00, 0xF0,
0x01, 0xD2, 0x0F, 0x00, 0x00, 0x00, 0x00,
0x01, 0xC2, 0xFE, 0x71, 0x90, 0x15, 0xF0};
const String onText = "Power: On, Mode: 1 (Cool), Temp: 25C, Fan: 2 (Low), "
"Swing: Off, Beep: Off, Clean: Off, Quiet: Off, "
"Powerful: Off, Breeze: Off, Light: On, Ion: Off";
const uint8_t extended_offState[kSamsungAcExtendedStateLength] = {
0x02, 0xB2, 0x0F, 0x00, 0x00, 0x00, 0xC0,
0x01, 0xD2, 0x0F, 0x00, 0x00, 0x00, 0x00,
0x01, 0xE2, 0xFE, 0x71, 0x90, 0x15, 0xC0};
const uint8_t short_offState[kSamsungAcStateLength] = {
0x02, 0xB2, 0x0F, 0x00, 0x00, 0x00, 0xC0,
0x01, 0xE2, 0xFE, 0x71, 0x90, 0x15, 0xC0};
const String offText = "Power: Off, Mode: 1 (Cool), Temp: 25C, Fan: 2 (Low), "
"Swing: Off, Beep: Off, Clean: Off, Quiet: Off, "
"Powerful: Off, Breeze: Off, Light: On, Ion: Off";
const uint8_t coolState[kSamsungAcStateLength] = {
0x02, 0x92, 0x0F, 0x00, 0x00, 0x00, 0xF0,
0x01, 0xC2, 0xFE, 0x71, 0x90, 0x15, 0xF0};

// "setup()"" from provided code.
ac.begin(); // User code
ac.off(); // User code
ac.setFan(kSamsungAcFanLow); // User code
ac.setMode(kSamsungAcCool); // User code
ac.setTemp(25); // User code
ac.setSwing(false); // User code

// Go through "loop()" from provided code.
for (uint8_t i = 0; i < 2; i++) {
ac.on(); // User code
ac.send(); // User code

// Verify what was sent.
ac._irsend.makeDecodeResult();
EXPECT_TRUE(irrecv.decode(&ac._irsend.capture));
EXPECT_EQ(SAMSUNG_AC, ac._irsend.capture.decode_type);
EXPECT_EQ(kSamsungAcExtendedBits, ac._irsend.capture.bits);
EXPECT_STATE_EQ(onState, ac._irsend.capture.state, ac._irsend.capture.bits);
EXPECT_EQ(onText, IRAcUtils::resultAcToString(&ac._irsend.capture));
EXPECT_TRUE(ac._lastsentpowerstate);
ac._irsend.reset();

ac.setMode(kSamsungAcCool); // User code
ac.send(); // User code

// Verify what was sent.
ac._irsend.makeDecodeResult();
EXPECT_TRUE(irrecv.decode(&ac._irsend.capture));
EXPECT_EQ(SAMSUNG_AC, ac._irsend.capture.decode_type);
EXPECT_EQ(kSamsungAcBits, ac._irsend.capture.bits);
EXPECT_STATE_EQ(coolState, ac._irsend.capture.state,
ac._irsend.capture.bits);
EXPECT_EQ(onText, IRAcUtils::resultAcToString(&ac._irsend.capture));
ac._irsend.reset();
EXPECT_TRUE(ac._lastsentpowerstate);
EXPECT_FALSE(ac._forcepower);

ac.off(); // User code
ac.send(); // User code

// Verify what was sent.
ac._irsend.makeDecodeResult();
EXPECT_TRUE(irrecv.decode(&ac._irsend.capture));
EXPECT_EQ(SAMSUNG_AC, ac._irsend.capture.decode_type);
EXPECT_EQ(kSamsungAcExtendedBits, ac._irsend.capture.bits);
EXPECT_STATE_EQ(extended_offState, ac._irsend.capture.state,
ac._irsend.capture.bits);
EXPECT_EQ(offText, IRAcUtils::resultAcToString(&ac._irsend.capture));
EXPECT_FALSE(ac._lastsentpowerstate);
ac._irsend.reset();

ac.off(); // User code
ac.send(); // User code

// Verify what was sent.
ac._irsend.makeDecodeResult();
EXPECT_TRUE(irrecv.decode(&ac._irsend.capture));
EXPECT_EQ(SAMSUNG_AC, ac._irsend.capture.decode_type);
EXPECT_EQ(kSamsungAcBits, ac._irsend.capture.bits);
EXPECT_STATE_EQ(short_offState, ac._irsend.capture.state,
ac._irsend.capture.bits);
EXPECT_EQ(offText, IRAcUtils::resultAcToString(&ac._irsend.capture));
EXPECT_FALSE(ac._lastsentpowerstate);
ac._irsend.reset();
// End of "loop()" code.
}

// Data from https://github.com/crankyoldgit/IRremoteESP8266/issues/1648#issuecomment-950822399
const uint8_t expectedState[kSamsungAcExtendedStateLength] = {
0x02, 0xB2, 0x0F, 0x00, 0x00, 0x00, 0xC0,
0x01, 0xD2, 0x0F, 0x00, 0x00, 0x00, 0x00,
0x01, 0x12, 0xAF, 0x71, 0x80, 0x15, 0xC0};
const String expectedText = "Power: Off, Mode: 1 (Cool), Temp: 24C, "
"Fan: 2 (Low), Swing: On, Beep: Off, Clean: Off, "
"Quiet: Off, Powerful: Off, Breeze: Off, "
"Light: On, Ion: Off";

ac.stateReset();
ac.setRaw(expectedState, kSamsungAcExtendedStateLength);
EXPECT_EQ(expectedText, ac.toString());

// Try to generate the same message.
ac.stateReset();
ac.off();
ac.setMode(kSamsungAcCool);
ac.setTemp(24);
ac.setFan(kSamsungAcFanLow);
ac.setSwing(true);
ac.setBeep(false);
ac.setClean(false);
ac.setQuiet(false);
ac.setPowerful(false);
ac.setBreeze(false);
ac.setDisplay(true);
ac.setIon(false);
EXPECT_EQ(expectedText, ac.toString());

ac.send();
ac._irsend.makeDecodeResult();
EXPECT_TRUE(irrecv.decode(&ac._irsend.capture));
EXPECT_EQ(SAMSUNG_AC, ac._irsend.capture.decode_type);
EXPECT_EQ(kSamsungAcExtendedBits, ac._irsend.capture.bits);
EXPECT_STATE_EQ(expectedState, ac._irsend.capture.state,
ac._irsend.capture.bits);
EXPECT_EQ(expectedText, IRAcUtils::resultAcToString(&ac._irsend.capture));
ac._irsend.reset();
}