263 lines
8.2 KiB
C++
263 lines
8.2 KiB
C++
#include <PanasonicCKPHeatpumpIR.h>
|
|
|
|
|
|
PanasonicCKPHeatpumpIR::PanasonicCKPHeatpumpIR()
|
|
{
|
|
static const char model[] PROGMEM = "panasonic_ckp";
|
|
static const char info[] PROGMEM = "{\"mdl\":\"panasonic_ckp\",\"dn\":\"Panasonic CKP\",\"mT\":16,\"xT\":30,\"fs\":6}";
|
|
|
|
_model = model;
|
|
_info = info;
|
|
}
|
|
|
|
|
|
// Panasonic CKP numeric values to command uint8_ts
|
|
void PanasonicCKPHeatpumpIR::send(IRSender& IR, uint8_t powerModeCmd, uint8_t operatingModeCmd, uint8_t fanSpeedCmd, uint8_t temperatureCmd, uint8_t swingVCmd, uint8_t swingHCmd)
|
|
{
|
|
// Sensible defaults for the heat pump mode
|
|
|
|
uint8_t powerMode = false;
|
|
uint8_t operatingMode = PANASONIC_AIRCON1_MODE_KEEP;
|
|
uint8_t fanSpeed = PANASONIC_AIRCON1_FAN_AUTO;
|
|
uint8_t temperature = 23;
|
|
uint8_t swingV = PANASONIC_AIRCON1_VS_UP;
|
|
uint8_t swingH = PANASONIC_AIRCON1_HS_SWING;
|
|
|
|
switch (powerModeCmd)
|
|
{
|
|
case POWER_ON:
|
|
powerMode = true;
|
|
break;
|
|
}
|
|
|
|
switch (operatingModeCmd)
|
|
{
|
|
case MODE_AUTO:
|
|
operatingMode |= PANASONIC_AIRCON1_MODE_AUTO;
|
|
break;
|
|
case MODE_HEAT:
|
|
operatingMode |= PANASONIC_AIRCON1_MODE_HEAT;
|
|
break;
|
|
case MODE_COOL:
|
|
operatingMode |= PANASONIC_AIRCON1_MODE_COOL;
|
|
break;
|
|
case MODE_DRY:
|
|
operatingMode |= PANASONIC_AIRCON1_MODE_DRY;
|
|
break;
|
|
case MODE_FAN:
|
|
operatingMode |= PANASONIC_AIRCON1_MODE_FAN;
|
|
temperatureCmd = 27; // Temperature is always 27 in FAN mode
|
|
break;
|
|
default:
|
|
operatingMode |= PANASONIC_AIRCON1_MODE_HEAT;
|
|
break;
|
|
}
|
|
|
|
switch (fanSpeedCmd)
|
|
{
|
|
case FAN_AUTO:
|
|
fanSpeed = PANASONIC_AIRCON1_FAN_AUTO;
|
|
break;
|
|
case FAN_1:
|
|
fanSpeed = PANASONIC_AIRCON1_FAN1;
|
|
break;
|
|
case FAN_2:
|
|
fanSpeed = PANASONIC_AIRCON1_FAN2;
|
|
break;
|
|
case FAN_3:
|
|
fanSpeed = PANASONIC_AIRCON1_FAN3;
|
|
break;
|
|
case FAN_4:
|
|
fanSpeed = PANASONIC_AIRCON1_FAN4;
|
|
break;
|
|
case FAN_5:
|
|
fanSpeed = PANASONIC_AIRCON1_FAN5;
|
|
break;
|
|
}
|
|
|
|
if ( temperatureCmd > 15 && temperatureCmd < 31)
|
|
{
|
|
temperature = temperatureCmd;
|
|
}
|
|
|
|
switch (swingVCmd)
|
|
{
|
|
case VDIR_SWING:
|
|
swingV = PANASONIC_AIRCON1_VS_SWING;
|
|
break;
|
|
case VDIR_UP:
|
|
swingV = PANASONIC_AIRCON1_VS_UP;
|
|
break;
|
|
case VDIR_MUP:
|
|
swingV = PANASONIC_AIRCON1_VS_MUP;
|
|
break;
|
|
case VDIR_MIDDLE:
|
|
swingV = PANASONIC_AIRCON1_VS_MIDDLE;
|
|
break;
|
|
case VDIR_MDOWN:
|
|
swingV = PANASONIC_AIRCON1_VS_MDOWN;
|
|
break;
|
|
case VDIR_DOWN:
|
|
swingV = PANASONIC_AIRCON1_VS_DOWN;
|
|
break;
|
|
}
|
|
|
|
switch (swingHCmd)
|
|
{
|
|
case HDIR_SWING:
|
|
swingH = PANASONIC_AIRCON1_HS_SWING;
|
|
break;
|
|
case HDIR_AUTO: // Well, just set it to manual
|
|
swingH = PANASONIC_AIRCON1_HS_MANUAL;
|
|
break;
|
|
}
|
|
|
|
sendPanasonicCKP(IR, operatingMode, fanSpeed, temperature, swingV, swingH);
|
|
delay(1000); // Sleep 1 second between the messages
|
|
|
|
// This will change the power state in one minute from now
|
|
sendPanasonicCKPOnOffTimerCancel(IR, powerMode, false);
|
|
/*
|
|
// Send the 'timer cancel' signal 2 minutes later
|
|
if (panasonicCancelTimer != 0)
|
|
{
|
|
timer.stop(panasonicCancelTimer);
|
|
panasonicCancelTimer = 0;
|
|
}
|
|
|
|
// Note that the argument to 'timer.after' has to be explicitly cast into 'long'
|
|
panasonicCancelTimer = timer.after(2L*60L*1000L, sendPanasonicCKPCancelTimer);
|
|
|
|
LOG(F("'Timer cancel' timer ID: "));
|
|
LOGLN(panasonicCancelTimer);
|
|
*/
|
|
}
|
|
|
|
// Send the Panasonic CKP code
|
|
void PanasonicCKPHeatpumpIR::sendPanasonicCKP(IRSender& IR, uint8_t operatingMode, uint8_t fanSpeed, uint8_t temperature, uint8_t swingV, uint8_t swingH)
|
|
{
|
|
uint8_t sendBuffer[4];
|
|
|
|
// Fan speed & temperature, temperature needs to be 27 in FAN mode
|
|
if (operatingMode == PANASONIC_AIRCON1_MODE_FAN || operatingMode == (PANASONIC_AIRCON1_MODE_FAN | PANASONIC_AIRCON1_MODE_KEEP ))
|
|
{
|
|
temperature = 27;
|
|
}
|
|
|
|
sendBuffer[0] = fanSpeed | (temperature - 15);
|
|
|
|
// Power toggle & operation mode
|
|
sendBuffer[1] = operatingMode;
|
|
|
|
// Swings
|
|
sendBuffer[2] = swingV | swingH;
|
|
|
|
// Always 0x36
|
|
sendBuffer[3] = 0x36;
|
|
|
|
// Send the code
|
|
sendPanasonicCKPraw(IR, sendBuffer);
|
|
}
|
|
|
|
// Send the Panasonic CKP raw code
|
|
void PanasonicCKPHeatpumpIR::sendPanasonicCKPraw(IRSender& IR, uint8_t sendBuffer[])
|
|
{
|
|
// 40 kHz PWM frequency
|
|
IR.setFrequency(38);
|
|
|
|
// Header, two first uint8_ts repeated
|
|
IR.mark(PANASONIC_AIRCON1_HDR_MARK);
|
|
IR.space(PANASONIC_AIRCON1_HDR_SPACE);
|
|
|
|
for (int i=0; i<2; i++) {
|
|
IR.sendIRbyte(sendBuffer[0], PANASONIC_AIRCON1_BIT_MARK, PANASONIC_AIRCON1_ZERO_SPACE, PANASONIC_AIRCON1_ONE_SPACE);
|
|
IR.sendIRbyte(sendBuffer[0], PANASONIC_AIRCON1_BIT_MARK, PANASONIC_AIRCON1_ZERO_SPACE, PANASONIC_AIRCON1_ONE_SPACE);
|
|
IR.sendIRbyte(sendBuffer[1], PANASONIC_AIRCON1_BIT_MARK, PANASONIC_AIRCON1_ZERO_SPACE, PANASONIC_AIRCON1_ONE_SPACE);
|
|
IR.sendIRbyte(sendBuffer[1], PANASONIC_AIRCON1_BIT_MARK, PANASONIC_AIRCON1_ZERO_SPACE, PANASONIC_AIRCON1_ONE_SPACE);
|
|
|
|
IR.mark(PANASONIC_AIRCON1_HDR_MARK);
|
|
IR.space(PANASONIC_AIRCON1_HDR_SPACE);
|
|
}
|
|
|
|
// Pause
|
|
|
|
IR.mark(PANASONIC_AIRCON1_BIT_MARK);
|
|
IR.space(PANASONIC_AIRCON1_MSG_SPACE);
|
|
|
|
// Header, two last uint8_ts repeated
|
|
|
|
IR.mark(PANASONIC_AIRCON1_HDR_MARK);
|
|
IR.space(PANASONIC_AIRCON1_HDR_SPACE);
|
|
|
|
for (int i=0; i<2; i++) {
|
|
IR.sendIRbyte(sendBuffer[2], PANASONIC_AIRCON1_BIT_MARK, PANASONIC_AIRCON1_ZERO_SPACE, PANASONIC_AIRCON1_ONE_SPACE);
|
|
IR.sendIRbyte(sendBuffer[2], PANASONIC_AIRCON1_BIT_MARK, PANASONIC_AIRCON1_ZERO_SPACE, PANASONIC_AIRCON1_ONE_SPACE);
|
|
IR.sendIRbyte(sendBuffer[3], PANASONIC_AIRCON1_BIT_MARK, PANASONIC_AIRCON1_ZERO_SPACE, PANASONIC_AIRCON1_ONE_SPACE);
|
|
IR.sendIRbyte(sendBuffer[3], PANASONIC_AIRCON1_BIT_MARK, PANASONIC_AIRCON1_ZERO_SPACE, PANASONIC_AIRCON1_ONE_SPACE);
|
|
|
|
IR.mark(PANASONIC_AIRCON1_HDR_MARK);
|
|
IR.space(PANASONIC_AIRCON1_HDR_SPACE);
|
|
}
|
|
|
|
IR.mark(PANASONIC_AIRCON1_BIT_MARK);
|
|
IR.space(0);
|
|
}
|
|
|
|
// Send the Panasonic CKP On/Off code
|
|
//
|
|
// CKP does not have discrete ON/OFF commands, but this can be emulated by using the timer
|
|
// The side-effects of using the timer are:
|
|
// * ONE minute delay before the power state changes
|
|
// * the 'TIMER' led (orange) is lit
|
|
// * a timer event is scheduled to cancel the timer after TWO minutes (the 'TIMER' led turns off
|
|
void PanasonicCKPHeatpumpIR::sendPanasonicCKPOnOffTimerCancel(IRSender& IR, boolean powerState, boolean cancelTimer)
|
|
{
|
|
static const uint8_t ON_msg[] PROGMEM = { 0x7F, 0x38, 0xBF, 0x38, 0x10, 0x3D, 0x80, 0x3D, 0x09, 0x34, 0x80, 0x34 }; // ON at 00:10, time now 00:09, no OFF timing
|
|
static const uint8_t OFF_msg[] PROGMEM = { 0x10, 0x38, 0x80, 0x38, 0x7F, 0x3D, 0xBF, 0x3D, 0x09, 0x34, 0x80, 0x34 }; // OFF at 00:10, time now 00:09, no ON timing
|
|
static const uint8_t CANCEL_msg[] PROGMEM = { 0x7F, 0x38, 0xBF, 0x38, 0x7F, 0x3D, 0xBF, 0x3D, 0x17, 0x34, 0x80, 0x34 }; // Timer CANCEL
|
|
|
|
// Save some SRAM by only having one copy of the template on the SRAM
|
|
uint8_t sendBuffer[sizeof(ON_msg)];
|
|
|
|
if ( cancelTimer == true ) {
|
|
memcpy_P(sendBuffer, CANCEL_msg, sizeof(ON_msg));
|
|
} else if ( powerState == true ) {
|
|
memcpy_P(sendBuffer, ON_msg, sizeof(ON_msg));
|
|
} else {
|
|
memcpy_P(sendBuffer, OFF_msg, sizeof(ON_msg));
|
|
}
|
|
|
|
// 40 kHz PWM frequency
|
|
IR.setFrequency(38);
|
|
|
|
for (int i=0; i<6; i++) {
|
|
IR.mark(PANASONIC_AIRCON1_HDR_MARK);
|
|
IR.space(PANASONIC_AIRCON1_HDR_SPACE);
|
|
|
|
IR.sendIRbyte(sendBuffer[i*2 + 0], PANASONIC_AIRCON1_BIT_MARK, PANASONIC_AIRCON1_ZERO_SPACE, PANASONIC_AIRCON1_ONE_SPACE);
|
|
IR.sendIRbyte(sendBuffer[i*2 + 0], PANASONIC_AIRCON1_BIT_MARK, PANASONIC_AIRCON1_ZERO_SPACE, PANASONIC_AIRCON1_ONE_SPACE);
|
|
IR.sendIRbyte(sendBuffer[i*2 + 1], PANASONIC_AIRCON1_BIT_MARK, PANASONIC_AIRCON1_ZERO_SPACE, PANASONIC_AIRCON1_ONE_SPACE);
|
|
IR.sendIRbyte(sendBuffer[i*2 + 1], PANASONIC_AIRCON1_BIT_MARK, PANASONIC_AIRCON1_ZERO_SPACE, PANASONIC_AIRCON1_ONE_SPACE);
|
|
|
|
IR.mark(PANASONIC_AIRCON1_HDR_MARK);
|
|
IR.space(PANASONIC_AIRCON1_HDR_SPACE);
|
|
|
|
if ( i < 5 ) {
|
|
IR.mark(PANASONIC_AIRCON1_BIT_MARK);
|
|
IR.space(PANASONIC_AIRCON1_MSG_SPACE);
|
|
}
|
|
}
|
|
|
|
IR.mark(PANASONIC_AIRCON1_BIT_MARK);
|
|
IR.space(0);
|
|
}
|
|
|
|
// Send the Panasonic CKP timer cancel
|
|
void PanasonicCKPHeatpumpIR::sendPanasonicCKPCancelTimer(IRSender& IR)
|
|
{
|
|
LOGLN(F("Sending Panasonic CKP timer cancel"));
|
|
|
|
sendPanasonicCKPOnOffTimerCancel(IR, false, true);
|
|
}
|
|
|