From d5d00a8686e49dc266218e37a8917a1d3e66026c Mon Sep 17 00:00:00 2001 From: ToniA Date: Thu, 26 Dec 2013 15:11:20 +0200 Subject: [PATCH] Initial HeatpumpIR library, refactored from my 'arduino-wp-heatpump-controller' repository --- CarrierHeatpumpIR.cpp | 123 +++++++ CarrierHeatpumpIR.h | 44 +++ FujitsuHeatpumpIR.cpp | 117 ++++++ FujitsuHeatpumpIR.h | 43 +++ HeatpumpIR.cpp | 10 + HeatpumpIR.h | 64 ++++ IRSender.cpp | 193 ++++++++++ IRSender.h | 23 ++ LICENSE | 339 ++++++++++++++++++ MideaHeatpumpIR.cpp | 141 ++++++++ MideaHeatpumpIR.h | 46 +++ MitsubishiHeatpumpIR.cpp | 118 ++++++ MitsubishiHeatpumpIR.h | 44 +++ PanasonicCKPHeatpumpIR.cpp | 257 +++++++++++++ PanasonicCKPHeatpumpIR.h | 57 +++ PanasonicHeatpumpIR.cpp | 218 +++++++++++ PanasonicHeatpumpIR.h | 86 +++++ README.md | 20 ++ arduino_irsender.png | Bin 0 -> 2752919 bytes .../PanasonicCKPTimer/PanasonicCKPTimer.ino | 57 +++ examples/simple/simple.ino | 37 ++ keywords.txt | 20 ++ 22 files changed, 2057 insertions(+) create mode 100644 CarrierHeatpumpIR.cpp create mode 100644 CarrierHeatpumpIR.h create mode 100644 FujitsuHeatpumpIR.cpp create mode 100644 FujitsuHeatpumpIR.h create mode 100644 HeatpumpIR.cpp create mode 100644 HeatpumpIR.h create mode 100644 IRSender.cpp create mode 100644 IRSender.h create mode 100644 LICENSE create mode 100644 MideaHeatpumpIR.cpp create mode 100644 MideaHeatpumpIR.h create mode 100644 MitsubishiHeatpumpIR.cpp create mode 100644 MitsubishiHeatpumpIR.h create mode 100644 PanasonicCKPHeatpumpIR.cpp create mode 100644 PanasonicCKPHeatpumpIR.h create mode 100644 PanasonicHeatpumpIR.cpp create mode 100644 PanasonicHeatpumpIR.h create mode 100644 README.md create mode 100644 arduino_irsender.png create mode 100644 examples/PanasonicCKPTimer/PanasonicCKPTimer.ino create mode 100644 examples/simple/simple.ino create mode 100644 keywords.txt diff --git a/CarrierHeatpumpIR.cpp b/CarrierHeatpumpIR.cpp new file mode 100644 index 0000000..b3883b2 --- /dev/null +++ b/CarrierHeatpumpIR.cpp @@ -0,0 +1,123 @@ +#include +#include "CarrierHeatpumpIR.h" + +CarrierHeatpumpIR::CarrierHeatpumpIR() +{ +} + +void CarrierHeatpumpIR::send(IRSender& IR, byte powerModeCmd, byte operatingModeCmd, byte fanSpeedCmd, byte temperatureCmd, byte swingVCmd, byte swingHCmd) +{ + // Sensible defaults for the heat pump mode + + byte operatingMode = CARRIER_AIRCON1_MODE_HEAT; + byte fanSpeed = CARRIER_AIRCON1_FAN_AUTO; + byte temperature = 23; + + if (powerModeCmd == POWER_OFF) + { + operatingMode = CARRIER_AIRCON1_MODE_OFF; + } + else + { + switch (operatingModeCmd) + { + case MODE_AUTO: + operatingMode = CARRIER_AIRCON1_MODE_AUTO; + break; + case MODE_HEAT: + operatingMode = CARRIER_AIRCON1_MODE_HEAT; + break; + case MODE_COOL: + operatingMode = CARRIER_AIRCON1_MODE_COOL; + break; + case MODE_DRY: + operatingMode = CARRIER_AIRCON1_MODE_DRY; + break; + case MODE_FAN: + operatingMode = CARRIER_AIRCON1_MODE_FAN; + temperatureCmd = 22; // Temperature is always 22 in FAN mode + break; + } + } + + switch (fanSpeedCmd) + { + case FAN_AUTO: + fanSpeed = CARRIER_AIRCON1_FAN_AUTO; + break; + case FAN_1: + fanSpeed = CARRIER_AIRCON1_FAN1; + break; + case FAN_2: + fanSpeed = CARRIER_AIRCON1_FAN2; + break; + case FAN_3: + fanSpeed = CARRIER_AIRCON1_FAN3; + break; + case FAN_4: + fanSpeed = CARRIER_AIRCON1_FAN4; + break; + case FAN_5: + fanSpeed = CARRIER_AIRCON1_FAN5; + break; + } + + if ( temperatureCmd > 16 && temperatureCmd < 31) + { + temperature = temperatureCmd; + } + + sendCarrier(IR, operatingMode, fanSpeed, temperature); +} + +// Send the Carrier code +// Carrier has the LSB and MSB in different format than Panasonic + +void CarrierHeatpumpIR::sendCarrier(IRSender& IR, byte operatingMode, byte fanSpeed, byte temperature) +{ + byte sendBuffer[9] = { 0x4f, 0xb0, 0xc0, 0x3f, 0x80, 0x00, 0x00, 0x00, 0x00 }; // The data is on the last four bytes + + static const prog_uint8_t temperatures[] PROGMEM = { 0x00, 0x08, 0x04, 0x0c, 0x02, 0x0a, 0x06, 0x0e, 0x01, 0x09, 0x05, 0x0d, 0x03, 0x0b }; + byte checksum = 0; + + // PROGMEM arrays cannot be addressed directly, see http://forum.arduino.cc/index.php?topic=106603.0 + sendBuffer[5] = pgm_read_byte(&(temperatures[(temperature-17)])); + + sendBuffer[6] = operatingMode | fanSpeed; + + // Checksum + + for (int i=0; i<8; i++) { + checksum += IR.bitReverse(sendBuffer[i]); + } + + sendBuffer[8] = IR.bitReverse(checksum); + + // 40 kHz PWM frequency + IR.setFrequency(40); + + // Header + IR.mark(CARRIER_AIRCON1_HDR_MARK); + IR.space(CARRIER_AIRCON1_HDR_SPACE); + + // Payload + for (int i=0; i +#include "IRSender.h" +#include "HeatpumpIR.h" + +// Carrier (42NQV035G / 38NYV035H2) timing constants (remote control P/N WH-L05SE) +#define CARRIER_AIRCON1_HDR_MARK 4320 +#define CARRIER_AIRCON1_HDR_SPACE 4350 +#define CARRIER_AIRCON1_BIT_MARK 500 +#define CARRIER_AIRCON1_ONE_SPACE 1650 +#define CARRIER_AIRCON1_ZERO_SPACE 550 +#define CARRIER_AIRCON1_MSG_SPACE 7400 + +// Carrier codes +#define CARRIER_AIRCON1_MODE_AUTO 0x00 // Operating mode +#define CARRIER_AIRCON1_MODE_HEAT 0xC0 +#define CARRIER_AIRCON1_MODE_COOL 0x80 +#define CARRIER_AIRCON1_MODE_DRY 0x40 +#define CARRIER_AIRCON1_MODE_FAN 0x20 +#define CARRIER_AIRCON1_MODE_OFF 0xE0 // Power OFF +#define CARRIER_AIRCON1_FAN_AUTO 0x00 // Fan speed +#define CARRIER_AIRCON1_FAN1 0x02 +#define CARRIER_AIRCON1_FAN2 0x06 +#define CARRIER_AIRCON1_FAN3 0x01 +#define CARRIER_AIRCON1_FAN4 0x05 +#define CARRIER_AIRCON1_FAN5 0x03 + + +class CarrierHeatpumpIR : public HeatpumpIR +{ + public: + CarrierHeatpumpIR(); + void send(IRSender& IR, byte powerModeCmd, byte operatingModeCmd, byte fanSpeedCmd, byte temperatureCmd, byte swingVCmd, byte swingHCmd); + + private: + void sendCarrier(IRSender& IR, byte operatingMode, byte fanSpeed, byte temperature); +}; + +#endif diff --git a/FujitsuHeatpumpIR.cpp b/FujitsuHeatpumpIR.cpp new file mode 100644 index 0000000..98503aa --- /dev/null +++ b/FujitsuHeatpumpIR.cpp @@ -0,0 +1,117 @@ +#include +#include "FujitsuHeatpumpIR.h" + +FujitsuHeatpumpIR::FujitsuHeatpumpIR() +{ +} + +void FujitsuHeatpumpIR::send(IRSender& IR, byte powerModeCmd, byte operatingModeCmd, byte fanSpeedCmd, byte temperatureCmd, byte swingVCmd, byte swingHCmd) +{ + // Sensible defaults for the heat pump mode + + byte operatingMode = FUJITSU_AIRCON1_MODE_HEAT; + byte fanSpeed = FUJITSU_AIRCON1_FAN_AUTO; + byte temperature = 23; + + if (powerModeCmd == POWER_OFF) + { + operatingMode = FUJITSU_AIRCON1_MODE_OFF; + } + else + { + switch (operatingModeCmd) + { + case MODE_AUTO: + operatingMode = FUJITSU_AIRCON1_MODE_AUTO; + break; + case MODE_HEAT: + operatingMode = FUJITSU_AIRCON1_MODE_HEAT; + break; + case MODE_COOL: + operatingMode = FUJITSU_AIRCON1_MODE_COOL; + break; + case MODE_DRY: + operatingMode = FUJITSU_AIRCON1_MODE_DRY; + break; + case MODE_FAN: + operatingMode = FUJITSU_AIRCON1_MODE_FAN; + // When Fujitsu goes to FAN mode, it sets the low bit of the byte with the temperature. What is the meaning of that? + break; + } + } + + switch (fanSpeedCmd) + { + case FAN_AUTO: + fanSpeed = FUJITSU_AIRCON1_FAN_AUTO; + break; + case FAN_1: + fanSpeed = FUJITSU_AIRCON1_FAN1; + break; + case FAN_2: + fanSpeed = FUJITSU_AIRCON1_FAN2; + break; + case FAN_3: + fanSpeed = FUJITSU_AIRCON1_FAN3; + break; + case FAN_4: + fanSpeed = FUJITSU_AIRCON1_FAN4; + break; + } + + if ( temperatureCmd > 15 && temperatureCmd < 31) + { + temperature = temperatureCmd; + } + + sendFujitsu(IR, operatingMode, fanSpeed, temperature); +} + +void FujitsuHeatpumpIR::sendFujitsu(IRSender& IR, byte operatingMode, byte fanSpeed, byte temperature) +{ + // ON, HEAT, AUTO FAN, +24 degrees + byte FujitsuTemplate[] = { 0x14, 0x63, 0x00, 0x10, 0x10, 0xFE, 0x09, 0x30, 0x80, 0x04, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00 }; + // 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 + + static const prog_uint8_t OFF_msg[] PROGMEM = { 0x14, 0x63, 0x00, 0x10, 0x10, 0x02, 0xFD }; + byte checksum = 0x00; + + // Set the operatingmode on the template message + FujitsuTemplate[9] = operatingMode; + + // Set the temperature on the template message + FujitsuTemplate[8] = (temperature - 16) << 4; + + // Set the fan speed on the template message + FujitsuTemplate[10] = fanSpeed; + + // Calculate the checksum + for (int i=0; i<15; i++) { + checksum += FujitsuTemplate[i]; + } + + FujitsuTemplate[15] = (byte)(0x9E - checksum); + + // 40 kHz PWM frequency + IR.setFrequency(40); + + // Header + IR.mark(FUJITSU_AIRCON1_HDR_MARK); + IR.space(FUJITSU_AIRCON1_HDR_SPACE); + + if (operatingMode == FUJITSU_AIRCON1_MODE_OFF) { + // OFF + for (int i=0; i +#include "IRSender.h" +#include "HeatpumpIR.h" + + +// Fujitsu Nocria (AWYZ14) timing constants (remote control P/N AR-PZ2) +#define FUJITSU_AIRCON1_HDR_MARK 3250 +#define FUJITSU_AIRCON1_HDR_SPACE 1550 +#define FUJITSU_AIRCON1_BIT_MARK 400 +#define FUJITSU_AIRCON1_ONE_SPACE 1200 +#define FUJITSU_AIRCON1_ZERO_SPACE 390 + +// Fujitsu codes +#define FUJITSU_AIRCON1_MODE_AUTO 0x00 // Operating mode +#define FUJITSU_AIRCON1_MODE_HEAT 0x04 +#define FUJITSU_AIRCON1_MODE_COOL 0x01 +#define FUJITSU_AIRCON1_MODE_DRY 0x02 +#define FUJITSU_AIRCON1_MODE_FAN 0x03 +#define FUJITSU_AIRCON1_MODE_OFF 0xFF // Power OFF - not real codes, but we need something... +#define FUJITSU_AIRCON1_FAN_AUTO 0x00 // Fan speed +#define FUJITSU_AIRCON1_FAN1 0x04 +#define FUJITSU_AIRCON1_FAN2 0x03 +#define FUJITSU_AIRCON1_FAN3 0x02 +#define FUJITSU_AIRCON1_FAN4 0x01 + + +class FujitsuHeatpumpIR : public HeatpumpIR +{ + public: + FujitsuHeatpumpIR(); + void send(IRSender& IR, byte powerModeCmd, byte operatingModeCmd, byte fanSpeedCmd, byte temperatureCmd, byte swingVCmd, byte swingHCmd); + + private: + void sendFujitsu(IRSender& IR, byte operatingMode, byte fanSpeed, byte temperature); +}; + +#endif diff --git a/HeatpumpIR.cpp b/HeatpumpIR.cpp new file mode 100644 index 0000000..388c97b --- /dev/null +++ b/HeatpumpIR.cpp @@ -0,0 +1,10 @@ +#include +#include "HeatpumpIR.h" + +HeatpumpIR::HeatpumpIR() +{ +} + +void HeatpumpIR::send(IRSender& IR, byte powerModeCmd, byte operatingModeCmd, byte fanSpeedCmd, byte temperatureCmd, byte swingVCmd, byte swingHCmd) +{ +} diff --git a/HeatpumpIR.h b/HeatpumpIR.h new file mode 100644 index 0000000..9898f8b --- /dev/null +++ b/HeatpumpIR.h @@ -0,0 +1,64 @@ +/* + The generic heatpump interface + * Constants for modes + * The constructor and the 'send' command interface +*/ + +#ifndef HeatpumpIR_h +#define HeatpumpIR_h + +#include +#include "IRSender.h" + + +// Power state +#define POWER_OFF 0 +#define POWER_ON 1 + +// Operating modes +#define MODE_AUTO 1 +#define MODE_HEAT 2 +#define MODE_COOL 3 +#define MODE_DRY 4 +#define MODE_FAN 5 +#define MODE_MAINT 6 + +// Fan speeds. Note that some heatpumps have less than 5 fan speeds +#define FAN_AUTO 1 +#define FAN_1 2 +#define FAN_2 3 +#define FAN_3 4 +#define FAN_4 5 +#define FAN_5 6 + +// Vertical air directions. Note that these cannot be set on all heat pumps +#define VDIR_AUTO 0 +#define VDIR_MANUAL 0 +#define VDIR_SWING 1 +#define VDIR_UP 2 +#define VDIR_MUP 3 +#define VDIR_MIDDLE 4 +#define VDIR_MDOWN 5 +#define VDIR_DOWN 6 + +// Horizontal air directions. Note that these cannot be set on all heat pumps +#define HDIR_AUTO 0 +#define HDIR_MANUAL 0 +#define HDIR_SWING 1 +#define HDIR_MIDDLE 2 +#define HDIR_LEFT 3 +#define HDIR_MLEFT 4 +#define HDIR_MRIGHT 5 +#define HDIR_RIGHT 6 + + +class HeatpumpIR +{ + protected: + HeatpumpIR(); // Cannot create generic heatpump instances + + public: + virtual void send(IRSender& IR, byte powerModeCmd, byte operatingModeCmd, byte fanSpeedCmd, byte temperatureCmd, byte swingVCmd, byte swingHCmd); +}; + +#endif diff --git a/IRSender.cpp b/IRSender.cpp new file mode 100644 index 0000000..b457699 --- /dev/null +++ b/IRSender.cpp @@ -0,0 +1,193 @@ +#include +#include "IRSender.h" + +// Heavily based on Ken Shirriff's IRRemote library: +// https://github.com/shirriff/Arduino-IRremote +// +// For PWM on Arduino, see http://playground.arduino.cc/Main/TimerPWMCheatsheet + +IRSender::IRSender(int pin) +{ + _pin = pin; +} + +// Set the PWM frequency. The selected pin determines which timer to use +void IRSender::setFrequency(int frequency) +{ + uint8_t pwmval8 = F_CPU / 2000 / (frequency); + uint16_t pwmval16 = F_CPU / 2000 / (frequency); + + pinMode(_pin, OUTPUT); + digitalWrite(_pin, LOW); // When not sending PWM, we want it low + + switch (_pin) + { +#if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) +// Arduino Mega + case 9: + // Fall-through to 10, timer2 controls both 9 and 10 + case 10: + TCCR2A = _BV(WGM20); + TCCR2B = _BV(WGM22) | _BV(CS20); + OCR2A = pwmval8; + OCR2B = pwmval8 / 3; + break; + case 11: + // Fall-through to 12, timer1 controls both 11 and 12 + case 12: + TCCR1A = _BV(WGM11); + TCCR1B = _BV(WGM13) | _BV(CS10); + ICR1 = pwmval16; + OCR1A = pwmval16 / 3; + OCR1B = pwmval16 / 3; + break; + case 46: // Timer 5 on Arduino Mega + TCCR5A = _BV(WGM51); + TCCR5B = _BV(WGM53) | _BV(CS50); + ICR5 = pwmval; + OCR5A = pwmval / 3; +#else +// Arduino Duemilanove etc + case 3: + // Fall-through to 11, timer2 controls both 3 and 11 + case 11: + TCCR2A = _BV(WGM20); + TCCR2B = _BV(WGM22) | _BV(CS20); + OCR2A = pwmval8; + OCR2B = pwmval8 / 3; + break; + case 9: + // Fall-through to 10, timer1 controls both 9 and 10 + case 10: + TCCR1A = _BV(WGM11); + TCCR1B = _BV(WGM13) | _BV(CS10); + ICR1 = pwmval16; + OCR1A = pwmval16 / 3; + OCR1B = pwmval16 / 3; + break; +#endif + } +} + +// Send a byte (8 bits) over IR +void IRSender::sendIRByte(byte sendByte, int bitMarkLength, int zeroSpaceLength, int oneSpaceLength) +{ + for (int i=0; i<8 ; i++) + { + if (sendByte & 0x01) + { + mark(bitMarkLength); + space(oneSpaceLength); + } + else + { + mark(bitMarkLength); + space(zeroSpaceLength); + } + + sendByte >>= 1; + } +} + +// The Carrier IR protocol has the bits in a reverse order (compared to the other heatpumps) +// See http://www.nrtm.org/index.php/2013/07/25/reverse-bits-in-a-byte/ +byte IRSender::bitReverse(byte x) +{ + // 01010101 | 10101010 + x = ((x >> 1) & 0x55) | ((x << 1) & 0xaa); + // 00110011 | 11001100 + x = ((x >> 2) & 0x33) | ((x << 2) & 0xcc); + // 00001111 | 11110000 + x = ((x >> 4) & 0x0f) | ((x << 4) & 0xf0); + return x; +} + +// Send an IR 'mark' symbol, i.e. transmitter ON +void IRSender::mark(int markLength) +{ + switch (_pin) + { +#if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) +// Arduino Mega + case 9: + (TCCR2A |= _BV(COM2B1)); // Enable pin 3 PWM output + break; + case 11: + (TCCR1A |= _BV(COM1A1)); // Enable pin 9 PWM output + break; + case 12: + (TCCR1A |= _BV(COM1B1)); // Enable pin 10 PWM output + break; + case 10: + (TCCR2A |= _BV(COM2A1)); // Enable pin 11 PWM output + break; + case 46: + (TCCR5A |= _BV(COM5A1)); // Enable pin 46 PWM output on Arduino Mega + break; +#else +// Arduino Duemilanove etc + case 3: + (TCCR2A |= _BV(COM2B1)); // Enable pin 3 PWM output + break; + case 9: + (TCCR1A |= _BV(COM1A1)); // Enable pin 9 PWM output + break; + case 10: + (TCCR1A |= _BV(COM1B1)); // Enable pin 10 PWM output + break; + case 11: + (TCCR2A |= _BV(COM2A1)); // Enable pin 11 PWM output + break; +#endif +} + + delayMicroseconds(markLength); +} + +// Send an IR 'space' symbol, i.e. transmitter OFF +void IRSender::space(int spaceLength) +{ + switch (_pin) + { +#if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) +// Arduino Mega + case 9: + (TCCR2A &= ~(_BV(COM2B1))); // Disable pin 3 PWM output + break; + case 11: + (TCCR1A &= ~(_BV(COM1A1))); // Disable pin 9 PWM output + break; + case 12: + (TCCR1A &= ~(_BV(COM1B1))); // Disable pin 10 PWM output + break; + case 10: + (TCCR2A &= ~(_BV(COM2A1))); // Disable pin 11 PWM output + break; + case 46: + (TCCR5A &= ~(_BV(COM5A1))); // Disable pin 46 PWM output on Arduino Mega +#else +// Arduino Duemilanove etc + case 3: + (TCCR2A &= ~(_BV(COM2B1))); // Disable pin 3 PWM output + break; + case 9: + (TCCR1A &= ~(_BV(COM1A1))); // Disable pin 9 PWM output + break; + case 10: + (TCCR1A &= ~(_BV(COM1B1))); // Disable pin 10 PWM output + break; + case 11: + (TCCR2A &= ~(_BV(COM2A1))); // Disable pin 11 PWM output + break; +#endif +} + + // Mitsubishi heatpump uses > 16383us spaces, and delayMicroseconds only works up to 2^14 - 1 us + // Use the less accurate milliseconds delay for longer delays + + if (spaceLength < 16383) { + delayMicroseconds(spaceLength); + } else { + delay(spaceLength/1000); + } +} diff --git a/IRSender.h b/IRSender.h new file mode 100644 index 0000000..e873d8c --- /dev/null +++ b/IRSender.h @@ -0,0 +1,23 @@ +/* + Class to send IR signals using the Arduino PWM +*/ +#ifndef IRSender_h +#define IRSender_h + +#include + +class IRSender +{ + public: + IRSender(int pin); + void setFrequency(int frequency); + void sendIRByte(byte sendByte, int bitMarkLength, int zeroSpaceLength, int oneSpaceLength); + byte bitReverse(byte x); + void space(int spaceLength); + void mark(int markLength); + + private: + int _pin; +}; + +#endif diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..d7f1051 --- /dev/null +++ b/LICENSE @@ -0,0 +1,339 @@ +GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + {description} + Copyright (C) {year} {fullname} + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + {signature of Ty Coon}, 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. diff --git a/MideaHeatpumpIR.cpp b/MideaHeatpumpIR.cpp new file mode 100644 index 0000000..b38de80 --- /dev/null +++ b/MideaHeatpumpIR.cpp @@ -0,0 +1,141 @@ +#include +#include "MideaHeatpumpIR.h" + +MideaHeatpumpIR::MideaHeatpumpIR() +{ +} + +void MideaHeatpumpIR::send(IRSender& IR, byte powerModeCmd, byte operatingModeCmd, byte fanSpeedCmd, byte temperatureCmd, byte swingVCmd, byte swingHCmd) +{ + // Sensible defaults for the heat pump mode + byte operatingMode = MIDEA_AIRCON1_MODE_HEAT; + byte fanSpeed = MIDEA_AIRCON1_FAN_AUTO; + byte temperature = 23; + + switch (powerModeCmd) + { + case 0: + // OFF is a special case + operatingMode = MIDEA_AIRCON1_MODE_OFF; + sendMidea(IR, operatingMode, fanSpeed, temperature); + return; + } + + switch (operatingModeCmd) + { + case MODE_AUTO: + operatingMode = MIDEA_AIRCON1_MODE_AUTO; + break; + case MODE_HEAT: + operatingMode = MIDEA_AIRCON1_MODE_HEAT; + break; + case MODE_COOL: + operatingMode = MIDEA_AIRCON1_MODE_COOL; + break; + case MODE_DRY: + operatingMode = MIDEA_AIRCON1_MODE_DRY; + break; + case MODE_FAN: + operatingMode = MIDEA_AIRCON1_MODE_FAN; + break; + case MODE_MAINT: + // Maintenance mode ('FP' on the remote) is a special mode on Midea + operatingMode = MIDEA_AIRCON1_MODE_FP; + sendMidea(IR, operatingMode, fanSpeed, temperature); + return; + } + + switch (fanSpeedCmd) + { + case FAN_AUTO: + fanSpeed = MIDEA_AIRCON1_FAN_AUTO; + break; + case FAN_1: + fanSpeed = MIDEA_AIRCON1_FAN1; + break; + case FAN_2: + fanSpeed = MIDEA_AIRCON1_FAN2; + break; + case FAN_3: + fanSpeed = MIDEA_AIRCON1_FAN3; + break; + } + + if ( temperatureCmd > 15 && temperatureCmd < 31) + { + temperature = temperatureCmd; + } + + sendMidea(IR, operatingMode, fanSpeed, temperature); +} + +// Send the Midea code +void MideaHeatpumpIR::sendMidea(IRSender& IR, byte operatingMode, byte fanSpeed, byte temperature) +{ + byte sendBuffer[3] = { 0x4D, 0x00, 0x00 }; // First byte is always 0x4D + + static const prog_uint8_t temperatures[] PROGMEM = {0, 8, 12, 4, 6, 14, 10, 2, 3, 11, 9, 1, 5, 13 }; + + static const prog_uint8_t OffMsg[] PROGMEM = {0x4D, 0xDE, 0x07 }; + static const prog_uint8_t FPMsg[] PROGMEM = {0xAD, 0xAF, 0xB5 }; + + if (operatingMode == MIDEA_AIRCON1_MODE_OFF) + { + memcpy_P(sendBuffer, OffMsg, sizeof(sendBuffer)); + } + else if (operatingMode == MIDEA_AIRCON1_MODE_FP) + { + memcpy_P(sendBuffer, FPMsg, sizeof(sendBuffer)); + } + else + { + sendBuffer[1] = ~fanSpeed; + + if ( operatingMode == MIDEA_AIRCON1_MODE_FAN ) + { + sendBuffer[2] = MIDEA_AIRCON1_MODE_DRY | 0x07; + } + else + { + sendBuffer[2] = operatingMode | temperatures[temperature-17]; + } + } + + // Send the code + sendMidearaw(IR, sendBuffer); +} + +// Send the Midea raw code +void MideaHeatpumpIR::sendMidearaw(IRSender& IR, byte sendBuffer[]) +{ + // 40 kHz PWM frequency + IR.setFrequency(40); + + // Header + IR.mark(MIDEA_AIRCON1_HDR_MARK); + IR.space(MIDEA_AIRCON1_HDR_SPACE); + + // Six bytes, every second byte is a bitwise not of the previous byte + for (int i=0; i<3; i++) { + IR.sendIRByte(sendBuffer[i], MIDEA_AIRCON1_BIT_MARK, MIDEA_AIRCON1_ZERO_SPACE, MIDEA_AIRCON1_ONE_SPACE); + IR.sendIRByte(~sendBuffer[i], MIDEA_AIRCON1_BIT_MARK, MIDEA_AIRCON1_ZERO_SPACE, MIDEA_AIRCON1_ONE_SPACE); + } + + // Pause + IR.mark(MIDEA_AIRCON1_BIT_MARK); + IR.space(MIDEA_AIRCON1_MSG_SPACE); + + // Header, two last bytes repeated + IR.mark(MIDEA_AIRCON1_HDR_MARK); + IR.space(MIDEA_AIRCON1_HDR_SPACE); + + // Six bytes, every second byte is a bitwise not of the previous byte + for (int i=0; i<3; i++) { + IR.sendIRByte(sendBuffer[i], MIDEA_AIRCON1_BIT_MARK, MIDEA_AIRCON1_ZERO_SPACE, MIDEA_AIRCON1_ONE_SPACE); + IR.sendIRByte(~sendBuffer[i], MIDEA_AIRCON1_BIT_MARK, MIDEA_AIRCON1_ZERO_SPACE, MIDEA_AIRCON1_ONE_SPACE); + } + + // End mark + IR.mark(MIDEA_AIRCON1_BIT_MARK); + IR.space(0); +} \ No newline at end of file diff --git a/MideaHeatpumpIR.h b/MideaHeatpumpIR.h new file mode 100644 index 0000000..801b6c2 --- /dev/null +++ b/MideaHeatpumpIR.h @@ -0,0 +1,46 @@ +/* + Midea MSR1-12HRN1-QC2 + MOA1-12HN1-QC2 heatpump control (remote control P/N RG51M1/E) + This heatpump is sold as 'Ultimate Pro Plus 13FP' in Finland, by www.ultimatemarket.com +*/ +#ifndef MideaHeatpumpIR_h +#define MideaHeatpumpIR_h + +#include +#include "IRSender.h" +#include "HeatpumpIR.h" + +// Midea timing constants, Midea MSR1-12HRN1-QC2 + MOA1-12HN1-QC2, sold as Ultimate Pro Plus Basic 13FP in Finland (remote control P/N RG51M1/E) +#define MIDEA_AIRCON1_HDR_MARK 4350 +#define MIDEA_AIRCON1_HDR_SPACE 4230 +#define MIDEA_AIRCON1_BIT_MARK 520 +#define MIDEA_AIRCON1_ONE_SPACE 1650 +#define MIDEA_AIRCON1_ZERO_SPACE 550 +#define MIDEA_AIRCON1_MSG_SPACE 5100 + +// MIDEA codes +#define MIDEA_AIRCON1_MODE_AUTO 0x10 // Operating mode +#define MIDEA_AIRCON1_MODE_HEAT 0x30 +#define MIDEA_AIRCON1_MODE_COOL 0x00 +#define MIDEA_AIRCON1_MODE_DRY 0x20 +#define MIDEA_AIRCON1_MODE_FAN 0x60 +#define MIDEA_AIRCON1_MODE_FP 0x70 // Not a real mode... +#define MIDEA_AIRCON1_MODE_OFF 0xFE // Power OFF - not real codes, but we need something... +#define MIDEA_AIRCON1_MODE_ON 0xFF // Power ON +#define MIDEA_AIRCON1_FAN_AUTO 0x02 // Fan speed +#define MIDEA_AIRCON1_FAN1 0x06 +#define MIDEA_AIRCON1_FAN2 0x05 +#define MIDEA_AIRCON1_FAN3 0x03 + + +class MideaHeatpumpIR : public HeatpumpIR +{ + public: + MideaHeatpumpIR(); + void send(IRSender& IR, byte powerModeCmd, byte operatingModeCmd, byte fanSpeedCmd, byte temperatureCmd, byte swingVCmd, byte swingHCmd); + + private: + void sendMidea(IRSender& IR, byte operatingMode, byte fanSpeed, byte temperature); + void sendMidearaw(IRSender& IR, byte sendBuffer[]); +}; + +#endif diff --git a/MitsubishiHeatpumpIR.cpp b/MitsubishiHeatpumpIR.cpp new file mode 100644 index 0000000..5eaa052 --- /dev/null +++ b/MitsubishiHeatpumpIR.cpp @@ -0,0 +1,118 @@ +#include +#include "MitsubishiHeatpumpIR.h" + +MitsubishiHeatpumpIR::MitsubishiHeatpumpIR() +{ +} + +void MitsubishiHeatpumpIR::send(IRSender& IR, byte powerModeCmd, byte operatingModeCmd, byte fanSpeedCmd, byte temperatureCmd, byte swingVCmd, byte swingHCmd) +{ + // Sensible defaults for the heat pump mode + + byte powerMode = MITSUBISHI_AIRCON1_MODE_ON; + byte operatingMode = MITSUBISHI_AIRCON1_MODE_HEAT; + byte fanSpeed = MITSUBISHI_AIRCON1_FAN_AUTO; + byte temperature = 23; + + if (powerModeCmd == 0) + { + powerMode = MITSUBISHI_AIRCON1_MODE_OFF; + } + + switch (operatingModeCmd) + { + case MODE_AUTO: + operatingMode = MITSUBISHI_AIRCON1_MODE_AUTO; + break; + case MODE_HEAT: + operatingMode = MITSUBISHI_AIRCON1_MODE_HEAT; + break; + case MODE_COOL: + operatingMode = MITSUBISHI_AIRCON1_MODE_COOL; + break; + case MODE_DRY: + operatingMode = MITSUBISHI_AIRCON1_MODE_DRY; + break; + case MODE_FAN: + operatingMode = MITSUBISHI_AIRCON1_MODE_COOL; + // Temperature needs to be set to 31 degrees for 'simulated' FAN mode + temperatureCmd = 31; + break; + } + + switch (fanSpeedCmd) + { + case FAN_AUTO: + fanSpeed = MITSUBISHI_AIRCON1_FAN_AUTO; + break; + case FAN_1: + fanSpeed = MITSUBISHI_AIRCON1_FAN1; + break; + case FAN_2: + fanSpeed = MITSUBISHI_AIRCON1_FAN2; + break; + case FAN_3: + fanSpeed = MITSUBISHI_AIRCON1_FAN3; + break; + case FAN_4: + fanSpeed = MITSUBISHI_AIRCON1_FAN4; + break; + } + + if ( temperatureCmd > 15 && temperatureCmd < 32) + { + temperature = temperatureCmd; + } + + sendMitsubishi(IR, powerMode, operatingMode, fanSpeed, temperature, 0, 0); +} + +void MitsubishiHeatpumpIR::sendMitsubishi(IRSender& IR, byte powerMode, byte operatingMode, byte fanSpeed, byte temperature, byte swingVCmd, byte swingHCmd) +{ + byte MitsubishiTemplate[] = { 0x23, 0xCB, 0x26, 0x01, 0x00, 0x20, 0x48, 0x00, 0xC0, 0x7A, 0x61, 0x00, 0x00, 0x00, 0x10, 0x40, 0x00, 0x00 }; + // 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 + + byte checksum = 0x00; + + // Set the operatingmode on the template message + MitsubishiTemplate[5] = powerMode; + MitsubishiTemplate[6] = operatingMode; + + // Set the temperature on the template message + MitsubishiTemplate[7] = temperature - 16; + + // Set the operatingmode on the template message + MitsubishiTemplate[9] = fanSpeed; + + // Calculate the checksum + for (int i=0; i<17; i++) { + checksum += MitsubishiTemplate[i]; + } + + MitsubishiTemplate[17] = checksum; + + // 40 kHz PWM frequency + IR.setFrequency(40); + + // The Mitsubishi data is repeated twice + for (int j=0; j<2; j++) { + // Header + IR.mark(MITSUBISHI_AIRCON1_HDR_MARK); + IR.space(MITSUBISHI_AIRCON1_HDR_SPACE); + + // Data + for (int i=0; i +#include "IRSender.h" +#include "HeatpumpIR.h" + + +// Mitsubishi MSZ FD-25 timing constants (remote control P/N KM09D 0052376) +#define MITSUBISHI_AIRCON1_HDR_MARK 3500 +#define MITSUBISHI_AIRCON1_HDR_SPACE 1700 +#define MITSUBISHI_AIRCON1_BIT_MARK 430 +#define MITSUBISHI_AIRCON1_ONE_SPACE 1250 +#define MITSUBISHI_AIRCON1_ZERO_SPACE 390 +#define MITSUBISHI_AIRCON1_MSG_SPACE 17500 + +// Mitsubishi codes +#define MITSUBISHI_AIRCON1_MODE_AUTO 0x60 // Operating mode +#define MITSUBISHI_AIRCON1_MODE_HEAT 0x48 +#define MITSUBISHI_AIRCON1_MODE_COOL 0x58 +#define MITSUBISHI_AIRCON1_MODE_DRY 0x50 +#define MITSUBISHI_AIRCON1_MODE_OFF 0x00 // Power OFF +#define MITSUBISHI_AIRCON1_MODE_ON 0x20 // Power ON +#define MITSUBISHI_AIRCON1_FAN_AUTO 0xB8 // Fan speed - mixed with vertical swing... +#define MITSUBISHI_AIRCON1_FAN1 0x79 +#define MITSUBISHI_AIRCON1_FAN2 0x7A +#define MITSUBISHI_AIRCON1_FAN3 0x7B +#define MITSUBISHI_AIRCON1_FAN4 0x7C + + +class MitsubishiHeatpumpIR : public HeatpumpIR +{ + public: + MitsubishiHeatpumpIR(); + void send(IRSender& IR, byte powerModeCmd, byte operatingModeCmd, byte fanSpeedCmd, byte temperatureCmd, byte swingVCmd, byte swingHCmd); + + private: + void sendMitsubishi(IRSender& IR, byte powerMode, byte operatingMode, byte fanSpeed, byte temperature, byte swingVCmd, byte swingHCmd); +}; + +#endif diff --git a/PanasonicCKPHeatpumpIR.cpp b/PanasonicCKPHeatpumpIR.cpp new file mode 100644 index 0000000..c451e94 --- /dev/null +++ b/PanasonicCKPHeatpumpIR.cpp @@ -0,0 +1,257 @@ +#include +#include "PanasonicCKPHeatpumpIR.h" + + +PanasonicCKPHeatpumpIR::PanasonicCKPHeatpumpIR() +{ +} + +// Panasonic CKP numeric values to command bytes +void PanasonicCKPHeatpumpIR::send(IRSender& IR, byte powerModeCmd, byte operatingModeCmd, byte fanSpeedCmd, byte temperatureCmd, byte swingVCmd, byte swingHCmd) +{ + // Sensible defaults for the heat pump mode + + byte powerMode = false; + byte operatingMode = PANASONIC_AIRCON1_MODE_KEEP; + byte fanSpeed = PANASONIC_AIRCON1_FAN_AUTO; + byte temperature = 23; + byte swingV = PANASONIC_AIRCON1_VS_UP; + byte 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); + + Serial.print(F("'Timer cancel' timer ID: ")); + Serial.println(panasonicCancelTimer); +*/ +} + +// Send the Panasonic CKP code +void PanasonicCKPHeatpumpIR::sendPanasonicCKP(IRSender& IR, byte operatingMode, byte fanSpeed, byte temperature, byte swingV, byte swingH) +{ + byte 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, byte sendBuffer[]) +{ + // 40 kHz PWM frequency + IR.setFrequency(40); + + // Header, two first bytes 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 bytes 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 prog_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 prog_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 prog_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 + byte 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(40); + + 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) +{ + Serial.println(F("Sending Panasonic CKP timer cancel")); + + sendPanasonicCKPOnOffTimerCancel(IR, false, true); +} + diff --git a/PanasonicCKPHeatpumpIR.h b/PanasonicCKPHeatpumpIR.h new file mode 100644 index 0000000..a7371eb --- /dev/null +++ b/PanasonicCKPHeatpumpIR.h @@ -0,0 +1,57 @@ +/* + Panasonic CKP heatpump control (remote control P/N A75C2295) +*/ +#ifndef PanasonicCKPHeatpumpIR_h +#define PanasonicCKPHeatpumpIR_h + +#include +#include "IRSender.h" +#include "HeatpumpIR.h" + + +// Panasonic CKP timing constants (remote control P/N A75C2295) +#define PANASONIC_AIRCON1_HDR_MARK 3400 +#define PANASONIC_AIRCON1_HDR_SPACE 3500 +#define PANASONIC_AIRCON1_BIT_MARK 800 +#define PANASONIC_AIRCON1_ONE_SPACE 2700 +#define PANASONIC_AIRCON1_ZERO_SPACE 1000 +#define PANASONIC_AIRCON1_MSG_SPACE 14000 + +// Panasonic CKP codes +#define PANASONIC_AIRCON1_MODE_AUTO 0x06 // Operating mode +#define PANASONIC_AIRCON1_MODE_HEAT 0x04 +#define PANASONIC_AIRCON1_MODE_COOL 0x02 +#define PANASONIC_AIRCON1_MODE_DRY 0x03 +#define PANASONIC_AIRCON1_MODE_FAN 0x01 +#define PANASONIC_AIRCON1_MODE_ONOFF 0x00 // Toggle ON/OFF +#define PANASONIC_AIRCON1_MODE_KEEP 0x08 // Do not toggle ON/OFF +#define PANASONIC_AIRCON1_FAN_AUTO 0xF0 // Fan speed +#define PANASONIC_AIRCON1_FAN1 0x20 +#define PANASONIC_AIRCON1_FAN2 0x30 +#define PANASONIC_AIRCON1_FAN3 0x40 +#define PANASONIC_AIRCON1_FAN4 0x50 +#define PANASONIC_AIRCON1_FAN5 0x60 +#define PANASONIC_AIRCON1_VS_SWING 0xF0 // Vertical swing +#define PANASONIC_AIRCON1_VS_UP 0x90 +#define PANASONIC_AIRCON1_VS_MUP 0xA0 +#define PANASONIC_AIRCON1_VS_MIDDLE 0xB0 +#define PANASONIC_AIRCON1_VS_MDOWN 0xC0 +#define PANASONIC_AIRCON1_VS_DOWN 0xD0 +#define PANASONIC_AIRCON1_HS_SWING 0x08 // Horizontal swing +#define PANASONIC_AIRCON1_HS_MANUAL 0x00 + + +class PanasonicCKPHeatpumpIR : public HeatpumpIR +{ + public: + PanasonicCKPHeatpumpIR(); + void send(IRSender& IR, byte powerModeCmd, byte operatingModeCmd, byte fanSpeedCmd, byte temperatureCmd, byte swingVCmd, byte swingHCmd); + void sendPanasonicCKPCancelTimer(IRSender& IR); + + private: + void sendPanasonicCKP(IRSender& IR, byte operatingMode, byte fanSpeed, byte temperature, byte swingV, byte swingH); + void sendPanasonicCKPraw(IRSender& IR, byte sendBuffer[]); + void sendPanasonicCKPOnOffTimerCancel(IRSender& IR, boolean powerState, boolean cancelTimer); +}; + +#endif diff --git a/PanasonicHeatpumpIR.cpp b/PanasonicHeatpumpIR.cpp new file mode 100644 index 0000000..42b58c7 --- /dev/null +++ b/PanasonicHeatpumpIR.cpp @@ -0,0 +1,218 @@ +#include +#include "PanasonicHeatpumpIR.h" + +// This is a protected method, i.e. generic Panasonic instances cannot be created +PanasonicHeatpumpIR::PanasonicHeatpumpIR() +{ +} + +// The different models just set the model accordingly +PanasonicDKEHeatpumpIR::PanasonicDKEHeatpumpIR() +{ + _panasonicModel = PANASONIC_DKE; +} + +PanasonicNKEHeatpumpIR::PanasonicNKEHeatpumpIR() +{ + _panasonicModel = PANASONIC_NKE; +} + +PanasonicJKEHeatpumpIR::PanasonicJKEHeatpumpIR() +{ + _panasonicModel = PANASONIC_JKE; +} + +// Panasonic DKE/NKE/JKE numeric values to command bytes +void PanasonicHeatpumpIR::send(IRSender& IR, byte powerModeCmd, byte operatingModeCmd, byte fanSpeedCmd, byte temperatureCmd, byte swingVCmd, byte swingHCmd) +{ + // Sensible defaults for the heat pump mode + + byte operatingMode = PANASONIC_AIRCON2_TIMER_CNL; + byte fanSpeed = PANASONIC_AIRCON2_FAN_AUTO; + byte temperature = 23; + byte swingV = PANASONIC_AIRCON2_VS_UP; + byte swingH = PANASONIC_AIRCON2_HS_AUTO; + + switch (powerModeCmd) + { + case POWER_ON: + operatingMode |= PANASONIC_AIRCON2_MODE_ON; + 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; + } + + // NKE has +8 / + 10 maintenance heating, which also means MAX fanspeed + if ( _panasonicModel == PANASONIC_NKE ) + { + if ( temperatureCmd == 8 || temperatureCmd == 10 ) + { + temperature = temperatureCmd; + fanSpeed = PANASONIC_AIRCON2_FAN5; + } + } + + sendPanasonic(IR, operatingMode, fanSpeed, temperature, swingV, swingH); +} + +// Send the Panasonic DKE/JKE/NKE code +void PanasonicHeatpumpIR::sendPanasonic(IRSender& IR, byte operatingMode, byte fanSpeed, byte temperature, byte swingV, byte swingH) +{ + // Only bytes 13, 14, 16, 17 and 26 are modified, DKE and JKE seem to share the same template? + static const prog_uint8_t panasonicProgmemTemplate[][27] PROGMEM = { + // DKE, model 0 + { 0x02, 0x20, 0xE0, 0x04, 0x00, 0x00, 0x00, 0x06, 0x02, 0x20, 0xE0, 0x04, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x0E, 0xE0, 0x00, 0x00, 0x01, 0x00, 0x06, 0x00 }, + // JKE, model 1 + { 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 }, + // NKE, model 2 + { 0x02, 0x20, 0xE0, 0x04, 0x00, 0x00, 0x00, 0x06, 0x02, 0x20, 0xE0, 0x04, 0x00, 0x00, 0x00, 0x80, 0x00, 0x06, 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 only having one copy of the template on the SRAM + byte panasonicTemplate[27]; + memcpy_P(panasonicTemplate, panasonicProgmemTemplate[_panasonicModel], sizeof(panasonicTemplate)); + + panasonicTemplate[13] = operatingMode; + panasonicTemplate[14] = temperature << 1; + panasonicTemplate[16] = fanSpeed | swingV; + + // Only the DKE model has a setting for the horizontal air flow + if ( _panasonicModel == PANASONIC_DKE) { + panasonicTemplate[17] = swingH; + } + + // Checksum calculation + + byte checksum = 0xF4; + + for (int i=0; i<26; i++) { + checksum += panasonicTemplate[i]; + } + + panasonicTemplate[26] = checksum; + + // 40 kHz PWM frequency + IR.setFrequency(40); + + // Header + IR.mark(PANASONIC_AIRCON2_HDR_MARK); + IR.space(PANASONIC_AIRCON2_HDR_SPACE); + + // First 8 bytes + for (int i=0; i<8; i++) { + IR.sendIRByte(panasonicTemplate[i], PANASONIC_AIRCON2_BIT_MARK, PANASONIC_AIRCON2_ZERO_SPACE, PANASONIC_AIRCON2_ONE_SPACE); + } + + // Pause + IR.mark(PANASONIC_AIRCON2_BIT_MARK); + IR.space(PANASONIC_AIRCON2_MSG_SPACE); + + // Header + IR.mark(PANASONIC_AIRCON2_HDR_MARK); + IR.space(PANASONIC_AIRCON2_HDR_SPACE); + + // Last 19 bytes + for (int i=8; i<27; i++) { + IR.sendIRByte(panasonicTemplate[i], PANASONIC_AIRCON2_BIT_MARK, PANASONIC_AIRCON2_ZERO_SPACE, PANASONIC_AIRCON2_ONE_SPACE); + } + + IR.mark(PANASONIC_AIRCON2_BIT_MARK); + IR.space(0); +} \ No newline at end of file diff --git a/PanasonicHeatpumpIR.h b/PanasonicHeatpumpIR.h new file mode 100644 index 0000000..35f0970 --- /dev/null +++ b/PanasonicHeatpumpIR.h @@ -0,0 +1,86 @@ +/* + Panasonic DKE/JKE/NKE heatpump control (DKE remote control P/N A75C2616 etc) +*/ +#ifndef PanasonicHeatpumpIR_h +#define PanasonicHeatpumpIR_h + +#include +#include "IRSender.h" +#include "HeatpumpIR.h" + + +// 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 + +// Panasonic message templates +#define PANASONIC_DKE 0 +#define PANASONIC_JKE 1 +#define PANASONIC_NKE 2 + + +class PanasonicHeatpumpIR : public HeatpumpIR +{ + protected: + PanasonicHeatpumpIR(); // Cannot create generic Panasonic heatpump instances + byte _panasonicModel; // Tells whether this is DKE, NKE or JKE (or other supported model...) + + public: + void send(IRSender& IR, byte powerModeCmd, byte operatingModeCmd, byte fanSpeedCmd, byte temperatureCmd, byte swingVCmd, byte swingHCmd); + + private: + void sendPanasonic(IRSender& IR, byte operatingMode, byte fanSpeed, byte temperature, byte swingV, byte swingH); +}; + +class PanasonicDKEHeatpumpIR : public PanasonicHeatpumpIR +{ + public: + PanasonicDKEHeatpumpIR(); +}; + +class PanasonicNKEHeatpumpIR : public PanasonicHeatpumpIR +{ + public: + PanasonicNKEHeatpumpIR(); +}; + +class PanasonicJKEHeatpumpIR : public PanasonicHeatpumpIR +{ + public: + PanasonicJKEHeatpumpIR(); +}; + + +#endif diff --git a/README.md b/README.md new file mode 100644 index 0000000..a886d1c --- /dev/null +++ b/README.md @@ -0,0 +1,20 @@ +arduino-heatpumpir +================== + +An Arduino library to control a Panasonic, Midea, Carrier, Fujitsu or Mitsubishi heat pump/split unit air conditioner. +Currently supports at least these models +* Panasonic E9/E12-CKP (Panasonic remote control P/N A75C2295) +* Panasonic E9/E12-DKE (Panasonic remote control P/N A75C2616) +* Panasonic E9/E12-JKE and E9/E12-NKE +* Midea MSR1-12HRN1-QC2 + MOA1-12HN1-QC2, sold as Ultimate Pro Plus Basic 13FP in Finland (Midea remote control P/N RG51M1/E) +* Carrier 42NQV035G / 38NYV035H2 (Carrier remote control P/N WH-L05SE) +* Fujitsu Nocria AWYZ14 (remote control P/N AR-PZ2) +* Mitsubishi MSZ FD-25, probably also FD-35 (remote control P/N KM09D 0052376) + + +Instructions +------------ +* Download the library, and place it under your personal Arduino 'libraries' directory, under directory 'HeatpumpIR' +* See the example sketches + +![Schema](https://raw.github.com/ToniA/arduino-heatpumpir/master/arduino_irsender.png) diff --git a/arduino_irsender.png b/arduino_irsender.png new file mode 100644 index 0000000000000000000000000000000000000000..7c9e1fba2c4732c7e44a76da784d5f848430937f GIT binary patch literal 2752919 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

