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
72 changes: 44 additions & 28 deletions src/ir_Samsung.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/621
/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1062
/// @see http://elektrolab.wz.cz/katalog/samsung_protocol.pdf
/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1538 (Checksum)

#include "ir_Samsung.h"
#include <algorithm>
Expand Down Expand Up @@ -290,46 +291,61 @@ void IRSamsungAc::stateReset(const bool forcepower, const bool initialPower) {
/// Set up hardware to be able to send a message.
void IRSamsungAc::begin(void) { _irsend.begin(); }

/// Calculate the checksum for a given state.
/// @param[in] state The array to calc the checksum of.
/// @param[in] length The length/size of the array.
/// Get the existing checksum for a given state section.
/// @param[in] section The array to extract the checksum from.
/// @return The existing checksum value.
/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1538#issuecomment-894645947
uint8_t IRSamsungAc::getSectionChecksum(const uint8_t *section) {
return ((GETBITS8(*(section + 2), kLowNibble, kNibbleSize) << kNibbleSize) +
GETBITS8(*(section + 1), kHighNibble, kNibbleSize));
}

/// Calculate the checksum for a given state section.
/// @param[in] section The array to calc the checksum of.
/// @return The calculated checksum value.
uint8_t IRSamsungAc::calcChecksum(const uint8_t state[],
const uint16_t length) {
/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1538#issuecomment-894645947
uint8_t IRSamsungAc::calcSectionChecksum(const uint8_t *section) {
uint8_t sum = 0;
// Safety check so we don't go outside the array.
if (length < 7) return 255;
// Shamelessly inspired by:
// https://github.com/adafruit/Raw-IR-decoder-for-Arduino/pull/3/files
// Count most of the '1' bits after the checksum location.
sum += countBits(state[length - 7], 8);
sum -= countBits(GETBITS8(state[length - 6], kLowNibble, kNibbleSize), 8);
sum += countBits(GETBITS8(state[length - 5], 1, 7), 8);
sum += countBits(state + length - 4, 3);
return GETBITS8(28 - sum, kLowNibble, kNibbleSize);

sum += countBits(*section, 8); // Include the entire first byte
// The lower half of the second byte.
sum += countBits(GETBITS8(*(section + 1), kLowNibble, kNibbleSize), 8);
// The upper half of the third byte.
sum += countBits(GETBITS8(*(section + 2), kHighNibble, kNibbleSize), 8);
// The next 4 bytes.
sum += countBits(section + 3, 4);
// Bitwise invert the result.
return sum ^ UINT8_MAX;
}

/// Verify the checksum is valid for a given state.
/// @param[in] state The array to verify the checksum of.
/// @param[in] length The length/size of the array.
/// @return true, if the state has a valid checksum. Otherwise, false.
bool IRSamsungAc::validChecksum(const uint8_t state[], const uint16_t length) {
if (length < kSamsungAcStateLength)
return true; // No checksum to compare with. Assume okay.
uint8_t offset = 0;
if (length >= kSamsungAcExtendedStateLength) offset = 7;
return (GETBITS8(state[length - 6], kHighNibble, kNibbleSize) ==
IRSamsungAc::calcChecksum(state, length)) &&
(GETBITS8(state[length - (13 + offset)], kHighNibble, kNibbleSize) ==
IRSamsungAc::calcChecksum(state, length - (7 + offset)));
bool result = true;
const uint16_t maxlength =
(length > kSamsungAcExtendedStateLength) ? kSamsungAcExtendedStateLength
: length;
for (uint16_t offset = 0;
offset + kSamsungAcSectionLength <= maxlength;
offset += kSamsungAcSectionLength)
result &= (getSectionChecksum(state + offset) ==
calcSectionChecksum(state + offset));
return result;
}

/// Update the checksum for the internal state.
/// @param[in] length The length/size of the internal array to checksum.
void IRSamsungAc::checksum(uint16_t length) {
if (length < 13) return;
_.Sum2 = calcChecksum(_.raw, length);
_.Sum1 = calcChecksum(_.raw, length - 7);
void IRSamsungAc::checksum(void) {
uint8_t sectionsum = calcSectionChecksum(_.raw);
_.Sum1Upper = GETBITS8(sectionsum, kHighNibble, kNibbleSize);
_.Sum1Lower = GETBITS8(sectionsum, kLowNibble, kNibbleSize);
sectionsum = calcSectionChecksum(_.raw + kSamsungAcSectionLength);
_.Sum2Upper = GETBITS8(sectionsum, kHighNibble, kNibbleSize);
_.Sum2Lower = GETBITS8(sectionsum, kLowNibble, kNibbleSize);
sectionsum = calcSectionChecksum(_.raw + kSamsungAcSectionLength * 2);
_.Sum3Upper = GETBITS8(sectionsum, kHighNibble, kNibbleSize);
_.Sum3Lower = GETBITS8(sectionsum, kLowNibble, kNibbleSize);
}

#if SEND_SAMSUNG_AC
Expand Down
71 changes: 56 additions & 15 deletions src/ir_Samsung.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/621
/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1062
/// @see http://elektrolab.wz.cz/katalog/samsung_protocol.pdf
/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1538 (Checksum)

// Supports:
// Brand: Samsung, Model: UA55H6300 TV (SAMSUNG)
Expand Down Expand Up @@ -88,19 +89,59 @@ union SamsungProtocol{
uint8_t :6;
};
struct {
uint8_t :8;
// 1st Section
// Byte 0
uint8_t :8;
// Byte 1
uint8_t :4;
uint8_t Sum1 :4;
uint8_t pad1[6];
uint8_t :4;
uint8_t Sum1Lower :4;
// Byte 2
uint8_t Sum1Upper :4;
uint8_t :4;
// Byte 3
uint8_t :8;
// Byte 4
uint8_t :8;
// Byte 5
uint8_t :8;
// Byte 6
uint8_t :8;
// 2nd Section
// Byte 7
uint8_t :8;
// Byte 8
uint8_t :4;
uint8_t Sum2 :4;
uint8_t :8;
uint8_t :4;
uint8_t Sum2Lower :4;
// Byte 9
uint8_t Sum2Upper :4;
uint8_t :4;
// Byte 10
uint8_t :1;
uint8_t Breeze :3; // WindFree
uint8_t :4;
uint8_t :1;
uint8_t Breeze :3; // WindFree
uint8_t :4;
// Byte 11
uint8_t :8;
// Byte 12
uint8_t :8;
// Byte 13
uint8_t :8;
// 3rd Section
// Byte 14
uint8_t :8;
// Byte 15
uint8_t :4;
uint8_t Sum3Lower :4;
// Byte 16
uint8_t Sum3Upper :4;
uint8_t :4;
// Byte 17
uint8_t :8;
// Byte 18
uint8_t :8;
// Byte 19
uint8_t :8;
// Byte 20
uint8_t :8;
};
};

Expand All @@ -110,8 +151,8 @@ const uint8_t kSamsungAcSwingMove = 0b010;
const uint8_t kSamsungAcSwingStop = 0b111;
const uint8_t kSamsungAcPowerful10On = 0b011;
const uint8_t kSamsungAcBreezeOn = 0b101;
const uint8_t kSamsungAcMinTemp = 16; // C Mask 0b11110000
const uint8_t kSamsungAcMaxTemp = 30; // C Mask 0b11110000
const uint8_t kSamsungAcMinTemp = 16; // C Mask 0b11110000
const uint8_t kSamsungAcMaxTemp = 30; // C Mask 0b11110000
const uint8_t kSamsungAcAutoTemp = 25; // C Mask 0b11110000
const uint8_t kSamsungAcAuto = 0;
const uint8_t kSamsungAcCool = 1;
Expand Down Expand Up @@ -177,10 +218,10 @@ class IRSamsungAc {
uint8_t* getRaw(void);
void setRaw(const uint8_t new_code[],
const uint16_t length = kSamsungAcStateLength);
static uint8_t calcSectionChecksum(const uint8_t *section);
static uint8_t getSectionChecksum(const uint8_t *section);
static bool validChecksum(const uint8_t state[],
const uint16_t length = kSamsungAcStateLength);
static uint8_t calcChecksum(const uint8_t state[],
const uint16_t length = kSamsungAcStateLength);
static uint8_t convertMode(const stdAc::opmode_t mode);
static uint8_t convertFan(const stdAc::fanspeed_t speed);
static stdAc::opmode_t toCommonMode(const uint8_t mode);
Expand All @@ -199,7 +240,7 @@ class IRSamsungAc {
SamsungProtocol _;
bool _forcepower; ///< Hack to know when we need to send a special power mesg
bool _lastsentpowerstate;
void checksum(const uint16_t length = kSamsungAcStateLength);
void checksum(void);
};

#endif // IR_SAMSUNG_H_
Loading