Merge pull request #211 from ToniA/development

Heatpump / air conditioner library 'HeatpumpIR' merged into MySensors
This commit is contained in:
Henrik Ekblad
2015-09-30 10:06:50 +02:00
42 changed files with 4018 additions and 0 deletions
+170
View File
@@ -0,0 +1,170 @@
#include <CarrierHeatpumpIR.h>
CarrierHeatpumpIR::CarrierHeatpumpIR() : HeatpumpIR()
{
static const char PROGMEM model[] PROGMEM = "carrier";
static const char PROGMEM info[] PROGMEM = "{\"mdl\":\"carrier\",\"dn\":\"Carrier\",\"mT\":17,\"xT\":30,\"fs\":6}";
_model = model;
_info = info;
}
void CarrierHeatpumpIR::send(IRSender& IR, byte powerModeCmd, byte operatingModeCmd, byte fanSpeedCmd, byte temperatureCmd, byte swingVCmd, byte swingHCmd)
{
(void)swingVCmd;
(void)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;
fanSpeedCmd = FAN_AUTO; // Fan speed is always 'AUTO' in DRY mode
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]);
}
// There's something really strange with the checksum calculation...
// With these many of the codes matchs with the code from the real Carrier remote
// Still certain temperatures do not work with fan speeds 1, 2 or 5
switch (sendBuffer[6] & 0xF0) {
case 0x00: // MODE_AUTO - certain temperature / fan speed combinations do not work
checksum += 0x02;
switch (sendBuffer[6] & 0x0F) {
case 0x02: // FAN1
case 0x03: // FAN5
case 0x06: // FAN2
checksum += 0x80;
break;
}
break;
case 0x40: // MODE_DRY - all settings should work
checksum += 0x02;
break;
case 0xC0: // MODE_HEAT - certain temperature / fan speed combinations do not work
switch (sendBuffer[6] & 0x0F) {
case 0x05: // FAN4
case 0x06: // FAN2
checksum += 0xC0;
break;
}
break;
case 0x20: // MODE_FAN - all settings should work
checksum += 0x02;
switch (sendBuffer[6] & 0x0F) {
case 0x02: // FAN1
case 0x03: // FAN5
case 0x06: // FAN2
checksum += 0x80;
break;
}
break;
}
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 (size_t i=0; i<sizeof(sendBuffer); i++) {
IR.sendIRByte(sendBuffer[i], CARRIER_AIRCON1_BIT_MARK, CARRIER_AIRCON1_ZERO_SPACE, CARRIER_AIRCON1_ONE_SPACE);
}
// Pause + new header
IR.mark(CARRIER_AIRCON1_BIT_MARK);
IR.space(CARRIER_AIRCON1_MSG_SPACE);
IR.mark(CARRIER_AIRCON1_HDR_MARK);
IR.space(CARRIER_AIRCON1_HDR_SPACE);
// Payload again
for (size_t i=0; i<sizeof(sendBuffer); i++) {
IR.sendIRByte(sendBuffer[i], CARRIER_AIRCON1_BIT_MARK, CARRIER_AIRCON1_ZERO_SPACE, CARRIER_AIRCON1_ONE_SPACE);
}
// End mark
IR.mark(CARRIER_AIRCON1_BIT_MARK);
IR.space(0);
}
+42
View File
@@ -0,0 +1,42 @@
/*
Carrier 42NQV035G / 38NYV035H2 heatpump control (remote control P/N WH-L05SE)
*/
#ifndef CarrierHeatpumpIR_h
#define CarrierHeatpumpIR_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
+193
View File
@@ -0,0 +1,193 @@
#include <FujitsuHeatpumpIR.h>
FujitsuHeatpumpIR::FujitsuHeatpumpIR() : HeatpumpIR()
{
static const char PROGMEM model[] PROGMEM = "fujitsu_awyz";
static const char PROGMEM info[] PROGMEM = "{\"mdl\":\"fujitsu_awyz\",\"dn\":\"Fujitsu AWYZ\",\"mT\":16,\"xT\":30,\"fs\":5}";
_model = model;
_info = info;
}
void FujitsuHeatpumpIR::send(IRSender& IR, byte powerModeCmd, byte operatingModeCmd, byte fanSpeedCmd, byte temperatureCmd, byte swingVCmd, byte swingHCmd)
{
send(IR, powerModeCmd, operatingModeCmd, fanSpeedCmd, temperatureCmd, swingVCmd, swingHCmd, false);
}
void FujitsuHeatpumpIR::send(IRSender& IR, byte powerModeCmd, byte operatingModeCmd, byte fanSpeedCmd, byte temperatureCmd, byte swingVCmd, byte swingHCmd, bool ecoModeCmd)
{
// Sensible defaults for the heat pump mode
byte operatingMode = FUJITSU_AIRCON1_MODE_HEAT;
byte fanSpeed = FUJITSU_AIRCON1_FAN_AUTO;
byte temperature = 23;
byte swingV = FUJITSU_AIRCON1_VDIR_MANUAL;
byte swingH = FUJITSU_AIRCON1_HDIR_MANUAL;
byte ecoMode = FUJITSU_AIRCON1_ECO_OFF;
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;
}
// Only support 'don't move' or 'swing' at the moment, not any specific position
if ( swingVCmd == VDIR_SWING) {
swingV = FUJITSU_AIRCON1_VDIR_SWING;
}
if ( swingHCmd == HDIR_SWING) {
swingH = FUJITSU_AIRCON1_HDIR_SWING;
}
if (ecoModeCmd) {
ecoMode = FUJITSU_AIRCON1_ECO_ON;
}
sendFujitsu(IR, operatingMode, fanSpeed, temperature, swingV, swingH, ecoMode);
}
void FujitsuHeatpumpIR::sendFujitsu(IRSender& IR, byte operatingMode, byte fanSpeed, byte temperature, byte swingV, byte swingH, byte ecoMode)
{
// 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
byte OFF_msg[] = { 0x14, 0x63, 0x00, 0x10, 0x10, 0x02, 0xFD };
byte checksum = 0x00;
/*
Fujitsu does not have codes to set the air direction to any specific position, but just go to the next position:
byte nextVerticalPosition_msg[] = { 0x14, 0x63, 0x00, 0x10, 0x10, 0x6C, 0x93 };
byte nextHorizontalPosition_msg[] = { 0x14, 0x63, 0x00, 0x10, 0x10, 0x79,0x86 };
These would need to be sent separately...
*/
// Set the operatingmode on the template message
FujitsuTemplate[9] = operatingMode;
// Set the eco mode on the template message
FujitsuTemplate[14] = ecoMode;
// Set the temperature on the template message. The least significant bit should be set to '1'
FujitsuTemplate[8] = (temperature - 16) << 4 | 0x01;
// Set the fan speed and air direction on the template message
FujitsuTemplate[10] = fanSpeed + swingV + swingH;
// Calculate the checksum
for (int i=0; i<15; i++) {
checksum += FujitsuTemplate[i];
}
FujitsuTemplate[15] = (byte)(0x9E - checksum);
if (operatingMode == FUJITSU_AIRCON1_MODE_OFF) {
// OFF
sendFujitsuMsg(IR, sizeof(OFF_msg), OFF_msg);
} else {
sendFujitsuMsg(IR, sizeof(FujitsuTemplate), FujitsuTemplate);
}
}
void FujitsuHeatpumpIR::sendFujitsuHiPower(IRSender& IR)
{
byte HiPower_msg[] = { 0x14, 0x63, 0x00, 0x10, 0x10, 0x39, 0xC6 };
sendFujitsuMsg(IR, sizeof(HiPower_msg), HiPower_msg);
}
void FujitsuHeatpumpIR::sendFujitsuFilterClean(IRSender& IR)
{
byte FilterClean_msg[] = { 0x14, 0x63, 0x00, 0x10, 0x10, 0x02, 0xFD };
sendFujitsuMsg(IR, sizeof(FilterClean_msg), FilterClean_msg);
}
void FujitsuHeatpumpIR::sendFujitsuSuperQuiet(IRSender& IR)
{
byte SuperQuiet_msg[] = { 0x14, 0x63, 0x00, 0x10, 0x10, 0x02, 0xFD };
sendFujitsuMsg(IR, sizeof(SuperQuiet_msg), SuperQuiet_msg);
}
void FujitsuHeatpumpIR::sendFujitsuTestRun(IRSender& IR)
{
byte TestRun_msg[] = { 0x14, 0x63, 0x00, 0x10, 0x10, 0x02, 0xFD };
sendFujitsuMsg(IR, sizeof(TestRun_msg), TestRun_msg);
}
void FujitsuHeatpumpIR::sendFujitsuMsg(IRSender& IR, byte msgSize, byte *msg)
{
// 40 kHz PWM frequency
IR.setFrequency(40);
// Header
IR.mark(FUJITSU_AIRCON1_HDR_MARK);
IR.space(FUJITSU_AIRCON1_HDR_SPACE);
// Data
for (byte i=0; i<msgSize; i++) {
IR.sendIRByte(msg[i], FUJITSU_AIRCON1_BIT_MARK, FUJITSU_AIRCON1_ZERO_SPACE, FUJITSU_AIRCON1_ONE_SPACE);
}
// End mark
IR.mark(FUJITSU_AIRCON1_BIT_MARK);
IR.space(0);
}
+54
View File
@@ -0,0 +1,54 @@
/*
Fujitsu Nocria (AWYZ14) heatpump control (remote control P/N AR-PZ2)
*/
#ifndef FujitsuHeatpumpIR_h
#define FujitsuHeatpumpIR_h
#include <HeatpumpIR.h>
// Fujitsu Nocria (AWYZ14) timing constants (remote control P/N AR-PZ2)
#define FUJITSU_AIRCON1_HDR_MARK 3210
#define FUJITSU_AIRCON1_HDR_SPACE 1680
#define FUJITSU_AIRCON1_BIT_MARK 410
#define FUJITSU_AIRCON1_ONE_SPACE 1230
#define FUJITSU_AIRCON1_ZERO_SPACE 440
// 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
#define FUJITSU_AIRCON1_VDIR_MANUAL 0x00 // Air direction modes
#define FUJITSU_AIRCON1_VDIR_SWING 0x10
#define FUJITSU_AIRCON1_HDIR_MANUAL 0x00
#define FUJITSU_AIRCON1_HDIR_SWING 0x20
#define FUJITSU_AIRCON1_ECO_OFF 0x20
#define FUJITSU_AIRCON1_ECO_ON 0x00
class FujitsuHeatpumpIR : public HeatpumpIR
{
public:
FujitsuHeatpumpIR();
void send(IRSender& IR, byte powerModeCmd, byte operatingModeCmd, byte fanSpeedCmd, byte temperatureCmd, byte swingVCmd, byte swingHCmd);
void send(IRSender& IR, byte powerModeCmd, byte operatingModeCmd, byte fanSpeedCmd, byte temperatureCmd, byte swingVCmd, byte swingHCmd, bool ecoModeCmd);
void sendFujitsuHiPower(IRSender& IR);
void sendFujitsuFilterClean(IRSender& IR);
void sendFujitsuSuperQuiet(IRSender& IR);
void sendFujitsuTestRun(IRSender& IR);
private:
void sendFujitsu(IRSender& IR, byte operatingMode, byte fanSpeed, byte temperature, byte swingV, byte swingH, byte ecoMode);
void sendFujitsuMsg(IRSender& IR, byte msgSize, byte *msg);
};
#endif
+21
View File
@@ -0,0 +1,21 @@
#include <HeatpumpIR.h>
HeatpumpIR::HeatpumpIR()
{
}
// This is a virtual function, i.e. never called
void HeatpumpIR::send(IRSender&, byte, byte, byte, byte, byte, byte)
{
}
// Heatpump model and info getters
const char PROGMEM* HeatpumpIR::model()
{
return _model;
}
const char PROGMEM* HeatpumpIR::info()
{
return _info;
}
+69
View File
@@ -0,0 +1,69 @@
/*
The generic heatpump interface
* Constants for modes
* The constructor and the 'send' command interface
*/
#ifndef HeatpumpIR_h
#define HeatpumpIR_h
#define __PROG_TYPES_COMPAT__
#include <Arduino.h>
#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 0
#define FAN_1 1
#define FAN_2 2
#define FAN_3 3
#define FAN_4 4
#define FAN_5 5
// 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
const char * _model;
const char * _info;
public:
virtual void send(IRSender& IR, byte powerModeCmd, byte operatingModeCmd, byte fanSpeedCmd, byte temperatureCmd, byte swingVCmd, byte swingHCmd);
const char PROGMEM* model();
const char PROGMEM* info();
};
#endif
+129
View File
@@ -0,0 +1,129 @@
#include <HisenseHeatpumpIR.h>
HisenseHeatpumpIR::HisenseHeatpumpIR() : HeatpumpIR()
{
static const char PROGMEM model[] PROGMEM = "hisense_aud";
static const char PROGMEM info[] PROGMEM = "{\"mdl\":\"hisense_aud\",\"dn\":\"Hisense AUD\",\"mT\":18,\"xT\":32,\"fs\":3}";
_model = model;
_info = info;
}
void HisenseHeatpumpIR::send(IRSender& IR, byte powerModeCmd, byte operatingModeCmd , byte fanSpeedCmd , byte temperatureCmd , byte swingVCmd , byte swingHCmd )
{
(void)swingVCmd;
(void)swingHCmd;
// Sensible defaults for the heat pump mode
byte powerMode = HISENSE_AIRCON1_POWER_ON;
byte operatingMode = HISENSE_AIRCON1_MODE_HEAT;
byte fanSpeed = HISENSE_AIRCON1_FAN_AUTO;
byte temperature = 21;
byte swingV=0;
byte swingH=0;
if (powerModeCmd == POWER_OFF)
{
powerMode = HISENSE_AIRCON1_POWER_OFF;
}
else
{
switch (operatingModeCmd)
{
case MODE_AUTO:
operatingMode = HISENSE_AIRCON1_MODE_AUTO;
fanSpeedCmd = FAN_AUTO; // Fan speed is always 'AUTO' in AUTO mode
break;
case MODE_HEAT:
operatingMode = HISENSE_AIRCON1_MODE_HEAT;
break;
case MODE_COOL:
operatingMode = HISENSE_AIRCON1_MODE_COOL;
break;
case MODE_DRY:
operatingMode = HISENSE_AIRCON1_MODE_DRY;
fanSpeedCmd = FAN_AUTO; // Fan speed is always 'AUTO' in DRY mode
break;
case MODE_FAN:
operatingMode = HISENSE_AIRCON1_MODE_FAN;
if ( fanSpeedCmd == FAN_AUTO ) {
fanSpeedCmd = FAN_1; // Fan speed cannot be 'AUTO' in FAN mode
temperatureCmd = 25; // Fixed temperature FAN mode
}
break;
}
}
switch (fanSpeedCmd)
{
case FAN_AUTO:
fanSpeed = HISENSE_AIRCON1_FAN_AUTO;
break;
case FAN_1:
fanSpeed = HISENSE_AIRCON1_FAN1;
break;
case FAN_2:
fanSpeed = HISENSE_AIRCON1_FAN2;
break;
case FAN_3:
fanSpeed = HISENSE_AIRCON1_FAN3;
break;
}
if ( temperatureCmd > 17 && temperatureCmd < 33)
{
temperature = temperatureCmd;
}
sendHisense(IR, powerMode, operatingMode, fanSpeed, temperature, swingV, swingH);
}
// Send the Hisense code
void HisenseHeatpumpIR::sendHisense(IRSender& IR, byte powerMode, byte operatingMode, byte fanSpeed, byte temperature, byte swingV ,byte swingH)
{
(void)swingV;
(void)swingH;
byte HisenseTemplate[] = { 0x87, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, // Header byte 0-1
0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00 }; //
byte i;
// Set the Fan speed, On/Off.
HisenseTemplate[2] = fanSpeed | powerMode;
HisenseTemplate[3] = (((temperature - 18) << 4) | operatingMode ) ;
// Calculate the byte checksum EXOR byte 2 to 12
HisenseTemplate[13] = HisenseTemplate[2];
for (i=3; i<13; i++) {
HisenseTemplate[13]= HisenseTemplate[i] ^ HisenseTemplate[13];
}
// 38 kHz PWM frequency
IR.setFrequency(38);
// Send Header mark
IR.mark(HISENSE_AIRCON1_HDR_MARK);
IR.space(HISENSE_AIRCON1_HDR_SPACE);
// Payload header part
for (i=0; i<7; i++) {
IR.sendIRByte(HisenseTemplate[i], HISENSE_AIRCON1_BIT_MARK, HISENSE_AIRCON1_ZERO_SPACE, HISENSE_AIRCON1_ONE_SPACE);
}
// Mesage space
IR.mark(HISENSE_AIRCON1_BIT_MARK);
IR.space(HISENSE_AIRCON1_MSG_SPACE);
// Payload message part
for (; i<14; i++) {
IR.sendIRByte(HisenseTemplate[i], HISENSE_AIRCON1_BIT_MARK, HISENSE_AIRCON1_ZERO_SPACE, HISENSE_AIRCON1_ONE_SPACE);
}
// End mark
IR.mark(HISENSE_AIRCON1_BIT_MARK);
IR.space(0);
}
+73
View File
@@ -0,0 +1,73 @@
/*
Hisense AQV12PSBN / AQV09ASA heatpump control (remote control P/N zzz)
*/
#ifndef HisenseHeatpumpIR_h
#define HisenseHeatpumpIR_h
#include <HeatpumpIR.h>
// Hisense timing constants
#define HISENSE_AIRCON1_HDR_MARK 9060
#define HISENSE_AIRCON1_HDR_SPACE 4550
#define HISENSE_AIRCON1_BIT_MARK 520
#define HISENSE_AIRCON1_ONE_SPACE 1700
#define HISENSE_AIRCON1_ZERO_SPACE 630
#define HISENSE_AIRCON1_MSG_SPACE 8140
// Power state
#define HISENSE_AIRCON1_POWER_OFF 0x04 //**
#define HISENSE_AIRCON1_POWER_ON 0x00 //**
// Operating modes
// Hisense codes
#define HISENSE_AIRCON1_MODE_AUTO 0x04 // Not available 0x00
#define HISENSE_AIRCON1_MODE_HEAT 0x00
#define HISENSE_AIRCON1_MODE_COOL 0x02
#define HISENSE_AIRCON1_MODE_DRY 0x03
#define HISENSE_AIRCON1_MODE_FAN 0x04
#define HISENSE_AIRCON1_MODE_MAINT 0x04 // Power OFF
// Fan speeds. Note that some heatpumps have less than 5 fan speeds
#define HISENSE_AIRCON1_FAN_AUTO 0x00 // Fan speed
#define HISENSE_AIRCON1_FAN1 0x03 // * low
#define HISENSE_AIRCON1_FAN2 0x02 // * med
#define HISENSE_AIRCON1_FAN3 0x01 // * high
#define HISENSE_AIRCON1_FAN4 0x01 // * high Not available
#define HISENSE_AIRCON1_FAN5 0x01 // * high Not available
// Not available in this model.
// Vertical air directions. Note that these cannot be set on all heat pumps
#define HISENSE_VDIR_AUTO 0
#define HISENSE_VDIR_MANUAL 0
#define HISENSE_VDIR_SWING 0
#define HISENSE_VDIR_UP 0
#define HISENSE_VDIR_MUP 0
#define HISENSE_VDIR_MIDDLE 0
#define HISENSE_VDIR_MDOWN 0
#define HISENSE_VDIR_DOWN 0
// Not available in this model.
// Horizontal air directions. Note that these cannot be set on all heat pumps
#define HISENSE_HDIR_AUTO 0
#define HISENSE_HDIR_MANUAL 0
#define HISENSE_HDIR_SWING 0
#define HISENSE_HDIR_MIDDLE 0
#define HISENSE_HDIR_LEFT 0
#define HISENSE_HDIR_MLEFT 0
#define HISENSE_HDIR_MRIGHT 0
#define HISENSE_HDIR_RIGHT 0
class HisenseHeatpumpIR : public HeatpumpIR
{
public:
HisenseHeatpumpIR();
void send(IRSender& IR, byte powerModeCmd, byte operatingModeCmd, byte fanSpeedCmd, byte temperatureCmd, byte swingVCmd, byte swingHCmd);
private:
void sendHisense(IRSender& IR, byte powerMode, byte operatingMode, byte fanSpeed, byte temperature, byte swingV, byte swingH);
};
#endif
+206
View File
@@ -0,0 +1,206 @@
#include <Arduino.h>
#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(byte 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 44:
// Fall-through to 46, timer 5 controls pins 44, 45 and 46 on Arduino Mega
case 45:
case 46:
TCCR5A = _BV(WGM51) | _BV(WGM50);
TCCR5B = _BV(WGM53) | _BV(CS50);
ICR5 = pwmval16;
OCR5A = pwmval16 / 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 44:
(TCCR5A |= _BV(COM5C1)); // Enable pin 44 PWM output on Arduino Mega
break;
case 45:
(TCCR5A |= _BV(COM5B1)); // Enable pin 45 PWM output on Arduino Mega
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 44:
(TCCR5A &= ~(_BV(COM5C1))); // Disable pin 44 PWM output on Arduino Mega
case 45:
(TCCR5A &= ~(_BV(COM5B1))); // Disable pin 45 PWM output on Arduino Mega
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);
}
}
+23
View File
@@ -0,0 +1,23 @@
/*
Class to send IR signals using the Arduino PWM
*/
#ifndef IRSender_h
#define IRSender_h
#include <Arduino.h>
class IRSender
{
public:
IRSender(byte 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:
byte _pin;
};
#endif
+339
View File
@@ -0,0 +1,339 @@
GNU GENERAL PUBLIC LICENSE
Version 2, June 1991
Copyright (C) 1989, 1991 Free Software Foundation, Inc., <http://fsf.org/>
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.
+150
View File
@@ -0,0 +1,150 @@
#include <MideaHeatpumpIR.h>
MideaHeatpumpIR::MideaHeatpumpIR() : HeatpumpIR()
{
static const char PROGMEM model[] PROGMEM = "midea"; // The basic and inverter Midea's are the same, i.e. at least Pro Plus 10FP, Pro Plus 13Fp, Pro Plus 9 Inverter and Pro Plus 12 Inverter should work
static const char PROGMEM info[] PROGMEM = "{\"mdl\":\"midea\",\"dn\":\"Ultimate Pro Plus\",\"mT\":16,\"xT\":30,\"fs\":4,\"maint\":[10]}";
_model = model;
_info = info;
}
void MideaHeatpumpIR::send(IRSender& IR, byte powerModeCmd, byte operatingModeCmd, byte fanSpeedCmd, byte temperatureCmd, byte swingVCmd, byte swingHCmd)
{
(void)swingVCmd;
(void)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
// Also, this is a switch between 'normal' operation and 'maintenance' operation,
// i.e. if already running on maintenance, the heatpump will go back to normal operation
operatingMode = MIDEA_AIRCON1_MODE_FP;
break;
}
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 > 16 && 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 | pgm_read_byte(&(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);
}
+47
View File
@@ -0,0 +1,47 @@
/*
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
Midea MSR1U-12HRDN1-QRC4W + MOB-12HFN1-QRC4W (remote control P/N RG51I20/BGE)
This heatpump is sold as 'Ultimate 12 Pro Plus Inverter' in Finland, by www.ultimatemarket.com
*/
#ifndef MideaHeatpumpIR_h
#define MideaHeatpumpIR_h
#include <HeatpumpIR.h>
// Midea timing constants
#define MIDEA_AIRCON1_HDR_MARK 4420
#define MIDEA_AIRCON1_HDR_SPACE 4300
#define MIDEA_AIRCON1_BIT_MARK 620
#define MIDEA_AIRCON1_ONE_SPACE 1560
#define MIDEA_AIRCON1_ZERO_SPACE 480
#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
@@ -0,0 +1,214 @@
#include <MitsubishiHeatpumpIR.h>
// These are protected methods, i.e. generic Mitsubishi instances cannot be created directly
MitsubishiHeatpumpIR::MitsubishiHeatpumpIR() : HeatpumpIR()
{
}
// The different models just set the model accordingly
MitsubishiFDHeatpumpIR::MitsubishiFDHeatpumpIR() : MitsubishiHeatpumpIR()
{
static const char PROGMEM model[] PROGMEM = "mitsubishi_fd";
static const char PROGMEM info[] PROGMEM = "{\"mdl\":\"mitsubishi_fd\",\"dn\":\"Mitsubishi FD\",\"mT\":16,\"xT\":31,\"fs\":5}";
_model = model;
_info = info;
_mitsubishiModel = MITSUBISHI_FD;
}
MitsubishiFEHeatpumpIR::MitsubishiFEHeatpumpIR() : MitsubishiHeatpumpIR()
{
static const char PROGMEM model[] PROGMEM = "mitsubishi_fe";
static const char PROGMEM info[] PROGMEM = "{\"mdl\":\"mitsubishi_fe\",\"dn\":\"Mitsubishi FE\",\"mT\":16,\"xT\":31,\"fs\":5,\"maint\":[10]}";
_model = model;
_info = info;
_mitsubishiModel = MITSUBISHI_FE;
}
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;
byte swingV = MITSUBISHI_AIRCON1_VS_AUTO;
byte swingH = MITSUBISHI_AIRCON1_HS_SWING;
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:
if (_mitsubishiModel == MITSUBISHI_FE) {
operatingMode = MITSUBISHI_AIRCON1_MODE_FAN;
temperatureCmd = 24;
} else {
operatingMode = MITSUBISHI_AIRCON1_MODE_COOL;
// Temperature needs to be set to 31 degrees for 'simulated' FAN mode
temperatureCmd = 31;
}
break;
case MODE_MAINT: // Maintenance mode is just the heat mode at +10, FAN5
if (_mitsubishiModel == MITSUBISHI_FE) {
operatingMode |= MITSUBISHI_AIRCON1_MODE_HEAT;
temperature = 10; // Default to +10 degrees
fanSpeedCmd = FAN_AUTO;
}
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 > 16 && temperatureCmd < 32)
{
temperature = temperatureCmd;
}
switch (swingVCmd)
{
case VDIR_AUTO:
swingV = MITSUBISHI_AIRCON1_VS_AUTO;
break;
case VDIR_SWING:
swingV = MITSUBISHI_AIRCON1_VS_SWING;
break;
case VDIR_UP:
swingV = MITSUBISHI_AIRCON1_VS_UP;
break;
case VDIR_MUP:
swingV = MITSUBISHI_AIRCON1_VS_MUP;
break;
case VDIR_MIDDLE:
swingV = MITSUBISHI_AIRCON1_VS_MIDDLE;
break;
case VDIR_MDOWN:
swingV = MITSUBISHI_AIRCON1_VS_MDOWN;
break;
case VDIR_DOWN:
swingV = MITSUBISHI_AIRCON1_VS_DOWN;
break;
}
switch (swingHCmd)
{
case HDIR_AUTO:
case HDIR_SWING:
swingH = MITSUBISHI_AIRCON1_HS_SWING;
break;
case HDIR_MIDDLE:
swingH = MITSUBISHI_AIRCON1_HS_MIDDLE;
break;
case HDIR_LEFT:
swingH = MITSUBISHI_AIRCON1_HS_LEFT;
break;
case HDIR_MLEFT:
swingH = MITSUBISHI_AIRCON1_HS_MLEFT;
break;
case HDIR_RIGHT:
swingH = MITSUBISHI_AIRCON1_HS_RIGHT;
break;
case HDIR_MRIGHT:
swingH = MITSUBISHI_AIRCON1_HS_MRIGHT;
break;
}
sendMitsubishi(IR, powerMode, operatingMode, fanSpeed, temperature, swingV, swingH);
}
void MitsubishiHeatpumpIR::sendMitsubishi(IRSender& IR, byte powerMode, byte operatingMode, byte fanSpeed, byte temperature, byte swingV, byte swingH)
{
byte MitsubishiTemplate[] = { 0x23, 0xCB, 0x26, 0x01, 0x00, 0x20, 0x48, 0x00, 0x00, 0x00, 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
if (temperature == 10) {
MitsubishiTemplate[7] = 0x00; // Maintenance mode
MitsubishiTemplate[15] = 0x20; // This seems to be set to 0x20 on maintenance mode
} else {
MitsubishiTemplate[7] = temperature - 16;
}
// Set the horizontal air direction on the template message
MitsubishiTemplate[8] = swingH;
// Set the fan speed and vertical air direction on the template message
MitsubishiTemplate[9] = fanSpeed | swingV;
// 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 (unsigned int i=0; i<sizeof(MitsubishiTemplate); i++) {
IR.sendIRByte(MitsubishiTemplate[i], MITSUBISHI_AIRCON1_BIT_MARK, MITSUBISHI_AIRCON1_ZERO_SPACE, MITSUBISHI_AIRCON1_ONE_SPACE);
}
// Pause between the first and the second data burst
if (j == 0) {
IR.mark(MITSUBISHI_AIRCON1_BIT_MARK);
IR.space(MITSUBISHI_AIRCON1_MSG_SPACE);
}
}
// End mark
IR.mark(MITSUBISHI_AIRCON1_BIT_MARK);
IR.space(0);
}
@@ -0,0 +1,79 @@
/*
Mitsubishi MSZ FD-25 heatpump control (remote control P/N KM09D 0052376)
*/
#ifndef MitsubishiHeatpumpIR_h
#define MitsubishiHeatpumpIR_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_FAN 0x38 // EF 'FAN' mode
#define MITSUBISHI_AIRCON1_MODE_OFF 0x00 // Power OFF
#define MITSUBISHI_AIRCON1_MODE_ON 0x20 // Power ON
#define MITSUBISHI_AIRCON1_FAN_AUTO 0x00 // Fan speed - mixed with vertical swing...
#define MITSUBISHI_AIRCON1_FAN1 0x01
#define MITSUBISHI_AIRCON1_FAN2 0x02
#define MITSUBISHI_AIRCON1_FAN3 0x03
#define MITSUBISHI_AIRCON1_FAN4 0x04
#define MITSUBISHI_AIRCON1_VS_SWING 0x78 // Vertical swing
#define MITSUBISHI_AIRCON1_VS_AUTO 0x40 // Automatic setting
#define MITSUBISHI_AIRCON1_VS_UP 0x48
#define MITSUBISHI_AIRCON1_VS_MUP 0x50
#define MITSUBISHI_AIRCON1_VS_MIDDLE 0x58
#define MITSUBISHI_AIRCON1_VS_MDOWN 0x60
#define MITSUBISHI_AIRCON1_VS_DOWN 0x68
#define MITSUBISHI_AIRCON1_VS_AUTO2 0x80 // ???
#define MITSUBISHI_AIRCON1_VS_AUTO3 0xB8 // ???
#define MITSUBISHI_AIRCON1_HS_SWING 0xC0 // Horizontal swing
#define MITSUBISHI_AIRCON1_HS_MIDDLE 0x30
#define MITSUBISHI_AIRCON1_HS_LEFT 0x10
#define MITSUBISHI_AIRCON1_HS_MLEFT 0x20
#define MITSUBISHI_AIRCON1_HS_MRIGHT 0x40
#define MITSUBISHI_AIRCON1_HS_RIGHT 0x50
// Mitsubishi model codes
#define MITSUBISHI_FD 0
#define MITSUBISHI_FE 1
class MitsubishiHeatpumpIR : public HeatpumpIR
{
protected: // Cannot create generic Mitsubishi heatpump instances
MitsubishiHeatpumpIR();
byte _mitsubishiModel; // Tells whether this is FD or EF (or other supported model...)
public:
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);
};
class MitsubishiFDHeatpumpIR : public MitsubishiHeatpumpIR
{
public:
MitsubishiFDHeatpumpIR();
};
class MitsubishiFEHeatpumpIR : public MitsubishiHeatpumpIR
{
public:
MitsubishiFEHeatpumpIR();
};
#endif
@@ -0,0 +1,262 @@
#include <PanasonicCKPHeatpumpIR.h>
PanasonicCKPHeatpumpIR::PanasonicCKPHeatpumpIR()
{
static const char PROGMEM model[] PROGMEM = "panasonic_ckp";
static const char PROGMEM info[] PROGMEM = "{\"mdl\":\"panasonic_ckp\",\"dn\":\"Panasonic CKP\",\"mT\":16,\"xT\":30,\"fs\":6}";
_model = model;
_info = info;
}
// 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);
}
@@ -0,0 +1,55 @@
/*
Panasonic CKP heatpump control (remote control P/N A75C2295)
*/
#ifndef PanasonicCKPHeatpumpIR_h
#define PanasonicCKPHeatpumpIR_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
@@ -0,0 +1,237 @@
#include <PanasonicHeatpumpIR.h>
// This is a protected method, i.e. generic Panasonic instances cannot be created
PanasonicHeatpumpIR::PanasonicHeatpumpIR() : HeatpumpIR()
{
}
// The different models just set the model accordingly
PanasonicDKEHeatpumpIR::PanasonicDKEHeatpumpIR() : PanasonicHeatpumpIR()
{
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;
}
PanasonicJKEHeatpumpIR::PanasonicJKEHeatpumpIR() : PanasonicHeatpumpIR()
{
static const char PROGMEM model[] PROGMEM = "panasonic_jke";
static const char PROGMEM info[] PROGMEM = "{\"mdl\":\"panasonic_jke\",\"dn\":\"Panasonic JKE\",\"mT\":16,\"xT\":30,\"fs\":6}";
_model = model;
_info = info;
_panasonicModel = PANASONIC_JKE;
}
PanasonicNKEHeatpumpIR::PanasonicNKEHeatpumpIR() : PanasonicHeatpumpIR()
{
static const char PROGMEM model[] PROGMEM = "panasonic_nke";
static const char PROGMEM info[] PROGMEM = "{\"mdl\":\"panasonic_nke\",\"dn\":\"Panasonic NKE\",\"mT\":16,\"xT\":30,\"fs\":6,\"maint\":[8,10]}";
_model = model;
_info = info;
_panasonicModel = PANASONIC_NKE;
}
// 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);
}
@@ -0,0 +1,83 @@
/*
Panasonic DKE/JKE/NKE heatpump control (DKE remote control P/N A75C2616 etc)
*/
#ifndef PanasonicHeatpumpIR_h
#define PanasonicHeatpumpIR_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 model codes
#define PANASONIC_DKE 0
#define PANASONIC_JKE 1
#define PANASONIC_NKE 2
class PanasonicHeatpumpIR : public HeatpumpIR
{
protected: // Cannot create generic Panasonic heatpump instances
PanasonicHeatpumpIR();
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 PanasonicJKEHeatpumpIR : public PanasonicHeatpumpIR
{
public:
PanasonicJKEHeatpumpIR();
};
class PanasonicNKEHeatpumpIR : public PanasonicHeatpumpIR
{
public:
PanasonicNKEHeatpumpIR();
};
#endif
+21
View File
@@ -0,0 +1,21 @@
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)
* Hisense AUD (remote control Y-H1-01, Y-H1-02(E), Y-J1, Y-E4-07) probably AUC model
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)
+175
View File
@@ -0,0 +1,175 @@
#include <SamsungHeatpumpIR.h>
SamsungHeatpumpIR::SamsungHeatpumpIR() : HeatpumpIR()
{
static const char PROGMEM model[] PROGMEM = "samsung";
static const char PROGMEM info[] PROGMEM = "{\"mdl\":\"samsung\",\"dn\":\"Samsung\",\"mT\":16,\"xT\":27,\"fs\":4}";
_model = model;
_info = info;
}
void SamsungHeatpumpIR::send(IRSender& IR, byte powerModeCmd, byte operatingModeCmd, byte fanSpeedCmd, byte temperatureCmd, byte swingVCmd, byte swingHCmd)
{
(void)swingVCmd;
(void)swingHCmd;
// Sensible defaults for the heat pump mode
byte powerMode = SAMSUNG_AIRCON1_MODE_ON;
byte operatingMode = SAMSUNG_AIRCON1_MODE_HEAT;
byte fanSpeed = SAMSUNG_AIRCON1_FAN_AUTO;
byte temperature = 23;
byte swingV = SAMSUNG_AIRCON1_VS_AUTO;
if (powerModeCmd == POWER_OFF)
{
powerMode = SAMSUNG_AIRCON1_MODE_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;
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;
}
sendSamsung(IR, powerMode, operatingMode, fanSpeed, temperature, swingV);
}
// Send the Samsung code
void SamsungHeatpumpIR::sendSamsung(IRSender& IR, byte powerMode, byte operatingMode, byte fanSpeed, byte temperature, byte swingV)
{
byte SamsungTemplate[] = { 0x02, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x00, // Header part
0x01, 0xD2, 0x0F, 0x00, 0x00, 0x00, 0x00, // Always the same data on POWER messages
0x01, 0x00, 0xFE, 0x71, 0x00, 0x00, 0x00 }; // The actual data is in this part, on bytes 14-20
byte 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 bytes 15-20
for (byte j=15; j<21; j++) {
byte SamsungByte = SamsungTemplate[j];
for (byte i=0; i<8; i++) {
if ( (SamsungByte & 0x01) == 0x01 ) {
SamsungChecksum++;
}
SamsungByte >>= 1;
}
}
// Transform the number of ONE bits to the actual checksum
SamsungChecksum = 32 - SamsungChecksum;
SamsungChecksum <<= 4;
SamsungChecksum += 0x02;
SamsungTemplate[15] = SamsungChecksum;
// 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);
}
+42
View File
@@ -0,0 +1,42 @@
/*
Samsung AQV12PSBN / AQV09ASA heatpump control (remote control P/N zzz)
*/
#ifndef SamsungHeatpumpIR_h
#define SamsungHeatpumpIR_h
#include <HeatpumpIR.h>
// Samsung timing constants
#define SAMSUNG_AIRCON1_HDR_MARK 3000
#define SAMSUNG_AIRCON1_HDR_SPACE 9000
#define SAMSUNG_AIRCON1_BIT_MARK 500
#define SAMSUNG_AIRCON1_ONE_SPACE 1500
#define SAMSUNG_AIRCON1_ZERO_SPACE 500
#define SAMSUNG_AIRCON1_MSG_SPACE 2000
// Samsung codes
#define SAMSUNG_AIRCON1_MODE_AUTO 0x00 // Operating mode
#define SAMSUNG_AIRCON1_MODE_HEAT 0x40
#define SAMSUNG_AIRCON1_MODE_COOL 0x10
#define SAMSUNG_AIRCON1_MODE_DRY 0x20
#define SAMSUNG_AIRCON1_MODE_FAN 0x30
#define SAMSUNG_AIRCON1_MODE_OFF 0xC0 // Power OFF
#define SAMSUNG_AIRCON1_MODE_ON 0xF0 // Power ON
#define SAMSUNG_AIRCON1_FAN_AUTO 0x01 // Fan speed
#define SAMSUNG_AIRCON1_FAN1 0x05 // * low
#define SAMSUNG_AIRCON1_FAN2 0x09 // * med
#define SAMSUNG_AIRCON1_FAN3 0x0B // * high
#define SAMSUNG_AIRCON1_VS_SWING 0xAE // Vertical swing
#define SAMSUNG_AIRCON1_VS_AUTO 0xFE
class SamsungHeatpumpIR : public HeatpumpIR
{
public:
SamsungHeatpumpIR();
void send(IRSender& IR, byte powerModeCmd, byte operatingModeCmd, byte fanSpeedCmd, byte temperatureCmd, byte swingVCmd, byte swingHCmd);
private:
void sendSamsung(IRSender& IR, byte powerMode, byte operatingMode, byte fanSpeed, byte temperature, byte swingV);
};
#endif
Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 MiB