d3Ic*adqn`M<38mH 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пk4W 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 literal 0 HcmV?d00001 diff --git a/examples/PanasonicCKPTimer/PanasonicCKPTimer.ino b/examples/PanasonicCKPTimer/PanasonicCKPTimer.ino new file mode 100644 index 0000000..2327fe9 --- /dev/null +++ b/examples/PanasonicCKPTimer/PanasonicCKPTimer.ino @@ -0,0 +1,57 @@ +#include +#include +#include + +/* + This schema demonstrates how to control the Panasonic CKP power state change. The CKP does not have discrete + 'ON' and 'OFF' commands, but only a state switch. So, if the initial power state is now known, switching the + state does not help much. + + Luckily this can be implemented by using the timer: + * The 'send' command will send all the settings AND program the timer so that the pump will turn ON in a minute + * The 'sendPanasonicCKPCancelTimer' will cancel the timer + + The 'turn OFF' must be implemented the same way. + + Of course you can choose to not turn off the timer, but that means that the heatpump will attempt to switch ON + (or OFF) every day at the same time, as the timer will still be active. +*/ + + +IRSender irSender(3); // IR led on Duemilanove digital pin 3 + +PanasonicCKPHeatpumpIR *heatpumpIR; + +Timer timer; + +void setup() +{ + Serial.begin(9600); + delay(500); + + heatpumpIR = new PanasonicCKPHeatpumpIR(); + + Serial.println("Turning the Panasonic CKP heatpump ON by using the timer"); + heatpumpIR->send(irSender, POWER_ON, MODE_HEAT, FAN_2, 24, VDIR_UP, HDIR_AUTO); + Serial.println("The heatpump should have beeped, and the TIMER led should be ON"); + + timer.after(60000, panasonicIsOn); // Called after 1 minute + timer.after(120000, panasonicCancelTimer); // Called after 2 minutes + +} + +void loop() +{ + timer.update(); +} + +void panasonicIsOn() +{ + Serial.println("The heatpump should should turn ON by now, the TIMER led is still ON"); +} + +void panasonicCancelTimer() +{ + heatpumpIR->sendPanasonicCKPCancelTimer(irSender); + Serial.println("The TIMER led should now be OFF"); +} \ No newline at end of file diff --git a/examples/simple/simple.ino b/examples/simple/simple.ino new file mode 100644 index 0000000..8473752 --- /dev/null +++ b/examples/simple/simple.ino @@ -0,0 +1,37 @@ +#include + +#include "FujitsuHeatpumpIR.h" +#include "PanasonicCKPHeatpumpIR.h" +#include "PanasonicHeatpumpIR.h" +#include "CarrierHeatpumpIR.h" +#include "MideaHeatpumpIR.h" +#include "MitsubishiHeatpumpIR.h" + +IRSender irSender(3); // IR led on Duemilanove digital pin 3 + +// Array with all supported heatpumps +HeatpumpIR *heatpumpIR[] = {new PanasonicCKPHeatpumpIR(), new PanasonicDKEHeatpumpIR(), new PanasonicJKEHeatpumpIR(), + new PanasonicNKEHeatpumpIR(), new CarrierHeatpumpIR(), new MideaHeatpumpIR(), + new FujitsuHeatpumpIR(), new MitsubishiHeatpumpIR(), NULL}; + +void setup() +{ + Serial.begin(9600); + delay(500); + + Serial.println("Starting"); +} + +void loop() +{ + int i = 0; + + do { + // Send the same IR command to all supported heatpumps + heatpumpIR[i]->send(irSender, POWER_ON, MODE_HEAT, FAN_2, 24, VDIR_UP, HDIR_AUTO); + delay(500); + } + while (heatpumpIR[++i] != NULL); + + delay(2000); +} \ No newline at end of file diff --git a/keywords.txt b/keywords.txt new file mode 100644 index 0000000..ac2aa0a --- /dev/null +++ b/keywords.txt @@ -0,0 +1,20 @@ +HeatpumpIR KEYWORD1 +PanasonicCKPHeatpumpIR KEYWORD1 +PanasonicDKEHeatpumpIR KEYWORD1 +PanasonicJKEHeatpumpIR KEYWORD1 +PanasonicNKEHeatpumpIR KEYWORD1 +CarrierHeatpumpIR KEYWORD1 +MideaHeatpumpIR KEYWORD1 +FujitsuHeatpumpIR KEYWORD1 +MitsubishiHeatpumpIR KEYWORD1 + +IRSender KEYWORD1 + +setFrequency KEYWORD2 +sendIRByte KEYWORD2 +bitReverse KEYWORD2 +space KEYWORD2 +mark KEYWORD2 + +send KEYWORD2 +sendPanasonicCKPCancelTimer KEYWORD2 \ No newline at end of file