#include // 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>= 1; } } checksum = 28 - checksum; checksum <<= 4; checksum |= 0x02; samsungTemplate[1] = checksum; for (size_t i=0; i