@@ -0,0 +1,56 @@
#include <HisenseHeatpumpIR.h>
IRSender irSender(3); // IR led on Duemilanove digital pin 3
HisenseHeatpumpIR *heatpumpIR;
void setup()
{
Serial.begin(9600);
delay(500);
heatpumpIR = new HisenseHeatpumpIR();
Serial.println(F("Starting"));
}
void loop()
{
int i = 0;
prog_char* buf;
// Send the same IR command to all supported heatpumps
Serial.print(F("Sending IR to "));
// Print the model
buf = (prog_char*)heatpumpIR->model();
// 'model' is a PROGMEM pointer, so need to write a byte at a time
while (char modelChar = pgm_read_byte(buf++))
{
Serial.print(modelChar);
}
Serial.print(F(", info: "));
// Print the info
buf = (prog_char*)heatpumpIR->info();
// 'info' is a PROGMEM pointer, so need to write a byte at a time
while (char infoChar = pgm_read_byte(buf++))
{
Serial.print(infoChar);
}
Serial.println();
heatpumpIR->send(irSender, POWER_ON, MODE_HEAT, FAN_3, 30, VDIR_AUTO, HDIR_AUTO);
delay(1500);
heatpumpIR->send(irSender, POWER_ON, MODE_DRY, FAN_2, 20, VDIR_AUTO, HDIR_AUTO);
delay(1500);
heatpumpIR->send(irSender, POWER_ON, MODE_FAN, FAN_AUTO, 18, VDIR_AUTO, HDIR_AUTO);
delay(1500);
heatpumpIR->send(irSender, POWER_ON, MODE_COOL, FAN_1, 18, VDIR_AUTO, HDIR_AUTO);
delay(2500);
heatpumpIR->send(irSender, POWER_OFF, NULL, NULL, NULL, NULL, NULL);
delay(1500);
}
@@ -0,0 +1,92 @@
#include <MideaHeatpumpIR.h> // https://github.com/ToniA/arduino-heatpumpir
#include <Button.h> // http://playground.arduino.cc/Code/Button
/*
This is a project I did for my my friend's summer cottage. He has an alarm system
with some extra relays which can be controlled via GSM SMS messages. With two relays
we can have four heatpump states:
* relay 1 OFF and relay 2 in any state: heatpump OFF
* relay 1 ON and relay 2 OFF: heatpump ON, normal heating state
* relay 1 ON and relay 2 ON: heatpump ON, maintenance heating (FP mode on Midea's remote)
The heatpump is 'Ultimate 12 Pro Plus Inverter', sold by ultimatemarket.com:
Midea MSR1U-12HRDN1-QRC4W + MOB-12HFN1-QRC4W, with remote control RG51I20/BGE
This same sketch would also control the older 'Ultimate Pro Plus 13FP':
Midea MSR1-12HRN1-QC2 + MOA1-12HN1-QC2 heatpump control, with remote control RG51M1/E
... and very likely many other Midea heatpumps...
The Midea 'FP', or 'maintenance mode', is not really a mode even if the HeatpumpIR library
interface makes it look like a mode. Instead it's a switch between the 'normal' and 'FP'
mode, so to be absolutely sure about the operating mode, we need to turn the heatpump OFF
first, and then turn it on (goes into 'normal' mode), and finally switch into 'FP'.
The IR led is driven by a transistor amplifier. I used 2 ohms as the resistor. I might sound
very small, but also the led operates on very short bursts. For example Panasonic uses 2 ohm
resistor for the IR led on its remotes.
*/
// Heatpump states
const byte heatpumpOff = 0;
const byte heatpumpNormal = 1;
const byte heatpumpMaintenance = 2;
Button relay1 = Button(11,PULLUP); // Heatpump ON-OFF state
Button relay2 = Button(12,PULLUP); // FP mode (maintenance heating at 8 degrees C) ON-OFF state
IRSender irSender(9); // IR led on Duemilanove digital pin 9
byte heatpumpState = heatpumpOff;
HeatpumpIR *heatpumpIR = new MideaHeatpumpIR();
void setup(){
Serial.begin(9600);
delay(500);
Serial.println(F("Starting..."));
}
void loop(){
if (relay1.isPressed()) {
// Normal heating operation
if (!relay2.isPressed() && heatpumpState != heatpumpNormal) {
Serial.println(F("Normal heating operation"));
Serial.println(F("* Sending power OFF"));
heatpumpIR->send(irSender, POWER_OFF, MODE_HEAT, FAN_2, 22, VDIR_UP, HDIR_AUTO);
delay(15 * 1000); // 15 seconds, to allow full shutdown
Serial.println(F("* Sending normal heat command"));
heatpumpIR->send(irSender, POWER_ON, MODE_HEAT, FAN_2, 22, VDIR_UP, HDIR_AUTO);
heatpumpState = heatpumpNormal;
// Maintenance heating operation
} else if (relay2.isPressed() && heatpumpState != heatpumpMaintenance) {
Serial.println(F("Maintenance heating operation"));
Serial.println(F("* Sending power OFF"));
heatpumpIR->send(irSender, POWER_OFF, MODE_HEAT, FAN_2, 22, VDIR_UP, HDIR_AUTO);
delay(15 * 1000); // 15 seconds, to allow full shutdown
Serial.println(F("* Sending normal heat command"));
heatpumpIR->send(irSender, POWER_ON, MODE_HEAT, FAN_2, 22, VDIR_UP, HDIR_AUTO);
delay(5 * 1000); // 15 seconds, to allow full start before switching to maintenance
Serial.println(F("* Switching to maintenance heating"));
heatpumpIR->send(irSender, POWER_ON, MODE_MAINT, FAN_3, 10, VDIR_UP, HDIR_AUTO);
heatpumpState = heatpumpMaintenance;
}
// Power OFF
} else if (!relay1.isPressed() && heatpumpState != heatpumpOff) {
heatpumpState = heatpumpOff;
Serial.println(F("Sending power OFF"));
heatpumpIR->send(irSender, POWER_OFF, MODE_HEAT, FAN_2, 22, VDIR_UP, HDIR_AUTO);
}
delay(500); // 0.5 seconds delay
}
@@ -0,0 +1,204 @@
// HeatpumpIR libraries
#include <FujitsuHeatpumpIR.h>
#include <PanasonicCKPHeatpumpIR.h>
#include <PanasonicHeatpumpIR.h>
#include <CarrierHeatpumpIR.h>
#include <MideaHeatpumpIR.h>
#include <MitsubishiHeatpumpIR.h>
#include <SamsungHeatpumpIR.h>
// Timer library, https://github.com/ToniA/Timer
#include <Timer.h>
// MySensors libraries
#include <MySensor.h>
#include <SPI.h>
// This MySensors node has three childs, with ID's 1, 2 & 3
#define CHILD_1 1
#define CHILD_2 2
#define CHILD_3 3
// MySensors definitions
MySensor gw;
MyMessage irMsg(CHILD_1, V_IR_RECEIVE);
// Domoticz does not yet properly support the S_IR sensor type
MyMessage textMsg(CHILD_2, V_TEXT);
MyMessage sendMsg(CHILD_3, V_LIGHT);
// Array with all supported heatpump models
HeatpumpIR *heatpumpIR[] = { new PanasonicCKPHeatpumpIR(), // 0, keep this if you don't remove the timer for cancelling Panasonic CKP messages
new PanasonicDKEHeatpumpIR(), // 1
new PanasonicJKEHeatpumpIR(), // 2
new PanasonicNKEHeatpumpIR(), // 3
new CarrierHeatpumpIR(), // 4
new MideaHeatpumpIR(), // 5
new FujitsuHeatpumpIR(), // 6
new MitsubishiFDHeatpumpIR(), // 7
new MitsubishiFEHeatpumpIR(), // 8
new SamsungHeatpumpIR() // 9
};
// IR led on PWM output-capable digital pin 3
IRSender irSender(3);
// Timer for sending Panasonic CKP series timer cancellation commands
Timer timer;
int8_t panasonicCKPTimer = 0;
// Number of supported models
byte models = 0;
void setup()
{
Serial.begin(115200);
delay(1000);
Serial.println(F("HeatpumpIR sensor starting up..."));
models = sizeof(heatpumpIR) / sizeof(HeatpumpIR*);
Serial.print(F("Number of supported models: ")); Serial.println(models);
// Auto-assign the node ID, do not forward messages
gw.begin(incomingMessage, AUTO, false);
// Send the sketch version information to the gateway and Controller
gw.sendSketchInfo("Heatpump Sensor", "1.0");
// Register a sensors to the MySensors Gateway
gw.present(CHILD_1, S_IR, "IR sender");
// For Domoticz
gw.present(CHILD_2, S_INFO, "IR data");
gw.present(CHILD_3, S_LIGHT, "IR send");
// The TEXT sensor is not created in Domoticz before data is sent
gw.send(textMsg.setSensor(CHILD_2).set("00000000"));
}
void loop()
{
gw.process();
timer.update();
}
// Handle incoming messages from the MySensors Gateway
void incomingMessage(const MyMessage &message) {
const char *irData;
long irCommand;
// V_IR type message
if (message.type==V_IR_SEND) {
Serial.println(F("Received IR send command..."));
irData = message.getString();
Serial.print(F("Code: 0x"));
Serial.println(irData);
sendHeatpumpIRCommand(irData);
}
// Domoticz
else if (message.type==V_LIGHT) {
// When the button is pressed on Domoticz, request the value of the TEXT sensor
Serial.println(F("Requesting IR code from Domoticz..."));
gw.request(CHILD_2, V_TEXT, 0);
} else if (message.type==V_TEXT) {
// TEXT sensor value is received as a result of the previous step
Serial.println(F("IR code received from Domoticz..."));
Serial.print(F("Code: 0x"));
irData = message.getString();
Serial.println(irData);
sendHeatpumpIRCommand(irData);
// Set the Domoticz switch back to 'OFF' state
gw.send(sendMsg.setSensor(CHILD_3).set(0));
}
}
// Decode the IR command and send the IR command to the heatpump
void sendHeatpumpIRCommand(const char *irCommandString) {
// irCommandString is an 8-digit hex digit
long irCommand = 0;
int sscanfStatus = sscanf(irCommandString, "%lx", &irCommand);
if (sscanfStatus == 1) {
Serial.print(F("IR code conversion OK: 0x"));
Serial.println(irCommand, HEX);
} else {
Serial.println(F("Failed to convert IR hex code to number"));
}
/*
The heatpump command is packed into a 32-bit hex number, see
libraries\HeatpumpIR\HeatpumpIR.h for the constants
12345678
3 MODEL
4 POWER
5 OPERATING MODE
6 FAN SPEED
78 TEMPERATURE IN HEX
00213416 (as an example of a valid code)
2 = PanasonicJKE
1 = Power ON
3 = COOL
4 = FAN 4
16 = Temperature 22 degrees (0x16 = 22)
*/
byte model = (irCommand & 0x00F00000) >> 20;
byte power = (irCommand & 0x00010000) >> 16;
byte mode = (irCommand & 0x0000F000) >> 12;
byte fan = (irCommand & 0x00000F00) >> 8;
byte temp = (irCommand & 0x000000FF);
prog_char* buf;
Serial.print(F("Model: "));
buf = (prog_char*)heatpumpIR[model]->model();
// 'model' is a PROGMEM pointer, so need to write a byte at a time
while (char modelChar = pgm_read_byte(buf++))
{
Serial.print(modelChar);
}
Serial.println();
Serial.print(F("Model #: ")); Serial.println(model);
Serial.print(F("Power: ")); Serial.println(power);
Serial.print(F("Mode: ")); Serial.println(mode);
Serial.print(F("Fan: ")); Serial.println(fan);
Serial.print(F("Temp: ")); Serial.println(temp);
// Heatpump models start from 0, i.e. model number is always less than the number of different models
if (model < models) {
// This is a supported model
// Cancel the timer on Panasonic CKP heatpump
if (model == 0) {
Serial.println(F("Cancelling timer on Panasonic CKP heatpump..."));
timer.stop(panasonicCKPTimer);
}
Serial.println(F("All OK - sending IR command to heatpump..."));
heatpumpIR[model]->send(irSender, power, mode, fan, temp, VDIR_UP, HDIR_AUTO);
if (model == 0) {
Serial.println(F("Scheduling timer cancellation on Panasonic CKP heatpump..."));
panasonicCKPTimer = timer.after(120000, panasonicCancelTimer, (void *)NULL); // Called after 2 minutes
}
}
}
// Cancel the timer on the Panasonic CKP heatpump
void panasonicCancelTimer(void *context)
{
PanasonicCKPHeatpumpIR *panasonicCKPHeatpumpIR = new PanasonicCKPHeatpumpIR();
panasonicCKPHeatpumpIR->sendPanasonicCKPCancelTimer(irSender);
Serial.println(F("The TIMER led should now be OFF"));
}
@@ -0,0 +1,73 @@
# MySensors example
This example shows how HeatpumpIR could be integrated with the MySensors sensor network. As this sensor sends infrared signals,
the natural sensor type is 'S_IR'. The heatpump/air conditioner signals are really long, so the signals need to be encoded somehow.
In this example the idea is to encode just this information, using 24 bits of data
* model code (see the index of the array heatpumpIR in the code)
* power state (for the rest, see the HeatpumpIR.h file for the constants)
* operating mode
* fan speed
* temperature (the temperature is directly the temperature in HEX)
```
An example:
00213416 (as an example of a valid code)
2 = PanasonicJKE
1 = Power ON
3 = COOL
4 = FAN 4
16 = Temperature 22 degrees (0x16 = 22)
```
This sketch creates three sensors:
* S_IR
* S_INFO
* S_LIGHT
The S_INFO and S_LIGHT sensors are for Domoticz, as it does not currently (as of V2.3093) support infrared code sending through the S_IR sensor type.
## Sending the code
### MYSController
* Select the IR sensor on the 'Heatpump Sensor 1.0' node
* Select 'IR_SEND' as the subtype
* Enter for example '00213416' as the payload
### Domoticz
As Domoticz does not really support sending IR code yet, the workaround is to use two sensors. When the node is connected, Domoticz should find two new devices
* A 'Lighting 2 / AC' type sensor with name 'IR send'
* A 'General / Text' type sensor with name 'Unknown' and Data '00000000'
To send a command, first add the sensors. Then update the data of the text sensor to contain the IR code to be sent, and then flip the switch to trigger the sending.
The data can be updated through the JSON REST API, or through the Lua event scripts.
REST API example (assuming that the idx of the text sensor is 105 and the switch is 104, and Domoticz is at 192.168.0.4:8080):
```
http://192.168.0.4:8080/json.htm?type=command&param=udevice&idx=105&nvalue=0&svalue=00213416
http://192.168.0.4:8080/json.htm?type=command&param=switchlight&idx=104&switchcmd=On
```
Lua example:
* Seems that the text sensor can't be updated through the commandArray...
* So now when a device called 'trigger' changes, the text sensor value is first updated to '00213416', and then the IR signal is sent
```
commandArray = {}
for key, value in pairs(devicechanged) do
if (key == 'trigger') then
print("Heatpump script")
commandArray['OpenURL']='http://192.168.0.4:8080/json.htm?type=command&param=udevice&idx=105&nvalue=0&svalue=00213416'
commandArray['IR send']='On'
end
end
return commandArray
```
## Hardware
Use any ATMega328-based Arduino, connect the radio, and finally connect an infrared led (in series with a resistor, like 100 Ohm) between digital pin 3 and GND.
@@ -0,0 +1,56 @@
#include <PanasonicCKPHeatpumpIR.h>
#include <Timer.h>
/*
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, NULL); // Called after 1 minute
timer.after(120000, panasonicCancelTimer, NULL); // Called after 2 minutes
}
void loop()
{
timer.update();
}
void panasonicIsOn(void *context)
{
Serial.println("The heatpump should should turn ON by now, the TIMER led is still ON");
}
void panasonicCancelTimer(void *context)
{
heatpumpIR->sendPanasonicCKPCancelTimer(irSender);
Serial.println("The TIMER led should now be OFF");
}
@@ -0,0 +1,57 @@
#include <FujitsuHeatpumpIR.h>
#include <PanasonicCKPHeatpumpIR.h>
#include <PanasonicHeatpumpIR.h>
#include <CarrierHeatpumpIR.h>
#include <MideaHeatpumpIR.h>
#include <MitsubishiHeatpumpIR.h>
#include <SamsungHeatpumpIR.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 MitsubishiFDHeatpumpIR(), new MitsubishiFEHeatpumpIR(),
new SamsungHeatpumpIR(), NULL};
void setup()
{
Serial.begin(9600);
delay(500);
Serial.println(F("Starting"));
}
void loop()
{
int i = 0;
prog_char* buf;
do {
// Send the same IR command to all supported heatpumps
Serial.print(F("Sending IR to "));
buf = (prog_char*)heatpumpIR[i]->model();
// 'model' is a PROGMEM pointer, so need to write a byte at a time
while (char modelChar = pgm_read_byte(buf++))
{
Serial.print(modelChar);
}
Serial.print(F(", info: "));
buf = (prog_char*)heatpumpIR[i]->info();
// 'info' is a PROGMEM pointer, so need to write a byte at a time
while (char infoChar = pgm_read_byte(buf++))
{
Serial.print(infoChar);
}
Serial.println();
// Send the IR command
heatpumpIR[i]->send(irSender, POWER_ON, MODE_HEAT, FAN_2, 24, VDIR_UP, HDIR_AUTO);
delay(500);
}
while (heatpumpIR[++i] != NULL);
delay(2000);
}
+26
View File
@@ -0,0 +1,26 @@
HeatpumpIR KEYWORD1
PanasonicHeatpumpIR KEYWORD1
PanasonicCKPHeatpumpIR KEYWORD1
PanasonicDKEHeatpumpIR KEYWORD1
PanasonicJKEHeatpumpIR KEYWORD1
PanasonicNKEHeatpumpIR KEYWORD1
CarrierHeatpumpIR KEYWORD1
MideaHeatpumpIR KEYWORD1
FujitsuHeatpumpIR KEYWORD1
MitsubishiHeatpumpIR KEYWORD1
MitsubishiFDHeatpumpIR KEYWORD1
MitsubishiFEHeatpumpIR KEYWORD1
SamsungHeatpumpIR KEYWORD1
IRSender KEYWORD1
setFrequency KEYWORD2
sendIRByte KEYWORD2
bitReverse KEYWORD2
space KEYWORD2
mark KEYWORD2
send KEYWORD2
model KEYWORD2
info KEYWORD2
sendPanasonicCKPCancelTimer KEYWORD2
@@ -0,0 +1,208 @@
// HeatpumpIR libraries, https://github.com/ToniA/arduino-heatpumpir
#include <FujitsuHeatpumpIR.h>
#include <PanasonicCKPHeatpumpIR.h>
#include <PanasonicHeatpumpIR.h>
#include <CarrierHeatpumpIR.h>
#include <MideaHeatpumpIR.h>
#include <MitsubishiHeatpumpIR.h>
#include <SamsungHeatpumpIR.h>
// Timer library, https://github.com/JChristensen/Timer
#include <Timer.h>
// MySensors libraries
#include <MySensor.h>
#include <SPI.h>
// Child ID's of this node
#define CHILD_1 1
// MySensors definitions
MySensor gw;
MyMessage irMsg(CHILD_1, V_IR_RECEIVE);
// -- Hacks for Domoticz start
#define CHILD_2 2
#define CHILD_3 3
// Domoticz does not yet properly support the S_IR sensor type
MyMessage textMsg(CHILD_2, V_TEXT);
MyMessage sendMsg(CHILD_3, V_LIGHT);
// -- Hacks for Domoticz end
// Array with all supported heatpump models
HeatpumpIR *heatpumpIR[] = { new PanasonicCKPHeatpumpIR(), // 0, keep this if you don't remove the timer for cancelling Panasonic CKP messages
new PanasonicDKEHeatpumpIR(), // 1
new PanasonicJKEHeatpumpIR(), // 2
new PanasonicNKEHeatpumpIR(), // 3
new CarrierHeatpumpIR(), // 4
new MideaHeatpumpIR(), // 5
new FujitsuHeatpumpIR(), // 6
new MitsubishiFDHeatpumpIR(), // 7
new MitsubishiFEHeatpumpIR(), // 8
new SamsungHeatpumpIR() // 9
};
// IR led on PWM output-capable digital pin 3
IRSender irSender(3);
// Timer for sending Panasonic CKP series timer cancellation commands
Timer timer;
int8_t panasonicCKPTimer = 0;
// Number of supported models
byte models = 0;
void setup()
{
Serial.begin(115200);
delay(1000);
Serial.println(F("HeatpumpIR sensor starting up..."));
models = sizeof(heatpumpIR) / sizeof(HeatpumpIR*);
Serial.print(F("Number of supported models: ")); Serial.println(models);
// Auto-assign the node ID, do not forward messages
gw.begin(incomingMessage, AUTO, false);
// Send the sketch version information to the gateway and Controller
gw.sendSketchInfo("Heatpump Sensor", "1.0");
// Register a sensors to the MySensors Gateway
gw.present(CHILD_1, S_IR, "IR sender");
// -- Hacks for Domoticz start
gw.present(CHILD_2, S_INFO, "IR data");
gw.present(CHILD_3, S_LIGHT, "IR send");
// The TEXT sensor is not created in Domoticz before data is sent
gw.send(textMsg.setSensor(CHILD_2).set("00000000"));
// -- Hacks for Domoticz end
}
void loop()
{
gw.process();
timer.update();
}
// Handle incoming messages from the MySensors Gateway
void incomingMessage(const MyMessage &message) {
const char *irData;
// V_IR type message
if (message.type==V_IR_SEND) {
Serial.println(F("Received IR send command..."));
irData = message.getString();
Serial.print(F("Code: 0x"));
Serial.println(irData);
sendHeatpumpIRCommand(irData);
}
// -- Hacks for Domoticz start
else if (message.type==V_LIGHT) {
// When the button is pressed on Domoticz, request the value of the TEXT sensor
Serial.println(F("Requesting IR code from Domoticz..."));
gw.request(CHILD_2, V_TEXT, 0);
} else if (message.type==V_TEXT) {
// TEXT sensor value is received as a result of the previous step
Serial.println(F("IR code received from Domoticz..."));
Serial.print(F("Code: 0x"));
irData = message.getString();
Serial.println(irData);
sendHeatpumpIRCommand(irData);
// Set the Domoticz switch back to 'OFF' state
gw.send(sendMsg.setSensor(CHILD_3).set(0));
}
// -- Hacks for Domoticz end
}
// Decode the IR command and send the IR command to the heatpump
void sendHeatpumpIRCommand(const char *irCommandString) {
// irCommandString is an 8-digit hex digit
long irCommand = 0;
int sscanfStatus = sscanf(irCommandString, "%lx", &irCommand);
if (sscanfStatus == 1) {
Serial.print(F("IR code conversion OK: 0x"));
Serial.println(irCommand, HEX);
} else {
Serial.println(F("Failed to convert IR hex code to number"));
}
/*
The heatpump command is packed into a 32-bit hex number, see
libraries\HeatpumpIR\HeatpumpIR.h for the constants
12345678
3 MODEL
4 POWER
5 OPERATING MODE
6 FAN SPEED
78 TEMPERATURE IN HEX
00213416 (as an example of a valid code)
2 = PanasonicJKE
1 = Power ON
3 = COOL
4 = FAN 4
16 = Temperature 22 degrees (0x16 = 22)
*/
byte model = (irCommand & 0x00F00000) >> 20;
byte power = (irCommand & 0x00010000) >> 16;
byte mode = (irCommand & 0x0000F000) >> 12;
byte fan = (irCommand & 0x00000F00) >> 8;
byte temp = (irCommand & 0x000000FF);
const char* buf;
Serial.print(F("Model: "));
buf = heatpumpIR[model]->model();
// 'model' is a PROGMEM pointer, so need to write a byte at a time
while (char modelChar = pgm_read_byte(buf++))
{
Serial.print(modelChar);
}
Serial.println();
Serial.print(F("Model #: ")); Serial.println(model);
Serial.print(F("Power: ")); Serial.println(power);
Serial.print(F("Mode: ")); Serial.println(mode);
Serial.print(F("Fan: ")); Serial.println(fan);
Serial.print(F("Temp: ")); Serial.println(temp);
// Heatpump models start from 0, i.e. model number is always less than the number of different models
if (model < models) {
// This is a supported model
// Cancel the timer on Panasonic CKP heatpump
if (model == 0) {
Serial.println(F("Cancelling timer on Panasonic CKP heatpump..."));
timer.stop(panasonicCKPTimer);
}
Serial.println(F("All OK - sending IR command to heatpump..."));
heatpumpIR[model]->send(irSender, power, mode, fan, temp, VDIR_UP, HDIR_AUTO);
if (model == 0) {
Serial.println(F("Scheduling timer cancellation on Panasonic CKP heatpump..."));
panasonicCKPTimer = timer.after(120000, panasonicCancelTimer); // Called after 2 minutes
}
}
}
// Cancel the timer on the Panasonic CKP heatpump
void panasonicCancelTimer()
{
PanasonicCKPHeatpumpIR *panasonicCKPHeatpumpIR = new PanasonicCKPHeatpumpIR();
panasonicCKPHeatpumpIR->sendPanasonicCKPCancelTimer(irSender);
Serial.println(F("The TIMER led should now be OFF"));
}
@@ -0,0 +1,73 @@
# MySensors example
This example shows how HeatpumpIR could be integrated with the MySensors sensor network. As this sensor sends infrared signals,
the natural sensor type is 'S_IR'. The heatpump/air conditioner signals are really long, so the signals need to be encoded somehow.
In this example the idea is to encode just this information, using 24 bits of data
* model code (see the index of the array heatpumpIR in the code)
* power state (for the rest, see the HeatpumpIR.h file for the constants)
* operating mode
* fan speed
* temperature (the temperature is directly the temperature in HEX)
```
An example:
00213416 (as an example of a valid code)
2 = PanasonicJKE
1 = Power ON
3 = COOL
4 = FAN 4
16 = Temperature 22 degrees (0x16 = 22)
```
This sketch creates three sensors:
* S_IR
* S_INFO
* S_LIGHT
The S_INFO and S_LIGHT sensors are for Domoticz, as it does not currently (as of V2.3093) support infrared code sending through the S_IR sensor type.
## Sending the code
### MYSController
* Select the IR sensor on the 'Heatpump Sensor 1.0' node
* Select 'IR_SEND' as the subtype
* Enter for example '00213416' as the payload
### Domoticz
As Domoticz does not really support sending IR code yet, the workaround is to use two sensors. When the node is connected, Domoticz should find two new devices
* A 'Lighting 2 / AC' type sensor with name 'IR send'
* A 'General / Text' type sensor with name 'Unknown' and Data '00000000'
To send a command, first add the sensors. Then update the data of the text sensor to contain the IR code to be sent, and then flip the switch to trigger the sending.
The data can be updated through the JSON REST API, or through the Lua event scripts.
REST API example (assuming that the idx of the text sensor is 105 and the switch is 104, and Domoticz is at 192.168.0.4:8080):
```
http://192.168.0.4:8080/json.htm?type=command&param=udevice&idx=105&nvalue=0&svalue=00213416
http://192.168.0.4:8080/json.htm?type=command&param=switchlight&idx=104&switchcmd=On
```
Lua example:
* Seems that the text sensor can't be updated through the commandArray...
* So now when a device called 'trigger' changes, the text sensor value is first updated to '00213416', and then the IR signal is sent
```
commandArray = {}
for key, value in pairs(devicechanged) do
if (key == 'trigger') then
print("Heatpump script")
commandArray['OpenURL']='http://192.168.0.4:8080/json.htm?type=command&param=udevice&idx=105&nvalue=0&svalue=00213416'
commandArray['IR send']='On'
end
end
return commandArray
```
## Hardware
Use any ATMega328-based Arduino, connect the radio, and finally connect an infrared led (in series with a resistor, like 100 Ohm) between digital pin 3 and GND.
+65
View File
@@ -0,0 +1,65 @@
/*
* 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.
*/
/* * * * * * * * * * * * * * * * * * * * * * * * * * * *
Code by Simon Monk
http://www.simonmonk.org
* * * * * * * * * * * * * * * * * * * * * * * * * * * * */
// For Arduino 1.0 and earlier
#if defined(ARDUINO) && ARDUINO >= 100
#include "Arduino.h"
#else
#include "WProgram.h"
#endif
#include "Event.h"
Event::Event(void)
{
eventType = EVENT_NONE;
}
void Event::update(void)
{
unsigned long now = millis();
update(now);
}
void Event::update(unsigned long now)
{
if (now - lastEventTime >= period)
{
switch (eventType)
{
case EVENT_EVERY:
(*callback)();
break;
case EVENT_OSCILLATE:
pinState = ! pinState;
digitalWrite(pin, pinState);
break;
}
lastEventTime = now;
count++;
}
if (repeatCount > -1 && count >= repeatCount)
{
eventType = EVENT_NONE;
}
}
+49
View File
@@ -0,0 +1,49 @@
/*
* 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.
*/
/* * * * * * * * * * * * * * * * * * * * * * * * * * * *
Code by Simon Monk
http://www.simonmonk.org
* * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#ifndef Event_h
#define Event_h
#include <inttypes.h>
#define EVENT_NONE 0
#define EVENT_EVERY 1
#define EVENT_OSCILLATE 2
class Event
{
public:
Event(void);
void update(void);
void update(unsigned long now);
int8_t eventType;
unsigned long period;
int repeatCount;
uint8_t pin;
uint8_t pinState;
void (*callback)(void);
unsigned long lastEventTime;
int count;
};
#endif
+29
View File
@@ -0,0 +1,29 @@
1.0 by Simon Monk
Library as downloaded 02Feb2012 22:55 UTC from http://srmonk.blogspot.com/2012/01/arduino-timer-library.html
1.1 by Jack Christensen
Changed data types of variables and functions:
o event types and indexes changed from int to int8_t.
o periods and durations changed from lon to unsigned long.
o update() and stop() functions typed as void, since they return nothing.
o pin numbers and pin values changed from int to uint8_t, this agrees with digitalWrite().
o added return values to Timer::pulse() and Timer::oscillate(uint8_t, unsigned long, uint8_t).
o changed test in Event::update() to use subtraction to avoid rollover issues.
o Updated keywords.txt file to include all functions.
1.2 by Damian Philipp
o Added a range check to Timer::stop() to avoid memory corruption.
o Added constants to <Timer.h>:
- NO_TIMER_AVAILABLE: Signals that while an event was to be queued, no free timer could be found.
- TIMER_NOT_AN_EVENT: Can be used to flag a variable that *might* contain a timer ID as
*not* containing a timer ID
o Replaced a bunch of magic numbers in <Timer.cpp> by the above constants
o Added several comments
o Added Timer::pulseImmediate(). pulseImmediate sets the pin to the specified value for the given
duration. After the duration, the pin is set to !value.
1.3 by Jack Christensen
o Added "blink2" example illustrating flashing two LEDs at different rates.
o 19Oct2013: This is the last v1.x release. It will continue to be available on GitHub
as a branch named v1.3. Future development will continue with Sandy Walsh's v2.0 which
can pass context (timer ID, etc.) to the callback functions.
+138
View File
@@ -0,0 +1,138 @@
/*
* 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.
*/
/* * * * * * * * * * * * * * * * * * * * * * * * * * * *
Code by Simon Monk
http://www.simonmonk.org
* * * * * * * * * * * * * * * * * * * * * * * * * * * * */
// For Arduino 1.0 and earlier
#if defined(ARDUINO) && ARDUINO >= 100
#include "Arduino.h"
#else
#include "WProgram.h"
#endif
#include "Timer.h"
Timer::Timer(void)
{
}
int8_t Timer::every(unsigned long period, void (*callback)(), int repeatCount)
{
int8_t i = findFreeEventIndex();
if (i == -1) return -1;
_events[i].eventType = EVENT_EVERY;
_events[i].period = period;
_events[i].repeatCount = repeatCount;
_events[i].callback = callback;
_events[i].lastEventTime = millis();
_events[i].count = 0;
return i;
}
int8_t Timer::every(unsigned long period, void (*callback)())
{
return every(period, callback, -1); // - means forever
}
int8_t Timer::after(unsigned long period, void (*callback)())
{
return every(period, callback, 1);
}
int8_t Timer::oscillate(uint8_t pin, unsigned long period, uint8_t startingValue, int repeatCount)
{
int8_t i = findFreeEventIndex();
if (i == NO_TIMER_AVAILABLE) return NO_TIMER_AVAILABLE;
_events[i].eventType = EVENT_OSCILLATE;
_events[i].pin = pin;
_events[i].period = period;
_events[i].pinState = startingValue;
digitalWrite(pin, startingValue);
_events[i].repeatCount = repeatCount * 2; // full cycles not transitions
_events[i].lastEventTime = millis();
_events[i].count = 0;
return i;
}
int8_t Timer::oscillate(uint8_t pin, unsigned long period, uint8_t startingValue)
{
return oscillate(pin, period, startingValue, -1); // forever
}
/**
* This method will generate a pulse of !startingValue, occuring period after the
* call of this method and lasting for period. The Pin will be left in !startingValue.
*/
int8_t Timer::pulse(uint8_t pin, unsigned long period, uint8_t startingValue)
{
return oscillate(pin, period, startingValue, 1); // once
}
/**
* This method will generate a pulse of startingValue, starting immediately and of
* length period. The pin will be left in the !startingValue state
*/
int8_t Timer::pulseImmediate(uint8_t pin, unsigned long period, uint8_t pulseValue)
{
int8_t id(oscillate(pin, period, pulseValue, 1));
// now fix the repeat count
if (id >= 0 && id < MAX_NUMBER_OF_EVENTS) {
_events[id].repeatCount = 1;
}
return id;
}
void Timer::stop(int8_t id)
{
if (id >= 0 && id < MAX_NUMBER_OF_EVENTS) {
_events[id].eventType = EVENT_NONE;
}
}
void Timer::update(void)
{
unsigned long now = millis();
update(now);
}
void Timer::update(unsigned long now)
{
for (int8_t i = 0; i < MAX_NUMBER_OF_EVENTS; i++)
{
if (_events[i].eventType != EVENT_NONE)
{
_events[i].update(now);
}
}
}
int8_t Timer::findFreeEventIndex(void)
{
for (int8_t i = 0; i < MAX_NUMBER_OF_EVENTS; i++)
{
if (_events[i].eventType == EVENT_NONE)
{
return i;
}
}
return NO_TIMER_AVAILABLE;
}
+67
View File
@@ -0,0 +1,67 @@
/*
* 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.
*/
/* * * * * * * * * * * * * * * * * * * * * * * * * * * *
Code by Simon Monk
http://www.simonmonk.org
* * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#ifndef Timer_h
#define Timer_h
#include <inttypes.h>
#include "Event.h"
#define MAX_NUMBER_OF_EVENTS (10)
#define TIMER_NOT_AN_EVENT (-2)
#define NO_TIMER_AVAILABLE (-1)
class Timer
{
public:
Timer(void);
int8_t every(unsigned long period, void (*callback)(void));
int8_t every(unsigned long period, void (*callback)(void), int repeatCount);
int8_t after(unsigned long duration, void (*callback)(void));
int8_t oscillate(uint8_t pin, unsigned long period, uint8_t startingValue);
int8_t oscillate(uint8_t pin, unsigned long period, uint8_t startingValue, int repeatCount);
/**
* This method will generate a pulse of !startingValue, occuring period after the
* call of this method and lasting for period. The Pin will be left in !startingValue.
*/
int8_t pulse(uint8_t pin, unsigned long period, uint8_t startingValue);
/**
* This method will generate a pulse of pulseValue, starting immediately and of
* length period. The pin will be left in the !pulseValue state
*/
int8_t pulseImmediate(uint8_t pin, unsigned long period, uint8_t pulseValue);
void stop(int8_t id);
void update(void);
void update(unsigned long now);
protected:
Event _events[MAX_NUMBER_OF_EVENTS];
int8_t findFreeEventIndex(void);
};
#endif
@@ -0,0 +1,29 @@
//Flash two LEDs at different rates using Simon Monk's Timer library
//http://www.doctormonk.com/2012/01/arduino-timer-library.html
//
//Jack Christensen 30Sep2013
//
//Beerware license: Free for any and all purposes, but if you find it
//useful AND we actually meet someday, you can buy me a beer!
#include "Timer.h" //http://github.com/JChristensen/Timer
const int LED1 = 8; //connect one LED to this pin (with appropriate current-limiting resistor of course)
const int LED2 = 9; //connect another LED to this pin (don't forget the resistor)
const unsigned long PERIOD1 = 1000; //one second
const unsigned long PERIOD2 = 10000; //ten seconds
Timer t; //instantiate the timer object
void setup(void)
{
pinMode(LED1, OUTPUT);
pinMode(LED2, OUTPUT);
t.oscillate(LED1, PERIOD1, HIGH);
t.oscillate(LED2, PERIOD2, HIGH);
}
void loop(void)
{
t.update();
}
@@ -0,0 +1,42 @@
#include "Timer.h"
Timer t;
int ledEvent;
void setup()
{
Serial.begin(9600);
int tickEvent = t.every(2000, doSomething);
Serial.print("2 second tick started id=");
Serial.println(tickEvent);
pinMode(13, OUTPUT);
ledEvent = t.oscillate(13, 50, HIGH);
Serial.print("LED event started id=");
Serial.println(ledEvent);
int afterEvent = t.after(10000, doAfter);
Serial.print("After event started id=");
Serial.println(afterEvent);
}
void loop()
{
t.update();
}
void doSomething()
{
Serial.print("2 second tick: millis()=");
Serial.println(millis());
}
void doAfter()
{
Serial.println("stop the led event");
t.stop(ledEvent);
t.oscillate(13, 500, HIGH, 5);
}
@@ -0,0 +1,17 @@
#include "Timer.h"
Timer t;
int pin = 13;
void setup()
{
pinMode(pin, OUTPUT);
t.pulse(pin, 10 * 1000, HIGH); // 10 seconds
// t.pulse(pin, 10 * 60 * 1000, HIGH); // 10 minutes
}
void loop()
{
t.update();
}
@@ -0,0 +1,22 @@
#include "Timer.h"
Timer t;
int pin = 13;
void setup()
{
Serial.begin(9600);
pinMode(pin, OUTPUT);
t.oscillate(pin, 100, LOW);
t.every(1000, takeReading);
}
void loop()
{
t.update();
}
void takeReading()
{
Serial.println(analogRead(0));
}
+31
View File
@@ -0,0 +1,31 @@
#######################################
# Syntax Coloring Map For Timer Library
#######################################
#######################################
# Datatypes (KEYWORD1)
#######################################
Timer KEYWORD1
Event KEYWORD1
#######################################
# Methods and Functions (KEYWORD2)
#######################################
every KEYWORD2
after KEYWORD2
oscillate KEYWORD2
pulse KEYWORD2
pulseImmediate KEYWORD2
stop KEYWORD2
update KEYWORD2
findFreeEventIndex KEYWORD2
#######################################
# Instances (KEYWORD2)
#######################################
#######################################
# Constants (LITERAL1)
#######################################