diff --git a/PanasonicAltDKEHeatpumpIR.cpp b/PanasonicAltDKEHeatpumpIR.cpp new file mode 100644 index 0000000..def187c --- /dev/null +++ b/PanasonicAltDKEHeatpumpIR.cpp @@ -0,0 +1,247 @@ +#include + +// This is a protected method, i.e. generic Panasonic instances cannot be created +PanasonicAltDKEHeatpumpIR::PanasonicAltDKEHeatpumpIR() : HeatpumpIR() +{ + static const char PROGMEM model[] PROGMEM = "panasonic_dke"; + static const char PROGMEM info[] PROGMEM = "{\"mdl\":\"panasonic_dke\",\"dn\":\"Panasonic DKE\",\"mT\":16,\"xT\":30,\"fs\":6}"; + + _model = model; + _info = info; + + _panasonicModel = PANASONIC_DKE; +} + + +// Panasonic DKE/NKE/JKE numeric values to command bytes +void PanasonicAltDKEHeatpumpIR::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); +} + +// Panasonic DKE/NKE/JKE numeric values to command bytes +void PanasonicAltDKEHeatpumpIR::send(IRSender& IR, uint8_t powerModeCmd, uint8_t operatingModeCmd, uint8_t fanSpeedCmd, uint8_t temperatureCmd, uint8_t swingVCmd, uint8_t swingHCmd, bool ionizerCmd) +{ + // Sensible defaults for the heat pump mode + + uint8_t operatingMode = PANASONIC_AIRCON2_TIMER_CNL; + uint8_t fanSpeed = PANASONIC_AIRCON2_FAN_AUTO; + uint8_t temperature = 23; + uint8_t swingV = PANASONIC_AIRCON2_VS_UP; + uint8_t swingH = PANASONIC_AIRCON2_HS_AUTO; + + switch (powerModeCmd) + { + case POWER_ON: + operatingMode |= PANASONIC_AIRCON2_MODE_ON; + break; + case POWER_OFF: + operatingMode |= PANASONIC_AIRCON2_MODE_OFF; + break; + } + + switch (operatingModeCmd) + { + case MODE_AUTO: + operatingMode |= PANASONIC_AIRCON2_MODE_AUTO; + break; + case MODE_HEAT: + operatingMode |= PANASONIC_AIRCON2_MODE_HEAT; + break; + case MODE_COOL: + operatingMode |= PANASONIC_AIRCON2_MODE_COOL; + break; + case MODE_DRY: + operatingMode |= PANASONIC_AIRCON2_MODE_DRY; + break; + case MODE_FAN: + operatingMode |= PANASONIC_AIRCON2_MODE_FAN; + temperatureCmd = 27; // Temperature is always 27 in FAN mode + break; + case MODE_MAINT: // Maintenance mode is just the heat mode at +8 or +10, FAN5 + operatingMode |= PANASONIC_AIRCON2_MODE_HEAT; + temperature = 10; // Default to +10 degrees + fanSpeedCmd = FAN_5; + break; + } + + switch (fanSpeedCmd) + { + case FAN_AUTO: + fanSpeed = PANASONIC_AIRCON2_FAN_AUTO; + break; + case FAN_1: + fanSpeed = PANASONIC_AIRCON2_FAN1; + break; + case FAN_2: + fanSpeed = PANASONIC_AIRCON2_FAN2; + break; + case FAN_3: + fanSpeed = PANASONIC_AIRCON2_FAN3; + break; + case FAN_4: + fanSpeed = PANASONIC_AIRCON2_FAN4; + break; + case FAN_5: + fanSpeed = PANASONIC_AIRCON2_FAN5; + break; + } + + if ( temperatureCmd > 15 && temperatureCmd < 31) + { + temperature = temperatureCmd; + } + + switch (swingVCmd) + { + case VDIR_AUTO: + case VDIR_SWING: + swingV = PANASONIC_AIRCON2_VS_AUTO; + break; + case VDIR_UP: + swingV = PANASONIC_AIRCON2_VS_UP; + break; + case VDIR_MUP: + swingV = PANASONIC_AIRCON2_VS_MUP; + break; + case VDIR_MIDDLE: + swingV = PANASONIC_AIRCON2_VS_MIDDLE; + break; + case VDIR_MDOWN: + swingV = PANASONIC_AIRCON2_VS_MDOWN; + break; + case VDIR_DOWN: + swingV = PANASONIC_AIRCON2_VS_DOWN; + break; + } + + switch (swingHCmd) + { + case HDIR_AUTO: + case HDIR_SWING: + swingH = PANASONIC_AIRCON2_HS_AUTO; + break; + case HDIR_MIDDLE: + swingH = PANASONIC_AIRCON2_HS_MIDDLE; + break; + case HDIR_LEFT: + swingH = PANASONIC_AIRCON2_HS_LEFT; + break; + case HDIR_MLEFT: + swingH = PANASONIC_AIRCON2_HS_MLEFT; + break; + case HDIR_RIGHT: + swingH = PANASONIC_AIRCON2_HS_RIGHT; + break; + case HDIR_MRIGHT: + swingH = PANASONIC_AIRCON2_HS_MRIGHT; + break; + } + + uint ionizer = ionizerCmd ? PANASONIC_AIRCON2_ION_ON : PANASONIC_AIRCON2_ION_OFF; + + sendPanasonicLong(IR, operatingMode, fanSpeed, temperature, swingV, swingH, ionizer); +} + +void PanasonicAltDKEHeatpumpIR::send(IRSender& IR, bool quiet, bool powerful) +{ + sendPanasonicShort(IR, quiet, powerful); +} + +// Send the Panasonic DKE/JKE/NKE/LKE code +void PanasonicAltDKEHeatpumpIR::sendPanasonicLong(IRSender& IR, uint8_t operatingMode, uint8_t fanSpeed, uint8_t temperature, uint8_t swingV, uint8_t swingH, uint8_t ionizer) +{ + // Only bytes 13, 14, 16, 17 and 26 are modified + static const uint8_t panasonicProgmemTemplate[27] PROGMEM = { + 0x02, 0x20, 0xE0, 0x04, 0x00, 0x00, 0x00, 0x06, 0x02, 0x20, 0xE0, 0x04, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x0E, 0xE0, 0x00, 0x00, 0x81, 0x00, 0x00, 0x00 + // 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 + }; + + // Save some SRAM by having the template in flash + uint8_t panasonicTemplate[27]; + memcpy_P(panasonicTemplate, panasonicProgmemTemplate, sizeof(panasonicTemplate)); + + switch(_panasonicModel) + { + case PANASONIC_DKE: + panasonicTemplate[17] = swingH; // Only the DKE model has a setting for the horizontal air flow + panasonicTemplate[23] = 0x01; + panasonicTemplate[25] = 0x06; + break; + case PANASONIC_JKE: + break; + case PANASONIC_NKE: + panasonicTemplate[17] = 0x06; + break; + case PANASONIC_LKE: + panasonicTemplate[17] = 0x06; + panasonicTemplate[13] = 0x02; + break; + } + + panasonicTemplate[13] |= operatingMode; + panasonicTemplate[14] = temperature << 1; + panasonicTemplate[16] = fanSpeed | swingV; + panasonicTemplate[22] = ionizer; + + sendPanasonic(IR, panasonicTemplate, 27); +} + +void PanasonicAltDKEHeatpumpIR::sendPanasonicShort(IRSender& IR, bool quiet, bool powerful) { + static const uint8_t panasonicProgmemTemplate[16] PROGMEM = { + 0x02, 0x20, 0xE0, 0x04, 0x00, 0x00, 0x00, 0x06, 0x02, 0x20, 0xE0, 0x04, 0x80, 0x00, 0x00, 0x00 + // 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 + }; + // Save some SRAM by having the template in flash + uint8_t panasonicTemplate[16]; + memcpy_P(panasonicTemplate, panasonicProgmemTemplate, sizeof(panasonicTemplate)); + + if (quiet) { + panasonicTemplate[13] = PANASONIC_AIRCON2_QUIET_M; + panasonicTemplate[14] = PANASONIC_AIRCON2_QUIET_L; + } else if (powerful) { + panasonicTemplate[13] = PANASONIC_AIRCON2_POWERFUL_M; + panasonicTemplate[14] = PANASONIC_AIRCON2_POWERFUL_L; + } + + sendPanasonic(IR, panasonicTemplate, 16); +} + +void PanasonicAltDKEHeatpumpIR::sendPanasonic(IRSender& IR, uint8_t * panasonicTemplate, uint8_t templateLength) { + // Checksum calculation + uint8_t checksum = 0xF4; + + for (int i=0; i + + +// Panasonic DKE, JKE & NKE timing constants (DKE remote control P/N A75C2616) +#define PANASONIC_AIRCON2_HDR_MARK 3500 +#define PANASONIC_AIRCON2_HDR_SPACE 1800 +#define PANASONIC_AIRCON2_BIT_MARK 420 +#define PANASONIC_AIRCON2_ONE_SPACE 1350 +#define PANASONIC_AIRCON2_ZERO_SPACE 470 +#define PANASONIC_AIRCON2_MSG_SPACE 10000 + +// Panasonic DKE, JNE & NKE codes +#define PANASONIC_AIRCON2_MODE_AUTO 0x00 // Operating mode +#define PANASONIC_AIRCON2_MODE_HEAT 0x40 +#define PANASONIC_AIRCON2_MODE_COOL 0x30 +#define PANASONIC_AIRCON2_MODE_DRY 0x20 +#define PANASONIC_AIRCON2_MODE_FAN 0x60 +#define PANASONIC_AIRCON2_MODE_OFF 0x00 // Power OFF +#define PANASONIC_AIRCON2_MODE_ON 0x01 +#define PANASONIC_AIRCON2_TIMER_CNL 0x08 +#define PANASONIC_AIRCON2_FAN_AUTO 0xA0 // Fan speed +#define PANASONIC_AIRCON2_FAN1 0x30 +#define PANASONIC_AIRCON2_FAN2 0x40 +#define PANASONIC_AIRCON2_FAN3 0x50 +#define PANASONIC_AIRCON2_FAN4 0x60 +#define PANASONIC_AIRCON2_FAN5 0x70 +#define PANASONIC_AIRCON2_VS_AUTO 0x0F // Vertical swing +#define PANASONIC_AIRCON2_VS_UP 0x01 +#define PANASONIC_AIRCON2_VS_MUP 0x02 +#define PANASONIC_AIRCON2_VS_MIDDLE 0x03 +#define PANASONIC_AIRCON2_VS_MDOWN 0x04 +#define PANASONIC_AIRCON2_VS_DOWN 0x05 +#define PANASONIC_AIRCON2_HS_AUTO 0x0D // Horizontal swing +#define PANASONIC_AIRCON2_HS_MIDDLE 0x06 +#define PANASONIC_AIRCON2_HS_LEFT 0x09 +#define PANASONIC_AIRCON2_HS_MLEFT 0x0A +#define PANASONIC_AIRCON2_HS_MRIGHT 0x0B +#define PANASONIC_AIRCON2_HS_RIGHT 0x0C +#define PANASONIC_AIRCON2_ION_ON 0x01 // Air ionizer on +#define PANASONIC_AIRCON2_ION_OFF 0x00 // Air ionizer off +#define PANASONIC_AIRCON2_QUIET_M 0x81 // Quiet setting, 1st byte +#define PANASONIC_AIRCON2_QUIET_L 0x33 // Quiet setting, 2nd byte +#define PANASONIC_AIRCON2_POWERFUL_M 0x86 // Powerful setting, 1st byte +#define PANASONIC_AIRCON2_POWERFUL_L 0x35 // Powerful setting, 2nd byte + +#define PANASONIC_DKE 0 + +class PanasonicAltDKEHeatpumpIR : public HeatpumpIR +{ + public: + PanasonicAltDKEHeatpumpIR(); + void send(IRSender& IR, uint8_t powerModeCmd, uint8_t operatingModeCmd, uint8_t fanSpeedCmd, uint8_t temperatureCmd, uint8_t swingVCmd, uint8_t swingHCmd); + void send(IRSender& IR, uint8_t powerModeCmd, uint8_t operatingModeCmd, uint8_t fanSpeedCmd, uint8_t temperatureCmd, uint8_t swingVCmd, uint8_t swingHCmd, bool ionizerCmd); + void send(IRSender& IR, bool quiet, bool powerful); + + private: + void sendPanasonicLong(IRSender& IR, uint8_t operatingMode, uint8_t fanSpeed, uint8_t temperature, uint8_t swingV, uint8_t swingH, uint8_t ionizer); + void sendPanasonicShort(IRSender& IR, bool quiet, bool powerful); + void sendPanasonic(IRSender& IR, uint8_t * panasonicTemplate, uint8_t templateLength); +}; + +#endif