401 lines
12 KiB
C++
401 lines
12 KiB
C++
#include <SamsungHeatpumpIR.h>
|
|
|
|
// These are protected methods, i.e. generic Samsung instances cannot be created directly
|
|
SamsungHeatpumpIR::SamsungHeatpumpIR() : HeatpumpIR()
|
|
{
|
|
}
|
|
|
|
void SamsungHeatpumpIR::send(IRSender& IR, uint8_t powerModeCmd, uint8_t operatingModeCmd, uint8_t fanSpeedCmd, uint8_t temperatureCmd, uint8_t swingVCmd, uint8_t swingHCmd)
|
|
{
|
|
}
|
|
|
|
// Samsung models
|
|
|
|
SamsungAQVHeatpumpIR::SamsungAQVHeatpumpIR() : SamsungHeatpumpIR()
|
|
{
|
|
static const char model[] PROGMEM = "samsung_aqv";
|
|
static const char info[] PROGMEM = "{\"mdl\":\"samsung_aqv\",\"dn\":\"Samsung AQV\",\"mT\":16,\"xT\":27,\"fs\":4}";
|
|
|
|
_model = model;
|
|
_info = info;
|
|
}
|
|
|
|
SamsungAQV12MSANHeatpumpIR::SamsungAQV12MSANHeatpumpIR() : SamsungAQVHeatpumpIR()
|
|
{
|
|
static const char model[] PROGMEM = "samsung_aqv12msan";
|
|
static const char info[] PROGMEM = "{\"mdl\":\"samsung_aqv12msan\",\"dn\":\"Samsung AQV12MSAN\",\"mT\":16,\"xT\":27,\"fs\":4}";
|
|
|
|
_model = model;
|
|
_info = info;
|
|
_samsungAQVModel = MODEL_AQV12_MSAN;
|
|
}
|
|
|
|
SamsungFJMHeatpumpIR::SamsungFJMHeatpumpIR() : SamsungHeatpumpIR()
|
|
{
|
|
static const char model[] PROGMEM = "samsung_fjm";
|
|
static const char info[] PROGMEM = "{\"mdl\":\"samsung_fjm\",\"dn\":\"Samsung FJM\",\"mT\":16,\"xT\":27,\"fs\":4}";
|
|
|
|
_model = model;
|
|
_info = info;
|
|
}
|
|
|
|
// Samsung AQV12PSBN / AQV09ASA heatpump control (remote control P/N zzz)
|
|
|
|
void SamsungAQVHeatpumpIR::send(IRSender& IR, uint8_t powerModeCmd, uint8_t operatingModeCmd, uint8_t fanSpeedCmd, uint8_t temperatureCmd, uint8_t swingVCmd, uint8_t swingHCmd)
|
|
{
|
|
(void)swingVCmd;
|
|
(void)swingHCmd;
|
|
|
|
// Sensible defaults for the heat pump mode
|
|
|
|
uint8_t powerMode = SAMSUNG_AIRCON1_MODE_ON;
|
|
uint8_t operatingMode = SAMSUNG_AIRCON1_MODE_HEAT;
|
|
uint8_t fanSpeed = SAMSUNG_AIRCON1_FAN_AUTO;
|
|
uint8_t temperature = 23;
|
|
uint8_t swingV = SAMSUNG_AIRCON1_VS_AUTO;
|
|
|
|
switch (operatingModeCmd)
|
|
{
|
|
case MODE_AUTO:
|
|
operatingMode = SAMSUNG_AIRCON1_MODE_AUTO;
|
|
fanSpeedCmd = FAN_AUTO; // Fan speed is always 'AUTO' in AUTO mode
|
|
break;
|
|
case MODE_HEAT:
|
|
operatingMode = SAMSUNG_AIRCON1_MODE_HEAT;
|
|
break;
|
|
case MODE_COOL:
|
|
operatingMode = SAMSUNG_AIRCON1_MODE_COOL;
|
|
break;
|
|
case MODE_DRY:
|
|
operatingMode = SAMSUNG_AIRCON1_MODE_DRY;
|
|
fanSpeedCmd = FAN_AUTO; // Fan speed is always 'AUTO' in DRY mode
|
|
break;
|
|
case MODE_FAN:
|
|
operatingMode = SAMSUNG_AIRCON1_MODE_FAN;
|
|
if ( fanSpeedCmd == FAN_AUTO ) {
|
|
fanSpeedCmd = FAN_1; // Fan speed cannot be 'AUTO' in FAN mode
|
|
}
|
|
break;
|
|
}
|
|
|
|
switch (fanSpeedCmd)
|
|
{
|
|
case FAN_AUTO:
|
|
fanSpeed = SAMSUNG_AIRCON1_FAN_AUTO;
|
|
break;
|
|
case FAN_1:
|
|
fanSpeed = SAMSUNG_AIRCON1_FAN1;
|
|
break;
|
|
case FAN_2:
|
|
fanSpeed = SAMSUNG_AIRCON1_FAN2;
|
|
break;
|
|
case FAN_3:
|
|
fanSpeed = SAMSUNG_AIRCON1_FAN3;
|
|
break;
|
|
}
|
|
|
|
if ( temperatureCmd > 15 && temperatureCmd < 28)
|
|
{
|
|
temperature = temperatureCmd;
|
|
}
|
|
|
|
switch (swingVCmd)
|
|
{
|
|
case VDIR_SWING:
|
|
swingV = SAMSUNG_AIRCON1_VS_SWING;
|
|
break;
|
|
}
|
|
|
|
// power offmode is something special, so set it latest to treat
|
|
if (powerModeCmd == POWER_OFF)
|
|
{
|
|
powerMode = SAMSUNG_AIRCON1_MODE_OFF;
|
|
if (_samsungAQVModel == MODEL_AQV12_MSAN)
|
|
{
|
|
if (operatingModeCmd == MODE_AUTO)
|
|
{
|
|
fanSpeed = 0x0D; // reverse enginering remote
|
|
}
|
|
}
|
|
}
|
|
|
|
sendSamsung(IR, powerMode, operatingMode, fanSpeed, temperature, swingV);
|
|
}
|
|
|
|
// Send the Samsung code
|
|
|
|
void SamsungAQVHeatpumpIR::sendSamsung(IRSender& IR, uint8_t powerMode, uint8_t operatingMode, uint8_t fanSpeed, uint8_t temperature, uint8_t swingV)
|
|
{
|
|
uint8_t SamsungTemplate[] = { 0x02, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x00, // Header part (0-6)
|
|
0x01, 0xD2, 0x0F, 0x00, 0x00, 0x00, 0x00, // Always the same data on POWER messages (7-13)
|
|
0x01, 0x00, 0xFE, 0x71, 0x00, 0x00, 0x00 }; // The actual data is in this part, on uint8_ts 14-20
|
|
|
|
uint8_t SamsungChecksum = 0;
|
|
|
|
// Set the power mode on the template message, also add the first part checksum
|
|
SamsungTemplate[6] = powerMode;
|
|
if ( powerMode == SAMSUNG_AIRCON1_MODE_ON ) {
|
|
SamsungTemplate[1] = 0x92;
|
|
} else {
|
|
SamsungTemplate[1] = 0xB2;
|
|
}
|
|
|
|
SamsungTemplate[20] = powerMode;
|
|
|
|
// Set the fan speed and the operating mode on the template message
|
|
SamsungTemplate[19] = operatingMode | fanSpeed;
|
|
|
|
// Set the temperature on the template message
|
|
SamsungTemplate[18] = (temperature - 16) << 4;
|
|
|
|
// Set the vertical swing mode on the template message
|
|
SamsungTemplate[16] = swingV;
|
|
|
|
// Calculate the byte 15 checksum
|
|
// Count the number of ONE bits on message uint8_ts 15-20
|
|
for (uint8_t j=16; j<20; j++) {
|
|
uint8_t Samsungbyte = SamsungTemplate[j];
|
|
for (uint8_t i=0; i<8; i++) {
|
|
if ( (Samsungbyte & 0x01) == 0x01 ) {
|
|
SamsungChecksum++;
|
|
}
|
|
Samsungbyte >>= 1;
|
|
}
|
|
}
|
|
|
|
// Transform the number of ONE bits to the actual checksum
|
|
SamsungChecksum = 28 - SamsungChecksum;
|
|
SamsungChecksum <<= 4;
|
|
SamsungChecksum |= (powerMode == SAMSUNG_AIRCON1_MODE_OFF && _samsungAQVModel == MODEL_AQV12_MSAN) ? 0x22 : 0x02;
|
|
|
|
SamsungTemplate[15] = SamsungChecksum;
|
|
|
|
// incredible hack if power off and temp = 20 and mode heat, dry or cool
|
|
if (powerMode == SAMSUNG_AIRCON1_MODE_OFF && _samsungAQVModel == MODEL_AQV12_MSAN
|
|
&& (SamsungTemplate[18] == SAMSUNG_AIRCON1_MODE_HEAT || SamsungTemplate[18] == SAMSUNG_AIRCON1_MODE_DRY || SamsungTemplate[18] == SAMSUNG_AIRCON1_MODE_COOL))
|
|
{
|
|
SamsungTemplate[15] = 0x02;
|
|
SamsungTemplate[16] = 0xFF; //normally this is swingV
|
|
}
|
|
|
|
// 38 kHz PWM frequency
|
|
IR.setFrequency(38);
|
|
|
|
// Header
|
|
IR.mark(SAMSUNG_AIRCON1_HDR_MARK);
|
|
IR.space(SAMSUNG_AIRCON1_HDR_SPACE);
|
|
|
|
// Payload header part
|
|
for (int i=0; i<7; i++) {
|
|
IR.sendIRbyte(SamsungTemplate[i], SAMSUNG_AIRCON1_BIT_MARK, SAMSUNG_AIRCON1_ZERO_SPACE, SAMSUNG_AIRCON1_ONE_SPACE);
|
|
}
|
|
|
|
// Pause + new header
|
|
IR.mark(SAMSUNG_AIRCON1_BIT_MARK);
|
|
IR.space(SAMSUNG_AIRCON1_MSG_SPACE);
|
|
|
|
IR.mark(SAMSUNG_AIRCON1_HDR_MARK);
|
|
IR.space(SAMSUNG_AIRCON1_HDR_SPACE);
|
|
|
|
// Payload power message part
|
|
for (int i=7; i<14; i++) {
|
|
IR.sendIRbyte(SamsungTemplate[i], SAMSUNG_AIRCON1_BIT_MARK, SAMSUNG_AIRCON1_ZERO_SPACE, SAMSUNG_AIRCON1_ONE_SPACE);
|
|
}
|
|
|
|
// Pause + new header
|
|
IR.mark(SAMSUNG_AIRCON1_BIT_MARK);
|
|
IR.space(SAMSUNG_AIRCON1_MSG_SPACE);
|
|
|
|
IR.mark(SAMSUNG_AIRCON1_HDR_MARK);
|
|
IR.space(SAMSUNG_AIRCON1_HDR_SPACE);
|
|
|
|
// Payload data message part
|
|
for (int i=14; i<21; i++) {
|
|
IR.sendIRbyte(SamsungTemplate[i], SAMSUNG_AIRCON1_BIT_MARK, SAMSUNG_AIRCON1_ZERO_SPACE, SAMSUNG_AIRCON1_ONE_SPACE);
|
|
}
|
|
|
|
// End mark
|
|
IR.mark(SAMSUNG_AIRCON1_BIT_MARK);
|
|
IR.space(0);
|
|
}
|
|
|
|
// Samsung FJM (RJ040F2HXEA / MH026FNEA) heatpump control (remote control P/N ARH-465)
|
|
|
|
void SamsungFJMHeatpumpIR::send(IRSender& IR, uint8_t powerModeCmd, uint8_t operatingModeCmd, uint8_t fanSpeedCmd, uint8_t temperatureCmd, uint8_t swingVCmd, uint8_t swingHCmd)
|
|
{
|
|
send(IR, powerModeCmd, operatingModeCmd, fanSpeedCmd, temperatureCmd, swingVCmd, swingHCmd, false);
|
|
}
|
|
|
|
void SamsungFJMHeatpumpIR::send(IRSender& IR, uint8_t powerModeCmd, uint8_t operatingModeCmd, uint8_t fanSpeedCmd, uint8_t temperatureCmd, uint8_t swingVCmd, uint8_t swingHCmd, bool turboModeCmd)
|
|
{
|
|
(void)swingVCmd;
|
|
(void)swingHCmd;
|
|
|
|
// Sensible defaults for the heat pump mode
|
|
|
|
uint8_t powerMode = SAMSUNG_AIRCON1_MODE_ON;
|
|
uint8_t operatingMode = SAMSUNG_AIRCON1_MODE_HEAT;
|
|
uint8_t fanSpeed = SAMSUNG_AIRCON1_FAN_AUTO;
|
|
uint8_t temperature = 23;
|
|
uint8_t swingV = SAMSUNG_AIRCON2_VS_AUTO;
|
|
|
|
// See also https://github.com/wisskar/souliss/blob/master/extras/SamsungMH026FB.cpp
|
|
|
|
if (powerModeCmd == POWER_OFF)
|
|
{
|
|
powerMode = POWER_OFF;
|
|
}
|
|
else
|
|
{
|
|
switch (operatingModeCmd)
|
|
{
|
|
case MODE_AUTO:
|
|
operatingMode = SAMSUNG_AIRCON1_MODE_AUTO;
|
|
fanSpeedCmd = FAN_AUTO; // Fan speed is always 'AUTO' in AUTO mode
|
|
break;
|
|
case MODE_HEAT:
|
|
operatingMode = SAMSUNG_AIRCON1_MODE_HEAT;
|
|
break;
|
|
case MODE_COOL:
|
|
operatingMode = SAMSUNG_AIRCON1_MODE_COOL;
|
|
break;
|
|
case MODE_DRY:
|
|
operatingMode = SAMSUNG_AIRCON1_MODE_DRY;
|
|
fanSpeedCmd = FAN_AUTO; // Fan speed is always 'AUTO' in DRY mode
|
|
break;
|
|
case MODE_FAN:
|
|
operatingMode = SAMSUNG_AIRCON1_MODE_FAN;
|
|
temperatureCmd = 24; // Temperature is always 24 in FAN mode
|
|
if ( fanSpeedCmd == FAN_AUTO ) {
|
|
fanSpeedCmd = FAN_1; // Fan speed cannot be 'AUTO' in FAN mode
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
switch (fanSpeedCmd)
|
|
{
|
|
case FAN_AUTO:
|
|
fanSpeed = SAMSUNG_AIRCON1_FAN_AUTO;
|
|
break;
|
|
case FAN_1:
|
|
fanSpeed = SAMSUNG_AIRCON1_FAN1;
|
|
break;
|
|
case FAN_2:
|
|
fanSpeed = SAMSUNG_AIRCON1_FAN2;
|
|
break;
|
|
case FAN_3:
|
|
fanSpeed = SAMSUNG_AIRCON1_FAN3;
|
|
break;
|
|
case FAN_4:
|
|
fanSpeed = SAMSUNG_AIRCON2_FAN4;
|
|
break;
|
|
}
|
|
|
|
if ( temperatureCmd > 15 && temperatureCmd < 31)
|
|
{
|
|
temperature = temperatureCmd;
|
|
}
|
|
|
|
switch (swingVCmd)
|
|
{
|
|
case VDIR_SWING:
|
|
swingV = SAMSUNG_AIRCON2_VS_SWING;
|
|
break;
|
|
}
|
|
|
|
sendSamsung(IR, powerMode, operatingMode, fanSpeed, temperature, swingV, turboModeCmd);
|
|
}
|
|
|
|
// Send the Samsung code
|
|
|
|
void SamsungFJMHeatpumpIR::sendSamsung(IRSender& IR, uint8_t powerMode, uint8_t operatingMode, uint8_t fanSpeed, uint8_t temperature, uint8_t swingV, bool turboMode)
|
|
{
|
|
static const uint8_t samsungOffCode[] PROGMEM = { 0x02, 0xB2, 0x0F, 0x00, 0x00, 0x00, 0xC0,
|
|
0x01, 0x72, 0x0F, 0x00, 0x90, 0xD0, 0x01,
|
|
0x01, 0x02, 0xFF, 0x01, 0x60, 0x4B, 0xC0 };
|
|
|
|
static const uint8_t samsungHeader[] PROGMEM = { 0x02, 0x92, 0x0F, 0x00, 0x00, 0x00, 0xF0 };
|
|
|
|
// uint8_t samsungTemplate[] = { 0x01, 0x00, 0x0E, 0x01, 0x00, 0x00, 0xF0 }; //Orig
|
|
uint8_t samsungTemplate[] = { 0x01, 0x00, 0x0F, 0x01, 0x00, 0x00, 0xF0 };
|
|
// 0 1 2 3 4 5 6
|
|
// 7 8 9 10 11 12 13 <- byte in whole sequence
|
|
|
|
// 38 kHz PWM frequency
|
|
IR.setFrequency(38);
|
|
|
|
// Header
|
|
IR.mark(SAMSUNG_AIRCON2_HDR_MARK);
|
|
IR.space(SAMSUNG_AIRCON2_HDR_SPACE);
|
|
|
|
if (powerMode == POWER_OFF)
|
|
{
|
|
for (size_t i=0; i<sizeof(samsungOffCode); i++) {
|
|
IR.sendIRbyte(pgm_read_byte(&samsungOffCode[i]), SAMSUNG_AIRCON2_BIT_MARK, SAMSUNG_AIRCON2_ZERO_SPACE, SAMSUNG_AIRCON2_ONE_SPACE);
|
|
|
|
if (i == 6 || i == 13)
|
|
{
|
|
IR.mark(SAMSUNG_AIRCON2_BIT_MARK);
|
|
IR.space(SAMSUNG_AIRCON2_ONE_SPACE);
|
|
IR.mark(SAMSUNG_AIRCON2_HDR_MARK);
|
|
IR.space(SAMSUNG_AIRCON2_HDR_SPACE);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for (size_t i=0; i<sizeof(samsungHeader); i++) {
|
|
IR.sendIRbyte(pgm_read_byte(&samsungHeader[i]), SAMSUNG_AIRCON2_BIT_MARK, SAMSUNG_AIRCON2_ZERO_SPACE, SAMSUNG_AIRCON2_ONE_SPACE);
|
|
}
|
|
|
|
IR.mark(SAMSUNG_AIRCON2_BIT_MARK);
|
|
IR.space(SAMSUNG_AIRCON2_ONE_SPACE);
|
|
IR.mark(SAMSUNG_AIRCON2_HDR_MARK);
|
|
IR.space(SAMSUNG_AIRCON2_HDR_SPACE);
|
|
|
|
// The actual data is in the last 7 bytes
|
|
samsungTemplate[2] |= swingV;
|
|
samsungTemplate[4] = (temperature - 16) << 4;
|
|
samsungTemplate[5] = operatingMode | fanSpeed;
|
|
|
|
if (turboMode)
|
|
{
|
|
samsungTemplate[3] |= SAMSUNG_AIRCON2_TURBO;
|
|
}
|
|
|
|
// Checksum calculation
|
|
uint8_t checksum = 0x00;
|
|
|
|
for (uint8_t j=2; j<6; j++) {
|
|
uint8_t samsungByte = samsungTemplate[j];
|
|
|
|
if (j == 2)
|
|
{
|
|
samsungByte &= 0b11111110;
|
|
}
|
|
|
|
for (uint8_t i=0; i<8; i++) {
|
|
if ( (samsungByte & 0x01) == 0x01 ) {
|
|
checksum++;
|
|
}
|
|
samsungByte >>= 1;
|
|
}
|
|
}
|
|
|
|
checksum = 28 - checksum;
|
|
checksum <<= 4;
|
|
checksum |= 0x02;
|
|
|
|
samsungTemplate[1] = checksum;
|
|
|
|
for (size_t i=0; i<sizeof(samsungTemplate); i++) {
|
|
IR.sendIRbyte(samsungTemplate[i], SAMSUNG_AIRCON2_BIT_MARK, SAMSUNG_AIRCON2_ZERO_SPACE, SAMSUNG_AIRCON2_ONE_SPACE);
|
|
}
|
|
}
|
|
|
|
// End mark
|
|
IR.mark(SAMSUNG_AIRCON2_BIT_MARK);
|
|
IR.space(0);
|
|
}
|