// Copyright 2019 crankyoldgit /// @file /// @brief Support for Sharp protocols. /// @see http://www.sbprojects.com/knowledge/ir/sharp.htm /// @see http://lirc.sourceforge.net/remotes/sharp/GA538WJSA /// @see http://www.mwftr.com/ucF08/LEC14%20PIC%20IR.pdf /// @see http://www.hifi-remote.com/johnsfine/DecodeIR.html#Sharp /// @see GlobalCache's IR Control Tower data. /// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/638 /// @see https://github.com/ToniA/arduino-heatpumpir/blob/master/SharpHeatpumpIR.cpp // Supports: // Brand: Sharp, Model: LC-52D62U TV // Brand: Sharp, Model: AY-ZP40KR A/C (A907) // Brand: Sharp, Model: AH-AxSAY A/C (A907) // Brand: Sharp, Model: CRMC-A907 JBEZ remote (A907) // Brand: Sharp, Model: AH-XP10NRY A/C (A907) // Brand: Sharp, Model: CRMC-820 JBEZ remote (A907) // Brand: Sharp, Model: CRMC-A705 JBEZ remote (A705) #ifndef IR_SHARP_H_ #define IR_SHARP_H_ #ifndef UNIT_TEST #include #endif #include "IRrecv.h" #include "IRremoteESP8266.h" #include "IRsend.h" #ifdef UNIT_TEST #include "IRsend_test.h" #endif #include "IRutils.h" // Constants const uint16_t kSharpAcHdrMark = 3800; const uint16_t kSharpAcHdrSpace = 1900; const uint16_t kSharpAcBitMark = 470; const uint16_t kSharpAcZeroSpace = 500; const uint16_t kSharpAcOneSpace = 1400; const uint32_t kSharpAcGap = kDefaultMessageGap; // Byte[4] const uint8_t kSharpAcModelBit = 4; // Mask 0b000x0000 const uint8_t kSharpAcByteTemp = 4; const uint8_t kSharpAcMinTemp = 15; // Celsius const uint8_t kSharpAcMaxTemp = 30; // Celsius // Byte[5] const uint8_t kSharpAcBytePowerSpecial = 5; const uint8_t kSharpAcPowerSetSpecialOffset = kHighNibble; // 0bxxxx0000 const uint8_t kSharpAcPowerSpecialSize = kNibbleSize; // 0bxxxx0000 const uint8_t kSharpAcPowerUnknown = 0; // 0b0000 const uint8_t kSharpAcPowerOnFromOff = 1; // 0b0001 const uint8_t kSharpAcPowerOff = 2; // 0b0010 const uint8_t kSharpAcPowerOn = 3; // 0b0011 (Normal) const uint8_t kSharpAcPowerSetSpecialOn = 6; // 0b0110 const uint8_t kSharpAcPowerSetSpecialOff = 7; // 0b0111 const uint8_t kSharpAcPowerTimerSetting = 8; // 0b1000 // Byte[6] const uint8_t kSharpAcByteMode = 6; const uint8_t kSharpAcModeSize = 2; // Mask 0b000000xx; const uint8_t kSharpAcAuto = 0b00; // A907 only const uint8_t kSharpAcFan = 0b00; // A705 only const uint8_t kSharpAcDry = 0b11; const uint8_t kSharpAcCool = 0b10; const uint8_t kSharpAcHeat = 0b01; // A907 only const uint8_t kSharpAcByteClean = kSharpAcByteMode; const uint8_t kSharpAcBitCleanOffset = 3; // Mask 0b0000x000 const uint8_t kSharpAcByteFan = kSharpAcByteMode; const uint8_t kSharpAcFanOffset = 4; // Mask 0b0xxx0000 const uint8_t kSharpAcFanSize = 3; // Nr. of Bits const uint8_t kSharpAcFanAuto = 0b010; // 2 const uint8_t kSharpAcFanMin = 0b100; // 4 (FAN1) const uint8_t kSharpAcFanMed = 0b011; // 3 (FAN2) const uint8_t kSharpAcFanA705Low = 0b011; // 3 const uint8_t kSharpAcFanHigh = 0b101; // 5 (FAN3) const uint8_t kSharpAcFanA705Med = 0b101; // 5 const uint8_t kSharpAcFanMax = 0b111; // 7 (FAN4) // Byte[7] const uint8_t kSharpAcByteTimer = 7; const uint8_t kSharpAcTimerIncrement = 30; // Mins const uint8_t kSharpAcTimerHoursOffset = kLowNibble; const uint8_t kSharpAcTimerHoursSize = kNibbleSize; // Mask 0b0000xxxx const uint8_t kSharpAcTimerHoursOff = 0b0000; const uint8_t kSharpAcTimerHoursMax = 0b1100; // 12 const uint8_t kSharpAcBitTimerType = 6; // Mask 0b0x000000 const uint8_t kSharpAcOffTimerType = 0b0; const uint8_t kSharpAcOnTimerType = 0b1; const uint8_t kSharpAcBitTimerEnabled = 7; // Mask 0bx0000000 // Byte[8] const uint8_t kSharpAcByteSwing = 8; const uint8_t kSharpAcSwingOffset = 0; const uint8_t kSharpAcSwingSize = 3; // Mask 0b00000xxx const uint8_t kSharpAcSwingToggle = 0b111; const uint8_t kSharpAcSwingNoToggle = 0b000; // Byte[10] const uint8_t kSharpAcByteSpecial = 10; // Mask 0bxxxxxxxx const uint8_t kSharpAcSpecialPower = 0x00; const uint8_t kSharpAcSpecialTurbo = 0x01; const uint8_t kSharpAcSpecialTempEcono = 0x04; const uint8_t kSharpAcSpecialFan = 0x05; const uint8_t kSharpAcSpecialSwing = 0x06; const uint8_t kSharpAcSpecialTimer = 0xC0; const uint8_t kSharpAcSpecialTimerHalfHour = 0xDE; // Byte[11] const uint8_t kSharpAcByteIon = 11; const uint8_t kSharpAcBitIonOffset = 2; // Mask 0b00000x00 // Byte[12] (Checksum) // Classes /// Class for handling detailed Sharp A/C messages. class IRSharpAc { public: explicit IRSharpAc(const uint16_t pin, const bool inverted = false, const bool use_modulation = true); #if SEND_SHARP_AC void send(const uint16_t repeat = kSharpAcDefaultRepeat); /// Run the calibration to calculate uSec timing offsets for this platform. /// @return The uSec timing offset needed per modulation of the IR Led. /// @note This will produce a 65ms IR signal pulse at 38kHz. /// Only ever needs to be run once per object instantiation, if at all. int8_t calibrate(void) { return _irsend.calibrate(); } #endif // SEND_SHARP_AC void begin(void); void setModel(const sharp_ac_remote_model_t model); sharp_ac_remote_model_t getModel(const bool raw = false); void on(void); void off(void); void setPower(const bool on, const bool prev_on = true); bool getPower(void); bool isPowerSpecial(void); void setTemp(const uint8_t temp, const bool save = true); uint8_t getTemp(void); void setFan(const uint8_t fan, const bool save = true); uint8_t getFan(void); void setMode(const uint8_t mode, const bool save = true); uint8_t getMode(void); void setSpecial(const uint8_t mode); uint8_t getSpecial(void); bool getTurbo(void); void setTurbo(const bool on); bool getSwingToggle(void); void setSwingToggle(const bool on); bool getIon(void); void setIon(const bool on); bool getEconoToggle(void); void setEconoToggle(const bool on); bool getLightToggle(void); void setLightToggle(const bool on); uint16_t getTimerTime(void); bool getTimerEnabled(void); bool getTimerType(void); void setTimer(bool enable, bool timer_type, uint16_t mins); bool getClean(void); void setClean(const bool on); uint8_t* getRaw(void); void setRaw(const uint8_t new_code[], const uint16_t length = kSharpAcStateLength); static bool validChecksum(uint8_t state[], const uint16_t length = kSharpAcStateLength); static uint8_t convertMode(const stdAc::opmode_t mode); static uint8_t convertFan(const stdAc::fanspeed_t speed); stdAc::opmode_t toCommonMode(const uint8_t mode); stdAc::fanspeed_t toCommonFanSpeed(const uint8_t speed); stdAc::state_t toCommon(void); String toString(void); #ifndef UNIT_TEST private: IRsend _irsend; ///< Instance of the IR send class #else // UNIT_TEST /// @cond IGNORE IRsendTest _irsend; ///< Instance of the testing IR send class /// @endcond #endif // UNIT_TEST uint8_t remote[kSharpAcStateLength]; ///< State of the remote in IR code form uint8_t _temp; ///< Saved copy of the desired temp. uint8_t _mode; ///< Saved copy of the desired mode. uint8_t _fan; ///< Saved copy of the desired fan speed. sharp_ac_remote_model_t _model; ///< Saved copy of the model. void stateReset(void); void checksum(void); static uint8_t calcChecksum(uint8_t state[], const uint16_t length = kSharpAcStateLength); void setPowerSpecial(const uint8_t value); uint8_t getPowerSpecial(void); void clearPowerSpecial(void); bool _getEconoToggle(void); void _setEconoToggle(const bool on); }; #endif // IR_SHARP_H_