Arduino-IRremote/src/ac_LG.hpp

323 lines
9.8 KiB
C++

/*
* ac_LG.hpp
*
* Contains functions for sending LG air conditioner IR Protocol
* There is no state plausibility check, e.g. you can send fan speed in Mode D and change temperature in mode F
*
* This file is part of Arduino-IRremote https://github.com/Arduino-IRremote/Arduino-IRremote.
*
************************************************************************************
* MIT License
*
* Copyright (c) 2021-2022 Armin Joachimsmeyer
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is furnished
* to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
* PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
* CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
* OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
************************************************************************************
*/
#ifndef _AC_LG_HPP
#define _AC_LG_HPP
#include <Arduino.h>
#if defined(INFO) && !defined(LOCAL_INFO)
#define LOCAL_INFO
#else
//#define LOCAL_INFO // This enables info output only for this file
#endif
//#define DEBUG // for more output from the LG-AC driver.
#include "IRremoteInt.h"
#include "ac_LG.h" // useful constants
#include "LongUnion.h"
/** \addtogroup Airconditoners Air conditioner special code
* @{
*/
/*
* LG remote measurements: Type AKB73315611, Ver1.1 from 2011.03.01
* Internal crystal: 4 MHz
* Header: 8.9 ms mark 4.15 ms space
* Data: 500 / 540 and 500 / 1580;
* Clock is nor synchronized with gate so you have 19 and sometimes 19 and a spike pulses for mark
* Duty: 9 us on 17 us off => around 33 % duty
* NO REPEAT: If value like temperature has changed during long press, the last value is send at button release
* If you do a double press -tested with the fan button-, the next value can be sent after 118 ms
*/
#define SIZE_OF_FAN_SPEED_MAPPING_TABLE 4
const int AC_FAN_TOWER[SIZE_OF_FAN_SPEED_MAPPING_TABLE] = { 0, 4, 6, 6 }; // last dummy entry to avoid out of bounds access
const int AC_FAN_WALL[SIZE_OF_FAN_SPEED_MAPPING_TABLE] = { 0, 2, 4, 5 }; // 0 -> low, 4 high, 5 -> cycle
void Aircondition_LG::setType(bool aIsWallType) {
ACIsWallType = aIsWallType;
#if defined(LOCAL_INFO)
Serial.print(F("Set wall type to "));
Serial.println(aIsWallType);
#endif
}
void Aircondition_LG::printMenu(Print *aSerial) {
aSerial->println();
aSerial->println();
aSerial->println(F("Type command and optional parameter without a separator"));
aSerial->println(F("0 Off"));
aSerial->println(F("1 On"));
aSerial->println(F("s Swing <0 or 1>"));
aSerial->println(F("a Auto clean <0 or 1>"));
aSerial->println(F("j Jet on"));
aSerial->println(F("e Energy saving <0 or 1>"));
aSerial->println(F("l Lights toggle"));
aSerial->println(F("f Fan speed <0 to 2 or 3 for cycle>"));
aSerial->println(F("t Temperature <18 to 30> degree"));
aSerial->println(F("+ Temperature + 1"));
aSerial->println(F("- Temperature - 1"));
aSerial->println(F("m <c(ool) or a(uto) or d(ehumidifying) or h(eating) or f(an) mode>"));
aSerial->println(F("S Sleep after <0 to 420> minutes"));
aSerial->println(F("T Timer on after <0 to 1439> minutes"));
aSerial->println(F("O Timer off after <0 to 1439> minutes"));
aSerial->println(F("C Clear all timer and sleep"));
aSerial->println(F("e.g. \"s1\" or \"t23\" or \"mc\" or \"O60\" or \"+\""));
aSerial->println(F("No plausibility check is made!"));
aSerial->println();
}
/*
* Send repeat
* Repeat commands should be sent in a 110 ms raster.
* @param aCommand one of LG_COMMAND_OFF, LG_COMMAND_ON etc.
*/
bool Aircondition_LG::sendCommandAndParameter(char aCommand, int aParameter) {
// Commands without parameter
switch (aCommand) {
case LG_COMMAND_OFF: // off
sendIRCommand(LG_POWER_DOWN);
PowerIsOn = false;
return true;
case LG_COMMAND_ON: // on
PowerIsOn = false; // set to false in order to suppress on bit
sendTemperatureFanSpeedAndMode();
return true;
case LG_COMMAND_JET:
IR_DEBUG_PRINTLN(F("Send jet on"));
sendIRCommand(LG_JET_ON);
return true;
case LG_COMMAND_LIGHT:
sendIRCommand(LG_LIGHT);
return true;
case LG_COMMAND_CLEAR_ALL:
sendIRCommand(LG_CLEAR_ALL);
return true;
case LG_COMMAND_TEMPERATURE_PLUS:
if (18 <= Temperature && Temperature <= 29) {
Temperature++;
sendTemperatureFanSpeedAndMode();
} else {
return false;
}
return true;
case LG_COMMAND_TEMPERATURE_MINUS:
if (19 <= Temperature && Temperature <= 30) {
Temperature--;
sendTemperatureFanSpeedAndMode();
} else {
return false;
}
return true;
}
PowerIsOn = true;
/*
* Now the commands which require a parameter
*/
if (aParameter < 0) {
IR_DEBUG_PRINT(F("Error: Parameter is less than 0"));
return false;
}
switch (aCommand) {
case LG_COMMAND_MODE:
Mode = aParameter + '0';
sendTemperatureFanSpeedAndMode();
break;
case LG_COMMAND_SWING:
IR_DEBUG_PRINT(F("Send air swing="));
IR_DEBUG_PRINTLN(aParameter);
if (ACIsWallType) {
if (aParameter) {
sendIRCommand(LG_WALL_SWING_ON);
} else {
sendIRCommand(LG_WALL_SWING_OFF);
}
} else {
if (aParameter) {
sendIRCommand(LG_SWING_ON);
} else {
sendIRCommand(LG_SWING_OFF);
}
}
break;
case LG_COMMAND_AUTO_CLEAN:
IR_DEBUG_PRINT(F("Send auto clean="));
IR_DEBUG_PRINTLN(aParameter);
if (aParameter) {
sendIRCommand(LG_AUTO_CLEAN_ON);
} else {
sendIRCommand(LG_AUTO_CLEAN_OFF);
}
break;
case LG_COMMAND_ENERGY:
IR_DEBUG_PRINT(F("Send energy saving on="));
IR_DEBUG_PRINTLN(aParameter);
if (aParameter) {
sendIRCommand(LG_ENERGY_SAVING_ON);
} else {
sendIRCommand(LG_ENERGY_SAVING_OFF);
}
break;
case LG_COMMAND_FAN_SPEED:
if (aParameter < SIZE_OF_FAN_SPEED_MAPPING_TABLE) {
FanIntensity = aParameter;
sendTemperatureFanSpeedAndMode();
} else {
return false;
}
break;
case LG_COMMAND_TEMPERATURE:
if (18 <= aParameter && aParameter <= 30) {
Temperature = aParameter;
sendTemperatureFanSpeedAndMode();
} else {
return false;
}
break;
case LG_COMMAND_SLEEP:
// 420 = maximum I have recorded
if (aParameter <= 420) {
sendIRCommand(LG_SLEEP + aParameter);
} else {
return false;
}
break;
case LG_COMMAND_TIMER_ON:
// 1440 = minutes of a day
if (aParameter <= 1439) {
sendIRCommand(LG_TIMER_ON + aParameter);
} else {
return false;
}
break;
case LG_COMMAND_TIMER_OFF:
if (aParameter <= 1439) {
sendIRCommand(LG_TIMER_OFF + aParameter);
} else {
return false;
}
break;
default:
return false;
}
return true;
}
void Aircondition_LG::sendIRCommand(uint16_t aCommand) {
#if defined(LOCAL_INFO)
Serial.print(F("Send code=0x"));
Serial.print(aCommand, HEX);
Serial.print(F(" | 0b"));
Serial.println(aCommand, BIN);
#endif
IrSender.sendLG2((uint8_t) LG_ADDRESS, aCommand, 0);
}
/*
* Takes values from static variables
*/
void Aircondition_LG::sendTemperatureFanSpeedAndMode() {
uint8_t tTemperature = Temperature;
#if defined(LOCAL_INFO)
Serial.print(F("Send temperature="));
Serial.print(tTemperature);
Serial.print(F(" fan intensity="));
Serial.print(FanIntensity);
Serial.print(F(" mode="));
Serial.println((char )Mode);
#endif
WordUnion tIRCommand;
tIRCommand.UWord = 0;
// Temperature is coded in the upper nibble of the LowByte
tIRCommand.UByte.LowByte = ((tTemperature - 15) << 4); // 16 -> 0x00, 18 -> 0x30, 30 -> 0xF0
// Fan intensity is coded in the lower nibble of the LowByte
if (ACIsWallType) {
tIRCommand.UByte.LowByte |= AC_FAN_WALL[FanIntensity];
} else {
tIRCommand.UByte.LowByte |= AC_FAN_TOWER[FanIntensity];
}
switch (Mode) {
case AC_MODE_COOLING:
tIRCommand.UByte.HighByte = LG_MODE_COOLING >> 8;
break;
case AC_MODE_HEATING:
tIRCommand.UByte.HighByte = LG_MODE_HEATING >> 8;
break;
case AC_MODE_AUTO:
tIRCommand.UByte.HighByte = LG_MODE_AUTO >> 8;
break;
case AC_MODE_FAN:
tTemperature = 18;
tIRCommand.UByte.HighByte = LG_MODE_FAN >> 8;
break;
case AC_MODE_DEHUMIDIFIYING:
tIRCommand.UWord = LG_MODE_DEHUMIDIFIYING;
break;
default:
break;
}
if (!PowerIsOn) {
// switch on requires masked bit
tIRCommand.UByte.HighByte &= ~(LG_SWITCH_ON_MASK >> 8);
}
PowerIsOn = true;
sendIRCommand(tIRCommand.UWord);
}
/** @}*/
#endif // _AC_LG_HPP