323 lines
9.8 KiB
C++
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
|