v0.9.337b
This commit is contained in:
@@ -230,6 +230,11 @@ Work is in progress...
|
||||
|
||||
---
|
||||
## Version history
|
||||
#### v0.9.337b
|
||||
- added support for Arduino ESP32 v3.0.0 and later
|
||||
- disabled SD indexing on startup; now the card is indexed only if the `data/index.dat` file is missing from the card
|
||||
- IRremoteESP8266 library integrated into the project (`yoRadio/src/IRremoteESP8266`)
|
||||
|
||||
#### v0.9.313b
|
||||
- added support for ESP32-S3 boards (ESP32 S3 Dev Module) (esp32 cores version 3.x.x is not supported yet)
|
||||
- fixes in displaying sliders in the web interface
|
||||
|
||||
@@ -19,6 +19,9 @@
|
||||
*/
|
||||
#include "Arduino.h"
|
||||
#include "AsyncEventSource.h"
|
||||
#if ESP_ARDUINO_VERSION >= ESP_ARDUINO_VERSION_VAL(3, 0, 0)
|
||||
#include "rom/ets_sys.h"
|
||||
#endif
|
||||
|
||||
static String generateEventMessage(const char *message, const char *event, uint32_t id, uint32_t reconnect){
|
||||
String ev = "";
|
||||
|
||||
@@ -28,6 +28,10 @@
|
||||
#else
|
||||
#include <Hash.h>
|
||||
#endif
|
||||
//https://github.com/me-no-dev/ESPAsyncWebServer/issues/1410
|
||||
#if ESP_ARDUINO_VERSION >= ESP_ARDUINO_VERSION_VAL(3, 0, 0)
|
||||
#include "mbedtls/compat-2.x.h"
|
||||
#endif
|
||||
|
||||
#define MAX_PRINTF_LEN 64
|
||||
|
||||
|
||||
@@ -22,6 +22,9 @@
|
||||
#include <libb64/cencode.h>
|
||||
#ifdef ESP32
|
||||
#include "mbedtls/md5.h"
|
||||
#if ESP_ARDUINO_VERSION >= ESP_ARDUINO_VERSION_VAL(3, 0, 0)
|
||||
#include "mbedtls/compat-2.x.h"
|
||||
#endif
|
||||
#else
|
||||
#include "md5.h"
|
||||
#endif
|
||||
|
||||
1
yoRadio/src/IRremoteESP8266/CPPLINT.cfg
Normal file
1
yoRadio/src/IRremoteESP8266/CPPLINT.cfg
Normal file
@@ -0,0 +1 @@
|
||||
filter=-build/include,+build/include_alpha,+build/include_order,+build/include_what_you_use
|
||||
5063
yoRadio/src/IRremoteESP8266/IRac.cpp
Normal file
5063
yoRadio/src/IRremoteESP8266/IRac.cpp
Normal file
File diff suppressed because it is too large
Load Diff
581
yoRadio/src/IRremoteESP8266/IRac.h
Normal file
581
yoRadio/src/IRremoteESP8266/IRac.h
Normal file
@@ -0,0 +1,581 @@
|
||||
#ifndef IRAC_H_
|
||||
#define IRAC_H_
|
||||
|
||||
// Copyright 2019 David Conran
|
||||
|
||||
#ifndef UNIT_TEST
|
||||
#include <Arduino.h>
|
||||
#else
|
||||
#include <memory>
|
||||
#endif
|
||||
#include "IRremoteESP8266.h"
|
||||
#include "ir_Airton.h"
|
||||
#include "ir_Airwell.h"
|
||||
#include "ir_Amcor.h"
|
||||
#include "ir_Argo.h"
|
||||
#include "ir_Bosch.h"
|
||||
#include "ir_Carrier.h"
|
||||
#include "ir_Coolix.h"
|
||||
#include "ir_Corona.h"
|
||||
#include "ir_Daikin.h"
|
||||
#include "ir_Delonghi.h"
|
||||
#include "ir_Fujitsu.h"
|
||||
#include "ir_Ecoclim.h"
|
||||
#include "ir_Electra.h"
|
||||
#include "ir_Goodweather.h"
|
||||
#include "ir_Gree.h"
|
||||
#include "ir_Haier.h"
|
||||
#include "ir_Hitachi.h"
|
||||
#include "ir_Kelon.h"
|
||||
#include "ir_Kelvinator.h"
|
||||
#include "ir_LG.h"
|
||||
#include "ir_Midea.h"
|
||||
#include "ir_Mirage.h"
|
||||
#include "ir_Mitsubishi.h"
|
||||
#include "ir_MitsubishiHeavy.h"
|
||||
#include "ir_Neoclima.h"
|
||||
#include "ir_Panasonic.h"
|
||||
#include "ir_Rhoss.h"
|
||||
#include "ir_Samsung.h"
|
||||
#include "ir_Sanyo.h"
|
||||
#include "ir_Sharp.h"
|
||||
#include "ir_Tcl.h"
|
||||
#include "ir_Technibel.h"
|
||||
#include "ir_Teco.h"
|
||||
#include "ir_Toshiba.h"
|
||||
#include "ir_Transcold.h"
|
||||
#include "ir_Trotec.h"
|
||||
#include "ir_Truma.h"
|
||||
#include "ir_Vestel.h"
|
||||
#include "ir_Voltas.h"
|
||||
#include "ir_Whirlpool.h"
|
||||
#include "ir_York.h"
|
||||
|
||||
// Constants
|
||||
const int8_t kGpioUnused = -1; ///< A placeholder for not using an actual GPIO.
|
||||
|
||||
// Class
|
||||
/// A universal/common/generic interface for controling supported A/Cs.
|
||||
class IRac {
|
||||
public:
|
||||
explicit IRac(const uint16_t pin, const bool inverted = false,
|
||||
const bool use_modulation = true);
|
||||
static bool isProtocolSupported(const decode_type_t protocol);
|
||||
static void initState(stdAc::state_t *state,
|
||||
const decode_type_t vendor, const int16_t model,
|
||||
const bool power, const stdAc::opmode_t mode,
|
||||
const float degrees, const bool celsius,
|
||||
const stdAc::fanspeed_t fan,
|
||||
const stdAc::swingv_t swingv,
|
||||
const stdAc::swingh_t swingh,
|
||||
const bool quiet, const bool turbo, const bool econo,
|
||||
const bool light, const bool filter, const bool clean,
|
||||
const bool beep, const int16_t sleep,
|
||||
const int16_t clock);
|
||||
static void initState(stdAc::state_t *state);
|
||||
void markAsSent(void);
|
||||
bool sendAc(void);
|
||||
bool sendAc(const stdAc::state_t desired, const stdAc::state_t *prev = NULL);
|
||||
bool sendAc(const decode_type_t vendor, const int16_t model,
|
||||
const bool power, const stdAc::opmode_t mode, const float degrees,
|
||||
const bool celsius, const stdAc::fanspeed_t fan,
|
||||
const stdAc::swingv_t swingv, const stdAc::swingh_t swingh,
|
||||
const bool quiet, const bool turbo, const bool econo,
|
||||
const bool light, const bool filter, const bool clean,
|
||||
const bool beep, const int16_t sleep = -1,
|
||||
const int16_t clock = -1);
|
||||
static bool cmpStates(const stdAc::state_t a, const stdAc::state_t b);
|
||||
static bool strToBool(const char *str, const bool def = false);
|
||||
static int16_t strToModel(const char *str, const int16_t def = -1);
|
||||
static stdAc::ac_command_t strToCommandType(const char *str,
|
||||
const stdAc::ac_command_t def = stdAc::ac_command_t::kControlCommand);
|
||||
static stdAc::opmode_t strToOpmode(
|
||||
const char *str, const stdAc::opmode_t def = stdAc::opmode_t::kAuto);
|
||||
static stdAc::fanspeed_t strToFanspeed(
|
||||
const char *str,
|
||||
const stdAc::fanspeed_t def = stdAc::fanspeed_t::kAuto);
|
||||
static stdAc::swingv_t strToSwingV(
|
||||
const char *str, const stdAc::swingv_t def = stdAc::swingv_t::kOff);
|
||||
static stdAc::swingh_t strToSwingH(
|
||||
const char *str, const stdAc::swingh_t def = stdAc::swingh_t::kOff);
|
||||
static String boolToString(const bool value);
|
||||
static String commandTypeToString(const stdAc::ac_command_t cmdType);
|
||||
static String opmodeToString(const stdAc::opmode_t mode,
|
||||
const bool ha = false);
|
||||
static String fanspeedToString(const stdAc::fanspeed_t speed);
|
||||
static String swingvToString(const stdAc::swingv_t swingv);
|
||||
static String swinghToString(const stdAc::swingh_t swingh);
|
||||
stdAc::state_t getState(void);
|
||||
stdAc::state_t getStatePrev(void);
|
||||
bool hasStateChanged(void);
|
||||
stdAc::state_t next; ///< The state we want the device to be in after we send
|
||||
#ifdef UNIT_TEST
|
||||
/// @cond IGNORE
|
||||
/// UT-specific
|
||||
/// See @c OUTPUT_DECODE_RESULTS_FOR_UT macro description in IRac.cpp
|
||||
std::shared_ptr<IRrecv> _utReceiver = nullptr;
|
||||
std::unique_ptr<decode_results> _lastDecodeResults = nullptr;
|
||||
/// @endcond
|
||||
#else
|
||||
|
||||
private:
|
||||
#endif // UNIT_TEST
|
||||
uint16_t _pin; ///< The GPIO to use to transmit messages from.
|
||||
bool _inverted; ///< IR LED is lit when GPIO is LOW (true) or HIGH (false)?
|
||||
bool _modulation; ///< Is frequency modulation to be used?
|
||||
stdAc::state_t _prev; ///< The state we expect the device to currently be in.
|
||||
#if SEND_AIRTON
|
||||
void airton(IRAirtonAc *ac,
|
||||
const bool on, const stdAc::opmode_t mode,
|
||||
const float degrees, const stdAc::fanspeed_t fan,
|
||||
const stdAc::swingv_t swingv, const bool turbo,
|
||||
const bool light, const bool econo, const bool filter,
|
||||
const int16_t sleep = -1);
|
||||
#endif // SEND_AIRTON
|
||||
#if SEND_AIRWELL
|
||||
void airwell(IRAirwellAc *ac,
|
||||
const bool on, const stdAc::opmode_t mode, const float degrees,
|
||||
const stdAc::fanspeed_t fan);
|
||||
#endif // SEND_AIRWELL
|
||||
#if SEND_AMCOR
|
||||
void amcor(IRAmcorAc *ac,
|
||||
const bool on, const stdAc::opmode_t mode, const float degrees,
|
||||
const stdAc::fanspeed_t fan);
|
||||
#endif // SEND_AMCOR
|
||||
#if SEND_ARGO
|
||||
void argo(IRArgoAC *ac,
|
||||
const bool on, const stdAc::opmode_t mode, const float degrees,
|
||||
const float sensorTemp, const stdAc::fanspeed_t fan,
|
||||
const stdAc::swingv_t swingv, const bool iFeel, const bool turbo,
|
||||
const int16_t sleep = -1);
|
||||
void argoWrem3_ACCommand(IRArgoAC_WREM3 *ac,
|
||||
const bool on, const stdAc::opmode_t mode, const float degrees,
|
||||
const float sensorTemp, const stdAc::fanspeed_t fan,
|
||||
const stdAc::swingv_t swingv, const bool iFeel, const bool night,
|
||||
const bool econo, const bool turbo, const bool filter, const bool light);
|
||||
void argoWrem3_iFeelReport(IRArgoAC_WREM3 *ac, const float sensorTemp);
|
||||
void argoWrem3_ConfigSet(IRArgoAC_WREM3 *ac, const uint8_t param,
|
||||
const uint8_t value, bool safe = true);
|
||||
void argoWrem3_SetTimer(IRArgoAC_WREM3 *ac, bool on,
|
||||
const uint16_t currentTime, const uint16_t delayMinutes);
|
||||
#endif // SEND_ARGO
|
||||
#if SEND_BOSCH144
|
||||
void bosch144(IRBosch144AC *ac,
|
||||
const bool on, const stdAc::opmode_t mode, const float degrees,
|
||||
const stdAc::fanspeed_t fan,
|
||||
const bool quiet);
|
||||
#endif // SEND_BOSCH144
|
||||
#if SEND_CARRIER_AC64
|
||||
void carrier64(IRCarrierAc64 *ac,
|
||||
const bool on, const stdAc::opmode_t mode,
|
||||
const float degrees, const stdAc::fanspeed_t fan,
|
||||
const stdAc::swingv_t swingv, const int16_t sleep = -1);
|
||||
#endif // SEND_CARRIER_AC64
|
||||
#if SEND_COOLIX
|
||||
void coolix(IRCoolixAC *ac,
|
||||
const bool on, const stdAc::opmode_t mode, const float degrees,
|
||||
const float sensorTemp, const stdAc::fanspeed_t fan,
|
||||
const stdAc::swingv_t swingv, const stdAc::swingh_t swingh,
|
||||
const bool iFeel, const bool turbo, const bool light,
|
||||
const bool clean, const int16_t sleep = -1);
|
||||
#endif // SEND_COOLIX
|
||||
#if SEND_CORONA_AC
|
||||
void corona(IRCoronaAc *ac,
|
||||
const bool on, const stdAc::opmode_t mode,
|
||||
const float degrees, const stdAc::fanspeed_t fan,
|
||||
const stdAc::swingv_t swingv, const bool econo);
|
||||
#endif // SEND_CORONA_AC
|
||||
#if SEND_DAIKIN
|
||||
void daikin(IRDaikinESP *ac,
|
||||
const bool on, const stdAc::opmode_t mode, const float degrees,
|
||||
const stdAc::fanspeed_t fan,
|
||||
const stdAc::swingv_t swingv, const stdAc::swingh_t swingh,
|
||||
const bool quiet, const bool turbo, const bool econo,
|
||||
const bool clean);
|
||||
#endif // SEND_DAIKIN
|
||||
#if SEND_DAIKIN128
|
||||
void daikin128(IRDaikin128 *ac,
|
||||
const bool on, const stdAc::opmode_t mode,
|
||||
const float degrees, const stdAc::fanspeed_t fan,
|
||||
const stdAc::swingv_t swingv,
|
||||
const bool quiet, const bool turbo, const bool light,
|
||||
const bool econo, const int16_t sleep = -1,
|
||||
const int16_t clock = -1);
|
||||
#endif // SEND_DAIKIN128
|
||||
#if SEND_DAIKIN152
|
||||
void daikin152(IRDaikin152 *ac,
|
||||
const bool on, const stdAc::opmode_t mode,
|
||||
const float degrees, const stdAc::fanspeed_t fan,
|
||||
const stdAc::swingv_t swingv,
|
||||
const bool quiet, const bool turbo, const bool econo);
|
||||
#endif // SEND_DAIKIN152
|
||||
#if SEND_DAIKIN160
|
||||
void daikin160(IRDaikin160 *ac,
|
||||
const bool on, const stdAc::opmode_t mode,
|
||||
const float degrees, const stdAc::fanspeed_t fan,
|
||||
const stdAc::swingv_t swingv);
|
||||
#endif // SEND_DAIKIN160
|
||||
#if SEND_DAIKIN176
|
||||
void daikin176(IRDaikin176 *ac,
|
||||
const bool on, const stdAc::opmode_t mode,
|
||||
const float degrees, const stdAc::fanspeed_t fan,
|
||||
const stdAc::swingh_t swingh);
|
||||
#endif // SEND_DAIKIN176
|
||||
#if SEND_DAIKIN2
|
||||
void daikin2(IRDaikin2 *ac,
|
||||
const bool on, const stdAc::opmode_t mode,
|
||||
const float degrees, const stdAc::fanspeed_t fan,
|
||||
const stdAc::swingv_t swingv, const stdAc::swingh_t swingh,
|
||||
const bool quiet, const bool turbo, const bool light,
|
||||
const bool econo, const bool filter, const bool clean,
|
||||
const bool beep, const int16_t sleep = -1,
|
||||
const int16_t clock = -1);
|
||||
#endif // SEND_DAIKIN2
|
||||
#if SEND_DAIKIN216
|
||||
void daikin216(IRDaikin216 *ac,
|
||||
const bool on, const stdAc::opmode_t mode,
|
||||
const float degrees, const stdAc::fanspeed_t fan,
|
||||
const stdAc::swingv_t swingv, const stdAc::swingh_t swingh,
|
||||
const bool quiet, const bool turbo);
|
||||
#endif // SEND_DAIKIN216
|
||||
#if SEND_DAIKIN64
|
||||
void daikin64(IRDaikin64 *ac,
|
||||
const bool on, const stdAc::opmode_t mode,
|
||||
const float degrees, const stdAc::fanspeed_t fan,
|
||||
const stdAc::swingv_t swingv,
|
||||
const bool quiet, const bool turbo,
|
||||
const int16_t sleep = -1, const int16_t clock = -1);
|
||||
#endif // SEND_DAIKIN64
|
||||
#if SEND_DELONGHI_AC
|
||||
void delonghiac(IRDelonghiAc *ac,
|
||||
const bool on, const stdAc::opmode_t mode, const bool celsius,
|
||||
const float degrees, const stdAc::fanspeed_t fan,
|
||||
const bool turbo, const int16_t sleep = -1);
|
||||
#endif // SEND_DELONGHI_AC
|
||||
#if SEND_ECOCLIM
|
||||
void ecoclim(IREcoclimAc *ac,
|
||||
const bool on, const stdAc::opmode_t mode,
|
||||
const float degrees, const float sensorTemp,
|
||||
const stdAc::fanspeed_t fan, const int16_t sleep = -1,
|
||||
const int16_t clock = -1);
|
||||
#endif // SEND_ECOCLIM
|
||||
#if SEND_ELECTRA_AC
|
||||
void electra(IRElectraAc *ac,
|
||||
const bool on, const stdAc::opmode_t mode,
|
||||
const float degrees, const float sensorTemp,
|
||||
const stdAc::fanspeed_t fan, const stdAc::swingv_t swingv,
|
||||
const stdAc::swingh_t swingh, const bool iFeel, const bool turbo,
|
||||
const bool lighttoggle, const bool clean);
|
||||
#endif // SEND_ELECTRA_AC
|
||||
#if SEND_FUJITSU_AC
|
||||
void fujitsu(IRFujitsuAC *ac, const fujitsu_ac_remote_model_t model,
|
||||
const bool on, const stdAc::opmode_t mode,
|
||||
const bool celsius, const float degrees,
|
||||
const stdAc::fanspeed_t fan,
|
||||
const stdAc::swingv_t swingv, const stdAc::swingh_t swingh,
|
||||
const bool quiet, const bool turbo, const bool econo,
|
||||
const bool filter, const bool clean, const int16_t sleep = -1);
|
||||
#endif // SEND_FUJITSU_AC
|
||||
#if SEND_GOODWEATHER
|
||||
void goodweather(IRGoodweatherAc *ac,
|
||||
const bool on, const stdAc::opmode_t mode,
|
||||
const float degrees,
|
||||
const stdAc::fanspeed_t fan,
|
||||
const stdAc::swingv_t swingv,
|
||||
const bool turbo, const bool light,
|
||||
const int16_t sleep = -1);
|
||||
#endif // SEND_GOODWEATHER
|
||||
#if SEND_GREE
|
||||
void gree(IRGreeAC *ac, const gree_ac_remote_model_t model,
|
||||
const bool on, const stdAc::opmode_t mode, const bool celsius,
|
||||
const float degrees, const stdAc::fanspeed_t fan,
|
||||
const stdAc::swingv_t swingv, const stdAc::swingh_t swingh,
|
||||
const bool iFeel, const bool turbo, const bool econo,
|
||||
const bool light, const bool clean, const int16_t sleep = -1);
|
||||
#endif // SEND_GREE
|
||||
#if SEND_HAIER_AC
|
||||
void haier(IRHaierAC *ac,
|
||||
const bool on, const stdAc::opmode_t mode, const float degrees,
|
||||
const stdAc::fanspeed_t fan, const stdAc::swingv_t swingv,
|
||||
const bool filter, const int16_t sleep = -1,
|
||||
const int16_t clock = -1);
|
||||
#endif // SEND_HAIER_AC
|
||||
#if SEND_HAIER_AC160
|
||||
void haier160(IRHaierAC160 *ac,
|
||||
const bool on, const stdAc::opmode_t mode, const bool celsius,
|
||||
const float degrees, const stdAc::fanspeed_t fan,
|
||||
const stdAc::swingv_t swingv,
|
||||
const bool turbo, const bool quiet, const bool filter,
|
||||
const bool clean, const bool light, const bool prevlight,
|
||||
const int16_t sleep = -1);
|
||||
#endif // SEND_HAIER_AC160
|
||||
#if SEND_HAIER_AC176
|
||||
void haier176(IRHaierAC176 *ac,
|
||||
const haier_ac176_remote_model_t model, const bool on,
|
||||
const stdAc::opmode_t mode, const bool celsius,
|
||||
const float degrees, const stdAc::fanspeed_t fan,
|
||||
const stdAc::swingv_t swingv, const stdAc::swingh_t swingh,
|
||||
const bool turbo, const bool quiet, const bool filter,
|
||||
const int16_t sleep = -1);
|
||||
#endif // SEND_HAIER_AC176
|
||||
#if SEND_HAIER_AC_YRW02
|
||||
void haierYrwo2(IRHaierACYRW02 *ac,
|
||||
const bool on, const stdAc::opmode_t mode,
|
||||
const bool celsius, const float degrees,
|
||||
const stdAc::fanspeed_t fan, const stdAc::swingv_t swingv,
|
||||
const stdAc::swingh_t swingh, const bool turbo,
|
||||
const bool quiet, const bool filter,
|
||||
const int16_t sleep = -1);
|
||||
#endif // SEND_HAIER_AC_YRW02
|
||||
#if SEND_HITACHI_AC
|
||||
void hitachi(IRHitachiAc *ac,
|
||||
const bool on, const stdAc::opmode_t mode,
|
||||
const float degrees, const stdAc::fanspeed_t fan,
|
||||
const stdAc::swingv_t swingv, const stdAc::swingh_t swingh);
|
||||
#endif // SEND_HITACHI_AC
|
||||
#if SEND_HITACHI_AC1
|
||||
void hitachi1(IRHitachiAc1 *ac, const hitachi_ac1_remote_model_t model,
|
||||
const bool on, const bool power_toggle,
|
||||
const stdAc::opmode_t mode,
|
||||
const float degrees, const stdAc::fanspeed_t fan,
|
||||
const stdAc::swingv_t swingv, const stdAc::swingh_t swingh,
|
||||
const bool swing_toggle, const int16_t sleep = -1);
|
||||
#endif // SEND_HITACHI_AC1
|
||||
#if SEND_HITACHI_AC264
|
||||
void hitachi264(IRHitachiAc264 *ac,
|
||||
const bool on, const stdAc::opmode_t mode,
|
||||
const float degrees, const stdAc::fanspeed_t fan);
|
||||
#endif // SEND_HITACHI_AC264
|
||||
#if SEND_HITACHI_AC296
|
||||
void hitachi296(IRHitachiAc296 *ac,
|
||||
const bool on, const stdAc::opmode_t mode,
|
||||
const float degrees, const stdAc::fanspeed_t fan);
|
||||
#endif // SEND_HITACHI_AC296
|
||||
#if SEND_HITACHI_AC344
|
||||
void hitachi344(IRHitachiAc344 *ac,
|
||||
const bool on, const stdAc::opmode_t mode,
|
||||
const float degrees, const stdAc::fanspeed_t fan,
|
||||
const stdAc::swingv_t swingv,
|
||||
const stdAc::swingh_t swingh);
|
||||
#endif // SEND_HITACHI_AC344
|
||||
#if SEND_HITACHI_AC424
|
||||
void hitachi424(IRHitachiAc424 *ac,
|
||||
const bool on, const stdAc::opmode_t mode,
|
||||
const float degrees, const stdAc::fanspeed_t fan,
|
||||
const stdAc::swingv_t swingv);
|
||||
#endif // SEND_HITACHI_AC424
|
||||
#if SEND_KELON
|
||||
void kelon(IRKelonAc *ac, const bool togglePower, const stdAc::opmode_t mode,
|
||||
const int8_t dryGrade, const float degrees,
|
||||
const stdAc::fanspeed_t fan, const bool toggleSwing,
|
||||
const bool superCool, const int16_t sleep);
|
||||
#endif // SEND_KELON
|
||||
#if SEND_KELVINATOR
|
||||
void kelvinator(IRKelvinatorAC *ac,
|
||||
const bool on, const stdAc::opmode_t mode,
|
||||
const float degrees, const stdAc::fanspeed_t fan,
|
||||
const stdAc::swingv_t swingv, const stdAc::swingh_t swingh,
|
||||
const bool quiet, const bool turbo, const bool light,
|
||||
const bool filter, const bool clean);
|
||||
#endif // SEND_KELVINATOR
|
||||
#if SEND_LG
|
||||
void lg(IRLgAc *ac, const lg_ac_remote_model_t model,
|
||||
const bool on, const stdAc::opmode_t mode,
|
||||
const float degrees, const stdAc::fanspeed_t fan,
|
||||
const stdAc::swingv_t swingv, const stdAc::swingv_t swingv_prev,
|
||||
const stdAc::swingh_t swingh, const bool light);
|
||||
#endif // SEND_LG
|
||||
#if SEND_MIDEA
|
||||
void midea(IRMideaAC *ac,
|
||||
const bool on, const stdAc::opmode_t mode, const bool celsius,
|
||||
const float degrees, const float sensorTemp,
|
||||
const stdAc::fanspeed_t fan, const stdAc::swingv_t swingv,
|
||||
const bool iFeel, const bool quiet, const bool quiet_prev,
|
||||
const bool turbo, const bool econo, const bool light,
|
||||
const bool clean, const int16_t sleep = -1);
|
||||
#endif // SEND_MIDEA
|
||||
#if SEND_MIRAGE
|
||||
void mirage(IRMirageAc *ac, const stdAc::state_t state);
|
||||
#endif // SEND_MIRAGE
|
||||
#if SEND_MITSUBISHI_AC
|
||||
void mitsubishi(IRMitsubishiAC *ac,
|
||||
const bool on, const stdAc::opmode_t mode,
|
||||
const float degrees,
|
||||
const stdAc::fanspeed_t fan, const stdAc::swingv_t swingv,
|
||||
const stdAc::swingh_t swingh,
|
||||
const bool quiet, const int16_t clock = -1);
|
||||
#endif // SEND_MITSUBISHI_AC
|
||||
#if SEND_MITSUBISHI112
|
||||
void mitsubishi112(IRMitsubishi112 *ac,
|
||||
const bool on, const stdAc::opmode_t mode,
|
||||
const float degrees, const stdAc::fanspeed_t fan,
|
||||
const stdAc::swingv_t swingv,
|
||||
const stdAc::swingh_t swingh,
|
||||
const bool quiet);
|
||||
#endif // SEND_MITSUBISHI112
|
||||
#if SEND_MITSUBISHI136
|
||||
void mitsubishi136(IRMitsubishi136 *ac,
|
||||
const bool on, const stdAc::opmode_t mode,
|
||||
const float degrees, const stdAc::fanspeed_t fan,
|
||||
const stdAc::swingv_t swingv, const bool quiet);
|
||||
#endif // SEND_MITSUBISHI136
|
||||
#if SEND_MITSUBISHIHEAVY
|
||||
void mitsubishiHeavy88(IRMitsubishiHeavy88Ac *ac,
|
||||
const bool on, const stdAc::opmode_t mode,
|
||||
const float degrees, const stdAc::fanspeed_t fan,
|
||||
const stdAc::swingv_t swingv,
|
||||
const stdAc::swingh_t swingh,
|
||||
const bool turbo, const bool econo, const bool clean);
|
||||
void mitsubishiHeavy152(IRMitsubishiHeavy152Ac *ac,
|
||||
const bool on, const stdAc::opmode_t mode,
|
||||
const float degrees, const stdAc::fanspeed_t fan,
|
||||
const stdAc::swingv_t swingv,
|
||||
const stdAc::swingh_t swingh,
|
||||
const bool quiet, const bool turbo, const bool econo,
|
||||
const bool filter, const bool clean,
|
||||
const int16_t sleep = -1);
|
||||
#endif // SEND_MITSUBISHIHEAVY
|
||||
#if SEND_NEOCLIMA
|
||||
void neoclima(IRNeoclimaAc *ac, const bool on, const stdAc::opmode_t mode,
|
||||
const bool celsius, const float degrees,
|
||||
const stdAc::fanspeed_t fan,
|
||||
const stdAc::swingv_t swingv, const stdAc::swingh_t swingh,
|
||||
const bool turbo, const bool econo, const bool light,
|
||||
const bool filter, const int16_t sleep = -1);
|
||||
#endif // SEND_NEOCLIMA
|
||||
#if SEND_PANASONIC_AC
|
||||
void panasonic(IRPanasonicAc *ac, const panasonic_ac_remote_model_t model,
|
||||
const bool on, const stdAc::opmode_t mode, const float degrees,
|
||||
const stdAc::fanspeed_t fan,
|
||||
const stdAc::swingv_t swingv, const stdAc::swingh_t swingh,
|
||||
const bool quiet, const bool turbo, const bool filter,
|
||||
const int16_t clock = -1);
|
||||
#endif // SEND_PANASONIC_AC
|
||||
#if SEND_PANASONIC_AC32
|
||||
void panasonic32(IRPanasonicAc32 *ac,
|
||||
const bool on, const stdAc::opmode_t mode,
|
||||
const float degrees, const stdAc::fanspeed_t fan,
|
||||
const stdAc::swingv_t swingv, const stdAc::swingh_t swingh);
|
||||
#endif // SEND_PANASONIC_AC32
|
||||
#if SEND_RHOSS
|
||||
void rhoss(IRRhossAc *ac,
|
||||
const bool on, const stdAc::opmode_t mode, const float degrees,
|
||||
const stdAc::fanspeed_t fan, const stdAc::swingv_t swing);
|
||||
#endif // SEND_RHOSS
|
||||
#if SEND_SAMSUNG_AC
|
||||
void samsung(IRSamsungAc *ac,
|
||||
const bool on, const stdAc::opmode_t mode, const float degrees,
|
||||
const stdAc::fanspeed_t fan,
|
||||
const stdAc::swingv_t swingv, const stdAc::swingh_t swingh,
|
||||
const bool quiet, const bool turbo, const bool econo,
|
||||
const bool light, const bool filter, const bool clean,
|
||||
const bool beep, const int16_t sleep = -1,
|
||||
const bool prevpower = true, const int16_t prevsleep = -1,
|
||||
const bool forceextended = true);
|
||||
#endif // SEND_SAMSUNG_AC
|
||||
#if SEND_SANYO_AC
|
||||
void sanyo(IRSanyoAc *ac,
|
||||
const bool on, const stdAc::opmode_t mode, const float degrees,
|
||||
const float sensorTemp, const stdAc::fanspeed_t fan,
|
||||
const stdAc::swingv_t swingv, const bool iFeel, const bool beep,
|
||||
const int16_t sleep = -1);
|
||||
#endif // SEND_SANYO_AC
|
||||
#if SEND_SANYO_AC88
|
||||
void sanyo88(IRSanyoAc88 *ac,
|
||||
const bool on, const stdAc::opmode_t mode,
|
||||
const float degrees, const stdAc::fanspeed_t fan,
|
||||
const stdAc::swingv_t swingv, const bool turbo,
|
||||
const bool filter,
|
||||
const int16_t sleep = -1, const int16_t clock = -1);
|
||||
#endif // SEND_SANYO_AC88
|
||||
#if SEND_SHARP_AC
|
||||
void sharp(IRSharpAc *ac, const sharp_ac_remote_model_t model,
|
||||
const bool on, const bool prev_power, const stdAc::opmode_t mode,
|
||||
const float degrees, const stdAc::fanspeed_t fan,
|
||||
const stdAc::swingv_t swingv, const stdAc::swingv_t swingv_prev,
|
||||
const bool turbo, const bool light,
|
||||
const bool filter, const bool clean);
|
||||
#endif // SEND_SHARP_AC
|
||||
#if SEND_TCL112AC
|
||||
void tcl112(IRTcl112Ac *ac, const tcl_ac_remote_model_t model,
|
||||
const bool on, const stdAc::opmode_t mode, const float degrees,
|
||||
const stdAc::fanspeed_t fan,
|
||||
const stdAc::swingv_t swingv, const stdAc::swingh_t swingh,
|
||||
const bool quiet, const bool turbo, const bool light,
|
||||
const bool econo, const bool filter);
|
||||
#endif // SEND_TCL112AC
|
||||
#if SEND_TECHNIBEL_AC
|
||||
void technibel(IRTechnibelAc *ac,
|
||||
const bool on, const stdAc::opmode_t mode, const bool celsius,
|
||||
const float degrees, const stdAc::fanspeed_t fan,
|
||||
const stdAc::swingv_t swingv, const int16_t sleep = -1);
|
||||
#endif // SEND_TECHNIBEL_AC
|
||||
#if SEND_TECO
|
||||
void teco(IRTecoAc *ac,
|
||||
const bool on, const stdAc::opmode_t mode, const float degrees,
|
||||
const stdAc::fanspeed_t fan, const stdAc::swingv_t swingv,
|
||||
const bool light, const int16_t sleep = -1);
|
||||
#endif // SEND_TECO
|
||||
#if SEND_TOSHIBA_AC
|
||||
void toshiba(IRToshibaAC *ac,
|
||||
const bool on, const stdAc::opmode_t mode, const float degrees,
|
||||
const stdAc::fanspeed_t fan, const stdAc::swingv_t swingv,
|
||||
const bool turbo, const bool econo, const bool filter);
|
||||
#endif // SEND_TOSHIBA_AC
|
||||
#if SEND_TROTEC
|
||||
void trotec(IRTrotecESP *ac,
|
||||
const bool on, const stdAc::opmode_t mode, const float degrees,
|
||||
const stdAc::fanspeed_t fan, const int16_t sleep = -1);
|
||||
#endif // SEND_TROTEC
|
||||
#if SEND_TROTEC_3550
|
||||
void trotec3550(IRTrotec3550 *ac,
|
||||
const bool on, const stdAc::opmode_t mode,
|
||||
const bool celsius, const float degrees,
|
||||
const stdAc::fanspeed_t fan, const stdAc::swingv_t swingv);
|
||||
#endif // SEND_TROTEC_3550
|
||||
#if SEND_TRUMA
|
||||
void truma(IRTrumaAc *ac,
|
||||
const bool on, const stdAc::opmode_t mode, const float degrees,
|
||||
const stdAc::fanspeed_t fan, const bool quiet);
|
||||
#endif // SEND_TRUMA
|
||||
#if SEND_VESTEL_AC
|
||||
void vestel(IRVestelAc *ac,
|
||||
const bool on, const stdAc::opmode_t mode, const float degrees,
|
||||
const stdAc::fanspeed_t fan, const stdAc::swingv_t swingv,
|
||||
const bool turbo, const bool filter,
|
||||
const int16_t sleep = -1, const int16_t clock = -1,
|
||||
const bool sendNormal = true);
|
||||
#endif // SEND_VESTEL_AC
|
||||
#if SEND_VOLTAS
|
||||
void voltas(IRVoltas *ac, const voltas_ac_remote_model_t model,
|
||||
const bool on, const stdAc::opmode_t mode,
|
||||
const float degrees, const stdAc::fanspeed_t fan,
|
||||
const stdAc::swingv_t swingv, const stdAc::swingh_t swingh,
|
||||
const bool turbo, const bool econo, const bool light,
|
||||
const int16_t sleep = -1);
|
||||
#endif // SEND_VOLTAS
|
||||
#if SEND_WHIRLPOOL_AC
|
||||
void whirlpool(IRWhirlpoolAc *ac, const whirlpool_ac_remote_model_t model,
|
||||
const bool on, const stdAc::opmode_t mode, const float degrees,
|
||||
const stdAc::fanspeed_t fan, const stdAc::swingv_t swingv,
|
||||
const bool turbo, const bool light,
|
||||
const int16_t sleep = -1, const int16_t clock = -1);
|
||||
#endif // SEND_WHIRLPOOL_AC
|
||||
#if SEND_TRANSCOLD
|
||||
void transcold(IRTranscoldAc *ac,
|
||||
const bool on, const stdAc::opmode_t mode, const float degrees,
|
||||
const stdAc::fanspeed_t fan,
|
||||
const stdAc::swingv_t swingv, const stdAc::swingh_t swingh);
|
||||
#endif // SEND_TRANSCOLD
|
||||
static stdAc::state_t cleanState(const stdAc::state_t state);
|
||||
static stdAc::state_t handleToggles(const stdAc::state_t desired,
|
||||
const stdAc::state_t *prev = NULL);
|
||||
}; // IRac class
|
||||
|
||||
/// Common functions for use with all A/Cs supported by the IRac class.
|
||||
namespace IRAcUtils {
|
||||
String resultAcToString(const decode_results * const results);
|
||||
bool decodeToState(const decode_results *decode, stdAc::state_t *result,
|
||||
const stdAc::state_t *prev = NULL);
|
||||
} // namespace IRAcUtils
|
||||
#endif // IRAC_H_
|
||||
53
yoRadio/src/IRremoteESP8266/IRmacros.h
Normal file
53
yoRadio/src/IRremoteESP8266/IRmacros.h
Normal file
@@ -0,0 +1,53 @@
|
||||
#ifndef IRMACROS_H_
|
||||
#define IRMACROS_H_
|
||||
/****************************************************************
|
||||
* Copyright 2022 IRremoteESP8266 project and others
|
||||
*/
|
||||
/// @file IRmacros.h
|
||||
|
||||
/**
|
||||
* VA_OPT_SUPPORTED macro to check if __VA_OPT__ is supported
|
||||
* Source: https://stackoverflow.com/a/48045656
|
||||
*/
|
||||
/// @cond TEST
|
||||
#define PP_THIRD_ARG(a, b, c, ...) c
|
||||
#define VA_OPT_SUPPORTED_I(...) \
|
||||
PP_THIRD_ARG(__VA_OPT__(, false), true, false, false)
|
||||
#define VA_OPT_SUPPORTED VA_OPT_SUPPORTED_I(?)
|
||||
/// @endcond
|
||||
/**
|
||||
* VA_OPT_SUPPORTED end
|
||||
*/
|
||||
|
||||
/**
|
||||
* COND() Set of macros to facilitate single-line conditional compilation
|
||||
* argument checking.
|
||||
* Found here: https://www.reddit.com/r/C_Programming/comments/ud3xrv/
|
||||
* conditional_preprocessor_macro_anyone/
|
||||
*
|
||||
* Usage:
|
||||
* COND(<define_to_test>[||/&&<more_define>...], <true_result>, <false_result>)
|
||||
*
|
||||
* NB: If __VA_OPT__ macro not supported, the <true_result> will be expanded!
|
||||
*/
|
||||
/// @cond TEST
|
||||
#if !VA_OPT_SUPPORTED
|
||||
// #pragma message("Compiler without __VA_OPT__ support")
|
||||
#define COND(cond, a, b) a
|
||||
#else // !VA_OPT_SUPPORTED
|
||||
#define NOTHING
|
||||
#define EXPAND(...) __VA_ARGS__
|
||||
#define STUFF_P(a, ...) __VA_OPT__(a)
|
||||
#define STUFF(...) STUFF_P(__VA_ARGS__)
|
||||
#define VA_TEST_P(a, ...) __VA_OPT__(NO)##THING
|
||||
#define VA_TEST(...) VA_TEST_P(__VA_ARGS__)
|
||||
#define NEGATE(a) VA_TEST(a, a)
|
||||
#define COND_P(cond, a, b) STUFF(a, cond)STUFF(b, NEGATE(cond))
|
||||
#define COND(cond, a, b) EXPAND(COND_P(cond, a, b))
|
||||
#endif // !VA_OPT_SUPPORTED
|
||||
/// @endcond
|
||||
/**
|
||||
* end of COND() set of macros
|
||||
*/
|
||||
|
||||
#endif // IRMACROS_H_
|
||||
2099
yoRadio/src/IRremoteESP8266/IRrecv.cpp
Normal file
2099
yoRadio/src/IRremoteESP8266/IRrecv.cpp
Normal file
File diff suppressed because it is too large
Load Diff
888
yoRadio/src/IRremoteESP8266/IRrecv.h
Normal file
888
yoRadio/src/IRremoteESP8266/IRrecv.h
Normal file
@@ -0,0 +1,888 @@
|
||||
// Copyright 2009 Ken Shirriff
|
||||
// Copyright 2015 Mark Szabo
|
||||
// Copyright 2015 Sebastien Warin
|
||||
// Copyright 2017 David Conran
|
||||
|
||||
#ifndef IRRECV_H_
|
||||
#define IRRECV_H_
|
||||
|
||||
#ifndef UNIT_TEST
|
||||
#include <Arduino.h>
|
||||
#endif
|
||||
#include <stddef.h>
|
||||
#define __STDC_LIMIT_MACROS
|
||||
#include <stdint.h>
|
||||
#include "IRremoteESP8266.h"
|
||||
|
||||
// Constants
|
||||
const uint16_t kHeader = 2; // Usual nr. of header entries.
|
||||
const uint16_t kFooter = 2; // Usual nr. of footer (stop bits) entries.
|
||||
const uint16_t kStartOffset = 1; // Usual rawbuf entry to start from.
|
||||
#define MS_TO_USEC(x) ((x) * 1000U) // Convert milli-Seconds to micro-Seconds.
|
||||
// Marks tend to be 100us too long, and spaces 100us too short
|
||||
// when received due to sensor lag.
|
||||
const uint16_t kMarkExcess = 50;
|
||||
const uint16_t kRawBuf = 100; // Default length of raw capture buffer
|
||||
const uint64_t kRepeat = UINT64_MAX;
|
||||
// Default min size of reported UNKNOWN messages.
|
||||
const uint16_t kUnknownThreshold = 6;
|
||||
|
||||
// receiver states
|
||||
const uint8_t kIdleState = 2;
|
||||
const uint8_t kMarkState = 3;
|
||||
const uint8_t kSpaceState = 4;
|
||||
const uint8_t kStopState = 5;
|
||||
const uint8_t kTolerance = 25; // default percent tolerance in measurements.
|
||||
const uint8_t kUseDefTol = 255; // Indicate to use the class default tolerance.
|
||||
const uint16_t kRawTick = 2; // Capture tick to uSec factor.
|
||||
#define RAWTICK kRawTick // Deprecated. For legacy user code support only.
|
||||
// How long (ms) before we give up wait for more data?
|
||||
// Don't exceed kMaxTimeoutMs without a good reason.
|
||||
// That is the capture buffers maximum value size. (UINT16_MAX / kRawTick)
|
||||
// Typically messages/protocols tend to repeat around the 100ms timeframe,
|
||||
// thus we should timeout before that to give us some time to try to decode
|
||||
// before we need to start capturing a possible new message.
|
||||
// Typically 15ms suits most applications. However, some protocols demand a
|
||||
// higher value. e.g. 90ms for XMP-1 and some aircon units.
|
||||
const uint8_t kTimeoutMs = 15; // In MilliSeconds.
|
||||
#define TIMEOUT_MS kTimeoutMs // For legacy documentation.
|
||||
const uint16_t kMaxTimeoutMs = kRawTick * (UINT16_MAX / MS_TO_USEC(1));
|
||||
|
||||
// Use FNV hash algorithm: http://isthe.com/chongo/tech/comp/fnv/#FNV-param
|
||||
const uint32_t kFnvPrime32 = 16777619UL;
|
||||
const uint32_t kFnvBasis32 = 2166136261UL;
|
||||
|
||||
#ifdef ESP32
|
||||
// Which of the ESP32 timers to use by default.
|
||||
// (3 for most ESP32s, 1 for ESP32-C3s)
|
||||
#ifdef SOC_TIMER_GROUP_TOTAL_TIMERS
|
||||
const uint8_t kDefaultESP32Timer = SOC_TIMER_GROUP_TOTAL_TIMERS - 1;
|
||||
#else // SOC_TIMER_GROUP_TOTAL_TIMERS
|
||||
const uint8_t kDefaultESP32Timer = 3;
|
||||
#endif // SOC_TIMER_GROUP_TOTAL_TIMERS
|
||||
#endif // ESP32
|
||||
|
||||
#if DECODE_AC
|
||||
// Hitachi AC is the current largest state size.
|
||||
const uint16_t kStateSizeMax = kHitachiAc2StateLength;
|
||||
#else // DECODE_AC
|
||||
// Just define something (a uint64_t)
|
||||
const uint16_t kStateSizeMax = sizeof(uint64_t);
|
||||
#endif // DECODE_AC
|
||||
|
||||
// Types
|
||||
|
||||
/// Information for the interrupt handler
|
||||
typedef struct {
|
||||
uint8_t recvpin; // pin for IR data from detector
|
||||
uint8_t rcvstate; // state machine
|
||||
uint16_t timer; // state timer, counts 50uS ticks.
|
||||
uint16_t bufsize; // max. nr. of entries in the capture buffer.
|
||||
uint16_t *rawbuf; // raw data
|
||||
// uint16_t is used for rawlen as it saves 3 bytes of iram in the interrupt
|
||||
// handler. Don't ask why, I don't know. It just does.
|
||||
uint16_t rawlen; // counter of entries in rawbuf.
|
||||
uint8_t overflow; // Buffer overflow indicator.
|
||||
uint8_t timeout; // Nr. of milliSeconds before we give up.
|
||||
} irparams_t;
|
||||
|
||||
/// Results from a data match
|
||||
typedef struct {
|
||||
bool success; // Was the match successful?
|
||||
uint64_t data; // The data found.
|
||||
uint16_t used; // How many buffer positions were used.
|
||||
} match_result_t;
|
||||
|
||||
// Classes
|
||||
|
||||
/// Results returned from the decoder
|
||||
class decode_results {
|
||||
public:
|
||||
decode_type_t decode_type; // NEC, SONY, RC5, UNKNOWN
|
||||
// value, address, & command are all mutually exclusive with state.
|
||||
// i.e. They MUST NOT be used at the same time as state, so we can use a union
|
||||
// structure to save us a handful of valuable bytes of memory.
|
||||
union {
|
||||
struct {
|
||||
uint64_t value; // Decoded value
|
||||
uint32_t address; // Decoded device address.
|
||||
uint32_t command; // Decoded command.
|
||||
};
|
||||
uint8_t state[kStateSizeMax]; // Multi-byte results.
|
||||
};
|
||||
uint16_t bits; // Number of bits in decoded value
|
||||
volatile uint16_t *rawbuf; // Raw intervals in .5 us ticks
|
||||
uint16_t rawlen; // Number of records in rawbuf.
|
||||
bool overflow;
|
||||
bool repeat; // Is the result a repeat code?
|
||||
};
|
||||
|
||||
/// Class for receiving IR messages.
|
||||
class IRrecv {
|
||||
public:
|
||||
#if defined(ESP32)
|
||||
explicit IRrecv(const uint16_t recvpin, const uint16_t bufsize = kRawBuf,
|
||||
const uint8_t timeout = kTimeoutMs,
|
||||
const bool save_buffer = false,
|
||||
const uint8_t timer_num = kDefaultESP32Timer); // Constructor
|
||||
#else // ESP32
|
||||
explicit IRrecv(const uint16_t recvpin, const uint16_t bufsize = kRawBuf,
|
||||
const uint8_t timeout = kTimeoutMs,
|
||||
const bool save_buffer = false); // Constructor
|
||||
#endif // ESP32
|
||||
~IRrecv(void); // Destructor
|
||||
void setTolerance(const uint8_t percent = kTolerance);
|
||||
uint8_t getTolerance(void);
|
||||
bool decode(decode_results *results, irparams_t *save = NULL,
|
||||
uint8_t max_skip = 0, uint16_t noise_floor = 0);
|
||||
void enableIRIn(const bool pullup = false);
|
||||
void disableIRIn(void);
|
||||
void pause(void);
|
||||
void resume(void);
|
||||
uint16_t getBufSize(void);
|
||||
#if DECODE_HASH
|
||||
void setUnknownThreshold(const uint16_t length);
|
||||
#endif
|
||||
bool match(const uint32_t measured, const uint32_t desired,
|
||||
const uint8_t tolerance = kUseDefTol,
|
||||
const uint16_t delta = 0);
|
||||
bool matchMark(const uint32_t measured, const uint32_t desired,
|
||||
const uint8_t tolerance = kUseDefTol,
|
||||
const int16_t excess = kMarkExcess);
|
||||
bool matchMarkRange(const uint32_t measured, const uint32_t desired,
|
||||
const uint16_t range = 100,
|
||||
const int16_t excess = kMarkExcess);
|
||||
bool matchSpace(const uint32_t measured, const uint32_t desired,
|
||||
const uint8_t tolerance = kUseDefTol,
|
||||
const int16_t excess = kMarkExcess);
|
||||
bool matchSpaceRange(const uint32_t measured, const uint32_t desired,
|
||||
const uint16_t range = 100,
|
||||
const int16_t excess = kMarkExcess);
|
||||
#ifndef UNIT_TEST
|
||||
|
||||
private:
|
||||
#endif
|
||||
irparams_t *irparams_save;
|
||||
uint8_t _tolerance;
|
||||
#if defined(ESP32)
|
||||
uint8_t _timer_num;
|
||||
#endif // defined(ESP32)
|
||||
#if DECODE_HASH
|
||||
uint16_t _unknown_threshold;
|
||||
#endif
|
||||
#ifdef UNIT_TEST
|
||||
volatile irparams_t *_getParamsPtr(void);
|
||||
#endif // UNIT_TEST
|
||||
// These are called by decode
|
||||
uint8_t _validTolerance(const uint8_t percentage);
|
||||
void copyIrParams(volatile irparams_t *src, irparams_t *dst);
|
||||
uint16_t compare(const uint16_t oldval, const uint16_t newval);
|
||||
uint32_t ticksLow(const uint32_t usecs,
|
||||
const uint8_t tolerance = kUseDefTol,
|
||||
const uint16_t delta = 0);
|
||||
uint32_t ticksHigh(const uint32_t usecs,
|
||||
const uint8_t tolerance = kUseDefTol,
|
||||
const uint16_t delta = 0);
|
||||
bool matchAtLeast(const uint32_t measured, const uint32_t desired,
|
||||
const uint8_t tolerance = kUseDefTol,
|
||||
const uint16_t delta = 0);
|
||||
uint16_t _matchGeneric(volatile uint16_t *data_ptr,
|
||||
uint64_t *result_bits_ptr,
|
||||
uint8_t *result_ptr,
|
||||
const bool use_bits,
|
||||
const uint16_t remaining,
|
||||
const uint16_t required,
|
||||
const uint16_t hdrmark,
|
||||
const uint32_t hdrspace,
|
||||
const uint16_t onemark,
|
||||
const uint32_t onespace,
|
||||
const uint16_t zeromark,
|
||||
const uint32_t zerospace,
|
||||
const uint16_t footermark,
|
||||
const uint32_t footerspace,
|
||||
const bool atleast = false,
|
||||
const uint8_t tolerance = kUseDefTol,
|
||||
const int16_t excess = kMarkExcess,
|
||||
const bool MSBfirst = true);
|
||||
match_result_t matchData(volatile uint16_t *data_ptr, const uint16_t nbits,
|
||||
const uint16_t onemark, const uint32_t onespace,
|
||||
const uint16_t zeromark, const uint32_t zerospace,
|
||||
const uint8_t tolerance = kUseDefTol,
|
||||
const int16_t excess = kMarkExcess,
|
||||
const bool MSBfirst = true,
|
||||
const bool expectlastspace = true);
|
||||
uint16_t matchBytes(volatile uint16_t *data_ptr, uint8_t *result_ptr,
|
||||
const uint16_t remaining, const uint16_t nbytes,
|
||||
const uint16_t onemark, const uint32_t onespace,
|
||||
const uint16_t zeromark, const uint32_t zerospace,
|
||||
const uint8_t tolerance = kUseDefTol,
|
||||
const int16_t excess = kMarkExcess,
|
||||
const bool MSBfirst = true,
|
||||
const bool expectlastspace = true);
|
||||
uint16_t matchGeneric(volatile uint16_t *data_ptr,
|
||||
uint64_t *result_ptr,
|
||||
const uint16_t remaining, const uint16_t nbits,
|
||||
const uint16_t hdrmark, const uint32_t hdrspace,
|
||||
const uint16_t onemark, const uint32_t onespace,
|
||||
const uint16_t zeromark, const uint32_t zerospace,
|
||||
const uint16_t footermark, const uint32_t footerspace,
|
||||
const bool atleast = false,
|
||||
const uint8_t tolerance = kUseDefTol,
|
||||
const int16_t excess = kMarkExcess,
|
||||
const bool MSBfirst = true);
|
||||
uint16_t matchGeneric(volatile uint16_t *data_ptr, uint8_t *result_ptr,
|
||||
const uint16_t remaining, const uint16_t nbits,
|
||||
const uint16_t hdrmark, const uint32_t hdrspace,
|
||||
const uint16_t onemark, const uint32_t onespace,
|
||||
const uint16_t zeromark, const uint32_t zerospace,
|
||||
const uint16_t footermark,
|
||||
const uint32_t footerspace,
|
||||
const bool atleast = false,
|
||||
const uint8_t tolerance = kUseDefTol,
|
||||
const int16_t excess = kMarkExcess,
|
||||
const bool MSBfirst = true);
|
||||
uint16_t matchGenericConstBitTime(volatile uint16_t *data_ptr,
|
||||
uint64_t *result_ptr,
|
||||
const uint16_t remaining,
|
||||
const uint16_t nbits,
|
||||
const uint16_t hdrmark,
|
||||
const uint32_t hdrspace,
|
||||
const uint16_t one,
|
||||
const uint32_t zero,
|
||||
const uint16_t footermark,
|
||||
const uint32_t footerspace,
|
||||
const bool atleast = false,
|
||||
const uint8_t tolerance = kUseDefTol,
|
||||
const int16_t excess = kMarkExcess,
|
||||
const bool MSBfirst = true);
|
||||
uint16_t matchManchesterData(volatile const uint16_t *data_ptr,
|
||||
uint64_t *result_ptr,
|
||||
const uint16_t remaining,
|
||||
const uint16_t nbits,
|
||||
const uint16_t half_period,
|
||||
const uint16_t starting_balance = 0,
|
||||
const uint8_t tolerance = kUseDefTol,
|
||||
const int16_t excess = kMarkExcess,
|
||||
const bool MSBfirst = true,
|
||||
const bool GEThomas = true);
|
||||
uint16_t matchManchester(volatile const uint16_t *data_ptr,
|
||||
uint64_t *result_ptr,
|
||||
const uint16_t remaining,
|
||||
const uint16_t nbits,
|
||||
const uint16_t hdrmark,
|
||||
const uint32_t hdrspace,
|
||||
const uint16_t clock_period,
|
||||
const uint16_t footermark,
|
||||
const uint32_t footerspace,
|
||||
const bool atleast = false,
|
||||
const uint8_t tolerance = kUseDefTol,
|
||||
const int16_t excess = kMarkExcess,
|
||||
const bool MSBfirst = true,
|
||||
const bool GEThomas = true);
|
||||
void crudeNoiseFilter(decode_results *results, const uint16_t floor = 0);
|
||||
bool decodeHash(decode_results *results);
|
||||
#if DECODE_VOLTAS
|
||||
bool decodeVoltas(decode_results *results,
|
||||
uint16_t offset = kStartOffset,
|
||||
const uint16_t nbits = kVoltasBits,
|
||||
const bool strict = true);
|
||||
#endif // DECODE_VOLTAS
|
||||
#if (DECODE_NEC || DECODE_SHERWOOD || DECODE_AIWA_RC_T501 || DECODE_SANYO)
|
||||
bool decodeNEC(decode_results *results, uint16_t offset = kStartOffset,
|
||||
const uint16_t nbits = kNECBits, const bool strict = true);
|
||||
#endif
|
||||
#if DECODE_ARGO
|
||||
bool decodeArgo(decode_results *results, uint16_t offset = kStartOffset,
|
||||
const uint16_t nbits = kArgoBits, const bool strict = true);
|
||||
bool decodeArgoWREM3(decode_results *results, uint16_t offset = kStartOffset,
|
||||
const uint16_t nbits = kArgo3AcControlStateLength * 8,
|
||||
const bool strict = true);
|
||||
#endif // DECODE_ARGO
|
||||
#if DECODE_ARRIS
|
||||
bool decodeArris(decode_results *results, uint16_t offset = kStartOffset,
|
||||
const uint16_t nbits = kArrisBits, const bool strict = true);
|
||||
#endif // DECODE_ARRIS
|
||||
#if DECODE_SONY
|
||||
bool decodeSony(decode_results *results, uint16_t offset = kStartOffset,
|
||||
const uint16_t nbits = kSonyMinBits,
|
||||
const bool strict = false);
|
||||
#endif
|
||||
#if DECODE_SANYO
|
||||
// DISABLED due to poor quality.
|
||||
// bool decodeSanyo(decode_results *results, uint16_t offset = kStartOffset,
|
||||
// uint16_t nbits = kSanyoSA8650BBits,
|
||||
// bool strict = false);
|
||||
bool decodeSanyoLC7461(decode_results *results,
|
||||
uint16_t offset = kStartOffset,
|
||||
const uint16_t nbits = kSanyoLC7461Bits,
|
||||
const bool strict = true);
|
||||
#endif
|
||||
#if DECODE_SANYO_AC
|
||||
bool decodeSanyoAc(decode_results *results,
|
||||
uint16_t offset = kStartOffset,
|
||||
const uint16_t nbits = kSanyoAcBits,
|
||||
const bool strict = true);
|
||||
#endif // DECODE_SANYO_AC
|
||||
#if DECODE_SANYO_AC88
|
||||
bool decodeSanyoAc88(decode_results *results,
|
||||
uint16_t offset = kStartOffset,
|
||||
const uint16_t nbits = kSanyoAc88Bits,
|
||||
const bool strict = true);
|
||||
#endif // DECODE_SANYO_AC88
|
||||
#if DECODE_SANYO_AC152
|
||||
bool decodeSanyoAc152(decode_results *results,
|
||||
uint16_t offset = kStartOffset,
|
||||
const uint16_t nbits = kSanyoAc152Bits,
|
||||
const bool strict = true);
|
||||
#endif // DECODE_SANYO_AC152
|
||||
#if DECODE_MITSUBISHI
|
||||
bool decodeMitsubishi(decode_results *results, uint16_t offset = kStartOffset,
|
||||
const uint16_t nbits = kMitsubishiBits,
|
||||
const bool strict = true);
|
||||
#endif
|
||||
#if DECODE_MITSUBISHI2
|
||||
bool decodeMitsubishi2(decode_results *results,
|
||||
uint16_t offset = kStartOffset,
|
||||
const uint16_t nbits = kMitsubishiBits,
|
||||
const bool strict = true);
|
||||
#endif
|
||||
#if DECODE_MITSUBISHI_AC
|
||||
bool decodeMitsubishiAC(decode_results *results,
|
||||
uint16_t offset = kStartOffset,
|
||||
const uint16_t nbits = kMitsubishiACBits,
|
||||
const bool strict = false);
|
||||
#endif
|
||||
#if DECODE_MITSUBISHI136
|
||||
bool decodeMitsubishi136(decode_results *results,
|
||||
uint16_t offset = kStartOffset,
|
||||
const uint16_t nbits = kMitsubishi136Bits,
|
||||
const bool strict = true);
|
||||
#endif
|
||||
#if DECODE_MITSUBISHI112
|
||||
bool decodeMitsubishi112(decode_results *results,
|
||||
uint16_t offset = kStartOffset,
|
||||
const uint16_t nbits = kMitsubishi112Bits,
|
||||
const bool strict = true);
|
||||
#endif
|
||||
#if DECODE_MITSUBISHIHEAVY
|
||||
bool decodeMitsubishiHeavy(decode_results *results,
|
||||
uint16_t offset = kStartOffset,
|
||||
const uint16_t nbits = kMitsubishiHeavy152Bits,
|
||||
const bool strict = true);
|
||||
#endif
|
||||
#if (DECODE_RC5 || DECODE_RC6 || DECODE_LASERTAG || DECODE_MWM)
|
||||
int16_t getRClevel(decode_results *results, uint16_t *offset, uint16_t *used,
|
||||
uint16_t bitTime, const uint8_t tolerance = kUseDefTol,
|
||||
const int16_t excess = kMarkExcess,
|
||||
const uint16_t delta = 0, const uint8_t maxwidth = 3);
|
||||
#endif
|
||||
#if DECODE_RC5
|
||||
bool decodeRC5(decode_results *results, uint16_t offset = kStartOffset,
|
||||
const uint16_t nbits = kRC5XBits,
|
||||
const bool strict = true);
|
||||
#endif
|
||||
#if DECODE_RC6
|
||||
bool decodeRC6(decode_results *results, uint16_t offset = kStartOffset,
|
||||
const uint16_t nbits = kRC6Mode0Bits,
|
||||
const bool strict = false);
|
||||
#endif
|
||||
#if DECODE_RCMM
|
||||
bool decodeRCMM(decode_results *results, uint16_t offset = kStartOffset,
|
||||
const uint16_t nbits = kRCMMBits,
|
||||
const bool strict = false);
|
||||
#endif
|
||||
#if (DECODE_PANASONIC || DECODE_DENON)
|
||||
bool decodePanasonic(decode_results *results, uint16_t offset = kStartOffset,
|
||||
const uint16_t nbits = kPanasonicBits,
|
||||
const bool strict = false,
|
||||
const uint32_t manufacturer = kPanasonicManufacturer);
|
||||
#endif
|
||||
#if DECODE_LG
|
||||
bool decodeLG(decode_results *results, uint16_t offset = kStartOffset,
|
||||
const uint16_t nbits = kLgBits,
|
||||
const bool strict = false);
|
||||
#endif
|
||||
#if DECODE_INAX
|
||||
bool decodeInax(decode_results *results, uint16_t offset = kStartOffset,
|
||||
const uint16_t nbits = kInaxBits,
|
||||
const bool strict = true);
|
||||
#endif // DECODE_INAX
|
||||
#if DECODE_JVC
|
||||
bool decodeJVC(decode_results *results, uint16_t offset = kStartOffset,
|
||||
const uint16_t nbits = kJvcBits,
|
||||
const bool strict = true);
|
||||
#endif
|
||||
#if DECODE_SAMSUNG
|
||||
bool decodeSAMSUNG(decode_results *results, uint16_t offset = kStartOffset,
|
||||
const uint16_t nbits = kSamsungBits,
|
||||
const bool strict = true);
|
||||
#endif
|
||||
#if DECODE_SAMSUNG
|
||||
bool decodeSamsung36(decode_results *results, uint16_t offset = kStartOffset,
|
||||
const uint16_t nbits = kSamsung36Bits,
|
||||
const bool strict = true);
|
||||
#endif
|
||||
#if DECODE_SAMSUNG_AC
|
||||
bool decodeSamsungAC(decode_results *results, uint16_t offset = kStartOffset,
|
||||
const uint16_t nbits = kSamsungAcBits,
|
||||
const bool strict = true);
|
||||
#endif
|
||||
#if DECODE_WHYNTER
|
||||
bool decodeWhynter(decode_results *results, uint16_t offset = kStartOffset,
|
||||
const uint16_t nbits = kWhynterBits,
|
||||
const bool strict = true);
|
||||
#endif
|
||||
#if DECODE_COOLIX
|
||||
bool decodeCOOLIX(decode_results *results, uint16_t offset = kStartOffset,
|
||||
const uint16_t nbits = kCoolixBits,
|
||||
const bool strict = true);
|
||||
#endif // DECODE_COOLIX
|
||||
#if DECODE_COOLIX48
|
||||
bool decodeCoolix48(decode_results *results, uint16_t offset = kStartOffset,
|
||||
const uint16_t nbits = kCoolix48Bits,
|
||||
const bool strict = true);
|
||||
#endif // DECODE_COOLIX48
|
||||
#if DECODE_DENON
|
||||
bool decodeDenon(decode_results *results, uint16_t offset = kStartOffset,
|
||||
const uint16_t nbits = kDenonBits,
|
||||
const bool strict = true);
|
||||
#endif
|
||||
#if DECODE_DISH
|
||||
bool decodeDISH(decode_results *results, uint16_t offset = kStartOffset,
|
||||
const uint16_t nbits = kDishBits,
|
||||
const bool strict = true);
|
||||
#endif
|
||||
#if (DECODE_SHARP || DECODE_DENON)
|
||||
bool decodeSharp(decode_results *results, uint16_t offset = kStartOffset,
|
||||
const uint16_t nbits = kSharpBits,
|
||||
const bool strict = true, const bool expansion = true);
|
||||
#endif
|
||||
#if DECODE_SHARP_AC
|
||||
bool decodeSharpAc(decode_results *results, uint16_t offset = kStartOffset,
|
||||
const uint16_t nbits = kSharpAcBits,
|
||||
const bool strict = true);
|
||||
#endif
|
||||
#if DECODE_AIWA_RC_T501
|
||||
bool decodeAiwaRCT501(decode_results *results, uint16_t offset = kStartOffset,
|
||||
const uint16_t nbits = kAiwaRcT501Bits,
|
||||
const bool strict = true);
|
||||
#endif
|
||||
#if DECODE_NIKAI
|
||||
bool decodeNikai(decode_results *results, uint16_t offset = kStartOffset,
|
||||
const uint16_t nbits = kNikaiBits,
|
||||
const bool strict = true);
|
||||
#endif
|
||||
#if DECODE_MAGIQUEST
|
||||
bool decodeMagiQuest(decode_results *results, uint16_t offset = kStartOffset,
|
||||
const uint16_t nbits = kMagiquestBits,
|
||||
const bool strict = true);
|
||||
#endif
|
||||
#if DECODE_KELVINATOR
|
||||
bool decodeKelvinator(decode_results *results, uint16_t offset = kStartOffset,
|
||||
const uint16_t nbits = kKelvinatorBits,
|
||||
const bool strict = true);
|
||||
#endif
|
||||
#if DECODE_DAIKIN
|
||||
bool decodeDaikin(decode_results *results, uint16_t offset = kStartOffset,
|
||||
const uint16_t nbits = kDaikinBits,
|
||||
const bool strict = true);
|
||||
#endif
|
||||
#if DECODE_DAIKIN64
|
||||
bool decodeDaikin64(decode_results *results, uint16_t offset = kStartOffset,
|
||||
const uint16_t nbits = kDaikin64Bits,
|
||||
const bool strict = true);
|
||||
#endif // DECODE_DAIKIN64
|
||||
#if DECODE_DAIKIN128
|
||||
bool decodeDaikin128(decode_results *results, uint16_t offset = kStartOffset,
|
||||
const uint16_t nbits = kDaikin128Bits,
|
||||
const bool strict = true);
|
||||
#endif // DECODE_DAIKIN128
|
||||
#if DECODE_DAIKIN152
|
||||
bool decodeDaikin152(decode_results *results, uint16_t offset = kStartOffset,
|
||||
const uint16_t nbits = kDaikin152Bits,
|
||||
const bool strict = true);
|
||||
#endif // DECODE_DAIKIN152
|
||||
#if DECODE_DAIKIN160
|
||||
bool decodeDaikin160(decode_results *results, uint16_t offset = kStartOffset,
|
||||
const uint16_t nbits = kDaikin160Bits,
|
||||
const bool strict = true);
|
||||
#endif // DECODE_DAIKIN160
|
||||
#if DECODE_DAIKIN176
|
||||
bool decodeDaikin176(decode_results *results, uint16_t offset = kStartOffset,
|
||||
const uint16_t nbits = kDaikin176Bits,
|
||||
const bool strict = true);
|
||||
#endif // DECODE_DAIKIN176
|
||||
#if DECODE_DAIKIN2
|
||||
bool decodeDaikin2(decode_results *results, uint16_t offset = kStartOffset,
|
||||
const uint16_t nbits = kDaikin2Bits,
|
||||
const bool strict = true);
|
||||
#endif
|
||||
#if DECODE_DAIKIN200
|
||||
bool decodeDaikin200(decode_results *results, uint16_t offset = kStartOffset,
|
||||
const uint16_t nbits = kDaikin200Bits,
|
||||
const bool strict = true);
|
||||
#endif // DECODE_DAIKIN200
|
||||
#if DECODE_DAIKIN216
|
||||
bool decodeDaikin216(decode_results *results, uint16_t offset = kStartOffset,
|
||||
const uint16_t nbits = kDaikin216Bits,
|
||||
const bool strict = true);
|
||||
#endif // DECODE_DAIKIN216
|
||||
#if DECODE_DAIKIN312
|
||||
bool decodeDaikin312(decode_results *results, uint16_t offset = kStartOffset,
|
||||
const uint16_t nbits = kDaikin312Bits,
|
||||
const bool strict = true);
|
||||
#endif // DECODE_DAIKIN312
|
||||
#if DECODE_TOSHIBA_AC
|
||||
bool decodeToshibaAC(decode_results *results, uint16_t offset = kStartOffset,
|
||||
const uint16_t nbits = kToshibaACBits,
|
||||
const bool strict = true);
|
||||
#endif
|
||||
#if DECODE_TROTEC
|
||||
bool decodeTrotec(decode_results *results, uint16_t offset = kStartOffset,
|
||||
const uint16_t nbits = kTrotecBits,
|
||||
const bool strict = true);
|
||||
#endif // DECODE_TROTEC
|
||||
#if DECODE_TROTEC_3550
|
||||
bool decodeTrotec3550(decode_results *results, uint16_t offset = kStartOffset,
|
||||
const uint16_t nbits = kTrotecBits,
|
||||
const bool strict = true);
|
||||
#endif // DECODE_TROTEC_3550
|
||||
#if DECODE_MIDEA
|
||||
bool decodeMidea(decode_results *results, uint16_t offset = kStartOffset,
|
||||
const uint16_t nbits = kMideaBits,
|
||||
const bool strict = true);
|
||||
#endif // DECODE_MIDEA
|
||||
#if DECODE_MIDEA24
|
||||
bool decodeMidea24(decode_results *results, uint16_t offset = kStartOffset,
|
||||
const uint16_t nbits = kMidea24Bits,
|
||||
const bool strict = true);
|
||||
#endif // DECODE_MIDEA24
|
||||
#if DECODE_FUJITSU_AC
|
||||
bool decodeFujitsuAC(decode_results *results, uint16_t offset = kStartOffset,
|
||||
const uint16_t nbits = kFujitsuAcBits,
|
||||
const bool strict = false);
|
||||
#endif
|
||||
#if DECODE_LASERTAG
|
||||
bool decodeLasertag(decode_results *results, uint16_t offset = kStartOffset,
|
||||
const uint16_t nbits = kLasertagBits,
|
||||
const bool strict = true);
|
||||
#endif
|
||||
#if DECODE_MILESTAG2
|
||||
bool decodeMilestag2(decode_results *results, uint16_t offset = kStartOffset,
|
||||
const uint16_t nbits = kMilesTag2ShotBits,
|
||||
const bool strict = true);
|
||||
#endif
|
||||
#if DECODE_CARRIER_AC
|
||||
bool decodeCarrierAC(decode_results *results, uint16_t offset = kStartOffset,
|
||||
const uint16_t nbits = kCarrierAcBits,
|
||||
const bool strict = true);
|
||||
#endif // DECODE_CARRIER_AC
|
||||
#if DECODE_CARRIER_AC40
|
||||
bool decodeCarrierAC40(decode_results *results,
|
||||
uint16_t offset = kStartOffset,
|
||||
const uint16_t nbits = kCarrierAc40Bits,
|
||||
const bool strict = true);
|
||||
#endif // DECODE_CARRIER_AC40
|
||||
#if DECODE_CARRIER_AC84
|
||||
bool decodeCarrierAC84(decode_results *results,
|
||||
uint16_t offset = kStartOffset,
|
||||
const uint16_t nbits = kCarrierAc84Bits,
|
||||
const bool strict = true);
|
||||
#endif // DECODE_CARRIER_AC84
|
||||
#if DECODE_CARRIER_AC64
|
||||
bool decodeCarrierAC64(decode_results *results,
|
||||
uint16_t offset = kStartOffset,
|
||||
const uint16_t nbits = kCarrierAc64Bits,
|
||||
const bool strict = true);
|
||||
#endif // DECODE_CARRIER_AC64
|
||||
#if DECODE_CARRIER_AC128
|
||||
bool decodeCarrierAC128(decode_results *results,
|
||||
uint16_t offset = kStartOffset,
|
||||
const uint16_t nbits = kCarrierAc128Bits,
|
||||
const bool strict = true);
|
||||
#endif // DECODE_CARRIER_AC128
|
||||
#if DECODE_GOODWEATHER
|
||||
bool decodeGoodweather(decode_results *results,
|
||||
uint16_t offset = kStartOffset,
|
||||
const uint16_t nbits = kGoodweatherBits,
|
||||
const bool strict = true);
|
||||
#endif // DECODE_GOODWEATHER
|
||||
#if DECODE_GORENJE
|
||||
bool decodeGorenje(decode_results *results, uint16_t offset = kStartOffset,
|
||||
const uint16_t nbits = kGorenjeBits,
|
||||
const bool strict = true);
|
||||
#endif // DECODE_GORENJE
|
||||
#if DECODE_GREE
|
||||
bool decodeGree(decode_results *results, uint16_t offset = kStartOffset,
|
||||
const uint16_t nbits = kGreeBits,
|
||||
const bool strict = true);
|
||||
#endif
|
||||
#if (DECODE_HAIER_AC | DECODE_HAIER_AC_YRW02 || DECODE_HAIER_AC160 || \
|
||||
DECODE_HAIER_AC176)
|
||||
bool decodeHaierAC(decode_results *results, uint16_t offset = kStartOffset,
|
||||
const uint16_t nbits = kHaierACBits,
|
||||
const bool strict = true);
|
||||
#endif
|
||||
#if DECODE_HAIER_AC_YRW02
|
||||
bool decodeHaierACYRW02(decode_results *results,
|
||||
uint16_t offset = kStartOffset,
|
||||
const uint16_t nbits = kHaierACYRW02Bits,
|
||||
const bool strict = true);
|
||||
#endif
|
||||
#if DECODE_HAIER_AC160
|
||||
bool decodeHaierAC160(decode_results *results,
|
||||
uint16_t offset = kStartOffset,
|
||||
const uint16_t nbits = kHaierAC160Bits,
|
||||
const bool strict = true);
|
||||
#endif // DECODE_HAIER_AC160
|
||||
#if DECODE_HAIER_AC176
|
||||
bool decodeHaierAC176(decode_results *results,
|
||||
uint16_t offset = kStartOffset,
|
||||
const uint16_t nbits = kHaierAC176Bits,
|
||||
const bool strict = true);
|
||||
#endif // DECODE_HAIER_AC176
|
||||
#if (DECODE_HITACHI_AC || DECODE_HITACHI_AC2 || DECODE_HITACHI_AC264 || \
|
||||
DECODE_HITACHI_AC344)
|
||||
bool decodeHitachiAC(decode_results *results, uint16_t offset = kStartOffset,
|
||||
const uint16_t nbits = kHitachiAcBits,
|
||||
const bool strict = true, const bool MSBfirst = true);
|
||||
#endif // (DECODE_HITACHI_AC || DECODE_HITACHI_AC2 || DECODE_HITACHI_AC264 ||
|
||||
// DECODE_HITACHI_AC344)
|
||||
#if DECODE_HITACHI_AC1
|
||||
bool decodeHitachiAC1(decode_results *results, uint16_t offset = kStartOffset,
|
||||
const uint16_t nbits = kHitachiAc1Bits,
|
||||
const bool strict = true);
|
||||
#endif
|
||||
#if DECODE_HITACHI_AC3
|
||||
bool decodeHitachiAc3(decode_results *results,
|
||||
uint16_t offset = kStartOffset,
|
||||
const uint16_t nbits = kHitachiAc3Bits,
|
||||
const bool strict = true);
|
||||
#endif // DECODE_HITACHI_AC3
|
||||
#if DECODE_HITACHI_AC296
|
||||
bool decodeHitachiAc296(decode_results *results,
|
||||
uint16_t offset = kStartOffset,
|
||||
const uint16_t nbits = kHitachiAc296Bits,
|
||||
const bool strict = true);
|
||||
#endif // DECODE_HITACHI_AC296
|
||||
#if DECODE_HITACHI_AC424
|
||||
bool decodeHitachiAc424(decode_results *results,
|
||||
uint16_t offset = kStartOffset,
|
||||
const uint16_t nbits = kHitachiAc424Bits,
|
||||
const bool strict = true);
|
||||
#endif // DECODE_HITACHI_AC424
|
||||
#if DECODE_GICABLE
|
||||
bool decodeGICable(decode_results *results, uint16_t offset = kStartOffset,
|
||||
const uint16_t nbits = kGicableBits,
|
||||
const bool strict = true);
|
||||
#endif
|
||||
#if DECODE_WHIRLPOOL_AC
|
||||
bool decodeWhirlpoolAC(decode_results *results,
|
||||
uint16_t offset = kStartOffset,
|
||||
const uint16_t nbits = kWhirlpoolAcBits,
|
||||
const bool strict = true);
|
||||
#endif
|
||||
#if DECODE_LUTRON
|
||||
bool decodeLutron(decode_results *results, uint16_t offset = kStartOffset,
|
||||
const uint16_t nbits = kLutronBits,
|
||||
const bool strict = true);
|
||||
#endif
|
||||
#if DECODE_ELECTRA_AC
|
||||
bool decodeElectraAC(decode_results *results, uint16_t offset = kStartOffset,
|
||||
const uint16_t nbits = kElectraAcBits,
|
||||
const bool strict = true);
|
||||
#endif
|
||||
#if DECODE_PANASONIC_AC
|
||||
bool decodePanasonicAC(decode_results *results,
|
||||
uint16_t offset = kStartOffset,
|
||||
const uint16_t nbits = kPanasonicAcBits,
|
||||
const bool strict = true);
|
||||
#endif // DECODE_PANASONIC_AC
|
||||
#if DECODE_PANASONIC_AC32
|
||||
bool decodePanasonicAC32(decode_results *results,
|
||||
uint16_t offset = kStartOffset,
|
||||
const uint16_t nbits = kPanasonicAc32Bits,
|
||||
const bool strict = true);
|
||||
#endif // DECODE_PANASONIC_AC32
|
||||
#if DECODE_PIONEER
|
||||
bool decodePioneer(decode_results *results, uint16_t offset = kStartOffset,
|
||||
const uint16_t nbits = kPioneerBits,
|
||||
const bool strict = true);
|
||||
#endif
|
||||
#if DECODE_MWM
|
||||
bool decodeMWM(decode_results *results, uint16_t offset = kStartOffset,
|
||||
const uint16_t nbits = 24,
|
||||
const bool strict = true);
|
||||
#endif
|
||||
#if DECODE_VESTEL_AC
|
||||
bool decodeVestelAc(decode_results *results, uint16_t offset = kStartOffset,
|
||||
const uint16_t nbits = kVestelAcBits,
|
||||
const bool strict = true);
|
||||
#endif
|
||||
#if DECODE_TECO
|
||||
bool decodeTeco(decode_results *results, uint16_t offset = kStartOffset,
|
||||
const uint16_t nbits = kTecoBits,
|
||||
const bool strict = false);
|
||||
#endif
|
||||
#if DECODE_LEGOPF
|
||||
bool decodeLegoPf(decode_results *results, uint16_t offset = kStartOffset,
|
||||
const uint16_t nbits = kLegoPfBits,
|
||||
const bool strict = true);
|
||||
#endif
|
||||
#if DECODE_NEOCLIMA
|
||||
bool decodeNeoclima(decode_results *results, uint16_t offset = kStartOffset,
|
||||
const uint16_t nbits = kNeoclimaBits,
|
||||
const bool strict = true);
|
||||
#endif // DECODE_NEOCLIMA
|
||||
#if DECODE_AMCOR
|
||||
bool decodeAmcor(decode_results *results, uint16_t offset = kStartOffset,
|
||||
const uint16_t nbits = kAmcorBits,
|
||||
const bool strict = true);
|
||||
#endif // DECODE_AMCOR
|
||||
#if DECODE_EPSON
|
||||
bool decodeEpson(decode_results *results, uint16_t offset = kStartOffset,
|
||||
const uint16_t nbits = kEpsonBits,
|
||||
const bool strict = true);
|
||||
#endif // DECODE_EPSON
|
||||
#if DECODE_SYMPHONY
|
||||
bool decodeSymphony(decode_results *results, uint16_t offset = kStartOffset,
|
||||
const uint16_t nbits = kSymphonyBits,
|
||||
const bool strict = true);
|
||||
#endif // DECODE_SYMPHONY
|
||||
#if DECODE_AIRWELL
|
||||
bool decodeAirwell(decode_results *results, uint16_t offset = kStartOffset,
|
||||
const uint16_t nbits = kAirwellBits,
|
||||
const bool strict = true);
|
||||
#endif // DECODE_AIRWELL
|
||||
#if DECODE_DELONGHI_AC
|
||||
bool decodeDelonghiAc(decode_results *results, uint16_t offset = kStartOffset,
|
||||
const uint16_t nbits = kDelonghiAcBits,
|
||||
const bool strict = true);
|
||||
#endif // DECODE_DELONGHI_AC
|
||||
#if DECODE_DOSHISHA
|
||||
bool decodeDoshisha(decode_results *results, uint16_t offset = kStartOffset,
|
||||
const uint16_t nbits = kDoshishaBits,
|
||||
const bool strict = true);
|
||||
#endif // DECODE_DOSHISHA
|
||||
#if DECODE_MULTIBRACKETS
|
||||
bool decodeMultibrackets(decode_results *results,
|
||||
uint16_t offset = kStartOffset,
|
||||
const uint16_t nbits = kMultibracketsBits,
|
||||
const bool strict = true);
|
||||
#endif // DECODE_MULTIBRACKETS
|
||||
#if DECODE_TECHNIBEL_AC
|
||||
bool decodeTechnibelAc(decode_results *results,
|
||||
uint16_t offset = kStartOffset,
|
||||
const uint16_t nbits = kTechnibelAcBits,
|
||||
const bool strict = true);
|
||||
#endif // DECODE_TECHNIBEL_AC
|
||||
#if DECODE_CORONA_AC
|
||||
bool decodeCoronaAc(decode_results *results, uint16_t offset = kStartOffset,
|
||||
const uint16_t nbits = kCoronaAcBitsShort,
|
||||
const bool strict = true);
|
||||
#endif // DECODE_CORONA_AC
|
||||
#if DECODE_ZEPEAL
|
||||
bool decodeZepeal(decode_results *results, uint16_t offset = kStartOffset,
|
||||
const uint16_t nbits = kZepealBits,
|
||||
const bool strict = true);
|
||||
#endif // DECODE_ZEPEAL
|
||||
#if DECODE_METZ
|
||||
bool decodeMetz(decode_results *results, uint16_t offset = kStartOffset,
|
||||
const uint16_t nbits = kMetzBits,
|
||||
const bool strict = true);
|
||||
#endif // DECODE_METZ
|
||||
#if DECODE_TRANSCOLD
|
||||
bool decodeTranscold(decode_results *results, uint16_t offset = kStartOffset,
|
||||
const uint16_t nbits = kTranscoldBits,
|
||||
const bool strict = true);
|
||||
#endif // DECODE_TRANSCOLD
|
||||
#if DECODE_MIRAGE
|
||||
bool decodeMirage(decode_results *results,
|
||||
uint16_t offset = kStartOffset,
|
||||
const uint16_t nbits = kMirageBits,
|
||||
const bool strict = true);
|
||||
#endif // DECODE_MIRAGE
|
||||
#if DECODE_ELITESCREENS
|
||||
bool decodeElitescreens(decode_results *results,
|
||||
uint16_t offset = kStartOffset,
|
||||
const uint16_t nbits = kEliteScreensBits,
|
||||
const bool strict = true);
|
||||
#endif // DECODE_ELITESCREENS
|
||||
#if DECODE_ECOCLIM
|
||||
bool decodeEcoclim(decode_results *results, uint16_t offset = kStartOffset,
|
||||
const uint16_t nbits = kEcoclimBits,
|
||||
const bool strict = true);
|
||||
#endif // DECODE_ECOCLIM
|
||||
#if DECODE_XMP
|
||||
bool decodeXmp(decode_results *results, uint16_t offset = kStartOffset,
|
||||
const uint16_t nbits = kXmpBits, const bool strict = true);
|
||||
#endif // DECODE_XMP
|
||||
#if DECODE_TRUMA
|
||||
bool decodeTruma(decode_results *results, uint16_t offset = kStartOffset,
|
||||
const uint16_t nbits = kTrumaBits, const bool strict = true);
|
||||
#endif // DECODE_TRUMA
|
||||
#if DECODE_TEKNOPOINT
|
||||
bool decodeTeknopoint(decode_results *results, uint16_t offset = kStartOffset,
|
||||
const uint16_t nbits = kTeknopointBits,
|
||||
const bool strict = true);
|
||||
#endif // DECODE_TEKNOPOINT
|
||||
#if DECODE_KELON
|
||||
bool decodeKelon(decode_results *results, uint16_t offset = kStartOffset,
|
||||
const uint16_t nbits = kKelonBits, const bool strict = true);
|
||||
#endif // DECODE_KELON
|
||||
#if DECODE_KELON168
|
||||
bool decodeKelon168(decode_results *results, uint16_t offset = kStartOffset,
|
||||
const uint16_t nbits = kKelon168Bits,
|
||||
const bool strict = true);
|
||||
#endif // DECODE_KELON168
|
||||
#if DECODE_BOSE
|
||||
bool decodeBose(decode_results *results, uint16_t offset = kStartOffset,
|
||||
const uint16_t nbits = kBoseBits, const bool strict = true);
|
||||
#endif // DECODE_BOSE
|
||||
#if DECODE_RHOSS
|
||||
bool decodeRhoss(decode_results *results, uint16_t offset = kStartOffset,
|
||||
const uint16_t nbits = kRhossBits, const bool strict = true);
|
||||
#endif // DECODE_RHOSS
|
||||
#if DECODE_AIRTON
|
||||
bool decodeAirton(decode_results *results, uint16_t offset = kStartOffset,
|
||||
const uint16_t nbits = kAirtonBits,
|
||||
const bool strict = true);
|
||||
#endif // DECODE_AIRTON
|
||||
#if DECODE_TOTO
|
||||
bool decodeToto(decode_results *results, uint16_t offset = kStartOffset,
|
||||
const uint16_t nbits = kTotoBits,
|
||||
const bool strict = true);
|
||||
#endif // DECODE_TOTO
|
||||
#if DECODE_CLIMABUTLER
|
||||
bool decodeClimaButler(decode_results *results,
|
||||
uint16_t offset = kStartOffset,
|
||||
const uint16_t nbits = kClimaButlerBits,
|
||||
const bool strict = true);
|
||||
#endif // DECODE_CLIMABUTLER
|
||||
#if DECODE_TCL96AC
|
||||
bool decodeTcl96Ac(decode_results *results,
|
||||
uint16_t offset = kStartOffset,
|
||||
const uint16_t nbits = kTcl96AcBits,
|
||||
const bool strict = true);
|
||||
#endif // DECODE_TCL96AC
|
||||
#if DECODE_BOSCH144
|
||||
bool decodeBosch144(decode_results *results,
|
||||
uint16_t offset = kStartOffset,
|
||||
const uint16_t nbits = kBosch144Bits,
|
||||
const bool strict = true);
|
||||
#endif // DECODE_BOSCH144
|
||||
#if DECODE_WOWWEE
|
||||
bool decodeWowwee(decode_results *results,
|
||||
uint16_t offset = kStartOffset,
|
||||
const uint16_t nbits = kWowweeBits,
|
||||
const bool strict = true);
|
||||
#endif // DECODE_WOWWEE
|
||||
#if DECODE_YORK
|
||||
bool decodeYork(decode_results *results,
|
||||
uint16_t kStartOffset,
|
||||
const uint16_t kYorkBits,
|
||||
const bool strict = true);
|
||||
#endif // DECODE_YORK
|
||||
};
|
||||
|
||||
#endif // IRRECV_H_
|
||||
1524
yoRadio/src/IRremoteESP8266/IRremoteESP8266.h
Normal file
1524
yoRadio/src/IRremoteESP8266/IRremoteESP8266.h
Normal file
File diff suppressed because it is too large
Load Diff
1441
yoRadio/src/IRremoteESP8266/IRsend.cpp
Normal file
1441
yoRadio/src/IRremoteESP8266/IRsend.cpp
Normal file
File diff suppressed because it is too large
Load Diff
921
yoRadio/src/IRremoteESP8266/IRsend.h
Normal file
921
yoRadio/src/IRremoteESP8266/IRsend.h
Normal file
@@ -0,0 +1,921 @@
|
||||
// Copyright 2009 Ken Shirriff
|
||||
// Copyright 2015 Mark Szabo
|
||||
// Copyright 2017 David Conran
|
||||
#ifndef IRSEND_H_
|
||||
#define IRSEND_H_
|
||||
|
||||
#define __STDC_LIMIT_MACROS
|
||||
#include <stdint.h>
|
||||
#include "IRremoteESP8266.h"
|
||||
|
||||
// Originally from https://github.com/shirriff/Arduino-IRremote/
|
||||
// Updated by markszabo (https://github.com/crankyoldgit/IRremoteESP8266) for
|
||||
// sending IR code on ESP8266
|
||||
|
||||
#if TEST || UNIT_TEST
|
||||
#define VIRTUAL virtual
|
||||
#else
|
||||
#define VIRTUAL
|
||||
#endif
|
||||
|
||||
// Constants
|
||||
// Offset (in microseconds) to use in Period time calculations to account for
|
||||
// code excution time in producing the software PWM signal.
|
||||
#if defined(ESP32)
|
||||
// Calculated on a generic ESP-WROOM-32 board with v3.2-18 SDK @ 240MHz
|
||||
const int8_t kPeriodOffset = -2;
|
||||
#elif (defined(ESP8266) && F_CPU == 160000000L) // NOLINT(whitespace/parens)
|
||||
// Calculated on an ESP8266 NodeMCU v2 board using:
|
||||
// v2.6.0 with v2.5.2 ESP core @ 160MHz
|
||||
const int8_t kPeriodOffset = -2;
|
||||
#else // (defined(ESP8266) && F_CPU == 160000000L)
|
||||
// Calculated on ESP8266 Wemos D1 mini using v2.4.1 with v2.4.0 ESP core @ 40MHz
|
||||
const int8_t kPeriodOffset = -5;
|
||||
#endif // (defined(ESP8266) && F_CPU == 160000000L)
|
||||
const uint8_t kDutyDefault = 50; // Percentage
|
||||
const uint8_t kDutyMax = 100; // Percentage
|
||||
// delayMicroseconds() is only accurate to 16383us.
|
||||
// Ref: https://www.arduino.cc/en/Reference/delayMicroseconds
|
||||
const uint16_t kMaxAccurateUsecDelay = 16383;
|
||||
// Usecs to wait between messages we don't know the proper gap time.
|
||||
const uint32_t kDefaultMessageGap = 100000;
|
||||
/// Placeholder for missing sensor temp value
|
||||
/// @note Not using "-1" as it may be a valid external temp
|
||||
const float kNoTempValue = -100.0;
|
||||
|
||||
/// Enumerators and Structures for the Common A/C API.
|
||||
namespace stdAc {
|
||||
/// Common A/C settings for A/C operating modes.
|
||||
enum class opmode_t {
|
||||
kOff = -1,
|
||||
kAuto = 0,
|
||||
kCool = 1,
|
||||
kHeat = 2,
|
||||
kDry = 3,
|
||||
kFan = 4,
|
||||
// Add new entries before this one, and update it to point to the last entry
|
||||
kLastOpmodeEnum = kFan,
|
||||
};
|
||||
|
||||
/// Common A/C settings for Fan Speeds.
|
||||
enum class fanspeed_t {
|
||||
kAuto = 0,
|
||||
kMin = 1,
|
||||
kLow = 2,
|
||||
kMedium = 3,
|
||||
kHigh = 4,
|
||||
kMax = 5,
|
||||
kMediumHigh = 6,
|
||||
// Add new entries before this one, and update it to point to the last entry
|
||||
kLastFanspeedEnum = kMediumHigh,
|
||||
};
|
||||
|
||||
/// Common A/C settings for Vertical Swing.
|
||||
enum class swingv_t {
|
||||
kOff = -1,
|
||||
kAuto = 0,
|
||||
kHighest = 1,
|
||||
kHigh = 2,
|
||||
kMiddle = 3,
|
||||
kLow = 4,
|
||||
kLowest = 5,
|
||||
kUpperMiddle = 6,
|
||||
// Add new entries before this one, and update it to point to the last entry
|
||||
kLastSwingvEnum = kUpperMiddle,
|
||||
};
|
||||
|
||||
/// @brief Tyoe of A/C command (if the remote uses different codes for each)
|
||||
/// @note Most remotes support only a single command or aggregate multiple
|
||||
/// into one (e.g. control+timer). Use @c kControlCommand in such case
|
||||
enum class ac_command_t {
|
||||
kControlCommand = 0,
|
||||
kSensorTempReport = 1,
|
||||
kTimerCommand = 2,
|
||||
kConfigCommand = 3,
|
||||
// Add new entries before this one, and update it to point to the last entry
|
||||
kLastAcCommandEnum = kConfigCommand,
|
||||
};
|
||||
|
||||
/// Common A/C settings for Horizontal Swing.
|
||||
enum class swingh_t {
|
||||
kOff = -1,
|
||||
kAuto = 0, // a.k.a. On.
|
||||
kLeftMax = 1,
|
||||
kLeft = 2,
|
||||
kMiddle = 3,
|
||||
kRight = 4,
|
||||
kRightMax = 5,
|
||||
kWide = 6, // a.k.a. left & right at the same time.
|
||||
// Add new entries before this one, and update it to point to the last entry
|
||||
kLastSwinghEnum = kWide,
|
||||
};
|
||||
|
||||
/// Structure to hold a common A/C state.
|
||||
struct state_t {
|
||||
decode_type_t protocol = decode_type_t::UNKNOWN;
|
||||
int16_t model = -1; // `-1` means unused.
|
||||
bool power = false;
|
||||
stdAc::opmode_t mode = stdAc::opmode_t::kOff;
|
||||
float degrees = 25;
|
||||
bool celsius = true;
|
||||
stdAc::fanspeed_t fanspeed = stdAc::fanspeed_t::kAuto;
|
||||
stdAc::swingv_t swingv = stdAc::swingv_t::kOff;
|
||||
stdAc::swingh_t swingh = stdAc::swingh_t::kOff;
|
||||
bool quiet = false;
|
||||
bool turbo = false;
|
||||
bool econo = false;
|
||||
bool light = false;
|
||||
bool filter = false;
|
||||
bool clean = false;
|
||||
bool beep = false;
|
||||
int16_t sleep = -1; // `-1` means off.
|
||||
int16_t clock = -1; // `-1` means not set.
|
||||
stdAc::ac_command_t command = stdAc::ac_command_t::kControlCommand;
|
||||
bool iFeel = false;
|
||||
float sensorTemperature = kNoTempValue; // `kNoTempValue` means not set.
|
||||
};
|
||||
}; // namespace stdAc
|
||||
|
||||
/// Fujitsu A/C model numbers
|
||||
enum fujitsu_ac_remote_model_t {
|
||||
ARRAH2E = 1, ///< (1) AR-RAH2E, AR-RAC1E, AR-RAE1E, AR-RCE1E, AR-RAH2U,
|
||||
///< AR-REG1U (Default)
|
||||
///< Warning: Use on incorrect models can cause the A/C to lock
|
||||
///< up, requring the A/C to be physically powered off to fix.
|
||||
///< e.g. AR-RAH1U may lock up with a Swing command.
|
||||
ARDB1, ///< (2) AR-DB1, AR-DL10 (AR-DL10 swing doesn't work)
|
||||
ARREB1E, ///< (3) AR-REB1E, AR-RAH1U (Similar to ARRAH2E but no horiz
|
||||
///< control)
|
||||
ARJW2, ///< (4) AR-JW2 (Same as ARDB1 but with horiz control)
|
||||
ARRY4, ///< (5) AR-RY4 (Same as AR-RAH2E but with clean & filter)
|
||||
ARREW4E, ///< (6) Similar to ARRAH2E, but with different temp config.
|
||||
};
|
||||
|
||||
/// Gree A/C model numbers
|
||||
enum gree_ac_remote_model_t {
|
||||
YAW1F = 1, // (1) Ultimate, EKOKAI, RusClimate (Default)
|
||||
YBOFB, // (2) Green, YBOFB2, YAPOF3
|
||||
YX1FSF, // (3) Soleus Air window unit (Similar to YAW1F, but with an
|
||||
// Operation mode of Energy Saver (Econo))
|
||||
};
|
||||
|
||||
/// HAIER_AC176 A/C model numbers
|
||||
enum haier_ac176_remote_model_t {
|
||||
V9014557_A = 1, // (1) V9014557 Remote in "A" setting. (Default)
|
||||
V9014557_B, // (2) V9014557 Remote in "B" setting.
|
||||
};
|
||||
|
||||
/// HITACHI_AC1 A/C model numbers
|
||||
enum hitachi_ac1_remote_model_t {
|
||||
R_LT0541_HTA_A = 1, // (1) R-LT0541-HTA Remote in "A" setting. (Default)
|
||||
R_LT0541_HTA_B, // (2) R-LT0541-HTA Remote in "B" setting.
|
||||
};
|
||||
|
||||
/// MIRAGE A/C model numbers
|
||||
enum mirage_ac_remote_model_t {
|
||||
KKG9AC1 = 1, // (1) KKG9A-C1 Remote. (Default)
|
||||
KKG29AC1, // (2) KKG29A-C1 Remote.
|
||||
};
|
||||
|
||||
/// Panasonic A/C model numbers
|
||||
enum panasonic_ac_remote_model_t {
|
||||
kPanasonicUnknown = 0,
|
||||
kPanasonicLke = 1,
|
||||
kPanasonicNke = 2,
|
||||
kPanasonicDke = 3, // PKR too.
|
||||
kPanasonicJke = 4,
|
||||
kPanasonicCkp = 5,
|
||||
kPanasonicRkr = 6,
|
||||
};
|
||||
|
||||
/// Sharp A/C model numbers
|
||||
enum sharp_ac_remote_model_t {
|
||||
A907 = 1,
|
||||
A705 = 2,
|
||||
A903 = 3, // 820 too
|
||||
};
|
||||
|
||||
/// TCL (& Teknopoint) A/C model numbers
|
||||
enum tcl_ac_remote_model_t {
|
||||
TAC09CHSD = 1,
|
||||
GZ055BE1 = 2, // Also Teknopoint GZ01-BEJ0-000
|
||||
};
|
||||
|
||||
/// Voltas A/C model numbers
|
||||
enum voltas_ac_remote_model_t {
|
||||
kVoltasUnknown = 0, // Full Function
|
||||
kVoltas122LZF = 1, // (1) 122LZF (No SwingH support) (Default)
|
||||
};
|
||||
|
||||
/// Whirlpool A/C model numbers
|
||||
enum whirlpool_ac_remote_model_t {
|
||||
DG11J13A = 1, // DG11J1-04 too
|
||||
DG11J191,
|
||||
};
|
||||
|
||||
/// LG A/C model numbers
|
||||
enum lg_ac_remote_model_t {
|
||||
GE6711AR2853M = 1, // (1) LG 28-bit Protocol (default)
|
||||
AKB75215403, // (2) LG2 28-bit Protocol
|
||||
AKB74955603, // (3) LG2 28-bit Protocol variant
|
||||
AKB73757604, // (4) LG2 Variant of AKB74955603
|
||||
LG6711A20083V, // (5) Same as GE6711AR2853M, but only SwingV toggle.
|
||||
};
|
||||
|
||||
/// Argo A/C model numbers
|
||||
enum argo_ac_remote_model_t {
|
||||
SAC_WREM2 = 1, // (1) ARGO WREM2 remote (default)
|
||||
SAC_WREM3 // (2) ARGO WREM3 remote (touch buttons), bit-len vary by cmd
|
||||
};
|
||||
|
||||
// Classes
|
||||
|
||||
/// Class for sending all basic IR protocols.
|
||||
/// @note Originally from https://github.com/shirriff/Arduino-IRremote/
|
||||
/// Updated by markszabo (https://github.com/crankyoldgit/IRremoteESP8266) for
|
||||
/// sending IR code on ESP8266
|
||||
class IRsend {
|
||||
public:
|
||||
explicit IRsend(uint16_t IRsendPin, bool inverted = false,
|
||||
bool use_modulation = true);
|
||||
void begin();
|
||||
void enableIROut(uint32_t freq, uint8_t duty = kDutyDefault);
|
||||
VIRTUAL void _delayMicroseconds(uint32_t usec);
|
||||
VIRTUAL uint16_t mark(uint16_t usec);
|
||||
VIRTUAL void space(uint32_t usec);
|
||||
int8_t calibrate(uint16_t hz = 38000U);
|
||||
void sendRaw(const uint16_t buf[], const uint16_t len, const uint16_t hz);
|
||||
void sendData(uint16_t onemark, uint32_t onespace, uint16_t zeromark,
|
||||
uint32_t zerospace, uint64_t data, uint16_t nbits,
|
||||
bool MSBfirst = true);
|
||||
void sendManchesterData(const uint16_t half_period, const uint64_t data,
|
||||
const uint16_t nbits, const bool MSBfirst = true,
|
||||
const bool GEThomas = true);
|
||||
void sendManchester(const uint16_t headermark, const uint32_t headerspace,
|
||||
const uint16_t half_period, const uint16_t footermark,
|
||||
const uint32_t gap, const uint64_t data,
|
||||
const uint16_t nbits, const uint16_t frequency = 38,
|
||||
const bool MSBfirst = true,
|
||||
const uint16_t repeat = kNoRepeat,
|
||||
const uint8_t dutycycle = kDutyDefault,
|
||||
const bool GEThomas = true);
|
||||
void sendGeneric(const uint16_t headermark, const uint32_t headerspace,
|
||||
const uint16_t onemark, const uint32_t onespace,
|
||||
const uint16_t zeromark, const uint32_t zerospace,
|
||||
const uint16_t footermark, const uint32_t gap,
|
||||
const uint64_t data, const uint16_t nbits,
|
||||
const uint16_t frequency, const bool MSBfirst,
|
||||
const uint16_t repeat, const uint8_t dutycycle);
|
||||
void sendGeneric(const uint16_t headermark, const uint32_t headerspace,
|
||||
const uint16_t onemark, const uint32_t onespace,
|
||||
const uint16_t zeromark, const uint32_t zerospace,
|
||||
const uint16_t footermark, const uint32_t gap,
|
||||
const uint32_t mesgtime, const uint64_t data,
|
||||
const uint16_t nbits, const uint16_t frequency,
|
||||
const bool MSBfirst, const uint16_t repeat,
|
||||
const uint8_t dutycycle);
|
||||
void sendGeneric(const uint16_t headermark, const uint32_t headerspace,
|
||||
const uint16_t onemark, const uint32_t onespace,
|
||||
const uint16_t zeromark, const uint32_t zerospace,
|
||||
const uint16_t footermark, const uint32_t gap,
|
||||
const uint8_t *dataptr, const uint16_t nbytes,
|
||||
const uint16_t frequency, const bool MSBfirst,
|
||||
const uint16_t repeat, const uint8_t dutycycle);
|
||||
static uint16_t minRepeats(const decode_type_t protocol);
|
||||
static uint16_t defaultBits(const decode_type_t protocol);
|
||||
bool send(const decode_type_t type, const uint64_t data,
|
||||
const uint16_t nbits, const uint16_t repeat = kNoRepeat);
|
||||
bool send(const decode_type_t type, const uint8_t *state,
|
||||
const uint16_t nbytes);
|
||||
#if (SEND_NEC || SEND_SHERWOOD || SEND_AIWA_RC_T501 || SEND_SANYO || \
|
||||
SEND_MIDEA24)
|
||||
void sendNEC(uint64_t data, uint16_t nbits = kNECBits,
|
||||
uint16_t repeat = kNoRepeat);
|
||||
uint32_t encodeNEC(uint16_t address, uint16_t command);
|
||||
#endif
|
||||
#if SEND_SONY
|
||||
// sendSony() should typically be called with repeat=2 as Sony devices
|
||||
// expect the code to be sent at least 3 times. (code + 2 repeats = 3 codes)
|
||||
// Legacy use of this procedure was to only send a single code so call it with
|
||||
// repeat=0 for backward compatibility. As of v2.0 it defaults to sending
|
||||
// a Sony command that will be accepted be a device.
|
||||
void sendSony(const uint64_t data, const uint16_t nbits = kSony20Bits,
|
||||
const uint16_t repeat = kSonyMinRepeat);
|
||||
void sendSony38(const uint64_t data, const uint16_t nbits = kSony20Bits,
|
||||
const uint16_t repeat = kSonyMinRepeat + 1);
|
||||
uint32_t encodeSony(const uint16_t nbits, const uint16_t command,
|
||||
const uint16_t address, const uint16_t extended = 0);
|
||||
#endif // SEND_SONY
|
||||
#if SEND_SHERWOOD
|
||||
void sendSherwood(uint64_t data, uint16_t nbits = kSherwoodBits,
|
||||
uint16_t repeat = kSherwoodMinRepeat);
|
||||
#endif
|
||||
// `sendSAMSUNG()` is required by `sendLG()`
|
||||
#if (SEND_SAMSUNG || SEND_LG)
|
||||
void sendSAMSUNG(const uint64_t data, const uint16_t nbits = kSamsungBits,
|
||||
const uint16_t repeat = kNoRepeat);
|
||||
uint32_t encodeSAMSUNG(const uint8_t customer, const uint8_t command);
|
||||
#endif // (SEND_SAMSUNG || SEND_LG)
|
||||
#if SEND_SAMSUNG36
|
||||
void sendSamsung36(const uint64_t data, const uint16_t nbits = kSamsung36Bits,
|
||||
const uint16_t repeat = kNoRepeat);
|
||||
#endif
|
||||
#if SEND_SAMSUNG_AC
|
||||
void sendSamsungAC(const unsigned char data[],
|
||||
const uint16_t nbytes = kSamsungAcStateLength,
|
||||
const uint16_t repeat = kSamsungAcDefaultRepeat);
|
||||
#endif
|
||||
#if SEND_LG
|
||||
void sendLG(uint64_t data, uint16_t nbits = kLgBits,
|
||||
uint16_t repeat = kNoRepeat);
|
||||
void sendLG2(uint64_t data, uint16_t nbits = kLgBits,
|
||||
uint16_t repeat = kNoRepeat);
|
||||
uint32_t encodeLG(uint16_t address, uint16_t command);
|
||||
#endif
|
||||
#if (SEND_SHARP || SEND_DENON)
|
||||
uint32_t encodeSharp(const uint16_t address, const uint16_t command,
|
||||
const uint16_t expansion = 1, const uint16_t check = 0,
|
||||
const bool MSBfirst = false);
|
||||
void sendSharp(const uint16_t address, const uint16_t command,
|
||||
const uint16_t nbits = kSharpBits,
|
||||
const uint16_t repeat = kNoRepeat);
|
||||
void sendSharpRaw(const uint64_t data, const uint16_t nbits = kSharpBits,
|
||||
const uint16_t repeat = kNoRepeat);
|
||||
#endif
|
||||
#if SEND_SHARP_AC
|
||||
void sendSharpAc(const unsigned char data[],
|
||||
const uint16_t nbytes = kSharpAcStateLength,
|
||||
const uint16_t repeat = kSharpAcDefaultRepeat);
|
||||
#endif // SEND_SHARP_AC
|
||||
#if SEND_JVC
|
||||
void sendJVC(uint64_t data, uint16_t nbits = kJvcBits,
|
||||
uint16_t repeat = kNoRepeat);
|
||||
uint16_t encodeJVC(uint8_t address, uint8_t command);
|
||||
#endif
|
||||
#if SEND_DENON
|
||||
void sendDenon(uint64_t data, uint16_t nbits = kDenonBits,
|
||||
uint16_t repeat = kNoRepeat);
|
||||
#endif
|
||||
#if SEND_SANYO
|
||||
uint64_t encodeSanyoLC7461(uint16_t address, uint8_t command);
|
||||
void sendSanyoLC7461(const uint64_t data,
|
||||
const uint16_t nbits = kSanyoLC7461Bits,
|
||||
const uint16_t repeat = kNoRepeat);
|
||||
#endif
|
||||
#if SEND_SANYO_AC
|
||||
void sendSanyoAc(const uint8_t *data,
|
||||
const uint16_t nbytes = kSanyoAcStateLength,
|
||||
const uint16_t repeat = kNoRepeat);
|
||||
#endif // SEND_SANYO_AC
|
||||
#if SEND_SANYO_AC88
|
||||
void sendSanyoAc88(const uint8_t *data,
|
||||
const uint16_t nbytes = kSanyoAc88StateLength,
|
||||
const uint16_t repeat = kSanyoAc88MinRepeat);
|
||||
#endif // SEND_SANYO_AC88
|
||||
#if SEND_SANYO_AC152
|
||||
void sendSanyoAc152(const uint8_t *data,
|
||||
const uint16_t nbytes = kSanyoAc152StateLength,
|
||||
const uint16_t repeat = kSanyoAc152MinRepeat);
|
||||
#endif // SEND_SANYO_AC152
|
||||
#if SEND_DISH
|
||||
// sendDISH() should typically be called with repeat=3 as DISH devices
|
||||
// expect the code to be sent at least 4 times. (code + 3 repeats = 4 codes)
|
||||
// Legacy use of this procedure was only to send a single code
|
||||
// so use repeat=0 for backward compatibility.
|
||||
void sendDISH(uint64_t data, uint16_t nbits = kDishBits,
|
||||
uint16_t repeat = kDishMinRepeat);
|
||||
#endif
|
||||
#if (SEND_PANASONIC || SEND_DENON)
|
||||
void sendPanasonic64(const uint64_t data,
|
||||
const uint16_t nbits = kPanasonicBits,
|
||||
const uint16_t repeat = kNoRepeat);
|
||||
void sendPanasonic(const uint16_t address, const uint32_t data,
|
||||
const uint16_t nbits = kPanasonicBits,
|
||||
const uint16_t repeat = kNoRepeat);
|
||||
uint64_t encodePanasonic(const uint16_t manufacturer, const uint8_t device,
|
||||
const uint8_t subdevice, const uint8_t function);
|
||||
#endif
|
||||
#if SEND_RC5
|
||||
void sendRC5(const uint64_t data, uint16_t nbits = kRC5XBits,
|
||||
const uint16_t repeat = kNoRepeat);
|
||||
uint16_t encodeRC5(const uint8_t address, const uint8_t command,
|
||||
const bool key_released = false);
|
||||
uint16_t encodeRC5X(const uint8_t address, const uint8_t command,
|
||||
const bool key_released = false);
|
||||
uint64_t toggleRC5(const uint64_t data);
|
||||
#endif
|
||||
#if SEND_RC6
|
||||
void sendRC6(const uint64_t data, const uint16_t nbits = kRC6Mode0Bits,
|
||||
const uint16_t repeat = kNoRepeat);
|
||||
uint64_t encodeRC6(const uint32_t address, const uint8_t command,
|
||||
const uint16_t mode = kRC6Mode0Bits);
|
||||
uint64_t toggleRC6(const uint64_t data, const uint16_t nbits = kRC6Mode0Bits);
|
||||
#endif
|
||||
#if SEND_RCMM
|
||||
void sendRCMM(uint64_t data, uint16_t nbits = kRCMMBits,
|
||||
uint16_t repeat = kNoRepeat);
|
||||
#endif
|
||||
#if SEND_COOLIX
|
||||
void sendCOOLIX(const uint64_t data, const uint16_t nbits = kCoolixBits,
|
||||
const uint16_t repeat = kCoolixDefaultRepeat);
|
||||
#endif // SEND_COOLIX
|
||||
#if SEND_COOLIX48
|
||||
void sendCoolix48(const uint64_t data, const uint16_t nbits = kCoolix48Bits,
|
||||
const uint16_t repeat = kCoolixDefaultRepeat);
|
||||
#endif // SEND_COOLIX48
|
||||
#if SEND_WHYNTER
|
||||
void sendWhynter(const uint64_t data, const uint16_t nbits = kWhynterBits,
|
||||
const uint16_t repeat = kNoRepeat);
|
||||
#endif
|
||||
#if SEND_MIRAGE
|
||||
void sendMirage(const unsigned char data[],
|
||||
const uint16_t nbytes = kMirageStateLength,
|
||||
const uint16_t repeat = kMirageMinRepeat);
|
||||
#endif // SEND_MIRAGE
|
||||
#if SEND_MITSUBISHI
|
||||
void sendMitsubishi(uint64_t data, uint16_t nbits = kMitsubishiBits,
|
||||
uint16_t repeat = kMitsubishiMinRepeat);
|
||||
#endif
|
||||
#if SEND_MITSUBISHI136
|
||||
void sendMitsubishi136(const unsigned char data[],
|
||||
const uint16_t nbytes = kMitsubishi136StateLength,
|
||||
const uint16_t repeat = kMitsubishi136MinRepeat);
|
||||
#endif
|
||||
#if SEND_MITSUBISHI112
|
||||
void sendMitsubishi112(const unsigned char data[],
|
||||
const uint16_t nbytes = kMitsubishi112StateLength,
|
||||
const uint16_t repeat = kMitsubishi112MinRepeat);
|
||||
#endif
|
||||
#if SEND_MITSUBISHI2
|
||||
void sendMitsubishi2(uint64_t data, uint16_t nbits = kMitsubishiBits,
|
||||
uint16_t repeat = kMitsubishiMinRepeat);
|
||||
#endif
|
||||
#if SEND_MITSUBISHI_AC
|
||||
void sendMitsubishiAC(const unsigned char data[],
|
||||
const uint16_t nbytes = kMitsubishiACStateLength,
|
||||
const uint16_t repeat = kMitsubishiACMinRepeat);
|
||||
#endif
|
||||
#if SEND_MITSUBISHIHEAVY
|
||||
void sendMitsubishiHeavy88(
|
||||
const unsigned char data[],
|
||||
const uint16_t nbytes = kMitsubishiHeavy88StateLength,
|
||||
const uint16_t repeat = kMitsubishiHeavy88MinRepeat);
|
||||
void sendMitsubishiHeavy152(
|
||||
const unsigned char data[],
|
||||
const uint16_t nbytes = kMitsubishiHeavy152StateLength,
|
||||
const uint16_t repeat = kMitsubishiHeavy152MinRepeat);
|
||||
#endif
|
||||
#if SEND_FUJITSU_AC
|
||||
void sendFujitsuAC(const unsigned char data[], const uint16_t nbytes,
|
||||
const uint16_t repeat = kFujitsuAcMinRepeat);
|
||||
#endif
|
||||
#if SEND_INAX
|
||||
void sendInax(const uint64_t data, const uint16_t nbits = kInaxBits,
|
||||
const uint16_t repeat = kInaxMinRepeat);
|
||||
#endif // SEND_INAX
|
||||
#if SEND_GLOBALCACHE
|
||||
void sendGC(uint16_t buf[], uint16_t len);
|
||||
#endif
|
||||
#if SEND_KELVINATOR
|
||||
void sendKelvinator(const unsigned char data[],
|
||||
const uint16_t nbytes = kKelvinatorStateLength,
|
||||
const uint16_t repeat = kKelvinatorDefaultRepeat);
|
||||
#endif
|
||||
#if SEND_DAIKIN
|
||||
void sendDaikin(const unsigned char data[],
|
||||
const uint16_t nbytes = kDaikinStateLength,
|
||||
const uint16_t repeat = kDaikinDefaultRepeat);
|
||||
#endif
|
||||
#if SEND_DAIKIN64
|
||||
void sendDaikin64(const uint64_t data, const uint16_t nbits = kDaikin64Bits,
|
||||
const uint16_t repeat = kDaikin64DefaultRepeat);
|
||||
#endif // SEND_DAIKIN64
|
||||
#if SEND_DAIKIN128
|
||||
void sendDaikin128(const unsigned char data[],
|
||||
const uint16_t nbytes = kDaikin128StateLength,
|
||||
const uint16_t repeat = kDaikin128DefaultRepeat);
|
||||
#endif // SEND_DAIKIN128
|
||||
#if SEND_DAIKIN152
|
||||
void sendDaikin152(const unsigned char data[],
|
||||
const uint16_t nbytes = kDaikin152StateLength,
|
||||
const uint16_t repeat = kDaikin152DefaultRepeat);
|
||||
#endif // SEND_DAIKIN152
|
||||
#if SEND_DAIKIN160
|
||||
void sendDaikin160(const unsigned char data[],
|
||||
const uint16_t nbytes = kDaikin160StateLength,
|
||||
const uint16_t repeat = kDaikin160DefaultRepeat);
|
||||
#endif // SEND_DAIKIN160
|
||||
#if SEND_DAIKIN176
|
||||
void sendDaikin176(const unsigned char data[],
|
||||
const uint16_t nbytes = kDaikin176StateLength,
|
||||
const uint16_t repeat = kDaikin176DefaultRepeat);
|
||||
#endif // SEND_DAIKIN176
|
||||
#if SEND_DAIKIN2
|
||||
void sendDaikin2(const unsigned char data[],
|
||||
const uint16_t nbytes = kDaikin2StateLength,
|
||||
const uint16_t repeat = kDaikin2DefaultRepeat);
|
||||
#endif
|
||||
#if SEND_DAIKIN200
|
||||
void sendDaikin200(const unsigned char data[],
|
||||
const uint16_t nbytes = kDaikin200StateLength,
|
||||
const uint16_t repeat = kDaikin200DefaultRepeat);
|
||||
#endif // SEND_DAIKIN200
|
||||
#if SEND_DAIKIN216
|
||||
void sendDaikin216(const unsigned char data[],
|
||||
const uint16_t nbytes = kDaikin216StateLength,
|
||||
const uint16_t repeat = kDaikin216DefaultRepeat);
|
||||
#endif // SEND_DAIKIN216
|
||||
#if SEND_DAIKIN312
|
||||
void sendDaikin312(const unsigned char data[],
|
||||
const uint16_t nbytes = kDaikin312StateLength,
|
||||
const uint16_t repeat = kDaikin312DefaultRepeat);
|
||||
#endif // SEND_DAIKIN312
|
||||
#if SEND_AIWA_RC_T501
|
||||
void sendAiwaRCT501(uint64_t data, uint16_t nbits = kAiwaRcT501Bits,
|
||||
uint16_t repeat = kAiwaRcT501MinRepeats);
|
||||
#endif
|
||||
#if SEND_GREE
|
||||
void sendGree(const uint64_t data, const uint16_t nbits = kGreeBits,
|
||||
const uint16_t repeat = kGreeDefaultRepeat);
|
||||
void sendGree(const uint8_t data[], const uint16_t nbytes = kGreeStateLength,
|
||||
const uint16_t repeat = kGreeDefaultRepeat);
|
||||
#endif
|
||||
#if SEND_GOODWEATHER
|
||||
void sendGoodweather(const uint64_t data,
|
||||
const uint16_t nbits = kGoodweatherBits,
|
||||
const uint16_t repeat = kGoodweatherMinRepeat);
|
||||
#endif // SEND_GOODWEATHER
|
||||
#if SEND_GORENJE
|
||||
void sendGorenje(const uint64_t data, const uint16_t nbits = kGorenjeBits,
|
||||
const uint16_t repeat = kNoRepeat);
|
||||
#endif // SEND_GORENJE
|
||||
#if SEND_PRONTO
|
||||
void sendPronto(uint16_t data[], uint16_t len, uint16_t repeat = kNoRepeat);
|
||||
#endif
|
||||
#if SEND_ARGO
|
||||
void sendArgo(const unsigned char data[],
|
||||
const uint16_t nbytes = kArgoStateLength,
|
||||
const uint16_t repeat = kArgoDefaultRepeat,
|
||||
bool sendFooter = false);
|
||||
void sendArgoWREM3(const unsigned char data[],
|
||||
const uint16_t nbytes = kArgoStateLength,
|
||||
const uint16_t repeat = kArgoDefaultRepeat);
|
||||
#endif // SEND_ARGO
|
||||
#if SEND_TROTEC
|
||||
void sendTrotec(const unsigned char data[],
|
||||
const uint16_t nbytes = kTrotecStateLength,
|
||||
const uint16_t repeat = kTrotecDefaultRepeat);
|
||||
#endif // SEND_TROTEC
|
||||
#if SEND_TROTEC_3550
|
||||
void sendTrotec3550(const unsigned char data[],
|
||||
const uint16_t nbytes = kTrotecStateLength,
|
||||
const uint16_t repeat = kTrotecDefaultRepeat);
|
||||
#endif // SEND_TROTEC_3550
|
||||
#if SEND_NIKAI
|
||||
void sendNikai(uint64_t data, uint16_t nbits = kNikaiBits,
|
||||
uint16_t repeat = kNoRepeat);
|
||||
#endif
|
||||
#if SEND_TOSHIBA_AC
|
||||
void sendToshibaAC(const uint8_t data[],
|
||||
const uint16_t nbytes = kToshibaACStateLength,
|
||||
const uint16_t repeat = kToshibaACMinRepeat);
|
||||
#endif
|
||||
#if SEND_MIDEA
|
||||
void sendMidea(uint64_t data, uint16_t nbits = kMideaBits,
|
||||
uint16_t repeat = kMideaMinRepeat);
|
||||
#endif // SEND_MIDEA
|
||||
#if SEND_MIDEA24
|
||||
void sendMidea24(const uint64_t data, const uint16_t nbits = kMidea24Bits,
|
||||
const uint16_t repeat = kMidea24MinRepeat);
|
||||
#endif // SEND_MIDEA24
|
||||
#if SEND_MAGIQUEST
|
||||
void sendMagiQuest(const uint64_t data, const uint16_t nbits = kMagiquestBits,
|
||||
const uint16_t repeat = kNoRepeat);
|
||||
uint64_t encodeMagiQuest(const uint32_t wand_id, const uint16_t magnitude);
|
||||
#endif
|
||||
#if SEND_LASERTAG
|
||||
void sendLasertag(uint64_t data, uint16_t nbits = kLasertagBits,
|
||||
uint16_t repeat = kLasertagMinRepeat);
|
||||
#endif
|
||||
#if SEND_CARRIER_AC
|
||||
void sendCarrierAC(uint64_t data, uint16_t nbits = kCarrierAcBits,
|
||||
uint16_t repeat = kCarrierAcMinRepeat);
|
||||
#endif
|
||||
#if SEND_CARRIER_AC40
|
||||
void sendCarrierAC40(uint64_t data, uint16_t nbits = kCarrierAc40Bits,
|
||||
uint16_t repeat = kCarrierAc40MinRepeat);
|
||||
#endif
|
||||
#if SEND_CARRIER_AC64
|
||||
void sendCarrierAC64(uint64_t data, uint16_t nbits = kCarrierAc64Bits,
|
||||
uint16_t repeat = kCarrierAc64MinRepeat);
|
||||
#endif
|
||||
#if SEND_CARRIER_AC84
|
||||
void sendCarrierAC84(const uint8_t data[],
|
||||
const uint16_t nbytes = kCarrierAc84StateLength,
|
||||
const uint16_t repeat = kNoRepeat);
|
||||
#endif // SEND_CARRIER_AC84
|
||||
#if SEND_CARRIER_AC128
|
||||
void sendCarrierAC128(const uint8_t data[],
|
||||
uint16_t nbytes = kCarrierAc128StateLength,
|
||||
uint16_t repeat = kCarrierAc128MinRepeat);
|
||||
#endif // SEND_CARRIER_AC128
|
||||
#if (SEND_HAIER_AC || SEND_HAIER_AC_YRW02 || SEND_HAIER_AC176)
|
||||
void sendHaierAC(const unsigned char data[],
|
||||
const uint16_t nbytes = kHaierACStateLength,
|
||||
const uint16_t repeat = kHaierAcDefaultRepeat);
|
||||
#endif // (SEND_HAIER_AC || SEND_HAIER_AC_YRW02 || SEND_HAIER_AC176)
|
||||
#if SEND_HAIER_AC_YRW02
|
||||
void sendHaierACYRW02(const unsigned char data[],
|
||||
const uint16_t nbytes = kHaierACYRW02StateLength,
|
||||
const uint16_t repeat = kHaierAcYrw02DefaultRepeat);
|
||||
#endif // SEND_HAIER_AC_YRW02
|
||||
#if SEND_HAIER_AC160
|
||||
void sendHaierAC160(const unsigned char data[],
|
||||
const uint16_t nbytes = kHaierAC160StateLength,
|
||||
const uint16_t repeat = kHaierAc160DefaultRepeat);
|
||||
#endif // SEND_HAIER_AC160
|
||||
#if SEND_HAIER_AC176
|
||||
void sendHaierAC176(const unsigned char data[],
|
||||
const uint16_t nbytes = kHaierAC176StateLength,
|
||||
const uint16_t repeat = kHaierAc176DefaultRepeat);
|
||||
#endif // SEND_HAIER_AC176
|
||||
#if SEND_HITACHI_AC
|
||||
void sendHitachiAC(const unsigned char data[],
|
||||
const uint16_t nbytes = kHitachiAcStateLength,
|
||||
const uint16_t repeat = kHitachiAcDefaultRepeat);
|
||||
#endif
|
||||
#if SEND_HITACHI_AC1
|
||||
void sendHitachiAC1(const unsigned char data[],
|
||||
const uint16_t nbytes = kHitachiAc1StateLength,
|
||||
const uint16_t repeat = kHitachiAcDefaultRepeat);
|
||||
#endif
|
||||
#if SEND_HITACHI_AC2
|
||||
void sendHitachiAC2(const unsigned char data[],
|
||||
const uint16_t nbytes = kHitachiAc2StateLength,
|
||||
const uint16_t repeat = kHitachiAcDefaultRepeat);
|
||||
#endif
|
||||
#if SEND_HITACHI_AC3
|
||||
void sendHitachiAc3(const unsigned char data[],
|
||||
const uint16_t nbytes, // No default as there as so many
|
||||
// different sizes
|
||||
const uint16_t repeat = kHitachiAcDefaultRepeat);
|
||||
#endif // SEND_HITACHI_AC3
|
||||
#if SEND_HITACHI_AC264
|
||||
void sendHitachiAc264(const unsigned char data[],
|
||||
const uint16_t nbytes = kHitachiAc264StateLength,
|
||||
const uint16_t repeat = kHitachiAcDefaultRepeat);
|
||||
#endif // SEND_HITACHI_AC264
|
||||
#if SEND_HITACHI_AC296
|
||||
void sendHitachiAc296(const unsigned char data[],
|
||||
const uint16_t nbytes = kHitachiAc296StateLength,
|
||||
const uint16_t repeat = kHitachiAcDefaultRepeat);
|
||||
#endif // SEND_HITACHI_AC296
|
||||
#if SEND_HITACHI_AC344
|
||||
void sendHitachiAc344(const unsigned char data[],
|
||||
const uint16_t nbytes = kHitachiAc344StateLength,
|
||||
const uint16_t repeat = kHitachiAcDefaultRepeat);
|
||||
#endif // SEND_HITACHI_AC344
|
||||
#if SEND_HITACHI_AC424
|
||||
void sendHitachiAc424(const unsigned char data[],
|
||||
const uint16_t nbytes = kHitachiAc424StateLength,
|
||||
const uint16_t repeat = kHitachiAcDefaultRepeat);
|
||||
#endif // SEND_HITACHI_AC424
|
||||
#if SEND_GICABLE
|
||||
void sendGICable(uint64_t data, uint16_t nbits = kGicableBits,
|
||||
uint16_t repeat = kGicableMinRepeat);
|
||||
#endif
|
||||
#if SEND_WHIRLPOOL_AC
|
||||
void sendWhirlpoolAC(const unsigned char data[],
|
||||
const uint16_t nbytes = kWhirlpoolAcStateLength,
|
||||
const uint16_t repeat = kWhirlpoolAcDefaultRepeat);
|
||||
#endif
|
||||
#if SEND_LUTRON
|
||||
void sendLutron(uint64_t data, uint16_t nbits = kLutronBits,
|
||||
uint16_t repeat = kNoRepeat);
|
||||
#endif
|
||||
#if SEND_ELECTRA_AC
|
||||
void sendElectraAC(const unsigned char data[],
|
||||
const uint16_t nbytes = kElectraAcStateLength,
|
||||
const uint16_t repeat = kNoRepeat);
|
||||
#endif
|
||||
#if SEND_PANASONIC_AC
|
||||
void sendPanasonicAC(const unsigned char data[],
|
||||
const uint16_t nbytes = kPanasonicAcStateLength,
|
||||
const uint16_t repeat = kPanasonicAcDefaultRepeat);
|
||||
#endif // SEND_PANASONIC_AC
|
||||
#if SEND_PANASONIC_AC32
|
||||
void sendPanasonicAC32(const uint64_t data,
|
||||
const uint16_t nbits = kPanasonicAc32Bits,
|
||||
const uint16_t repeat = kPanasonicAcDefaultRepeat);
|
||||
#endif // SEND_PANASONIC_AC32
|
||||
#if SEND_PIONEER
|
||||
void sendPioneer(const uint64_t data, const uint16_t nbits = kPioneerBits,
|
||||
const uint16_t repeat = kNoRepeat);
|
||||
uint64_t encodePioneer(uint16_t address, uint16_t command);
|
||||
#endif
|
||||
#if SEND_MWM
|
||||
void sendMWM(const unsigned char data[], const uint16_t nbytes,
|
||||
const uint16_t repeat = kNoRepeat);
|
||||
#endif
|
||||
#if SEND_VESTEL_AC
|
||||
void sendVestelAc(const uint64_t data, const uint16_t nbits = kVestelAcBits,
|
||||
const uint16_t repeat = kNoRepeat);
|
||||
#endif
|
||||
#if SEND_TCL96AC
|
||||
void sendTcl96Ac(const unsigned char data[],
|
||||
const uint16_t nbytes = kTcl96AcStateLength,
|
||||
const uint16_t repeat = kTcl96AcDefaultRepeat);
|
||||
#endif // SEND_TCL96AC
|
||||
#if SEND_TCL112AC
|
||||
void sendTcl112Ac(const unsigned char data[],
|
||||
const uint16_t nbytes = kTcl112AcStateLength,
|
||||
const uint16_t repeat = kTcl112AcDefaultRepeat);
|
||||
#endif // SEND_TCL112AC
|
||||
#if SEND_TECO
|
||||
void sendTeco(const uint64_t data, const uint16_t nbits = kTecoBits,
|
||||
const uint16_t repeat = kNoRepeat);
|
||||
#endif
|
||||
#if SEND_LEGOPF
|
||||
void sendLegoPf(const uint64_t data, const uint16_t nbits = kLegoPfBits,
|
||||
const uint16_t repeat = kLegoPfMinRepeat);
|
||||
#endif
|
||||
#if SEND_NEOCLIMA
|
||||
void sendNeoclima(const unsigned char data[],
|
||||
const uint16_t nbytes = kNeoclimaStateLength,
|
||||
const uint16_t repeat = kNeoclimaMinRepeat);
|
||||
#endif // SEND_NEOCLIMA
|
||||
#if SEND_AMCOR
|
||||
void sendAmcor(const unsigned char data[],
|
||||
const uint16_t nbytes = kAmcorStateLength,
|
||||
const uint16_t repeat = kAmcorDefaultRepeat);
|
||||
#endif // SEND_AMCOR
|
||||
#if SEND_EPSON
|
||||
void sendEpson(uint64_t data, uint16_t nbits = kEpsonBits,
|
||||
uint16_t repeat = kEpsonMinRepeat);
|
||||
#endif
|
||||
#if SEND_SYMPHONY
|
||||
void sendSymphony(uint64_t data, uint16_t nbits = kSymphonyBits,
|
||||
uint16_t repeat = kSymphonyDefaultRepeat);
|
||||
#endif
|
||||
#if SEND_AIRWELL
|
||||
void sendAirwell(uint64_t data, uint16_t nbits = kAirwellBits,
|
||||
uint16_t repeat = kAirwellMinRepeats);
|
||||
#endif
|
||||
#if SEND_DELONGHI_AC
|
||||
void sendDelonghiAc(uint64_t data, uint16_t nbits = kDelonghiAcBits,
|
||||
uint16_t repeat = kDelonghiAcDefaultRepeat);
|
||||
#endif
|
||||
#if SEND_DOSHISHA
|
||||
void sendDoshisha(const uint64_t data, uint16_t nbits = kDoshishaBits,
|
||||
const uint16_t repeat = kNoRepeat);
|
||||
uint64_t encodeDoshisha(const uint8_t command, const uint8_t channel = 0);
|
||||
#endif // SEND_DOSHISHA
|
||||
#if SEND_MULTIBRACKETS
|
||||
void sendMultibrackets(const uint64_t data,
|
||||
const uint16_t nbits = kMultibracketsBits,
|
||||
const uint16_t repeat = kMultibracketsDefaultRepeat);
|
||||
#endif
|
||||
#if SEND_TECHNIBEL_AC
|
||||
void sendTechnibelAc(uint64_t data, uint16_t nbits = kTechnibelAcBits,
|
||||
uint16_t repeat = kTechnibelAcDefaultRepeat);
|
||||
#endif
|
||||
#if SEND_CORONA_AC
|
||||
void sendCoronaAc(const uint8_t data[],
|
||||
const uint16_t nbytes = kCoronaAcStateLength,
|
||||
const uint16_t repeat = kNoRepeat);
|
||||
#endif // SEND_CORONA_AC
|
||||
#if SEND_ZEPEAL
|
||||
void sendZepeal(const uint64_t data,
|
||||
const uint16_t nbits = kZepealBits,
|
||||
const uint16_t repeat = kZepealMinRepeat);
|
||||
#endif // SEND_ZEPEAL
|
||||
#if SEND_VOLTAS
|
||||
void sendVoltas(const unsigned char data[],
|
||||
const uint16_t nbytes = kVoltasStateLength,
|
||||
const uint16_t repeat = kNoRepeat);
|
||||
#endif // SEND_VOLTAS
|
||||
#if SEND_METZ
|
||||
void sendMetz(const uint64_t data,
|
||||
const uint16_t nbits = kMetzBits,
|
||||
const uint16_t repeat = kMetzMinRepeat);
|
||||
static uint32_t encodeMetz(const uint8_t address, const uint8_t command,
|
||||
const bool toggle = false);
|
||||
#endif // SEND_METZ
|
||||
#if SEND_TRANSCOLD
|
||||
void sendTranscold(const uint64_t data, const uint16_t nbits = kTranscoldBits,
|
||||
const uint16_t repeat = kTranscoldDefaultRepeat);
|
||||
#endif // SEND_TRANSCOLD
|
||||
#if SEND_ELITESCREENS
|
||||
void sendElitescreens(const uint64_t data,
|
||||
const uint16_t nbits = kEliteScreensBits,
|
||||
const uint16_t repeat = kEliteScreensDefaultRepeat);
|
||||
#endif // SEND_ELITESCREENS
|
||||
#if SEND_MILESTAG2
|
||||
// Since There 2 types of transmissions
|
||||
// (14bits for Shooting by default, you can set 24 bit for msg delivery)
|
||||
void sendMilestag2(const uint64_t data,
|
||||
const uint16_t nbits = kMilesTag2ShotBits,
|
||||
const uint16_t repeat = kMilesMinRepeat);
|
||||
#endif // SEND_MILESTAG2
|
||||
#if SEND_ECOCLIM
|
||||
void sendEcoclim(const uint64_t data, const uint16_t nbits = kEcoclimBits,
|
||||
const uint16_t repeat = kNoRepeat);
|
||||
#endif // SEND_ECOCLIM
|
||||
#if SEND_XMP
|
||||
void sendXmp(const uint64_t data, const uint16_t nbits = kXmpBits,
|
||||
const uint16_t repeat = kNoRepeat);
|
||||
#endif // SEND_XMP
|
||||
#if SEND_TRUMA
|
||||
void sendTruma(const uint64_t data, const uint16_t nbits = kTrumaBits,
|
||||
const uint16_t repeat = kNoRepeat);
|
||||
#endif // SEND_TRUMA
|
||||
#if SEND_TEKNOPOINT
|
||||
void sendTeknopoint(const unsigned char data[],
|
||||
const uint16_t nbytes = kTeknopointStateLength,
|
||||
const uint16_t repeat = kNoRepeat);
|
||||
#endif // SEND_TEKNOPOINT
|
||||
#if SEND_KELON
|
||||
void sendKelon(const uint64_t data, const uint16_t nbits = kKelonBits,
|
||||
const uint16_t repeat = kNoRepeat);
|
||||
#endif // SEND_KELON
|
||||
#if SEND_KELON168
|
||||
void sendKelon168(const unsigned char data[],
|
||||
const uint16_t nbytes = kKelon168StateLength,
|
||||
const uint16_t repeat = kNoRepeat);
|
||||
#endif // SEND_KELON168
|
||||
#if SEND_BOSE
|
||||
void sendBose(const uint64_t data, const uint16_t nbits = kBoseBits,
|
||||
const uint16_t repeat = kNoRepeat);
|
||||
#endif // SEND_BOSE
|
||||
#if SEND_ARRIS
|
||||
void sendArris(const uint64_t data, const uint16_t nbits = kArrisBits,
|
||||
const uint16_t repeat = kNoRepeat);
|
||||
static uint32_t toggleArrisRelease(const uint32_t data);
|
||||
static uint32_t encodeArris(const uint32_t command, const bool release);
|
||||
#endif // SEND_ARRIS
|
||||
#if SEND_RHOSS
|
||||
void sendRhoss(const unsigned char data[],
|
||||
const uint16_t nbytes = kRhossStateLength,
|
||||
const uint16_t repeat = kRhossDefaultRepeat);
|
||||
#endif // SEND_RHOSS
|
||||
#if SEND_AIRTON
|
||||
void sendAirton(const uint64_t data, const uint16_t nbits = kAirtonBits,
|
||||
const uint16_t repeat = kAirtonDefaultRepeat);
|
||||
#endif // SEND_AIRTON
|
||||
#if SEND_TOTO
|
||||
void sendToto(const uint64_t data, const uint16_t nbits = kTotoBits,
|
||||
const uint16_t repeat = kTotoDefaultRepeat);
|
||||
#endif // SEND_TOTO
|
||||
#if SEND_CLIMABUTLER
|
||||
void sendClimaButler(const uint64_t data,
|
||||
const uint16_t nbits = kClimaButlerBits,
|
||||
const uint16_t repeat = kNoRepeat);
|
||||
#endif // SEND_CLIMABUTLER
|
||||
#if SEND_BOSCH144
|
||||
void sendBosch144(const unsigned char data[],
|
||||
const uint16_t nbytes = kBosch144StateLength,
|
||||
const uint16_t repeat = kNoRepeat);
|
||||
#endif // SEND_BOSCH144
|
||||
#if SEND_WOWWEE
|
||||
void sendWowwee(const uint64_t data, const uint16_t nbits = kWowweeBits,
|
||||
const uint16_t repeat = kWowweeDefaultRepeat);
|
||||
#endif // SEND_WOWWEE
|
||||
#if SEND_YORK
|
||||
void sendYork(const unsigned char data[],
|
||||
const uint16_t nbytes = kYorkStateLength,
|
||||
const uint16_t repeat = kNoRepeat);
|
||||
#endif // SEND_YORK
|
||||
|
||||
protected:
|
||||
#ifdef UNIT_TEST
|
||||
#ifndef HIGH
|
||||
#define HIGH 0x1
|
||||
#endif
|
||||
#ifndef LOW
|
||||
#define LOW 0x0
|
||||
#endif
|
||||
#endif // UNIT_TEST
|
||||
uint8_t outputOn;
|
||||
uint8_t outputOff;
|
||||
VIRTUAL void ledOff();
|
||||
VIRTUAL void ledOn();
|
||||
#ifndef UNIT_TEST
|
||||
|
||||
private:
|
||||
#else
|
||||
uint32_t _freq_unittest;
|
||||
#endif // UNIT_TEST
|
||||
uint16_t onTimePeriod;
|
||||
uint16_t offTimePeriod;
|
||||
uint16_t IRpin;
|
||||
int8_t periodOffset;
|
||||
uint8_t _dutycycle;
|
||||
bool modulation;
|
||||
uint32_t calcUSecPeriod(uint32_t hz, bool use_offset = true);
|
||||
#if SEND_SONY
|
||||
void _sendSony(const uint64_t data, const uint16_t nbits,
|
||||
const uint16_t repeat, const uint16_t freq);
|
||||
#endif // SEND_SONY
|
||||
};
|
||||
|
||||
#endif // IRSEND_H_
|
||||
561
yoRadio/src/IRremoteESP8266/IRtext.cpp
Normal file
561
yoRadio/src/IRremoteESP8266/IRtext.cpp
Normal file
@@ -0,0 +1,561 @@
|
||||
// Copyright 2019-2021 - David Conran (@crankyoldgit)
|
||||
|
||||
/// @file IRtext.cpp
|
||||
/// @warning If you add or remove an entry in this file, you should run:
|
||||
/// '../tools/generate_irtext_h.sh' to rebuild the `IRtext.h` file.
|
||||
|
||||
#include "IRtext.h"
|
||||
#ifndef UNIT_TEST
|
||||
#include <Arduino.h>
|
||||
#endif // UNIT_TEST
|
||||
#include "IRremoteESP8266.h"
|
||||
#include "i18n.h"
|
||||
|
||||
#include "IRmacros.h"
|
||||
|
||||
#ifndef PROGMEM
|
||||
#define PROGMEM // Pretend we have the PROGMEM macro even if we really don't.
|
||||
#endif
|
||||
|
||||
#ifndef FPSTR
|
||||
#define FPSTR(X) X // Also pretend we have flash-string helper class cast.
|
||||
#endif
|
||||
|
||||
#define IRTEXT_CONST_BLOB_NAME(NAME)\
|
||||
NAME ## Blob
|
||||
|
||||
#define IRTEXT_CONST_BLOB_DECL(NAME)\
|
||||
const char IRTEXT_CONST_BLOB_NAME(NAME) [] PROGMEM
|
||||
|
||||
#define IRTEXT_CONST_BLOB_PTR(NAME)\
|
||||
IRTEXT_CONST_PTR(NAME) {\
|
||||
IRTEXT_CONST_PTR_CAST(IRTEXT_CONST_BLOB_NAME(NAME)) }
|
||||
|
||||
#define IRTEXT_CONST_STRING(NAME, VALUE)\
|
||||
static IRTEXT_CONST_BLOB_DECL(NAME) { VALUE };\
|
||||
IRTEXT_CONST_PTR(NAME) PROGMEM {\
|
||||
IRTEXT_CONST_PTR_CAST(&(IRTEXT_CONST_BLOB_NAME(NAME))[0]) }
|
||||
|
||||
// Common
|
||||
IRTEXT_CONST_STRING(kUnknownStr, D_STR_UNKNOWN); ///< "Unknown"
|
||||
IRTEXT_CONST_STRING(kProtocolStr, D_STR_PROTOCOL); ///< "Protocol"
|
||||
IRTEXT_CONST_STRING(kPowerStr, D_STR_POWER); ///< "Power"
|
||||
IRTEXT_CONST_STRING(kOnStr, D_STR_ON); ///< "On"
|
||||
IRTEXT_CONST_STRING(kOffStr, D_STR_OFF); ///< "Off"
|
||||
IRTEXT_CONST_STRING(k1Str, D_STR_1); ///< "1"
|
||||
IRTEXT_CONST_STRING(k0Str, D_STR_0); ///< "0"
|
||||
IRTEXT_CONST_STRING(kModeStr, D_STR_MODE); ///< "Mode"
|
||||
IRTEXT_CONST_STRING(kToggleStr, D_STR_TOGGLE); ///< "Toggle"
|
||||
IRTEXT_CONST_STRING(kTurboStr, D_STR_TURBO); ///< "Turbo"
|
||||
IRTEXT_CONST_STRING(kSuperStr, D_STR_SUPER); ///< "Super"
|
||||
IRTEXT_CONST_STRING(kSleepStr, D_STR_SLEEP); ///< "Sleep"
|
||||
IRTEXT_CONST_STRING(kLightStr, D_STR_LIGHT); ///< "Light"
|
||||
IRTEXT_CONST_STRING(kPowerfulStr, D_STR_POWERFUL); ///< "Powerful"
|
||||
IRTEXT_CONST_STRING(kQuietStr, D_STR_QUIET); ///< "Quiet"
|
||||
IRTEXT_CONST_STRING(kEconoStr, D_STR_ECONO); ///< "Econo"
|
||||
IRTEXT_CONST_STRING(kSwingStr, D_STR_SWING); ///< "Swing"
|
||||
IRTEXT_CONST_STRING(kSwingHStr, D_STR_SWINGH); ///< "SwingH"
|
||||
IRTEXT_CONST_STRING(kSwingVStr, D_STR_SWINGV); ///< "SwingV"
|
||||
IRTEXT_CONST_STRING(kBeepStr, D_STR_BEEP); ///< "Beep"
|
||||
IRTEXT_CONST_STRING(kZoneFollowStr, D_STR_ZONEFOLLOW); ///< "Zone Follow"
|
||||
IRTEXT_CONST_STRING(kFixedStr, D_STR_FIXED); ///< "Fixed"
|
||||
IRTEXT_CONST_STRING(kMouldStr, D_STR_MOULD); ///< "Mould"
|
||||
IRTEXT_CONST_STRING(kCleanStr, D_STR_CLEAN); ///< "Clean"
|
||||
IRTEXT_CONST_STRING(kPurifyStr, D_STR_PURIFY); ///< "Purify"
|
||||
IRTEXT_CONST_STRING(kTimerStr, D_STR_TIMER); ///< "Timer"
|
||||
IRTEXT_CONST_STRING(kOnTimerStr, D_STR_ONTIMER); ///< "On Timer"
|
||||
IRTEXT_CONST_STRING(kOffTimerStr, D_STR_OFFTIMER); ///< "Off Timer"
|
||||
IRTEXT_CONST_STRING(kTimerModeStr, D_STR_TIMERMODE); ///< "Timer Mode"
|
||||
IRTEXT_CONST_STRING(kClockStr, D_STR_CLOCK); ///< "Clock"
|
||||
IRTEXT_CONST_STRING(kCommandStr, D_STR_COMMAND); ///< "Command"
|
||||
IRTEXT_CONST_STRING(kConfigCommandStr, D_STR_CONFIG); ///< "Config"
|
||||
IRTEXT_CONST_STRING(kControlCommandStr, D_STR_CONTROL); ///< "Control"
|
||||
IRTEXT_CONST_STRING(kXFanStr, D_STR_XFAN); ///< "XFan"
|
||||
IRTEXT_CONST_STRING(kHealthStr, D_STR_HEALTH); ///< "Health"
|
||||
IRTEXT_CONST_STRING(kModelStr, D_STR_MODEL); ///< "Model"
|
||||
IRTEXT_CONST_STRING(kTempStr, D_STR_TEMP); ///< "Temp"
|
||||
IRTEXT_CONST_STRING(kIFeelReportStr, D_STR_IFEELREPORT); ///< "IFeel Report"
|
||||
IRTEXT_CONST_STRING(kIFeelStr, D_STR_IFEEL); ///< "IFeel"
|
||||
IRTEXT_CONST_STRING(kHumidStr, D_STR_HUMID); ///< "Humid"
|
||||
IRTEXT_CONST_STRING(kSaveStr, D_STR_SAVE); ///< "Save"
|
||||
IRTEXT_CONST_STRING(kEyeStr, D_STR_EYE); ///< "Eye"
|
||||
IRTEXT_CONST_STRING(kFollowStr, D_STR_FOLLOW); ///< "Follow"
|
||||
IRTEXT_CONST_STRING(kIonStr, D_STR_ION); ///< "Ion"
|
||||
IRTEXT_CONST_STRING(kFreshStr, D_STR_FRESH); ///< "Fresh"
|
||||
IRTEXT_CONST_STRING(kHoldStr, D_STR_HOLD); ///< "Hold"
|
||||
IRTEXT_CONST_STRING(kButtonStr, D_STR_BUTTON); ///< "Button"
|
||||
IRTEXT_CONST_STRING(k8CHeatStr, D_STR_8C_HEAT); ///< "8C Heat"
|
||||
IRTEXT_CONST_STRING(k10CHeatStr, D_STR_10C_HEAT); ///< "10C Heat"
|
||||
IRTEXT_CONST_STRING(kISeeStr, D_STR_ISEE); ///< "ISee"
|
||||
IRTEXT_CONST_STRING(kAbsenseDetectStr, D_STR_ABSENSEDETECT);
|
||||
///< "AbsenseDetect"
|
||||
IRTEXT_CONST_STRING(kDirectIndirectModeStr, D_STR_DIRECTINDIRECTMODE);
|
||||
///< "Direct/Indirect mode"
|
||||
IRTEXT_CONST_STRING(kDirectStr, D_STR_DIRECT); ///< "Direct"
|
||||
IRTEXT_CONST_STRING(kIndirectStr, D_STR_INDIRECT); ///< "Indirect"
|
||||
|
||||
IRTEXT_CONST_STRING(kNightStr, D_STR_NIGHT); ///< "Night"
|
||||
IRTEXT_CONST_STRING(kSilentStr, D_STR_SILENT); ///< "Silent"
|
||||
IRTEXT_CONST_STRING(kFilterStr, D_STR_FILTER); ///< "Filter"
|
||||
IRTEXT_CONST_STRING(k3DStr, D_STR_3D); ///< "3D"
|
||||
IRTEXT_CONST_STRING(kCelsiusStr, D_STR_CELSIUS); ///< "Celsius"
|
||||
IRTEXT_CONST_STRING(kCelsiusFahrenheitStr, D_STR_CELSIUS_FAHRENHEIT); ///<
|
||||
///< "Celsius/Fahrenheit"
|
||||
IRTEXT_CONST_STRING(kTempUpStr, D_STR_TEMPUP); ///< "Temp Up"
|
||||
IRTEXT_CONST_STRING(kTempDownStr, D_STR_TEMPDOWN); ///< "Temp Down"
|
||||
IRTEXT_CONST_STRING(kStartStr, D_STR_START); ///< "Start"
|
||||
IRTEXT_CONST_STRING(kStopStr, D_STR_STOP); ///< "Stop"
|
||||
IRTEXT_CONST_STRING(kMoveStr, D_STR_MOVE); ///< "Move"
|
||||
IRTEXT_CONST_STRING(kSetStr, D_STR_SET); ///< "Set"
|
||||
IRTEXT_CONST_STRING(kCancelStr, D_STR_CANCEL); ///< "Cancel"
|
||||
IRTEXT_CONST_STRING(kUpStr, D_STR_UP); ///< "Up"
|
||||
IRTEXT_CONST_STRING(kDownStr, D_STR_DOWN); ///< "Down"
|
||||
IRTEXT_CONST_STRING(kChangeStr, D_STR_CHANGE); ///< "Change"
|
||||
IRTEXT_CONST_STRING(kComfortStr, D_STR_COMFORT); ///< "Comfort"
|
||||
IRTEXT_CONST_STRING(kSensorStr, D_STR_SENSOR); ///< "Sensor"
|
||||
IRTEXT_CONST_STRING(kWeeklyTimerStr, D_STR_WEEKLYTIMER); ///< "WeeklyTimer"
|
||||
IRTEXT_CONST_STRING(kWifiStr, D_STR_WIFI); ///< "Wifi"
|
||||
IRTEXT_CONST_STRING(kLastStr, D_STR_LAST); ///< "Last"
|
||||
IRTEXT_CONST_STRING(kFastStr, D_STR_FAST); ///< "Fast"
|
||||
IRTEXT_CONST_STRING(kSlowStr, D_STR_SLOW); ///< "Slow"
|
||||
IRTEXT_CONST_STRING(kAirFlowStr, D_STR_AIRFLOW); ///< "Air Flow"
|
||||
IRTEXT_CONST_STRING(kStepStr, D_STR_STEP); ///< "Step"
|
||||
IRTEXT_CONST_STRING(kNAStr, D_STR_NA); ///< "N/A"
|
||||
IRTEXT_CONST_STRING(kInsideStr, D_STR_INSIDE); ///< "Inside"
|
||||
IRTEXT_CONST_STRING(kOutsideStr, D_STR_OUTSIDE); ///< "Outside"
|
||||
IRTEXT_CONST_STRING(kLoudStr, D_STR_LOUD); ///< "Loud"
|
||||
IRTEXT_CONST_STRING(kLowerStr, D_STR_LOWER); ///< "Lower"
|
||||
IRTEXT_CONST_STRING(kUpperStr, D_STR_UPPER); ///< "Upper"
|
||||
IRTEXT_CONST_STRING(kUpperMiddleStr, D_STR_UPPER_MIDDLE); ///< "Upper-Middle"
|
||||
IRTEXT_CONST_STRING(kBreezeStr, D_STR_BREEZE); ///< "Breeze"
|
||||
IRTEXT_CONST_STRING(kCirculateStr, D_STR_CIRCULATE); ///< "Circulate"
|
||||
IRTEXT_CONST_STRING(kCeilingStr, D_STR_CEILING); ///< "Ceiling"
|
||||
IRTEXT_CONST_STRING(kWallStr, D_STR_WALL); ///< "Wall"
|
||||
IRTEXT_CONST_STRING(kRoomStr, D_STR_ROOM); ///< "Room"
|
||||
IRTEXT_CONST_STRING(k6thSenseStr, D_STR_6THSENSE); ///< "6th Sense"
|
||||
IRTEXT_CONST_STRING(kTypeStr, D_STR_TYPE); ///< "Type"
|
||||
IRTEXT_CONST_STRING(kSpecialStr, D_STR_SPECIAL); ///< "Special"
|
||||
IRTEXT_CONST_STRING(kIdStr, D_STR_ID); ///< "Id" / Device Identifier
|
||||
IRTEXT_CONST_STRING(kVaneStr, D_STR_VANE); ///< "Vane"
|
||||
IRTEXT_CONST_STRING(kLockStr, D_STR_LOCK); ///< "Lock"
|
||||
|
||||
IRTEXT_CONST_STRING(kAutoStr, D_STR_AUTO); ///< "Auto"
|
||||
IRTEXT_CONST_STRING(kAutomaticStr, D_STR_AUTOMATIC); ///< "Automatic"
|
||||
IRTEXT_CONST_STRING(kManualStr, D_STR_MANUAL); ///< "Manual"
|
||||
IRTEXT_CONST_STRING(kCoolStr, D_STR_COOL); ///< "Cool"
|
||||
IRTEXT_CONST_STRING(kCoolingStr, D_STR_COOLING); ///< "Cooling"
|
||||
IRTEXT_CONST_STRING(kHeatStr, D_STR_HEAT); ///< "Heat"
|
||||
IRTEXT_CONST_STRING(kHeatingStr, D_STR_HEATING); ///< "Heating"
|
||||
IRTEXT_CONST_STRING(kDryStr, D_STR_DRY); ///< "Dry"
|
||||
IRTEXT_CONST_STRING(kDryingStr, D_STR_DRYING); ///< "Drying"
|
||||
IRTEXT_CONST_STRING(kDehumidifyStr, D_STR_DEHUMIDIFY); ///< "Dehumidify"
|
||||
IRTEXT_CONST_STRING(kFanStr, D_STR_FAN); ///< "Fan"
|
||||
// The following Fans strings with "only" are required to help with
|
||||
// HomeAssistant & Google Home Climate integration. For compatibility only.
|
||||
// Ref: https://www.home-assistant.io/integrations/google_assistant/#climate-operation-modes
|
||||
IRTEXT_CONST_STRING(kFanOnlyStr, D_STR_FANONLY); ///< "fan-only"
|
||||
IRTEXT_CONST_STRING(kFan_OnlyStr, D_STR_FAN_ONLY); ///< "fan_only" (HA/legacy)
|
||||
IRTEXT_CONST_STRING(kFanOnlyWithSpaceStr, D_STR_FANSPACEONLY); ///< "Fan Only"
|
||||
IRTEXT_CONST_STRING(kFanOnlyNoSpaceStr, D_STR_FANONLYNOSPACE); ///< "FanOnly"
|
||||
|
||||
IRTEXT_CONST_STRING(kRecycleStr, D_STR_RECYCLE); ///< "Recycle"
|
||||
|
||||
IRTEXT_CONST_STRING(kMaxStr, D_STR_MAX); ///< "Max"
|
||||
IRTEXT_CONST_STRING(kMaximumStr, D_STR_MAXIMUM); ///< "Maximum"
|
||||
IRTEXT_CONST_STRING(kMinStr, D_STR_MIN); ///< "Min"
|
||||
IRTEXT_CONST_STRING(kMinimumStr, D_STR_MINIMUM); ///< "Minimum"
|
||||
IRTEXT_CONST_STRING(kMedHighStr, D_STR_MED_HIGH); ///< "Med-high"
|
||||
IRTEXT_CONST_STRING(kMedStr, D_STR_MED); ///< "Med"
|
||||
IRTEXT_CONST_STRING(kMediumStr, D_STR_MEDIUM); ///< "Medium"
|
||||
|
||||
IRTEXT_CONST_STRING(kHighestStr, D_STR_HIGHEST); ///< "Highest"
|
||||
IRTEXT_CONST_STRING(kHighStr, D_STR_HIGH); ///< "High"
|
||||
IRTEXT_CONST_STRING(kHiStr, D_STR_HI); ///< "Hi"
|
||||
IRTEXT_CONST_STRING(kMidStr, D_STR_MID); ///< "Mid"
|
||||
IRTEXT_CONST_STRING(kMiddleStr, D_STR_MIDDLE); ///< "Middle"
|
||||
IRTEXT_CONST_STRING(kLowStr, D_STR_LOW); ///< "Low"
|
||||
IRTEXT_CONST_STRING(kLoStr, D_STR_LO); ///< "Lo"
|
||||
IRTEXT_CONST_STRING(kLowestStr, D_STR_LOWEST); ///< "Lowest"
|
||||
IRTEXT_CONST_STRING(kMaxRightStr, D_STR_MAXRIGHT); ///< "Max Right"
|
||||
IRTEXT_CONST_STRING(kMaxRightNoSpaceStr, D_STR_MAXRIGHT_NOSPACE); ///<
|
||||
///< "MaxRight"
|
||||
IRTEXT_CONST_STRING(kRightMaxStr, D_STR_RIGHTMAX); ///< "Right Max"
|
||||
IRTEXT_CONST_STRING(kRightMaxNoSpaceStr, D_STR_RIGHTMAX_NOSPACE); ///<
|
||||
///< "RightMax"
|
||||
IRTEXT_CONST_STRING(kRightStr, D_STR_RIGHT); ///< "Right"
|
||||
IRTEXT_CONST_STRING(kLeftStr, D_STR_LEFT); ///< "Left"
|
||||
IRTEXT_CONST_STRING(kMaxLeftStr, D_STR_MAXLEFT); ///< "Max Left"
|
||||
IRTEXT_CONST_STRING(kMaxLeftNoSpaceStr, D_STR_MAXLEFT_NOSPACE); ///< "MaxLeft"
|
||||
IRTEXT_CONST_STRING(kLeftMaxStr, D_STR_LEFTMAX); ///< "Left Max"
|
||||
IRTEXT_CONST_STRING(kLeftMaxNoSpaceStr, D_STR_LEFTMAX_NOSPACE); ///< "LeftMax"
|
||||
IRTEXT_CONST_STRING(kWideStr, D_STR_WIDE); ///< "Wide"
|
||||
IRTEXT_CONST_STRING(kCentreStr, D_STR_CENTRE); ///< "Centre"
|
||||
IRTEXT_CONST_STRING(kTopStr, D_STR_TOP); ///< "Top"
|
||||
IRTEXT_CONST_STRING(kBottomStr, D_STR_BOTTOM); ///< "Bottom"
|
||||
|
||||
// Compound words/phrases/descriptions from pre-defined words.
|
||||
IRTEXT_CONST_STRING(kEconoToggleStr, D_STR_ECONOTOGGLE); ///< "Econo Toggle"
|
||||
IRTEXT_CONST_STRING(kEyeAutoStr, D_STR_EYEAUTO); ///< "Eye Auto"
|
||||
IRTEXT_CONST_STRING(kLightToggleStr, D_STR_LIGHTTOGGLE); ///< "Light Toggle"
|
||||
///< "Outside Quiet"
|
||||
IRTEXT_CONST_STRING(kOutsideQuietStr, D_STR_OUTSIDEQUIET);
|
||||
IRTEXT_CONST_STRING(kPowerToggleStr, D_STR_POWERTOGGLE); ///< "Power Toggle"
|
||||
IRTEXT_CONST_STRING(kPowerButtonStr, D_STR_POWERBUTTON); ///< "Power Button"
|
||||
IRTEXT_CONST_STRING(kPreviousPowerStr, D_STR_PREVIOUSPOWER); ///<
|
||||
///< "Previous Power"
|
||||
IRTEXT_CONST_STRING(kDisplayTempStr, D_STR_DISPLAYTEMP); ///< "Display Temp"
|
||||
IRTEXT_CONST_STRING(kSensorTempStr, D_STR_SENSORTEMP); ///< "Sensor Temp"
|
||||
IRTEXT_CONST_STRING(kSleepTimerStr, D_STR_SLEEP_TIMER); ///< "Sleep Timer"
|
||||
IRTEXT_CONST_STRING(kSwingVModeStr, D_STR_SWINGVMODE); ///< "Swing(V) Mode"
|
||||
IRTEXT_CONST_STRING(kSwingVToggleStr, D_STR_SWINGVTOGGLE); ///<
|
||||
///< "Swing(V) Toggle"
|
||||
IRTEXT_CONST_STRING(kTurboToggleStr, D_STR_TURBOTOGGLE); ///< "Turbo Toggle"
|
||||
IRTEXT_CONST_STRING(kSetTimerCommandStr, D_STR_SET_TIMER); ///< "Set Timer"
|
||||
IRTEXT_CONST_STRING(kScheduleStr, D_STR_SCHEDULE); ///< "Schedule"
|
||||
IRTEXT_CONST_STRING(kChStr, D_STR_CH); ///< "CH#"
|
||||
IRTEXT_CONST_STRING(kTimerActiveDaysStr, D_STR_TIMER_ACTIVE_DAYS);
|
||||
///< "TimerActiveDays"
|
||||
IRTEXT_CONST_STRING(kKeyStr, D_STR_KEY); ///< "Key"
|
||||
IRTEXT_CONST_STRING(kValueStr, D_STR_VALUE); ///< "Value"
|
||||
|
||||
// Separators & Punctuation
|
||||
const char kTimeSep = D_CHR_TIME_SEP; ///< ':'
|
||||
IRTEXT_CONST_STRING(kSpaceLBraceStr, D_STR_SPACELBRACE); ///< " ("
|
||||
IRTEXT_CONST_STRING(kCommaSpaceStr, D_STR_COMMASPACE); ///< ", "
|
||||
IRTEXT_CONST_STRING(kColonSpaceStr, D_STR_COLONSPACE); ///< ": "
|
||||
IRTEXT_CONST_STRING(kDashStr, D_STR_DASH); ///< "-"
|
||||
|
||||
// IRutils
|
||||
// - Time
|
||||
IRTEXT_CONST_STRING(kDayStr, D_STR_DAY); ///< "Day"
|
||||
IRTEXT_CONST_STRING(kDaysStr, D_STR_DAYS); ///< "Days"
|
||||
IRTEXT_CONST_STRING(kHourStr, D_STR_HOUR); ///< "Hour"
|
||||
IRTEXT_CONST_STRING(kHoursStr, D_STR_HOURS); ///< "Hours"
|
||||
IRTEXT_CONST_STRING(kMinuteStr, D_STR_MINUTE); ///< "Minute"
|
||||
IRTEXT_CONST_STRING(kMinutesStr, D_STR_MINUTES); ///< "Minutes"
|
||||
IRTEXT_CONST_STRING(kSecondStr, D_STR_SECOND); ///< "Second"
|
||||
IRTEXT_CONST_STRING(kSecondsStr, D_STR_SECONDS); ///< "Seconds"
|
||||
IRTEXT_CONST_STRING(kNowStr, D_STR_NOW); ///< "Now"
|
||||
IRTEXT_CONST_STRING(kThreeLetterDayOfWeekStr, D_STR_THREELETTERDAYS); ///<
|
||||
///< "SunMonTueWedThuFriSat"
|
||||
IRTEXT_CONST_STRING(kYesStr, D_STR_YES); ///< "Yes"
|
||||
IRTEXT_CONST_STRING(kNoStr, D_STR_NO); ///< "No"
|
||||
IRTEXT_CONST_STRING(kTrueStr, D_STR_TRUE); ///< "True"
|
||||
IRTEXT_CONST_STRING(kFalseStr, D_STR_FALSE); ///< "False"
|
||||
|
||||
IRTEXT_CONST_STRING(kRepeatStr, D_STR_REPEAT); ///< "Repeat"
|
||||
IRTEXT_CONST_STRING(kCodeStr, D_STR_CODE); ///< "Code"
|
||||
IRTEXT_CONST_STRING(kBitsStr, D_STR_BITS); ///< "Bits"
|
||||
|
||||
// Model Names
|
||||
IRTEXT_CONST_STRING(kYaw1fStr, D_STR_YAW1F); ///< "YAW1F"
|
||||
IRTEXT_CONST_STRING(kYbofbStr, D_STR_YBOFB); ///< "YBOFB"
|
||||
IRTEXT_CONST_STRING(kYx1fsfStr, D_STR_YX1FSF); ///< "YX1FSF"
|
||||
IRTEXT_CONST_STRING(kV9014557AStr, D_STR_V9014557_A); ///< "V9014557-A"
|
||||
IRTEXT_CONST_STRING(kV9014557BStr, D_STR_V9014557_B); ///< "V9014557-B"
|
||||
IRTEXT_CONST_STRING(kRlt0541htaaStr, D_STR_RLT0541HTA_A); ///< "R-LT0541-HTA-A"
|
||||
IRTEXT_CONST_STRING(kRlt0541htabStr, D_STR_RLT0541HTA_B); ///< "R-LT0541-HTA-B"
|
||||
IRTEXT_CONST_STRING(kArrah2eStr, D_STR_ARRAH2E); ///< "ARRAH2E"
|
||||
IRTEXT_CONST_STRING(kArdb1Str, D_STR_ARDB1); ///< "ARDB1"
|
||||
IRTEXT_CONST_STRING(kArreb1eStr, D_STR_ARREB1E); ///< "ARREB1E"
|
||||
IRTEXT_CONST_STRING(kArjw2Str, D_STR_ARJW2); ///< "ARJW2"
|
||||
IRTEXT_CONST_STRING(kArry4Str, D_STR_ARRY4); ///< "ARRY4"
|
||||
IRTEXT_CONST_STRING(kArrew4eStr, D_STR_ARREW4E); ///< "ARREW4E"
|
||||
IRTEXT_CONST_STRING(kGe6711ar2853mStr, D_STR_GE6711AR2853M); ///<
|
||||
///< "GE6711AR2853M"
|
||||
IRTEXT_CONST_STRING(kAkb75215403Str, D_STR_AKB75215403); ///< "AKB75215403"
|
||||
IRTEXT_CONST_STRING(kAkb74955603Str, D_STR_AKB74955603); ///< "AKB74955603"
|
||||
IRTEXT_CONST_STRING(kAkb73757604Str, D_STR_AKB73757604); ///< "AKB73757604"
|
||||
IRTEXT_CONST_STRING(kLg6711a20083vStr, D_STR_LG6711A20083V); ///<
|
||||
///< "LG6711A20083V"
|
||||
IRTEXT_CONST_STRING(kKkg9ac1Str, D_STR_KKG9AC1); ///< "KKG9AC1"
|
||||
IRTEXT_CONST_STRING(kKkg29ac1Str, D_STR_KKG29AC1); ///< "KKG29AC1"
|
||||
IRTEXT_CONST_STRING(kLkeStr, D_STR_LKE); ///< "LKE"
|
||||
IRTEXT_CONST_STRING(kNkeStr, D_STR_NKE); ///< "NKE"
|
||||
IRTEXT_CONST_STRING(kDkeStr, D_STR_DKE); ///< "DKE"
|
||||
IRTEXT_CONST_STRING(kPkrStr, D_STR_PKR); ///< "PKR"
|
||||
IRTEXT_CONST_STRING(kJkeStr, D_STR_JKE); ///< "JKE"
|
||||
IRTEXT_CONST_STRING(kCkpStr, D_STR_CKP); ///< "CKP"
|
||||
IRTEXT_CONST_STRING(kRkrStr, D_STR_RKR); ///< "RKR"
|
||||
IRTEXT_CONST_STRING(kPanasonicLkeStr, D_STR_PANASONICLKE); ///< "PANASONICLKE"
|
||||
IRTEXT_CONST_STRING(kPanasonicNkeStr, D_STR_PANASONICNKE); ///< "PANASONICNKE"
|
||||
IRTEXT_CONST_STRING(kPanasonicDkeStr, D_STR_PANASONICDKE); ///< "PANASONICDKE"
|
||||
IRTEXT_CONST_STRING(kPanasonicPkrStr, D_STR_PANASONICPKR); ///< "PANASONICPKR"
|
||||
IRTEXT_CONST_STRING(kPanasonicJkeStr, D_STR_PANASONICJKE); ///< "PANASONICJKE"
|
||||
IRTEXT_CONST_STRING(kPanasonicCkpStr, D_STR_PANASONICCKP); ///< "PANASONICCKP"
|
||||
IRTEXT_CONST_STRING(kPanasonicRkrStr, D_STR_PANASONICRKR); ///< "PANASONICRKR"
|
||||
IRTEXT_CONST_STRING(kA907Str, D_STR_A907); ///< "A907"
|
||||
IRTEXT_CONST_STRING(kA705Str, D_STR_A705); ///< "A705"
|
||||
IRTEXT_CONST_STRING(kA903Str, D_STR_A903); ///< "A903"
|
||||
IRTEXT_CONST_STRING(kTac09chsdStr, D_STR_TAC09CHSD); ///< "TAC09CHSD"
|
||||
IRTEXT_CONST_STRING(kGz055be1Str, D_STR_GZ055BE1); ///< "GZ055BE1"
|
||||
IRTEXT_CONST_STRING(k122lzfStr, D_STR_122LZF); ///< "122LZF"
|
||||
IRTEXT_CONST_STRING(kDg11j13aStr, D_STR_DG11J13A); ///< "DG11J13A"
|
||||
IRTEXT_CONST_STRING(kDg11j104Str, D_STR_DG11J104); ///< "DG11J104"
|
||||
IRTEXT_CONST_STRING(kDg11j191Str, D_STR_DG11J191); ///< "DG11J191"
|
||||
IRTEXT_CONST_STRING(kArgoWrem2Str, D_STR_ARGO_WREM2); ///< "WREM3"
|
||||
IRTEXT_CONST_STRING(kArgoWrem3Str, D_STR_ARGO_WREM3); ///< "WREM3"
|
||||
|
||||
#define D_STR_UNSUPPORTED "?" // Unsupported protocols will be showing as
|
||||
// a question mark, check for length > 1
|
||||
// to show only currently included protocols
|
||||
// Protocol Names
|
||||
// Needs to be in decode_type_t order.
|
||||
IRTEXT_CONST_BLOB_DECL(kAllProtocolNamesStr) {
|
||||
D_STR_UNUSED "\x0"
|
||||
COND(DECODE_RC5 || SEND_RC5,
|
||||
D_STR_RC5, D_STR_UNSUPPORTED) "\x0"
|
||||
COND(DECODE_RC6 || SEND_RC6,
|
||||
D_STR_RC6, D_STR_UNSUPPORTED) "\x0"
|
||||
COND(DECODE_NEC || SEND_NEC,
|
||||
D_STR_NEC, D_STR_UNSUPPORTED) "\x0"
|
||||
COND(DECODE_SONY || SEND_SONY,
|
||||
D_STR_SONY, D_STR_UNSUPPORTED) "\x0"
|
||||
COND(DECODE_PANASONIC || SEND_PANASONIC,
|
||||
D_STR_PANASONIC, D_STR_UNSUPPORTED) "\x0"
|
||||
COND(DECODE_JVC || SEND_JVC,
|
||||
D_STR_JVC, D_STR_UNSUPPORTED) "\x0"
|
||||
COND(DECODE_SAMSUNG || SEND_SAMSUNG,
|
||||
D_STR_SAMSUNG, D_STR_UNSUPPORTED) "\x0"
|
||||
COND(DECODE_WHYNTER || SEND_WHYNTER,
|
||||
D_STR_WHYNTER, D_STR_UNSUPPORTED) "\x0"
|
||||
COND(DECODE_AIWA_RC_T501 || SEND_AIWA_RC_T501,
|
||||
D_STR_AIWA_RC_T501, D_STR_UNSUPPORTED) "\x0"
|
||||
COND(DECODE_LG || SEND_LG,
|
||||
D_STR_LG, D_STR_UNSUPPORTED) "\x0"
|
||||
COND(DECODE_SANYO || SEND_SANYO,
|
||||
D_STR_SANYO, D_STR_UNSUPPORTED) "\x0"
|
||||
COND(DECODE_MITSUBISHI || SEND_MITSUBISHI,
|
||||
D_STR_MITSUBISHI, D_STR_UNSUPPORTED) "\x0"
|
||||
COND(DECODE_DISH || SEND_DISH,
|
||||
D_STR_DISH, D_STR_UNSUPPORTED) "\x0"
|
||||
COND(DECODE_SHARP || SEND_SHARP,
|
||||
D_STR_SHARP, D_STR_UNSUPPORTED) "\x0"
|
||||
COND(DECODE_COOLIX || SEND_COOLIX,
|
||||
D_STR_COOLIX, D_STR_UNSUPPORTED) "\x0"
|
||||
COND(DECODE_DAIKIN || SEND_DAIKIN,
|
||||
D_STR_DAIKIN, D_STR_UNSUPPORTED) "\x0"
|
||||
COND(DECODE_DENON || SEND_DENON,
|
||||
D_STR_DENON, D_STR_UNSUPPORTED) "\x0"
|
||||
COND(DECODE_KELVINATOR || SEND_KELVINATOR,
|
||||
D_STR_KELVINATOR, D_STR_UNSUPPORTED) "\x0"
|
||||
COND(SEND_SHERWOOD,
|
||||
D_STR_SHERWOOD, D_STR_UNSUPPORTED) "\x0" // SEND-ONLY
|
||||
COND(DECODE_MITSUBISHI_AC || SEND_MITSUBISHI_AC,
|
||||
D_STR_MITSUBISHI_AC, D_STR_UNSUPPORTED) "\x0"
|
||||
COND(DECODE_RCMM || SEND_RCMM,
|
||||
D_STR_RCMM, D_STR_UNSUPPORTED) "\x0"
|
||||
COND(DECODE_SANYO || SEND_SANYO,
|
||||
D_STR_SANYO_LC7461, D_STR_UNSUPPORTED) "\x0"
|
||||
COND(DECODE_RC5 || SEND_RC5,
|
||||
D_STR_RC5X, D_STR_UNSUPPORTED) "\x0"
|
||||
COND(DECODE_GREE || SEND_GREE,
|
||||
D_STR_GREE, D_STR_UNSUPPORTED) "\x0"
|
||||
COND(SEND_PRONTO,
|
||||
D_STR_PRONTO, D_STR_UNSUPPORTED) "\x0" // SEND-ONLY
|
||||
COND(DECODE_NEC || SEND_NEC,
|
||||
D_STR_NEC_LIKE, D_STR_UNSUPPORTED) "\x0"
|
||||
COND(DECODE_ARGO || SEND_ARGO,
|
||||
D_STR_ARGO, D_STR_UNSUPPORTED) "\x0"
|
||||
COND(DECODE_TROTEC || SEND_TROTEC,
|
||||
D_STR_TROTEC, D_STR_UNSUPPORTED) "\x0"
|
||||
COND(DECODE_NIKAI || SEND_NIKAI,
|
||||
D_STR_NIKAI, D_STR_UNSUPPORTED) "\x0"
|
||||
COND(SEND_RAW,
|
||||
D_STR_RAW, D_STR_UNSUPPORTED) "\x0" // SEND-ONLY
|
||||
COND(SEND_GLOBALCACHE,
|
||||
D_STR_GLOBALCACHE, D_STR_UNSUPPORTED) "\x0" // SEND
|
||||
COND(DECODE_TOSHIBA_AC || SEND_TOSHIBA_AC,
|
||||
D_STR_TOSHIBA_AC, D_STR_UNSUPPORTED) "\x0"
|
||||
COND(DECODE_FUJITSU_AC || SEND_FUJITSU_AC,
|
||||
D_STR_FUJITSU_AC, D_STR_UNSUPPORTED) "\x0"
|
||||
COND(DECODE_MIDEA || SEND_MIDEA,
|
||||
D_STR_MIDEA, D_STR_UNSUPPORTED) "\x0"
|
||||
COND(DECODE_MAGIQUEST || SEND_MAGIQUEST,
|
||||
D_STR_MAGIQUEST, D_STR_UNSUPPORTED) "\x0"
|
||||
COND(DECODE_LASERTAG || SEND_LASERTAG,
|
||||
D_STR_LASERTAG, D_STR_UNSUPPORTED) "\x0"
|
||||
COND(DECODE_CARRIER_AC || SEND_CARRIER_AC,
|
||||
D_STR_CARRIER_AC, D_STR_UNSUPPORTED) "\x0"
|
||||
COND(DECODE_HAIER_AC || SEND_HAIER_AC,
|
||||
D_STR_HAIER_AC, D_STR_UNSUPPORTED) "\x0"
|
||||
COND(DECODE_MITSUBISHI2 || SEND_MITSUBISHI2,
|
||||
D_STR_MITSUBISHI2, D_STR_UNSUPPORTED) "\x0"
|
||||
COND(DECODE_HITACHI_AC || SEND_HITACHI_AC,
|
||||
D_STR_HITACHI_AC, D_STR_UNSUPPORTED) "\x0"
|
||||
COND(DECODE_HITACHI_AC1 || SEND_HITACHI_AC1,
|
||||
D_STR_HITACHI_AC1, D_STR_UNSUPPORTED) "\x0"
|
||||
COND(DECODE_HITACHI_AC2 || SEND_HITACHI_AC2,
|
||||
D_STR_HITACHI_AC2, D_STR_UNSUPPORTED) "\x0"
|
||||
COND(DECODE_GICABLE || SEND_GICABLE,
|
||||
D_STR_GICABLE, D_STR_UNSUPPORTED) "\x0"
|
||||
COND(DECODE_HAIER_AC_YRW02 || SEND_HAIER_AC_YRW02,
|
||||
D_STR_HAIER_AC_YRW02, D_STR_UNSUPPORTED) "\x0"
|
||||
COND(DECODE_WHIRLPOOL_AC || SEND_WHIRLPOOL_AC,
|
||||
D_STR_WHIRLPOOL_AC, D_STR_UNSUPPORTED) "\x0"
|
||||
COND(DECODE_SAMSUNG_AC || SEND_SAMSUNG_AC,
|
||||
D_STR_SAMSUNG_AC, D_STR_UNSUPPORTED) "\x0"
|
||||
COND(DECODE_LUTRON || SEND_LUTRON,
|
||||
D_STR_LUTRON, D_STR_UNSUPPORTED) "\x0"
|
||||
COND(DECODE_ELECTRA_AC || SEND_ELECTRA_AC,
|
||||
D_STR_ELECTRA_AC, D_STR_UNSUPPORTED) "\x0"
|
||||
COND(DECODE_PANASONIC_AC || SEND_PANASONIC_AC,
|
||||
D_STR_PANASONIC_AC, D_STR_UNSUPPORTED) "\x0"
|
||||
COND(DECODE_PIONEER || SEND_PIONEER,
|
||||
D_STR_PIONEER, D_STR_UNSUPPORTED) "\x0"
|
||||
COND(DECODE_LG || SEND_LG,
|
||||
D_STR_LG2, D_STR_UNSUPPORTED) "\x0"
|
||||
COND(DECODE_MWM || SEND_MWM,
|
||||
D_STR_MWM, D_STR_UNSUPPORTED) "\x0"
|
||||
COND(DECODE_DAIKIN2 || SEND_DAIKIN2,
|
||||
D_STR_DAIKIN2, D_STR_UNSUPPORTED) "\x0"
|
||||
COND(DECODE_VESTEL_AC || SEND_VESTEL_AC,
|
||||
D_STR_VESTEL_AC, D_STR_UNSUPPORTED) "\x0"
|
||||
COND(DECODE_TECO || SEND_TECO,
|
||||
D_STR_TECO, D_STR_UNSUPPORTED) "\x0"
|
||||
COND(DECODE_SAMSUNG36 || SEND_SAMSUNG36,
|
||||
D_STR_SAMSUNG36, D_STR_UNSUPPORTED) "\x0"
|
||||
COND(DECODE_TCL112AC || SEND_TCL112AC,
|
||||
D_STR_TCL112AC, D_STR_UNSUPPORTED) "\x0"
|
||||
COND(DECODE_LEGOPF || SEND_LEGOPF,
|
||||
D_STR_LEGOPF, D_STR_UNSUPPORTED) "\x0"
|
||||
COND(DECODE_MITSUBISHIHEAVY || SEND_MITSUBISHIHEAVY,
|
||||
D_STR_MITSUBISHI_HEAVY_88, D_STR_UNSUPPORTED) "\x0"
|
||||
COND(DECODE_MITSUBISHIHEAVY || SEND_MITSUBISHIHEAVY,
|
||||
D_STR_MITSUBISHI_HEAVY_152, D_STR_UNSUPPORTED) "\x0"
|
||||
COND(DECODE_DAIKIN216 || SEND_DAIKIN216,
|
||||
D_STR_DAIKIN216, D_STR_UNSUPPORTED) "\x0"
|
||||
COND(DECODE_SHARP_AC || SEND_SHARP_AC,
|
||||
D_STR_SHARP_AC, D_STR_UNSUPPORTED) "\x0"
|
||||
COND(DECODE_GOODWEATHER || SEND_GOODWEATHER,
|
||||
D_STR_GOODWEATHER, D_STR_UNSUPPORTED) "\x0"
|
||||
COND(DECODE_INAX || SEND_INAX,
|
||||
D_STR_INAX, D_STR_UNSUPPORTED) "\x0"
|
||||
COND(DECODE_DAIKIN160 || SEND_DAIKIN160,
|
||||
D_STR_DAIKIN160, D_STR_UNSUPPORTED) "\x0"
|
||||
COND(DECODE_NEOCLIMA || SEND_NEOCLIMA,
|
||||
D_STR_NEOCLIMA, D_STR_UNSUPPORTED) "\x0"
|
||||
COND(DECODE_DAIKIN176 || SEND_DAIKIN176,
|
||||
D_STR_DAIKIN176, D_STR_UNSUPPORTED) "\x0"
|
||||
COND(DECODE_DAIKIN128 || SEND_DAIKIN128,
|
||||
D_STR_DAIKIN128, D_STR_UNSUPPORTED) "\x0"
|
||||
COND(DECODE_AMCOR || SEND_AMCOR,
|
||||
D_STR_AMCOR, D_STR_UNSUPPORTED) "\x0"
|
||||
COND(DECODE_DAIKIN152 || SEND_DAIKIN152,
|
||||
D_STR_DAIKIN152, D_STR_UNSUPPORTED) "\x0"
|
||||
COND(DECODE_MITSUBISHI136 || SEND_MITSUBISHI136,
|
||||
D_STR_MITSUBISHI136, D_STR_UNSUPPORTED) "\x0"
|
||||
COND(DECODE_MITSUBISHI112 || SEND_MITSUBISHI112,
|
||||
D_STR_MITSUBISHI112, D_STR_UNSUPPORTED) "\x0"
|
||||
COND(DECODE_HITACHI_AC424 || SEND_HITACHI_AC424,
|
||||
D_STR_HITACHI_AC424, D_STR_UNSUPPORTED) "\x0"
|
||||
COND(SEND_SONY,
|
||||
D_STR_SONY_38K, D_STR_UNSUPPORTED) "\x0"
|
||||
COND(DECODE_EPSON || SEND_EPSON,
|
||||
D_STR_EPSON, D_STR_UNSUPPORTED) "\x0"
|
||||
COND(DECODE_SYMPHONY || SEND_SYMPHONY,
|
||||
D_STR_SYMPHONY, D_STR_UNSUPPORTED) "\x0"
|
||||
COND(DECODE_HITACHI_AC3 || SEND_HITACHI_AC3,
|
||||
D_STR_HITACHI_AC3, D_STR_UNSUPPORTED) "\x0"
|
||||
COND(DECODE_DAIKIN64 || SEND_DAIKIN64,
|
||||
D_STR_DAIKIN64, D_STR_UNSUPPORTED) "\x0"
|
||||
COND(DECODE_AIRWELL || SEND_AIRWELL,
|
||||
D_STR_AIRWELL, D_STR_UNSUPPORTED) "\x0"
|
||||
COND(DECODE_DELONGHI_AC || SEND_DELONGHI_AC,
|
||||
D_STR_DELONGHI_AC, D_STR_UNSUPPORTED) "\x0"
|
||||
COND(DECODE_DOSHISHA || SEND_DOSHISHA,
|
||||
D_STR_DOSHISHA, D_STR_UNSUPPORTED) "\x0"
|
||||
COND(DECODE_MULTIBRACKETS || SEND_MULTIBRACKETS,
|
||||
D_STR_MULTIBRACKETS, D_STR_UNSUPPORTED) "\x0"
|
||||
COND(DECODE_CARRIER_AC40 || SEND_CARRIER_AC40,
|
||||
D_STR_CARRIER_AC40, D_STR_UNSUPPORTED) "\x0"
|
||||
COND(DECODE_CARRIER_AC64 || SEND_CARRIER_AC64,
|
||||
D_STR_CARRIER_AC64, D_STR_UNSUPPORTED) "\x0"
|
||||
COND(DECODE_HITACHI_AC344 || SEND_HITACHI_AC344,
|
||||
D_STR_HITACHI_AC344, D_STR_UNSUPPORTED) "\x0"
|
||||
COND(DECODE_CORONA_AC || SEND_CORONA_AC,
|
||||
D_STR_CORONA_AC, D_STR_UNSUPPORTED) "\x0"
|
||||
COND(DECODE_MIDEA24 || SEND_MIDEA24,
|
||||
D_STR_MIDEA24, D_STR_UNSUPPORTED) "\x0"
|
||||
COND(DECODE_ZEPEAL || SEND_ZEPEAL,
|
||||
D_STR_ZEPEAL, D_STR_UNSUPPORTED) "\x0"
|
||||
COND(DECODE_SANYO_AC || SEND_SANYO_AC,
|
||||
D_STR_SANYO_AC, D_STR_UNSUPPORTED) "\x0"
|
||||
COND(DECODE_VOLTAS || SEND_VOLTAS,
|
||||
D_STR_VOLTAS, D_STR_UNSUPPORTED) "\x0"
|
||||
COND(DECODE_METZ || SEND_METZ,
|
||||
D_STR_METZ, D_STR_UNSUPPORTED) "\x0"
|
||||
COND(DECODE_TRANSCOLD || SEND_TRANSCOLD,
|
||||
D_STR_TRANSCOLD, D_STR_UNSUPPORTED) "\x0"
|
||||
COND(DECODE_TECHNIBEL_AC || SEND_TECHNIBEL_AC,
|
||||
D_STR_TECHNIBEL_AC, D_STR_UNSUPPORTED) "\x0"
|
||||
COND(DECODE_MIRAGE || SEND_MIRAGE,
|
||||
D_STR_MIRAGE, D_STR_UNSUPPORTED) "\x0"
|
||||
COND(DECODE_ELITESCREENS || SEND_ELITESCREENS,
|
||||
D_STR_ELITESCREENS, D_STR_UNSUPPORTED) "\x0"
|
||||
COND(DECODE_PANASONIC_AC32 || SEND_PANASONIC_AC32,
|
||||
D_STR_PANASONIC_AC32, D_STR_UNSUPPORTED) "\x0"
|
||||
COND(DECODE_MILESTAG2 || SEND_MILESTAG2,
|
||||
D_STR_MILESTAG2, D_STR_UNSUPPORTED) "\x0"
|
||||
COND(DECODE_ECOCLIM || SEND_ECOCLIM,
|
||||
D_STR_ECOCLIM, D_STR_UNSUPPORTED) "\x0"
|
||||
COND(DECODE_XMP || SEND_XMP,
|
||||
D_STR_XMP, D_STR_UNSUPPORTED) "\x0"
|
||||
COND(DECODE_TRUMA || SEND_TRUMA,
|
||||
D_STR_TRUMA, D_STR_UNSUPPORTED) "\x0"
|
||||
COND(DECODE_HAIER_AC176 || SEND_HAIER_AC176,
|
||||
D_STR_HAIER_AC176, D_STR_UNSUPPORTED) "\x0"
|
||||
COND(DECODE_TEKNOPOINT || SEND_TEKNOPOINT,
|
||||
D_STR_TEKNOPOINT, D_STR_UNSUPPORTED) "\x0"
|
||||
COND(DECODE_KELON || SEND_KELON,
|
||||
D_STR_KELON, D_STR_UNSUPPORTED) "\x0"
|
||||
COND(DECODE_TROTEC_3550 || SEND_TROTEC_3550,
|
||||
D_STR_TROTEC_3550, D_STR_UNSUPPORTED) "\x0"
|
||||
COND(DECODE_SANYO_AC88 || SEND_SANYO_AC88,
|
||||
D_STR_SANYO_AC88, D_STR_UNSUPPORTED) "\x0"
|
||||
COND(DECODE_BOSE || SEND_BOSE,
|
||||
D_STR_BOSE, D_STR_UNSUPPORTED) "\x0"
|
||||
COND(DECODE_ARRIS || SEND_ARRIS,
|
||||
D_STR_ARRIS, D_STR_UNSUPPORTED) "\x0"
|
||||
COND(DECODE_RHOSS || SEND_RHOSS,
|
||||
D_STR_RHOSS, D_STR_UNSUPPORTED) "\x0"
|
||||
COND(DECODE_AIRTON || SEND_AIRTON,
|
||||
D_STR_AIRTON, D_STR_UNSUPPORTED) "\x0"
|
||||
COND(DECODE_COOLIX48 || SEND_COOLIX48,
|
||||
D_STR_COOLIX48, D_STR_UNSUPPORTED) "\x0"
|
||||
COND(DECODE_HITACHI_AC264 || SEND_HITACHI_AC264,
|
||||
D_STR_HITACHI_AC264, D_STR_UNSUPPORTED) "\x0"
|
||||
COND(DECODE_KELON168 || SEND_KELON168,
|
||||
D_STR_KELON168, D_STR_UNSUPPORTED) "\x0"
|
||||
COND(DECODE_HITACHI_AC296 || SEND_HITACHI_AC296,
|
||||
D_STR_HITACHI_AC296, D_STR_UNSUPPORTED) "\x0"
|
||||
COND(DECODE_DAIKIN200 || SEND_DAIKIN200,
|
||||
D_STR_DAIKIN200, D_STR_UNSUPPORTED) "\x0"
|
||||
COND(DECODE_HAIER_AC160 || SEND_HAIER_AC160,
|
||||
D_STR_HAIER_AC160, D_STR_UNSUPPORTED) "\x0"
|
||||
COND(DECODE_CARRIER_AC128 || SEND_CARRIER_AC128,
|
||||
D_STR_CARRIER_AC128, D_STR_UNSUPPORTED) "\x0"
|
||||
COND(DECODE_TOTO || SEND_TOTO,
|
||||
D_STR_TOTO, D_STR_UNSUPPORTED) "\x0"
|
||||
COND(DECODE_CLIMABUTLER || SEND_CLIMABUTLER,
|
||||
D_STR_CLIMABUTLER, D_STR_UNSUPPORTED) "\x0"
|
||||
COND(DECODE_TCL96AC || SEND_TCL96AC,
|
||||
D_STR_TCL96AC, D_STR_UNSUPPORTED) "\x0"
|
||||
COND(DECODE_BOSCH144 || SEND_BOSCH144,
|
||||
D_STR_BOSCH144, D_STR_UNSUPPORTED) "\x0"
|
||||
COND(DECODE_SANYO_AC152 || SEND_SANYO_AC152,
|
||||
D_STR_SANYO_AC152, D_STR_UNSUPPORTED) "\x0"
|
||||
COND(DECODE_DAIKIN312 || SEND_DAIKIN312,
|
||||
D_STR_DAIKIN312, D_STR_UNSUPPORTED) "\x0"
|
||||
COND(DECODE_GORENJE || SEND_GORENJE,
|
||||
D_STR_GORENJE, D_STR_UNSUPPORTED) "\x0"
|
||||
COND(DECODE_WOWWEE || SEND_WOWWEE,
|
||||
D_STR_WOWWEE, D_STR_UNSUPPORTED) "\x0"
|
||||
COND(DECODE_CARRIER_AC84 || SEND_CARRIER_AC84,
|
||||
D_STR_CARRIER_AC84, D_STR_UNSUPPORTED) "\x0"
|
||||
COND(DECODE_YORK || SEND_YORK,
|
||||
D_STR_YORK, D_STR_UNSUPPORTED) "\x0"
|
||||
///< New protocol (macro) strings should be added just above this line.
|
||||
"\x0" ///< This string requires double null termination.
|
||||
};
|
||||
IRTEXT_CONST_BLOB_PTR(kAllProtocolNamesStr);
|
||||
256
yoRadio/src/IRremoteESP8266/IRtext.h
Normal file
256
yoRadio/src/IRremoteESP8266/IRtext.h
Normal file
@@ -0,0 +1,256 @@
|
||||
// Copyright 2019-2022 - David Conran (@crankyoldgit)
|
||||
// This header file is to be included in files **other than** 'IRtext.cpp'.
|
||||
//
|
||||
// WARNING: Do not edit this file! This file is automatically generated by
|
||||
// '../tools/generate_irtext_h.sh'.
|
||||
|
||||
#ifndef IRTEXT_H_
|
||||
#define IRTEXT_H_
|
||||
|
||||
#include "i18n.h"
|
||||
|
||||
// Constant text to be shared across all object files.
|
||||
// This means there is only one copy of the character/string/text etc.
|
||||
|
||||
#ifdef ESP8266
|
||||
class __FlashStringHelper;
|
||||
#define IRTEXT_CONST_PTR_CAST(PTR)\
|
||||
reinterpret_cast<const __FlashStringHelper*>(PTR)
|
||||
#define IRTEXT_CONST_PTR(NAME) const __FlashStringHelper* const NAME
|
||||
#else // ESP8266
|
||||
#define IRTEXT_CONST_PTR_CAST(PTR) PTR
|
||||
#define IRTEXT_CONST_PTR(NAME) const char* const NAME
|
||||
#endif // ESP8266
|
||||
|
||||
extern const char kTimeSep;
|
||||
extern IRTEXT_CONST_PTR(k0Str);
|
||||
extern IRTEXT_CONST_PTR(k10CHeatStr);
|
||||
extern IRTEXT_CONST_PTR(k122lzfStr);
|
||||
extern IRTEXT_CONST_PTR(k1Str);
|
||||
extern IRTEXT_CONST_PTR(k3DStr);
|
||||
extern IRTEXT_CONST_PTR(k6thSenseStr);
|
||||
extern IRTEXT_CONST_PTR(k8CHeatStr);
|
||||
extern IRTEXT_CONST_PTR(kA705Str);
|
||||
extern IRTEXT_CONST_PTR(kA903Str);
|
||||
extern IRTEXT_CONST_PTR(kA907Str);
|
||||
extern IRTEXT_CONST_PTR(kAbsenseDetectStr);
|
||||
extern IRTEXT_CONST_PTR(kAirFlowStr);
|
||||
extern IRTEXT_CONST_PTR(kAkb73757604Str);
|
||||
extern IRTEXT_CONST_PTR(kAkb74955603Str);
|
||||
extern IRTEXT_CONST_PTR(kAkb75215403Str);
|
||||
extern IRTEXT_CONST_PTR(kArdb1Str);
|
||||
extern IRTEXT_CONST_PTR(kArgoWrem2Str);
|
||||
extern IRTEXT_CONST_PTR(kArgoWrem3Str);
|
||||
extern IRTEXT_CONST_PTR(kArjw2Str);
|
||||
extern IRTEXT_CONST_PTR(kArrah2eStr);
|
||||
extern IRTEXT_CONST_PTR(kArreb1eStr);
|
||||
extern IRTEXT_CONST_PTR(kArrew4eStr);
|
||||
extern IRTEXT_CONST_PTR(kArry4Str);
|
||||
extern IRTEXT_CONST_PTR(kAutoStr);
|
||||
extern IRTEXT_CONST_PTR(kAutomaticStr);
|
||||
extern IRTEXT_CONST_PTR(kBeepStr);
|
||||
extern IRTEXT_CONST_PTR(kBitsStr);
|
||||
extern IRTEXT_CONST_PTR(kBottomStr);
|
||||
extern IRTEXT_CONST_PTR(kBreezeStr);
|
||||
extern IRTEXT_CONST_PTR(kButtonStr);
|
||||
extern IRTEXT_CONST_PTR(kCancelStr);
|
||||
extern IRTEXT_CONST_PTR(kCeilingStr);
|
||||
extern IRTEXT_CONST_PTR(kCelsiusFahrenheitStr);
|
||||
extern IRTEXT_CONST_PTR(kCelsiusStr);
|
||||
extern IRTEXT_CONST_PTR(kCentreStr);
|
||||
extern IRTEXT_CONST_PTR(kChangeStr);
|
||||
extern IRTEXT_CONST_PTR(kChStr);
|
||||
extern IRTEXT_CONST_PTR(kCirculateStr);
|
||||
extern IRTEXT_CONST_PTR(kCkpStr);
|
||||
extern IRTEXT_CONST_PTR(kCleanStr);
|
||||
extern IRTEXT_CONST_PTR(kClockStr);
|
||||
extern IRTEXT_CONST_PTR(kCodeStr);
|
||||
extern IRTEXT_CONST_PTR(kColonSpaceStr);
|
||||
extern IRTEXT_CONST_PTR(kComfortStr);
|
||||
extern IRTEXT_CONST_PTR(kCommaSpaceStr);
|
||||
extern IRTEXT_CONST_PTR(kCommandStr);
|
||||
extern IRTEXT_CONST_PTR(kConfigCommandStr);
|
||||
extern IRTEXT_CONST_PTR(kControlCommandStr);
|
||||
extern IRTEXT_CONST_PTR(kCoolStr);
|
||||
extern IRTEXT_CONST_PTR(kCoolingStr);
|
||||
extern IRTEXT_CONST_PTR(kDashStr);
|
||||
extern IRTEXT_CONST_PTR(kDayStr);
|
||||
extern IRTEXT_CONST_PTR(kDaysStr);
|
||||
extern IRTEXT_CONST_PTR(kDehumidifyStr);
|
||||
extern IRTEXT_CONST_PTR(kDg11j104Str);
|
||||
extern IRTEXT_CONST_PTR(kDg11j13aStr);
|
||||
extern IRTEXT_CONST_PTR(kDg11j191Str);
|
||||
extern IRTEXT_CONST_PTR(kDirectIndirectModeStr);
|
||||
extern IRTEXT_CONST_PTR(kDirectStr);
|
||||
extern IRTEXT_CONST_PTR(kDisplayTempStr);
|
||||
extern IRTEXT_CONST_PTR(kDkeStr);
|
||||
extern IRTEXT_CONST_PTR(kDownStr);
|
||||
extern IRTEXT_CONST_PTR(kDryStr);
|
||||
extern IRTEXT_CONST_PTR(kDryingStr);
|
||||
extern IRTEXT_CONST_PTR(kEconoStr);
|
||||
extern IRTEXT_CONST_PTR(kEconoToggleStr);
|
||||
extern IRTEXT_CONST_PTR(kEyeAutoStr);
|
||||
extern IRTEXT_CONST_PTR(kEyeStr);
|
||||
extern IRTEXT_CONST_PTR(kFalseStr);
|
||||
extern IRTEXT_CONST_PTR(kFanOnlyNoSpaceStr);
|
||||
extern IRTEXT_CONST_PTR(kFanOnlyStr);
|
||||
extern IRTEXT_CONST_PTR(kFanOnlyWithSpaceStr);
|
||||
extern IRTEXT_CONST_PTR(kFanStr);
|
||||
extern IRTEXT_CONST_PTR(kFan_OnlyStr);
|
||||
extern IRTEXT_CONST_PTR(kFastStr);
|
||||
extern IRTEXT_CONST_PTR(kFilterStr);
|
||||
extern IRTEXT_CONST_PTR(kFixedStr);
|
||||
extern IRTEXT_CONST_PTR(kFollowStr);
|
||||
extern IRTEXT_CONST_PTR(kFreshStr);
|
||||
extern IRTEXT_CONST_PTR(kGe6711ar2853mStr);
|
||||
extern IRTEXT_CONST_PTR(kGz055be1Str);
|
||||
extern IRTEXT_CONST_PTR(kHealthStr);
|
||||
extern IRTEXT_CONST_PTR(kHeatStr);
|
||||
extern IRTEXT_CONST_PTR(kHeatingStr);
|
||||
extern IRTEXT_CONST_PTR(kHiStr);
|
||||
extern IRTEXT_CONST_PTR(kHighStr);
|
||||
extern IRTEXT_CONST_PTR(kHighestStr);
|
||||
extern IRTEXT_CONST_PTR(kHoldStr);
|
||||
extern IRTEXT_CONST_PTR(kHourStr);
|
||||
extern IRTEXT_CONST_PTR(kHoursStr);
|
||||
extern IRTEXT_CONST_PTR(kHumidStr);
|
||||
extern IRTEXT_CONST_PTR(kIFeelReportStr);
|
||||
extern IRTEXT_CONST_PTR(kIFeelStr);
|
||||
extern IRTEXT_CONST_PTR(kISeeStr);
|
||||
extern IRTEXT_CONST_PTR(kIdStr);
|
||||
extern IRTEXT_CONST_PTR(kIndirectStr);
|
||||
extern IRTEXT_CONST_PTR(kInsideStr);
|
||||
extern IRTEXT_CONST_PTR(kIonStr);
|
||||
extern IRTEXT_CONST_PTR(kJkeStr);
|
||||
extern IRTEXT_CONST_PTR(kKeyStr);
|
||||
extern IRTEXT_CONST_PTR(kKkg29ac1Str);
|
||||
extern IRTEXT_CONST_PTR(kKkg9ac1Str);
|
||||
extern IRTEXT_CONST_PTR(kLastStr);
|
||||
extern IRTEXT_CONST_PTR(kLeftMaxNoSpaceStr);
|
||||
extern IRTEXT_CONST_PTR(kLeftMaxStr);
|
||||
extern IRTEXT_CONST_PTR(kLeftStr);
|
||||
extern IRTEXT_CONST_PTR(kLg6711a20083vStr);
|
||||
extern IRTEXT_CONST_PTR(kLightStr);
|
||||
extern IRTEXT_CONST_PTR(kLightToggleStr);
|
||||
extern IRTEXT_CONST_PTR(kLkeStr);
|
||||
extern IRTEXT_CONST_PTR(kLoStr);
|
||||
extern IRTEXT_CONST_PTR(kLockStr);
|
||||
extern IRTEXT_CONST_PTR(kLoudStr);
|
||||
extern IRTEXT_CONST_PTR(kLowStr);
|
||||
extern IRTEXT_CONST_PTR(kLowerStr);
|
||||
extern IRTEXT_CONST_PTR(kLowestStr);
|
||||
extern IRTEXT_CONST_PTR(kManualStr);
|
||||
extern IRTEXT_CONST_PTR(kMaxLeftNoSpaceStr);
|
||||
extern IRTEXT_CONST_PTR(kMaxLeftStr);
|
||||
extern IRTEXT_CONST_PTR(kMaxRightNoSpaceStr);
|
||||
extern IRTEXT_CONST_PTR(kMaxRightStr);
|
||||
extern IRTEXT_CONST_PTR(kMaxStr);
|
||||
extern IRTEXT_CONST_PTR(kMaximumStr);
|
||||
extern IRTEXT_CONST_PTR(kMedHighStr);
|
||||
extern IRTEXT_CONST_PTR(kMedStr);
|
||||
extern IRTEXT_CONST_PTR(kMediumStr);
|
||||
extern IRTEXT_CONST_PTR(kMidStr);
|
||||
extern IRTEXT_CONST_PTR(kMiddleStr);
|
||||
extern IRTEXT_CONST_PTR(kMinStr);
|
||||
extern IRTEXT_CONST_PTR(kMinimumStr);
|
||||
extern IRTEXT_CONST_PTR(kMinuteStr);
|
||||
extern IRTEXT_CONST_PTR(kMinutesStr);
|
||||
extern IRTEXT_CONST_PTR(kModeStr);
|
||||
extern IRTEXT_CONST_PTR(kModelStr);
|
||||
extern IRTEXT_CONST_PTR(kMouldStr);
|
||||
extern IRTEXT_CONST_PTR(kMoveStr);
|
||||
extern IRTEXT_CONST_PTR(kNAStr);
|
||||
extern IRTEXT_CONST_PTR(kNightStr);
|
||||
extern IRTEXT_CONST_PTR(kNkeStr);
|
||||
extern IRTEXT_CONST_PTR(kNoStr);
|
||||
extern IRTEXT_CONST_PTR(kNowStr);
|
||||
extern IRTEXT_CONST_PTR(kOffStr);
|
||||
extern IRTEXT_CONST_PTR(kOffTimerStr);
|
||||
extern IRTEXT_CONST_PTR(kOnStr);
|
||||
extern IRTEXT_CONST_PTR(kOnTimerStr);
|
||||
extern IRTEXT_CONST_PTR(kOutsideQuietStr);
|
||||
extern IRTEXT_CONST_PTR(kOutsideStr);
|
||||
extern IRTEXT_CONST_PTR(kPanasonicCkpStr);
|
||||
extern IRTEXT_CONST_PTR(kPanasonicDkeStr);
|
||||
extern IRTEXT_CONST_PTR(kPanasonicJkeStr);
|
||||
extern IRTEXT_CONST_PTR(kPanasonicLkeStr);
|
||||
extern IRTEXT_CONST_PTR(kPanasonicNkeStr);
|
||||
extern IRTEXT_CONST_PTR(kPanasonicPkrStr);
|
||||
extern IRTEXT_CONST_PTR(kPanasonicRkrStr);
|
||||
extern IRTEXT_CONST_PTR(kPkrStr);
|
||||
extern IRTEXT_CONST_PTR(kPowerButtonStr);
|
||||
extern IRTEXT_CONST_PTR(kPowerStr);
|
||||
extern IRTEXT_CONST_PTR(kPowerToggleStr);
|
||||
extern IRTEXT_CONST_PTR(kPowerfulStr);
|
||||
extern IRTEXT_CONST_PTR(kPreviousPowerStr);
|
||||
extern IRTEXT_CONST_PTR(kProtocolStr);
|
||||
extern IRTEXT_CONST_PTR(kPurifyStr);
|
||||
extern IRTEXT_CONST_PTR(kQuietStr);
|
||||
extern IRTEXT_CONST_PTR(kRecycleStr);
|
||||
extern IRTEXT_CONST_PTR(kRepeatStr);
|
||||
extern IRTEXT_CONST_PTR(kRightMaxNoSpaceStr);
|
||||
extern IRTEXT_CONST_PTR(kRightMaxStr);
|
||||
extern IRTEXT_CONST_PTR(kRightStr);
|
||||
extern IRTEXT_CONST_PTR(kRkrStr);
|
||||
extern IRTEXT_CONST_PTR(kRlt0541htaaStr);
|
||||
extern IRTEXT_CONST_PTR(kRlt0541htabStr);
|
||||
extern IRTEXT_CONST_PTR(kRoomStr);
|
||||
extern IRTEXT_CONST_PTR(kSaveStr);
|
||||
extern IRTEXT_CONST_PTR(kScheduleStr);
|
||||
extern IRTEXT_CONST_PTR(kSecondStr);
|
||||
extern IRTEXT_CONST_PTR(kSecondsStr);
|
||||
extern IRTEXT_CONST_PTR(kSensorReportStr);
|
||||
extern IRTEXT_CONST_PTR(kSensorStr);
|
||||
extern IRTEXT_CONST_PTR(kSensorTempStr);
|
||||
extern IRTEXT_CONST_PTR(kSetStr);
|
||||
extern IRTEXT_CONST_PTR(kSilentStr);
|
||||
extern IRTEXT_CONST_PTR(kSleepStr);
|
||||
extern IRTEXT_CONST_PTR(kSleepTimerStr);
|
||||
extern IRTEXT_CONST_PTR(kSlowStr);
|
||||
extern IRTEXT_CONST_PTR(kSpaceLBraceStr);
|
||||
extern IRTEXT_CONST_PTR(kSpecialStr);
|
||||
extern IRTEXT_CONST_PTR(kStartStr);
|
||||
extern IRTEXT_CONST_PTR(kStepStr);
|
||||
extern IRTEXT_CONST_PTR(kStopStr);
|
||||
extern IRTEXT_CONST_PTR(kSuperStr);
|
||||
extern IRTEXT_CONST_PTR(kSwingHStr);
|
||||
extern IRTEXT_CONST_PTR(kSwingStr);
|
||||
extern IRTEXT_CONST_PTR(kSwingVModeStr);
|
||||
extern IRTEXT_CONST_PTR(kSwingVStr);
|
||||
extern IRTEXT_CONST_PTR(kSwingVToggleStr);
|
||||
extern IRTEXT_CONST_PTR(kTac09chsdStr);
|
||||
extern IRTEXT_CONST_PTR(kTempDownStr);
|
||||
extern IRTEXT_CONST_PTR(kTempStr);
|
||||
extern IRTEXT_CONST_PTR(kTempUpStr);
|
||||
extern IRTEXT_CONST_PTR(kThreeLetterDayOfWeekStr);
|
||||
extern IRTEXT_CONST_PTR(kTimerActiveDaysStr);
|
||||
extern IRTEXT_CONST_PTR(kTimerModeStr);
|
||||
extern IRTEXT_CONST_PTR(kSetTimerCommandStr);
|
||||
extern IRTEXT_CONST_PTR(kTimerStr);
|
||||
extern IRTEXT_CONST_PTR(kToggleStr);
|
||||
extern IRTEXT_CONST_PTR(kTopStr);
|
||||
extern IRTEXT_CONST_PTR(kTrueStr);
|
||||
extern IRTEXT_CONST_PTR(kTurboStr);
|
||||
extern IRTEXT_CONST_PTR(kTurboToggleStr);
|
||||
extern IRTEXT_CONST_PTR(kTypeStr);
|
||||
extern IRTEXT_CONST_PTR(kUnknownStr);
|
||||
extern IRTEXT_CONST_PTR(kUpStr);
|
||||
extern IRTEXT_CONST_PTR(kUpperStr);
|
||||
extern IRTEXT_CONST_PTR(kUpperMiddleStr);
|
||||
extern IRTEXT_CONST_PTR(kValueStr);
|
||||
extern IRTEXT_CONST_PTR(kV9014557AStr);
|
||||
extern IRTEXT_CONST_PTR(kV9014557BStr);
|
||||
extern IRTEXT_CONST_PTR(kVaneStr);
|
||||
extern IRTEXT_CONST_PTR(kWallStr);
|
||||
extern IRTEXT_CONST_PTR(kWeeklyTimerStr);
|
||||
extern IRTEXT_CONST_PTR(kWideStr);
|
||||
extern IRTEXT_CONST_PTR(kWifiStr);
|
||||
extern IRTEXT_CONST_PTR(kXFanStr);
|
||||
extern IRTEXT_CONST_PTR(kYaw1fStr);
|
||||
extern IRTEXT_CONST_PTR(kYbofbStr);
|
||||
extern IRTEXT_CONST_PTR(kYesStr);
|
||||
extern IRTEXT_CONST_PTR(kYx1fsfStr);
|
||||
extern IRTEXT_CONST_PTR(kZoneFollowStr);
|
||||
extern IRTEXT_CONST_PTR(kAllProtocolNamesStr);
|
||||
|
||||
#endif // IRTEXT_H_
|
||||
78
yoRadio/src/IRremoteESP8266/IRtimer.cpp
Normal file
78
yoRadio/src/IRremoteESP8266/IRtimer.cpp
Normal file
@@ -0,0 +1,78 @@
|
||||
// Copyright 2017 David Conran
|
||||
|
||||
#include "IRtimer.h"
|
||||
#ifndef UNIT_TEST
|
||||
#include <Arduino.h>
|
||||
#endif
|
||||
|
||||
#ifdef UNIT_TEST
|
||||
// Used to help simulate elapsed time in unit tests.
|
||||
uint32_t _IRtimer_unittest_now = 0;
|
||||
uint32_t _TimerMs_unittest_now = 0;
|
||||
#endif // UNIT_TEST
|
||||
|
||||
/// Class constructor.
|
||||
IRtimer::IRtimer() { reset(); }
|
||||
|
||||
/// Resets the IRtimer object. I.e. The counter starts again from now.
|
||||
void IRtimer::reset() {
|
||||
#ifndef UNIT_TEST
|
||||
start = micros();
|
||||
#else
|
||||
start = _IRtimer_unittest_now;
|
||||
#endif
|
||||
}
|
||||
|
||||
/// Calculate how many microseconds have elapsed since the timer was started.
|
||||
/// @return Nr. of microseconds.
|
||||
uint32_t IRtimer::elapsed() {
|
||||
#ifndef UNIT_TEST
|
||||
uint32_t now = micros();
|
||||
#else
|
||||
uint32_t now = _IRtimer_unittest_now;
|
||||
#endif
|
||||
if (start <= now) // Check if the system timer has wrapped.
|
||||
return now - start; // No wrap.
|
||||
else
|
||||
return UINT32_MAX - start + now; // Has wrapped.
|
||||
}
|
||||
|
||||
/// Add time to the timer to simulate elapsed time.
|
||||
/// @param[in] usecs Nr. of uSeconds to be added.
|
||||
/// @note Only used in unit testing.
|
||||
#ifdef UNIT_TEST
|
||||
void IRtimer::add(uint32_t usecs) { _IRtimer_unittest_now += usecs; }
|
||||
#endif // UNIT_TEST
|
||||
|
||||
/// Class constructor.
|
||||
TimerMs::TimerMs() { reset(); }
|
||||
|
||||
/// Resets the TimerMs object. I.e. The counter starts again from now.
|
||||
void TimerMs::reset() {
|
||||
#ifndef UNIT_TEST
|
||||
start = millis();
|
||||
#else
|
||||
start = _TimerMs_unittest_now;
|
||||
#endif
|
||||
}
|
||||
|
||||
/// Calculate how many milliseconds have elapsed since the timer was started.
|
||||
/// @return Nr. of milliseconds.
|
||||
uint32_t TimerMs::elapsed() {
|
||||
#ifndef UNIT_TEST
|
||||
uint32_t now = millis();
|
||||
#else
|
||||
uint32_t now = _TimerMs_unittest_now;
|
||||
#endif
|
||||
if (start <= now) // Check if the system timer has wrapped.
|
||||
return now - start; // No wrap.
|
||||
else
|
||||
return UINT32_MAX - start + now; // Has wrapped.
|
||||
}
|
||||
|
||||
/// Add time to the timer to simulate elapsed time.
|
||||
/// @param[in] msecs Nr. of mSeconds to be added.
|
||||
/// @note Only used in unit testing.
|
||||
#ifdef UNIT_TEST
|
||||
void TimerMs::add(uint32_t msecs) { _IRtimer_unittest_now += msecs; }
|
||||
#endif // UNIT_TEST
|
||||
40
yoRadio/src/IRremoteESP8266/IRtimer.h
Normal file
40
yoRadio/src/IRremoteESP8266/IRtimer.h
Normal file
@@ -0,0 +1,40 @@
|
||||
// Copyright 2017 David Conran
|
||||
|
||||
#ifndef IRTIMER_H_
|
||||
#define IRTIMER_H_
|
||||
|
||||
#define __STDC_LIMIT_MACROS
|
||||
#include <stdint.h>
|
||||
|
||||
// Classes
|
||||
|
||||
/// This class offers a simple counter in micro-seconds since instantiated.
|
||||
/// @note Handles when the system timer wraps around (once).
|
||||
class IRtimer {
|
||||
public:
|
||||
IRtimer();
|
||||
void reset();
|
||||
uint32_t elapsed();
|
||||
#ifdef UNIT_TEST
|
||||
static void add(uint32_t usecs);
|
||||
#endif // UNIT_TEST
|
||||
|
||||
private:
|
||||
uint32_t start; ///< Time in uSeconds when the class was instantiated/reset.
|
||||
};
|
||||
|
||||
/// This class offers a simple counter in milli-seconds since instantiated.
|
||||
/// @note Handles when the system timer wraps around (once).
|
||||
class TimerMs {
|
||||
public:
|
||||
TimerMs();
|
||||
void reset();
|
||||
uint32_t elapsed();
|
||||
#ifdef UNIT_TEST
|
||||
static void add(uint32_t msecs);
|
||||
#endif // UNIT_TEST
|
||||
|
||||
private:
|
||||
uint32_t start; ///< Time in mSeconds when the class was instantiated/reset.
|
||||
};
|
||||
#endif // IRTIMER_H_
|
||||
1438
yoRadio/src/IRremoteESP8266/IRutils.cpp
Normal file
1438
yoRadio/src/IRremoteESP8266/IRutils.cpp
Normal file
File diff suppressed because it is too large
Load Diff
155
yoRadio/src/IRremoteESP8266/IRutils.h
Normal file
155
yoRadio/src/IRremoteESP8266/IRutils.h
Normal file
@@ -0,0 +1,155 @@
|
||||
#ifndef IRUTILS_H_
|
||||
#define IRUTILS_H_
|
||||
|
||||
// Copyright 2017 David Conran
|
||||
|
||||
#ifndef UNIT_TEST
|
||||
#include <Arduino.h>
|
||||
#endif
|
||||
#define __STDC_LIMIT_MACROS
|
||||
#include <stdint.h>
|
||||
#ifndef ARDUINO
|
||||
#include <string>
|
||||
#endif
|
||||
#include "IRremoteESP8266.h"
|
||||
#include "IRrecv.h"
|
||||
|
||||
const uint8_t kNibbleSize = 4;
|
||||
const uint8_t kLowNibble = 0;
|
||||
const uint8_t kHighNibble = 4;
|
||||
const uint8_t kModeBitsSize = 3;
|
||||
uint64_t reverseBits(uint64_t input, uint16_t nbits);
|
||||
String uint64ToString(uint64_t input, uint8_t base = 10);
|
||||
String int64ToString(int64_t input, uint8_t base = 10);
|
||||
String typeToString(const decode_type_t protocol,
|
||||
const bool isRepeat = false);
|
||||
void serialPrintUint64(uint64_t input, uint8_t base = 10);
|
||||
String resultToSourceCode(const decode_results * const results);
|
||||
String resultToTimingInfo(const decode_results * const results);
|
||||
String resultToHumanReadableBasic(const decode_results * const results);
|
||||
String resultToHexidecimal(const decode_results * const result);
|
||||
bool hasACState(const decode_type_t protocol);
|
||||
uint16_t getCorrectedRawLength(const decode_results * const results);
|
||||
uint16_t *resultToRawArray(const decode_results * const decode);
|
||||
uint8_t sumBytes(const uint8_t * const start, const uint16_t length,
|
||||
const uint8_t init = 0);
|
||||
uint8_t xorBytes(const uint8_t * const start, const uint16_t length,
|
||||
const uint8_t init = 0);
|
||||
uint16_t countBits(const uint8_t * const start, const uint16_t length,
|
||||
const bool ones = true, const uint16_t init = 0);
|
||||
uint16_t countBits(const uint64_t data, const uint8_t length,
|
||||
const bool ones = true, const uint16_t init = 0);
|
||||
uint64_t invertBits(const uint64_t data, const uint16_t nbits);
|
||||
decode_type_t strToDecodeType(const char *str);
|
||||
float celsiusToFahrenheit(const float deg);
|
||||
float fahrenheitToCelsius(const float deg);
|
||||
/// Namespace for covering common functions & procedures for advancd protocol
|
||||
/// handlers
|
||||
namespace irutils {
|
||||
String addBoolToString(const bool value, const String label,
|
||||
const bool precomma = true);
|
||||
String addToggleToString(const bool toggle, const String label,
|
||||
const bool precomma = true);
|
||||
String addIntToString(const uint16_t value, const String label,
|
||||
const bool precomma = true);
|
||||
String addSignedIntToString(const int16_t value, const String label,
|
||||
const bool precomma = true);
|
||||
String modelToStr(const decode_type_t protocol, const int16_t model);
|
||||
String addModelToString(const decode_type_t protocol, const int16_t model,
|
||||
const bool precomma = true);
|
||||
String addLabeledString(const String value, const String label,
|
||||
const bool precomma = true);
|
||||
String addTempToString(const uint16_t degrees, const bool celsius = true,
|
||||
const bool precomma = true,
|
||||
const bool isSensorTemp = false);
|
||||
String addTempFloatToString(const float degrees, const bool celsius = true,
|
||||
const bool precomma = true,
|
||||
const bool isSensorTemp = false);
|
||||
String addModeToString(const uint8_t mode, const uint8_t automatic,
|
||||
const uint8_t cool, const uint8_t heat,
|
||||
const uint8_t dry, const uint8_t fan);
|
||||
String addFanToString(const uint8_t speed, const uint8_t high,
|
||||
const uint8_t low, const uint8_t automatic,
|
||||
const uint8_t quiet, const uint8_t medium,
|
||||
const uint8_t maximum = 0xFF,
|
||||
const uint8_t medium_high = 0xFF);
|
||||
String addSwingHToString(const uint8_t position, const uint8_t automatic,
|
||||
const uint8_t maxleft, const uint8_t left,
|
||||
const uint8_t middle,
|
||||
const uint8_t right, const uint8_t maxright,
|
||||
const uint8_t off,
|
||||
const uint8_t leftright, const uint8_t rightleft,
|
||||
const uint8_t threed, const uint8_t wide);
|
||||
String addSwingVToString(const uint8_t position, const uint8_t automatic,
|
||||
const uint8_t highest, const uint8_t high,
|
||||
const uint8_t uppermiddle,
|
||||
const uint8_t middle,
|
||||
const uint8_t lowermiddle,
|
||||
const uint8_t low, const uint8_t lowest,
|
||||
const uint8_t off, const uint8_t swing,
|
||||
const uint8_t breeze, const uint8_t circulate);
|
||||
String addDayToString(const uint8_t day_of_week, const int8_t offset = 0,
|
||||
const bool precomma = true);
|
||||
String addTimerModeToString(const uint8_t timerType, const uint8_t noTimer,
|
||||
const uint8_t delayTimer,
|
||||
const uint8_t schedule1 = 0xFF,
|
||||
const uint8_t schedule2 = 0xFF,
|
||||
const uint8_t schedule3 = 0xFF,
|
||||
const bool precomma = true);
|
||||
String irCommandTypeToString(uint8_t commandType, uint8_t acControlCmd,
|
||||
uint8_t iFeelReportCmd = 0xFF,
|
||||
uint8_t timerCmd = 0xFF,
|
||||
uint8_t configCmd = 0xFF);
|
||||
String dayToString(const uint8_t day_of_week, const int8_t offset = 0);
|
||||
String daysBitmaskToString(uint8_t daysBitmap, uint8_t offset = 0);
|
||||
String channelToString(const uint8_t channel);
|
||||
String htmlEscape(const String unescaped);
|
||||
String msToString(uint32_t const msecs);
|
||||
String minsToString(const uint16_t mins);
|
||||
uint8_t sumNibbles(const uint8_t * const start, const uint16_t length,
|
||||
const uint8_t init = 0);
|
||||
uint8_t sumNibbles(const uint64_t data, const uint8_t count = 16,
|
||||
const uint8_t init = 0, const bool nibbleonly = true);
|
||||
uint16_t sumBytes(const uint64_t data, const uint8_t count = 8,
|
||||
const uint8_t init = 0, const bool byteonly = true);
|
||||
uint8_t bcdToUint8(const uint8_t bcd);
|
||||
uint8_t uint8ToBcd(const uint8_t integer);
|
||||
bool getBit(const uint64_t data, const uint8_t position,
|
||||
const uint8_t size = 64);
|
||||
bool getBit(const uint8_t data, const uint8_t position);
|
||||
#define GETBIT8(a, b) ((a) & ((uint8_t)1 << (b)))
|
||||
#define GETBIT16(a, b) ((a) & ((uint16_t)1 << (b)))
|
||||
#define GETBIT32(a, b) ((a) & ((uint32_t)1 << (b)))
|
||||
#define GETBIT64(a, b) ((a) & ((uint64_t)1 << (b)))
|
||||
#define GETBITS8(data, offset, size) \
|
||||
(((data) & (((uint8_t)UINT8_MAX >> (8 - (size))) << (offset))) >> (offset))
|
||||
#define GETBITS16(data, offset, size) \
|
||||
(((data) & (((uint16_t)UINT16_MAX >> (16 - (size))) << (offset))) >> \
|
||||
(offset))
|
||||
#define GETBITS32(data, offset, size) \
|
||||
(((data) & (((uint32_t)UINT32_MAX >> (32 - (size))) << (offset))) >> \
|
||||
(offset))
|
||||
#define GETBITS64(data, offset, size) \
|
||||
(((data) & (((uint64_t)UINT64_MAX >> (64 - (size))) << (offset))) >> \
|
||||
(offset))
|
||||
uint64_t setBit(const uint64_t data, const uint8_t position,
|
||||
const bool on = true, const uint8_t size = 64);
|
||||
uint8_t setBit(const uint8_t data, const uint8_t position,
|
||||
const bool on = true);
|
||||
void setBit(uint8_t * const data, const uint8_t position,
|
||||
const bool on = true);
|
||||
void setBit(uint32_t * const data, const uint8_t position,
|
||||
const bool on = true);
|
||||
void setBit(uint64_t * const data, const uint8_t position,
|
||||
const bool on = true);
|
||||
void setBits(uint8_t * const dst, const uint8_t offset, const uint8_t nbits,
|
||||
const uint8_t data);
|
||||
void setBits(uint32_t * const dst, const uint8_t offset, const uint8_t nbits,
|
||||
const uint32_t data);
|
||||
void setBits(uint64_t * const dst, const uint8_t offset, const uint8_t nbits,
|
||||
const uint64_t data);
|
||||
uint8_t * invertBytePairs(uint8_t *ptr, const uint16_t length);
|
||||
bool checkInvertedBytePairs(const uint8_t * const ptr, const uint16_t length);
|
||||
uint8_t lowLevelSanityCheck(void);
|
||||
} // namespace irutils
|
||||
#endif // IRUTILS_H_
|
||||
25
yoRadio/src/IRremoteESP8266/i18n.h
Normal file
25
yoRadio/src/IRremoteESP8266/i18n.h
Normal file
@@ -0,0 +1,25 @@
|
||||
// Copyright 2019 - David Conran (@crankyoldgit)
|
||||
|
||||
#ifndef I18N_H_
|
||||
#define I18N_H_
|
||||
|
||||
#include "IRremoteESP8266.h"
|
||||
|
||||
// Load the appropriate locale header file.
|
||||
#ifndef _IR_LOCALE_
|
||||
#define _IR_LOCALE_ en-AU
|
||||
#endif // _IR_LOCALE_
|
||||
|
||||
#define ENQUOTE_(x) #x
|
||||
#define ENQUOTE(x) ENQUOTE_(x)
|
||||
|
||||
// Load the desired/requested locale.
|
||||
#ifdef _IR_LOCALE_
|
||||
#include ENQUOTE(locale/_IR_LOCALE_.h)
|
||||
#endif // _IR_LOCALE_
|
||||
|
||||
// Now that any specific locale has been loaded, we can safely load the defaults
|
||||
// as the defaults should not override anything that has now set.
|
||||
#include "locale/defaults.h"
|
||||
|
||||
#endif // I18N_H_
|
||||
362
yoRadio/src/IRremoteESP8266/ir_Airton.cpp
Normal file
362
yoRadio/src/IRremoteESP8266/ir_Airton.cpp
Normal file
@@ -0,0 +1,362 @@
|
||||
// Copyright 2021 David Conran (crankyoldgit)
|
||||
/// @file
|
||||
/// @brief Support for Airton protocol
|
||||
/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1670
|
||||
|
||||
#include "ir_Airton.h"
|
||||
#include <algorithm>
|
||||
#include "IRrecv.h"
|
||||
#include "IRsend.h"
|
||||
#include "IRtext.h"
|
||||
#include "IRutils.h"
|
||||
|
||||
const uint16_t kAirtonHdrMark = 6630;
|
||||
const uint16_t kAirtonBitMark = 400;
|
||||
const uint16_t kAirtonHdrSpace = 3350;
|
||||
const uint16_t kAirtonOneSpace = 1260;
|
||||
const uint16_t kAirtonZeroSpace = 430;
|
||||
const uint16_t kAirtonFreq = 38000; // Hz. (Just a guess)
|
||||
|
||||
using irutils::addBoolToString;
|
||||
using irutils::addModeToString;
|
||||
using irutils::addFanToString;
|
||||
using irutils::addTempToString;
|
||||
using irutils::sumBytes;
|
||||
|
||||
#if SEND_AIRTON
|
||||
// Function should be safe up to 64 bits.
|
||||
/// Send a Airton formatted message.
|
||||
/// Status: STABLE / Confirmed working.
|
||||
/// @param[in] data containing the IR command.
|
||||
/// @param[in] nbits Nr. of bits to send. usually kAirtonBits
|
||||
/// @param[in] repeat Nr. of times the message is to be repeated.
|
||||
void IRsend::sendAirton(const uint64_t data, const uint16_t nbits,
|
||||
const uint16_t repeat) {
|
||||
sendGeneric(kAirtonHdrMark, kAirtonHdrSpace,
|
||||
kAirtonBitMark, kAirtonOneSpace,
|
||||
kAirtonBitMark, kAirtonZeroSpace,
|
||||
kAirtonBitMark, kDefaultMessageGap,
|
||||
data, nbits, kAirtonFreq, false, repeat, kDutyDefault);
|
||||
}
|
||||
#endif // SEND_AIRTON
|
||||
|
||||
#if DECODE_AIRTON
|
||||
/// Decode the supplied Airton message.
|
||||
/// Status: STABLE / Confirmed working. LSBF ordering confirmed via temperature.
|
||||
/// @param[in,out] results Ptr to the data to decode & where to store the decode
|
||||
/// @param[in] offset The starting index to use when attempting to decode the
|
||||
/// raw data. Typically/Defaults to kStartOffset.
|
||||
/// @param[in] nbits The number of data bits to expect.
|
||||
/// @param[in] strict Flag indicating if we should perform strict matching.
|
||||
/// @return A boolean. True if it can decode it, false if it can't.
|
||||
bool IRrecv::decodeAirton(decode_results *results, uint16_t offset,
|
||||
const uint16_t nbits, const bool strict) {
|
||||
if (results->rawlen < 2 * nbits + kHeader + kFooter - offset)
|
||||
return false; // Too short a message to match.
|
||||
if (strict && nbits != kAirtonBits)
|
||||
return false;
|
||||
|
||||
// Header + Data + Footer
|
||||
if (!matchGeneric(&(results->rawbuf[offset]), &(results->value),
|
||||
results->rawlen - offset, nbits,
|
||||
kAirtonHdrMark, kAirtonHdrSpace,
|
||||
kAirtonBitMark, kAirtonOneSpace,
|
||||
kAirtonBitMark, kAirtonZeroSpace,
|
||||
kAirtonBitMark, kDefaultMessageGap,
|
||||
true, kUseDefTol, kMarkExcess, false)) return false;
|
||||
// Compliance
|
||||
if (strict && !IRAirtonAc::validChecksum(results->value)) return false;
|
||||
// Success
|
||||
results->decode_type = decode_type_t::AIRTON;
|
||||
results->bits = nbits;
|
||||
results->command = 0;
|
||||
results->address = 0;
|
||||
return true;
|
||||
}
|
||||
#endif // DECODE_AIRTON
|
||||
|
||||
/// Class constructor
|
||||
/// @param[in] pin GPIO to be used when sending.
|
||||
/// @param[in] inverted Is the output signal to be inverted?
|
||||
/// @param[in] use_modulation Is frequency modulation to be used?
|
||||
IRAirtonAc::IRAirtonAc(const uint16_t pin, const bool inverted,
|
||||
const bool use_modulation)
|
||||
: _irsend(pin, inverted, use_modulation) { stateReset(); }
|
||||
|
||||
/// Set up hardware to be able to send a message.
|
||||
void IRAirtonAc::begin(void) { _irsend.begin(); }
|
||||
|
||||
#if SEND_AIRTON
|
||||
/// Send the current internal state as an IR message.
|
||||
/// @param[in] repeat Nr. of times the message will be repeated.
|
||||
void IRAirtonAc::send(const uint16_t repeat) {
|
||||
_irsend.sendAirton(getRaw(), kAirtonBits, repeat);
|
||||
}
|
||||
#endif // SEND_AIRTON
|
||||
|
||||
/// Calculate the checksum for the supplied state.
|
||||
/// @param[in] state The source state to generate the checksum from.
|
||||
/// @return The checksum value.
|
||||
uint8_t IRAirtonAc::calcChecksum(const uint64_t state) {
|
||||
return (uint8_t)(0x7F - sumBytes(state, 6)) ^ 0x2C;
|
||||
}
|
||||
|
||||
/// Verify the checksum is valid for a given state.
|
||||
/// @param[in] state The value to verify the checksum of.
|
||||
/// @return A boolean indicating if it's checksum is valid.
|
||||
bool IRAirtonAc::validChecksum(const uint64_t state) {
|
||||
AirtonProtocol p;
|
||||
p.raw = state;
|
||||
return p.Sum == IRAirtonAc::calcChecksum(state);
|
||||
}
|
||||
|
||||
/// Update the checksum value for the internal state.
|
||||
void IRAirtonAc::checksum(void) { _.Sum = IRAirtonAc::calcChecksum(_.raw); }
|
||||
|
||||
/// Reset the internals of the object to a known good state.
|
||||
void IRAirtonAc::stateReset(void) { setRaw(0x11D3); }
|
||||
|
||||
/// Get the raw state of the object, suitable to be sent with the appropriate
|
||||
/// IRsend object method.
|
||||
/// @return A copy to the internal state.
|
||||
uint64_t IRAirtonAc::getRaw(void) {
|
||||
checksum(); // Ensure correct bit array before returning
|
||||
return _.raw;
|
||||
}
|
||||
|
||||
/// Set the raw state of the object.
|
||||
/// @param[in] state The raw state from the native IR message.
|
||||
void IRAirtonAc::setRaw(const uint64_t state) { _.raw = state; }
|
||||
|
||||
|
||||
/// Set the internal state to have the power on.
|
||||
void IRAirtonAc::on(void) { setPower(true); }
|
||||
|
||||
/// Set the internal state to have the power off.
|
||||
void IRAirtonAc::off(void) { setPower(false); }
|
||||
|
||||
/// Set the internal state to have the desired power.
|
||||
/// @param[in] on The desired power state.
|
||||
void IRAirtonAc::setPower(const bool on) {
|
||||
_.Power = on;
|
||||
setMode(getMode()); // Re-do the mode incase we need to do something special.
|
||||
}
|
||||
|
||||
/// Get the power setting from the internal state.
|
||||
/// @return A boolean indicating the power setting.
|
||||
bool IRAirtonAc::getPower(void) const { return _.Power; }
|
||||
|
||||
/// Get the current operation mode setting.
|
||||
/// @return The current operation mode.
|
||||
uint8_t IRAirtonAc::getMode(void) const { return _.Mode; }
|
||||
|
||||
/// Set the desired operation mode.
|
||||
/// @param[in] mode The desired operation mode.
|
||||
void IRAirtonAc::setMode(const uint8_t mode) {
|
||||
// Changing the mode always removes the sleep setting.
|
||||
if (mode != _.Mode) setSleep(false);
|
||||
// Set the actual mode.
|
||||
_.Mode = (mode > kAirtonHeat) ? kAirtonAuto : mode;
|
||||
// Handle special settings for each mode.
|
||||
switch (_.Mode) {
|
||||
case kAirtonAuto:
|
||||
setTemp(25); // Auto has a fixed temp.
|
||||
_.NotAutoOn = !getPower();
|
||||
break;
|
||||
case kAirtonHeat:
|
||||
// When powered on and in Heat mode, set a special bit.
|
||||
_.HeatOn = getPower();
|
||||
// FALL-THRU
|
||||
default:
|
||||
_.NotAutoOn = true;
|
||||
}
|
||||
// Reset the economy setting if we need to.
|
||||
setEcono(getEcono());
|
||||
}
|
||||
|
||||
/// Convert a stdAc::opmode_t enum into its native mode.
|
||||
/// @param[in] mode The enum to be converted.
|
||||
/// @return The native equivalent of the enum.
|
||||
uint8_t IRAirtonAc::convertMode(const stdAc::opmode_t mode) {
|
||||
switch (mode) {
|
||||
case stdAc::opmode_t::kCool: return kAirtonCool;
|
||||
case stdAc::opmode_t::kHeat: return kAirtonHeat;
|
||||
case stdAc::opmode_t::kDry: return kAirtonDry;
|
||||
case stdAc::opmode_t::kFan: return kAirtonFan;
|
||||
default: return kAirtonAuto;
|
||||
}
|
||||
}
|
||||
|
||||
/// Convert a native mode into its stdAc equivalent.
|
||||
/// @param[in] mode The native setting to be converted.
|
||||
/// @return The stdAc equivalent of the native setting.
|
||||
stdAc::opmode_t IRAirtonAc::toCommonMode(const uint8_t mode) {
|
||||
switch (mode) {
|
||||
case kAirtonCool: return stdAc::opmode_t::kCool;
|
||||
case kAirtonHeat: return stdAc::opmode_t::kHeat;
|
||||
case kAirtonDry: return stdAc::opmode_t::kDry;
|
||||
case kAirtonFan: return stdAc::opmode_t::kFan;
|
||||
default: return stdAc::opmode_t::kAuto;
|
||||
}
|
||||
}
|
||||
|
||||
/// Set the temperature.
|
||||
/// @param[in] degrees The temperature in degrees celsius.
|
||||
void IRAirtonAc::setTemp(const uint8_t degrees) {
|
||||
uint8_t temp = std::max(kAirtonMinTemp, degrees);
|
||||
temp = std::min(kAirtonMaxTemp, temp);
|
||||
if (_.Mode == kAirtonAuto) temp = kAirtonMaxTemp; // Auto has a fixed temp.
|
||||
_.Temp = temp - kAirtonMinTemp;
|
||||
}
|
||||
|
||||
/// Get the current temperature setting.
|
||||
/// @return Get current setting for temp. in degrees celsius.
|
||||
uint8_t IRAirtonAc::getTemp(void) const { return _.Temp + kAirtonMinTemp; }
|
||||
|
||||
|
||||
/// Set the speed of the fan.
|
||||
/// @param[in] speed The desired setting.
|
||||
void IRAirtonAc::setFan(const uint8_t speed) {
|
||||
_.Fan = (speed > kAirtonFanMax) ? kAirtonFanAuto : speed;
|
||||
}
|
||||
|
||||
/// Get the current fan speed setting.
|
||||
/// @return The current fan speed.
|
||||
uint8_t IRAirtonAc::getFan(void) const { return _.Fan; }
|
||||
|
||||
/// Convert a stdAc::fanspeed_t enum into it's native speed.
|
||||
/// @param[in] speed The enum to be converted.
|
||||
/// @return The native equivalent of the enum.
|
||||
uint8_t IRAirtonAc::convertFan(const stdAc::fanspeed_t speed) {
|
||||
switch (speed) {
|
||||
case stdAc::fanspeed_t::kMin: return kAirtonFanMin;
|
||||
case stdAc::fanspeed_t::kLow: return kAirtonFanLow;
|
||||
case stdAc::fanspeed_t::kMedium: return kAirtonFanMed;
|
||||
case stdAc::fanspeed_t::kHigh: return kAirtonFanHigh;
|
||||
case stdAc::fanspeed_t::kMax: return kAirtonFanMax;
|
||||
default: return kAirtonFanAuto;
|
||||
}
|
||||
}
|
||||
|
||||
/// Convert a native fan speed into its stdAc equivalent.
|
||||
/// @param[in] speed The native setting to be converted.
|
||||
/// @return The stdAc equivalent of the native setting.
|
||||
stdAc::fanspeed_t IRAirtonAc::toCommonFanSpeed(const uint8_t speed) {
|
||||
switch (speed) {
|
||||
case kAirtonFanMax: return stdAc::fanspeed_t::kMax;
|
||||
case kAirtonFanHigh: return stdAc::fanspeed_t::kHigh;
|
||||
case kAirtonFanMed: return stdAc::fanspeed_t::kMedium;
|
||||
case kAirtonFanLow: return stdAc::fanspeed_t::kLow;
|
||||
case kAirtonFanMin: return stdAc::fanspeed_t::kMin;
|
||||
default: return stdAc::fanspeed_t::kAuto;
|
||||
}
|
||||
}
|
||||
|
||||
/// Set the Vertical Swing setting of the A/C.
|
||||
/// @param[in] on true, the setting is on. false, the setting is off.
|
||||
void IRAirtonAc::setSwingV(const bool on) { _.SwingV = on; }
|
||||
|
||||
/// Get the Vertical Swing setting of the A/C.
|
||||
/// @return true, the setting is on. false, the setting is off.
|
||||
bool IRAirtonAc::getSwingV(void) const { return _.SwingV; }
|
||||
|
||||
/// Set the Light/LED/Display setting of the A/C.
|
||||
/// @param[in] on true, the setting is on. false, the setting is off.
|
||||
void IRAirtonAc::setLight(const bool on) { _.Light = on; }
|
||||
|
||||
/// Get the Light/LED/Display setting of the A/C.
|
||||
/// @return true, the setting is on. false, the setting is off.
|
||||
bool IRAirtonAc::getLight(void) const { return _.Light; }
|
||||
|
||||
/// Set the Economy setting of the A/C.
|
||||
/// @param[in] on true, the setting is on. false, the setting is off.
|
||||
/// @note Only available in Cool mode.
|
||||
void IRAirtonAc::setEcono(const bool on) {
|
||||
_.Econo = on && (getMode() == kAirtonCool);
|
||||
}
|
||||
|
||||
/// Get the Economy setting of the A/C.
|
||||
/// @return true, the setting is on. false, the setting is off.
|
||||
bool IRAirtonAc::getEcono(void) const { return _.Econo; }
|
||||
|
||||
/// Set the Turbo setting of the A/C.
|
||||
/// @param[in] on true, the setting is on. false, the setting is off.
|
||||
void IRAirtonAc::setTurbo(const bool on) {
|
||||
_.Turbo = on;
|
||||
// Pressing the turbo button sets the fan to max as well.
|
||||
if (on) setFan(kAirtonFanMax);
|
||||
}
|
||||
|
||||
/// Get the Turbo setting of the A/C.
|
||||
/// @return true, the setting is on. false, the setting is off.
|
||||
bool IRAirtonAc::getTurbo(void) const { return _.Turbo; }
|
||||
|
||||
/// Set the Sleep setting of the A/C.
|
||||
/// @param[in] on true, the setting is on. false, the setting is off.
|
||||
/// @note Sleep not available in fan or auto mode.
|
||||
void IRAirtonAc::setSleep(const bool on) {
|
||||
switch (getMode()) {
|
||||
case kAirtonAuto:
|
||||
case kAirtonFan: _.Sleep = false; break;
|
||||
default: _.Sleep = on;
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the Sleep setting of the A/C.
|
||||
/// @return true, the setting is on. false, the setting is off.
|
||||
bool IRAirtonAc::getSleep(void) const { return _.Sleep; }
|
||||
|
||||
/// Set the Health/Filter setting of the A/C.
|
||||
/// @param[in] on true, the setting is on. false, the setting is off.
|
||||
void IRAirtonAc::setHealth(const bool on) { _.Health = on; }
|
||||
|
||||
/// Get the Health/Filter setting of the A/C.
|
||||
/// @return true, the setting is on. false, the setting is off.
|
||||
bool IRAirtonAc::getHealth(void) const { return _.Health; }
|
||||
|
||||
/// Convert the current internal state into its stdAc::state_t equivalent.
|
||||
/// @return The stdAc equivalent of the native settings.
|
||||
stdAc::state_t IRAirtonAc::toCommon(void) const {
|
||||
stdAc::state_t result{};
|
||||
result.protocol = decode_type_t::AIRTON;
|
||||
result.power = getPower();
|
||||
result.mode = toCommonMode(getMode());
|
||||
result.celsius = true;
|
||||
result.degrees = getTemp();
|
||||
result.fanspeed = toCommonFanSpeed(getFan());
|
||||
result.swingv = getSwingV() ? stdAc::swingv_t::kAuto : stdAc::swingv_t::kOff;
|
||||
result.econo = getEcono();
|
||||
result.turbo = getTurbo();
|
||||
result.filter = getHealth();
|
||||
result.light = getLight();
|
||||
result.sleep = getSleep() ? 0 : -1;
|
||||
// Not supported.
|
||||
result.model = -1;
|
||||
result.swingh = stdAc::swingh_t::kOff;
|
||||
result.quiet = false;
|
||||
result.clean = false;
|
||||
result.beep = false;
|
||||
result.clock = -1;
|
||||
return result;
|
||||
}
|
||||
|
||||
/// Convert the current internal state into a human readable string.
|
||||
/// @return A human readable string.
|
||||
String IRAirtonAc::toString(void) const {
|
||||
String result = "";
|
||||
result.reserve(135); // Reserve some heap for the string to reduce fragging.
|
||||
result += addBoolToString(getPower(), kPowerStr, false);
|
||||
result += addModeToString(_.Mode, kAirtonAuto, kAirtonCool,
|
||||
kAirtonHeat, kAirtonDry, kAirtonFan);
|
||||
result += addFanToString(_.Fan, kAirtonFanHigh, kAirtonFanLow,
|
||||
kAirtonFanAuto, kAirtonFanMin, kAirtonFanMed,
|
||||
kAirtonFanMax);
|
||||
result += addTempToString(getTemp());
|
||||
result += addBoolToString(getSwingV(), kSwingVStr);
|
||||
result += addBoolToString(getEcono(), kEconoStr);
|
||||
result += addBoolToString(getTurbo(), kTurboStr);
|
||||
result += addBoolToString(getLight(), kLightStr);
|
||||
result += addBoolToString(getHealth(), kHealthStr);
|
||||
result += addBoolToString(getSleep(), kSleepStr);
|
||||
return result;
|
||||
}
|
||||
134
yoRadio/src/IRremoteESP8266/ir_Airton.h
Normal file
134
yoRadio/src/IRremoteESP8266/ir_Airton.h
Normal file
@@ -0,0 +1,134 @@
|
||||
// Copyright 2021 David Conran (crankyoldgit)
|
||||
/// @file
|
||||
/// @brief Support for Airton protocol
|
||||
/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1670
|
||||
|
||||
// Supports:
|
||||
// Brand: Airton, Model: SMVH09B-2A2A3NH ref. 409730 A/C
|
||||
// Brand: Airton, Model: RD1A1 remote
|
||||
|
||||
#ifndef IR_AIRTON_H_
|
||||
#define IR_AIRTON_H_
|
||||
|
||||
#define __STDC_LIMIT_MACROS
|
||||
#include <stdint.h>
|
||||
#ifndef UNIT_TEST
|
||||
#include <Arduino.h>
|
||||
#endif
|
||||
#include "IRremoteESP8266.h"
|
||||
#include "IRsend.h"
|
||||
#ifdef UNIT_TEST
|
||||
#include "IRsend_test.h"
|
||||
#endif
|
||||
|
||||
/// Native representation of a Airton 56 A/C message.
|
||||
/// @see https://docs.google.com/spreadsheets/d/1Kpq7WCkh85heLnTQGlwUfCR6eeu_vfBHvhii8wtP4LU/edit?usp=sharing
|
||||
union AirtonProtocol{
|
||||
uint64_t raw; ///< The state in code form.
|
||||
struct { // Common
|
||||
// Byte 1 & 0 (LSB)
|
||||
uint16_t Header :16; // Header. (0x11D3)
|
||||
// Byte 2
|
||||
uint8_t Mode :3; // Operating Mode
|
||||
uint8_t Power :1; // Power Control
|
||||
uint8_t Fan :3;
|
||||
uint8_t Turbo :1;
|
||||
// Byte 3
|
||||
uint8_t Temp :4; // Degrees Celsius (+16 offset)
|
||||
uint8_t :4; // Unknown / Unused.
|
||||
// Byte 4
|
||||
uint8_t SwingV :1;
|
||||
uint8_t :7; // Unknown / Unused.
|
||||
// Byte 5
|
||||
uint8_t Econo :1;
|
||||
uint8_t Sleep :1;
|
||||
uint8_t NotAutoOn :1;
|
||||
uint8_t :1; // Unknown / Unused.
|
||||
uint8_t HeatOn :1;
|
||||
uint8_t :1; // Unknown / Unused.
|
||||
uint8_t Health :1;
|
||||
uint8_t Light :1;
|
||||
// Byte 6
|
||||
uint8_t Sum :8; // Sepecial checksum value
|
||||
};
|
||||
};
|
||||
|
||||
// Constants
|
||||
const uint8_t kAirtonAuto = 0b000; // 0
|
||||
const uint8_t kAirtonCool = 0b001; // 1
|
||||
const uint8_t kAirtonDry = 0b010; // 2
|
||||
const uint8_t kAirtonFan = 0b011; // 3
|
||||
const uint8_t kAirtonHeat = 0b100; // 4
|
||||
|
||||
const uint8_t kAirtonFanAuto = 0b000; // 0
|
||||
const uint8_t kAirtonFanMin = 0b001; // 1
|
||||
const uint8_t kAirtonFanLow = 0b010; // 2
|
||||
const uint8_t kAirtonFanMed = 0b011; // 3
|
||||
const uint8_t kAirtonFanHigh = 0b100; // 4
|
||||
const uint8_t kAirtonFanMax = 0b101; // 5
|
||||
|
||||
const uint8_t kAirtonMinTemp = 16; // 16C
|
||||
const uint8_t kAirtonMaxTemp = 25; // 25C
|
||||
|
||||
|
||||
/// Class for handling detailed Airton 56-bit A/C messages.
|
||||
class IRAirtonAc {
|
||||
public:
|
||||
explicit IRAirtonAc(const uint16_t pin, const bool inverted = false,
|
||||
const bool use_modulation = true);
|
||||
void stateReset(void);
|
||||
#if SEND_AIRTON
|
||||
void send(const uint16_t repeat = kAirtonDefaultRepeat);
|
||||
/// 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_AIRTON
|
||||
void begin(void);
|
||||
void on(void);
|
||||
void off(void);
|
||||
void setPower(const bool on);
|
||||
bool getPower(void) const;
|
||||
void setTemp(const uint8_t degrees);
|
||||
uint8_t getTemp(void) const;
|
||||
void setFan(const uint8_t speed);
|
||||
uint8_t getFan(void) const;
|
||||
void setMode(const uint8_t mode);
|
||||
uint8_t getMode(void) const;
|
||||
uint64_t getRaw(void);
|
||||
void setRaw(const uint64_t data);
|
||||
void setLight(const bool on);
|
||||
bool getLight(void) const;
|
||||
void setEcono(const bool on);
|
||||
bool getEcono(void) const;
|
||||
void setTurbo(const bool on);
|
||||
bool getTurbo(void) const;
|
||||
void setHealth(const bool on);
|
||||
bool getHealth(void) const;
|
||||
void setSleep(const bool on);
|
||||
bool getSleep(void) const;
|
||||
void setSwingV(const bool on);
|
||||
bool getSwingV(void) const;
|
||||
static bool validChecksum(const uint64_t data);
|
||||
static uint8_t calcChecksum(const uint64_t data);
|
||||
static uint8_t convertMode(const stdAc::opmode_t mode);
|
||||
static uint8_t convertFan(const stdAc::fanspeed_t speed);
|
||||
static uint8_t convertSwingV(const stdAc::swingv_t position);
|
||||
static stdAc::opmode_t toCommonMode(const uint8_t mode);
|
||||
static stdAc::fanspeed_t toCommonFanSpeed(const uint8_t speed);
|
||||
stdAc::state_t toCommon(void) const;
|
||||
String toString(void) const;
|
||||
#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
|
||||
AirtonProtocol _;
|
||||
void checksum(void);
|
||||
};
|
||||
#endif // IR_AIRTON_H_
|
||||
286
yoRadio/src/IRremoteESP8266/ir_Airwell.cpp
Normal file
286
yoRadio/src/IRremoteESP8266/ir_Airwell.cpp
Normal file
@@ -0,0 +1,286 @@
|
||||
// Copyright 2020 David Conran
|
||||
#include "ir_Airwell.h"
|
||||
#include <algorithm>
|
||||
#include "IRrecv.h"
|
||||
#include "IRsend.h"
|
||||
#include "IRtext.h"
|
||||
#include "IRutils.h"
|
||||
|
||||
/// @file
|
||||
/// @brief Airwell "Manchester code" based protocol.
|
||||
/// Some other Airwell products use the COOLIX protocol.
|
||||
|
||||
const uint8_t kAirwellOverhead = 4;
|
||||
const uint16_t kAirwellHalfClockPeriod = 950; // uSeconds
|
||||
const uint16_t kAirwellHdrMark = 3 * kAirwellHalfClockPeriod; // uSeconds
|
||||
const uint16_t kAirwellHdrSpace = 3 * kAirwellHalfClockPeriod; // uSeconds
|
||||
const uint16_t kAirwellFooterMark = 5 * kAirwellHalfClockPeriod; // uSeconds
|
||||
|
||||
using irutils::addBoolToString;
|
||||
using irutils::addModeToString;
|
||||
using irutils::addFanToString;
|
||||
using irutils::addTempToString;
|
||||
|
||||
#if SEND_AIRWELL
|
||||
/// Send an Airwell Manchester Code formatted message.
|
||||
/// Status: BETA / Appears to be working.
|
||||
/// @param[in] data The message to be sent.
|
||||
/// @param[in] nbits The number of bits of the message to be sent.
|
||||
/// @param[in] repeat The number of times the command is to be repeated.
|
||||
/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1069
|
||||
void IRsend::sendAirwell(uint64_t data, uint16_t nbits, uint16_t repeat) {
|
||||
// Header + Data
|
||||
sendManchester(kAirwellHdrMark, kAirwellHdrMark, kAirwellHalfClockPeriod,
|
||||
0, 0, data, nbits, 38000, true, repeat, kDutyDefault, false);
|
||||
// Footer
|
||||
mark(kAirwellHdrMark + kAirwellHalfClockPeriod);
|
||||
space(kDefaultMessageGap); // A guess.
|
||||
}
|
||||
#endif
|
||||
|
||||
#if DECODE_AIRWELL
|
||||
/// Decode the supplied Airwell "Manchester code" message.
|
||||
///
|
||||
/// Status: BETA / Appears to be working.
|
||||
/// @param[in,out] results Ptr to the data to decode & where to store the decode
|
||||
/// result.
|
||||
/// @param[in] offset The starting index to use when attempting to decode the
|
||||
/// raw data. Typically/Defaults to kStartOffset.
|
||||
/// @param[in] nbits The number of data bits to expect.
|
||||
/// @param[in] strict Flag indicating if we should perform strict matching.
|
||||
/// @return A boolean. True if it can decode it, false if it can't.
|
||||
/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1069
|
||||
bool IRrecv::decodeAirwell(decode_results *results, uint16_t offset,
|
||||
const uint16_t nbits, const bool strict) {
|
||||
if (results->rawlen < nbits + kAirwellOverhead - offset)
|
||||
return false; // Too short a message to match.
|
||||
|
||||
// Compliance
|
||||
if (strict && nbits != kAirwellBits)
|
||||
return false; // Doesn't match our protocol defn.
|
||||
|
||||
// Header #1 + Data #1 + Footer #1 (There are total of 3 sections)
|
||||
uint16_t used = matchManchester(results->rawbuf + offset, &results->value,
|
||||
results->rawlen - offset, nbits,
|
||||
kAirwellHdrMark, kAirwellHdrMark,
|
||||
kAirwellHalfClockPeriod,
|
||||
kAirwellHdrMark, kAirwellHdrSpace,
|
||||
true, kUseDefTol, kMarkExcess, true, false);
|
||||
if (used == 0) return false;
|
||||
offset += used;
|
||||
|
||||
// Success
|
||||
results->decode_type = decode_type_t::AIRWELL;
|
||||
results->bits = nbits;
|
||||
results->address = 0;
|
||||
results->command = 0;
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
/// Class constructor
|
||||
/// @param[in] pin GPIO to be used when sending.
|
||||
/// @param[in] inverted Is the output signal to be inverted?
|
||||
/// @param[in] use_modulation Is frequency modulation to be used?
|
||||
IRAirwellAc::IRAirwellAc(const uint16_t pin, const bool inverted,
|
||||
const bool use_modulation)
|
||||
: _irsend(pin, inverted, use_modulation) { stateReset(); }
|
||||
|
||||
/// Set up hardware to be able to send a message.
|
||||
void IRAirwellAc::begin(void) { _irsend.begin(); }
|
||||
|
||||
/// Get the raw state of the object, suitable to be sent with the appropriate
|
||||
/// IRsend object method.
|
||||
/// @return A copy of the internal state.
|
||||
uint64_t IRAirwellAc::getRaw(void) const {
|
||||
return _.raw;
|
||||
}
|
||||
|
||||
/// Set the raw state of the object.
|
||||
/// @param[in] state The raw state from the native IR message.
|
||||
void IRAirwellAc::setRaw(const uint64_t state) {
|
||||
_.raw = state;
|
||||
}
|
||||
|
||||
#if SEND_AIRWELL
|
||||
/// Send the current internal state as an IR message.
|
||||
/// @param[in] repeat Nr. of times the message will be repeated.
|
||||
void IRAirwellAc::send(const uint16_t repeat) {
|
||||
_irsend.sendAirwell(getRaw(), kAirwellBits, repeat);
|
||||
}
|
||||
#endif // SEND_AIRWELL
|
||||
|
||||
/// Reset the internals of the object to a known good state.
|
||||
void IRAirwellAc::stateReset(void) {
|
||||
_.raw = kAirwellKnownGoodState;
|
||||
}
|
||||
|
||||
/// Turn on/off the Power Airwell setting.
|
||||
/// @param[in] on The desired setting state.
|
||||
void IRAirwellAc::setPowerToggle(const bool on) {
|
||||
_.PowerToggle = on;
|
||||
}
|
||||
|
||||
/// Get the power toggle setting from the internal state.
|
||||
/// @return A boolean indicating the setting.
|
||||
bool IRAirwellAc::getPowerToggle(void) const {
|
||||
return _.PowerToggle;
|
||||
}
|
||||
|
||||
/// Get the current operation mode setting.
|
||||
/// @return The current operation mode.
|
||||
uint8_t IRAirwellAc::getMode(void) const {
|
||||
return _.Mode;
|
||||
}
|
||||
|
||||
/// Set the desired operation mode.
|
||||
/// @param[in] mode The desired operation mode.
|
||||
void IRAirwellAc::setMode(const uint8_t mode) {
|
||||
switch (mode) {
|
||||
case kAirwellFan:
|
||||
case kAirwellCool:
|
||||
case kAirwellHeat:
|
||||
case kAirwellDry:
|
||||
case kAirwellAuto:
|
||||
_.Mode = mode;
|
||||
break;
|
||||
default:
|
||||
_.Mode = kAirwellAuto;
|
||||
}
|
||||
setFan(getFan()); // Ensure the fan is at the correct speed for the new mode.
|
||||
}
|
||||
|
||||
/// Convert a stdAc::opmode_t enum into its native mode.
|
||||
/// @param[in] mode The enum to be converted.
|
||||
/// @return The native equivalent of the enum.
|
||||
uint8_t IRAirwellAc::convertMode(const stdAc::opmode_t mode) {
|
||||
switch (mode) {
|
||||
case stdAc::opmode_t::kCool: return kAirwellCool;
|
||||
case stdAc::opmode_t::kHeat: return kAirwellHeat;
|
||||
case stdAc::opmode_t::kDry: return kAirwellDry;
|
||||
case stdAc::opmode_t::kFan: return kAirwellFan;
|
||||
default: return kAirwellAuto;
|
||||
}
|
||||
}
|
||||
|
||||
/// Convert a native mode into its stdAc equivalent.
|
||||
/// @param[in] mode The native setting to be converted.
|
||||
/// @return The stdAc equivalent of the native setting.
|
||||
stdAc::opmode_t IRAirwellAc::toCommonMode(const uint8_t mode) {
|
||||
switch (mode) {
|
||||
case kAirwellCool: return stdAc::opmode_t::kCool;
|
||||
case kAirwellHeat: return stdAc::opmode_t::kHeat;
|
||||
case kAirwellDry: return stdAc::opmode_t::kDry;
|
||||
case kAirwellFan: return stdAc::opmode_t::kFan;
|
||||
default: return stdAc::opmode_t::kAuto;
|
||||
}
|
||||
}
|
||||
|
||||
/// Set the speed of the fan.
|
||||
/// @param[in] speed The desired setting.
|
||||
/// @note The speed is locked to Low when in Dry mode.
|
||||
void IRAirwellAc::setFan(const uint8_t speed) {
|
||||
_.Fan = (_.Mode == kAirwellDry) ? kAirwellFanLow
|
||||
: std::min(speed, kAirwellFanAuto);
|
||||
}
|
||||
|
||||
/// Get the current fan speed setting.
|
||||
/// @return The current fan speed.
|
||||
uint8_t IRAirwellAc::getFan(void) const {
|
||||
return _.Fan;
|
||||
}
|
||||
|
||||
/// Convert a stdAc::fanspeed_t enum into it's native speed.
|
||||
/// @param[in] speed The enum to be converted.
|
||||
/// @return The native equivalent of the enum.
|
||||
uint8_t IRAirwellAc::convertFan(const stdAc::fanspeed_t speed) {
|
||||
switch (speed) {
|
||||
case stdAc::fanspeed_t::kMin:
|
||||
case stdAc::fanspeed_t::kLow:
|
||||
return kAirwellFanLow;
|
||||
case stdAc::fanspeed_t::kMedium:
|
||||
return kAirwellFanMedium;
|
||||
case stdAc::fanspeed_t::kHigh:
|
||||
case stdAc::fanspeed_t::kMax:
|
||||
return kAirwellFanHigh;
|
||||
default:
|
||||
return kAirwellFanAuto;
|
||||
}
|
||||
}
|
||||
|
||||
/// Convert a native fan speed into its stdAc equivalent.
|
||||
/// @param[in] speed The native setting to be converted.
|
||||
/// @return The stdAc equivalent of the native setting.
|
||||
stdAc::fanspeed_t IRAirwellAc::toCommonFanSpeed(const uint8_t speed) {
|
||||
switch (speed) {
|
||||
case kAirwellFanHigh: return stdAc::fanspeed_t::kMax;
|
||||
case kAirwellFanMedium: return stdAc::fanspeed_t::kMedium;
|
||||
case kAirwellFanLow: return stdAc::fanspeed_t::kMin;
|
||||
default: return stdAc::fanspeed_t::kAuto;
|
||||
}
|
||||
}
|
||||
|
||||
/// Set the temperature.
|
||||
/// @param[in] degrees The temperature in degrees celsius.
|
||||
void IRAirwellAc::setTemp(const uint8_t degrees) {
|
||||
uint8_t temp = std::max(kAirwellMinTemp, degrees);
|
||||
temp = std::min(kAirwellMaxTemp, temp);
|
||||
_.Temp = (temp - kAirwellMinTemp + 1);
|
||||
}
|
||||
|
||||
/// Get the current temperature setting.
|
||||
/// @return Get current setting for temp. in degrees celsius.
|
||||
uint8_t IRAirwellAc::getTemp(void) const {
|
||||
return _.Temp + kAirwellMinTemp - 1;
|
||||
}
|
||||
|
||||
/// Convert the current internal state into its stdAc::state_t equivalent.
|
||||
/// @param[in] prev Ptr to the previous state if required.
|
||||
/// @return The stdAc equivalent of the native settings.
|
||||
stdAc::state_t IRAirwellAc::toCommon(const stdAc::state_t *prev) const {
|
||||
stdAc::state_t result{};
|
||||
// Start with the previous state if given it.
|
||||
if (prev != NULL) {
|
||||
result = *prev;
|
||||
} else {
|
||||
// Set defaults for non-zero values that are not implicitly set for when
|
||||
// there is no previous state.
|
||||
// e.g. Any setting that toggles should probably go here.
|
||||
result.power = false;
|
||||
}
|
||||
result.protocol = decode_type_t::AIRWELL;
|
||||
if (_.PowerToggle) result.power = !result.power;
|
||||
result.mode = toCommonMode(_.Mode);
|
||||
result.celsius = true;
|
||||
result.degrees = getTemp();
|
||||
result.fanspeed = toCommonFanSpeed(_.Fan);
|
||||
// Not supported.
|
||||
result.model = -1;
|
||||
result.turbo = false;
|
||||
result.swingv = stdAc::swingv_t::kOff;
|
||||
result.swingh = stdAc::swingh_t::kOff;
|
||||
result.light = false;
|
||||
result.filter = false;
|
||||
result.econo = false;
|
||||
result.quiet = false;
|
||||
result.clean = false;
|
||||
result.beep = false;
|
||||
result.sleep = -1;
|
||||
result.clock = -1;
|
||||
return result;
|
||||
}
|
||||
|
||||
/// Convert the current internal state into a human readable string.
|
||||
/// @return A human readable string.
|
||||
String IRAirwellAc::toString(void) const {
|
||||
String result = "";
|
||||
result.reserve(70); // Reserve some heap for the string to reduce fragging.
|
||||
result += addBoolToString(_.PowerToggle, kPowerToggleStr, false);
|
||||
result += addModeToString(_.Mode, kAirwellAuto, kAirwellCool,
|
||||
kAirwellHeat, kAirwellDry, kAirwellFan);
|
||||
result += addFanToString(_.Fan, kAirwellFanHigh, kAirwellFanLow,
|
||||
kAirwellFanAuto, kAirwellFanAuto,
|
||||
kAirwellFanMedium);
|
||||
result += addTempToString(getTemp());
|
||||
return result;
|
||||
}
|
||||
101
yoRadio/src/IRremoteESP8266/ir_Airwell.h
Normal file
101
yoRadio/src/IRremoteESP8266/ir_Airwell.h
Normal file
@@ -0,0 +1,101 @@
|
||||
// Copyright 2020 David Conran
|
||||
|
||||
/// @file
|
||||
/// @brief Airwell "Manchester code" based protocol.
|
||||
/// Some other Airwell products use the COOLIX protocol.
|
||||
|
||||
// Supports:
|
||||
// Brand: Airwell, Model: RC08W remote
|
||||
// Brand: Airwell, Model: RC04 remote
|
||||
// Brand: Airwell, Model: DLS 21 DCI R410 AW A/C
|
||||
|
||||
#ifndef IR_AIRWELL_H_
|
||||
#define IR_AIRWELL_H_
|
||||
|
||||
#define __STDC_LIMIT_MACROS
|
||||
#include <stdint.h>
|
||||
#ifndef UNIT_TEST
|
||||
#include <Arduino.h>
|
||||
#endif
|
||||
#include "IRremoteESP8266.h"
|
||||
#include "IRsend.h"
|
||||
#ifdef UNIT_TEST
|
||||
#include "IRsend_test.h"
|
||||
#endif
|
||||
|
||||
/// Native representation of a Airwell A/C message.
|
||||
union AirwellProtocol{
|
||||
uint64_t raw; // The state of the IR remote in native IR code form.
|
||||
struct {
|
||||
uint64_t :19;
|
||||
uint64_t Temp :4;
|
||||
uint64_t :5;
|
||||
uint64_t Fan :2;
|
||||
uint64_t Mode :3;
|
||||
uint64_t PowerToggle:1;
|
||||
uint64_t :0;
|
||||
};
|
||||
};
|
||||
|
||||
// Constants
|
||||
const uint64_t kAirwellKnownGoodState = 0x140500002; // Mode Fan, Speed 1, 25C
|
||||
// Temperature
|
||||
const uint8_t kAirwellMinTemp = 16; // Celsius
|
||||
const uint8_t kAirwellMaxTemp = 30; // Celsius
|
||||
// Fan
|
||||
const uint8_t kAirwellFanLow = 0; // 0b00
|
||||
const uint8_t kAirwellFanMedium = 1; // 0b01
|
||||
const uint8_t kAirwellFanHigh = 2; // 0b10
|
||||
const uint8_t kAirwellFanAuto = 3; // 0b11
|
||||
// Modes
|
||||
const uint8_t kAirwellCool = 1; // 0b001
|
||||
const uint8_t kAirwellHeat = 2; // 0b010
|
||||
const uint8_t kAirwellAuto = 3; // 0b011
|
||||
const uint8_t kAirwellDry = 4; // 0b100
|
||||
const uint8_t kAirwellFan = 5; // 0b101
|
||||
|
||||
|
||||
// Classes
|
||||
/// Class for handling detailed Airwell A/C messages.
|
||||
class IRAirwellAc {
|
||||
public:
|
||||
explicit IRAirwellAc(const uint16_t pin, const bool inverted = false,
|
||||
const bool use_modulation = true);
|
||||
void stateReset();
|
||||
#if SEND_AIRWELL
|
||||
void send(const uint16_t repeat = kAirwellMinRepeats);
|
||||
/// 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_AIRWELL
|
||||
void begin();
|
||||
void setPowerToggle(const bool on);
|
||||
bool getPowerToggle() const;
|
||||
void setTemp(const uint8_t temp);
|
||||
uint8_t getTemp() const;
|
||||
void setFan(const uint8_t speed);
|
||||
uint8_t getFan() const;
|
||||
void setMode(const uint8_t mode);
|
||||
uint8_t getMode() const;
|
||||
uint64_t getRaw() const;
|
||||
void setRaw(const uint64_t state);
|
||||
static uint8_t convertMode(const stdAc::opmode_t mode);
|
||||
static uint8_t convertFan(const stdAc::fanspeed_t speed);
|
||||
static stdAc::opmode_t toCommonMode(const uint8_t mode);
|
||||
static stdAc::fanspeed_t toCommonFanSpeed(const uint8_t speed);
|
||||
stdAc::state_t toCommon(const stdAc::state_t *prev = NULL) const;
|
||||
String toString() const;
|
||||
#ifndef UNIT_TEST
|
||||
|
||||
private:
|
||||
IRsend _irsend; ///< Instance of the IR send class
|
||||
#else
|
||||
/// @cond IGNORE
|
||||
IRsendTest _irsend; ///< Instance of the testing IR send class
|
||||
/// @endcond
|
||||
#endif
|
||||
AirwellProtocol _;
|
||||
};
|
||||
#endif // IR_AIRWELL_H_
|
||||
105
yoRadio/src/IRremoteESP8266/ir_Aiwa.cpp
Normal file
105
yoRadio/src/IRremoteESP8266/ir_Aiwa.cpp
Normal file
@@ -0,0 +1,105 @@
|
||||
// Copyright 2017 David Conran
|
||||
|
||||
#include "IRrecv.h"
|
||||
#include "IRsend.h"
|
||||
|
||||
/// @file
|
||||
/// @brief Aiwa based protocol.
|
||||
/// Based off the RC-T501 RCU
|
||||
/// Inspired by IRremoteESP8266's implementation
|
||||
/// @see https://github.com/z3t0/Arduino-IRremote
|
||||
|
||||
// Supports:
|
||||
// Brand: Aiwa, Model: RC-T501 RCU
|
||||
|
||||
const uint16_t kAiwaRcT501PreBits = 26;
|
||||
const uint16_t kAiwaRcT501PostBits = 1;
|
||||
// NOTE: These are the compliment (inverted) of lirc values as
|
||||
// lirc uses a '0' for a mark, and a '1' for a space.
|
||||
const uint64_t kAiwaRcT501PreData = 0x1D8113FULL; // 26-bits
|
||||
const uint64_t kAiwaRcT501PostData = 1ULL;
|
||||
|
||||
#if SEND_AIWA_RC_T501
|
||||
/// Send an Aiwa RC T501 formatted message.
|
||||
/// Status: BETA / Should work.
|
||||
/// @param[in] data The message to be sent.
|
||||
/// @param[in] nbits The number of bits of the message to be sent.
|
||||
/// Typically kAiwaRcT501Bits. Max is 37 = (64 - 27)
|
||||
/// @param[in] repeat The number of times the command is to be repeated.
|
||||
/// @see http://lirc.sourceforge.net/remotes/aiwa/RC-T501
|
||||
void IRsend::sendAiwaRCT501(uint64_t data, uint16_t nbits, uint16_t repeat) {
|
||||
// Appears to be an extended NEC1 protocol. i.e. 42 bits instead of 32 bits.
|
||||
// So use sendNEC instead, however the twist is it has a fixed 26 bit
|
||||
// prefix, and a fixed postfix bit.
|
||||
uint64_t new_data = ((kAiwaRcT501PreData << (nbits + kAiwaRcT501PostBits)) |
|
||||
(data << kAiwaRcT501PostBits) | kAiwaRcT501PostData);
|
||||
nbits += kAiwaRcT501PreBits + kAiwaRcT501PostBits;
|
||||
if (nbits > sizeof(new_data) * 8)
|
||||
return; // We are overflowing. Abort, and don't send.
|
||||
sendNEC(new_data, nbits, repeat);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if DECODE_AIWA_RC_T501
|
||||
/// Decode the supplied Aiwa RC T501 message.
|
||||
/// Status: BETA / Should work.
|
||||
/// @param[in,out] results Ptr to the data to decode & where to store the decode
|
||||
/// result.
|
||||
/// @param[in] offset The starting index to use when attempting to decode the
|
||||
/// raw data. Typically/Defaults to kStartOffset.
|
||||
/// @param[in] nbits The number of data bits to expect.
|
||||
/// @param[in] strict Flag indicating if we should perform strict matching.
|
||||
/// @return A boolean. True if it can decode it, false if it can't.
|
||||
/// @note
|
||||
/// Aiwa RC T501 appears to be a 42 bit variant of the NEC1 protocol.
|
||||
/// However, we historically (original Arduino IRremote project) treats it as
|
||||
/// a 15 bit (data) protocol. So, we expect nbits to typically be 15, and we
|
||||
/// will remove the prefix and postfix from the raw data, and use that as
|
||||
/// the result.
|
||||
/// @see http://www.sbprojects.net/knowledge/ir/nec.php
|
||||
/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1069
|
||||
bool IRrecv::decodeAiwaRCT501(decode_results *results, uint16_t offset,
|
||||
const uint16_t nbits, const bool strict) {
|
||||
// Compliance
|
||||
if (strict && nbits != kAiwaRcT501Bits)
|
||||
return false; // Doesn't match our protocol defn.
|
||||
|
||||
// Add on the pre & post bits to our requested bit length.
|
||||
uint16_t expected_nbits = nbits + kAiwaRcT501PreBits + kAiwaRcT501PostBits;
|
||||
uint64_t new_data;
|
||||
if (expected_nbits > sizeof(new_data) * 8)
|
||||
return false; // We can't possibly match something that big.
|
||||
// Decode it as a much bigger (non-standard) NEC message, so we have to turn
|
||||
// off strict mode checking for NEC.
|
||||
if (!decodeNEC(results, offset, expected_nbits, false))
|
||||
return false; // The NEC decode had a problem, so we should too.
|
||||
uint16_t actual_bits = results->bits;
|
||||
new_data = results->value;
|
||||
if (actual_bits < expected_nbits)
|
||||
return false; // The data we caught was undersized. Throw it back.
|
||||
if ((new_data & 0x1ULL) != kAiwaRcT501PostData)
|
||||
return false; // The post data doesn't match, so it can't be this protocol.
|
||||
// Trim off the post data bit.
|
||||
new_data >>= kAiwaRcT501PostBits;
|
||||
actual_bits -= kAiwaRcT501PostBits;
|
||||
|
||||
// Extract out our likely new value and put it back in the results.
|
||||
actual_bits -= kAiwaRcT501PreBits;
|
||||
results->value = new_data & ((1ULL << actual_bits) - 1);
|
||||
|
||||
// Check the prefix data matches.
|
||||
new_data >>= actual_bits; // Trim off the new data to expose the prefix.
|
||||
if (new_data != kAiwaRcT501PreData) // Check the prefix.
|
||||
return false;
|
||||
|
||||
// Compliance
|
||||
if (strict && results->bits != expected_nbits) return false;
|
||||
|
||||
// Success
|
||||
results->decode_type = AIWA_RC_T501;
|
||||
results->bits = actual_bits;
|
||||
results->address = 0;
|
||||
results->command = 0;
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
354
yoRadio/src/IRremoteESP8266/ir_Amcor.cpp
Normal file
354
yoRadio/src/IRremoteESP8266/ir_Amcor.cpp
Normal file
@@ -0,0 +1,354 @@
|
||||
// Copyright 2019 David Conran
|
||||
|
||||
/// @file
|
||||
/// @brief Amcor A/C protocol.
|
||||
/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/385
|
||||
/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/834
|
||||
|
||||
#include "ir_Amcor.h"
|
||||
#include <algorithm>
|
||||
#include <cstring>
|
||||
#include "IRrecv.h"
|
||||
#include "IRsend.h"
|
||||
#include "IRtext.h"
|
||||
#include "IRutils.h"
|
||||
|
||||
// Constants
|
||||
const uint16_t kAmcorHdrMark = 8200;
|
||||
const uint16_t kAmcorHdrSpace = 4200;
|
||||
const uint16_t kAmcorOneMark = 1500;
|
||||
const uint16_t kAmcorZeroMark = 600;
|
||||
const uint16_t kAmcorOneSpace = kAmcorZeroMark;
|
||||
const uint16_t kAmcorZeroSpace = kAmcorOneMark;
|
||||
const uint16_t kAmcorFooterMark = 1900;
|
||||
const uint16_t kAmcorGap = 34300;
|
||||
const uint8_t kAmcorTolerance = 40;
|
||||
|
||||
using irutils::addBoolToString;
|
||||
using irutils::addModeToString;
|
||||
using irutils::addFanToString;
|
||||
using irutils::addTempToString;
|
||||
|
||||
#if SEND_AMCOR
|
||||
/// Send a Amcor HVAC formatted message.
|
||||
/// Status: STABLE / Reported as working.
|
||||
/// @param[in] data The message to be sent.
|
||||
/// @param[in] nbytes The number of bytes of message to be sent.
|
||||
/// @param[in] repeat The number of times the command is to be repeated.
|
||||
void IRsend::sendAmcor(const unsigned char data[], const uint16_t nbytes,
|
||||
const uint16_t repeat) {
|
||||
// Check if we have enough bytes to send a proper message.
|
||||
if (nbytes < kAmcorStateLength) return;
|
||||
sendGeneric(kAmcorHdrMark, kAmcorHdrSpace, kAmcorOneMark, kAmcorOneSpace,
|
||||
kAmcorZeroMark, kAmcorZeroSpace, kAmcorFooterMark, kAmcorGap,
|
||||
data, nbytes, 38, false, repeat, kDutyDefault);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if DECODE_AMCOR
|
||||
/// Decode the supplied Amcor HVAC message.
|
||||
/// Status: STABLE / Reported as working.
|
||||
/// @param[in,out] results Ptr to the data to decode & where to store the decode
|
||||
/// result.
|
||||
/// @param[in] offset The starting index to use when attempting to decode the
|
||||
/// raw data. Typically/Defaults to kStartOffset.
|
||||
/// @param[in] nbits The number of data bits to expect.
|
||||
/// @param[in] strict Flag indicating if we should perform strict matching.
|
||||
/// @return A boolean. True if it can decode it, false if it can't.
|
||||
bool IRrecv::decodeAmcor(decode_results *results, uint16_t offset,
|
||||
const uint16_t nbits, const bool strict) {
|
||||
if (results->rawlen <= 2 * nbits + kHeader - 1 + offset)
|
||||
return false; // Can't possibly be a valid Amcor message.
|
||||
if (strict && nbits != kAmcorBits)
|
||||
return false; // We expect Amcor to be 64 bits of message.
|
||||
|
||||
uint16_t used;
|
||||
// Header + Data Block (64 bits) + Footer
|
||||
used = matchGeneric(results->rawbuf + offset, results->state,
|
||||
results->rawlen - offset, 64,
|
||||
kAmcorHdrMark, kAmcorHdrSpace,
|
||||
kAmcorOneMark, kAmcorOneSpace,
|
||||
kAmcorZeroMark, kAmcorZeroSpace,
|
||||
kAmcorFooterMark, kAmcorGap, true,
|
||||
kAmcorTolerance, 0, false);
|
||||
if (!used) return false;
|
||||
offset += used;
|
||||
|
||||
if (strict) {
|
||||
if (!IRAmcorAc::validChecksum(results->state)) return false;
|
||||
}
|
||||
|
||||
// Success
|
||||
results->bits = nbits;
|
||||
results->decode_type = AMCOR;
|
||||
// No need to record the state as we stored it as we decoded it.
|
||||
// As we use result->state, we don't record value, address, or command as it
|
||||
// is a union data type.
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
/// Class constructor
|
||||
/// @param[in] pin GPIO to be used when sending.
|
||||
/// @param[in] inverted Is the output signal to be inverted?
|
||||
/// @param[in] use_modulation Is frequency modulation to be used?
|
||||
IRAmcorAc::IRAmcorAc(const uint16_t pin, const bool inverted,
|
||||
const bool use_modulation)
|
||||
: _irsend(pin, inverted, use_modulation) { this->stateReset(); }
|
||||
|
||||
/// Set up hardware to be able to send a message.
|
||||
void IRAmcorAc::begin(void) { _irsend.begin(); }
|
||||
|
||||
#if SEND_AMCOR
|
||||
/// Send the current internal state as an IR message.
|
||||
/// @param[in] repeat Nr. of times the message will be repeated.
|
||||
void IRAmcorAc::send(const uint16_t repeat) {
|
||||
_irsend.sendAmcor(getRaw(), kAmcorStateLength, repeat);
|
||||
}
|
||||
#endif // SEND_AMCOR
|
||||
|
||||
/// Calculate the checksum for the supplied state.
|
||||
/// @param[in] state The source state to generate the checksum from.
|
||||
/// @param[in] length Length of the supplied state to checksum.
|
||||
/// @return The checksum value.
|
||||
uint8_t IRAmcorAc::calcChecksum(const uint8_t state[], const uint16_t length) {
|
||||
return irutils::sumNibbles(state, length - 1);
|
||||
}
|
||||
|
||||
/// Verify the checksum is valid for a given state.
|
||||
/// @param[in] state The array to verify the checksum of.
|
||||
/// @param[in] length The size of the state.
|
||||
/// @return A boolean indicating if it's checksum is valid.
|
||||
bool IRAmcorAc::validChecksum(const uint8_t state[], const uint16_t length) {
|
||||
return (state[length - 1] == IRAmcorAc::calcChecksum(state, length));
|
||||
}
|
||||
|
||||
/// Update the checksum value for the internal state.
|
||||
void IRAmcorAc::checksum(void) {
|
||||
_.Sum = IRAmcorAc::calcChecksum(_.raw, kAmcorStateLength);
|
||||
}
|
||||
|
||||
/// Reset the internals of the object to a known good state.
|
||||
void IRAmcorAc::stateReset(void) {
|
||||
for (uint8_t i = 1; i < kAmcorStateLength; i++) _.raw[i] = 0x0;
|
||||
_.raw[0] = 0x01;
|
||||
_.Fan = kAmcorFanAuto;
|
||||
_.Mode = kAmcorAuto;
|
||||
_.Temp = 25; // 25C
|
||||
}
|
||||
|
||||
/// Get the raw state of the object, suitable to be sent with the appropriate
|
||||
/// IRsend object method.
|
||||
/// @return A PTR to the internal state.
|
||||
uint8_t* IRAmcorAc::getRaw(void) {
|
||||
checksum(); // Ensure correct bit array before returning
|
||||
return _.raw;
|
||||
}
|
||||
|
||||
/// Set the raw state of the object.
|
||||
/// @param[in] state The raw state from the native IR message.
|
||||
void IRAmcorAc::setRaw(const uint8_t state[]) {
|
||||
std::memcpy(_.raw, state, kAmcorStateLength);
|
||||
}
|
||||
|
||||
/// Set the internal state to have the power on.
|
||||
void IRAmcorAc::on(void) { setPower(true); }
|
||||
|
||||
/// Set the internal state to have the power off.
|
||||
void IRAmcorAc::off(void) { setPower(false); }
|
||||
|
||||
/// Set the internal state to have the desired power.
|
||||
/// @param[in] on The desired power state.
|
||||
void IRAmcorAc::setPower(const bool on) {
|
||||
_.Power = (on ? kAmcorPowerOn : kAmcorPowerOff);
|
||||
}
|
||||
|
||||
/// Get the power setting from the internal state.
|
||||
/// @return A boolean indicating the power setting.
|
||||
bool IRAmcorAc::getPower(void) const {
|
||||
return _.Power == kAmcorPowerOn;
|
||||
}
|
||||
|
||||
/// Set the temperature.
|
||||
/// @param[in] degrees The temperature in degrees celsius.
|
||||
void IRAmcorAc::setTemp(const uint8_t degrees) {
|
||||
uint8_t temp = std::max(kAmcorMinTemp, degrees);
|
||||
temp = std::min(kAmcorMaxTemp, temp);
|
||||
_.Temp = temp;
|
||||
}
|
||||
|
||||
/// Get the current temperature setting.
|
||||
/// @return Get current setting for temp. in degrees celsius.
|
||||
uint8_t IRAmcorAc::getTemp(void) const {
|
||||
return _.Temp;
|
||||
}
|
||||
|
||||
/// Control the current Maximum Cooling or Heating setting. (i.e. Turbo)
|
||||
/// @note Only allowed in Cool or Heat mode.
|
||||
/// @param[in] on The desired setting.
|
||||
void IRAmcorAc::setMax(const bool on) {
|
||||
if (on) {
|
||||
switch (_.Mode) {
|
||||
case kAmcorCool: _.Temp = kAmcorMinTemp; break;
|
||||
case kAmcorHeat: _.Temp = kAmcorMaxTemp; break;
|
||||
// Not allowed in all other operating modes.
|
||||
default: return;
|
||||
}
|
||||
}
|
||||
_.Max = (on ? kAmcorMax : 0);
|
||||
}
|
||||
|
||||
/// Is the Maximum Cooling or Heating setting (i.e. Turbo) setting on?
|
||||
/// @return The current value.
|
||||
bool IRAmcorAc::getMax(void) const {
|
||||
return _.Max == kAmcorMax;
|
||||
}
|
||||
|
||||
/// Set the speed of the fan.
|
||||
/// @param[in] speed The desired setting.
|
||||
void IRAmcorAc::setFan(const uint8_t speed) {
|
||||
switch (speed) {
|
||||
case kAmcorFanAuto:
|
||||
case kAmcorFanMin:
|
||||
case kAmcorFanMed:
|
||||
case kAmcorFanMax:
|
||||
_.Fan = speed;
|
||||
break;
|
||||
default:
|
||||
_.Fan = kAmcorFanAuto;
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the current fan speed setting.
|
||||
/// @return The current fan speed.
|
||||
uint8_t IRAmcorAc::getFan(void) const {
|
||||
return _.Fan;
|
||||
}
|
||||
|
||||
/// Get the current operation mode setting.
|
||||
/// @return The current operation mode.
|
||||
uint8_t IRAmcorAc::getMode(void) const {
|
||||
return _.Mode;
|
||||
}
|
||||
|
||||
/// Set the desired operation mode.
|
||||
/// @param[in] mode The desired operation mode.
|
||||
void IRAmcorAc::setMode(const uint8_t mode) {
|
||||
switch (mode) {
|
||||
case kAmcorFan:
|
||||
case kAmcorCool:
|
||||
case kAmcorHeat:
|
||||
case kAmcorDry:
|
||||
case kAmcorAuto:
|
||||
_.Vent = (mode == kAmcorFan) ? kAmcorVentOn : 0;
|
||||
_.Mode = mode;
|
||||
return;
|
||||
default:
|
||||
_.Vent = 0;
|
||||
_.Mode = kAmcorAuto;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/// Convert a stdAc::opmode_t enum into its native mode.
|
||||
/// @param[in] mode The enum to be converted.
|
||||
/// @return The native equivalent of the enum.
|
||||
uint8_t IRAmcorAc::convertMode(const stdAc::opmode_t mode) {
|
||||
switch (mode) {
|
||||
case stdAc::opmode_t::kCool:
|
||||
return kAmcorCool;
|
||||
case stdAc::opmode_t::kHeat:
|
||||
return kAmcorHeat;
|
||||
case stdAc::opmode_t::kDry:
|
||||
return kAmcorDry;
|
||||
case stdAc::opmode_t::kFan:
|
||||
return kAmcorFan;
|
||||
default:
|
||||
return kAmcorAuto;
|
||||
}
|
||||
}
|
||||
|
||||
/// Convert a stdAc::fanspeed_t enum into it's native speed.
|
||||
/// @param[in] speed The enum to be converted.
|
||||
/// @return The native equivalent of the enum.
|
||||
uint8_t IRAmcorAc::convertFan(const stdAc::fanspeed_t speed) {
|
||||
switch (speed) {
|
||||
case stdAc::fanspeed_t::kMin:
|
||||
case stdAc::fanspeed_t::kLow:
|
||||
return kAmcorFanMin;
|
||||
case stdAc::fanspeed_t::kMedium:
|
||||
return kAmcorFanMed;
|
||||
case stdAc::fanspeed_t::kHigh:
|
||||
case stdAc::fanspeed_t::kMax:
|
||||
return kAmcorFanMax;
|
||||
default:
|
||||
return kAmcorFanAuto;
|
||||
}
|
||||
}
|
||||
|
||||
/// Convert a native mode into its stdAc equivalent.
|
||||
/// @param[in] mode The native setting to be converted.
|
||||
/// @return The stdAc equivalent of the native setting.
|
||||
stdAc::opmode_t IRAmcorAc::toCommonMode(const uint8_t mode) {
|
||||
switch (mode) {
|
||||
case kAmcorCool: return stdAc::opmode_t::kCool;
|
||||
case kAmcorHeat: return stdAc::opmode_t::kHeat;
|
||||
case kAmcorDry: return stdAc::opmode_t::kDry;
|
||||
case kAmcorFan: return stdAc::opmode_t::kFan;
|
||||
default: return stdAc::opmode_t::kAuto;
|
||||
}
|
||||
}
|
||||
|
||||
/// Convert a native fan speed into its stdAc equivalent.
|
||||
/// @param[in] speed The native setting to be converted.
|
||||
/// @return The stdAc equivalent of the native setting.
|
||||
stdAc::fanspeed_t IRAmcorAc::toCommonFanSpeed(const uint8_t speed) {
|
||||
switch (speed) {
|
||||
case kAmcorFanMax: return stdAc::fanspeed_t::kMax;
|
||||
case kAmcorFanMed: return stdAc::fanspeed_t::kMedium;
|
||||
case kAmcorFanMin: return stdAc::fanspeed_t::kMin;
|
||||
default: return stdAc::fanspeed_t::kAuto;
|
||||
}
|
||||
}
|
||||
|
||||
/// Convert the current internal state into its stdAc::state_t equivalent.
|
||||
/// @return The stdAc equivalent of the native settings.
|
||||
stdAc::state_t IRAmcorAc::toCommon(void) const {
|
||||
stdAc::state_t result{};
|
||||
result.protocol = decode_type_t::AMCOR;
|
||||
result.power = getPower();
|
||||
result.mode = toCommonMode(_.Mode);
|
||||
result.celsius = true;
|
||||
result.degrees = _.Temp;
|
||||
result.fanspeed = toCommonFanSpeed(_.Fan);
|
||||
// Not supported.
|
||||
result.model = -1;
|
||||
result.turbo = false;
|
||||
result.swingv = stdAc::swingv_t::kOff;
|
||||
result.swingh = stdAc::swingh_t::kOff;
|
||||
result.light = false;
|
||||
result.filter = false;
|
||||
result.econo = false;
|
||||
result.quiet = false;
|
||||
result.clean = false;
|
||||
result.beep = false;
|
||||
result.sleep = -1;
|
||||
result.clock = -1;
|
||||
return result;
|
||||
}
|
||||
|
||||
/// Convert the current internal state into a human readable string.
|
||||
/// @return A human readable string.
|
||||
String IRAmcorAc::toString(void) const {
|
||||
String result = "";
|
||||
result.reserve(70); // Reserve some heap for the string to reduce fragging.
|
||||
result += addBoolToString(getPower(), kPowerStr, false);
|
||||
result += addModeToString(_.Mode, kAmcorAuto, kAmcorCool,
|
||||
kAmcorHeat, kAmcorDry, kAmcorFan);
|
||||
result += addFanToString(_.Fan, kAmcorFanMax, kAmcorFanMin,
|
||||
kAmcorFanAuto, kAmcorFanAuto,
|
||||
kAmcorFanMed);
|
||||
result += addTempToString(_.Temp);
|
||||
result += addBoolToString(getMax(), kMaxStr);
|
||||
return result;
|
||||
}
|
||||
141
yoRadio/src/IRremoteESP8266/ir_Amcor.h
Normal file
141
yoRadio/src/IRremoteESP8266/ir_Amcor.h
Normal file
@@ -0,0 +1,141 @@
|
||||
// Copyright 2019 David Conran
|
||||
|
||||
/// @file
|
||||
/// @brief Amcor A/C protocol.
|
||||
/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/834
|
||||
/// @remark Kudos to ldellus; For the breakdown and mapping of the bit values.
|
||||
// Supports:
|
||||
// Brand: Amcor, Model: ADR-853H A/C
|
||||
// Brand: Amcor, Model: TAC-495 remote
|
||||
// Brand: Amcor, Model: TAC-444 remote
|
||||
|
||||
#ifndef IR_AMCOR_H_
|
||||
#define IR_AMCOR_H_
|
||||
|
||||
#define __STDC_LIMIT_MACROS
|
||||
#include <stdint.h>
|
||||
#ifndef UNIT_TEST
|
||||
#include <Arduino.h>
|
||||
#endif
|
||||
#include "IRremoteESP8266.h"
|
||||
#include "IRsend.h"
|
||||
#ifdef UNIT_TEST
|
||||
#include "IRsend_test.h"
|
||||
#endif
|
||||
|
||||
|
||||
/// Native representation of a Amcor A/C message.
|
||||
union AmcorProtocol{
|
||||
uint8_t raw[kAmcorStateLength]; // The state of the IR remote.
|
||||
struct {
|
||||
// Byte 0
|
||||
uint8_t :8; // Typically 0x01
|
||||
// Byte 1
|
||||
uint8_t Mode :3;
|
||||
uint8_t :1;
|
||||
uint8_t Fan :3;
|
||||
uint8_t :1;
|
||||
// Byte 2
|
||||
uint8_t :1;
|
||||
uint8_t Temp :6;
|
||||
uint8_t :1;
|
||||
// Byte 3
|
||||
uint8_t :8;
|
||||
// Byte 4
|
||||
uint8_t :8;
|
||||
// Byte 5
|
||||
uint8_t :4;
|
||||
uint8_t Power :4;
|
||||
// Byte 6
|
||||
uint8_t Max :2;
|
||||
uint8_t :4;
|
||||
uint8_t Vent :2;
|
||||
// Byte 7
|
||||
uint8_t Sum :8;
|
||||
};
|
||||
};
|
||||
|
||||
// Constants
|
||||
|
||||
// Fan Control
|
||||
const uint8_t kAmcorFanMin = 0b001;
|
||||
const uint8_t kAmcorFanMed = 0b010;
|
||||
const uint8_t kAmcorFanMax = 0b011;
|
||||
const uint8_t kAmcorFanAuto = 0b100;
|
||||
// Modes
|
||||
const uint8_t kAmcorCool = 0b001;
|
||||
const uint8_t kAmcorHeat = 0b010;
|
||||
const uint8_t kAmcorFan = 0b011; // Aka "Vent"
|
||||
const uint8_t kAmcorDry = 0b100;
|
||||
const uint8_t kAmcorAuto = 0b101;
|
||||
|
||||
// Temperature
|
||||
const uint8_t kAmcorMinTemp = 12; // Celsius
|
||||
const uint8_t kAmcorMaxTemp = 32; // Celsius
|
||||
|
||||
// Power
|
||||
const uint8_t kAmcorPowerOn = 0b0011; // 0x3
|
||||
const uint8_t kAmcorPowerOff = 0b1100; // 0xC
|
||||
|
||||
// Max Mode (aka "Lo" in Cool and "Hi" in Heat)
|
||||
const uint8_t kAmcorMax = 0b11;
|
||||
|
||||
// "Vent" Mode
|
||||
const uint8_t kAmcorVentOn = 0b11;
|
||||
|
||||
|
||||
// Classes
|
||||
|
||||
/// Class for handling detailed Amcor A/C messages.
|
||||
class IRAmcorAc {
|
||||
public:
|
||||
explicit IRAmcorAc(const uint16_t pin, const bool inverted = false,
|
||||
const bool use_modulation = true);
|
||||
|
||||
void stateReset();
|
||||
#if SEND_AMCOR
|
||||
void send(const uint16_t repeat = kAmcorDefaultRepeat);
|
||||
/// 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_AMCOR
|
||||
void begin();
|
||||
static uint8_t calcChecksum(const uint8_t state[],
|
||||
const uint16_t length = kAmcorStateLength);
|
||||
static bool validChecksum(const uint8_t state[],
|
||||
const uint16_t length = kAmcorStateLength);
|
||||
void setPower(const bool state);
|
||||
bool getPower(void) const;
|
||||
void on(void);
|
||||
void off(void);
|
||||
void setTemp(const uint8_t temp);
|
||||
uint8_t getTemp(void) const;
|
||||
void setMax(const bool on);
|
||||
bool getMax(void) const;
|
||||
void setFan(const uint8_t speed);
|
||||
uint8_t getFan(void) const;
|
||||
void setMode(const uint8_t mode);
|
||||
uint8_t getMode(void) const;
|
||||
uint8_t* getRaw(void);
|
||||
void setRaw(const uint8_t state[]);
|
||||
static uint8_t convertMode(const stdAc::opmode_t mode);
|
||||
static uint8_t convertFan(const stdAc::fanspeed_t speed);
|
||||
static stdAc::opmode_t toCommonMode(const uint8_t mode);
|
||||
static stdAc::fanspeed_t toCommonFanSpeed(const uint8_t speed);
|
||||
stdAc::state_t toCommon(void) const;
|
||||
String toString(void) const;
|
||||
#ifndef UNIT_TEST
|
||||
|
||||
private:
|
||||
IRsend _irsend;
|
||||
#else
|
||||
/// @cond IGNORE
|
||||
IRsendTest _irsend;
|
||||
/// @endcond
|
||||
#endif
|
||||
AmcorProtocol _;
|
||||
void checksum(void);
|
||||
};
|
||||
#endif // IR_AMCOR_H_
|
||||
1822
yoRadio/src/IRremoteESP8266/ir_Argo.cpp
Normal file
1822
yoRadio/src/IRremoteESP8266/ir_Argo.cpp
Normal file
File diff suppressed because it is too large
Load Diff
521
yoRadio/src/IRremoteESP8266/ir_Argo.h
Normal file
521
yoRadio/src/IRremoteESP8266/ir_Argo.h
Normal file
@@ -0,0 +1,521 @@
|
||||
// Copyright 2017 Schmolders
|
||||
// Copyright 2022 crankyoldgit
|
||||
// Copyright 2022 Mateusz Bronk (mbronk)
|
||||
/// @file
|
||||
/// @brief Support for Argo Ulisse 13 DCI Mobile Split ACs.
|
||||
|
||||
// Supports:
|
||||
// Brand: Argo, Model: Ulisse 13 DCI Mobile Split A/C [WREM2 remote]
|
||||
// Brand: Argo, Model: Ulisse Eco Mobile Split A/C (Wifi) [WREM3 remote]
|
||||
|
||||
#ifndef IR_ARGO_H_
|
||||
#define IR_ARGO_H_
|
||||
|
||||
#include <set>
|
||||
#include <utility>
|
||||
#ifndef UNIT_TEST
|
||||
#include <Arduino.h>
|
||||
#endif
|
||||
#include "IRremoteESP8266.h"
|
||||
#include "IRsend.h"
|
||||
#ifdef UNIT_TEST
|
||||
#include "IRsend_test.h"
|
||||
#endif
|
||||
|
||||
|
||||
// ARGO Ulisse DCI
|
||||
|
||||
/// Native representation of a Argo A/C message for WREM-2 remote.
|
||||
union ArgoProtocol {
|
||||
uint8_t raw[kArgoStateLength]; ///< The state in native IR code form
|
||||
struct {
|
||||
// Byte 0
|
||||
uint64_t Pre1 :8; // Typically 0b00110101
|
||||
// Byte 1
|
||||
uint64_t Pre2 :8; // Typically 0b10101111
|
||||
// Byte 2~4
|
||||
uint64_t :3;
|
||||
uint64_t Mode :3;
|
||||
uint64_t Temp :5; // straddle byte 2 and 3
|
||||
uint64_t Fan :2;
|
||||
uint64_t RoomTemp :5; // straddle byte 3 and 4
|
||||
uint64_t Flap :3; // SwingV
|
||||
uint64_t :3; // OnTimer, maybe hours
|
||||
// Byte 5
|
||||
uint64_t :8; // OnTimer, maybe minutes
|
||||
// Byte 6
|
||||
uint64_t :8; // OffTimer, maybe minutes
|
||||
// Byte 7
|
||||
uint64_t :3; // OffTimer, maybe hours
|
||||
uint64_t :5; // Time
|
||||
// Byte 8
|
||||
uint32_t :6; // Time
|
||||
uint32_t :1; // Timer On/Off
|
||||
uint32_t :1; // Timer Program
|
||||
// Byte 9
|
||||
uint32_t :1; // Timer Program
|
||||
uint32_t :1; // Timer 1h
|
||||
uint32_t Night :1;
|
||||
uint32_t Max :1;
|
||||
uint32_t :1; // Filter
|
||||
uint32_t Power :1;
|
||||
uint32_t :1; // const 0
|
||||
uint32_t iFeel :1;
|
||||
// Byte 10~11
|
||||
uint32_t Post :2;
|
||||
uint32_t Sum :8; // straddle byte 10 and 11
|
||||
uint32_t :6;
|
||||
};
|
||||
struct {
|
||||
// Byte 0-1
|
||||
uint8_t :8;
|
||||
uint8_t :8;
|
||||
// Byte 2-3
|
||||
uint8_t CheckHi :3;
|
||||
uint8_t SensorT :5;
|
||||
uint8_t Fixed :3; // Typically 0b011
|
||||
uint8_t CheckLo :5;
|
||||
};
|
||||
};
|
||||
|
||||
/// Native representation of A/C IR message for WREM-3 remote
|
||||
/// @note The remote sends 4 different IR command types, varying in length
|
||||
/// and methods of checksum calculation
|
||||
/// - [0b00] Regular A/C command (change operation mode) - 6-byte
|
||||
/// - [0b01] iFeel Temperature report - 2-byte
|
||||
/// - [0b10] Timer command - 9-byte
|
||||
/// - [0b11] Config command - 4-byte
|
||||
/// @note The 1st 2 structures are unnamed for compat. with @c ArgoProtocol
|
||||
/// 1st byte definition is a header common across all commands though
|
||||
union ArgoProtocolWREM3 {
|
||||
uint8_t raw[kArgoStateLength]; ///< The state in native IR code form
|
||||
struct {
|
||||
// Byte 0 (same definition across the union)
|
||||
uint8_t Pre1 :4; /// Preamble: 0b1011 @ref kArgoWrem3Preamble
|
||||
uint8_t IrChannel :2; /// 0..3 range
|
||||
uint8_t IrCommandType :2; /// @ref argoIrMessageType_t
|
||||
// Byte 1
|
||||
uint8_t RoomTemp :5; // in Celsius, range: 4..35 (offset by -4[*C])
|
||||
uint8_t Mode :3; /// @ref argoMode_t
|
||||
// Byte 2
|
||||
uint8_t Temp :5; // in Celsius, range: 10..32 (offset by -4[*C])
|
||||
uint8_t Fan :3; /// @ref argoFan_t
|
||||
// Byte3
|
||||
uint8_t Flap :3; /// SwingV @ref argoFlap_t
|
||||
uint8_t Power :1;
|
||||
uint8_t iFeel :1;
|
||||
uint8_t Night :1;
|
||||
uint8_t Eco :1;
|
||||
uint8_t Max :1; ///< a.k.a. Turbo
|
||||
// Byte4
|
||||
uint8_t Filter :1;
|
||||
uint8_t Light :1;
|
||||
uint8_t Post1 :6; /// Unknown, always 0b110000 (TempScale?)
|
||||
// Byte5
|
||||
uint8_t Sum :8; /// Checksum
|
||||
};
|
||||
struct {
|
||||
// Byte 0 (same definition across the union)
|
||||
uint8_t :8; // {Pre1 | IrChannel | IrCommandType}
|
||||
// Byte 1
|
||||
uint8_t SensorT :5; // in Celsius, range: 4..35 (offset by -4[*C])
|
||||
uint8_t CheckHi :3; // Checksum (short)
|
||||
};
|
||||
struct Timer {
|
||||
// Byte 0 (same definition across the union)
|
||||
uint8_t : 8; // {Pre1 | IrChannel | IrCommandType}
|
||||
// Byte 1
|
||||
uint8_t IsOn : 1;
|
||||
uint8_t TimerType : 3;
|
||||
uint8_t CurrentTimeLo : 4;
|
||||
// Byte 2
|
||||
uint8_t CurrentTimeHi : 7;
|
||||
uint8_t CurrentWeekdayLo : 1;
|
||||
// Byte 3
|
||||
uint8_t CurrentWeekdayHi : 2;
|
||||
uint8_t DelayTimeLo : 6;
|
||||
// Byte 4
|
||||
uint8_t DelayTimeHi : 5;
|
||||
uint8_t TimerStartLo : 3;
|
||||
// Byte 5
|
||||
uint8_t TimerStartHi : 8;
|
||||
// Byte 6
|
||||
uint8_t TimerEndLo : 8;
|
||||
// Byte 7
|
||||
uint8_t TimerEndHi : 3;
|
||||
uint8_t TimerActiveDaysLo : 5; // Bitmap (LSBit is Sunday)
|
||||
// Byte 8
|
||||
uint8_t TimerActiveDaysHi : 2; // Bitmap (LSBit is Sunday)
|
||||
uint8_t Post1 : 1; // Unknown, always 1
|
||||
uint8_t Checksum : 5;
|
||||
} timer;
|
||||
struct Config {
|
||||
uint8_t :8; // Byte 0 {Pre1 | IrChannel | IrCommandType}
|
||||
uint8_t Key :8; // Byte 1
|
||||
uint8_t Value :8; // Byte 2
|
||||
uint8_t Checksum :8; // Byte 3
|
||||
} config;
|
||||
};
|
||||
|
||||
// Constants (WREM-2). Store MSB left.
|
||||
const uint8_t kArgoHeatBit = 0b00100000;
|
||||
const uint8_t kArgoPreamble1 = 0b10101100;
|
||||
const uint8_t kArgoPreamble2 = 0b11110101;
|
||||
const uint8_t kArgoPost = 0b00000010;
|
||||
|
||||
// Constants (generic)
|
||||
const uint16_t kArgoFrequency = 38000; // Hz
|
||||
// Temp
|
||||
const uint8_t kArgoTempDelta = 4;
|
||||
const uint8_t kArgoMaxRoomTemp = 35; // Celsius
|
||||
const uint8_t kArgoMinTemp = 10; // Celsius delta +4
|
||||
const uint8_t kArgoMaxTemp = 32; // Celsius
|
||||
const uint8_t kArgoMaxChannel = 3;
|
||||
|
||||
|
||||
/// @brief IR message type (determines the payload part of IR command)
|
||||
/// @note Raw values match WREM-3 protocol, but the enum is used in generic
|
||||
/// context
|
||||
/// @note WREM-3 remote supports all commands separately, whereas
|
||||
/// WREM-2 (allegedly) only has the @c AC_CONTROL and @c IFEEL_TEMP_REPORT
|
||||
/// (timers are part of @c AC_CONTROL command), and there's no config.
|
||||
enum class argoIrMessageType_t : uint8_t {
|
||||
AC_CONTROL = 0b00,
|
||||
IFEEL_TEMP_REPORT = 0b01,
|
||||
TIMER_COMMAND = 0b10, // WREM-3 only (WREM-2 has it under AC_CONTROL)
|
||||
CONFIG_PARAM_SET = 0b11 // WREM-3 only
|
||||
};
|
||||
|
||||
/// @brief A/C operation mode
|
||||
/// @note Raw values match WREM-3 protocol, but the enum is used in generic
|
||||
/// context
|
||||
enum class argoMode_t : uint8_t {
|
||||
COOL = 0b001,
|
||||
DRY = 0b010,
|
||||
HEAT = 0b011,
|
||||
FAN = 0b100,
|
||||
AUTO = 0b101
|
||||
};
|
||||
|
||||
// Raw mode definitions for WREM-2 remote
|
||||
// (not wraped into a ns nor enum for backwards-compat.)
|
||||
const uint8_t kArgoCool = 0b000;
|
||||
const uint8_t kArgoDry = 0b001;
|
||||
const uint8_t kArgoAuto = 0b010;
|
||||
const uint8_t kArgoOff = 0b011;
|
||||
const uint8_t kArgoHeat = 0b100;
|
||||
const uint8_t kArgoHeatAuto = 0b101;
|
||||
// ?no idea what mode that is
|
||||
const uint8_t kArgoHeatBlink = 0b110;
|
||||
|
||||
/// @brief Fan speed
|
||||
/// @note Raw values match WREM-3 protocol, but the enum is used in generic
|
||||
/// context
|
||||
enum class argoFan_t : uint8_t {
|
||||
FAN_AUTO = 0b000,
|
||||
FAN_LOWEST = 0b001,
|
||||
FAN_LOWER = 0b010,
|
||||
FAN_LOW = 0b011,
|
||||
FAN_MEDIUM = 0b100,
|
||||
FAN_HIGH = 0b101,
|
||||
FAN_HIGHEST = 0b110
|
||||
};
|
||||
|
||||
// Raw fan speed definitions for WREM-2 remote
|
||||
// (not wraped into a ns nor enum for backwards-compat.)
|
||||
const uint8_t kArgoFanAuto = 0; // 0b00
|
||||
const uint8_t kArgoFan1 = 1; // 0b01
|
||||
const uint8_t kArgoFan2 = 2; // 0b10
|
||||
const uint8_t kArgoFan3 = 3; // 0b11
|
||||
|
||||
/// @brief Flap position (swing-V)
|
||||
/// @note Raw values match WREM-3 protocol, but the enum is used in generic
|
||||
/// context
|
||||
enum class argoFlap_t : uint8_t {
|
||||
FLAP_AUTO = 0,
|
||||
FLAP_1 = 1, // Highest
|
||||
FLAP_2 = 2,
|
||||
FLAP_3 = 3,
|
||||
FLAP_4 = 4,
|
||||
FLAP_5 = 5,
|
||||
FLAP_6 = 6, // Lowest
|
||||
FLAP_FULL = 7
|
||||
};
|
||||
|
||||
// Raw Flap/SwingV definitions for WREM-2 remote
|
||||
// (not wraped into a ns nor enum for backwards-compat.)
|
||||
const uint8_t kArgoFlapAuto = 0;
|
||||
const uint8_t kArgoFlap1 = 1;
|
||||
const uint8_t kArgoFlap2 = 2;
|
||||
const uint8_t kArgoFlap3 = 3;
|
||||
const uint8_t kArgoFlap4 = 4;
|
||||
const uint8_t kArgoFlap5 = 5;
|
||||
const uint8_t kArgoFlap6 = 6;
|
||||
const uint8_t kArgoFlapFull = 7;
|
||||
|
||||
// Legacy defines. (Deprecated)
|
||||
#define ARGO_COOL_ON kArgoCoolOn
|
||||
#define ARGO_COOL_OFF kArgoCoolOff
|
||||
#define ARGO_COOL_AUTO kArgoCoolAuto
|
||||
#define ARGO_COOL_HUM kArgoCoolHum
|
||||
#define ARGO_HEAT_ON kArgoHeatOn
|
||||
#define ARGO_HEAT_AUTO kArgoHeatAuto
|
||||
#define ARGO_HEAT_BLINK kArgoHeatBlink
|
||||
#define ARGO_MIN_TEMP kArgoMinTemp
|
||||
#define ARGO_MAX_TEMP kArgoMaxTemp
|
||||
#define ARGO_FAN_AUTO kArgoFanAuto
|
||||
#define ARGO_FAN_3 kArgoFan3
|
||||
#define ARGO_FAN_2 kArgoFan2
|
||||
#define ARGO_FAN_1 kArgoFan1
|
||||
#define ARGO_FLAP_AUTO kArgoFlapAuto
|
||||
#define ARGO_FLAP_1 kArgoFlap1
|
||||
#define ARGO_FLAP_2 kArgoFlap2
|
||||
#define ARGO_FLAP_3 kArgoFlap3
|
||||
#define ARGO_FLAP_4 kArgoFlap4
|
||||
#define ARGO_FLAP_5 kArgoFlap5
|
||||
#define ARGO_FLAP_6 kArgoFlap6
|
||||
#define ARGO_FLAP_FULL kArgoFlapFull
|
||||
|
||||
|
||||
/// @brief Timer type to set (for @c argoIrMessageType_t::TIMER_COMMAND)
|
||||
/// @note Raw values match WREM-3 protocol
|
||||
enum class argoTimerType_t : uint8_t {
|
||||
NO_TIMER = 0b000,
|
||||
DELAY_TIMER = 0b001,
|
||||
SCHEDULE_TIMER_1 = 0b010,
|
||||
SCHEDULE_TIMER_2 = 0b011,
|
||||
SCHEDULE_TIMER_3 = 0b100
|
||||
};
|
||||
|
||||
/// @brief Day type to set (for @c argoIrMessageType_t::TIMER_COMMAND)
|
||||
/// @note Raw values match WREM-3 protocol
|
||||
enum class argoWeekday : uint8_t {
|
||||
SUNDAY = 0b000,
|
||||
MONDAY = 0b001,
|
||||
TUESDAY = 0b010,
|
||||
WEDNESDAY = 0b011,
|
||||
THURSDAY = 0b100,
|
||||
FRIDAY = 0b101,
|
||||
SATURDAY = 0b110
|
||||
};
|
||||
|
||||
|
||||
|
||||
/// @brief Base class for handling *common* support for Argo remote protocols
|
||||
/// (functionality is shared across WREM-2 and WREM-3 IR protocols)
|
||||
/// @note This class uses static polymorphism and full template specializations
|
||||
/// when required, to avoid a performance penalty of doing v-table lookup.
|
||||
/// 2 instantiations are forced in impl. file: for @c ArgoProtocol and
|
||||
/// @c ArgoProtocolWREM3
|
||||
/// @note This class is abstract (though does not declare a pure-virtual fn.
|
||||
/// for abovementioned reasons), and instead declares protected c-tor
|
||||
/// @tparam ARGO_PROTOCOL_T The Raw device protocol/message used
|
||||
template <typename ARGO_PROTOCOL_T>
|
||||
class IRArgoACBase {
|
||||
#ifndef UNIT_TEST // A less cloggy way of expressing FRIEND_TEST(...)
|
||||
|
||||
protected:
|
||||
#else
|
||||
|
||||
public:
|
||||
#endif
|
||||
explicit IRArgoACBase(const uint16_t pin, const bool inverted = false,
|
||||
const bool use_modulation = true);
|
||||
|
||||
public:
|
||||
#if SEND_ARGO
|
||||
void send(const uint16_t repeat = kArgoDefaultRepeat);
|
||||
/// 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_ARGO
|
||||
|
||||
void begin(void);
|
||||
void on(void);
|
||||
void off(void);
|
||||
|
||||
void setPower(const bool on);
|
||||
bool getPower(void) const;
|
||||
|
||||
void setTemp(const uint8_t degrees);
|
||||
uint8_t getTemp(void) const;
|
||||
|
||||
void setSensorTemp(const uint8_t degrees);
|
||||
uint8_t getSensorTemp(void) const;
|
||||
|
||||
void setFan(const argoFan_t fan);
|
||||
void setFanEx(const argoFan_t fan) { setFan(fan); }
|
||||
argoFan_t getFanEx(void) const; ///< `-Ex` for backw. compat w/ @c IRArgoAC
|
||||
|
||||
void setFlap(const argoFlap_t flap);
|
||||
void setFlapEx(const argoFlap_t flap) { setFlap(flap); }
|
||||
argoFlap_t getFlapEx(void) const; ///< `-Ex` for backw. compat w/ @c IRArgoAC
|
||||
|
||||
void setMode(const argoMode_t mode);
|
||||
void setModeEx(const argoMode_t mode) { setMode(mode); }
|
||||
argoMode_t getModeEx(void) const; ///< `-Ex` for backw. compat w/ @c IRArgoAC
|
||||
|
||||
void setMax(const bool on);
|
||||
bool getMax(void) const;
|
||||
|
||||
void setNight(const bool on);
|
||||
bool getNight(void) const;
|
||||
|
||||
void setiFeel(const bool on);
|
||||
bool getiFeel(void) const;
|
||||
|
||||
void setMessageType(const argoIrMessageType_t msgType);
|
||||
argoIrMessageType_t getMessageType(void) const;
|
||||
static argoIrMessageType_t getMessageType(const uint8_t state[],
|
||||
const uint16_t length);
|
||||
|
||||
uint8_t* getRaw(void);
|
||||
uint16_t getRawByteLength() const;
|
||||
static uint16_t getStateLengthForIrMsgType(argoIrMessageType_t type);
|
||||
void setRaw(const uint8_t state[], const uint16_t length);
|
||||
|
||||
static bool validChecksum(const uint8_t state[], const uint16_t length);
|
||||
|
||||
static argoMode_t convertMode(const stdAc::opmode_t mode);
|
||||
static argoFan_t convertFan(const stdAc::fanspeed_t speed);
|
||||
static argoFlap_t convertSwingV(const stdAc::swingv_t position);
|
||||
static argoIrMessageType_t convertCommand(const stdAc::ac_command_t command);
|
||||
|
||||
protected:
|
||||
void _stateReset(ARGO_PROTOCOL_T *state, argoIrMessageType_t messageType
|
||||
= argoIrMessageType_t::AC_CONTROL);
|
||||
void stateReset(argoIrMessageType_t messageType
|
||||
= argoIrMessageType_t::AC_CONTROL);
|
||||
void _checksum(ARGO_PROTOCOL_T *state);
|
||||
void checksum(void);
|
||||
static uint16_t getRawByteLength(const ARGO_PROTOCOL_T& raw,
|
||||
argoIrMessageType_t messageTypeHint = argoIrMessageType_t::AC_CONTROL);
|
||||
static uint8_t calcChecksum(const uint8_t state[], const uint16_t length);
|
||||
static uint8_t getChecksum(const uint8_t state[], const uint16_t length);
|
||||
|
||||
static stdAc::opmode_t toCommonMode(const argoMode_t mode);
|
||||
static stdAc::fanspeed_t toCommonFanSpeed(const argoFan_t speed);
|
||||
static stdAc::swingv_t toCommonSwingV(const uint8_t position);
|
||||
static stdAc::ac_command_t toCommonCommand(const argoIrMessageType_t command);
|
||||
|
||||
// Attributes
|
||||
ARGO_PROTOCOL_T _; ///< The raw protocol data
|
||||
uint16_t _length = kArgoStateLength;
|
||||
argoIrMessageType_t _messageType = argoIrMessageType_t::AC_CONTROL;
|
||||
|
||||
#ifndef UNIT_TEST
|
||||
|
||||
protected:
|
||||
IRsend _irsend; ///< instance of the IR send class
|
||||
#else
|
||||
|
||||
public:
|
||||
/// @cond IGNORE
|
||||
IRsendTest _irsend; ///< instance of the testing IR send class
|
||||
/// @endcond
|
||||
#endif
|
||||
};
|
||||
|
||||
/// @brief Supports Argo A/C SAC-WREM2 IR remote protocol
|
||||
class IRArgoAC : public IRArgoACBase<ArgoProtocol> {
|
||||
public:
|
||||
explicit IRArgoAC(const uint16_t pin, const bool inverted = false,
|
||||
const bool use_modulation = true);
|
||||
|
||||
#if SEND_ARGO
|
||||
void sendSensorTemp(const uint8_t degrees,
|
||||
const uint16_t repeat = kArgoDefaultRepeat);
|
||||
#endif // SEND_ARGO
|
||||
|
||||
String toString(void) const;
|
||||
stdAc::state_t toCommon(void) const;
|
||||
|
||||
using IRArgoACBase<ArgoProtocol>::setMode;
|
||||
void setMode(const uint8_t mode); /// @deprecated, for backwards-compat.
|
||||
uint8_t getMode(void) const; /// @deprecated, for backwards-compat.
|
||||
|
||||
using IRArgoACBase<ArgoProtocol>::setFan;
|
||||
void setFan(const uint8_t fan); /// @deprecated, for backwards-compat.
|
||||
uint8_t getFan(void) const; /// @deprecated, for backwards-compat.
|
||||
|
||||
using IRArgoACBase<ArgoProtocol>::setFlap;
|
||||
void setFlap(const uint8_t flap); /// @deprecated, for backwards-compat.
|
||||
uint8_t getFlap(void) const; /// @deprecated, for backwards-compat.
|
||||
};
|
||||
|
||||
/// @brief Supports Argo A/C SAC-WREM3 IR remote protocol
|
||||
class IRArgoAC_WREM3 : public IRArgoACBase<ArgoProtocolWREM3> {
|
||||
public:
|
||||
explicit IRArgoAC_WREM3(const uint16_t pin, const bool inverted = false,
|
||||
const bool use_modulation = true);
|
||||
|
||||
#if SEND_ARGO
|
||||
void sendSensorTemp(const uint8_t degrees,
|
||||
const uint16_t repeat = kArgoDefaultRepeat);
|
||||
#endif // SEND_ARGO
|
||||
|
||||
argo_ac_remote_model_t getModel(void) const;
|
||||
|
||||
|
||||
argoFan_t getFan(void) const;
|
||||
argoFlap_t getFlap(void) const;
|
||||
argoMode_t getMode(void) const;
|
||||
|
||||
void setEco(const bool on);
|
||||
bool getEco(void) const;
|
||||
|
||||
void setFilter(const bool on);
|
||||
bool getFilter(void) const;
|
||||
|
||||
void setLight(const bool on);
|
||||
bool getLight(void) const;
|
||||
|
||||
void setChannel(const uint8_t channel);
|
||||
uint8_t getChannel(void) const;
|
||||
|
||||
void setConfigEntry(const uint8_t paramId, const uint8_t value);
|
||||
std::pair<uint8_t, uint8_t> getConfigEntry(void) const;
|
||||
|
||||
void setCurrentTimeMinutes(uint16_t currentTimeMinutes);
|
||||
uint16_t getCurrentTimeMinutes(void) const;
|
||||
|
||||
void setCurrentDayOfWeek(argoWeekday dayOfWeek);
|
||||
argoWeekday getCurrentDayOfWeek(void) const;
|
||||
|
||||
void setTimerType(const argoTimerType_t timerType);
|
||||
argoTimerType_t getTimerType(void) const;
|
||||
|
||||
void setDelayTimerMinutes(const uint16_t delayMinutes);
|
||||
uint16_t getDelayTimerMinutes(void) const;
|
||||
|
||||
void setScheduleTimerStartMinutes(const uint16_t startTimeMinutes);
|
||||
uint16_t getScheduleTimerStartMinutes(void) const;
|
||||
// uint16_t getTimerXStartMinutes(void) const
|
||||
|
||||
void setScheduleTimerStopMinutes(const uint16_t stopTimeMinutes);
|
||||
uint16_t getScheduleTimerStopMinutes(void) const;
|
||||
// uint16_t getTimerXStopMinutes(void) const;
|
||||
|
||||
|
||||
void setScheduleTimerActiveDays(const std::set<argoWeekday>& days);
|
||||
std::set<argoWeekday> getScheduleTimerActiveDays(void) const;
|
||||
uint8_t getTimerActiveDaysBitmap(void) const;
|
||||
|
||||
using IRArgoACBase<ArgoProtocolWREM3>::getMessageType;
|
||||
static argoIrMessageType_t getMessageType(const ArgoProtocolWREM3& raw);
|
||||
|
||||
String toString(void) const;
|
||||
stdAc::state_t toCommon(void) const;
|
||||
|
||||
static bool hasValidPreamble(const uint8_t state[], const uint16_t length);
|
||||
|
||||
public:
|
||||
#if DECODE_ARGO
|
||||
static bool isValidWrem3Message(const uint8_t state[], const uint16_t nbits,
|
||||
bool verifyChecksum = true);
|
||||
#endif
|
||||
};
|
||||
|
||||
#endif // IR_ARGO_H_
|
||||
123
yoRadio/src/IRremoteESP8266/ir_Arris.cpp
Normal file
123
yoRadio/src/IRremoteESP8266/ir_Arris.cpp
Normal file
@@ -0,0 +1,123 @@
|
||||
// Copyright 2021 David Conran
|
||||
#include "IRrecv.h"
|
||||
#include "IRsend.h"
|
||||
#include "IRutils.h"
|
||||
|
||||
/// @file
|
||||
/// @brief Arris "Manchester code" based protocol.
|
||||
/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1595
|
||||
|
||||
// Supports:
|
||||
// Brand: Arris, Model: VIP1113M Set-top box
|
||||
// Brand: Arris, Model: 120A V1.0 A18 remote
|
||||
|
||||
const uint8_t kArrisOverhead = 2;
|
||||
const uint16_t kArrisHalfClockPeriod = 320; // uSeconds
|
||||
const uint16_t kArrisHdrMark = 8 * kArrisHalfClockPeriod; // uSeconds
|
||||
const uint16_t kArrisHdrSpace = 6 * kArrisHalfClockPeriod; // uSeconds
|
||||
/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1595#issuecomment-913755841
|
||||
// aka. 77184 uSeconds.
|
||||
const uint32_t kArrisGapSpace = 102144 - ((8 + 6 + kArrisBits * 2) *
|
||||
kArrisHalfClockPeriod); // uSeconds
|
||||
const uint32_t kArrisReleaseToggle = 0x800008;
|
||||
const uint8_t kArrisChecksumSize = 4;
|
||||
const uint8_t kArrisCommandSize = 19;
|
||||
const uint8_t kArrisReleaseBit = kArrisChecksumSize + kArrisCommandSize;
|
||||
|
||||
using irutils::sumNibbles;
|
||||
|
||||
#if SEND_ARRIS
|
||||
/// Send an Arris Manchester Code formatted message.
|
||||
/// Status: STABLE / Confirmed working.
|
||||
/// @param[in] data The message to be sent.
|
||||
/// @param[in] nbits The number of bits of the message to be sent.
|
||||
/// @param[in] repeat The number of times the command is to be repeated.
|
||||
/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1595
|
||||
void IRsend::sendArris(const uint64_t data, const uint16_t nbits,
|
||||
const uint16_t repeat) {
|
||||
enableIROut(38);
|
||||
for (uint16_t r = 0; r <= repeat; r++) {
|
||||
// Header (part 1)
|
||||
mark(kArrisHdrMark);
|
||||
space(kArrisHdrSpace);
|
||||
// Header (part 2) + Data + Footer
|
||||
sendManchester(kArrisHalfClockPeriod * 2, 0, kArrisHalfClockPeriod,
|
||||
0, kArrisGapSpace, data, nbits);
|
||||
}
|
||||
}
|
||||
|
||||
/// Flip the toggle button release bits of an Arris message.
|
||||
/// Used to indicate a change of remote button's state. e.g. Press vs. Release.
|
||||
/// @param[in] data The existing Arris message.
|
||||
/// @return A data message suitable for use in sendArris() with the release bits
|
||||
/// flipped.
|
||||
uint32_t IRsend::toggleArrisRelease(const uint32_t data) {
|
||||
return data ^ kArrisReleaseToggle;
|
||||
}
|
||||
|
||||
/// Construct a raw 32-bit Arris message code from the supplied command &
|
||||
/// release setting.
|
||||
/// @param[in] command The command code.
|
||||
/// @param[in] release The button/command action: press (false), release (true)
|
||||
/// @return A raw 32-bit Arris message code suitable for sendArris() etc.
|
||||
/// @note Sequence of bits = header + release + command + checksum.
|
||||
uint32_t IRsend::encodeArris(const uint32_t command, const bool release) {
|
||||
uint32_t result = 0x10000000;
|
||||
irutils::setBits(&result, kArrisChecksumSize, kArrisCommandSize, command);
|
||||
irutils::setBit(&result, kArrisReleaseBit, release);
|
||||
return result + sumNibbles(result);
|
||||
}
|
||||
#endif // SEND_ARRIS
|
||||
|
||||
#if DECODE_ARRIS
|
||||
/// Decode the supplied Arris "Manchester code" message.
|
||||
/// Status: STABLE / Confirmed working.
|
||||
/// @param[in,out] results Ptr to the data to decode & where to store the decode
|
||||
/// result.
|
||||
/// @param[in] offset The starting index to use when attempting to decode the
|
||||
/// raw data. Typically/Defaults to kStartOffset.
|
||||
/// @param[in] nbits The number of data bits to expect.
|
||||
/// @param[in] strict Flag indicating if we should perform strict matching.
|
||||
/// @return A boolean. True if it can decode it, false if it can't.
|
||||
/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1595
|
||||
bool IRrecv::decodeArris(decode_results *results, uint16_t offset,
|
||||
const uint16_t nbits, const bool strict) {
|
||||
if (results->rawlen < nbits + kArrisOverhead - offset)
|
||||
return false; // Too short a message to match.
|
||||
|
||||
// Compliance
|
||||
if (strict && nbits != kArrisBits)
|
||||
return false; // Doesn't match our protocol defn.
|
||||
|
||||
// Header (part 1)
|
||||
if (!matchMark(results->rawbuf[offset++], kArrisHdrMark)) return false;
|
||||
if (!matchSpace(results->rawbuf[offset++], kArrisHdrSpace)) return false;
|
||||
|
||||
// Header (part 2) + Data
|
||||
uint64_t data = 0;
|
||||
if (!matchManchester(results->rawbuf + offset, &data,
|
||||
results->rawlen - offset, nbits,
|
||||
kArrisHalfClockPeriod * 2, 0,
|
||||
kArrisHalfClockPeriod, 0, 0,
|
||||
false, kUseDefTol, kMarkExcess, true, false))
|
||||
return false;
|
||||
|
||||
// Compliance
|
||||
if (strict)
|
||||
// Validate the checksum.
|
||||
if (GETBITS32(data, 0, kArrisChecksumSize) !=
|
||||
sumNibbles(data >> kArrisChecksumSize))
|
||||
return false;
|
||||
|
||||
// Success
|
||||
results->decode_type = decode_type_t::ARRIS;
|
||||
results->bits = nbits;
|
||||
results->value = data;
|
||||
// Set the address as the Release Bit for something useful.
|
||||
results->address = static_cast<bool>(GETBIT32(data, kArrisReleaseBit));
|
||||
// The last 4 bits are likely a checksum value, so skip those. Everything else
|
||||
// after the release bit. e.g. Bits 10-28
|
||||
results->command = GETBITS32(data, kArrisChecksumSize, kArrisCommandSize);
|
||||
return true;
|
||||
}
|
||||
#endif // DECODE_ARRIS
|
||||
331
yoRadio/src/IRremoteESP8266/ir_Bosch.cpp
Normal file
331
yoRadio/src/IRremoteESP8266/ir_Bosch.cpp
Normal file
@@ -0,0 +1,331 @@
|
||||
// Copyright 2022 David Conran
|
||||
// Copyright 2022 Nico Thien
|
||||
/// @file
|
||||
/// @brief Support for the Bosch A/C / heatpump protocol
|
||||
/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1787
|
||||
|
||||
#include "ir_Bosch.h"
|
||||
|
||||
#if SEND_BOSCH144
|
||||
/// Send a Bosch 144-bit / 18-byte message (96-bit message are also possible)
|
||||
/// Status: BETA / Probably Working.
|
||||
/// @param[in] data The message to be sent.
|
||||
/// @param[in] nbytes The number of bytes of message to be sent.
|
||||
/// @param[in] repeat The number of times the command is to be repeated.
|
||||
void IRsend::sendBosch144(const unsigned char data[], const uint16_t nbytes,
|
||||
const uint16_t repeat) {
|
||||
// nbytes is required to be a multiple of kBosch144BytesPerSection.
|
||||
if (nbytes % kBosch144BytesPerSection != 0)return;
|
||||
// Set IR carrier frequency
|
||||
enableIROut(kBoschFreq);
|
||||
|
||||
for (uint16_t r = 0; r <= repeat; r++) {
|
||||
for (uint16_t offset=0; offset < nbytes; offset += kBosch144BytesPerSection)
|
||||
// Section Header + Data + Footer
|
||||
sendGeneric(kBoschHdrMark, kBoschHdrSpace,
|
||||
kBoschBitMark, kBoschOneSpace,
|
||||
kBoschBitMark, kBoschZeroSpace,
|
||||
kBoschBitMark, kBoschFooterSpace,
|
||||
data + offset, kBosch144BytesPerSection,
|
||||
kBoschFreq, true, 0, kDutyDefault);
|
||||
space(kDefaultMessageGap); // Complete guess
|
||||
}
|
||||
}
|
||||
|
||||
#endif // SEND_BOSCH144
|
||||
|
||||
/// Class constructor.
|
||||
/// @param[in] pin GPIO to be used when sending.
|
||||
/// @param[in] inverted Is the output signal to be inverted?
|
||||
/// @param[in] use_modulation Is frequency modulation to be used?
|
||||
IRBosch144AC::IRBosch144AC(const uint16_t pin, const bool inverted,
|
||||
const bool use_modulation)
|
||||
: _irsend(pin, inverted, use_modulation) { stateReset(); }
|
||||
|
||||
/// Reset the internal state to a fixed known good state.
|
||||
void IRBosch144AC::stateReset(void) {
|
||||
setRaw(kBosch144DefaultState, kBosch144StateLength);
|
||||
setPower(true);
|
||||
}
|
||||
|
||||
/// Set up hardware to be able to send a message.
|
||||
void IRBosch144AC::begin(void) { _irsend.begin(); }
|
||||
|
||||
#if SEND_BOSCH144
|
||||
/// Send the current internal state as an IR message.
|
||||
/// @param[in] repeat Nr. of times the message will be repeated.
|
||||
void IRBosch144AC::send(const uint16_t repeat) {
|
||||
if (!powerFlag) { // "Off" is a 96bit message
|
||||
_irsend.sendBosch144(kBosch144Off, sizeof(kBosch144Off), repeat);
|
||||
} else {
|
||||
_irsend.sendBosch144(getRaw(), kBosch144StateLength, repeat);
|
||||
} // other 96bit messages are not yet supported
|
||||
}
|
||||
#endif // SEND_BOSCH144
|
||||
|
||||
/// Get a copy of the internal state as a valid code for this protocol.
|
||||
/// @return A valid code for this protocol based on the current internal state.
|
||||
unsigned char* IRBosch144AC::getRaw(void) {
|
||||
setInvertBytes();
|
||||
setCheckSumS3();
|
||||
return _.raw;
|
||||
}
|
||||
|
||||
/// Set the internal state from a valid code for this protocol.
|
||||
/// @param[in] new_code A valid code for this protocol.
|
||||
/// @param[in] length Size of the array being passed in in bytes.
|
||||
void IRBosch144AC::setRaw(const uint8_t new_code[], const uint16_t length) {
|
||||
const uint16_t len = min(length, kBosch144StateLength);
|
||||
const uint16_t lenOff = sizeof(kBosch144Off);
|
||||
// Is it an off message?
|
||||
if (memcmp(kBosch144Off, new_code, min(lenOff, len)) == 0)
|
||||
setPower(false); // It is.
|
||||
else
|
||||
setPower(true);
|
||||
memcpy(_.raw, new_code, len);
|
||||
}
|
||||
|
||||
void IRBosch144AC::setPower(const bool on) {
|
||||
powerFlag = on;
|
||||
}
|
||||
|
||||
bool IRBosch144AC::getPower(void) const {
|
||||
return powerFlag;
|
||||
}
|
||||
|
||||
void IRBosch144AC::setTempRaw(const uint8_t code) {
|
||||
_.TempS1 = _.TempS2 = code >> 1; // save 4 bits in S1 and S2
|
||||
_.TempS3 = code & 1; // save 1 bit in Section3
|
||||
}
|
||||
|
||||
/// Set the temperature.
|
||||
/// @param[in] degrees The temperature in degrees celsius.
|
||||
void IRBosch144AC::setTemp(const uint8_t degrees) {
|
||||
uint8_t temp = max(kBosch144TempMin, degrees);
|
||||
temp = min(kBosch144TempMax, temp);
|
||||
setTempRaw(kBosch144TempMap[temp - kBosch144TempMin]);
|
||||
}
|
||||
|
||||
uint8_t IRBosch144AC::getTemp(void) const {
|
||||
uint8_t temp = (_.TempS1 << 1) + _.TempS3;
|
||||
uint8_t retemp = 25;
|
||||
for (uint8_t i = 0; i < kBosch144TempRange; i++) {
|
||||
if (temp == kBosch144TempMap[i]) {
|
||||
retemp = kBosch144TempMin + i;
|
||||
}
|
||||
}
|
||||
return retemp;
|
||||
}
|
||||
|
||||
/// Set the speed of the fan.
|
||||
/// @param[in] speed The desired setting.
|
||||
void IRBosch144AC::setFan(const uint16_t speed) {
|
||||
_.FanS1 = _.FanS2 = speed >> 6; // save 3 bits in S1 and S2
|
||||
_.FanS3 = speed & 0b111111; // save 6 bits in Section3
|
||||
}
|
||||
|
||||
uint16_t IRBosch144AC::getFan(void) const {
|
||||
return (_.FanS1 << 6) + _.FanS3;
|
||||
}
|
||||
|
||||
/// Set the desired operation mode.
|
||||
/// @param[in] mode The desired operation mode.
|
||||
void IRBosch144AC::setMode(const uint8_t mode) {
|
||||
_.ModeS1 = _.ModeS2 = mode >> 1; // save 2 bits in S1 and S2
|
||||
_.ModeS3 = mode & 0b1; // save 1 bit in Section3
|
||||
if (mode == kBosch144Auto || mode == kBosch144Dry) {
|
||||
_.FanS1 = _.FanS2 = 0b000; // save 3 bits in S1 and S2
|
||||
_.FanS3 = kBosch144FanAuto0; // save 6 bits in Section3
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t IRBosch144AC::getMode(void) const {
|
||||
return (_.ModeS1 << 1) + _.ModeS3;
|
||||
}
|
||||
|
||||
/// Set the Quiet mode of the A/C.
|
||||
/// @param[in] on true, the setting is on. false, the setting is off.
|
||||
void IRBosch144AC::setQuiet(const bool on) {
|
||||
_.Quiet = on; // save 1 bit in Section3
|
||||
setFan(kBosch144FanAuto); // set Fan -> Auto
|
||||
}
|
||||
|
||||
/// Get the Quiet mode of the A/C.
|
||||
/// @return true, the setting is on. false, the setting is off.
|
||||
bool IRBosch144AC::getQuiet(void) const { return _.Quiet; }
|
||||
|
||||
/// Convert a stdAc::opmode_t enum into its native mode.
|
||||
/// @param[in] mode The enum to be converted.
|
||||
/// @return The native equivalent of the enum.
|
||||
uint8_t IRBosch144AC::convertMode(const stdAc::opmode_t mode) {
|
||||
switch (mode) {
|
||||
case stdAc::opmode_t::kCool:
|
||||
return kBosch144Cool;
|
||||
case stdAc::opmode_t::kHeat:
|
||||
return kBosch144Heat;
|
||||
case stdAc::opmode_t::kDry:
|
||||
return kBosch144Dry;
|
||||
case stdAc::opmode_t::kFan:
|
||||
return kBosch144Fan;
|
||||
default:
|
||||
return kBosch144Auto;
|
||||
}
|
||||
}
|
||||
|
||||
/// Convert a stdAc::fanspeed_t enum into it's native speed.
|
||||
/// @param[in] speed The enum to be converted.
|
||||
/// @return The native equivalent of the enum.
|
||||
uint16_t IRBosch144AC::convertFan(const stdAc::fanspeed_t speed) {
|
||||
switch (speed) {
|
||||
case stdAc::fanspeed_t::kMin:
|
||||
return kBosch144Fan20;
|
||||
case stdAc::fanspeed_t::kLow:
|
||||
return kBosch144Fan40;
|
||||
case stdAc::fanspeed_t::kMedium:
|
||||
return kBosch144Fan60;
|
||||
case stdAc::fanspeed_t::kHigh:
|
||||
return kBosch144Fan80;
|
||||
case stdAc::fanspeed_t::kMax:
|
||||
return kBosch144Fan100;
|
||||
default:
|
||||
return kBosch144FanAuto;
|
||||
}
|
||||
}
|
||||
|
||||
/// Convert a native mode into its stdAc equivalent.
|
||||
/// @param[in] mode The native setting to be converted.
|
||||
/// @return The stdAc equivalent of the native setting.
|
||||
stdAc::opmode_t IRBosch144AC::toCommonMode(const uint8_t mode) {
|
||||
switch (mode) {
|
||||
case kBosch144Cool: return stdAc::opmode_t::kCool;
|
||||
case kBosch144Heat: return stdAc::opmode_t::kHeat;
|
||||
case kBosch144Dry: return stdAc::opmode_t::kDry;
|
||||
case kBosch144Fan: return stdAc::opmode_t::kFan;
|
||||
default: return stdAc::opmode_t::kAuto;
|
||||
}
|
||||
}
|
||||
|
||||
/// Convert a native fan speed into its stdAc equivalent.
|
||||
/// @param[in] speed The native setting to be converted.
|
||||
/// @return The stdAc equivalent of the native setting.
|
||||
stdAc::fanspeed_t IRBosch144AC::toCommonFanSpeed(const uint16_t speed) {
|
||||
switch (speed) {
|
||||
case kBosch144Fan100: return stdAc::fanspeed_t::kMax;
|
||||
case kBosch144Fan80: return stdAc::fanspeed_t::kHigh;
|
||||
case kBosch144Fan60: return stdAc::fanspeed_t::kMedium;
|
||||
case kBosch144Fan40: return stdAc::fanspeed_t::kLow;
|
||||
case kBosch144Fan20: return stdAc::fanspeed_t::kMin;
|
||||
default: return stdAc::fanspeed_t::kAuto;
|
||||
}
|
||||
}
|
||||
|
||||
/// Convert the current internal state into its stdAc::state_t equivalent.
|
||||
/// @return The stdAc equivalent of the native settings.
|
||||
stdAc::state_t IRBosch144AC::toCommon(void) const {
|
||||
stdAc::state_t result{};
|
||||
result.protocol = decode_type_t::BOSCH144;
|
||||
result.power = getPower();
|
||||
result.mode = toCommonMode(getMode());
|
||||
result.celsius = true;
|
||||
result.degrees = getTemp();
|
||||
result.fanspeed = toCommonFanSpeed(getFan());
|
||||
result.quiet = getQuiet();
|
||||
// Not supported.
|
||||
result.model = -1;
|
||||
result.turbo = false;
|
||||
result.swingv = stdAc::swingv_t::kOff;
|
||||
result.swingh = stdAc::swingh_t::kOff;
|
||||
result.light = false;
|
||||
result.filter = false;
|
||||
result.econo = false;
|
||||
result.clean = false;
|
||||
result.beep = false;
|
||||
result.clock = -1;
|
||||
result.sleep = -1;
|
||||
return result;
|
||||
}
|
||||
|
||||
/// Convert the current internal state into a human readable string.
|
||||
/// @return A human readable string.
|
||||
String IRBosch144AC::toString(void) const {
|
||||
uint8_t mode = getMode();
|
||||
uint8_t fan = static_cast<int>(toCommonFanSpeed(getFan()));
|
||||
String result = "";
|
||||
result.reserve(70); // Reserve some heap for the string to reduce fragging.
|
||||
result += addBoolToString(getPower(), kPowerStr, false);
|
||||
result += addModeToString(mode, kBosch144Auto, kBosch144Cool,
|
||||
kBosch144Heat, kBosch144Dry, kBosch144Fan);
|
||||
result += addFanToString(fan, static_cast<int>(stdAc::fanspeed_t::kMax),
|
||||
static_cast<int>(stdAc::fanspeed_t::kMin),
|
||||
static_cast<int>(stdAc::fanspeed_t::kAuto),
|
||||
static_cast<int>(stdAc::fanspeed_t::kAuto),
|
||||
static_cast<int>(stdAc::fanspeed_t::kMedium));
|
||||
result += addTempToString(getTemp());
|
||||
result += addBoolToString(_.Quiet, kQuietStr);
|
||||
return result;
|
||||
}
|
||||
|
||||
void IRBosch144AC::setInvertBytes() {
|
||||
for (uint8_t i = 0; i <= 10; i += 2) {
|
||||
_.raw[i + 1] = ~_.raw[i];
|
||||
}
|
||||
}
|
||||
|
||||
void IRBosch144AC::setCheckSumS3() {
|
||||
_.ChecksumS3 = sumBytes(&(_.raw[12]), 5);
|
||||
}
|
||||
|
||||
#if DECODE_BOSCH144
|
||||
/// Decode the supplied Bosch 144-bit / 18-byte A/C message.
|
||||
/// Status: STABLE / Confirmed Working.
|
||||
/// @param[in,out] results Ptr to the data to decode & where to store the decode
|
||||
/// result.
|
||||
/// @param[in] offset The starting index to use when attempting to decode the
|
||||
/// raw data. Typically/Defaults to kStartOffset.
|
||||
/// @param[in] nbits The number of data bits to expect.
|
||||
/// @param[in] strict Flag indicating if we should perform strict matching.
|
||||
/// @return A boolean. True if it can decode it, false if it can't.
|
||||
bool IRrecv::decodeBosch144(decode_results *results, uint16_t offset,
|
||||
const uint16_t nbits, const bool strict) {
|
||||
if (results->rawlen < 2 * nbits +
|
||||
kBosch144NrOfSections * (kHeader + kFooter) -
|
||||
1 + offset)
|
||||
return false; // Can't possibly be a valid BOSCH144 message.
|
||||
if (strict && nbits != kBosch144Bits)
|
||||
return false; // Not strictly a BOSCH144 message.
|
||||
if (nbits % 8 != 0) // nbits has to be a multiple of nr. of bits in a byte.
|
||||
return false;
|
||||
if (nbits % kBosch144NrOfSections != 0)
|
||||
return false; // nbits has to be a multiple of kBosch144NrOfSections.
|
||||
const uint16_t kSectionBits = nbits / kBosch144NrOfSections;
|
||||
const uint16_t kSectionBytes = kSectionBits / 8;
|
||||
const uint16_t kNBytes = kSectionBytes * kBosch144NrOfSections;
|
||||
// Capture each section individually
|
||||
for (uint16_t pos = 0, section = 0;
|
||||
pos < kNBytes;
|
||||
pos += kSectionBytes, section++) {
|
||||
uint16_t used = 0;
|
||||
// Section Header + Section Data + Section Footer
|
||||
used = matchGeneric(results->rawbuf + offset, results->state + pos,
|
||||
results->rawlen - offset, kSectionBits,
|
||||
kBoschHdrMark, kBoschHdrSpace,
|
||||
kBoschBitMark, kBoschOneSpace,
|
||||
kBoschBitMark, kBoschZeroSpace,
|
||||
kBoschBitMark, kBoschFooterSpace,
|
||||
section >= kBosch144NrOfSections - 1,
|
||||
_tolerance, kMarkExcess, true);
|
||||
if (!used) return false; // Didn't match.
|
||||
offset += used;
|
||||
}
|
||||
|
||||
// Compliance
|
||||
|
||||
// Success
|
||||
results->decode_type = decode_type_t::BOSCH144;
|
||||
results->bits = nbits;
|
||||
// No need to record the state as we stored it as we decoded it.
|
||||
// As we use result->state, we don't record value, address, or command as it
|
||||
// is a union data type.
|
||||
return true;
|
||||
}
|
||||
#endif // DECODE_BOSCH144
|
||||
193
yoRadio/src/IRremoteESP8266/ir_Bosch.h
Normal file
193
yoRadio/src/IRremoteESP8266/ir_Bosch.h
Normal file
@@ -0,0 +1,193 @@
|
||||
// Copyright 2022 Nico Thien
|
||||
/// @file
|
||||
/// @brief Support for Bosch A/C protocol
|
||||
/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1787
|
||||
|
||||
// Supports:
|
||||
// Brand: Bosch, Model: CL3000i-Set 26 E A/C
|
||||
// Brand: Bosch, Model: RG10A(G2S)BGEF remote
|
||||
|
||||
|
||||
#ifndef IR_BOSCH_H_
|
||||
#define IR_BOSCH_H_
|
||||
|
||||
#define __STDC_LIMIT_MACROS
|
||||
#include <stdint.h>
|
||||
#include <algorithm>
|
||||
#include <cstring>
|
||||
#include "IRremoteESP8266.h"
|
||||
#include "IRsend.h"
|
||||
#include "IRrecv.h"
|
||||
#include "IRtext.h"
|
||||
#include "IRutils.h"
|
||||
#ifndef UNIT_TEST
|
||||
#include <Arduino.h>
|
||||
#endif
|
||||
#ifdef UNIT_TEST
|
||||
#include "IRsend_test.h"
|
||||
#endif
|
||||
|
||||
// Constants
|
||||
const uint16_t kBoschHdrMark = 4366;
|
||||
const uint16_t kBoschBitMark = 502;
|
||||
const uint16_t kBoschHdrSpace = 4415;
|
||||
const uint16_t kBoschOneSpace = 1645;
|
||||
const uint16_t kBoschZeroSpace = 571;
|
||||
const uint16_t kBoschFooterSpace = 5235;
|
||||
const uint16_t kBoschFreq = 38000; // Hz. (Guessing the most common frequency.)
|
||||
const uint16_t kBosch144NrOfSections = 3;
|
||||
const uint16_t kBosch144BytesPerSection = 6;
|
||||
|
||||
using irutils::addBoolToString;
|
||||
using irutils::addModeToString;
|
||||
using irutils::addFanToString;
|
||||
using irutils::addTempToString;
|
||||
using std::min;
|
||||
using std::max;
|
||||
using std::memcpy;
|
||||
using std::memcmp;
|
||||
|
||||
// Modes Bit[0] to Section 3 Bit[1-2] to Section 1
|
||||
// ModeS3 ModeS1
|
||||
const uint8_t kBosch144Cool = 0b000;
|
||||
const uint8_t kBosch144Dry = 0b011;
|
||||
const uint8_t kBosch144Auto = 0b101;
|
||||
const uint8_t kBosch144Heat = 0b110;
|
||||
const uint8_t kBosch144Fan = 0b010;
|
||||
|
||||
// Fan Control Bit[0-5] to Section 3 Bit[6-8] to Section 1
|
||||
// FanS3 FanS1
|
||||
const uint16_t kBosch144Fan20 = 0b111001010;
|
||||
const uint16_t kBosch144Fan40 = 0b100010100;
|
||||
const uint16_t kBosch144Fan60 = 0b010011110;
|
||||
const uint16_t kBosch144Fan80 = 0b001101000;
|
||||
const uint16_t kBosch144Fan100 = 0b001110010;
|
||||
const uint16_t kBosch144FanAuto = 0b101110011;
|
||||
const uint16_t kBosch144FanAuto0 = 0b000110011;
|
||||
|
||||
// Temperature
|
||||
const uint8_t kBosch144TempMin = 16; // Celsius
|
||||
const uint8_t kBosch144TempMax = 30; // Celsius
|
||||
const uint8_t kBosch144TempRange = kBosch144TempMax - kBosch144TempMin + 1;
|
||||
const uint8_t kBosch144TempMap[kBosch144TempRange] = {
|
||||
0b00001, // 16C // Bit[0] to Section 3 Bit[1-4] to Section 1
|
||||
0b00000, // 17C // TempS3 TempS1
|
||||
0b00010, // 18c
|
||||
0b00110, // 19C
|
||||
0b00100, // 20C
|
||||
0b01100, // 21C
|
||||
0b01110, // 22C
|
||||
0b01010, // 23C
|
||||
0b01000, // 24C
|
||||
0b11000, // 25C
|
||||
0b11010, // 26C
|
||||
0b10010, // 27C
|
||||
0b10000, // 28C
|
||||
0b10100, // 29C
|
||||
0b10110 // 30C
|
||||
};
|
||||
|
||||
// "OFF" is a 96bit-message the same as Coolix protocol
|
||||
const uint8_t kBosch144Off[] = {0xB2, 0x4D, 0x7B, 0x84, 0xE0, 0x1F,
|
||||
0xB2, 0x4D, 0x7B, 0x84, 0xE0, 0x1F};
|
||||
|
||||
// On, 25C, Mode: Auto
|
||||
const uint8_t kBosch144DefaultState[kBosch144StateLength] = {
|
||||
0xB2, 0x4D, 0x1F, 0xE0, 0xC8, 0x37,
|
||||
0xB2, 0x4D, 0x1F, 0xE0, 0xC8, 0x37,
|
||||
0xD5, 0x65, 0x00, 0x00, 0x00, 0x3A};
|
||||
|
||||
union Bosch144Protocol {
|
||||
uint8_t raw[kBosch144StateLength]; ///< The state in IR code form.
|
||||
struct {
|
||||
uint8_t :8; // Fixed value 0b10110010 / 0xB2. ############
|
||||
uint8_t InnvertS1_1:8; // Invert byte 0b01001101 / 0x4D #
|
||||
uint8_t :5; // not used (without timer use) #
|
||||
uint8_t FanS1 :3; // Fan speed bits in Section 1 #
|
||||
uint8_t InnvertS1_2:8; // Invert byte # Section 1 =
|
||||
uint8_t :2; // not used (without timer use) # Sektion 2
|
||||
uint8_t ModeS1 :2; // Operation mode bits S1 #
|
||||
uint8_t TempS1 :4; // Desired temperature (Celsius) S2 #
|
||||
uint8_t InnvertS1_3:8; // Invert byte (without timer use) ############
|
||||
|
||||
uint8_t :8; // Fixed value 0b10110010 / 0xB2. ############
|
||||
uint8_t InnvertS2_1:8; // Invert byte 0b01001101 / 0x4D #
|
||||
uint8_t :5; // not used (without timer use) #
|
||||
uint8_t FanS2 :3; // Fan speed bits in Section 2 #
|
||||
uint8_t InnvertS2_2:8; // Invert byte # Section 2 =
|
||||
uint8_t :2; // not used (without timer use) # Sektion 1
|
||||
uint8_t ModeS2 :2; // Operation mode bits S2 #
|
||||
uint8_t TempS2 :4; // Desired temperature (Celsius) S2 #
|
||||
uint8_t InnvertS2_3:8; // Invert byte (without timer use) ###########
|
||||
|
||||
uint8_t :8; // Fixed value 0b11010101 / 0xD5 ###########
|
||||
uint8_t ModeS3 :1; // ModeBit in Section 3 #
|
||||
uint8_t FanS3 :6; // Fan speed bits in Section 3 #
|
||||
uint8_t :1; // Unknown #
|
||||
uint8_t :7; // Unknown #
|
||||
uint8_t Quiet :1; // Silent-Mode # Section 3
|
||||
uint8_t :4; // Unknown #
|
||||
uint8_t TempS3 :1; // Desired temp. Bit in Section3 #
|
||||
uint8_t :3; // Unknown #
|
||||
uint8_t :8; // Unknown #
|
||||
uint8_t ChecksumS3 :8; // Checksum from byte 13-17 ###########
|
||||
};
|
||||
};
|
||||
|
||||
// Classes
|
||||
|
||||
/// Class for handling detailed Bosch144 A/C messages.
|
||||
class IRBosch144AC {
|
||||
public:
|
||||
explicit IRBosch144AC(const uint16_t pin, const bool inverted = false,
|
||||
const bool use_modulation = true);
|
||||
void stateReset(void);
|
||||
#if SEND_BOSCH144
|
||||
void send(const uint16_t repeat = 0);
|
||||
/// 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_BOSCH144
|
||||
void begin();
|
||||
void setPower(const bool state);
|
||||
bool getPower(void) const;
|
||||
void setTemp(const uint8_t temp);
|
||||
uint8_t getTemp(void) const;
|
||||
void setFan(const uint16_t speed);
|
||||
uint16_t getFan(void) const;
|
||||
void setMode(const uint8_t mode);
|
||||
uint8_t getMode(void) const;
|
||||
void setQuiet(const bool on);
|
||||
bool getQuiet(void) const;
|
||||
uint8_t* getRaw(void);
|
||||
void setRaw(const uint8_t new_code[],
|
||||
const uint16_t length = kBosch144StateLength);
|
||||
static uint8_t convertMode(const stdAc::opmode_t mode);
|
||||
static uint16_t convertFan(const stdAc::fanspeed_t speed);
|
||||
static stdAc::opmode_t toCommonMode(const uint8_t mode);
|
||||
static stdAc::fanspeed_t toCommonFanSpeed(const uint16_t speed);
|
||||
stdAc::state_t toCommon(void) const;
|
||||
String toString(void) const;
|
||||
#ifndef UNIT_TEST
|
||||
|
||||
private:
|
||||
IRsend _irsend; ///< Instance of the IR send class
|
||||
#else
|
||||
/// @cond IGNORE
|
||||
IRsendTest _irsend; ///< Instance of the testing IR send class
|
||||
/// @endcond
|
||||
#endif
|
||||
Bosch144Protocol _; ///< The state of the IR remote in IR code form.
|
||||
|
||||
// Internal State settings
|
||||
bool powerFlag;
|
||||
|
||||
void setInvertBytes();
|
||||
void setCheckSumS3();
|
||||
void setTempRaw(const uint8_t code);
|
||||
uint8_t getTempRaw(void) const;
|
||||
};
|
||||
|
||||
#endif // IR_BOSCH_H_
|
||||
69
yoRadio/src/IRremoteESP8266/ir_Bose.cpp
Normal file
69
yoRadio/src/IRremoteESP8266/ir_Bose.cpp
Normal file
@@ -0,0 +1,69 @@
|
||||
// Copyright 2021 parsnip42
|
||||
// Copyright 2021 David Conran
|
||||
|
||||
/// @file
|
||||
/// @brief Support for Bose protocols.
|
||||
/// @note Currently only tested against Bose TV Speaker.
|
||||
/// @see https://github.com/crankyoldgit/IRremoteESP8266/pull/1579
|
||||
|
||||
// Supports:
|
||||
// Brand: Bose, Model: Bose TV Speaker
|
||||
|
||||
#include "IRrecv.h"
|
||||
#include "IRsend.h"
|
||||
|
||||
const uint16_t kBoseHdrMark = 1100;
|
||||
const uint16_t kBoseHdrSpace = 1350;
|
||||
const uint16_t kBoseBitMark = 555;
|
||||
const uint16_t kBoseOneSpace = 1435;
|
||||
const uint16_t kBoseZeroSpace = 500;
|
||||
const uint32_t kBoseGap = kDefaultMessageGap;
|
||||
const uint16_t kBoseFreq = 38;
|
||||
|
||||
#if SEND_BOSE
|
||||
/// Send a Bose formatted message.
|
||||
/// Status: STABLE / Known working.
|
||||
/// @param[in] data The message to be sent.
|
||||
/// @param[in] nbits The number of bits of message to be sent.
|
||||
/// @param[in] repeat The number of times the command is to be repeated.
|
||||
void IRsend::sendBose(const uint64_t data, const uint16_t nbits,
|
||||
const uint16_t repeat) {
|
||||
sendGeneric(kBoseHdrMark, kBoseHdrSpace,
|
||||
kBoseBitMark, kBoseOneSpace,
|
||||
kBoseBitMark, kBoseZeroSpace,
|
||||
kBoseBitMark, kBoseGap,
|
||||
data, nbits, kBoseFreq, false,
|
||||
repeat, kDutyDefault);
|
||||
}
|
||||
#endif // SEND_BOSE
|
||||
|
||||
#if DECODE_BOSE
|
||||
/// Decode the supplied Bose formatted message.
|
||||
/// Status: STABLE / Known working.
|
||||
/// @param[in,out] results Ptr to the data to decode & where to store the result
|
||||
/// @param[in] offset The starting index to use when attempting to decode the
|
||||
/// raw data. Typically/Defaults to kStartOffset.
|
||||
/// @param[in] nbits The number of data bits to expect.
|
||||
/// @param[in] strict Flag indicating if we should perform strict matching.
|
||||
bool IRrecv::decodeBose(decode_results *results, uint16_t offset,
|
||||
const uint16_t nbits, const bool strict) {
|
||||
if (strict && nbits != kBoseBits) return false;
|
||||
|
||||
if (!matchGeneric(results->rawbuf + offset, &(results->value),
|
||||
results->rawlen - offset, nbits,
|
||||
kBoseHdrMark, kBoseHdrSpace,
|
||||
kBoseBitMark, kBoseOneSpace,
|
||||
kBoseBitMark, kBoseZeroSpace,
|
||||
kBoseBitMark, kBoseGap, true,
|
||||
kUseDefTol, 0, false)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
//
|
||||
results->decode_type = decode_type_t::BOSE;
|
||||
results->bits = nbits;
|
||||
results->address = 0;
|
||||
results->command = 0;
|
||||
return true;
|
||||
}
|
||||
#endif // DECODE_BOSE
|
||||
747
yoRadio/src/IRremoteESP8266/ir_Carrier.cpp
Normal file
747
yoRadio/src/IRremoteESP8266/ir_Carrier.cpp
Normal file
@@ -0,0 +1,747 @@
|
||||
// Copyright 2018-2022 David Conran
|
||||
/// @file
|
||||
/// @brief Carrier protocols.
|
||||
/// @see CarrierAc https://github.com/crankyoldgit/IRremoteESP8266/issues/385
|
||||
/// @see CarrierAc64 https://github.com/crankyoldgit/IRremoteESP8266/issues/1127
|
||||
/// @see CarrierAc128 https://github.com/crankyoldgit/IRremoteESP8266/issues/1797
|
||||
|
||||
#include "ir_Carrier.h"
|
||||
#include <algorithm>
|
||||
#include "IRac.h"
|
||||
#include "IRrecv.h"
|
||||
#include "IRsend.h"
|
||||
#include "IRtext.h"
|
||||
#include "IRutils.h"
|
||||
|
||||
using irutils::addBoolToString;
|
||||
using irutils::addIntToString;
|
||||
using irutils::addLabeledString;
|
||||
using irutils::addModeToString;
|
||||
using irutils::addTempToString;
|
||||
using irutils::addFanToString;
|
||||
using irutils::minsToString;
|
||||
using irutils::sumNibbles;
|
||||
|
||||
// Constants
|
||||
const uint16_t kCarrierAcHdrMark = 8532;
|
||||
const uint16_t kCarrierAcHdrSpace = 4228;
|
||||
const uint16_t kCarrierAcBitMark = 628;
|
||||
const uint16_t kCarrierAcOneSpace = 1320;
|
||||
const uint16_t kCarrierAcZeroSpace = 532;
|
||||
const uint16_t kCarrierAcGap = 20000;
|
||||
const uint16_t kCarrierAcFreq = 38; // kHz. (An educated guess)
|
||||
|
||||
const uint16_t kCarrierAc40HdrMark = 8402;
|
||||
const uint16_t kCarrierAc40HdrSpace = 4166;
|
||||
const uint16_t kCarrierAc40BitMark = 547;
|
||||
const uint16_t kCarrierAc40OneSpace = 1540;
|
||||
const uint16_t kCarrierAc40ZeroSpace = 497;
|
||||
const uint32_t kCarrierAc40Gap = 150000; ///<
|
||||
///< @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1190#issuecomment-643380155
|
||||
|
||||
const uint16_t kCarrierAc64HdrMark = 8940;
|
||||
const uint16_t kCarrierAc64HdrSpace = 4556;
|
||||
const uint16_t kCarrierAc64BitMark = 503;
|
||||
const uint16_t kCarrierAc64OneSpace = 1736;
|
||||
const uint16_t kCarrierAc64ZeroSpace = 615;
|
||||
const uint32_t kCarrierAc64Gap = kDefaultMessageGap; // A guess.
|
||||
|
||||
//< @see: https://github.com/crankyoldgit/IRremoteESP8266/issues/1943#issue-1519570772
|
||||
const uint16_t kCarrierAc84HdrMark = 5850;
|
||||
const uint16_t kCarrierAc84Zero = 1175;
|
||||
const uint16_t kCarrierAc84One = 430;
|
||||
const uint16_t kCarrierAc84HdrSpace = kCarrierAc84Zero;
|
||||
const uint32_t kCarrierAc84Gap = kDefaultMessageGap; // A guess.
|
||||
const uint8_t kCarrierAc84ExtraBits = 4;
|
||||
const uint8_t kCarrierAc84ExtraTolerance = 5;
|
||||
|
||||
const uint16_t kCarrierAc128HdrMark = 4600;
|
||||
const uint16_t kCarrierAc128HdrSpace = 2600;
|
||||
const uint16_t kCarrierAc128Hdr2Mark = 9300;
|
||||
const uint16_t kCarrierAc128Hdr2Space = 5000;
|
||||
const uint16_t kCarrierAc128BitMark = 340;
|
||||
const uint16_t kCarrierAc128OneSpace = 1000;
|
||||
const uint16_t kCarrierAc128ZeroSpace = 400;
|
||||
const uint16_t kCarrierAc128SectionGap = 20600;
|
||||
const uint16_t kCarrierAc128InterSpace = 6700;
|
||||
const uint16_t kCarrierAc128SectionBits = kCarrierAc128Bits / 2;
|
||||
|
||||
#if SEND_CARRIER_AC
|
||||
/// Send a Carrier HVAC formatted message.
|
||||
/// Status: STABLE / Works on real devices.
|
||||
/// @param[in] data The message to be sent.
|
||||
/// @param[in] nbits The number of bits of message to be sent.
|
||||
/// @param[in] repeat The number of times the command is to be repeated.
|
||||
void IRsend::sendCarrierAC(uint64_t data, uint16_t nbits, uint16_t repeat) {
|
||||
for (uint16_t r = 0; r <= repeat; r++) {
|
||||
uint64_t temp_data = data;
|
||||
// Carrier sends the data block three times. normal + inverted + normal.
|
||||
for (uint16_t i = 0; i < 3; i++) {
|
||||
sendGeneric(kCarrierAcHdrMark, kCarrierAcHdrSpace, kCarrierAcBitMark,
|
||||
kCarrierAcOneSpace, kCarrierAcBitMark, kCarrierAcZeroSpace,
|
||||
kCarrierAcBitMark, kCarrierAcGap, temp_data, nbits, 38, true,
|
||||
0, kDutyDefault);
|
||||
temp_data = invertBits(temp_data, nbits);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#if DECODE_CARRIER_AC
|
||||
/// Decode the supplied Carrier HVAC message.
|
||||
/// @note Carrier HVAC messages contain only 32 bits, but it is sent three(3)
|
||||
/// times. i.e. normal + inverted + normal
|
||||
/// Status: BETA / Probably works.
|
||||
/// @param[in,out] results Ptr to the data to decode & where to store the decode
|
||||
/// result.
|
||||
/// @param[in] offset The starting index to use when attempting to decode the
|
||||
/// raw data. Typically/Defaults to kStartOffset.
|
||||
/// @param[in] nbits The number of data bits to expect.
|
||||
/// @param[in] strict Flag indicating if we should perform strict matching.
|
||||
/// @return A boolean. True if it can decode it, false if it can't.
|
||||
bool IRrecv::decodeCarrierAC(decode_results *results, uint16_t offset,
|
||||
const uint16_t nbits, const bool strict) {
|
||||
if (results->rawlen < ((2 * nbits + kHeader + kFooter) * 3) - 1 + offset)
|
||||
return false; // Can't possibly be a valid Carrier message.
|
||||
if (strict && nbits != kCarrierAcBits)
|
||||
return false; // We expect Carrier to be 32 bits of message.
|
||||
|
||||
uint64_t data = 0;
|
||||
uint64_t prev_data = 0;
|
||||
|
||||
for (uint8_t i = 0; i < 3; i++) {
|
||||
prev_data = data;
|
||||
// Match Header + Data + Footer
|
||||
uint16_t used;
|
||||
used = matchGeneric(results->rawbuf + offset, &data,
|
||||
results->rawlen - offset, nbits,
|
||||
kCarrierAcHdrMark, kCarrierAcHdrSpace,
|
||||
kCarrierAcBitMark, kCarrierAcOneSpace,
|
||||
kCarrierAcBitMark, kCarrierAcZeroSpace,
|
||||
kCarrierAcBitMark, kCarrierAcGap, true);
|
||||
if (!used) return false;
|
||||
offset += used;
|
||||
// Compliance.
|
||||
if (strict) {
|
||||
// Check if the data is an inverted copy of the previous data.
|
||||
if (i > 0 && prev_data != invertBits(data, nbits)) return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Success
|
||||
results->bits = nbits;
|
||||
results->value = data;
|
||||
results->decode_type = CARRIER_AC;
|
||||
results->address = data >> 16;
|
||||
results->command = data & 0xFFFF;
|
||||
return true;
|
||||
}
|
||||
#endif // DECODE_CARRIER_AC
|
||||
|
||||
#if SEND_CARRIER_AC40
|
||||
/// Send a Carrier 40bit HVAC formatted message.
|
||||
/// Status: STABLE / Tested against a real device.
|
||||
/// @param[in] data The message to be sent.
|
||||
/// @param[in] nbits The bit size of the message being sent.
|
||||
/// @param[in] repeat The number of times the message is to be repeated.
|
||||
void IRsend::sendCarrierAC40(const uint64_t data, const uint16_t nbits,
|
||||
const uint16_t repeat) {
|
||||
sendGeneric(kCarrierAc40HdrMark, kCarrierAc40HdrSpace, kCarrierAc40BitMark,
|
||||
kCarrierAc40OneSpace, kCarrierAc40BitMark, kCarrierAc40ZeroSpace,
|
||||
kCarrierAc40BitMark, kCarrierAc40Gap,
|
||||
data, nbits, kCarrierAcFreq, true, repeat, kDutyDefault);
|
||||
}
|
||||
#endif // SEND_CARRIER_AC40
|
||||
|
||||
#if DECODE_CARRIER_AC40
|
||||
/// Decode the supplied Carrier 40-bit HVAC message.
|
||||
/// Carrier HVAC messages contain only 40 bits, but it is sent three(3) times.
|
||||
/// Status: STABLE / Tested against a real device.
|
||||
/// @param[in,out] results Ptr to the data to decode & where to store the decode
|
||||
/// result.
|
||||
/// @param[in] offset The starting index to use when attempting to decode the
|
||||
/// raw data. Typically/Defaults to kStartOffset.
|
||||
/// @param[in] nbits The number of data bits to expect.
|
||||
/// @param[in] strict Flag indicating if we should perform strict matching.
|
||||
/// @return A boolean. True if it can decode it, false if it can't.
|
||||
bool IRrecv::decodeCarrierAC40(decode_results *results, uint16_t offset,
|
||||
const uint16_t nbits, const bool strict) {
|
||||
if (results->rawlen < 2 * nbits + kHeader + kFooter - 1 + offset)
|
||||
return false; // Can't possibly be a valid Carrier message.
|
||||
if (strict && nbits != kCarrierAc40Bits)
|
||||
return false; // We expect Carrier to be 40 bits of message.
|
||||
|
||||
if (!matchGeneric(results->rawbuf + offset, &(results->value),
|
||||
results->rawlen - offset, nbits,
|
||||
kCarrierAc40HdrMark, kCarrierAc40HdrSpace,
|
||||
kCarrierAc40BitMark, kCarrierAc40OneSpace,
|
||||
kCarrierAc40BitMark, kCarrierAc40ZeroSpace,
|
||||
kCarrierAc40BitMark, kCarrierAc40Gap, true)) return false;
|
||||
|
||||
// Success
|
||||
results->bits = nbits;
|
||||
results->decode_type = CARRIER_AC40;
|
||||
results->address = 0;
|
||||
results->command = 0;
|
||||
return true;
|
||||
}
|
||||
#endif // DECODE_CARRIER_AC40
|
||||
|
||||
#if SEND_CARRIER_AC64
|
||||
/// Send a Carrier 64bit HVAC formatted message.
|
||||
/// Status: STABLE / Known to be working.
|
||||
/// @param[in] data The message to be sent.
|
||||
/// @param[in] nbits The bit size of the message being sent.
|
||||
/// @param[in] repeat The number of times the message is to be repeated.
|
||||
void IRsend::sendCarrierAC64(const uint64_t data, const uint16_t nbits,
|
||||
const uint16_t repeat) {
|
||||
sendGeneric(kCarrierAc64HdrMark, kCarrierAc64HdrSpace, kCarrierAc64BitMark,
|
||||
kCarrierAc64OneSpace, kCarrierAc64BitMark, kCarrierAc64ZeroSpace,
|
||||
kCarrierAc64BitMark, kCarrierAc64Gap,
|
||||
data, nbits, kCarrierAcFreq, false, repeat, kDutyDefault);
|
||||
}
|
||||
#endif // SEND_CARRIER_AC64
|
||||
|
||||
#if DECODE_CARRIER_AC64
|
||||
/// Decode the supplied Carrier 64-bit HVAC message.
|
||||
/// Status: STABLE / Known to be working.
|
||||
/// @param[in,out] results Ptr to the data to decode & where to store the decode
|
||||
/// result.
|
||||
/// @param[in] offset The starting index to use when attempting to decode the
|
||||
/// raw data. Typically/Defaults to kStartOffset.
|
||||
/// @param[in] nbits The number of data bits to expect.
|
||||
/// @param[in] strict Flag indicating if we should perform strict matching.
|
||||
/// @return A boolean. True if it can decode it, false if it can't.
|
||||
bool IRrecv::decodeCarrierAC64(decode_results *results, uint16_t offset,
|
||||
const uint16_t nbits, const bool strict) {
|
||||
if (results->rawlen < 2 * nbits + kHeader + kFooter - 1 + offset)
|
||||
return false; // Can't possibly be a valid Carrier message.
|
||||
if (strict && nbits != kCarrierAc64Bits)
|
||||
return false; // We expect Carrier to be 64 bits of message.
|
||||
|
||||
if (!matchGeneric(results->rawbuf + offset, &(results->value),
|
||||
results->rawlen - offset, nbits,
|
||||
kCarrierAc64HdrMark, kCarrierAc64HdrSpace,
|
||||
kCarrierAc64BitMark, kCarrierAc64OneSpace,
|
||||
kCarrierAc64BitMark, kCarrierAc64ZeroSpace,
|
||||
kCarrierAc64BitMark, kCarrierAc64Gap, true,
|
||||
kUseDefTol, kMarkExcess, false)) return false;
|
||||
|
||||
// Compliance
|
||||
if (strict && !IRCarrierAc64::validChecksum(results->value)) return false;
|
||||
|
||||
// Success
|
||||
results->bits = nbits;
|
||||
results->decode_type = CARRIER_AC64;
|
||||
results->address = 0;
|
||||
results->command = 0;
|
||||
return true;
|
||||
}
|
||||
#endif // DECODE_CARRIER_AC64
|
||||
|
||||
/// Class constructor.
|
||||
/// @param[in] pin GPIO to be used when sending.
|
||||
/// @param[in] inverted Is the output signal to be inverted?
|
||||
/// @param[in] use_modulation Is frequency modulation to be used?
|
||||
IRCarrierAc64::IRCarrierAc64(const uint16_t pin, const bool inverted,
|
||||
const bool use_modulation)
|
||||
: _irsend(pin, inverted, use_modulation) { stateReset(); }
|
||||
|
||||
/// Reset the internal state to a fixed known good state.
|
||||
/// @note The state is powered off.
|
||||
void IRCarrierAc64::stateReset(void) { _.raw = 0x109000002C2A5584; }
|
||||
|
||||
/// Calculate the checksum for a given state.
|
||||
/// @param[in] state The value to calc the checksum of.
|
||||
/// @return The 4-bit checksum stored in a uint_8.
|
||||
uint8_t IRCarrierAc64::calcChecksum(const uint64_t state) {
|
||||
uint64_t data = GETBITS64(state,
|
||||
kCarrierAc64ChecksumOffset + kCarrierAc64ChecksumSize, kCarrierAc64Bits -
|
||||
(kCarrierAc64ChecksumOffset + kCarrierAc64ChecksumSize));
|
||||
uint8_t result = 0;
|
||||
for (; data; data >>= 4) // Add each nibble together.
|
||||
result += GETBITS64(data, 0, 4);
|
||||
return result & 0xF;
|
||||
}
|
||||
|
||||
/// Verify the checksum is valid for a given state.
|
||||
/// @param[in] state The array to verify the checksum of.
|
||||
/// @return true, if the state has a valid checksum. Otherwise, false.
|
||||
bool IRCarrierAc64::validChecksum(const uint64_t state) {
|
||||
// Validate the checksum of the given state.
|
||||
return (GETBITS64(state, kCarrierAc64ChecksumOffset,
|
||||
kCarrierAc64ChecksumSize) == calcChecksum(state));
|
||||
}
|
||||
|
||||
/// Calculate and set the checksum values for the internal state.
|
||||
void IRCarrierAc64::checksum(void) {
|
||||
_.Sum = calcChecksum(_.raw);
|
||||
}
|
||||
|
||||
/// Set up hardware to be able to send a message.
|
||||
void IRCarrierAc64::begin(void) { _irsend.begin(); }
|
||||
|
||||
#if SEND_CARRIER_AC64
|
||||
/// Send the current internal state as an IR message.
|
||||
/// @param[in] repeat Nr. of times the message will be repeated.
|
||||
void IRCarrierAc64::send(const uint16_t repeat) {
|
||||
_irsend.sendCarrierAC64(getRaw(), kCarrierAc64Bits, repeat);
|
||||
}
|
||||
#endif // SEND_CARRIER_AC64
|
||||
|
||||
/// Get a copy of the internal state as a valid code for this protocol.
|
||||
/// @return A valid code for this protocol based on the current internal state.
|
||||
uint64_t IRCarrierAc64::getRaw(void) {
|
||||
checksum(); // Ensure correct settings before sending.
|
||||
return _.raw;
|
||||
}
|
||||
|
||||
/// Set the internal state from a valid code for this protocol.
|
||||
/// @param[in] state A valid code for this protocol.
|
||||
void IRCarrierAc64::setRaw(const uint64_t state) { _.raw = state; }
|
||||
|
||||
/// Set the temp in deg C.
|
||||
/// @param[in] temp The desired temperature in Celsius.
|
||||
void IRCarrierAc64::setTemp(const uint8_t temp) {
|
||||
uint8_t degrees = std::max(temp, kCarrierAc64MinTemp);
|
||||
degrees = std::min(degrees, kCarrierAc64MaxTemp);
|
||||
_.Temp = degrees - kCarrierAc64MinTemp;
|
||||
}
|
||||
|
||||
/// Get the current temperature from the internal state.
|
||||
/// @return The current temperature in Celsius.
|
||||
uint8_t IRCarrierAc64::getTemp(void) const {
|
||||
return _.Temp + kCarrierAc64MinTemp;
|
||||
}
|
||||
|
||||
/// Change the power setting.
|
||||
/// @param[in] on true, the setting is on. false, the setting is off.
|
||||
void IRCarrierAc64::setPower(const bool on) {
|
||||
_.Power = on;
|
||||
}
|
||||
|
||||
/// Get the value of the current power setting.
|
||||
/// @return true, the setting is on. false, the setting is off.
|
||||
bool IRCarrierAc64::getPower(void) const {
|
||||
return _.Power;
|
||||
}
|
||||
|
||||
/// Change the power setting to On.
|
||||
void IRCarrierAc64::on(void) { setPower(true); }
|
||||
|
||||
/// Change the power setting to Off.
|
||||
void IRCarrierAc64::off(void) { setPower(false); }
|
||||
|
||||
/// Get the operating mode setting of the A/C.
|
||||
/// @return The current operating mode setting.
|
||||
uint8_t IRCarrierAc64::getMode(void) const {
|
||||
return _.Mode;
|
||||
}
|
||||
|
||||
/// Set the operating mode of the A/C.
|
||||
/// @param[in] mode The desired operating mode.
|
||||
void IRCarrierAc64::setMode(const uint8_t mode) {
|
||||
switch (mode) {
|
||||
case kCarrierAc64Heat:
|
||||
case kCarrierAc64Cool:
|
||||
case kCarrierAc64Fan:
|
||||
_.Mode = mode;
|
||||
return;
|
||||
default:
|
||||
_.Mode = kCarrierAc64Cool;
|
||||
}
|
||||
}
|
||||
|
||||
/// Convert a standard A/C mode into its native mode.
|
||||
/// @param[in] mode A stdAc::opmode_t to be converted to it's native equivalent.
|
||||
/// @return The corresponding native mode.
|
||||
uint8_t IRCarrierAc64::convertMode(const stdAc::opmode_t mode) {
|
||||
switch (mode) {
|
||||
case stdAc::opmode_t::kHeat: return kCarrierAc64Heat;
|
||||
case stdAc::opmode_t::kFan: return kCarrierAc64Fan;
|
||||
default: return kCarrierAc64Cool;
|
||||
}
|
||||
}
|
||||
|
||||
/// Convert a native mode to it's common stdAc::opmode_t equivalent.
|
||||
/// @param[in] mode A native operation mode to be converted.
|
||||
/// @return The corresponding common stdAc::opmode_t mode.
|
||||
stdAc::opmode_t IRCarrierAc64::toCommonMode(const uint8_t mode) {
|
||||
switch (mode) {
|
||||
case kCarrierAc64Heat: return stdAc::opmode_t::kHeat;
|
||||
case kCarrierAc64Fan: return stdAc::opmode_t::kFan;
|
||||
default: return stdAc::opmode_t::kCool;
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the current fan speed setting.
|
||||
/// @return The current fan speed.
|
||||
uint8_t IRCarrierAc64::getFan(void) const {
|
||||
return _.Fan;
|
||||
}
|
||||
|
||||
/// Set the speed of the fan.
|
||||
/// @param[in] speed The desired setting.
|
||||
void IRCarrierAc64::setFan(const uint8_t speed) {
|
||||
if (speed > kCarrierAc64FanHigh)
|
||||
_.Fan = kCarrierAc64FanAuto;
|
||||
else
|
||||
_.Fan = speed;
|
||||
}
|
||||
|
||||
/// Convert a stdAc::fanspeed_t enum into it's native speed.
|
||||
/// @param[in] speed The enum to be converted.
|
||||
/// @return The native equivalent of the enum.
|
||||
uint8_t IRCarrierAc64::convertFan(const stdAc::fanspeed_t speed) {
|
||||
switch (speed) {
|
||||
case stdAc::fanspeed_t::kMin:
|
||||
case stdAc::fanspeed_t::kLow: return kCarrierAc64FanLow;
|
||||
case stdAc::fanspeed_t::kMedium: return kCarrierAc64FanMedium;
|
||||
case stdAc::fanspeed_t::kHigh:
|
||||
case stdAc::fanspeed_t::kMax: return kCarrierAc64FanHigh;
|
||||
default: return kCarrierAc64FanAuto;
|
||||
}
|
||||
}
|
||||
|
||||
/// Convert a native fan speed into its stdAc equivalent.
|
||||
/// @param[in] speed The native setting to be converted.
|
||||
/// @return The stdAc equivalent of the native setting.
|
||||
stdAc::fanspeed_t IRCarrierAc64::toCommonFanSpeed(const uint8_t speed) {
|
||||
switch (speed) {
|
||||
case kCarrierAc64FanHigh: return stdAc::fanspeed_t::kHigh;
|
||||
case kCarrierAc64FanMedium: return stdAc::fanspeed_t::kMedium;
|
||||
case kCarrierAc64FanLow: return stdAc::fanspeed_t::kLow;
|
||||
default: return stdAc::fanspeed_t::kAuto;
|
||||
}
|
||||
}
|
||||
|
||||
/// Set the Vertical Swing mode of the A/C.
|
||||
/// @param[in] on true, the setting is on. false, the setting is off.
|
||||
void IRCarrierAc64::setSwingV(const bool on) {
|
||||
_.SwingV = on;
|
||||
}
|
||||
|
||||
/// Get the Vertical Swing mode of the A/C.
|
||||
/// @return true, the setting is on. false, the setting is off.
|
||||
bool IRCarrierAc64::getSwingV(void) const {
|
||||
return _.SwingV;
|
||||
}
|
||||
|
||||
/// Set the Sleep mode of the A/C.
|
||||
/// @param[in] on true, the setting is on. false, the setting is off.
|
||||
void IRCarrierAc64::setSleep(const bool on) {
|
||||
if (on) {
|
||||
// Sleep sets a default value in the Off timer, and disables both timers.
|
||||
setOffTimer(2 * 60);
|
||||
// Clear the enable bits for each timer.
|
||||
_cancelOnTimer();
|
||||
_cancelOffTimer();
|
||||
}
|
||||
_.Sleep = on;
|
||||
}
|
||||
|
||||
/// Get the Sleep mode of the A/C.
|
||||
/// @return true, the setting is on. false, the setting is off.
|
||||
bool IRCarrierAc64::getSleep(void) const {
|
||||
return _.Sleep;
|
||||
}
|
||||
|
||||
/// Clear the On Timer enable bit.
|
||||
void IRCarrierAc64::_cancelOnTimer(void) {
|
||||
_.OnTimerEnable = false;
|
||||
}
|
||||
|
||||
/// Get the current On Timer time.
|
||||
/// @return The number of minutes it is set for. 0 means it's off.
|
||||
/// @note The A/C protocol only supports one hour increments.
|
||||
uint16_t IRCarrierAc64::getOnTimer(void) const {
|
||||
if (_.OnTimerEnable)
|
||||
return _.OnTimer * 60;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// Set the On Timer time.
|
||||
/// @param[in] nr_of_mins Number of minutes to set the timer to.
|
||||
/// (< 60 is disable).
|
||||
/// @note The A/C protocol only supports one hour increments.
|
||||
void IRCarrierAc64::setOnTimer(const uint16_t nr_of_mins) {
|
||||
uint8_t hours = std::min((uint8_t)(nr_of_mins / 60), kCarrierAc64TimerMax);
|
||||
_.OnTimerEnable = static_cast<bool>(hours); // Enable
|
||||
_.OnTimer = std::max(kCarrierAc64TimerMin, hours); // Hours
|
||||
if (hours) { // If enabled, disable the Off Timer & Sleep mode.
|
||||
_cancelOffTimer();
|
||||
setSleep(false);
|
||||
}
|
||||
}
|
||||
|
||||
/// Clear the Off Timer enable bit.
|
||||
void IRCarrierAc64::_cancelOffTimer(void) {
|
||||
_.OffTimerEnable = false;
|
||||
}
|
||||
|
||||
/// Get the current Off Timer time.
|
||||
/// @return The number of minutes it is set for. 0 means it's off.
|
||||
/// @note The A/C protocol only supports one hour increments.
|
||||
uint16_t IRCarrierAc64::getOffTimer(void) const {
|
||||
if (_.OffTimerEnable)
|
||||
return _.OffTimer * 60;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// Set the Off Timer time.
|
||||
/// @param[in] nr_of_mins Number of minutes to set the timer to.
|
||||
/// (< 60 is disable).
|
||||
/// @note The A/C protocol only supports one hour increments.
|
||||
void IRCarrierAc64::setOffTimer(const uint16_t nr_of_mins) {
|
||||
uint8_t hours = std::min((uint8_t)(nr_of_mins / 60), kCarrierAc64TimerMax);
|
||||
// The time can be changed in sleep mode, but doesn't set the flag.
|
||||
_.OffTimerEnable = (hours && !_.Sleep);
|
||||
_.OffTimer = std::max(kCarrierAc64TimerMin, hours); // Hours
|
||||
if (hours) { // If enabled, disable the On Timer & Sleep mode.
|
||||
_cancelOnTimer();
|
||||
setSleep(false);
|
||||
}
|
||||
}
|
||||
|
||||
/// Convert the internal state into a human readable string.
|
||||
/// @return The current internal state expressed as a human readable String.
|
||||
String IRCarrierAc64::toString(void) const {
|
||||
String result = "";
|
||||
result.reserve(120); // Reserve some heap for the string to reduce fragging.
|
||||
result += addBoolToString(_.Power, kPowerStr, false);
|
||||
result += addModeToString(_.Mode, 0xFF, kCarrierAc64Cool,
|
||||
kCarrierAc64Heat, 0xFF, kCarrierAc64Fan);
|
||||
result += addTempToString(getTemp());
|
||||
result += addFanToString(_.Fan, kCarrierAc64FanHigh, kCarrierAc64FanLow,
|
||||
kCarrierAc64FanAuto, kCarrierAc64FanAuto,
|
||||
kCarrierAc64FanMedium);
|
||||
result += addBoolToString(_.SwingV, kSwingVStr);
|
||||
result += addBoolToString(_.Sleep, kSleepStr);
|
||||
result += addLabeledString(getOnTimer()
|
||||
? minsToString(getOnTimer()) : kOffStr,
|
||||
kOnTimerStr);
|
||||
result += addLabeledString(getOffTimer()
|
||||
? minsToString(getOffTimer()) : kOffStr,
|
||||
kOffTimerStr);
|
||||
return result;
|
||||
}
|
||||
|
||||
/// Convert the A/C state to it's common stdAc::state_t equivalent.
|
||||
/// @return A stdAc::state_t state.
|
||||
stdAc::state_t IRCarrierAc64::toCommon(void) const {
|
||||
stdAc::state_t result{};
|
||||
result.protocol = decode_type_t::CARRIER_AC64;
|
||||
result.model = -1; // No models used.
|
||||
result.power = _.Power;
|
||||
result.mode = toCommonMode(_.Mode);
|
||||
result.celsius = true;
|
||||
result.degrees = getTemp();
|
||||
result.fanspeed = toCommonFanSpeed(_.Fan);
|
||||
result.swingv = _.SwingV ? stdAc::swingv_t::kAuto : stdAc::swingv_t::kOff;
|
||||
result.sleep = _.Sleep ? 0 : -1;
|
||||
// Not supported.
|
||||
result.swingh = stdAc::swingh_t::kOff;
|
||||
result.turbo = false;
|
||||
result.quiet = false;
|
||||
result.clean = false;
|
||||
result.filter = false;
|
||||
result.beep = false;
|
||||
result.econo = false;
|
||||
result.light = false;
|
||||
result.clock = -1;
|
||||
return result;
|
||||
}
|
||||
|
||||
#if SEND_CARRIER_AC128
|
||||
/// Send a Carrier 128bit HVAC formatted message.
|
||||
/// Status: BETA / Seems to work with tests. Needs testing agaisnt real devices.
|
||||
/// @param[in] data The message to be sent.
|
||||
/// @param[in] nbytes The byte size of the message being sent.
|
||||
/// @param[in] repeat The number of times the message is to be repeated.
|
||||
void IRsend::sendCarrierAC128(const uint8_t data[], const uint16_t nbytes,
|
||||
const uint16_t repeat) {
|
||||
// Min length check.
|
||||
if (nbytes <= kCarrierAc128StateLength / 2) return;
|
||||
|
||||
enableIROut(kCarrierAcFreq);
|
||||
// Handle repeats.
|
||||
for (uint16_t r = 0; r <= repeat; r++) {
|
||||
// First part of the message.
|
||||
// Headers + Data + SectionGap
|
||||
sendGeneric(kCarrierAc128HdrMark, kCarrierAc128HdrSpace,
|
||||
kCarrierAc128BitMark, kCarrierAc128OneSpace,
|
||||
kCarrierAc128BitMark, kCarrierAc128ZeroSpace,
|
||||
kCarrierAc128BitMark, kCarrierAc128SectionGap,
|
||||
data, nbytes / 2, kCarrierAcFreq, false, 0, kDutyDefault);
|
||||
// Inter-message markers
|
||||
mark(kCarrierAc128HdrMark);
|
||||
space(kCarrierAc128InterSpace);
|
||||
// Second part of the message
|
||||
// Headers + Data + SectionGap
|
||||
sendGeneric(kCarrierAc128Hdr2Mark, kCarrierAc128Hdr2Space,
|
||||
kCarrierAc128BitMark, kCarrierAc128OneSpace,
|
||||
kCarrierAc128BitMark, kCarrierAc128ZeroSpace,
|
||||
kCarrierAc128BitMark, kCarrierAc128SectionGap,
|
||||
data + (nbytes / 2), nbytes / 2, kCarrierAcFreq,
|
||||
false, 0, kDutyDefault);
|
||||
// Footer
|
||||
mark(kCarrierAc128HdrMark);
|
||||
space(kDefaultMessageGap);
|
||||
}
|
||||
}
|
||||
#endif // SEND_CARRIER_AC128
|
||||
|
||||
#if DECODE_CARRIER_AC128
|
||||
/// Decode the supplied Carrier 128-bit HVAC message.
|
||||
/// Status: STABLE / Expected to work.
|
||||
/// @param[in,out] results Ptr to the data to decode & where to store the decode
|
||||
/// result.
|
||||
/// @param[in] offset The starting index to use when attempting to decode the
|
||||
/// raw data. Typically/Defaults to kStartOffset.
|
||||
/// @param[in] nbits The number of data bits to expect.
|
||||
/// @param[in] strict Flag indicating if we should perform strict matching.
|
||||
/// @return A boolean. True if it can decode it, false if it can't.
|
||||
bool IRrecv::decodeCarrierAC128(decode_results *results, uint16_t offset,
|
||||
const uint16_t nbits, const bool strict) {
|
||||
if (results->rawlen < 2 * (nbits + 2 * kHeader + kFooter) - 1 + offset)
|
||||
return false; // Can't possibly be a valid Carrier message.
|
||||
if (strict && nbits != kCarrierAc128Bits)
|
||||
return false; // We expect Carrier to be 128 bits of message.
|
||||
|
||||
uint16_t used;
|
||||
uint16_t pos = 0;
|
||||
const uint16_t sectionbits = nbits / 2;
|
||||
// Match the first section.
|
||||
used = matchGeneric(results->rawbuf + offset, results->state,
|
||||
results->rawlen - offset, sectionbits,
|
||||
kCarrierAc128HdrMark, kCarrierAc128HdrSpace,
|
||||
kCarrierAc128BitMark, kCarrierAc128OneSpace,
|
||||
kCarrierAc128BitMark, kCarrierAc128ZeroSpace,
|
||||
kCarrierAc128BitMark, kCarrierAc128SectionGap, true,
|
||||
kUseDefTol, kMarkExcess, false);
|
||||
if (used == 0) return false; // No match.
|
||||
offset += used;
|
||||
pos += sectionbits / 8;
|
||||
// Look for the inter-message markers.
|
||||
if (!matchMark(results->rawbuf[offset++], kCarrierAc128HdrMark))
|
||||
return false;
|
||||
if (!matchSpace(results->rawbuf[offset++], kCarrierAc128InterSpace))
|
||||
return false;
|
||||
// Now look for the second section.
|
||||
used = matchGeneric(results->rawbuf + offset, results->state + pos,
|
||||
results->rawlen - offset, sectionbits,
|
||||
kCarrierAc128Hdr2Mark, kCarrierAc128Hdr2Space,
|
||||
kCarrierAc128BitMark, kCarrierAc128OneSpace,
|
||||
kCarrierAc128BitMark, kCarrierAc128ZeroSpace,
|
||||
kCarrierAc128BitMark, kCarrierAc128SectionGap, true,
|
||||
kUseDefTol, kMarkExcess, false);
|
||||
if (used == 0) return false; // No match.
|
||||
offset += used;
|
||||
// Now check for the Footer.
|
||||
if (!matchMark(results->rawbuf[offset++], kCarrierAc128HdrMark)) return false;
|
||||
if (offset < results->rawlen &&
|
||||
!matchAtLeast(results->rawbuf[offset], kDefaultMessageGap)) return false;
|
||||
|
||||
|
||||
// Compliance
|
||||
// if (strict && !IRCarrierAc128::validChecksum(results->value)) return false;
|
||||
|
||||
// Success
|
||||
results->bits = nbits;
|
||||
results->decode_type = CARRIER_AC128;
|
||||
return true;
|
||||
}
|
||||
#endif // DECODE_CARRIER_AC128
|
||||
|
||||
#if SEND_CARRIER_AC84
|
||||
/// Send a Carroer A/C 84 Bit formatted message.
|
||||
/// Status: BETA / Untested but probably works.
|
||||
/// @param[in] data The message to be sent.
|
||||
/// @param[in] nbytes The byte size of the message being sent.
|
||||
/// @param[in] repeat The number of times the command is to be repeated.
|
||||
void IRsend::sendCarrierAC84(const uint8_t data[], const uint16_t nbytes,
|
||||
const uint16_t repeat) {
|
||||
// Protocol uses a constant bit time encoding.
|
||||
for (uint16_t r = 0; r <= repeat; r++) {
|
||||
if (nbytes) {
|
||||
// The least significant `kCarrierAc84ExtraBits` bits of the first byte
|
||||
sendGeneric(kCarrierAc84HdrMark, kCarrierAc84HdrSpace, // Header
|
||||
kCarrierAc84Zero, kCarrierAc84One, // Data
|
||||
kCarrierAc84One, kCarrierAc84Zero,
|
||||
0, 0, // No footer
|
||||
GETBITS64(data[0], 0, kCarrierAc84ExtraBits),
|
||||
kCarrierAc84ExtraBits,
|
||||
38000, false, 0, 33);
|
||||
// The rest of the data.
|
||||
sendGeneric(0, 0, // No Header
|
||||
kCarrierAc84Zero, kCarrierAc84One, // Data
|
||||
kCarrierAc84One, kCarrierAc84Zero,
|
||||
kCarrierAc84Zero, kDefaultMessageGap, // Footer
|
||||
data + 1, nbytes - 1, 38000, false, 0, 33);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif // SEND_CARRIER_AC84
|
||||
|
||||
#if DECODE_CARRIER_AC84
|
||||
/// Decode the supplied Carroer A/C 84 Bit formatted message.
|
||||
/// Status: STABLE / Confirmed Working.
|
||||
/// @param[in,out] results Ptr to the data to decode & where to store the decode
|
||||
/// result.
|
||||
/// @param[in] offset The starting index to use when attempting to decode the
|
||||
/// raw data. Typically/Defaults to kStartOffset.
|
||||
/// @param[in] nbits The number of data bits to expect.
|
||||
/// @param[in] strict Flag indicating if we should perform strict matching.
|
||||
/// @return A boolean. True if it can decode it, false if it can't.
|
||||
bool IRrecv::decodeCarrierAC84(decode_results *results, uint16_t offset,
|
||||
const uint16_t nbits, const bool strict) {
|
||||
// Check if we have enough data to even possibly match.
|
||||
if (results->rawlen < 2 * nbits + kHeader + kFooter - 1 + offset)
|
||||
return false; // Can't possibly be a valid Carrier message.
|
||||
// Compliance check.
|
||||
if (strict && nbits != kCarrierAc84Bits) return false;
|
||||
|
||||
// This decoder expects to decode an unusual number of bits. Check before we
|
||||
// start.
|
||||
if (nbits % 8 != kCarrierAc84ExtraBits) return false;
|
||||
|
||||
uint64_t data = 0;
|
||||
uint16_t used = 0;
|
||||
|
||||
// Header + Data (kCarrierAc84ExtraBits only)
|
||||
used = matchGenericConstBitTime(results->rawbuf + offset, &data,
|
||||
results->rawlen - offset,
|
||||
kCarrierAc84ExtraBits,
|
||||
// Header (None)
|
||||
kCarrierAc84HdrMark, kCarrierAc84HdrSpace,
|
||||
// Data
|
||||
kCarrierAc84Zero, kCarrierAc84One,
|
||||
// No Footer
|
||||
0, 0,
|
||||
false,
|
||||
_tolerance + kCarrierAc84ExtraTolerance,
|
||||
kMarkExcess, false);
|
||||
if (!used) return false;
|
||||
// Stuff the captured data so far into the first byte of the state.
|
||||
*results->state = data;
|
||||
offset += used;
|
||||
// Capture the rest of the data as normal as we should be on a byte boundary.
|
||||
// Data + Footer
|
||||
if (!matchGeneric(results->rawbuf + offset, results->state + 1,
|
||||
results->rawlen - offset, nbits - kCarrierAc84ExtraBits,
|
||||
0, 0, // No Header expected.
|
||||
kCarrierAc84Zero, kCarrierAc84One, // Data
|
||||
kCarrierAc84One, kCarrierAc84Zero,
|
||||
kCarrierAc84Zero, kDefaultMessageGap, true,
|
||||
_tolerance + kCarrierAc84ExtraTolerance,
|
||||
kMarkExcess, false)) return false;
|
||||
|
||||
// Success
|
||||
results->decode_type = decode_type_t::CARRIER_AC84;
|
||||
results->bits = nbits;
|
||||
results->repeat = false;
|
||||
return true;
|
||||
}
|
||||
#endif // DECODE_CARRIER_AC84
|
||||
146
yoRadio/src/IRremoteESP8266/ir_Carrier.h
Normal file
146
yoRadio/src/IRremoteESP8266/ir_Carrier.h
Normal file
@@ -0,0 +1,146 @@
|
||||
// Copyright 2020-2022 David Conran
|
||||
/// @file
|
||||
/// @brief Carrier A/C
|
||||
/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1127
|
||||
/// @see https://docs.google.com/spreadsheets/d/1EZy78L0cn1KDIX1aKq2biptejFqCjD5HO3tLiRvXf48/edit#gid=0
|
||||
/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1797
|
||||
/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1943
|
||||
|
||||
// Supports:
|
||||
// Brand: Carrier/Surrey, Model: 42QG5A55970 remote
|
||||
// Brand: Carrier/Surrey, Model: 619EGX0090E0 A/C
|
||||
// Brand: Carrier/Surrey, Model: 619EGX0120E0 A/C
|
||||
// Brand: Carrier/Surrey, Model: 619EGX0180E0 A/C
|
||||
// Brand: Carrier/Surrey, Model: 619EGX0220E0 A/C
|
||||
// Brand: Carrier/Surrey, Model: 53NGK009/012 Inverter
|
||||
// Brand: Carrier, Model: 40GKX0E2006 remote (CARRIER_AC128)
|
||||
// Brand: Carrier, Model: 3021203 RR03-S-Remote (CARRIER_AC84)
|
||||
// Brand: Carrier, Model: 342WM100CT A/C (CARRIER_AC84)
|
||||
|
||||
#ifndef IR_CARRIER_H_
|
||||
#define IR_CARRIER_H_
|
||||
|
||||
#define __STDC_LIMIT_MACROS
|
||||
#include <stdint.h>
|
||||
#ifndef UNIT_TEST
|
||||
#include <Arduino.h>
|
||||
#endif
|
||||
#include "IRremoteESP8266.h"
|
||||
#include "IRsend.h"
|
||||
#ifdef UNIT_TEST
|
||||
#include "IRsend_test.h"
|
||||
#endif
|
||||
|
||||
/// Native representation of a Carrier A/C message.
|
||||
union CarrierProtocol {
|
||||
uint64_t raw; ///< The state of the IR remote.
|
||||
struct {
|
||||
// Byte 0
|
||||
uint8_t :8;
|
||||
// Byte 1
|
||||
uint8_t :8;
|
||||
// Byte 2
|
||||
uint8_t Sum:4;
|
||||
uint8_t Mode:2;
|
||||
uint8_t Fan:2;
|
||||
// Byte 3
|
||||
uint8_t Temp:4;
|
||||
uint8_t :1;
|
||||
uint8_t SwingV:1;
|
||||
uint8_t :2;
|
||||
// Byte 4
|
||||
uint8_t :4;
|
||||
uint8_t Power:1;
|
||||
uint8_t OffTimerEnable:1;
|
||||
uint8_t OnTimerEnable:1;
|
||||
uint8_t Sleep:1;
|
||||
// Byte 5
|
||||
uint8_t :8;
|
||||
// Byte 6
|
||||
uint8_t :4;
|
||||
uint8_t OnTimer:4;
|
||||
// Byte 7
|
||||
uint8_t :4;
|
||||
uint8_t OffTimer:4;
|
||||
};
|
||||
};
|
||||
|
||||
// Constants
|
||||
|
||||
// CARRIER_AC64
|
||||
const uint8_t kCarrierAc64ChecksumOffset = 16;
|
||||
const uint8_t kCarrierAc64ChecksumSize = 4;
|
||||
const uint8_t kCarrierAc64Heat = 0b01; // 1
|
||||
const uint8_t kCarrierAc64Cool = 0b10; // 2
|
||||
const uint8_t kCarrierAc64Fan = 0b11; // 3
|
||||
const uint8_t kCarrierAc64FanAuto = 0b00; // 0
|
||||
const uint8_t kCarrierAc64FanLow = 0b01; // 1
|
||||
const uint8_t kCarrierAc64FanMedium = 0b10; // 2
|
||||
const uint8_t kCarrierAc64FanHigh = 0b11; // 3
|
||||
const uint8_t kCarrierAc64MinTemp = 16; // Celsius
|
||||
const uint8_t kCarrierAc64MaxTemp = 30; // Celsius
|
||||
const uint8_t kCarrierAc64TimerMax = 9; // Hours.
|
||||
const uint8_t kCarrierAc64TimerMin = 1; // Hours.
|
||||
|
||||
|
||||
// Classes
|
||||
|
||||
/// Class for handling detailed Carrier 64 bit A/C messages.
|
||||
class IRCarrierAc64 {
|
||||
public:
|
||||
explicit IRCarrierAc64(const uint16_t pin, const bool inverted = false,
|
||||
const bool use_modulation = true);
|
||||
|
||||
void stateReset();
|
||||
#if SEND_CARRIER_AC64
|
||||
void send(const uint16_t repeat = kCarrierAc64MinRepeat);
|
||||
/// 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_CARRIER_AC64
|
||||
void begin();
|
||||
static uint8_t calcChecksum(const uint64_t state);
|
||||
static bool validChecksum(const uint64_t state);
|
||||
void setPower(const bool on);
|
||||
bool getPower(void) const;
|
||||
void on(void);
|
||||
void off(void);
|
||||
void setTemp(const uint8_t temp);
|
||||
uint8_t getTemp(void) const;
|
||||
void setSwingV(const bool on);
|
||||
bool getSwingV(void) const;
|
||||
void setSleep(const bool on);
|
||||
bool getSleep(void) const;
|
||||
void setFan(const uint8_t speed);
|
||||
uint8_t getFan(void) const;
|
||||
void setMode(const uint8_t mode);
|
||||
uint8_t getMode(void) const;
|
||||
void setOnTimer(const uint16_t nr_of_mins);
|
||||
uint16_t getOnTimer(void) const;
|
||||
void setOffTimer(const uint16_t nr_of_mins);
|
||||
uint16_t getOffTimer(void) const;
|
||||
uint64_t getRaw(void);
|
||||
void setRaw(const uint64_t state);
|
||||
static uint8_t convertMode(const stdAc::opmode_t mode);
|
||||
static uint8_t convertFan(const stdAc::fanspeed_t speed);
|
||||
static stdAc::opmode_t toCommonMode(const uint8_t mode);
|
||||
static stdAc::fanspeed_t toCommonFanSpeed(const uint8_t speed);
|
||||
stdAc::state_t toCommon(void) const;
|
||||
String toString(void) const;
|
||||
#ifndef UNIT_TEST
|
||||
|
||||
private:
|
||||
IRsend _irsend; ///< Instance of the IR send class
|
||||
#else
|
||||
/// @cond IGNORE
|
||||
IRsendTest _irsend; ///< Instance of the testing IR send class
|
||||
/// @endcond
|
||||
#endif
|
||||
CarrierProtocol _;
|
||||
void checksum(void);
|
||||
void _cancelOnTimer(void);
|
||||
void _cancelOffTimer(void);
|
||||
};
|
||||
#endif // IR_CARRIER_H_
|
||||
86
yoRadio/src/IRremoteESP8266/ir_ClimaButler.cpp
Normal file
86
yoRadio/src/IRremoteESP8266/ir_ClimaButler.cpp
Normal file
@@ -0,0 +1,86 @@
|
||||
// Copyright 2022 benjy3gg
|
||||
// Copyright 2022 David Conran (crankyoldgit)
|
||||
/// @file
|
||||
/// @brief Support for Clima-Butler protocol
|
||||
/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1812
|
||||
|
||||
// Supports:
|
||||
// Brand: Clima-Butler, Model: AR-715 remote
|
||||
// Brand: Clima-Butler, Model: RCS-SD43UWI A/C
|
||||
|
||||
#include "IRrecv.h"
|
||||
#include "IRsend.h"
|
||||
#include "IRutils.h"
|
||||
|
||||
|
||||
const uint16_t kClimaButlerBitMark = 511; // uSeconds
|
||||
const uint16_t kClimaButlerHdrMark = kClimaButlerBitMark;
|
||||
const uint16_t kClimaButlerHdrSpace = 3492; // uSeconds
|
||||
const uint16_t kClimaButlerOneSpace = 1540; // uSeconds
|
||||
const uint16_t kClimaButlerZeroSpace = 548; // uSeconds
|
||||
const uint32_t kClimaButlerGap = kDefaultMessageGap; // uSeconds (A guess.)
|
||||
const uint16_t kClimaButlerFreq = 38000; // Hz. (Guess.)
|
||||
|
||||
#if SEND_CLIMABUTLER
|
||||
/// Send a ClimaButler formatted message.
|
||||
/// Status: STABLE / Confirmed working.
|
||||
/// @param[in] data containing the IR command.
|
||||
/// @param[in] nbits Nr. of bits to send. usually kClimaButlerBits
|
||||
/// @param[in] repeat Nr. of times the message is to be repeated.
|
||||
void IRsend::sendClimaButler(const uint64_t data, const uint16_t nbits,
|
||||
const uint16_t repeat) {
|
||||
enableIROut(kClimaButlerFreq);
|
||||
for (uint16_t r = 0; r <= repeat; r++) {
|
||||
// Header + Data
|
||||
sendGeneric(kClimaButlerHdrMark, kClimaButlerHdrSpace,
|
||||
kClimaButlerBitMark, kClimaButlerOneSpace,
|
||||
kClimaButlerBitMark, kClimaButlerZeroSpace,
|
||||
kClimaButlerBitMark, kClimaButlerHdrSpace,
|
||||
data, nbits, kClimaButlerFreq, true, 0, kDutyDefault);
|
||||
// Footer
|
||||
mark(kClimaButlerBitMark);
|
||||
space(kClimaButlerGap);
|
||||
}
|
||||
}
|
||||
#endif // SEND_CLIMABUTLER
|
||||
|
||||
#if DECODE_CLIMABUTLER
|
||||
/// Decode the supplied ClimaButler message.
|
||||
/// Status: STABLE / Confirmed working.
|
||||
/// @param[in,out] results Ptr to the data to decode & where to store the decode
|
||||
/// @param[in] offset The starting index to use when attempting to decode the
|
||||
/// raw data. Typically/Defaults to kStartOffset.
|
||||
/// @param[in] nbits The number of data bits to expect.
|
||||
/// @param[in] strict Flag indicating if we should perform strict matching.
|
||||
/// @return A boolean. True if it can decode it, false if it can't.
|
||||
bool IRrecv::decodeClimaButler(decode_results *results, uint16_t offset,
|
||||
const uint16_t nbits, const bool strict) {
|
||||
if (results->rawlen < 2 * nbits + kHeader + 2 * kFooter - offset)
|
||||
return false; // Too short a message to match.
|
||||
if (strict && nbits != kClimaButlerBits)
|
||||
return false;
|
||||
|
||||
// Header + Data
|
||||
uint16_t used = matchGeneric(results->rawbuf + offset, &(results->value),
|
||||
results->rawlen - offset, nbits,
|
||||
kClimaButlerHdrMark, kClimaButlerHdrSpace,
|
||||
kClimaButlerBitMark, kClimaButlerOneSpace,
|
||||
kClimaButlerBitMark, kClimaButlerZeroSpace,
|
||||
kClimaButlerBitMark, kClimaButlerHdrSpace);
|
||||
if (!used) return false; // Didn't matched.
|
||||
offset += used;
|
||||
// Footer
|
||||
if (!matchMark(results->rawbuf[offset++], kClimaButlerBitMark))
|
||||
return false;
|
||||
if (results->rawlen <= offset && !matchAtLeast(results->rawbuf[offset],
|
||||
kClimaButlerGap))
|
||||
return false;
|
||||
|
||||
// Success
|
||||
results->decode_type = decode_type_t::CLIMABUTLER;
|
||||
results->bits = nbits;
|
||||
results->command = 0;
|
||||
results->address = 0;
|
||||
return true;
|
||||
}
|
||||
#endif // DECODE_CLIMABUTLER
|
||||
760
yoRadio/src/IRremoteESP8266/ir_Coolix.cpp
Normal file
760
yoRadio/src/IRremoteESP8266/ir_Coolix.cpp
Normal file
@@ -0,0 +1,760 @@
|
||||
// Copyright bakrus
|
||||
// Copyright 2017,2019 David Conran
|
||||
// added by (send) bakrus & (decode) crankyoldgit
|
||||
/// @file
|
||||
/// @brief Coolix A/C / heatpump
|
||||
/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/484
|
||||
|
||||
#include "ir_Coolix.h"
|
||||
#include <algorithm>
|
||||
#ifndef ARDUINO
|
||||
#include <string>
|
||||
#endif
|
||||
#include "IRrecv.h"
|
||||
#include "IRsend.h"
|
||||
#include "IRtext.h"
|
||||
#include "IRutils.h"
|
||||
|
||||
// Constants
|
||||
// Pulse parms are *50-100 for the Mark and *50+100 for the space
|
||||
// First MARK is the one after the long gap
|
||||
// pulse parameters in usec
|
||||
const uint16_t kCoolixTick = 276; // Approximately 10.5 cycles at 38kHz
|
||||
const uint16_t kCoolixBitMarkTicks = 2;
|
||||
const uint16_t kCoolixBitMark = kCoolixBitMarkTicks * kCoolixTick; // 552us
|
||||
const uint16_t kCoolixOneSpaceTicks = 6;
|
||||
const uint16_t kCoolixOneSpace = kCoolixOneSpaceTicks * kCoolixTick; // 1656us
|
||||
const uint16_t kCoolixZeroSpaceTicks = 2;
|
||||
const uint16_t kCoolixZeroSpace = kCoolixZeroSpaceTicks * kCoolixTick; // 552us
|
||||
const uint16_t kCoolixHdrMarkTicks = 17;
|
||||
const uint16_t kCoolixHdrMark = kCoolixHdrMarkTicks * kCoolixTick; // 4692us
|
||||
const uint16_t kCoolixHdrSpaceTicks = 16;
|
||||
const uint16_t kCoolixHdrSpace = kCoolixHdrSpaceTicks * kCoolixTick; // 4416us
|
||||
const uint16_t kCoolixMinGapTicks = kCoolixHdrMarkTicks + kCoolixZeroSpaceTicks;
|
||||
const uint16_t kCoolixMinGap = kCoolixMinGapTicks * kCoolixTick; // 5244us
|
||||
const uint8_t kCoolixExtraTolerance = 5; // Percent
|
||||
|
||||
using irutils::addBoolToString;
|
||||
using irutils::addIntToString;
|
||||
using irutils::addLabeledString;
|
||||
using irutils::addModeToString;
|
||||
using irutils::addTempToString;
|
||||
|
||||
#if SEND_COOLIX
|
||||
/// Send a Coolix 24-bit message
|
||||
/// Status: STABLE / Confirmed Working.
|
||||
/// @param[in] data The message to be sent.
|
||||
/// @param[in] nbits The number of bits of message to be sent.
|
||||
/// @param[in] repeat The number of times the command is to be repeated.
|
||||
/// @see https://github.com/z3t0/Arduino-IRremote/blob/master/ir_COOLIX.cpp
|
||||
void IRsend::sendCOOLIX(uint64_t data, uint16_t nbits, uint16_t repeat) {
|
||||
if (nbits % 8 != 0) return; // nbits is required to be a multiple of 8.
|
||||
|
||||
// Set IR carrier frequency
|
||||
enableIROut(38);
|
||||
|
||||
for (uint16_t r = 0; r <= repeat; r++) {
|
||||
// Header
|
||||
mark(kCoolixHdrMark);
|
||||
space(kCoolixHdrSpace);
|
||||
|
||||
// Data
|
||||
// Break data into byte segments, starting at the Most Significant
|
||||
// Byte. Each byte then being sent normal, then followed inverted.
|
||||
for (uint16_t i = 8; i <= nbits; i += 8) {
|
||||
// Grab a bytes worth of data.
|
||||
uint8_t segment = (data >> (nbits - i)) & 0xFF;
|
||||
// Normal
|
||||
sendData(kCoolixBitMark, kCoolixOneSpace, kCoolixBitMark,
|
||||
kCoolixZeroSpace, segment, 8, true);
|
||||
// Inverted.
|
||||
sendData(kCoolixBitMark, kCoolixOneSpace, kCoolixBitMark,
|
||||
kCoolixZeroSpace, segment ^ 0xFF, 8, true);
|
||||
}
|
||||
|
||||
// Footer
|
||||
mark(kCoolixBitMark);
|
||||
space(kCoolixMinGap); // Pause before repeating
|
||||
}
|
||||
space(kDefaultMessageGap);
|
||||
}
|
||||
#endif // SEND_COOLIX
|
||||
|
||||
/// Class constructor.
|
||||
/// @param[in] pin GPIO to be used when sending.
|
||||
/// @param[in] inverted Is the output signal to be inverted?
|
||||
/// @param[in] use_modulation Is frequency modulation to be used?
|
||||
IRCoolixAC::IRCoolixAC(const uint16_t pin, const bool inverted,
|
||||
const bool use_modulation)
|
||||
: _irsend(pin, inverted, use_modulation) { stateReset(); }
|
||||
|
||||
/// Reset the internal state to a fixed known good state.
|
||||
void IRCoolixAC::stateReset(void) {
|
||||
setRaw(kCoolixDefaultState);
|
||||
savedFan = getFan();
|
||||
clearSensorTemp();
|
||||
powerFlag = false;
|
||||
turboFlag = false;
|
||||
ledFlag = false;
|
||||
cleanFlag = false;
|
||||
sleepFlag = false;
|
||||
swingFlag = false;
|
||||
}
|
||||
|
||||
/// Set up hardware to be able to send a message.
|
||||
void IRCoolixAC::begin(void) { _irsend.begin(); }
|
||||
|
||||
#if SEND_COOLIX
|
||||
/// Send the current internal state as an IR message.
|
||||
/// @param[in] repeat Nr. of times the message will be repeated.
|
||||
void IRCoolixAC::send(const uint16_t repeat) {
|
||||
// SwingVStep (aka. Direct / Vane step) needs to be sent with `0` repeats.
|
||||
// Typically repeat is `kCoolixDefaultRepeat` which is `1`, so this allows
|
||||
// it to be 0 normally for this command, and allows additional repeats if
|
||||
// requested rather always 0 for that command.
|
||||
_irsend.sendCOOLIX(getRaw(), kCoolixBits, repeat - (getSwingVStep() &&
|
||||
repeat > 0) ? 1 : 0);
|
||||
// make sure to remove special state from the internal state
|
||||
// after command has being transmitted.
|
||||
recoverSavedState();
|
||||
}
|
||||
#endif // SEND_COOLIX
|
||||
|
||||
/// Get a copy of the internal state as a valid code for this protocol.
|
||||
/// @return A valid code for this protocol based on the current internal state.
|
||||
uint32_t IRCoolixAC::getRaw(void) const { return _.raw; }
|
||||
|
||||
/// Set the internal state from a valid code for this protocol.
|
||||
/// @param[in] new_code A valid code for this protocol.
|
||||
void IRCoolixAC::setRaw(const uint32_t new_code) {
|
||||
powerFlag = true; // Everything that is not the special power off mesg is On.
|
||||
if (!handleSpecialState(new_code)) {
|
||||
// it isn`t special so might affect Temp|mode|Fan
|
||||
if (new_code == kCoolixCmdFan) {
|
||||
setMode(kCoolixFan);
|
||||
return;
|
||||
}
|
||||
}
|
||||
// must be a command changing Temp|Mode|Fan
|
||||
// it is safe to just copy to remote var
|
||||
_.raw = new_code;
|
||||
}
|
||||
|
||||
/// Is the current state is a special state?
|
||||
/// @return true, if it is. false if it isn't.
|
||||
bool IRCoolixAC::isSpecialState(void) const {
|
||||
switch (_.raw) {
|
||||
case kCoolixClean:
|
||||
case kCoolixLed:
|
||||
case kCoolixOff:
|
||||
case kCoolixSwing:
|
||||
case kCoolixSwingV:
|
||||
case kCoolixSleep:
|
||||
case kCoolixTurbo: return true;
|
||||
default: return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// Adjust any internal settings based on the type of special state we are
|
||||
/// supplied. Does nothing if it isn't a special state.
|
||||
/// @param[in] data The state we need to act upon.
|
||||
/// @note Special state means commands that are not affecting
|
||||
/// Temperature/Mode/Fan, and they toggle a setting.
|
||||
/// e.g. Swing Step is not a special state by this definition.
|
||||
/// @return true, if it is a special state. false if it isn't.
|
||||
bool IRCoolixAC::handleSpecialState(const uint32_t data) {
|
||||
switch (data) {
|
||||
case kCoolixClean:
|
||||
cleanFlag = !cleanFlag;
|
||||
break;
|
||||
case kCoolixLed:
|
||||
ledFlag = !ledFlag;
|
||||
break;
|
||||
case kCoolixOff:
|
||||
powerFlag = false;
|
||||
break;
|
||||
case kCoolixSwing:
|
||||
swingFlag = !swingFlag;
|
||||
break;
|
||||
case kCoolixSleep:
|
||||
sleepFlag = !sleepFlag;
|
||||
break;
|
||||
case kCoolixTurbo:
|
||||
turboFlag = !turboFlag;
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/// Backup the current internal state as long as it isn't a special state and
|
||||
/// set the new state.
|
||||
/// @note: Must be called before every special state to make sure the
|
||||
/// internal state is safe.
|
||||
/// @param[in] raw_state A valid raw state/code for this protocol.
|
||||
void IRCoolixAC::updateAndSaveState(const uint32_t raw_state) {
|
||||
if (!isSpecialState()) _saved = _;
|
||||
_.raw = raw_state;
|
||||
}
|
||||
|
||||
/// Restore the current internal state from backup as long as it isn't a
|
||||
/// special state.
|
||||
void IRCoolixAC::recoverSavedState(void) {
|
||||
// If the current state is a special one, last known normal one.
|
||||
if (isSpecialState()) _ = _saved;
|
||||
// If the saved state was also a special state, reset as we expect a normal
|
||||
// state out of all this.
|
||||
if (isSpecialState()) stateReset();
|
||||
}
|
||||
|
||||
/// Set the raw (native) temperature value.
|
||||
/// @note Bypasses any checks.
|
||||
/// @param[in] code The desired native temperature.
|
||||
void IRCoolixAC::setTempRaw(const uint8_t code) { _.Temp = code; }
|
||||
|
||||
/// Get the raw (native) temperature value.
|
||||
/// @return The native temperature value.
|
||||
uint8_t IRCoolixAC::getTempRaw(void) const { return _.Temp; }
|
||||
|
||||
/// Set the temperature.
|
||||
/// @param[in] desired The temperature in degrees celsius.
|
||||
void IRCoolixAC::setTemp(const uint8_t desired) {
|
||||
// Range check.
|
||||
uint8_t temp = std::min(desired, kCoolixTempMax);
|
||||
temp = std::max(temp, kCoolixTempMin);
|
||||
setTempRaw(kCoolixTempMap[temp - kCoolixTempMin]);
|
||||
}
|
||||
|
||||
/// Get the current temperature setting.
|
||||
/// @return The current setting for temp. in degrees celsius.
|
||||
uint8_t IRCoolixAC::getTemp(void) const {
|
||||
const uint8_t code = getTempRaw();
|
||||
for (uint8_t i = 0; i < kCoolixTempRange; i++)
|
||||
if (kCoolixTempMap[i] == code) return kCoolixTempMin + i;
|
||||
return kCoolixTempMax; // Not a temp we expected.
|
||||
}
|
||||
|
||||
/// Set the raw (native) sensor temperature value.
|
||||
/// @note Bypasses any checks or additional actions.
|
||||
/// @param[in] code The desired native sensor temperature.
|
||||
void IRCoolixAC::setSensorTempRaw(const uint8_t code) { _.SensorTemp = code; }
|
||||
|
||||
/// Set the sensor temperature.
|
||||
/// @param[in] temp The temperature in degrees celsius.
|
||||
/// @warning Do not send messages with a Sensor Temp more frequently than once
|
||||
/// per minute, otherwise the A/C unit will ignore them.
|
||||
void IRCoolixAC::setSensorTemp(const uint8_t temp) {
|
||||
setSensorTempRaw(std::min(temp, kCoolixSensorTempMax));
|
||||
setZoneFollow(true); // Setting a Sensor temp means you want to Zone Follow.
|
||||
}
|
||||
|
||||
/// Get the sensor temperature setting.
|
||||
/// @return The current setting for sensor temp. in degrees celsius.
|
||||
uint8_t IRCoolixAC::getSensorTemp(void) const { return _.SensorTemp; }
|
||||
|
||||
/// Get the value of the current power setting.
|
||||
/// @return true, the setting is on. false, the setting is off.
|
||||
/// @note There is only an "off" state. Everything else is "on".
|
||||
bool IRCoolixAC::getPower(void) const { return powerFlag; }
|
||||
|
||||
/// Change the power setting.
|
||||
/// @param[in] on true, the setting is on. false, the setting is off.
|
||||
void IRCoolixAC::setPower(const bool on) {
|
||||
if (!on)
|
||||
updateAndSaveState(kCoolixOff);
|
||||
else if (!powerFlag)
|
||||
// at this point state must be ready
|
||||
// to be transmitted
|
||||
recoverSavedState();
|
||||
powerFlag = on;
|
||||
}
|
||||
|
||||
/// Change the power setting to On.
|
||||
void IRCoolixAC::on(void) { setPower(true); }
|
||||
|
||||
/// Change the power setting to Off.
|
||||
void IRCoolixAC::off(void) { setPower(false); }
|
||||
|
||||
/// Get the Swing setting of the A/C.
|
||||
/// @return true, the setting is on. false, the setting is off.
|
||||
bool IRCoolixAC::getSwing(void) const { return swingFlag; }
|
||||
|
||||
/// Toggle the Swing mode of the A/C.
|
||||
void IRCoolixAC::setSwing(void) {
|
||||
// Assumes that repeated sending "swing" toggles the action on the device.
|
||||
updateAndSaveState(kCoolixSwing);
|
||||
swingFlag = !swingFlag;
|
||||
}
|
||||
|
||||
/// Get the Vertical Swing Step setting of the A/C.
|
||||
/// @return true, the setting is on. false, the setting is off.
|
||||
bool IRCoolixAC::getSwingVStep(void) const { return _.raw == kCoolixSwingV; }
|
||||
|
||||
/// Set the Vertical Swing Step setting of the A/C.
|
||||
void IRCoolixAC::setSwingVStep(void) {
|
||||
updateAndSaveState(kCoolixSwingV);
|
||||
}
|
||||
|
||||
/// Get the Sleep setting of the A/C.
|
||||
/// @return true, the setting is on. false, the setting is off.
|
||||
bool IRCoolixAC::getSleep(void) const { return sleepFlag; }
|
||||
|
||||
/// Toggle the Sleep mode of the A/C.
|
||||
void IRCoolixAC::setSleep(void) {
|
||||
updateAndSaveState(kCoolixSleep);
|
||||
sleepFlag = !sleepFlag;
|
||||
}
|
||||
|
||||
/// Get the Turbo setting of the A/C.
|
||||
/// @return true, the setting is on. false, the setting is off.
|
||||
bool IRCoolixAC::getTurbo(void) const { return turboFlag; }
|
||||
|
||||
/// Toggle the Turbo mode of the A/C.
|
||||
void IRCoolixAC::setTurbo(void) {
|
||||
// Assumes that repeated sending "turbo" toggles the action on the device.
|
||||
updateAndSaveState(kCoolixTurbo);
|
||||
turboFlag = !turboFlag;
|
||||
}
|
||||
|
||||
/// Get the Led (light) setting of the A/C.
|
||||
/// @return true, the setting is on. false, the setting is off.
|
||||
bool IRCoolixAC::getLed(void) const { return ledFlag; }
|
||||
|
||||
/// Toggle the Led (light) mode of the A/C.
|
||||
void IRCoolixAC::setLed(void) {
|
||||
// Assumes that repeated sending "Led" toggles the action on the device.
|
||||
updateAndSaveState(kCoolixLed);
|
||||
ledFlag = !ledFlag;
|
||||
}
|
||||
|
||||
/// Get the Clean setting of the A/C.
|
||||
/// @return true, the setting is on. false, the setting is off.
|
||||
bool IRCoolixAC::getClean(void) const { return cleanFlag; }
|
||||
|
||||
/// Toggle the Clean mode of the A/C.
|
||||
void IRCoolixAC::setClean(void) {
|
||||
updateAndSaveState(kCoolixClean);
|
||||
cleanFlag = !cleanFlag;
|
||||
}
|
||||
|
||||
/// Get the Zone Follow setting of the A/C.
|
||||
/// @return true, the setting is on. false, the setting is off.
|
||||
bool IRCoolixAC::getZoneFollow(void) const {
|
||||
return _.ZoneFollow1 && _.ZoneFollow2;
|
||||
}
|
||||
|
||||
/// Change the Zone Follow setting.
|
||||
/// @note Internal use only.
|
||||
/// @param[in] on true, the setting is on. false, the setting is off.
|
||||
void IRCoolixAC::setZoneFollow(const bool on) {
|
||||
_.ZoneFollow1 = on;
|
||||
_.ZoneFollow2 = on;
|
||||
setFan(on ? kCoolixFanZoneFollow : savedFan);
|
||||
}
|
||||
|
||||
/// Clear the Sensor Temperature setting..
|
||||
void IRCoolixAC::clearSensorTemp(void) {
|
||||
setZoneFollow(false);
|
||||
setSensorTempRaw(kCoolixSensorTempIgnoreCode);
|
||||
}
|
||||
|
||||
/// Set the operating mode of the A/C.
|
||||
/// @param[in] mode The desired operating mode.
|
||||
void IRCoolixAC::setMode(const uint8_t mode) {
|
||||
uint32_t actualmode = mode;
|
||||
switch (actualmode) {
|
||||
case kCoolixAuto:
|
||||
case kCoolixDry:
|
||||
setFan(kCoolixFanAuto0, false);
|
||||
break;
|
||||
case kCoolixCool:
|
||||
case kCoolixHeat:
|
||||
case kCoolixFan:
|
||||
setFan(kCoolixFanAuto, false);
|
||||
break;
|
||||
default: // Anything else, go with Auto mode.
|
||||
setMode(kCoolixAuto);
|
||||
setFan(kCoolixFanAuto0, false);
|
||||
return;
|
||||
}
|
||||
setTemp(getTemp());
|
||||
// Fan mode is a special case of Dry.
|
||||
if (mode == kCoolixFan) {
|
||||
actualmode = kCoolixDry;
|
||||
setTempRaw(kCoolixFanTempCode);
|
||||
}
|
||||
_.Mode = actualmode;
|
||||
}
|
||||
|
||||
/// Get the operating mode setting of the A/C.
|
||||
/// @return The current operating mode setting.
|
||||
uint8_t IRCoolixAC::getMode(void) const {
|
||||
const uint8_t mode = _.Mode;
|
||||
if (mode == kCoolixDry)
|
||||
if (getTempRaw() == kCoolixFanTempCode) return kCoolixFan;
|
||||
return mode;
|
||||
}
|
||||
|
||||
/// Get the current fan speed setting.
|
||||
/// @return The current fan speed.
|
||||
uint8_t IRCoolixAC::getFan(void) const { return _.Fan; }
|
||||
|
||||
/// Set the speed of the fan.
|
||||
/// @param[in] speed The desired setting.
|
||||
/// @param[in] modecheck Do we enforce any mode limitations before setting?
|
||||
void IRCoolixAC::setFan(const uint8_t speed, const bool modecheck) {
|
||||
uint8_t newspeed = speed;
|
||||
switch (speed) {
|
||||
case kCoolixFanAuto: // Dry & Auto mode can't have this speed.
|
||||
if (modecheck) {
|
||||
switch (getMode()) {
|
||||
case kCoolixAuto:
|
||||
case kCoolixDry:
|
||||
newspeed = kCoolixFanAuto0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case kCoolixFanAuto0: // Only Dry & Auto mode can have this speed.
|
||||
if (modecheck) {
|
||||
switch (getMode()) {
|
||||
case kCoolixAuto:
|
||||
case kCoolixDry: break;
|
||||
default: newspeed = kCoolixFanAuto;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case kCoolixFanMin:
|
||||
case kCoolixFanMed:
|
||||
case kCoolixFanMax:
|
||||
case kCoolixFanZoneFollow:
|
||||
case kCoolixFanFixed:
|
||||
break;
|
||||
default: // Unknown speed requested.
|
||||
newspeed = kCoolixFanAuto;
|
||||
break;
|
||||
}
|
||||
// Keep a copy of the last non-ZoneFollow fan setting.
|
||||
savedFan = (_.Fan == kCoolixFanZoneFollow) ? savedFan : _.Fan;
|
||||
_.Fan = newspeed;
|
||||
}
|
||||
|
||||
/// Convert a standard A/C mode into its native mode.
|
||||
/// @param[in] mode A stdAc::opmode_t to be converted to it's native equivalent.
|
||||
/// @return The corresponding native mode.
|
||||
uint8_t IRCoolixAC::convertMode(const stdAc::opmode_t mode) {
|
||||
switch (mode) {
|
||||
case stdAc::opmode_t::kCool: return kCoolixCool;
|
||||
case stdAc::opmode_t::kHeat: return kCoolixHeat;
|
||||
case stdAc::opmode_t::kDry: return kCoolixDry;
|
||||
case stdAc::opmode_t::kFan: return kCoolixFan;
|
||||
default: return kCoolixAuto;
|
||||
}
|
||||
}
|
||||
|
||||
/// Convert a stdAc::fanspeed_t enum into it's native speed.
|
||||
/// @param[in] speed The enum to be converted.
|
||||
/// @return The native equivalent of the enum.
|
||||
uint8_t IRCoolixAC::convertFan(const stdAc::fanspeed_t speed) {
|
||||
switch (speed) {
|
||||
case stdAc::fanspeed_t::kMin:
|
||||
case stdAc::fanspeed_t::kLow: return kCoolixFanMin;
|
||||
case stdAc::fanspeed_t::kMedium: return kCoolixFanMed;
|
||||
case stdAc::fanspeed_t::kHigh:
|
||||
case stdAc::fanspeed_t::kMax: return kCoolixFanMax;
|
||||
default: return kCoolixFanAuto;
|
||||
}
|
||||
}
|
||||
|
||||
/// Convert a native mode to it's common stdAc::opmode_t equivalent.
|
||||
/// @param[in] mode A native operation mode to be converted.
|
||||
/// @return The corresponding common stdAc::opmode_t mode.
|
||||
stdAc::opmode_t IRCoolixAC::toCommonMode(const uint8_t mode) {
|
||||
switch (mode) {
|
||||
case kCoolixCool: return stdAc::opmode_t::kCool;
|
||||
case kCoolixHeat: return stdAc::opmode_t::kHeat;
|
||||
case kCoolixDry: return stdAc::opmode_t::kDry;
|
||||
case kCoolixFan: return stdAc::opmode_t::kFan;
|
||||
default: return stdAc::opmode_t::kAuto;
|
||||
}
|
||||
}
|
||||
|
||||
/// Convert a native fan speed into its stdAc equivalent.
|
||||
/// @param[in] speed The native setting to be converted.
|
||||
/// @return The stdAc equivalent of the native setting.
|
||||
stdAc::fanspeed_t IRCoolixAC::toCommonFanSpeed(const uint8_t speed) {
|
||||
switch (speed) {
|
||||
case kCoolixFanMax: return stdAc::fanspeed_t::kMax;
|
||||
case kCoolixFanMed: return stdAc::fanspeed_t::kMedium;
|
||||
case kCoolixFanMin: return stdAc::fanspeed_t::kMin;
|
||||
default: return stdAc::fanspeed_t::kAuto;
|
||||
}
|
||||
}
|
||||
|
||||
/// Convert the A/C state to it's common stdAc::state_t equivalent.
|
||||
/// @param[in] prev Ptr to the previous state if required.
|
||||
/// @return A stdAc::state_t state.
|
||||
stdAc::state_t IRCoolixAC::toCommon(const stdAc::state_t *prev) const {
|
||||
stdAc::state_t result{};
|
||||
// Start with the previous state if given it.
|
||||
if (prev != NULL) {
|
||||
result = *prev;
|
||||
} else {
|
||||
// Set defaults for non-zero values that are not implicitly set for when
|
||||
// there is no previous state.
|
||||
// e.g. Any setting that toggles should probably go here.
|
||||
result.swingv = stdAc::swingv_t::kOff;
|
||||
result.turbo = false;
|
||||
result.clean = false;
|
||||
result.light = false;
|
||||
result.sleep = -1;
|
||||
}
|
||||
// Not supported.
|
||||
result.model = -1; // No models used.
|
||||
result.swingh = stdAc::swingh_t::kOff;
|
||||
result.quiet = false;
|
||||
result.econo = false;
|
||||
result.filter = false;
|
||||
result.beep = false;
|
||||
result.clock = -1;
|
||||
|
||||
// Supported.
|
||||
result.protocol = decode_type_t::COOLIX;
|
||||
result.celsius = true;
|
||||
result.power = getPower();
|
||||
// Power off state no other state info. Use the previous state if we have it.
|
||||
if (!result.power) return result;
|
||||
// Handle the special single command (Swing/Turbo/Light/Clean/Sleep) toggle
|
||||
// messages. These have no other state info so use the rest of the previous
|
||||
// state if we have it for them.
|
||||
if (getSwing()) {
|
||||
result.swingv = result.swingv != stdAc::swingv_t::kOff ?
|
||||
stdAc::swingv_t::kOff : stdAc::swingv_t::kAuto; // Invert swing.
|
||||
return result;
|
||||
} else if (getTurbo()) {
|
||||
result.turbo = !result.turbo;
|
||||
return result;
|
||||
} else if (getLed()) {
|
||||
result.light = !result.light;
|
||||
return result;
|
||||
} else if (getClean()) {
|
||||
result.clean = !result.clean;
|
||||
return result;
|
||||
} else if (getSleep()) {
|
||||
result.sleep = result.sleep >= 0 ? -1 : 0; // Invert sleep.
|
||||
return result;
|
||||
}
|
||||
// Back to "normal" stateful messages.
|
||||
result.mode = toCommonMode(getMode());
|
||||
result.degrees = getTemp();
|
||||
result.sensorTemperature = getSensorTemp();
|
||||
if (result.sensorTemperature == kCoolixSensorTempIgnoreCode) {
|
||||
result.sensorTemperature = kNoTempValue;
|
||||
}
|
||||
result.iFeel = getZoneFollow();
|
||||
result.fanspeed = toCommonFanSpeed(getFan());
|
||||
return result;
|
||||
}
|
||||
|
||||
/// Convert the internal state into a human readable string.
|
||||
/// @return The current internal state expressed as a human readable String.
|
||||
String IRCoolixAC::toString(void) const {
|
||||
String result = "";
|
||||
result.reserve(100); // Reserve some heap for the string to reduce fragging.
|
||||
result += addBoolToString(getPower(), kPowerStr, false);
|
||||
if (!getPower()) return result; // If it's off, there is no other info.
|
||||
if (isSpecialState()) {
|
||||
// Special modes.
|
||||
result += kCommaSpaceStr;
|
||||
if (getSwing()) result += kSwingStr;
|
||||
else if (getSwingVStep()) result += kSwingVStr;
|
||||
else if (getSleep()) result += kSleepStr;
|
||||
else if (getTurbo()) result += kTurboStr;
|
||||
else if (getLed()) result += kLightStr;
|
||||
else if (getClean()) result += kCleanStr;
|
||||
|
||||
result += kColonSpaceStr;
|
||||
if (getSwingVStep())
|
||||
result += kStepStr;
|
||||
else
|
||||
result += kToggleStr;
|
||||
return result;
|
||||
}
|
||||
result += addModeToString(getMode(), kCoolixAuto, kCoolixCool, kCoolixHeat,
|
||||
kCoolixDry, kCoolixFan);
|
||||
result += addIntToString(getFan(), kFanStr);
|
||||
result += kSpaceLBraceStr;
|
||||
switch (getFan()) {
|
||||
case kCoolixFanAuto:
|
||||
result += kAutoStr;
|
||||
break;
|
||||
case kCoolixFanAuto0:
|
||||
result += kAutoStr;
|
||||
result += '0';
|
||||
break;
|
||||
case kCoolixFanMax:
|
||||
result += kMaxStr;
|
||||
break;
|
||||
case kCoolixFanMin:
|
||||
result += kMinStr;
|
||||
break;
|
||||
case kCoolixFanMed:
|
||||
result += kMedStr;
|
||||
break;
|
||||
case kCoolixFanZoneFollow:
|
||||
result += kZoneFollowStr;
|
||||
break;
|
||||
case kCoolixFanFixed:
|
||||
result += kFixedStr;
|
||||
break;
|
||||
default:
|
||||
result += kUnknownStr;
|
||||
}
|
||||
result += ')';
|
||||
// Fan mode doesn't have a temperature.
|
||||
if (getMode() != kCoolixFan) result += addTempToString(getTemp());
|
||||
result += addBoolToString(getZoneFollow(), kZoneFollowStr);
|
||||
result += addLabeledString(
|
||||
(getSensorTemp() == kCoolixSensorTempIgnoreCode)
|
||||
// Encasing with String(blah) to keep compatible with old arduino
|
||||
// frameworks. Not needed with 3.0.2.
|
||||
///> @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1639#issuecomment-944906016
|
||||
? kOffStr : String(uint64ToString(getSensorTemp()) + 'C'),
|
||||
kSensorTempStr);
|
||||
return result;
|
||||
}
|
||||
|
||||
#if DECODE_COOLIX
|
||||
/// Decode the supplied Coolix 24-bit A/C message.
|
||||
/// Status: STABLE / Known Working.
|
||||
/// @param[in,out] results Ptr to the data to decode & where to store the decode
|
||||
/// result.
|
||||
/// @param[in] offset The starting index to use when attempting to decode the
|
||||
/// raw data. Typically/Defaults to kStartOffset.
|
||||
/// @param[in] nbits The number of data bits to expect.
|
||||
/// @param[in] strict Flag indicating if we should perform strict matching.
|
||||
/// @return A boolean. True if it can decode it, false if it can't.
|
||||
bool IRrecv::decodeCOOLIX(decode_results *results, uint16_t offset,
|
||||
const uint16_t nbits, const bool strict) {
|
||||
// The protocol sends the data normal + inverted, alternating on
|
||||
// each byte. Hence twice the number of expected data bits.
|
||||
if (results->rawlen < 2 * 2 * nbits + kHeader + kFooter - 1 + offset)
|
||||
return false; // Can't possibly be a valid COOLIX message.
|
||||
if (strict && nbits != kCoolixBits)
|
||||
return false; // Not strictly a COOLIX message.
|
||||
if (nbits % 8 != 0) // nbits has to be a multiple of nr. of bits in a byte.
|
||||
return false;
|
||||
|
||||
uint64_t data = 0;
|
||||
uint64_t inverted = 0;
|
||||
|
||||
if (nbits > sizeof(data) * 8)
|
||||
return false; // We can't possibly capture a Coolix packet that big.
|
||||
|
||||
// Header
|
||||
if (!matchMark(results->rawbuf[offset++], kCoolixHdrMark)) return false;
|
||||
if (!matchSpace(results->rawbuf[offset++], kCoolixHdrSpace)) return false;
|
||||
|
||||
// Data
|
||||
// Twice as many bits as there are normal plus inverted bits.
|
||||
for (uint16_t i = 0; i < nbits * 2; i += 8) {
|
||||
const bool flip = (i / 8) % 2;
|
||||
uint64_t result = 0;
|
||||
// Read the next byte of data.
|
||||
const uint16_t used = matchGeneric(results->rawbuf + offset, &result,
|
||||
results->rawlen - offset, 8,
|
||||
0, 0, // No Header
|
||||
kCoolixBitMark, kCoolixOneSpace, // Data
|
||||
kCoolixBitMark, kCoolixZeroSpace,
|
||||
0, 0, // No Footer
|
||||
false,
|
||||
_tolerance + kCoolixExtraTolerance,
|
||||
0, true);
|
||||
if (!used) return false; // Didn't match a bytes worth of data.
|
||||
offset += used;
|
||||
if (flip) { // The inverted byte.
|
||||
inverted <<= 8;
|
||||
inverted |= result;
|
||||
} else {
|
||||
data <<= 8;
|
||||
data |= result;
|
||||
}
|
||||
}
|
||||
|
||||
// Footer
|
||||
if (!matchMark(results->rawbuf[offset++], kCoolixBitMark)) return false;
|
||||
if (offset < results->rawlen &&
|
||||
!matchAtLeast(results->rawbuf[offset], kCoolixMinGap)) return false;
|
||||
|
||||
// Compliance
|
||||
uint64_t orig = data; // Save a copy of the data.
|
||||
if (strict) {
|
||||
for (uint16_t i = 0; i < nbits; i += 8, data >>= 8, inverted >>= 8)
|
||||
if ((data & 0xFF) != ((inverted & 0xFF) ^ 0xFF)) return false;
|
||||
}
|
||||
|
||||
// Success
|
||||
results->decode_type = COOLIX;
|
||||
results->bits = nbits;
|
||||
results->value = orig;
|
||||
results->address = 0;
|
||||
results->command = 0;
|
||||
return true;
|
||||
}
|
||||
#endif // DECODE_COOLIX
|
||||
|
||||
#if SEND_COOLIX48
|
||||
/// Send a Coolix 48-bit message.
|
||||
/// Status: ALPHA / Untested.
|
||||
/// @param[in] data The message to be sent.
|
||||
/// @param[in] nbits The number of bits of message to be sent.
|
||||
/// @param[in] repeat The number of times the command is to be repeated.
|
||||
/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1694
|
||||
/// @note This is effectively the same as `sendCOOLIX()` except requiring the
|
||||
/// bit flipping be done prior to the call.
|
||||
void IRsend::sendCoolix48(const uint64_t data, const uint16_t nbits,
|
||||
const uint16_t repeat) {
|
||||
// Header + Data + Footer
|
||||
sendGeneric(kCoolixHdrMark, kCoolixHdrSpace,
|
||||
kCoolixBitMark, kCoolixOneSpace,
|
||||
kCoolixBitMark, kCoolixZeroSpace,
|
||||
kCoolixBitMark, kCoolixMinGap,
|
||||
data, nbits, 38000, true, repeat, 33);
|
||||
}
|
||||
#endif // SEND_COOLIX48
|
||||
|
||||
#if DECODE_COOLIX48
|
||||
/// Decode the supplied Coolix 48-bit A/C message.
|
||||
/// Status: BETA / Probably Working.
|
||||
/// @param[in,out] results Ptr to the data to decode & where to store the decode
|
||||
/// result.
|
||||
/// @param[in] offset The starting index to use when attempting to decode the
|
||||
/// raw data. Typically/Defaults to kStartOffset.
|
||||
/// @param[in] nbits The number of data bits to expect.
|
||||
/// @param[in] strict Flag indicating if we should perform strict matching.
|
||||
/// @return A boolean. True if it can decode it, false if it can't.
|
||||
/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1694
|
||||
bool IRrecv::decodeCoolix48(decode_results *results, uint16_t offset,
|
||||
const uint16_t nbits, const bool strict) {
|
||||
if (strict && nbits != kCoolix48Bits)
|
||||
return false; // Not strictly a COOLIX48 message.
|
||||
|
||||
// Header + Data + Footer
|
||||
if (!matchGeneric(results->rawbuf + offset, &(results->value),
|
||||
results->rawlen - offset, nbits,
|
||||
kCoolixHdrMark, kCoolixHdrSpace,
|
||||
kCoolixBitMark, kCoolixOneSpace,
|
||||
kCoolixBitMark, kCoolixZeroSpace,
|
||||
kCoolixBitMark, kCoolixMinGap,
|
||||
true, _tolerance + kCoolixExtraTolerance, 0, true))
|
||||
return false;
|
||||
|
||||
// Success
|
||||
results->decode_type = COOLIX48;
|
||||
results->bits = nbits;
|
||||
results->address = 0;
|
||||
results->command = 0;
|
||||
return true;
|
||||
}
|
||||
#endif // DECODE_COOLIX48
|
||||
200
yoRadio/src/IRremoteESP8266/ir_Coolix.h
Normal file
200
yoRadio/src/IRremoteESP8266/ir_Coolix.h
Normal file
@@ -0,0 +1,200 @@
|
||||
// Copyright 2018 David Conran
|
||||
/// @file
|
||||
/// @brief Support for Coolix A/C protocols.
|
||||
/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/484
|
||||
/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1318
|
||||
/// @note Kudos:
|
||||
/// Hamper: For the breakdown and mapping of the bit values.
|
||||
/// fraschizzato: For additional ZoneFollow & SwingVStep analysis.
|
||||
/// @note Timers seem to use the `COOLIX48` protocol.
|
||||
/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1694
|
||||
|
||||
// Supports:
|
||||
// Brand: Beko, Model: RG57K7(B)/BGEF Remote
|
||||
// Brand: Beko, Model: BINR 070/071 split-type A/C
|
||||
// Brand: Midea, Model: RG52D/BGE Remote
|
||||
// Brand: Midea, Model: MS12FU-10HRDN1-QRD0GW(B) A/C
|
||||
// Brand: Midea, Model: MSABAU-07HRFN1-QRD0GW A/C (circa 2016)
|
||||
// Brand: Tokio, Model: AATOEMF17-12CHR1SW split-type RG51|50/BGE Remote
|
||||
// Brand: Airwell, Model: RC08B remote
|
||||
// Brand: Kastron, Model: RG57A7/BGEF Inverter remote
|
||||
// Brand: Kaysun, Model: Casual CF A/C
|
||||
// Brand: Toshiba, Model: RAS-M10YKV-E A/C
|
||||
// Brand: Toshiba, Model: RAS-M13YKV-E A/C
|
||||
// Brand: Toshiba, Model: RAS-4M27YAV-E A/C
|
||||
// Brand: Toshiba, Model: WH-E1YE remote
|
||||
// Brand: Bosch, Model: RG36B4/BGE remote
|
||||
// Brand: Bosch, Model: B1ZAI2441W/B1ZAO2441W A/C
|
||||
|
||||
#ifndef IR_COOLIX_H_
|
||||
#define IR_COOLIX_H_
|
||||
|
||||
#define __STDC_LIMIT_MACROS
|
||||
#include <stdint.h>
|
||||
#ifndef UNIT_TEST
|
||||
#include <Arduino.h>
|
||||
#endif
|
||||
#include "IRremoteESP8266.h"
|
||||
#include "IRsend.h"
|
||||
#ifdef UNIT_TEST
|
||||
#include "IRsend_test.h"
|
||||
#endif
|
||||
|
||||
// Constants
|
||||
// Modes
|
||||
const uint8_t kCoolixCool = 0b000;
|
||||
const uint8_t kCoolixDry = 0b001;
|
||||
const uint8_t kCoolixAuto = 0b010;
|
||||
const uint8_t kCoolixHeat = 0b011;
|
||||
const uint8_t kCoolixFan = 0b100; // Synthetic.
|
||||
// const uint32_t kCoolixModeMask = 0b000000000000000000001100; // 0xC
|
||||
// const uint32_t kCoolixZoneFollowMask = 0b000010000000000000000010 0x80002
|
||||
// Fan Control
|
||||
const uint8_t kCoolixFanMin = 0b100;
|
||||
const uint8_t kCoolixFanMed = 0b010;
|
||||
const uint8_t kCoolixFanMax = 0b001;
|
||||
const uint8_t kCoolixFanAuto = 0b101;
|
||||
const uint8_t kCoolixFanAuto0 = 0b000;
|
||||
const uint8_t kCoolixFanZoneFollow = 0b110;
|
||||
const uint8_t kCoolixFanFixed = 0b111;
|
||||
// Temperature
|
||||
const uint8_t kCoolixTempMin = 17; // Celsius
|
||||
const uint8_t kCoolixTempMax = 30; // Celsius
|
||||
const uint8_t kCoolixTempRange = kCoolixTempMax - kCoolixTempMin + 1;
|
||||
const uint8_t kCoolixFanTempCode = 0b1110; // Part of Fan Mode.
|
||||
const uint8_t kCoolixTempMap[kCoolixTempRange] = {
|
||||
0b0000, // 17C
|
||||
0b0001, // 18c
|
||||
0b0011, // 19C
|
||||
0b0010, // 20C
|
||||
0b0110, // 21C
|
||||
0b0111, // 22C
|
||||
0b0101, // 23C
|
||||
0b0100, // 24C
|
||||
0b1100, // 25C
|
||||
0b1101, // 26C
|
||||
0b1001, // 27C
|
||||
0b1000, // 28C
|
||||
0b1010, // 29C
|
||||
0b1011 // 30C
|
||||
};
|
||||
const uint8_t kCoolixSensorTempMax = 30; // Celsius
|
||||
const uint8_t kCoolixSensorTempIgnoreCode = 0b11111; // 0x1F / 31 (DEC)
|
||||
// kCoolixSensorTempMask = 0b000000000000111100000000; // 0xF00
|
||||
// Fixed states/messages.
|
||||
const uint32_t kCoolixOff = 0b101100100111101111100000; // 0xB27BE0
|
||||
const uint32_t kCoolixSwing = 0b101100100110101111100000; // 0xB26BE0
|
||||
const uint32_t kCoolixSwingH = 0b101100101111010110100010; // 0xB5F5A2
|
||||
const uint32_t kCoolixSwingV = 0b101100100000111111100000; // 0xB20FE0
|
||||
const uint32_t kCoolixSleep = 0b101100101110000000000011; // 0xB2E003
|
||||
const uint32_t kCoolixTurbo = 0b101101011111010110100010; // 0xB5F5A2
|
||||
const uint32_t kCoolixLed = 0b101101011111010110100101; // 0xB5F5A5
|
||||
const uint32_t kCoolixClean = 0b101101011111010110101010; // 0xB5F5AA
|
||||
const uint32_t kCoolixCmdFan = 0b101100101011111111100100; // 0xB2BFE4
|
||||
// On, 25C, Mode: Auto, Fan: Auto, Zone Follow: Off, Sensor Temp: Ignore.
|
||||
const uint32_t kCoolixDefaultState = 0b101100100001111111001000; // 0xB21FC8
|
||||
|
||||
/// Native representation of a Coolix A/C message.
|
||||
union CoolixProtocol {
|
||||
uint32_t raw; ///< The state in IR code form.
|
||||
struct { // Only 24 bits are used.
|
||||
// Byte
|
||||
uint32_t :1; // Unknown
|
||||
uint32_t ZoneFollow1:1; ///< Control bit for Zone Follow mode.
|
||||
uint32_t Mode :2; ///< Operation mode.
|
||||
uint32_t Temp :4; ///< Desired temperature (Celsius)
|
||||
// Byte
|
||||
uint32_t SensorTemp :5; ///< The temperature sensor in the IR remote.
|
||||
uint32_t Fan :3; ///< Fan speed
|
||||
// Byte
|
||||
uint32_t :3; // Unknown
|
||||
uint32_t ZoneFollow2:1; ///< Additional control bit for Zone Follow mode.
|
||||
uint32_t :4; ///< Fixed value 0b1011 / 0xB.
|
||||
};
|
||||
};
|
||||
|
||||
// Classes
|
||||
|
||||
/// Class for handling detailed Coolix A/C messages.
|
||||
/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/484
|
||||
class IRCoolixAC {
|
||||
public:
|
||||
explicit IRCoolixAC(const uint16_t pin, const bool inverted = false,
|
||||
const bool use_modulation = true);
|
||||
void stateReset(void);
|
||||
#if SEND_COOLIX
|
||||
void send(const uint16_t repeat = kCoolixDefaultRepeat);
|
||||
/// 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_COOLIX
|
||||
void begin(void);
|
||||
void on(void);
|
||||
void off(void);
|
||||
void setPower(const bool on);
|
||||
bool getPower(void) const;
|
||||
void setTemp(const uint8_t temp);
|
||||
uint8_t getTemp(void) const;
|
||||
void setSensorTemp(const uint8_t temp);
|
||||
uint8_t getSensorTemp(void) const;
|
||||
void clearSensorTemp(void);
|
||||
void setFan(const uint8_t speed, const bool modecheck = true);
|
||||
uint8_t getFan(void) const;
|
||||
void setMode(const uint8_t mode);
|
||||
uint8_t getMode(void) const;
|
||||
void setSwing(void);
|
||||
bool getSwing(void) const;
|
||||
void setSwingVStep(void);
|
||||
bool getSwingVStep(void) const;
|
||||
void setSleep(void);
|
||||
bool getSleep(void) const;
|
||||
void setTurbo(void);
|
||||
bool getTurbo(void) const;
|
||||
void setLed(void);
|
||||
bool getLed(void) const;
|
||||
void setClean(void);
|
||||
bool getClean(void) const;
|
||||
bool getZoneFollow(void) const;
|
||||
uint32_t getRaw(void) const;
|
||||
void setRaw(const uint32_t new_code);
|
||||
static uint8_t convertMode(const stdAc::opmode_t mode);
|
||||
static uint8_t convertFan(const stdAc::fanspeed_t speed);
|
||||
static stdAc::opmode_t toCommonMode(const uint8_t mode);
|
||||
static stdAc::fanspeed_t toCommonFanSpeed(const uint8_t speed);
|
||||
stdAc::state_t toCommon(const stdAc::state_t *prev = NULL) const;
|
||||
String toString(void) const;
|
||||
void setZoneFollow(const bool on);
|
||||
#ifndef UNIT_TEST
|
||||
|
||||
private:
|
||||
IRsend _irsend; ///< Instance of the IR send class
|
||||
#else
|
||||
/// @cond IGNORE
|
||||
IRsendTest _irsend; ///< Instance of the testing IR send class
|
||||
/// @endcond
|
||||
#endif
|
||||
CoolixProtocol _; ///< The state of the IR remote in IR code form.
|
||||
CoolixProtocol _saved; ///< Copy of the state if we required a special mode.
|
||||
|
||||
// Internal State settings
|
||||
bool powerFlag;
|
||||
bool turboFlag;
|
||||
bool ledFlag;
|
||||
bool cleanFlag;
|
||||
bool sleepFlag;
|
||||
bool swingFlag;
|
||||
uint8_t savedFan;
|
||||
|
||||
void setTempRaw(const uint8_t code);
|
||||
uint8_t getTempRaw(void) const;
|
||||
void setSensorTempRaw(const uint8_t code);
|
||||
bool isSpecialState(void) const;
|
||||
bool handleSpecialState(const uint32_t data);
|
||||
void updateAndSaveState(const uint32_t raw_state);
|
||||
void recoverSavedState(void);
|
||||
uint32_t getNormalState(void);
|
||||
};
|
||||
|
||||
#endif // IR_COOLIX_H_
|
||||
575
yoRadio/src/IRremoteESP8266/ir_Corona.cpp
Normal file
575
yoRadio/src/IRremoteESP8266/ir_Corona.cpp
Normal file
@@ -0,0 +1,575 @@
|
||||
// Copyright 2020 Christian Nilsson
|
||||
//
|
||||
/// @file
|
||||
/// @brief Corona A/C protocol
|
||||
/// @note Unsupported:
|
||||
/// - Auto/Max button press (special format)
|
||||
|
||||
#include "ir_Corona.h"
|
||||
#include <algorithm>
|
||||
#include <cstring>
|
||||
#include "IRac.h"
|
||||
#include "IRrecv.h"
|
||||
#include "IRsend.h"
|
||||
#include "IRtext.h"
|
||||
#include "IRutils.h"
|
||||
|
||||
using irutils::addBoolToString;
|
||||
using irutils::addLabeledString;
|
||||
using irutils::addModeToString;
|
||||
using irutils::addTempToString;
|
||||
using irutils::addFanToString;
|
||||
using irutils::minsToString;
|
||||
using irutils::setBits;
|
||||
|
||||
// Constants
|
||||
const uint16_t kCoronaAcHdrMark = 3500;
|
||||
const uint16_t kCoronaAcHdrSpace = 1680;
|
||||
const uint16_t kCoronaAcBitMark = 450;
|
||||
const uint16_t kCoronaAcOneSpace = 1270;
|
||||
const uint16_t kCoronaAcZeroSpace = 420;
|
||||
const uint16_t kCoronaAcSpaceGap = 10800;
|
||||
const uint16_t kCoronaAcFreq = 38000; // Hz.
|
||||
const uint16_t kCoronaAcOverheadShort = 3;
|
||||
const uint16_t kCoronaAcOverhead = 11; // full message
|
||||
const uint8_t kCoronaTolerance = 5; // +5%
|
||||
|
||||
#if SEND_CORONA_AC
|
||||
/// Send a CoronaAc formatted message.
|
||||
/// Status: STABLE / Working on real device.
|
||||
/// @param[in] data An array of bytes containing the IR command.
|
||||
/// @param[in] nbytes Nr. of bytes of data in the array.
|
||||
/// e.g.
|
||||
/// @code
|
||||
/// uint8_t data[kCoronaAcStateLength] = {
|
||||
/// 0x28, 0x61, 0x3D, 0x19, 0xE6, 0x37, 0xC8,
|
||||
/// 0x28, 0x61, 0x6D, 0xFF, 0x00, 0xFF, 0x00,
|
||||
/// 0x28, 0x61, 0xCD, 0xFF, 0x00, 0xFF, 0x00};
|
||||
/// @endcode
|
||||
/// @param[in] repeat Nr. of times the message is to be repeated.
|
||||
void IRsend::sendCoronaAc(const uint8_t data[],
|
||||
const uint16_t nbytes, const uint16_t repeat) {
|
||||
if (nbytes < kCoronaAcSectionBytes) return;
|
||||
if (kCoronaAcSectionBytes < nbytes &&
|
||||
nbytes < kCoronaAcStateLength) return;
|
||||
for (uint16_t r = 0; r <= repeat; r++) {
|
||||
uint16_t pos = 0;
|
||||
// Data Section #1 - 3 loop
|
||||
// e.g.
|
||||
// bits = 56; bytes = 7;
|
||||
// #1 *(data + pos) = {0x28, 0x61, 0x3D, 0x19, 0xE6, 0x37, 0xC8};
|
||||
// #2 *(data + pos) = {0x28, 0x61, 0x6D, 0xFF, 0x00, 0xFF, 0x00};
|
||||
// #3 *(data + pos) = {0x28, 0x61, 0xCD, 0xFF, 0x00, 0xFF, 0x00};
|
||||
for (uint8_t section = 0; section < kCoronaAcSections; section++) {
|
||||
sendGeneric(kCoronaAcHdrMark, kCoronaAcHdrSpace,
|
||||
kCoronaAcBitMark, kCoronaAcOneSpace,
|
||||
kCoronaAcBitMark, kCoronaAcZeroSpace,
|
||||
kCoronaAcBitMark, kCoronaAcSpaceGap,
|
||||
data + pos, kCoronaAcSectionBytes,
|
||||
kCoronaAcFreq, false, kNoRepeat, kDutyDefault);
|
||||
pos += kCoronaAcSectionBytes; // Adjust by how many bytes was sent
|
||||
// don't send more data then what we have
|
||||
if (nbytes <= pos)
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif // SEND_CORONA_AC
|
||||
|
||||
#if DECODE_CORONA_AC
|
||||
/// Decode the supplied CoronaAc message.
|
||||
/// Status: STABLE / Appears to be working.
|
||||
/// @param[in,out] results Ptr to the data to decode & where to store it
|
||||
/// @param[in] offset The starting index to use when attempting to decode the
|
||||
/// raw data. Typically/Defaults to kStartOffset.
|
||||
/// @param[in] nbits The number of data bits to expect.
|
||||
/// @param[in] strict Flag indicating if we should perform strict matching.
|
||||
/// @return A boolean. True if it can decode it, false if it can't.
|
||||
bool IRrecv::decodeCoronaAc(decode_results *results, uint16_t offset,
|
||||
const uint16_t nbits, const bool strict) {
|
||||
bool isLong = results->rawlen >= kCoronaAcBits * 2;
|
||||
if (results->rawlen < 2 * nbits +
|
||||
(isLong ? kCoronaAcOverhead : kCoronaAcOverheadShort)
|
||||
- offset)
|
||||
return false; // Too short a message to match.
|
||||
if (strict && nbits != kCoronaAcBits && nbits != kCoronaAcBitsShort)
|
||||
return false;
|
||||
|
||||
uint16_t pos = 0;
|
||||
uint16_t used = 0;
|
||||
|
||||
// Data Section #1 - 3 loop
|
||||
// e.g.
|
||||
// bits = 56; bytes = 7;
|
||||
// #1 *(results->state + pos) = {0x28, 0x61, 0x3D, 0x19, 0xE6, 0x37, 0xC8};
|
||||
// #2 *(results->state + pos) = {0x28, 0x61, 0x6D, 0xFF, 0x00, 0xFF, 0x00};
|
||||
// #3 *(results->state + pos) = {0x28, 0x61, 0xCD, 0xFF, 0x00, 0xFF, 0x00};
|
||||
for (uint8_t section = 0; section < kCoronaAcSections; section++) {
|
||||
DPRINT(uint64ToString(section));
|
||||
used = matchGeneric(results->rawbuf + offset, results->state + pos,
|
||||
results->rawlen - offset, kCoronaAcBitsShort,
|
||||
kCoronaAcHdrMark, kCoronaAcHdrSpace,
|
||||
kCoronaAcBitMark, kCoronaAcOneSpace,
|
||||
kCoronaAcBitMark, kCoronaAcZeroSpace,
|
||||
kCoronaAcBitMark, kCoronaAcSpaceGap, true,
|
||||
_tolerance + kCoronaTolerance, kMarkExcess, false);
|
||||
if (used == 0) return false; // We failed to find any data.
|
||||
// short versions section 0 is special
|
||||
if (strict && !IRCoronaAc::validSection(results->state, pos,
|
||||
isLong ? section : 3))
|
||||
return false;
|
||||
offset += used; // Adjust for how much of the message we read.
|
||||
pos += kCoronaAcSectionBytes; // Adjust by how many bytes of data was read
|
||||
// don't read more data then what we have
|
||||
if (results->rawlen <= offset)
|
||||
break;
|
||||
}
|
||||
|
||||
// Re-check we got the correct size/length due to the way we read the data.
|
||||
if (strict && pos * 8 != kCoronaAcBits && pos * 8 != kCoronaAcBitsShort) {
|
||||
DPRINTLN("strict bit match fail");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Success
|
||||
results->decode_type = decode_type_t::CORONA_AC;
|
||||
results->bits = pos * 8;
|
||||
// No need to record the state as we stored it as we decoded it.
|
||||
// As we use result->state, we don't record value, address, or command as it
|
||||
// is a union data type.
|
||||
return true;
|
||||
}
|
||||
#endif // DECODE_CORONA_AC
|
||||
|
||||
/// Class constructor for handling detailed Corona A/C messages.
|
||||
/// @param[in] pin GPIO to be used when sending.
|
||||
/// @param[in] inverted Is the output signal to be inverted?
|
||||
/// @param[in] use_modulation Is frequency modulation to be used?
|
||||
IRCoronaAc::IRCoronaAc(const uint16_t pin, const bool inverted,
|
||||
const bool use_modulation)
|
||||
: _irsend(pin, inverted, use_modulation) { stateReset(); }
|
||||
|
||||
/// Reset the internal state to a fixed known good state.
|
||||
/// @note The state is powered off.
|
||||
void IRCoronaAc::stateReset(void) {
|
||||
// known good state
|
||||
_.sections[kCoronaAcSettingsSection].Data0 = kCoronaAcSectionData0Base;
|
||||
_.sections[kCoronaAcSettingsSection].Data1 = 0x00; // ensure no unset mem
|
||||
setPowerButton(true); // we default to this on, any timer removes it
|
||||
setTemp(kCoronaAcMinTemp);
|
||||
setMode(kCoronaAcModeCool);
|
||||
setFan(kCoronaAcFanAuto);
|
||||
setOnTimer(kCoronaAcTimerOff);
|
||||
setOffTimer(kCoronaAcTimerOff);
|
||||
// headers and checks are fixed in getRaw by checksum(_.raw)
|
||||
}
|
||||
|
||||
/// Get the byte that identifies the section
|
||||
/// @param[in] section Index of the section 0-2,
|
||||
/// 3 and above is used as the special case for short message
|
||||
/// @return The byte used for the section
|
||||
uint8_t IRCoronaAc::getSectionByte(const uint8_t section) {
|
||||
// base byte
|
||||
uint8_t b = kCoronaAcSectionLabelBase;
|
||||
// 2 enabled bits shifted 0-2 bits depending on section
|
||||
if (section >= 3)
|
||||
return 0b10010000 | b;
|
||||
setBits(&b, kHighNibble, kNibbleSize, 0b11 << section);
|
||||
return b;
|
||||
}
|
||||
|
||||
/// Check that a CoronaAc Section part is valid with section byte and inverted
|
||||
/// @param[in] state An array of bytes containing the section
|
||||
/// @param[in] pos Where to start in the state array
|
||||
/// @param[in] section Which section to work with
|
||||
/// Used to get the section byte, and is validated against pos
|
||||
/// @return true if section is valid, otherwise false
|
||||
bool IRCoronaAc::validSection(const uint8_t state[], const uint16_t pos,
|
||||
const uint8_t section) {
|
||||
// sanity check, pos must match section, section 4 is at pos 0
|
||||
if ((section % kCoronaAcSections) * kCoronaAcSectionBytes != pos)
|
||||
return false;
|
||||
// all individual sections has the same prefix
|
||||
const CoronaSection *p = reinterpret_cast<const CoronaSection*>(state + pos);
|
||||
if (p->Header0 != kCoronaAcSectionHeader0) {
|
||||
DPRINT("State ");
|
||||
DPRINT(&(p->Header0) - state);
|
||||
DPRINT(" expected 0x28 was ");
|
||||
DPRINTLN(uint64ToString(p->Header0, 16));
|
||||
return false;
|
||||
}
|
||||
if (p->Header1 != kCoronaAcSectionHeader1) {
|
||||
DPRINT("State ");
|
||||
DPRINT(&(p->Header1) - state);
|
||||
DPRINT(" expected 0x61 was ");
|
||||
DPRINTLN(uint64ToString(p->Header1, 16));
|
||||
return false;
|
||||
}
|
||||
|
||||
// checking section byte
|
||||
if (p->Label != getSectionByte(section)) {
|
||||
DPRINT("check 2 not matching, got ");
|
||||
DPRINT(uint64ToString(p->Label, 16));
|
||||
DPRINT(" expected ");
|
||||
DPRINTLN(uint64ToString(getSectionByte(section), 16));
|
||||
return false;
|
||||
}
|
||||
|
||||
// checking inverts
|
||||
uint8_t d0invinv = ~p->Data0Inv;
|
||||
if (p->Data0 != d0invinv) {
|
||||
DPRINT("inverted 3 - 4 not matching, got ");
|
||||
DPRINT(uint64ToString(p->Data0, 16));
|
||||
DPRINT(" vs ");
|
||||
DPRINTLN(uint64ToString(p->Data0Inv, 16));
|
||||
return false;
|
||||
}
|
||||
uint8_t d1invinv = ~p->Data1Inv;
|
||||
if (p->Data1 != d1invinv) {
|
||||
DPRINT("inverted 5 - 6 not matching, got ");
|
||||
DPRINT(uint64ToString(p->Data1, 16));
|
||||
DPRINT(" vs ");
|
||||
DPRINTLN(uint64ToString(p->Data1Inv, 16));
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/// Calculate and set the check values for the internal state.
|
||||
/// @param[in,out] data The array to be modified
|
||||
void IRCoronaAc::checksum(uint8_t* data) {
|
||||
CoronaProtocol *p = reinterpret_cast<CoronaProtocol*>(data);
|
||||
for (uint8_t i = 0; i < kCoronaAcSections; i++) {
|
||||
p->sections[i].Header0 = kCoronaAcSectionHeader0;
|
||||
p->sections[i].Header1 = kCoronaAcSectionHeader1;
|
||||
p->sections[i].Label = getSectionByte(i);
|
||||
p->sections[i].Data0Inv = ~p->sections[i].Data0;
|
||||
p->sections[i].Data1Inv = ~p->sections[i].Data1;
|
||||
}
|
||||
}
|
||||
|
||||
/// Set up hardware to be able to send a message.
|
||||
void IRCoronaAc::begin(void) { _irsend.begin(); }
|
||||
|
||||
#if SEND_CORONA_AC
|
||||
/// Send the current internal state as an IR message.
|
||||
/// @param[in] repeat Nr. of times the message will be repeated.
|
||||
void IRCoronaAc::send(const uint16_t repeat) {
|
||||
// if no timer, always send once without power press
|
||||
if (!getOnTimer() && !getOffTimer()) {
|
||||
setPowerButton(false);
|
||||
_irsend.sendCoronaAc(getRaw(), kCoronaAcStateLength, repeat);
|
||||
// and then with power press
|
||||
setPowerButton(true);
|
||||
}
|
||||
_irsend.sendCoronaAc(getRaw(), kCoronaAcStateLength, repeat);
|
||||
}
|
||||
#endif // SEND_CORONA_AC
|
||||
|
||||
/// Get a copy of the internal state as a valid code for this protocol.
|
||||
/// @return A Ptr to a valid code for this protocol based on the current
|
||||
/// internal state.
|
||||
/// @note To get stable AC state, if no timers, send once
|
||||
/// without PowerButton set, and once with
|
||||
uint8_t* IRCoronaAc::getRaw(void) {
|
||||
checksum(_.raw); // Ensure correct check bits before sending.
|
||||
return _.raw;
|
||||
}
|
||||
|
||||
/// Set the internal state from a valid code for this protocol.
|
||||
/// @param[in] new_code A valid state for this protocol.
|
||||
/// @param[in] length of the new_code array.
|
||||
void IRCoronaAc::setRaw(const uint8_t new_code[], const uint16_t length) {
|
||||
memcpy(_.raw, new_code, std::min(length, kCoronaAcStateLength));
|
||||
}
|
||||
|
||||
/// Set the temp in deg C.
|
||||
/// @param[in] temp The desired temperature in Celsius.
|
||||
void IRCoronaAc::setTemp(const uint8_t temp) {
|
||||
uint8_t degrees = std::max(temp, kCoronaAcMinTemp);
|
||||
degrees = std::min(degrees, kCoronaAcMaxTemp);
|
||||
_.Temp = degrees - kCoronaAcMinTemp + 1;
|
||||
}
|
||||
|
||||
/// Get the current temperature from the internal state.
|
||||
/// @return The current temperature in Celsius.
|
||||
uint8_t IRCoronaAc::getTemp(void) const {
|
||||
return _.Temp + kCoronaAcMinTemp - 1;
|
||||
}
|
||||
|
||||
/// Change the power setting. (in practice Standby, remote power)
|
||||
/// @param[in] on true, the setting is on. false, the setting is off.
|
||||
/// @note If changed, setPowerButton is also needed,
|
||||
/// unless timer is or was active
|
||||
void IRCoronaAc::setPower(const bool on) {
|
||||
_.Power = on;
|
||||
// setting power state resets timers that would cause the state
|
||||
if (on)
|
||||
setOnTimer(kCoronaAcTimerOff);
|
||||
else
|
||||
setOffTimer(kCoronaAcTimerOff);
|
||||
}
|
||||
|
||||
/// Get the current power setting. (in practice Standby, remote power)
|
||||
/// @return true, the setting is on. false, the setting is off.
|
||||
bool IRCoronaAc::getPower(void) const {
|
||||
return _.Power;
|
||||
}
|
||||
|
||||
/// Change the power button setting.
|
||||
/// @param[in] on true, the setting is on. false, the setting is off.
|
||||
/// @note this sets that the AC should set power,
|
||||
/// use setPower to define if the AC should end up as on or off
|
||||
/// When no timer is active, the below is a truth table
|
||||
/// With AC On, a command with setPower and setPowerButton gives nothing
|
||||
/// With AC On, a command with setPower but not setPowerButton is ok
|
||||
/// With AC Off, a command with setPower but not setPowerButton gives nothing
|
||||
/// With AC Off, a command with setPower and setPowerButton is ok
|
||||
void IRCoronaAc::setPowerButton(const bool on) {
|
||||
_.PowerButton = on;
|
||||
}
|
||||
|
||||
/// Get the value of the current power button setting.
|
||||
/// @return true, the setting is on. false, the setting is off.
|
||||
bool IRCoronaAc::getPowerButton(void) const {
|
||||
return _.PowerButton;
|
||||
}
|
||||
|
||||
/// Change the power setting to On.
|
||||
void IRCoronaAc::on(void) { setPower(true); }
|
||||
|
||||
/// Change the power setting to Off.
|
||||
void IRCoronaAc::off(void) { setPower(false); }
|
||||
|
||||
/// Get the operating mode setting of the A/C.
|
||||
/// @return The current operating mode setting.
|
||||
uint8_t IRCoronaAc::getMode(void) const {
|
||||
return _.Mode;
|
||||
}
|
||||
|
||||
/// Set the operating mode of the A/C.
|
||||
/// @param[in] mode The desired operating mode.
|
||||
void IRCoronaAc::setMode(const uint8_t mode) {
|
||||
switch (mode) {
|
||||
case kCoronaAcModeCool:
|
||||
case kCoronaAcModeDry:
|
||||
case kCoronaAcModeFan:
|
||||
case kCoronaAcModeHeat:
|
||||
_.Mode = mode;
|
||||
return;
|
||||
default:
|
||||
_.Mode = kCoronaAcModeCool;
|
||||
}
|
||||
}
|
||||
|
||||
/// Convert a standard A/C mode into its native mode.
|
||||
/// @param[in] mode A stdAc::opmode_t mode to be
|
||||
/// converted to it's native equivalent
|
||||
/// @return The corresponding native mode.
|
||||
uint8_t IRCoronaAc::convertMode(const stdAc::opmode_t mode) {
|
||||
switch (mode) {
|
||||
case stdAc::opmode_t::kFan: return kCoronaAcModeFan;
|
||||
case stdAc::opmode_t::kDry: return kCoronaAcModeDry;
|
||||
case stdAc::opmode_t::kHeat: return kCoronaAcModeHeat;
|
||||
default: return kCoronaAcModeCool;
|
||||
}
|
||||
}
|
||||
|
||||
/// Convert a native mode to it's common stdAc::opmode_t equivalent.
|
||||
/// @param[in] mode A native operation mode to be converted.
|
||||
/// @return The corresponding common stdAc::opmode_t mode.
|
||||
stdAc::opmode_t IRCoronaAc::toCommonMode(const uint8_t mode) {
|
||||
switch (mode) {
|
||||
case kCoronaAcModeFan: return stdAc::opmode_t::kFan;
|
||||
case kCoronaAcModeDry: return stdAc::opmode_t::kDry;
|
||||
case kCoronaAcModeHeat: return stdAc::opmode_t::kHeat;
|
||||
default: return stdAc::opmode_t::kCool;
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the operating speed of the A/C Fan
|
||||
/// @return The current operating fan speed setting
|
||||
uint8_t IRCoronaAc::getFan(void) const {
|
||||
return _.Fan;
|
||||
}
|
||||
|
||||
/// Set the operating speed of the A/C Fan
|
||||
/// @param[in] speed The desired fan speed
|
||||
void IRCoronaAc::setFan(const uint8_t speed) {
|
||||
if (speed > kCoronaAcFanHigh)
|
||||
_.Fan = kCoronaAcFanAuto;
|
||||
else
|
||||
_.Fan = speed;
|
||||
}
|
||||
|
||||
/// Change the powersave setting.
|
||||
/// @param[in] on true, the setting is on. false, the setting is off.
|
||||
void IRCoronaAc::setEcono(const bool on) {
|
||||
_.Econo = on;
|
||||
}
|
||||
|
||||
/// Get the value of the current powersave setting.
|
||||
/// @return true, the setting is on. false, the setting is off.
|
||||
bool IRCoronaAc::getEcono(void) const {
|
||||
return _.Econo;
|
||||
}
|
||||
|
||||
/// Convert a standard A/C Fan speed into its native fan speed.
|
||||
/// @param[in] speed The desired stdAc::fanspeed_t fan speed
|
||||
/// @return The given fan speed in native format
|
||||
uint8_t IRCoronaAc::convertFan(const stdAc::fanspeed_t speed) {
|
||||
switch (speed) {
|
||||
case stdAc::fanspeed_t::kMin:
|
||||
case stdAc::fanspeed_t::kLow: return kCoronaAcFanLow;
|
||||
case stdAc::fanspeed_t::kMedium: return kCoronaAcFanMedium;
|
||||
case stdAc::fanspeed_t::kHigh:
|
||||
case stdAc::fanspeed_t::kMax: return kCoronaAcFanHigh;
|
||||
default: return kCoronaAcFanAuto;
|
||||
}
|
||||
}
|
||||
|
||||
/// Convert a native fan speed to it's common equivalent.
|
||||
/// @param[in] speed The desired native fan speed
|
||||
/// @return The given fan speed in stdAc::fanspeed_t format
|
||||
stdAc::fanspeed_t IRCoronaAc::toCommonFanSpeed(const uint8_t speed) {
|
||||
switch (speed) {
|
||||
case kCoronaAcFanHigh: return stdAc::fanspeed_t::kHigh;
|
||||
case kCoronaAcFanMedium: return stdAc::fanspeed_t::kMedium;
|
||||
case kCoronaAcFanLow: return stdAc::fanspeed_t::kLow;
|
||||
default: return stdAc::fanspeed_t::kAuto;
|
||||
}
|
||||
}
|
||||
|
||||
/// Set the Vertical Swing toggle setting
|
||||
/// @param[in] on true, the setting is on. false, the setting is off.
|
||||
/// @note This is a button press, and not a state
|
||||
/// after sending it once you should turn it off
|
||||
void IRCoronaAc::setSwingVToggle(const bool on) {
|
||||
_.SwingVToggle = on;
|
||||
}
|
||||
|
||||
/// Get the Vertical Swing toggle setting
|
||||
/// @return true, the setting is on. false, the setting is off.
|
||||
bool IRCoronaAc::getSwingVToggle(void) const {
|
||||
return _.SwingVToggle;
|
||||
}
|
||||
|
||||
/// Set the Timer time
|
||||
/// @param[in] section index of section, used for offset.
|
||||
/// @param[in] nr_of_mins Number of minutes to set the timer to.
|
||||
/// (non in range value is disable).
|
||||
/// Valid is from 1 minute to 12 hours
|
||||
void IRCoronaAc::_setTimer(const uint8_t section, const uint16_t nr_of_mins) {
|
||||
// default to off
|
||||
uint16_t hsecs = kCoronaAcTimerOff;
|
||||
if (1 <= nr_of_mins && nr_of_mins <= kCoronaAcTimerMax)
|
||||
hsecs = nr_of_mins * kCoronaAcTimerUnitsPerMin;
|
||||
|
||||
// convert 16 bit value to separate 8 bit parts
|
||||
_.sections[section].Data1 = hsecs >> 8;
|
||||
_.sections[section].Data0 = hsecs;
|
||||
|
||||
// if any timer is enabled, then (remote) ac must be on (Standby)
|
||||
if (hsecs != kCoronaAcTimerOff) {
|
||||
_.Power = true;
|
||||
setPowerButton(false);
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the current Timer time
|
||||
/// @return The number of minutes it is set for. 0 means it's off.
|
||||
/// @note The A/C protocol supports 2 second increments
|
||||
uint16_t IRCoronaAc::_getTimer(const uint8_t section) const {
|
||||
// combine separate 8 bit parts to 16 bit value
|
||||
uint16_t hsecs = _.sections[section].Data1 << 8 |
|
||||
_.sections[section].Data0;
|
||||
|
||||
if (hsecs == kCoronaAcTimerOff)
|
||||
return 0;
|
||||
|
||||
return hsecs / kCoronaAcTimerUnitsPerMin;
|
||||
}
|
||||
|
||||
/// Get the current On Timer time
|
||||
/// @return The number of minutes it is set for. 0 means it's off.
|
||||
uint16_t IRCoronaAc::getOnTimer(void) const {
|
||||
return _getTimer(kCoronaAcOnTimerSection);
|
||||
}
|
||||
|
||||
/// Set the On Timer time
|
||||
/// @param[in] nr_of_mins Number of minutes to set the timer to.
|
||||
/// (0 or kCoronaAcTimerOff is disable).
|
||||
void IRCoronaAc::setOnTimer(const uint16_t nr_of_mins) {
|
||||
_setTimer(kCoronaAcOnTimerSection, nr_of_mins);
|
||||
// if we set a timer value, clear the other timer
|
||||
if (getOnTimer())
|
||||
setOffTimer(kCoronaAcTimerOff);
|
||||
}
|
||||
|
||||
/// Get the current Off Timer time
|
||||
/// @return The number of minutes it is set for. 0 means it's off.
|
||||
uint16_t IRCoronaAc::getOffTimer(void) const {
|
||||
return _getTimer(kCoronaAcOffTimerSection);
|
||||
}
|
||||
|
||||
/// Set the Off Timer time
|
||||
/// @param[in] nr_of_mins Number of minutes to set the timer to.
|
||||
/// (0 or kCoronaAcTimerOff is disable).
|
||||
void IRCoronaAc::setOffTimer(const uint16_t nr_of_mins) {
|
||||
_setTimer(kCoronaAcOffTimerSection, nr_of_mins);
|
||||
// if we set a timer value, clear the other timer
|
||||
if (getOffTimer())
|
||||
setOnTimer(kCoronaAcTimerOff);
|
||||
}
|
||||
|
||||
/// Convert the internal state into a human readable string.
|
||||
/// @return The current internal state expressed as a human readable String.
|
||||
String IRCoronaAc::toString(void) const {
|
||||
String result = "";
|
||||
result.reserve(140); // Reserve some heap for the string to reduce fragging.
|
||||
result += addBoolToString(_.Power, kPowerStr, false);
|
||||
result += addBoolToString(_.PowerButton, kPowerButtonStr);
|
||||
result += addModeToString(_.Mode, 0xFF, kCoronaAcModeCool,
|
||||
kCoronaAcModeHeat, kCoronaAcModeDry,
|
||||
kCoronaAcModeFan);
|
||||
result += addTempToString(getTemp());
|
||||
result += addFanToString(_.Fan, kCoronaAcFanHigh, kCoronaAcFanLow,
|
||||
kCoronaAcFanAuto, kCoronaAcFanAuto,
|
||||
kCoronaAcFanMedium);
|
||||
result += addBoolToString(_.SwingVToggle, kSwingVToggleStr);
|
||||
result += addBoolToString(_.Econo, kEconoStr);
|
||||
result += addLabeledString(getOnTimer()
|
||||
? minsToString(getOnTimer()) : kOffStr,
|
||||
kOnTimerStr);
|
||||
result += addLabeledString(getOffTimer()
|
||||
? minsToString(getOffTimer()) : kOffStr,
|
||||
kOffTimerStr);
|
||||
return result;
|
||||
}
|
||||
|
||||
/// Convert the A/C state to it's common stdAc::state_t equivalent.
|
||||
/// @return A stdAc::state_t state.
|
||||
stdAc::state_t IRCoronaAc::toCommon() const {
|
||||
stdAc::state_t result{};
|
||||
result.protocol = decode_type_t::CORONA_AC;
|
||||
result.model = -1; // No models used.
|
||||
result.power = _.Power;
|
||||
result.mode = toCommonMode(_.Mode);
|
||||
result.celsius = true;
|
||||
result.degrees = getTemp();
|
||||
result.fanspeed = toCommonFanSpeed(_.Fan);
|
||||
result.swingv = _.SwingVToggle ?
|
||||
stdAc::swingv_t::kAuto : stdAc::swingv_t::kOff;
|
||||
result.econo = _.Econo;
|
||||
// Not supported.
|
||||
result.sleep = -1;
|
||||
result.swingh = stdAc::swingh_t::kOff;
|
||||
result.turbo = false;
|
||||
result.quiet = false;
|
||||
result.clean = false;
|
||||
result.filter = false;
|
||||
result.beep = false;
|
||||
result.light = false;
|
||||
result.clock = -1;
|
||||
return result;
|
||||
}
|
||||
168
yoRadio/src/IRremoteESP8266/ir_Corona.h
Normal file
168
yoRadio/src/IRremoteESP8266/ir_Corona.h
Normal file
@@ -0,0 +1,168 @@
|
||||
// Corona A/C
|
||||
//
|
||||
// Copyright 2020 Christian Nilsson
|
||||
|
||||
// Supports:
|
||||
// Brand: Corona, Model: CSH-N2211 A/C
|
||||
// Brand: Corona, Model: CSH-N2511 A/C
|
||||
// Brand: Corona, Model: CSH-N2811 A/C
|
||||
// Brand: Corona, Model: CSH-N4011 A/C
|
||||
// Brand: Corona, Model: AR-01 remote
|
||||
//
|
||||
// Ref: https://docs.google.com/spreadsheets/d/1zzDEUQ52y7MZ7_xCU3pdjdqbRXOwZLsbTGvKWcicqCI/
|
||||
// Ref: https://www.corona.co.jp/box/download.php?id=145060636229
|
||||
|
||||
#ifndef IR_CORONA_H_
|
||||
#define IR_CORONA_H_
|
||||
|
||||
#define __STDC_LIMIT_MACROS
|
||||
#include <stdint.h>
|
||||
#ifndef UNIT_TEST
|
||||
#include <Arduino.h>
|
||||
#endif
|
||||
#include "IRremoteESP8266.h"
|
||||
#include "IRsend.h"
|
||||
#ifdef UNIT_TEST
|
||||
#include "IRsend_test.h"
|
||||
#endif
|
||||
|
||||
/// Native representation of a section of a Corona A/C message.
|
||||
struct CoronaSection {
|
||||
uint8_t Header0;
|
||||
uint8_t Header1;
|
||||
uint8_t Label;
|
||||
uint8_t Data0;
|
||||
uint8_t Data0Inv;
|
||||
uint8_t Data1;
|
||||
uint8_t Data1Inv;
|
||||
};
|
||||
|
||||
const uint8_t kCoronaAcSections = 3;
|
||||
|
||||
/// Native representation of a Corona A/C message.
|
||||
union CoronaProtocol {
|
||||
uint8_t raw[kCoronaAcStateLength]; ///< The state of the IR remote.
|
||||
CoronaSection sections[kCoronaAcSections];
|
||||
struct {
|
||||
// Byte 0
|
||||
uint8_t :8;
|
||||
// Byte 1
|
||||
uint8_t :8;
|
||||
// Byte 2
|
||||
uint8_t :8;
|
||||
// Byte 3
|
||||
uint8_t Fan :2;
|
||||
uint8_t :1;
|
||||
uint8_t Econo :1;
|
||||
uint8_t :1; // always on
|
||||
uint8_t :1;
|
||||
uint8_t SwingVToggle :1;
|
||||
uint8_t :1;
|
||||
// Byte 4
|
||||
uint8_t :8;
|
||||
// Byte 5
|
||||
uint8_t Temp :4;
|
||||
uint8_t Power :1;
|
||||
uint8_t PowerButton :1;
|
||||
uint8_t Mode :2;
|
||||
};
|
||||
};
|
||||
|
||||
// Constants
|
||||
|
||||
// CORONA_AC
|
||||
const uint8_t kCoronaAcSectionBytes = 7; // kCoronaAcStateLengthShort
|
||||
const uint8_t kCoronaAcSectionHeader0 = 0x28;
|
||||
const uint8_t kCoronaAcSectionHeader1 = 0x61;
|
||||
const uint8_t kCoronaAcSectionLabelBase = 0x0D; // 0b1101
|
||||
const uint8_t kCoronaAcSectionData0Base = 0x10; // D0 Pos 4 always on
|
||||
|
||||
const uint8_t kCoronaAcFanAuto = 0b00; // 0
|
||||
const uint8_t kCoronaAcFanLow = 0b01; // 1
|
||||
const uint8_t kCoronaAcFanMedium = 0b10; // 2
|
||||
const uint8_t kCoronaAcFanHigh = 0b11; // 3
|
||||
|
||||
/* full auto mode not supported by this code yet
|
||||
const uint8_t kCoronaAcAutoD0 = 0b00010100; // only combined with power save
|
||||
const uint8_t kCoronaAcAutoD1 = 0b10000011; // only combined with power
|
||||
*/
|
||||
const uint8_t kCoronaAcMinTemp = 17; // Celsius = 0b0001
|
||||
const uint8_t kCoronaAcMaxTemp = 30; // Celsius = 0b1110
|
||||
const uint8_t kCoronaAcModeHeat = 0b00; // 0
|
||||
const uint8_t kCoronaAcModeDry = 0b01; // 1
|
||||
const uint8_t kCoronaAcModeCool = 0b10; // 2
|
||||
const uint8_t kCoronaAcModeFan = 0b11; // 3
|
||||
|
||||
const uint8_t kCoronaAcSettingsSection = 0;
|
||||
const uint8_t kCoronaAcOnTimerSection = 1;
|
||||
const uint8_t kCoronaAcOffTimerSection = 2;
|
||||
const uint16_t kCoronaAcTimerMax = 12 * 60; // 12H in Minutes
|
||||
// Min value on remote is 1 hour, actual sent value can be 2 secs
|
||||
const uint16_t kCoronaAcTimerOff = 0xffff;
|
||||
const uint16_t kCoronaAcTimerUnitsPerMin = 30; // 30 units = 1 minute
|
||||
|
||||
// Classes
|
||||
|
||||
/// Class for handling detailed Corona A/C messages.
|
||||
class IRCoronaAc {
|
||||
public:
|
||||
explicit IRCoronaAc(const uint16_t pin, const bool inverted = false,
|
||||
const bool use_modulation = true);
|
||||
|
||||
void stateReset();
|
||||
#if SEND_CORONA_AC
|
||||
void send(const uint16_t repeat = kNoRepeat);
|
||||
/// 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_CORONA_AC
|
||||
void begin();
|
||||
static bool validSection(const uint8_t state[], const uint16_t pos,
|
||||
const uint8_t section);
|
||||
void setPower(const bool on);
|
||||
bool getPower(void) const;
|
||||
bool getPowerButton(void) const;
|
||||
void on(void);
|
||||
void off(void);
|
||||
void setTemp(const uint8_t temp);
|
||||
uint8_t getTemp(void) const;
|
||||
void setSwingVToggle(const bool on);
|
||||
bool getSwingVToggle(void) const;
|
||||
void setFan(const uint8_t speed);
|
||||
uint8_t getFan(void) const;
|
||||
void setMode(const uint8_t mode);
|
||||
uint8_t getMode(void) const;
|
||||
void setEcono(const bool on);
|
||||
bool getEcono(void) const;
|
||||
void setOnTimer(const uint16_t nr_of_mins);
|
||||
uint16_t getOnTimer(void) const;
|
||||
void setOffTimer(const uint16_t nr_of_mins);
|
||||
uint16_t getOffTimer(void) const;
|
||||
uint8_t* getRaw();
|
||||
void setRaw(const uint8_t new_code[],
|
||||
const uint16_t length = kCoronaAcStateLength);
|
||||
static uint8_t convertMode(const stdAc::opmode_t mode);
|
||||
static uint8_t convertFan(const stdAc::fanspeed_t speed);
|
||||
static stdAc::opmode_t toCommonMode(const uint8_t mode);
|
||||
static stdAc::fanspeed_t toCommonFanSpeed(const uint8_t speed);
|
||||
stdAc::state_t toCommon(void) const;
|
||||
String toString(void) const;
|
||||
#ifndef UNIT_TEST
|
||||
|
||||
private:
|
||||
IRsend _irsend; ///< Instance of the IR send class
|
||||
#else
|
||||
/// @cond IGNORE
|
||||
IRsendTest _irsend; ///< Instance of the testing IR send class
|
||||
/// @endcond
|
||||
#endif
|
||||
CoronaProtocol _;
|
||||
static uint8_t getSectionByte(const uint8_t section);
|
||||
static void checksum(uint8_t* data);
|
||||
void setPowerButton(const bool on);
|
||||
void _setTimer(const uint8_t section, const uint16_t nr_of_mins);
|
||||
uint16_t _getTimer(const uint8_t section) const;
|
||||
};
|
||||
#endif // IR_CORONA_H_
|
||||
3926
yoRadio/src/IRremoteESP8266/ir_Daikin.cpp
Normal file
3926
yoRadio/src/IRremoteESP8266/ir_Daikin.cpp
Normal file
File diff suppressed because it is too large
Load Diff
1263
yoRadio/src/IRremoteESP8266/ir_Daikin.h
Normal file
1263
yoRadio/src/IRremoteESP8266/ir_Daikin.h
Normal file
File diff suppressed because it is too large
Load Diff
470
yoRadio/src/IRremoteESP8266/ir_Delonghi.cpp
Normal file
470
yoRadio/src/IRremoteESP8266/ir_Delonghi.cpp
Normal file
@@ -0,0 +1,470 @@
|
||||
// Copyright 2020 David Conran
|
||||
/// @file
|
||||
/// @brief Delonghi based protocol.
|
||||
|
||||
#include "ir_Delonghi.h"
|
||||
#include "IRrecv.h"
|
||||
#include "IRsend.h"
|
||||
#include "IRtext.h"
|
||||
#include "IRutils.h"
|
||||
#include <algorithm>
|
||||
|
||||
using irutils::addBoolToString;
|
||||
using irutils::addModeToString;
|
||||
using irutils::addFanToString;
|
||||
using irutils::addLabeledString;
|
||||
using irutils::addTempToString;
|
||||
using irutils::minsToString;
|
||||
|
||||
const uint16_t kDelonghiAcHdrMark = 8984;
|
||||
const uint16_t kDelonghiAcBitMark = 572;
|
||||
const uint16_t kDelonghiAcHdrSpace = 4200;
|
||||
const uint16_t kDelonghiAcOneSpace = 1558;
|
||||
const uint16_t kDelonghiAcZeroSpace = 510;
|
||||
const uint32_t kDelonghiAcGap = kDefaultMessageGap; // A totally made-up guess.
|
||||
const uint16_t kDelonghiAcFreq = 38000; // Hz. (Guess: most common frequency.)
|
||||
const uint16_t kDelonghiAcOverhead = 3;
|
||||
|
||||
|
||||
#if SEND_DELONGHI_AC
|
||||
/// Send a Delonghi A/C formatted message.
|
||||
/// Status: STABLE / Reported as working on a real device.
|
||||
/// @param[in] data The message to be sent.
|
||||
/// @param[in] nbits The number of bits of message to be sent.
|
||||
/// @param[in] repeat The number of times the command is to be repeated.
|
||||
/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1096
|
||||
void IRsend::sendDelonghiAc(const uint64_t data, const uint16_t nbits,
|
||||
const uint16_t repeat) {
|
||||
sendGeneric(kDelonghiAcHdrMark, kDelonghiAcHdrSpace,
|
||||
kDelonghiAcBitMark, kDelonghiAcOneSpace,
|
||||
kDelonghiAcBitMark, kDelonghiAcZeroSpace,
|
||||
kDelonghiAcBitMark, kDelonghiAcGap,
|
||||
data, nbits, kDelonghiAcFreq, false, // LSB First.
|
||||
repeat, kDutyDefault);
|
||||
}
|
||||
#endif // SEND_DELONGHI_AC
|
||||
|
||||
#if DECODE_DELONGHI_AC
|
||||
/// Decode the supplied Delonghi A/C message.
|
||||
/// Status: STABLE / Expected to be working.
|
||||
/// @param[in,out] results Ptr to the data to decode & where to store the decode
|
||||
/// result.
|
||||
/// @param[in] offset The starting index to use when attempting to decode the
|
||||
/// raw data. Typically/Defaults to kStartOffset.
|
||||
/// @param[in] nbits The number of data bits to expect.
|
||||
/// @param[in] strict Flag indicating if we should perform strict matching.
|
||||
/// @return A boolean. True if it can decode it, false if it can't.
|
||||
/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1096
|
||||
bool IRrecv::decodeDelonghiAc(decode_results *results, uint16_t offset,
|
||||
const uint16_t nbits, const bool strict) {
|
||||
if (results->rawlen < 2 * nbits + kDelonghiAcOverhead - offset)
|
||||
return false; // Too short a message to match.
|
||||
if (strict && nbits != kDelonghiAcBits)
|
||||
return false;
|
||||
|
||||
uint64_t data = 0;
|
||||
|
||||
// Header + Data + Footer
|
||||
if (!matchGeneric(results->rawbuf + offset, &data,
|
||||
results->rawlen - offset, nbits,
|
||||
kDelonghiAcHdrMark, kDelonghiAcHdrSpace,
|
||||
kDelonghiAcBitMark, kDelonghiAcOneSpace,
|
||||
kDelonghiAcBitMark, kDelonghiAcZeroSpace,
|
||||
kDelonghiAcBitMark, kDelonghiAcGap, true,
|
||||
_tolerance, kMarkExcess, false)) return false;
|
||||
|
||||
// Compliance
|
||||
if (strict && !IRDelonghiAc::validChecksum(data)) return false;
|
||||
|
||||
// Success
|
||||
results->decode_type = decode_type_t::DELONGHI_AC;
|
||||
results->bits = nbits;
|
||||
results->value = data;
|
||||
results->command = 0;
|
||||
results->address = 0;
|
||||
return true;
|
||||
}
|
||||
#endif // DECODE_DELONGHI_AC
|
||||
|
||||
/// Class constructor.
|
||||
/// @param[in] pin GPIO to be used when sending.
|
||||
/// @param[in] inverted Is the output signal to be inverted?
|
||||
/// @param[in] use_modulation Is frequency modulation to be used?
|
||||
IRDelonghiAc::IRDelonghiAc(const uint16_t pin, const bool inverted,
|
||||
const bool use_modulation)
|
||||
: _irsend(pin, inverted, use_modulation) { stateReset(); }
|
||||
|
||||
/// Set up hardware to be able to send a message.
|
||||
void IRDelonghiAc::begin(void) { _irsend.begin(); }
|
||||
|
||||
#if SEND_DELONGHI_AC
|
||||
/// Send the current internal state as an IR message.
|
||||
/// @param[in] repeat Nr. of times the message will be repeated.
|
||||
void IRDelonghiAc::send(const uint16_t repeat) {
|
||||
_irsend.sendDelonghiAc(getRaw(), kDelonghiAcBits, repeat);
|
||||
}
|
||||
#endif // SEND_DELONGHI_AC
|
||||
|
||||
/// Calculate the checksum for a given state.
|
||||
/// @param[in] state The value to calc the checksum of.
|
||||
/// @return A valid checksum value.
|
||||
uint8_t IRDelonghiAc::calcChecksum(const uint64_t state) {
|
||||
uint8_t sum = 0;
|
||||
// Add up all the 8 bit chunks except for Most-significant 8 bits.
|
||||
for (uint8_t offset = 0; offset < kDelonghiAcChecksumOffset; offset += 8) {
|
||||
sum += GETBITS64(state, offset, 8);
|
||||
}
|
||||
return sum;
|
||||
}
|
||||
|
||||
/// Verify the checksum is valid for a given state.
|
||||
/// @param[in] state The state to verify the checksum of.
|
||||
/// @return true, if the state has a valid checksum. Otherwise, false.
|
||||
bool IRDelonghiAc::validChecksum(const uint64_t state) {
|
||||
DelonghiProtocol dp;
|
||||
dp.raw = state;
|
||||
return (dp.Sum == IRDelonghiAc::calcChecksum(state));
|
||||
}
|
||||
|
||||
/// Calculate and set the checksum values for the internal state.
|
||||
void IRDelonghiAc::checksum(void) {
|
||||
_.Sum = calcChecksum(_.raw);
|
||||
}
|
||||
|
||||
/// Reset the internal state to a fixed known good state.
|
||||
void IRDelonghiAc::stateReset(void) {
|
||||
_.raw = 0x5400000000000153;
|
||||
_saved_temp = 23; // DegC (Random reasonable default value)
|
||||
_saved_temp_units = 0; // Celsius
|
||||
}
|
||||
|
||||
/// Get a copy of the internal state as a valid code for this protocol.
|
||||
/// @return A valid code for this protocol based on the current internal state.
|
||||
uint64_t IRDelonghiAc::getRaw(void) {
|
||||
checksum(); // Ensure correct bit array before returning
|
||||
return _.raw;
|
||||
}
|
||||
|
||||
/// Set the internal state from a valid code for this protocol.
|
||||
/// @param[in] state A valid code for this protocol.
|
||||
void IRDelonghiAc::setRaw(const uint64_t state) { _.raw = state; }
|
||||
|
||||
/// Change the power setting to On.
|
||||
void IRDelonghiAc::on(void) { setPower(true); }
|
||||
|
||||
/// Change the power setting to Off.
|
||||
void IRDelonghiAc::off(void) { setPower(false); }
|
||||
|
||||
/// Change the power setting.
|
||||
/// @param[in] on true, the setting is on. false, the setting is off.
|
||||
void IRDelonghiAc::setPower(const bool on) {
|
||||
_.Power = on;
|
||||
}
|
||||
|
||||
/// Get the value of the current power setting.
|
||||
/// @return true, the setting is on. false, the setting is off.
|
||||
bool IRDelonghiAc::getPower(void) const {
|
||||
return _.Power;
|
||||
}
|
||||
|
||||
/// Change the temperature scale units.
|
||||
/// @param[in] fahrenheit true, use Fahrenheit. false, use Celsius.
|
||||
void IRDelonghiAc::setTempUnit(const bool fahrenheit) {
|
||||
_.Fahrenheit = fahrenheit;
|
||||
}
|
||||
|
||||
/// Get the temperature scale unit of measure currently in use.
|
||||
/// @return true, is Fahrenheit. false, is Celsius.
|
||||
bool IRDelonghiAc::getTempUnit(void) const {
|
||||
return _.Fahrenheit;
|
||||
}
|
||||
|
||||
/// Set the temperature.
|
||||
/// @param[in] degrees The temperature in degrees.
|
||||
/// @param[in] fahrenheit Use Fahrenheit as the temperature scale.
|
||||
/// @param[in] force Do we ignore any sanity checks?
|
||||
void IRDelonghiAc::setTemp(const uint8_t degrees, const bool fahrenheit,
|
||||
const bool force) {
|
||||
uint8_t temp;
|
||||
if (force) {
|
||||
temp = degrees; // We've been asked to force set this value.
|
||||
} else {
|
||||
uint8_t temp_min = kDelonghiAcTempMinC;
|
||||
uint8_t temp_max = kDelonghiAcTempMaxC;
|
||||
setTempUnit(fahrenheit);
|
||||
if (fahrenheit) {
|
||||
temp_min = kDelonghiAcTempMinF;
|
||||
temp_max = kDelonghiAcTempMaxF;
|
||||
}
|
||||
temp = std::max(temp_min, degrees);
|
||||
temp = std::min(temp_max, temp);
|
||||
_saved_temp = temp;
|
||||
_saved_temp_units = fahrenheit;
|
||||
temp = temp - temp_min + 1;
|
||||
}
|
||||
_.Temp = temp;
|
||||
}
|
||||
|
||||
/// Get the current temperature setting.
|
||||
/// @return The current setting for temp. in currently configured units/scale.
|
||||
uint8_t IRDelonghiAc::getTemp(void) const {
|
||||
return _.Temp + (_.Fahrenheit ? kDelonghiAcTempMinF
|
||||
: kDelonghiAcTempMinC) - 1;
|
||||
}
|
||||
|
||||
/// Set the speed of the fan.
|
||||
/// @param[in] speed The desired native setting.
|
||||
void IRDelonghiAc::setFan(const uint8_t speed) {
|
||||
// Mode fan speed rules.
|
||||
switch (_.Mode) {
|
||||
case kDelonghiAcFan:
|
||||
// Fan mode can't have auto fan speed.
|
||||
if (speed == kDelonghiAcFanAuto) {
|
||||
if (_.Fan == kDelonghiAcFanAuto) _.Fan = kDelonghiAcFanHigh;
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case kDelonghiAcAuto:
|
||||
case kDelonghiAcDry:
|
||||
// Auto & Dry modes only allows auto fan speed.
|
||||
if (speed != kDelonghiAcFanAuto) {
|
||||
_.Fan = kDelonghiAcFanAuto;
|
||||
return;
|
||||
}
|
||||
break;
|
||||
}
|
||||
// Bounds check enforcement
|
||||
if (speed > kDelonghiAcFanLow)
|
||||
_.Fan = kDelonghiAcFanAuto;
|
||||
else
|
||||
_.Fan = speed;
|
||||
}
|
||||
|
||||
/// Get the current native fan speed setting.
|
||||
/// @return The current fan speed.
|
||||
uint8_t IRDelonghiAc::getFan(void) const {
|
||||
return _.Fan;
|
||||
}
|
||||
|
||||
/// Convert a stdAc::fanspeed_t enum into it's native speed.
|
||||
/// @param[in] speed The enum to be converted.
|
||||
/// @return The native equivalent of the enum.
|
||||
uint8_t IRDelonghiAc::convertFan(const stdAc::fanspeed_t speed) {
|
||||
switch (speed) {
|
||||
case stdAc::fanspeed_t::kMin:
|
||||
case stdAc::fanspeed_t::kLow:
|
||||
return kDelonghiAcFanLow;
|
||||
case stdAc::fanspeed_t::kMedium:
|
||||
return kDelonghiAcFanMedium;
|
||||
case stdAc::fanspeed_t::kHigh:
|
||||
case stdAc::fanspeed_t::kMax:
|
||||
return kDelonghiAcFanHigh;
|
||||
default:
|
||||
return kDelonghiAcFanAuto;
|
||||
}
|
||||
}
|
||||
|
||||
/// Convert a native fan speed into its stdAc equivalent.
|
||||
/// @param[in] speed The native setting to be converted.
|
||||
/// @return The stdAc equivalent of the native setting.
|
||||
stdAc::fanspeed_t IRDelonghiAc::toCommonFanSpeed(const uint8_t speed) {
|
||||
switch (speed) {
|
||||
case kDelonghiAcFanHigh: return stdAc::fanspeed_t::kMax;
|
||||
case kDelonghiAcFanMedium: return stdAc::fanspeed_t::kMedium;
|
||||
case kDelonghiAcFanLow: return stdAc::fanspeed_t::kMin;
|
||||
default: return stdAc::fanspeed_t::kAuto;
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the operating mode setting of the A/C.
|
||||
/// @return The current operating mode setting.
|
||||
uint8_t IRDelonghiAc::getMode(void) const {
|
||||
return _.Mode;
|
||||
}
|
||||
|
||||
/// Set the operating mode of the A/C.
|
||||
/// @param[in] mode The desired native operating mode.
|
||||
void IRDelonghiAc::setMode(const uint8_t mode) {
|
||||
_.Mode = mode;
|
||||
switch (mode) {
|
||||
case kDelonghiAcAuto:
|
||||
case kDelonghiAcDry:
|
||||
// Set special temp for these modes.
|
||||
setTemp(kDelonghiAcTempAutoDryMode, _.Fahrenheit, true);
|
||||
break;
|
||||
case kDelonghiAcFan:
|
||||
// Set special temp for this mode.
|
||||
setTemp(kDelonghiAcTempFanMode, _.Fahrenheit, true);
|
||||
break;
|
||||
case kDelonghiAcCool:
|
||||
// Restore previous temp settings for cool mode.
|
||||
setTemp(_saved_temp, _saved_temp_units);
|
||||
break;
|
||||
default:
|
||||
_.Mode = kDelonghiAcAuto;
|
||||
setTemp(kDelonghiAcTempAutoDryMode, _.Fahrenheit, true);
|
||||
break;
|
||||
}
|
||||
setFan(_.Fan); // Re-force any fan speed constraints.
|
||||
}
|
||||
|
||||
/// Convert a stdAc::opmode_t enum into its native mode.
|
||||
/// @param[in] mode The enum to be converted.
|
||||
/// @return The native equivalent of the enum.
|
||||
uint8_t IRDelonghiAc::convertMode(const stdAc::opmode_t mode) {
|
||||
switch (mode) {
|
||||
case stdAc::opmode_t::kCool:
|
||||
return kDelonghiAcCool;
|
||||
case stdAc::opmode_t::kDry:
|
||||
return kDelonghiAcDry;
|
||||
case stdAc::opmode_t::kFan:
|
||||
return kDelonghiAcFan;
|
||||
default:
|
||||
return kDelonghiAcAuto;
|
||||
}
|
||||
}
|
||||
|
||||
/// Convert a native mode into its stdAc equivalent.
|
||||
/// @param[in] mode The native setting to be converted.
|
||||
/// @return The stdAc equivalent of the native setting.
|
||||
stdAc::opmode_t IRDelonghiAc::toCommonMode(const uint8_t mode) {
|
||||
switch (mode) {
|
||||
case kDelonghiAcCool: return stdAc::opmode_t::kCool;
|
||||
case kDelonghiAcDry: return stdAc::opmode_t::kDry;
|
||||
case kDelonghiAcFan: return stdAc::opmode_t::kFan;
|
||||
default: return stdAc::opmode_t::kAuto;
|
||||
}
|
||||
}
|
||||
|
||||
/// Set the Boost (Turbo) mode of the A/C.
|
||||
/// @param[in] on true, the setting is on. false, the setting is off.
|
||||
void IRDelonghiAc::setBoost(const bool on) {
|
||||
_.Boost = on;
|
||||
}
|
||||
|
||||
/// Get the Boost (Turbo) mode of the A/C.
|
||||
/// @return true, the setting is on. false, the setting is off.
|
||||
bool IRDelonghiAc::getBoost(void) const {
|
||||
return _.Boost;
|
||||
}
|
||||
|
||||
/// Set the Sleep mode of the A/C.
|
||||
/// @param[in] on true, the setting is on. false, the setting is off.
|
||||
void IRDelonghiAc::setSleep(const bool on) {
|
||||
_.Sleep = on;
|
||||
}
|
||||
|
||||
/// Get the Sleep mode status of the A/C.
|
||||
/// @return true, the setting is on. false, the setting is off.
|
||||
bool IRDelonghiAc::getSleep(void) const {
|
||||
return _.Sleep;
|
||||
}
|
||||
|
||||
/// Set the enable status of the On Timer.
|
||||
/// @param[in] on true, the setting is on. false, the setting is off.
|
||||
void IRDelonghiAc::setOnTimerEnabled(const bool on) {
|
||||
_.OnTimer = on;
|
||||
}
|
||||
|
||||
/// Get the enable status of the On Timer.
|
||||
/// @return true, the setting is on. false, the setting is off.
|
||||
bool IRDelonghiAc::getOnTimerEnabled(void) const {
|
||||
return _.OnTimer;
|
||||
}
|
||||
|
||||
/// Set the On timer to activate in nr of minutes.
|
||||
/// @param[in] nr_of_mins Total nr of mins to wait before waking the device.
|
||||
/// @note Max 23 hrs and 59 minutes. i.e. 1439 mins.
|
||||
void IRDelonghiAc::setOnTimer(const uint16_t nr_of_mins) {
|
||||
uint16_t value = std::min(kDelonghiAcTimerMax, nr_of_mins);
|
||||
_.OnMins = value % 60;
|
||||
_.OnHours = value / 60;
|
||||
// Enable or not?
|
||||
setOnTimerEnabled(value > 0);
|
||||
}
|
||||
|
||||
/// Get the On timer time.
|
||||
/// @return Total nr of mins before the device turns on.
|
||||
uint16_t IRDelonghiAc::getOnTimer(void) const {
|
||||
return _.OnHours * 60 + _.OnMins;
|
||||
}
|
||||
|
||||
/// Set the enable status of the Off Timer.
|
||||
/// @param[in] on true, the setting is on. false, the setting is off.
|
||||
void IRDelonghiAc::setOffTimerEnabled(const bool on) {
|
||||
_.OffTimer = on;
|
||||
}
|
||||
|
||||
/// Get the enable status of the Off Timer.
|
||||
/// @return true, the setting is on. false, the setting is off.
|
||||
bool IRDelonghiAc::getOffTimerEnabled(void) const {
|
||||
return _.OffTimer;
|
||||
}
|
||||
|
||||
/// Set the Off timer to activate in nr of minutes.
|
||||
/// @param[in] nr_of_mins Total nr of mins to wait before turning off the device
|
||||
/// @note Max 23 hrs and 59 minutes. i.e. 1439 mins.
|
||||
void IRDelonghiAc::setOffTimer(const uint16_t nr_of_mins) {
|
||||
uint16_t value = std::min(kDelonghiAcTimerMax, nr_of_mins);
|
||||
_.OffMins = value % 60;
|
||||
_.OffHours = value / 60;
|
||||
// Enable or not?
|
||||
setOffTimerEnabled(value > 0);
|
||||
}
|
||||
|
||||
/// Get the Off timer time.
|
||||
/// @return Total nr of mins before the device turns off.
|
||||
uint16_t IRDelonghiAc::getOffTimer(void) const {
|
||||
return _.OffHours * 60 + _.OffMins;
|
||||
}
|
||||
|
||||
/// Convert the current internal state into its stdAc::state_t equivalent.
|
||||
/// @return The stdAc equivalent of the native settings.
|
||||
stdAc::state_t IRDelonghiAc::toCommon(void) const {
|
||||
stdAc::state_t result{};
|
||||
result.protocol = decode_type_t::DELONGHI_AC;
|
||||
result.power = _.Power;
|
||||
// result.mode = toCommonMode(getMode());
|
||||
result.celsius = !_.Fahrenheit;
|
||||
result.degrees = getTemp();
|
||||
result.fanspeed = toCommonFanSpeed(_.Fan);
|
||||
result.turbo = _.Boost;
|
||||
result.sleep = _.Sleep ? 0 : -1;
|
||||
// Not supported.
|
||||
result.model = -1;
|
||||
result.swingv = stdAc::swingv_t::kOff;
|
||||
result.swingh = stdAc::swingh_t::kOff;
|
||||
result.light = false;
|
||||
result.filter = false;
|
||||
result.econo = false;
|
||||
result.quiet = false;
|
||||
result.clean = false;
|
||||
result.beep = false;
|
||||
result.clock = -1;
|
||||
return result;
|
||||
}
|
||||
|
||||
/// Convert the current internal state into a human readable string.
|
||||
/// @return A human readable string.
|
||||
String IRDelonghiAc::toString(void) const {
|
||||
String result = "";
|
||||
result.reserve(80); // Reserve some heap for the string to reduce fragging.
|
||||
result += addBoolToString(_.Power, kPowerStr, false);
|
||||
result += addModeToString(_.Mode, kDelonghiAcAuto, kDelonghiAcCool,
|
||||
kDelonghiAcAuto, kDelonghiAcDry, kDelonghiAcFan);
|
||||
result += addFanToString(_.Fan, kDelonghiAcFanHigh, kDelonghiAcFanLow,
|
||||
kDelonghiAcFanAuto, kDelonghiAcFanAuto,
|
||||
kDelonghiAcFanMedium);
|
||||
result += addTempToString(getTemp(), !_.Fahrenheit);
|
||||
result += addBoolToString(_.Boost, kTurboStr);
|
||||
result += addBoolToString(_.Sleep, kSleepStr);
|
||||
uint16_t mins = getOnTimer();
|
||||
result += addLabeledString((mins && _.OnTimer) ? minsToString(mins)
|
||||
: kOffStr,
|
||||
kOnTimerStr);
|
||||
mins = getOffTimer();
|
||||
result += addLabeledString((mins && _.OffTimer) ? minsToString(mins)
|
||||
: kOffStr,
|
||||
kOffTimerStr);
|
||||
return result;
|
||||
}
|
||||
136
yoRadio/src/IRremoteESP8266/ir_Delonghi.h
Normal file
136
yoRadio/src/IRremoteESP8266/ir_Delonghi.h
Normal file
@@ -0,0 +1,136 @@
|
||||
// Copyright 2020 David Conran
|
||||
|
||||
/// @file
|
||||
/// @brief Delonghi A/C
|
||||
/// @note Kudos to TheMaxxz For the breakdown and mapping of the bit values.
|
||||
/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1096
|
||||
|
||||
// Supports:
|
||||
// Brand: Delonghi, Model: PAC A95
|
||||
|
||||
#ifndef IR_DELONGHI_H_
|
||||
#define IR_DELONGHI_H_
|
||||
|
||||
#define __STDC_LIMIT_MACROS
|
||||
#include <stdint.h>
|
||||
#ifndef UNIT_TEST
|
||||
#include <Arduino.h>
|
||||
#endif
|
||||
#include "IRremoteESP8266.h"
|
||||
#include "IRsend.h"
|
||||
#ifdef UNIT_TEST
|
||||
#include "IRsend_test.h"
|
||||
#endif
|
||||
|
||||
/// Native representation of a Delonghi A/C message.
|
||||
union DelonghiProtocol{
|
||||
uint64_t raw; ///< The state of the IR remote.
|
||||
struct {
|
||||
uint8_t :8; // Header
|
||||
uint8_t Temp :5;
|
||||
uint8_t Fan :2;
|
||||
uint8_t Fahrenheit:1;
|
||||
uint8_t Power :1;
|
||||
uint8_t Mode :3;
|
||||
uint8_t Boost :1;
|
||||
uint8_t Sleep :1;
|
||||
uint8_t :2;
|
||||
uint8_t OnTimer :1;
|
||||
uint8_t OnHours :5;
|
||||
uint8_t :2;
|
||||
uint8_t OnMins :6;
|
||||
uint8_t :2;
|
||||
uint8_t OffTimer :1;
|
||||
uint8_t OffHours :5;
|
||||
uint8_t :2;
|
||||
uint8_t OffMins :6;
|
||||
uint8_t :2;
|
||||
uint8_t Sum :8;
|
||||
};
|
||||
};
|
||||
|
||||
// Constants
|
||||
const uint8_t kDelonghiAcTempMinC = 18; // Deg C
|
||||
const uint8_t kDelonghiAcTempMaxC = 32; // Deg C
|
||||
const uint8_t kDelonghiAcTempMinF = 64; // Deg F
|
||||
const uint8_t kDelonghiAcTempMaxF = 90; // Deg F
|
||||
const uint8_t kDelonghiAcTempAutoDryMode = 0;
|
||||
const uint8_t kDelonghiAcTempFanMode = 0b00110;
|
||||
const uint8_t kDelonghiAcFanAuto = 0b00;
|
||||
const uint8_t kDelonghiAcFanHigh = 0b01;
|
||||
const uint8_t kDelonghiAcFanMedium = 0b10;
|
||||
const uint8_t kDelonghiAcFanLow = 0b11;
|
||||
const uint8_t kDelonghiAcCool = 0b000;
|
||||
const uint8_t kDelonghiAcDry = 0b001;
|
||||
const uint8_t kDelonghiAcFan = 0b010;
|
||||
const uint8_t kDelonghiAcAuto = 0b100;
|
||||
const uint16_t kDelonghiAcTimerMax = 23 * 60 + 59;
|
||||
const uint8_t kDelonghiAcChecksumOffset = 56;
|
||||
|
||||
// Classes
|
||||
|
||||
/// Class for handling detailed Delonghi A/C messages.
|
||||
class IRDelonghiAc {
|
||||
public:
|
||||
explicit IRDelonghiAc(const uint16_t pin, const bool inverted = false,
|
||||
const bool use_modulation = true);
|
||||
void stateReset(void);
|
||||
#if SEND_DELONGHI_AC
|
||||
void send(const uint16_t repeat = kDelonghiAcDefaultRepeat);
|
||||
/// 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_DELONGHI_AC
|
||||
void begin(void);
|
||||
static uint8_t calcChecksum(const uint64_t state);
|
||||
static bool validChecksum(const uint64_t state);
|
||||
void setPower(const bool on);
|
||||
bool getPower(void) const;
|
||||
void on(void);
|
||||
void off(void);
|
||||
void setTempUnit(const bool celsius);
|
||||
bool getTempUnit(void) const;
|
||||
void setTemp(const uint8_t temp, const bool fahrenheit = false,
|
||||
const bool force = false);
|
||||
uint8_t getTemp(void) const;
|
||||
void setFan(const uint8_t speed);
|
||||
uint8_t getFan(void) const;
|
||||
void setMode(const uint8_t mode);
|
||||
uint8_t getMode(void) const;
|
||||
void setBoost(const bool on); // Aka Turbo
|
||||
bool getBoost(void) const; // Aka Turbo
|
||||
void setSleep(const bool on);
|
||||
bool getSleep(void) const;
|
||||
void setOnTimerEnabled(const bool on);
|
||||
bool getOnTimerEnabled(void) const;
|
||||
void setOnTimer(const uint16_t nr_of_mins);
|
||||
uint16_t getOnTimer(void) const;
|
||||
void setOffTimerEnabled(const bool on);
|
||||
bool getOffTimerEnabled(void) const;
|
||||
void setOffTimer(const uint16_t nr_of_mins);
|
||||
uint16_t getOffTimer(void) const;
|
||||
uint64_t getRaw(void);
|
||||
void setRaw(const uint64_t state);
|
||||
static uint8_t convertMode(const stdAc::opmode_t mode);
|
||||
static uint8_t convertFan(const stdAc::fanspeed_t speed);
|
||||
static stdAc::opmode_t toCommonMode(const uint8_t mode);
|
||||
static stdAc::fanspeed_t toCommonFanSpeed(const uint8_t speed);
|
||||
stdAc::state_t toCommon(void) const;
|
||||
String toString(void) const;
|
||||
#ifndef UNIT_TEST
|
||||
|
||||
private:
|
||||
IRsend _irsend; ///< instance of the IR send class
|
||||
#else
|
||||
/// @cond IGNORE
|
||||
IRsendTest _irsend; ///< instance of the testing IR send class
|
||||
/// @endcond
|
||||
#endif
|
||||
DelonghiProtocol _;
|
||||
uint8_t _saved_temp; ///< The previously user requested temp value.
|
||||
uint8_t _saved_temp_units; ///< The previously user requested temp units.
|
||||
void checksum(void);
|
||||
};
|
||||
#endif // IR_DELONGHI_H_
|
||||
122
yoRadio/src/IRremoteESP8266/ir_Denon.cpp
Normal file
122
yoRadio/src/IRremoteESP8266/ir_Denon.cpp
Normal file
@@ -0,0 +1,122 @@
|
||||
// Copyright 2016 Massimiliano Pinto
|
||||
// Copyright 2017 David Conran
|
||||
/// @file
|
||||
/// @brief Denon support
|
||||
/// Original Denon support added by https://github.com/csBlueChip
|
||||
/// Ported over by Massimiliano Pinto
|
||||
/// @see https://github.com/z3t0/Arduino-IRremote/blob/master/ir_Denon.cpp
|
||||
/// @see http://assets.denon.com/documentmaster/us/denon%20master%20ir%20hex.xls
|
||||
|
||||
// Supports:
|
||||
// Brand: Denon, Model: AVR-3801 A/V Receiver (probably)
|
||||
|
||||
#include <algorithm>
|
||||
#include "IRrecv.h"
|
||||
#include "IRsend.h"
|
||||
#include "IRutils.h"
|
||||
|
||||
|
||||
// Constants
|
||||
const uint16_t kDenonTick = 263;
|
||||
const uint16_t kDenonHdrMarkTicks = 1;
|
||||
const uint16_t kDenonHdrMark = kDenonHdrMarkTicks * kDenonTick;
|
||||
const uint16_t kDenonHdrSpaceTicks = 3;
|
||||
const uint16_t kDenonHdrSpace = kDenonHdrSpaceTicks * kDenonTick;
|
||||
const uint16_t kDenonBitMarkTicks = 1;
|
||||
const uint16_t kDenonBitMark = kDenonBitMarkTicks * kDenonTick;
|
||||
const uint16_t kDenonOneSpaceTicks = 7;
|
||||
const uint16_t kDenonOneSpace = kDenonOneSpaceTicks * kDenonTick;
|
||||
const uint16_t kDenonZeroSpaceTicks = 3;
|
||||
const uint16_t kDenonZeroSpace = kDenonZeroSpaceTicks * kDenonTick;
|
||||
const uint16_t kDenonMinCommandLengthTicks = 510;
|
||||
const uint16_t kDenonMinGapTicks =
|
||||
kDenonMinCommandLengthTicks -
|
||||
(kDenonHdrMarkTicks + kDenonHdrSpaceTicks +
|
||||
kDenonBits * (kDenonBitMarkTicks + kDenonOneSpaceTicks) +
|
||||
kDenonBitMarkTicks);
|
||||
const uint32_t kDenonMinGap = kDenonMinGapTicks * kDenonTick;
|
||||
const uint64_t kDenonManufacturer = 0x2A4CULL;
|
||||
|
||||
#if SEND_DENON
|
||||
/// Send a Denon formatted message.
|
||||
/// Status: STABLE / Should be working.
|
||||
/// @param[in] data The message to be sent.
|
||||
/// @param[in] nbits The number of bits of message to be sent.
|
||||
/// @param[in] repeat The number of times the command is to be repeated.
|
||||
/// @note Some Denon devices use a Kaseikyo/Panasonic 48-bit format
|
||||
/// Others use the Sharp protocol.
|
||||
void IRsend::sendDenon(uint64_t data, uint16_t nbits, uint16_t repeat) {
|
||||
if (nbits >= kPanasonicBits) // Is this really Panasonic?
|
||||
sendPanasonic64(data, nbits, repeat);
|
||||
else if (nbits == kDenonLegacyBits)
|
||||
// Support legacy (broken) calls of sendDenon().
|
||||
sendSharpRaw(data & (~0x2000ULL), nbits + 1, repeat);
|
||||
else
|
||||
sendSharpRaw(data, nbits, repeat);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if DECODE_DENON
|
||||
/// Decode the supplied Delonghi A/C message.
|
||||
/// Status: STABLE / Should work fine.
|
||||
/// @param[in,out] results Ptr to the data to decode & where to store the decode
|
||||
/// result.
|
||||
/// @param[in] offset The starting index to use when attempting to decode the
|
||||
/// raw data. Typically/Defaults to kStartOffset.
|
||||
/// @param[in] nbits The number of data bits to expect.
|
||||
/// @param[in] strict Flag indicating if we should perform strict matching.
|
||||
/// @return A boolean. True if it can decode it, false if it can't.
|
||||
/// @see https://github.com/z3t0/Arduino-IRremote/blob/master/ir_Denon.cpp
|
||||
bool IRrecv::decodeDenon(decode_results *results, uint16_t offset,
|
||||
const uint16_t nbits, const bool strict) {
|
||||
// Compliance
|
||||
if (strict) {
|
||||
switch (nbits) {
|
||||
case kDenonBits:
|
||||
case kDenon48Bits:
|
||||
case kDenonLegacyBits:
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Denon uses the Sharp & Panasonic(Kaseikyo) protocol for some
|
||||
// devices, so check for those first.
|
||||
// It is not exactly like Sharp's protocols, but close enough.
|
||||
// e.g. The expansion bit is not set for Denon vs. set for Sharp.
|
||||
// Ditto for Panasonic, it's the same except for a different
|
||||
// manufacturer code.
|
||||
|
||||
if (!decodeSharp(results, offset, nbits, true, false) &&
|
||||
!decodePanasonic(results, offset, nbits, true, kDenonManufacturer)) {
|
||||
// We couldn't decode it as expected, so try the old legacy method.
|
||||
// NOTE: I don't think this following protocol actually exists.
|
||||
// Looks like a partial version of the Sharp protocol.
|
||||
if (strict && nbits != kDenonLegacyBits) return false;
|
||||
|
||||
uint64_t data = 0;
|
||||
|
||||
// Match Header + Data + Footer
|
||||
if (!matchGeneric(results->rawbuf + offset, &data,
|
||||
results->rawlen - offset, nbits,
|
||||
kDenonHdrMark, kDenonHdrSpace,
|
||||
kDenonBitMark, kDenonOneSpace,
|
||||
kDenonBitMark, kDenonZeroSpace,
|
||||
kDenonBitMark, 0, false)) return false;
|
||||
|
||||
// Success
|
||||
results->bits = nbits;
|
||||
results->value = data;
|
||||
results->address = 0;
|
||||
results->command = 0;
|
||||
} // Legacy decode.
|
||||
|
||||
// Compliance
|
||||
if (strict && nbits != results->bits) return false;
|
||||
|
||||
// Success
|
||||
results->decode_type = DENON;
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
103
yoRadio/src/IRremoteESP8266/ir_Dish.cpp
Normal file
103
yoRadio/src/IRremoteESP8266/ir_Dish.cpp
Normal file
@@ -0,0 +1,103 @@
|
||||
// Copyright Todd Treece
|
||||
// Copyright 2017 David Conran
|
||||
/// @file
|
||||
/// @brief DISH Network protocol support
|
||||
/// DISH support originally by Todd Treece
|
||||
/// @see http://unionbridge.org/design/ircommand
|
||||
/// @see https://github.com/marcosamarinho/IRremoteESP8266/blob/master/ir_Dish.cpp
|
||||
/// @see http://www.hifi-remote.com/wiki/index.php?title=Dish
|
||||
|
||||
// Supports:
|
||||
// Brand: DISH NETWORK, Model: echostar 301
|
||||
|
||||
#include "IRrecv.h"
|
||||
#include "IRsend.h"
|
||||
#include "IRutils.h"
|
||||
|
||||
|
||||
// Constants
|
||||
const uint16_t kDishTick = 100;
|
||||
const uint16_t kDishHdrMarkTicks = 4;
|
||||
const uint16_t kDishHdrMark = kDishHdrMarkTicks * kDishTick;
|
||||
const uint16_t kDishHdrSpaceTicks = 61;
|
||||
const uint16_t kDishHdrSpace = kDishHdrSpaceTicks * kDishTick;
|
||||
const uint16_t kDishBitMarkTicks = 4;
|
||||
const uint16_t kDishBitMark = kDishBitMarkTicks * kDishTick;
|
||||
const uint16_t kDishOneSpaceTicks = 17;
|
||||
const uint16_t kDishOneSpace = kDishOneSpaceTicks * kDishTick;
|
||||
const uint16_t kDishZeroSpaceTicks = 28;
|
||||
const uint16_t kDishZeroSpace = kDishZeroSpaceTicks * kDishTick;
|
||||
const uint16_t kDishRptSpaceTicks = kDishHdrSpaceTicks;
|
||||
const uint16_t kDishRptSpace = kDishRptSpaceTicks * kDishTick;
|
||||
|
||||
#if SEND_DISH
|
||||
/// Send a DISH NETWORK formatted message.
|
||||
/// Status: STABLE / Working.
|
||||
/// @param[in] data The message to be sent.
|
||||
/// @param[in] nbits The number of bits of message to be sent.
|
||||
/// @param[in] repeat The number of times the command is to be repeated.
|
||||
/// @note Dishplayer is a different protocol.
|
||||
/// Typically a DISH device needs to get a command a total of at least 4
|
||||
/// times to accept it. e.g. repeat=3
|
||||
///
|
||||
/// Here is the LIRC file I found that seems to match the remote codes from the
|
||||
/// oscilloscope:
|
||||
/// DISH NETWORK (echostar 301):
|
||||
/// @see http://lirc.sourceforge.net/remotes/echostar/301_501_3100_5100_58xx_59xx
|
||||
/// @see http://www.hifi-remote.com/wiki/index.php?title=Dish
|
||||
void IRsend::sendDISH(uint64_t data, uint16_t nbits, uint16_t repeat) {
|
||||
enableIROut(57600); // Set modulation freq. to 57.6kHz.
|
||||
// Header is only ever sent once.
|
||||
mark(kDishHdrMark);
|
||||
space(kDishHdrSpace);
|
||||
|
||||
sendGeneric(0, 0, // No headers from here on in.
|
||||
kDishBitMark, kDishOneSpace, kDishBitMark, kDishZeroSpace,
|
||||
kDishBitMark, kDishRptSpace, data, nbits, 57600, true, repeat,
|
||||
50);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if DECODE_DISH
|
||||
/// Decode the supplied DISH NETWORK message.
|
||||
/// Status: ALPHA (untested and unconfirmed.)
|
||||
/// @param[in,out] results Ptr to the data to decode & where to store the decode
|
||||
/// result.
|
||||
/// @param[in] offset The starting index to use when attempting to decode the
|
||||
/// raw data. Typically/Defaults to kStartOffset.
|
||||
/// @param[in] nbits The number of data bits to expect.
|
||||
/// @param[in] strict Flag indicating if we should perform strict matching.
|
||||
/// @return A boolean. True if it can decode it, false if it can't.
|
||||
/// @note Dishplayer is a different protocol.
|
||||
/// Typically a DISH device needs to get a command a total of at least 4
|
||||
/// times to accept it.
|
||||
/// @see http://www.hifi-remote.com/wiki/index.php?title=Dish
|
||||
/// @see http://lirc.sourceforge.net/remotes/echostar/301_501_3100_5100_58xx_59xx
|
||||
/// @see https://github.com/marcosamarinho/IRremoteESP8266/blob/master/ir_Dish.cpp
|
||||
bool IRrecv::decodeDISH(decode_results *results, uint16_t offset,
|
||||
const uint16_t nbits, const bool strict) {
|
||||
if (strict && nbits != kDishBits) return false; // Not strictly compliant.
|
||||
|
||||
uint64_t data = 0;
|
||||
|
||||
// Match Header + Data + Footer
|
||||
if (!matchGeneric(results->rawbuf + offset, &data,
|
||||
results->rawlen - offset, nbits,
|
||||
kDishHdrMark, kDishHdrSpace,
|
||||
kDishBitMark, kDishOneSpace,
|
||||
kDishBitMark, kDishZeroSpace,
|
||||
kDishBitMark,
|
||||
// The DISH protocol calls for a repeated message, so
|
||||
// strictly speaking there should be a code following this.
|
||||
// Only require it if we are set to strict matching.
|
||||
strict ? kDishRptSpace : 0, false)) return false;
|
||||
|
||||
// Success
|
||||
results->decode_type = DISH;
|
||||
results->bits = nbits;
|
||||
results->value = data;
|
||||
results->address = 0;
|
||||
results->command = 0;
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
124
yoRadio/src/IRremoteESP8266/ir_Doshisha.cpp
Normal file
124
yoRadio/src/IRremoteESP8266/ir_Doshisha.cpp
Normal file
@@ -0,0 +1,124 @@
|
||||
// Copyright 2020 Christian (nikize)
|
||||
/// @file
|
||||
/// @brief Doshisha protocol support
|
||||
/// @see https://www.doshisha-led.com/
|
||||
|
||||
// Supports:
|
||||
// Brand: Doshisha, Model: CZ-S32D LED Light
|
||||
// Brand: Doshisha, Model: CZ-S38D LED Light
|
||||
// Brand: Doshisha, Model: CZ-S50D LED Light
|
||||
// Brand: Doshisha, Model: RCZ01 remote
|
||||
|
||||
#include "IRrecv.h"
|
||||
#include "IRsend.h"
|
||||
#include "IRutils.h"
|
||||
|
||||
|
||||
const uint16_t kDoshishaHdrMark = 3412;
|
||||
const uint16_t kDoshishaHdrSpace = 1722;
|
||||
const uint16_t kDoshishaBitMark = 420;
|
||||
const uint16_t kDoshishaOneSpace = 1310;
|
||||
const uint16_t kDoshishaZeroSpace = 452;
|
||||
|
||||
// basic structure of bits, and mask
|
||||
const uint64_t kRcz01SignatureMask = 0xffffffff00;
|
||||
const uint64_t kRcz01Signature = 0x800B304800;
|
||||
const uint8_t kRcz01CommandMask = 0xFE;
|
||||
const uint8_t kRcz01ChannelMask = 0x01;
|
||||
|
||||
// Known commands - Here for documentation rather than actual usage
|
||||
const uint8_t kRcz01CommandSwitchChannel = 0xD2;
|
||||
const uint8_t kRcz01CommandTimmer60 = 0x52;
|
||||
const uint8_t kRcz01CommandTimmer30 = 0x92;
|
||||
const uint8_t kRcz01CommandOff = 0xA0;
|
||||
|
||||
const uint8_t kRcz01CommandLevelDown = 0x2C;
|
||||
const uint8_t kRcz01CommandLevelUp = 0xCC;
|
||||
// below are the only ones that turns it on
|
||||
const uint8_t kRcz01CommandLevel1 = 0xA4;
|
||||
const uint8_t kRcz01CommandLevel2 = 0x24;
|
||||
const uint8_t kRcz01CommandLevel3 = 0xC4;
|
||||
const uint8_t kRcz01CommandLevel4 = 0xD0;
|
||||
|
||||
const uint8_t kRcz01CommandOn = 0xC0;
|
||||
const uint8_t kRcz01CommandNightLight = 0xC8;
|
||||
// end Known commands
|
||||
|
||||
#if SEND_DOSHISHA
|
||||
/// Send a Doshisha formatted message.
|
||||
/// Status: STABLE / Works on real device.
|
||||
/// @param[in] data The message to be sent.
|
||||
/// @param[in] nbits The number of bits of message to be sent.
|
||||
/// @param[in] repeat The number of times the command is to be repeated.
|
||||
void IRsend::sendDoshisha(const uint64_t data, const uint16_t nbits,
|
||||
const uint16_t repeat) {
|
||||
sendGeneric(kDoshishaHdrMark, kDoshishaHdrSpace,
|
||||
kDoshishaBitMark, kDoshishaOneSpace,
|
||||
kDoshishaBitMark, kDoshishaZeroSpace,
|
||||
kDoshishaBitMark, kDefaultMessageGap,
|
||||
data, nbits, 38, true, repeat, kDutyDefault);
|
||||
}
|
||||
|
||||
/// Encode Doshisha combining constant values with command and channel.
|
||||
/// Status: STABLE / Working.
|
||||
/// @param[in] command The command code to be sent.
|
||||
/// @param[in] channel The one bit channel 0 for CH1 and 1 for CH2
|
||||
/// @return The corresponding Doshisha code.
|
||||
uint64_t IRsend::encodeDoshisha(const uint8_t command, const uint8_t channel) {
|
||||
uint64_t data = kRcz01Signature |
|
||||
(command & kRcz01CommandMask) |
|
||||
(channel & kRcz01ChannelMask);
|
||||
return data;
|
||||
}
|
||||
#endif // SEND_DOSHISHA
|
||||
|
||||
#if DECODE_DOSHISHA
|
||||
/// Decode the supplied Doshisha message.
|
||||
/// Status: STABLE / Works on real device.
|
||||
/// @param[in,out] results Ptr to the data to decode & where to store the decode
|
||||
/// result.
|
||||
/// @param[in] offset The starting index to use when attempting to decode the
|
||||
/// raw data. Typically/Defaults to kStartOffset.
|
||||
/// @param[in] nbits The number of data bits to expect.
|
||||
/// @param[in] strict Flag indicating if we should perform strict matching.
|
||||
/// @return A boolean. True if it can decode it, false if it can't.
|
||||
bool IRrecv::decodeDoshisha(decode_results *results, uint16_t offset,
|
||||
const uint16_t nbits, const bool strict) {
|
||||
if (results->rawlen < 2 * nbits + kHeader + kFooter - 1 + offset)
|
||||
return false; // Can't possibly be a valid message.
|
||||
if (strict && nbits != kDoshishaBits)
|
||||
return false;
|
||||
|
||||
uint64_t data = 0;
|
||||
// Match Header + Data
|
||||
if (!matchGeneric(results->rawbuf + offset, &data,
|
||||
results->rawlen - offset, nbits,
|
||||
kDoshishaHdrMark, kDoshishaHdrSpace,
|
||||
kDoshishaBitMark, kDoshishaOneSpace,
|
||||
kDoshishaBitMark, kDoshishaZeroSpace,
|
||||
kDoshishaBitMark, 0,
|
||||
true, kTolerance, kMarkExcess, true)) return false;
|
||||
|
||||
// e.g. data = 0x800B3048C0, nbits = 40
|
||||
|
||||
// RCZ01 remote commands starts with a lead bit set
|
||||
if ((data & kRcz01SignatureMask) != kRcz01Signature) {
|
||||
DPRINT(" decodeDoshisha data ");
|
||||
DPRINT(uint64ToString(data, 16));
|
||||
DPRINT(" masked ");
|
||||
DPRINT(uint64ToString(data & kRcz01SignatureMask, 16));
|
||||
DPRINT(" not matching ");
|
||||
DPRINT(uint64ToString(kRcz01Signature, 16));
|
||||
DPRINTLN(" .");
|
||||
return false; // expected lead bits not matching
|
||||
}
|
||||
|
||||
// Success
|
||||
results->decode_type = decode_type_t::DOSHISHA;
|
||||
results->bits = nbits;
|
||||
results->value = data;
|
||||
results->command = data & kRcz01CommandMask;
|
||||
results->address = data & kRcz01ChannelMask;
|
||||
return true;
|
||||
}
|
||||
#endif // DECODE_DOSHISHA
|
||||
424
yoRadio/src/IRremoteESP8266/ir_Ecoclim.cpp
Normal file
424
yoRadio/src/IRremoteESP8266/ir_Ecoclim.cpp
Normal file
@@ -0,0 +1,424 @@
|
||||
// Copyright 2021 David Conran
|
||||
|
||||
/// @file
|
||||
/// @brief EcoClim A/C protocol.
|
||||
/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1397
|
||||
|
||||
#include "ir_Ecoclim.h"
|
||||
#include <algorithm>
|
||||
#include <cstring>
|
||||
#include "IRac.h"
|
||||
#include "IRrecv.h"
|
||||
#include "IRsend.h"
|
||||
#include "IRtext.h"
|
||||
#include "IRutils.h"
|
||||
|
||||
// Constants
|
||||
const uint8_t kEcoclimSections = 3;
|
||||
const uint8_t kEcoclimExtraTolerance = 5; ///< Percentage (extra)
|
||||
const uint16_t kEcoclimHdrMark = 5730; ///< uSeconds
|
||||
const uint16_t kEcoclimHdrSpace = 1935; ///< uSeconds
|
||||
const uint16_t kEcoclimBitMark = 440; ///< uSeconds
|
||||
const uint16_t kEcoclimOneSpace = 1739; ///< uSeconds
|
||||
const uint16_t kEcoclimZeroSpace = 637; ///< uSeconds
|
||||
const uint16_t kEcoclimFooterMark = 7820; ///< uSeconds
|
||||
const uint32_t kEcoclimGap = kDefaultMessageGap; // Just a guess.
|
||||
|
||||
using irutils::addBoolToString;
|
||||
using irutils::addFanToString;
|
||||
using irutils::addIntToString;
|
||||
using irutils::addLabeledString;
|
||||
using irutils::addModeToString;
|
||||
using irutils::addTempToString;
|
||||
using irutils::minsToString;
|
||||
|
||||
#if SEND_ECOCLIM
|
||||
/// Send a EcoClim A/C formatted message.
|
||||
/// Status: STABLE / Confirmed working on real device.
|
||||
/// @param[in] data The message to be sent.
|
||||
/// @param[in] nbits The number of bits of message to be sent.
|
||||
/// @param[in] repeat The number of times the command is to be repeated.
|
||||
void IRsend::sendEcoclim(const uint64_t data, const uint16_t nbits,
|
||||
const uint16_t repeat) {
|
||||
enableIROut(38, kDutyDefault);
|
||||
for (uint16_t r = 0; r <= repeat; r++) {
|
||||
for (uint8_t section = 0; section < kEcoclimSections; section++) {
|
||||
// Header + Data
|
||||
sendGeneric(kEcoclimHdrMark, kEcoclimHdrSpace,
|
||||
kEcoclimBitMark, kEcoclimOneSpace,
|
||||
kEcoclimBitMark, kEcoclimZeroSpace,
|
||||
0, 0, data, nbits, 38, true, 0, kDutyDefault);
|
||||
}
|
||||
mark(kEcoclimFooterMark);
|
||||
space(kEcoclimGap);
|
||||
}
|
||||
}
|
||||
#endif // SEND_ECOCLIM
|
||||
|
||||
#if DECODE_ECOCLIM
|
||||
/// Decode the supplied EcoClim A/C message.
|
||||
/// Status: STABLE / Confirmed working on real remote.
|
||||
/// @param[in,out] results Ptr to the data to decode & where to store the decode
|
||||
/// result.
|
||||
/// @param[in] offset The starting index to use when attempting to decode the
|
||||
/// raw data. Typically/Defaults to kStartOffset.
|
||||
/// @param[in] nbits The number of data bits to expect.
|
||||
/// @param[in] strict Flag indicating if we should perform strict matching.
|
||||
/// @return A boolean. True if it can decode it, false if it can't.
|
||||
bool IRrecv::decodeEcoclim(decode_results *results, uint16_t offset,
|
||||
const uint16_t nbits, const bool strict) {
|
||||
if (results->rawlen < (2 * nbits + kHeader) * kEcoclimSections +
|
||||
kFooter - 1 + offset)
|
||||
return false; // Can't possibly be a valid Ecoclim message.
|
||||
if (strict) {
|
||||
switch (nbits) {
|
||||
case kEcoclimShortBits:
|
||||
case kEcoclimBits:
|
||||
break;
|
||||
default:
|
||||
return false; // Unexpected bit size.
|
||||
}
|
||||
}
|
||||
|
||||
for (uint8_t section = 0; section < kEcoclimSections; section++) {
|
||||
uint16_t used;
|
||||
uint64_t data;
|
||||
// Header + Data Block
|
||||
used = matchGeneric(results->rawbuf + offset, &data,
|
||||
results->rawlen - offset, nbits,
|
||||
kEcoclimHdrMark, kEcoclimHdrSpace,
|
||||
kEcoclimBitMark, kEcoclimOneSpace,
|
||||
kEcoclimBitMark, kEcoclimZeroSpace,
|
||||
0, 0, // No footer.
|
||||
false, _tolerance + kEcoclimExtraTolerance);
|
||||
if (!used) return false;
|
||||
DPRINTLN("DEBUG: Data section matched okay.");
|
||||
offset += used;
|
||||
// Compliance
|
||||
if (strict) {
|
||||
if (section) { // Each section should contain the same data.
|
||||
if (data != results->value) return false;
|
||||
} else {
|
||||
results->value = data;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Footer
|
||||
if (!matchMark(results->rawbuf[offset++], kEcoclimFooterMark,
|
||||
_tolerance + kEcoclimExtraTolerance))
|
||||
return false;
|
||||
if (results->rawlen <= offset && !matchAtLeast(results->rawbuf[offset++],
|
||||
kEcoclimGap))
|
||||
return false;
|
||||
// Success
|
||||
results->bits = nbits;
|
||||
results->decode_type = ECOCLIM;
|
||||
// No need to record the value as we stored it as we decoded it.
|
||||
return true;
|
||||
}
|
||||
#endif // DECODE_ECOCLIM
|
||||
|
||||
/// Class constructor.
|
||||
/// @param[in] pin GPIO to be used when sending.
|
||||
/// @param[in] inverted Is the output signal to be inverted?
|
||||
/// @param[in] use_modulation Is frequency modulation to be used?
|
||||
IREcoclimAc::IREcoclimAc(const uint16_t pin, const bool inverted,
|
||||
const bool use_modulation)
|
||||
: _irsend(pin, inverted, use_modulation) { stateReset(); }
|
||||
|
||||
/// Reset the internal state to a fixed known good state.
|
||||
void IREcoclimAc::stateReset(void) { _.raw = kEcoclimDefaultState; }
|
||||
|
||||
/// Set up hardware to be able to send a message.
|
||||
void IREcoclimAc::begin(void) { _irsend.begin(); }
|
||||
|
||||
#if SEND_ECOCLIM
|
||||
/// Send the current internal state as an IR message.
|
||||
/// @param[in] repeat Nr. of times the message will be repeated.
|
||||
void IREcoclimAc::send(const uint16_t repeat) {
|
||||
_irsend.sendEcoclim(getRaw(), kEcoclimBits, repeat);
|
||||
}
|
||||
#endif // SEND_ECOCLIM
|
||||
|
||||
/// Get a copy of the internal state as a valid code for this protocol.
|
||||
/// @return A valid code for this protocol based on the current internal state.
|
||||
uint64_t IREcoclimAc::getRaw(void) const { return _.raw; }
|
||||
|
||||
/// Set the internal state from a valid code for this protocol.
|
||||
/// @param[in] new_code A valid code for this protocol.
|
||||
void IREcoclimAc::setRaw(const uint64_t new_code) { _.raw = new_code; }
|
||||
|
||||
/// Set the temperature.
|
||||
/// @param[in] celsius The temperature in degrees celsius.
|
||||
void IREcoclimAc::setTemp(const uint8_t celsius) {
|
||||
// Range check.
|
||||
uint8_t temp = std::min(celsius, kEcoclimTempMax);
|
||||
temp = std::max(temp, kEcoclimTempMin);
|
||||
_.Temp = temp - kEcoclimTempMin;
|
||||
}
|
||||
|
||||
/// Get the current temperature setting.
|
||||
/// @return The current setting for temp. in degrees celsius.
|
||||
uint8_t IREcoclimAc::getTemp(void) const { return _.Temp + kEcoclimTempMin; }
|
||||
|
||||
/// Set the sensor temperature.
|
||||
/// @param[in] celsius The temperature in degrees celsius.
|
||||
void IREcoclimAc::setSensorTemp(const uint8_t celsius) {
|
||||
// Range check.
|
||||
uint8_t temp = std::min(celsius, kEcoclimTempMax);
|
||||
temp = std::max(temp, kEcoclimTempMin);
|
||||
_.SensorTemp = temp - kEcoclimTempMin;
|
||||
}
|
||||
|
||||
/// Get the sensor temperature setting.
|
||||
/// @return The current setting for sensor temp. in degrees celsius.
|
||||
uint8_t IREcoclimAc::getSensorTemp(void) const {
|
||||
return _.SensorTemp + kEcoclimTempMin;
|
||||
}
|
||||
|
||||
/// Get the value of the current power setting.
|
||||
/// @return true, the setting is on. false, the setting is off.
|
||||
bool IREcoclimAc::getPower(void) const { return _.Power; }
|
||||
|
||||
/// Change the power setting.
|
||||
/// @param[in] on true, the setting is on. false, the setting is off.
|
||||
void IREcoclimAc::setPower(const bool on) { _.Power = on; }
|
||||
|
||||
/// Change the power setting to On.
|
||||
void IREcoclimAc::on(void) { setPower(true); }
|
||||
|
||||
/// Change the power setting to Off.
|
||||
void IREcoclimAc::off(void) { setPower(false); }
|
||||
|
||||
/// Get the current fan speed setting.
|
||||
/// @return The current fan speed.
|
||||
uint8_t IREcoclimAc::getFan(void) const { return _.Fan; }
|
||||
|
||||
/// Set the speed of the fan.
|
||||
/// @param[in] speed The desired setting.
|
||||
void IREcoclimAc::setFan(const uint8_t speed) {
|
||||
_.Fan = std::min(speed, kEcoclimFanAuto);
|
||||
}
|
||||
|
||||
/// Convert a stdAc::fanspeed_t enum into it's native speed.
|
||||
/// @param[in] speed The enum to be converted.
|
||||
/// @return The native equivalent of the enum.
|
||||
uint8_t IREcoclimAc::convertFan(const stdAc::fanspeed_t speed) {
|
||||
switch (speed) {
|
||||
case stdAc::fanspeed_t::kMin:
|
||||
case stdAc::fanspeed_t::kLow: return kEcoclimFanMin;
|
||||
case stdAc::fanspeed_t::kMedium: return kEcoclimFanMed;
|
||||
case stdAc::fanspeed_t::kHigh:
|
||||
case stdAc::fanspeed_t::kMax: return kEcoclimFanMax;
|
||||
default: return kCoolixFanAuto;
|
||||
}
|
||||
}
|
||||
|
||||
/// Convert a native fan speed into its stdAc equivalent.
|
||||
/// @param[in] speed The native setting to be converted.
|
||||
/// @return The stdAc equivalent of the native setting.
|
||||
stdAc::fanspeed_t IREcoclimAc::toCommonFanSpeed(const uint8_t speed) {
|
||||
switch (speed) {
|
||||
case kEcoclimFanMax: return stdAc::fanspeed_t::kMax;
|
||||
case kEcoclimFanMed: return stdAc::fanspeed_t::kMedium;
|
||||
case kEcoclimFanMin: return stdAc::fanspeed_t::kMin;
|
||||
default: return stdAc::fanspeed_t::kAuto;
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the operating mode setting of the A/C.
|
||||
/// @return The current operating mode setting.
|
||||
uint8_t IREcoclimAc::getMode(void) const { return _.Mode; }
|
||||
|
||||
/// Set the operating mode of the A/C.
|
||||
/// @param[in] mode The desired operating mode.
|
||||
void IREcoclimAc::setMode(const uint8_t mode) {
|
||||
switch (mode) {
|
||||
case kEcoclimAuto:
|
||||
case kEcoclimCool:
|
||||
case kEcoclimDry:
|
||||
case kEcoclimRecycle:
|
||||
case kEcoclimFan:
|
||||
case kEcoclimHeat:
|
||||
case kEcoclimSleep:
|
||||
_.Mode = mode;
|
||||
break;
|
||||
default: // Anything else, go with Auto mode.
|
||||
setMode(kEcoclimAuto);
|
||||
}
|
||||
}
|
||||
|
||||
/// Convert a standard A/C mode into its native mode.
|
||||
/// @param[in] mode A stdAc::opmode_t to be converted to it's native equivalent.
|
||||
/// @return The corresponding native mode.
|
||||
uint8_t IREcoclimAc::convertMode(const stdAc::opmode_t mode) {
|
||||
switch (mode) {
|
||||
case stdAc::opmode_t::kCool: return kEcoclimCool;
|
||||
case stdAc::opmode_t::kHeat: return kEcoclimHeat;
|
||||
case stdAc::opmode_t::kDry: return kEcoclimDry;
|
||||
case stdAc::opmode_t::kFan: return kEcoclimFan;
|
||||
default: return kEcoclimAuto;
|
||||
}
|
||||
}
|
||||
|
||||
/// Convert a native mode to it's common stdAc::opmode_t equivalent.
|
||||
/// @param[in] mode A native operation mode to be converted.
|
||||
/// @return The corresponding common stdAc::opmode_t mode.
|
||||
stdAc::opmode_t IREcoclimAc::toCommonMode(const uint8_t mode) {
|
||||
switch (mode) {
|
||||
case kEcoclimCool: return stdAc::opmode_t::kCool;
|
||||
case kEcoclimHeat: return stdAc::opmode_t::kHeat;
|
||||
case kEcoclimDry: return stdAc::opmode_t::kDry;
|
||||
case kEcoclimFan: return stdAc::opmode_t::kFan;
|
||||
default: return stdAc::opmode_t::kAuto;
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the clock time of the A/C unit.
|
||||
/// @return Nr. of minutes past midnight.
|
||||
uint16_t IREcoclimAc::getClock(void) const { return _.Clock; }
|
||||
|
||||
/// Set the clock time on the A/C unit.
|
||||
/// @param[in] nr_of_mins Nr. of minutes past midnight.
|
||||
void IREcoclimAc::setClock(const uint16_t nr_of_mins) {
|
||||
_.Clock = std::min(nr_of_mins, (uint16_t)(24 * 60 - 1));
|
||||
}
|
||||
|
||||
/// Get the Unit type/DIP switch settings of the remote.
|
||||
/// @return The binary representation of the 4 DIP switches on the remote.
|
||||
uint8_t IREcoclimAc::getType(void) const { return _.DipConfig; }
|
||||
|
||||
/// Set the Unit type/DIP switch settings for the remote.
|
||||
/// @param[in] code The binary representation of the remote's 4 DIP switches.
|
||||
void IREcoclimAc::setType(const uint8_t code) {
|
||||
switch (code) {
|
||||
case kEcoclimDipMaster:
|
||||
case kEcoclimDipSlave:
|
||||
_.DipConfig = code;
|
||||
break;
|
||||
default:
|
||||
setType(kEcoclimDipMaster);
|
||||
}
|
||||
}
|
||||
|
||||
/// Set & enable the On Timer for the A/C.
|
||||
/// @param[in] nr_of_mins The time, in minutes since midnight.
|
||||
void IREcoclimAc::setOnTimer(const uint16_t nr_of_mins) {
|
||||
if (nr_of_mins < 24 * 60) {
|
||||
_.OnHours = nr_of_mins / 60;
|
||||
_.OnTenMins = (nr_of_mins % 60) / 10; // Store in tens of mins resolution.
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the On Timer for the A/C.
|
||||
/// @return The On Time, in minutes since midnight.
|
||||
uint16_t IREcoclimAc::getOnTimer(void) const {
|
||||
return _.OnHours * 60 + _.OnTenMins * 10;
|
||||
}
|
||||
|
||||
/// Check if the On Timer is enabled.
|
||||
/// @return true, if the timer is enabled, otherwise false.
|
||||
bool IREcoclimAc::isOnTimerEnabled(void) const {
|
||||
return (getOnTimer() != kEcoclimTimerDisable);
|
||||
}
|
||||
|
||||
/// Disable & clear the On Timer.
|
||||
void IREcoclimAc::disableOnTimer(void) {
|
||||
_.OnHours = 0x1F;
|
||||
_.OnTenMins = 0x7;
|
||||
}
|
||||
|
||||
/// Set & enable the Off Timer for the A/C.
|
||||
/// @param[in] nr_of_mins The time, in minutes since midnight.
|
||||
void IREcoclimAc::setOffTimer(const uint16_t nr_of_mins) {
|
||||
if (nr_of_mins < 24 * 60) {
|
||||
_.OffHours = nr_of_mins / 60;
|
||||
_.OffTenMins = (nr_of_mins % 60) / 10; // Store in tens of mins resolution.
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the Off Timer for the A/C.
|
||||
/// @return The Off Time, in minutes since midnight.
|
||||
uint16_t IREcoclimAc::getOffTimer(void) const {
|
||||
return _.OffHours * 60 + _.OffTenMins * 10;
|
||||
}
|
||||
|
||||
/// Check if the Off Timer is enabled.
|
||||
/// @return true, if the timer is enabled, otherwise false.
|
||||
bool IREcoclimAc::isOffTimerEnabled(void) const {
|
||||
return (getOffTimer() != kEcoclimTimerDisable);
|
||||
}
|
||||
|
||||
/// Disable & clear the Off Timer.
|
||||
void IREcoclimAc::disableOffTimer(void) {
|
||||
_.OffHours = 0x1F;
|
||||
_.OffTenMins = 0x7;
|
||||
}
|
||||
|
||||
/// Convert the current internal state into its stdAc::state_t equivalent.
|
||||
/// @return The stdAc equivalent of the native settings.
|
||||
stdAc::state_t IREcoclimAc::toCommon(void) const {
|
||||
stdAc::state_t result{};
|
||||
result.protocol = decode_type_t::ECOCLIM;
|
||||
result.power = _.Power;
|
||||
result.mode = toCommonMode(getMode());
|
||||
result.celsius = true;
|
||||
result.degrees = getTemp();
|
||||
result.sensorTemperature = getSensorTemp();
|
||||
result.fanspeed = toCommonFanSpeed(_.Fan);
|
||||
result.sleep = (getMode() == kEcoclimSleep) ? 0 : -1;
|
||||
result.clock = getClock();
|
||||
// Not supported.
|
||||
result.model = -1;
|
||||
result.turbo = false;
|
||||
result.swingv = stdAc::swingv_t::kOff;
|
||||
result.swingh = stdAc::swingh_t::kOff;
|
||||
result.light = false;
|
||||
result.filter = false;
|
||||
result.econo = false;
|
||||
result.quiet = false;
|
||||
result.clean = false;
|
||||
result.beep = false;
|
||||
return result;
|
||||
}
|
||||
|
||||
/// Convert the internal state into a human readable string.
|
||||
/// @return A string containing the settings in human-readable form.
|
||||
String IREcoclimAc::toString(void) const {
|
||||
String result = "";
|
||||
result.reserve(140); // Reserve some heap for the string to reduce fragging.
|
||||
result += addBoolToString(_.Power, kPowerStr, false);
|
||||
// Custom Mode output as this protocol has Recycle and Sleep as modes.
|
||||
result += addIntToString(_.Mode, kModeStr);
|
||||
result += kSpaceLBraceStr;
|
||||
switch (_.Mode) {
|
||||
case kEcoclimAuto: result += kAutoStr; break;
|
||||
case kEcoclimCool: result += kCoolStr; break;
|
||||
case kEcoclimHeat: result += kHeatStr; break;
|
||||
case kEcoclimDry: result += kDryStr; break;
|
||||
case kEcoclimFan: result += kFanStr; break;
|
||||
case kEcoclimRecycle: result += kRecycleStr; break;
|
||||
case kEcoclimSleep: result += kSleepStr; break;
|
||||
default: result += kUnknownStr;
|
||||
}
|
||||
result += ')';
|
||||
result += addTempToString(getTemp());
|
||||
result += kCommaSpaceStr;
|
||||
result += kSensorStr;
|
||||
result += addTempToString(getSensorTemp(), true, false);
|
||||
result += addFanToString(_.Fan, kEcoclimFanMax,
|
||||
kEcoclimFanMin,
|
||||
kEcoclimFanAuto,
|
||||
kEcoclimFanAuto, // Unused (No Quiet)
|
||||
kEcoclimFanMed,
|
||||
kEcoclimFanMax);
|
||||
result += addLabeledString(minsToString(_.Clock), kClockStr);
|
||||
result += addLabeledString(
|
||||
isOnTimerEnabled() ? minsToString(getOnTimer()) : kOffStr, kOnTimerStr);
|
||||
result += addLabeledString(
|
||||
isOffTimerEnabled() ? minsToString(getOffTimer()) : kOffStr,
|
||||
kOffTimerStr);
|
||||
result += addIntToString(_.DipConfig, kTypeStr);
|
||||
return result;
|
||||
}
|
||||
142
yoRadio/src/IRremoteESP8266/ir_Ecoclim.h
Normal file
142
yoRadio/src/IRremoteESP8266/ir_Ecoclim.h
Normal file
@@ -0,0 +1,142 @@
|
||||
// Copyright 2021 David Conran
|
||||
|
||||
/// @file
|
||||
/// @brief EcoClim A/C protocol.
|
||||
/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1397
|
||||
|
||||
// Supports:
|
||||
// Brand: EcoClim, Model: HYSFR-P348 remote
|
||||
// Brand: EcoClim, Model: ZC200DPO A/C
|
||||
|
||||
#ifndef IR_ECOCLIM_H_
|
||||
#define IR_ECOCLIM_H_
|
||||
|
||||
#define __STDC_LIMIT_MACROS
|
||||
#include <stdint.h>
|
||||
#ifndef UNIT_TEST
|
||||
#include <Arduino.h>
|
||||
#endif
|
||||
#include "IRremoteESP8266.h"
|
||||
#include "IRsend.h"
|
||||
#ifdef UNIT_TEST
|
||||
#include "IRsend_test.h"
|
||||
#endif
|
||||
|
||||
// Constants
|
||||
// Modes
|
||||
const uint8_t kEcoclimAuto = 0b000; ///< 0. a.k.a Slave
|
||||
const uint8_t kEcoclimCool = 0b001; ///< 1
|
||||
const uint8_t kEcoclimDry = 0b010; ///< 2
|
||||
const uint8_t kEcoclimRecycle = 0b011; ///< 3
|
||||
const uint8_t kEcoclimFan = 0b100; ///< 4
|
||||
const uint8_t kEcoclimHeat = 0b101; ///< 5
|
||||
const uint8_t kEcoclimSleep = 0b111; ///< 7
|
||||
// Fan Control
|
||||
const uint8_t kEcoclimFanMin = 0b00; ///< 0
|
||||
const uint8_t kEcoclimFanMed = 0b01; ///< 1
|
||||
const uint8_t kEcoclimFanMax = 0b10; ///< 2
|
||||
const uint8_t kEcoclimFanAuto = 0b11; ///< 3
|
||||
// DIP settings
|
||||
const uint8_t kEcoclimDipMaster = 0b0000;
|
||||
const uint8_t kEcoclimDipSlave = 0b0111;
|
||||
// Temperature
|
||||
const uint8_t kEcoclimTempMin = 5; // Celsius
|
||||
const uint8_t kEcoclimTempMax = kEcoclimTempMin + 31; // Celsius
|
||||
// Timer
|
||||
const uint16_t kEcoclimTimerDisable = 0x1F * 60 + 7 * 10; // 4774
|
||||
|
||||
// Power: Off, Mode: Auto, Temp: 11C, Sensor: 22C, Fan: Auto, Clock: 00:00
|
||||
const uint64_t kEcoclimDefaultState = 0x11063000FFFF02;
|
||||
|
||||
/// Native representation of a Ecoclim A/C message.
|
||||
union EcoclimProtocol {
|
||||
uint64_t raw; ///< The state in IR code form.
|
||||
struct { // Only 56 bits (7 bytes are used.
|
||||
// Byte
|
||||
uint64_t :3; ///< Fixed 0b010
|
||||
uint64_t :1; ///< Unknown
|
||||
uint64_t DipConfig :4; ///< 0b0000 = Master, 0b0111 = Slave
|
||||
// Byte
|
||||
uint64_t OffTenMins :3; ///< Off Timer minutes (in tens of mins)
|
||||
uint64_t OffHours :5; ///< Off Timer nr of Hours
|
||||
// Byte
|
||||
uint64_t OnTenMins :3; ///< On Timer minutes (in tens of mins)
|
||||
uint64_t OnHours :5; ///< On Timer nr of Hours
|
||||
// Byte+Byte
|
||||
uint64_t Clock :11;
|
||||
uint64_t :1; ///< Unknown
|
||||
uint64_t Fan :2; ///< Fan Speed
|
||||
uint64_t Power :1; ///< Power control
|
||||
uint64_t Clear :1; // Not sure what this is
|
||||
// Byte
|
||||
uint64_t Temp :5; ///< Desired Temperature (Celsius)
|
||||
uint64_t Mode :3; ///< Operating Mode
|
||||
// Byte
|
||||
uint64_t SensorTemp :5; ///< Sensed Temperature (Celsius)
|
||||
uint64_t :3; ///< Fixed
|
||||
};
|
||||
};
|
||||
|
||||
// Classes
|
||||
|
||||
/// Class for handling detailed EcoClim A/C 56 bit messages.
|
||||
/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1397
|
||||
class IREcoclimAc {
|
||||
public:
|
||||
explicit IREcoclimAc(const uint16_t pin, const bool inverted = false,
|
||||
const bool use_modulation = true);
|
||||
void stateReset(void);
|
||||
#if SEND_ECOCLIM
|
||||
void send(const uint16_t repeat = kNoRepeat);
|
||||
/// 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_ECOCLIM
|
||||
void begin(void);
|
||||
void on(void);
|
||||
void off(void);
|
||||
void setPower(const bool on);
|
||||
bool getPower(void) const;
|
||||
void setTemp(const uint8_t celsius);
|
||||
uint8_t getTemp(void) const;
|
||||
void setSensorTemp(const uint8_t celsius);
|
||||
uint8_t getSensorTemp(void) const;
|
||||
void setFan(const uint8_t speed);
|
||||
uint8_t getFan(void) const;
|
||||
void setMode(const uint8_t mode);
|
||||
uint8_t getMode(void) const;
|
||||
void setClock(const uint16_t nr_of_mins);
|
||||
uint16_t getClock(void) const;
|
||||
uint64_t getRaw(void) const;
|
||||
void setRaw(const uint64_t new_code);
|
||||
void setType(const uint8_t code);
|
||||
uint8_t getType(void) const;
|
||||
static uint8_t convertMode(const stdAc::opmode_t mode);
|
||||
static uint8_t convertFan(const stdAc::fanspeed_t speed);
|
||||
static stdAc::opmode_t toCommonMode(const uint8_t mode);
|
||||
static stdAc::fanspeed_t toCommonFanSpeed(const uint8_t speed);
|
||||
stdAc::state_t toCommon(void) const;
|
||||
void setOnTimer(const uint16_t nr_of_mins);
|
||||
uint16_t getOnTimer(void) const;
|
||||
bool isOnTimerEnabled(void) const;
|
||||
void disableOnTimer(void);
|
||||
void setOffTimer(const uint16_t nr_of_mins);
|
||||
uint16_t getOffTimer(void) const;
|
||||
bool isOffTimerEnabled(void) const;
|
||||
void disableOffTimer(void);
|
||||
String toString(void) const;
|
||||
#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
|
||||
EcoclimProtocol _; ///< The state of the IR remote in IR code form.
|
||||
};
|
||||
|
||||
#endif // IR_ECOCLIM_H_
|
||||
457
yoRadio/src/IRremoteESP8266/ir_Electra.cpp
Normal file
457
yoRadio/src/IRremoteESP8266/ir_Electra.cpp
Normal file
@@ -0,0 +1,457 @@
|
||||
// Copyright 2018-2021 David Conran
|
||||
/// @file
|
||||
/// @brief Support for Electra A/C protocols.
|
||||
/// @see https://github.com/ToniA/arduino-heatpumpir/blob/master/AUXHeatpumpIR.cpp
|
||||
/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/527
|
||||
/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/642
|
||||
/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/778
|
||||
|
||||
#include "ir_Electra.h"
|
||||
#include <algorithm>
|
||||
#include <cstring>
|
||||
#include "IRrecv.h"
|
||||
#include "IRsend.h"
|
||||
#include "IRtext.h"
|
||||
#include "IRutils.h"
|
||||
|
||||
// Constants
|
||||
const uint16_t kElectraAcHdrMark = 9166;
|
||||
const uint16_t kElectraAcBitMark = 646;
|
||||
const uint16_t kElectraAcHdrSpace = 4470;
|
||||
const uint16_t kElectraAcOneSpace = 1647;
|
||||
const uint16_t kElectraAcZeroSpace = 547;
|
||||
const uint32_t kElectraAcMessageGap = kDefaultMessageGap; // Just a guess.
|
||||
|
||||
using irutils::addBoolToString;
|
||||
using irutils::addIntToString;
|
||||
using irutils::addLabeledString;
|
||||
using irutils::addModeToString;
|
||||
using irutils::addFanToString;
|
||||
using irutils::addTempToString;
|
||||
using irutils::addToggleToString;
|
||||
|
||||
#if SEND_ELECTRA_AC
|
||||
/// Send a Electra A/C formatted message.
|
||||
/// Status: Alpha / Needs testing against a real device.
|
||||
/// @param[in] data The message to be sent.
|
||||
/// @note Guessing MSBF order.
|
||||
/// @param[in] nbytes The number of bytes of message to be sent.
|
||||
/// @param[in] repeat The number of times the command is to be repeated.
|
||||
void IRsend::sendElectraAC(const uint8_t data[], const uint16_t nbytes,
|
||||
const uint16_t repeat) {
|
||||
for (uint16_t r = 0; r <= repeat; r++)
|
||||
sendGeneric(kElectraAcHdrMark, kElectraAcHdrSpace, kElectraAcBitMark,
|
||||
kElectraAcOneSpace, kElectraAcBitMark, kElectraAcZeroSpace,
|
||||
kElectraAcBitMark, kElectraAcMessageGap, data, nbytes,
|
||||
38000, // Complete guess of the modulation frequency.
|
||||
false, // Send data in LSB order per byte
|
||||
0, 50);
|
||||
}
|
||||
#endif
|
||||
|
||||
/// Class constructor.
|
||||
/// @param[in] pin GPIO to be used when sending.
|
||||
/// @param[in] inverted Is the output signal to be inverted?
|
||||
/// @param[in] use_modulation Is frequency modulation to be used?
|
||||
IRElectraAc::IRElectraAc(const uint16_t pin, const bool inverted,
|
||||
const bool use_modulation)
|
||||
: _irsend(pin, inverted, use_modulation) {
|
||||
stateReset();
|
||||
}
|
||||
|
||||
/// Reset the internal state to a fixed known good state.
|
||||
void IRElectraAc::stateReset(void) {
|
||||
for (uint8_t i = 1; i < kElectraAcStateLength - 2; i++) _.raw[i] = 0;
|
||||
_.raw[0] = 0xC3;
|
||||
_.LightToggle = kElectraAcLightToggleOff;
|
||||
// [12] is the checksum.
|
||||
}
|
||||
|
||||
/// Set up hardware to be able to send a message.
|
||||
void IRElectraAc::begin(void) { _irsend.begin(); }
|
||||
|
||||
/// Calculate the checksum for a given state.
|
||||
/// @param[in] state The value to calc the checksum of.
|
||||
/// @param[in] length The length of the state array.
|
||||
/// @return The calculated checksum stored in a uint_8.
|
||||
uint8_t IRElectraAc::calcChecksum(const uint8_t state[],
|
||||
const uint16_t length) {
|
||||
if (length == 0) return state[0];
|
||||
return sumBytes(state, length - 1);
|
||||
}
|
||||
|
||||
/// Verify the checksum is valid for a given state.
|
||||
/// @param[in] state The state to verify the checksum of.
|
||||
/// @param[in] length The length of the state array.
|
||||
/// @return true, if the state has a valid checksum. Otherwise, false.
|
||||
bool IRElectraAc::validChecksum(const uint8_t state[], const uint16_t length) {
|
||||
if (length < 2)
|
||||
return true; // No checksum to compare with. Assume okay.
|
||||
return (state[length - 1] == calcChecksum(state, length));
|
||||
}
|
||||
|
||||
/// Calculate and set the checksum values for the internal state.
|
||||
/// @param[in] length The length of the state array.
|
||||
void IRElectraAc::checksum(uint16_t length) {
|
||||
if (length < 2) return;
|
||||
_.Sum = calcChecksum(_.raw, length);
|
||||
}
|
||||
|
||||
#if SEND_ELECTRA_AC
|
||||
/// Send the current internal state as an IR message.
|
||||
/// @param[in] repeat Nr. of times the message will be repeated.
|
||||
void IRElectraAc::send(const uint16_t repeat) {
|
||||
_irsend.sendElectraAC(getRaw(), kElectraAcStateLength, repeat);
|
||||
}
|
||||
#endif // SEND_ELECTRA_AC
|
||||
|
||||
/// Get a PTR to the internal state/code for this protocol.
|
||||
/// @return PTR to a code for this protocol based on the current internal state.
|
||||
uint8_t *IRElectraAc::getRaw(void) {
|
||||
checksum();
|
||||
return _.raw;
|
||||
}
|
||||
|
||||
/// Set the internal state from a valid code for this protocol.
|
||||
/// @param[in] new_code A valid code for this protocol.
|
||||
/// @param[in] length The length of the code array.
|
||||
void IRElectraAc::setRaw(const uint8_t new_code[], const uint16_t length) {
|
||||
std::memcpy(_.raw, new_code, std::min(length, kElectraAcStateLength));
|
||||
}
|
||||
|
||||
/// Change the power setting to On.
|
||||
void IRElectraAc::on(void) { setPower(true); }
|
||||
|
||||
/// Change the power setting to Off.
|
||||
void IRElectraAc::off(void) { setPower(false); }
|
||||
|
||||
/// Change the power setting.
|
||||
/// @param[in] on true, the setting is on. false, the setting is off.
|
||||
void IRElectraAc::setPower(const bool on) {
|
||||
_.Power = on;
|
||||
}
|
||||
|
||||
/// Get the value of the current power setting.
|
||||
/// @return true, the setting is on. false, the setting is off.
|
||||
bool IRElectraAc::getPower(void) const {
|
||||
return _.Power;
|
||||
}
|
||||
|
||||
/// Set the operating mode of the A/C.
|
||||
/// @param[in] mode The desired operating mode.
|
||||
void IRElectraAc::setMode(const uint8_t mode) {
|
||||
switch (mode) {
|
||||
case kElectraAcAuto:
|
||||
case kElectraAcDry:
|
||||
case kElectraAcCool:
|
||||
case kElectraAcHeat:
|
||||
case kElectraAcFan:
|
||||
_.Mode = mode;
|
||||
break;
|
||||
default:
|
||||
// If we get an unexpected mode, default to AUTO.
|
||||
_.Mode = kElectraAcAuto;
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the operating mode setting of the A/C.
|
||||
/// @return The current operating mode setting.
|
||||
uint8_t IRElectraAc::getMode(void) const {
|
||||
return _.Mode;
|
||||
}
|
||||
|
||||
/// Convert a stdAc::opmode_t enum into its native mode.
|
||||
/// @param[in] mode The enum to be converted.
|
||||
/// @return The native equivalent of the enum.
|
||||
uint8_t IRElectraAc::convertMode(const stdAc::opmode_t mode) {
|
||||
switch (mode) {
|
||||
case stdAc::opmode_t::kCool: return kElectraAcCool;
|
||||
case stdAc::opmode_t::kHeat: return kElectraAcHeat;
|
||||
case stdAc::opmode_t::kDry: return kElectraAcDry;
|
||||
case stdAc::opmode_t::kFan: return kElectraAcFan;
|
||||
default: return kElectraAcAuto;
|
||||
}
|
||||
}
|
||||
|
||||
/// Convert a native mode into its stdAc equivalent.
|
||||
/// @param[in] mode The native setting to be converted.
|
||||
/// @return The stdAc equivalent of the native setting.
|
||||
stdAc::opmode_t IRElectraAc::toCommonMode(const uint8_t mode) {
|
||||
switch (mode) {
|
||||
case kElectraAcCool: return stdAc::opmode_t::kCool;
|
||||
case kElectraAcHeat: return stdAc::opmode_t::kHeat;
|
||||
case kElectraAcDry: return stdAc::opmode_t::kDry;
|
||||
case kElectraAcFan: return stdAc::opmode_t::kFan;
|
||||
default: return stdAc::opmode_t::kAuto;
|
||||
}
|
||||
}
|
||||
|
||||
/// Set the temperature.
|
||||
/// @param[in] temp The temperature in degrees celsius.
|
||||
void IRElectraAc::setTemp(const uint8_t temp) {
|
||||
uint8_t newtemp = std::max(kElectraAcMinTemp, temp);
|
||||
newtemp = std::min(kElectraAcMaxTemp, newtemp) - kElectraAcTempDelta;
|
||||
_.Temp = newtemp;
|
||||
}
|
||||
|
||||
/// Get the current temperature setting.
|
||||
/// @return The current setting for temp. in degrees celsius.
|
||||
uint8_t IRElectraAc::getTemp(void) const {
|
||||
return _.Temp + kElectraAcTempDelta;
|
||||
}
|
||||
|
||||
/// Set the speed of the fan.
|
||||
/// @param[in] speed The desired setting.
|
||||
/// @note 0 is auto, 1-3 is the speed
|
||||
void IRElectraAc::setFan(const uint8_t speed) {
|
||||
switch (speed) {
|
||||
case kElectraAcFanAuto:
|
||||
case kElectraAcFanHigh:
|
||||
case kElectraAcFanMed:
|
||||
case kElectraAcFanLow:
|
||||
_.Fan = speed;
|
||||
break;
|
||||
default:
|
||||
// If we get an unexpected speed, default to Auto.
|
||||
_.Fan = kElectraAcFanAuto;
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the current fan speed setting.
|
||||
/// @return The current fan speed.
|
||||
uint8_t IRElectraAc::getFan(void) const {
|
||||
return _.Fan;
|
||||
}
|
||||
|
||||
/// Convert a stdAc::fanspeed_t enum into it's native speed.
|
||||
/// @param[in] speed The enum to be converted.
|
||||
/// @return The native equivalent of the enum.
|
||||
uint8_t IRElectraAc::convertFan(const stdAc::fanspeed_t speed) {
|
||||
switch (speed) {
|
||||
case stdAc::fanspeed_t::kMin:
|
||||
case stdAc::fanspeed_t::kLow: return kElectraAcFanLow;
|
||||
case stdAc::fanspeed_t::kMedium: return kElectraAcFanMed;
|
||||
case stdAc::fanspeed_t::kHigh:
|
||||
case stdAc::fanspeed_t::kMax: return kElectraAcFanHigh;
|
||||
default: return kElectraAcFanAuto;
|
||||
}
|
||||
}
|
||||
|
||||
/// Convert a native fan speed into its stdAc equivalent.
|
||||
/// @param[in] speed The native setting to be converted.
|
||||
/// @return The stdAc equivalent of the native setting.
|
||||
stdAc::fanspeed_t IRElectraAc::toCommonFanSpeed(const uint8_t speed) {
|
||||
switch (speed) {
|
||||
case kElectraAcFanHigh: return stdAc::fanspeed_t::kMax;
|
||||
case kElectraAcFanMed: return stdAc::fanspeed_t::kMedium;
|
||||
case kElectraAcFanLow: return stdAc::fanspeed_t::kMin;
|
||||
default: return stdAc::fanspeed_t::kAuto;
|
||||
}
|
||||
}
|
||||
|
||||
/// Set the Vertical Swing mode of the A/C.
|
||||
/// @param[in] on true, the setting is on. false, the setting is off.
|
||||
void IRElectraAc::setSwingV(const bool on) {
|
||||
_.SwingV = (on ? kElectraAcSwingOn : kElectraAcSwingOff);
|
||||
}
|
||||
|
||||
/// Get the Vertical Swing mode of the A/C.
|
||||
/// @return true, the setting is on. false, the setting is off.
|
||||
bool IRElectraAc::getSwingV(void) const {
|
||||
return !_.SwingV;
|
||||
}
|
||||
|
||||
/// Set the Horizontal Swing mode of the A/C.
|
||||
/// @param[in] on true, the setting is on. false, the setting is off.
|
||||
void IRElectraAc::setSwingH(const bool on) {
|
||||
_.SwingH = (on ? kElectraAcSwingOn : kElectraAcSwingOff);
|
||||
}
|
||||
|
||||
/// Get the Horizontal Swing mode of the A/C.
|
||||
/// @return true, the setting is on. false, the setting is off.
|
||||
bool IRElectraAc::getSwingH(void) const {
|
||||
return !_.SwingH;
|
||||
}
|
||||
|
||||
/// Set the Light (LED) Toggle mode of the A/C.
|
||||
/// @param[in] on true, the setting is on. false, the setting is off.
|
||||
void IRElectraAc::setLightToggle(const bool on) {
|
||||
_.LightToggle = (on ? kElectraAcLightToggleOn : kElectraAcLightToggleOff);
|
||||
}
|
||||
|
||||
/// Get the Light (LED) Toggle mode of the A/C.
|
||||
/// @return true, the setting is on. false, the setting is off.
|
||||
bool IRElectraAc::getLightToggle(void) const {
|
||||
return (_.LightToggle & kElectraAcLightToggleMask) ==
|
||||
kElectraAcLightToggleMask;
|
||||
}
|
||||
|
||||
/// Set the Clean mode of the A/C.
|
||||
/// @param[in] on true, the setting is on. false, the setting is off.
|
||||
void IRElectraAc::setClean(const bool on) {
|
||||
_.Clean = on;
|
||||
}
|
||||
|
||||
/// Get the Clean mode of the A/C.
|
||||
/// @return true, the setting is on. false, the setting is off.
|
||||
bool IRElectraAc::getClean(void) const {
|
||||
return _.Clean;
|
||||
}
|
||||
|
||||
/// Set the Turbo mode of the A/C.
|
||||
/// @param[in] on true, the setting is on. false, the setting is off.
|
||||
void IRElectraAc::setTurbo(const bool on) {
|
||||
_.Turbo = on;
|
||||
}
|
||||
|
||||
/// Get the Turbo mode of the A/C.
|
||||
/// @return true, the setting is on. false, the setting is off.
|
||||
bool IRElectraAc::getTurbo(void) const {
|
||||
return _.Turbo;
|
||||
}
|
||||
|
||||
/// Get the IFeel mode of the A/C.
|
||||
/// @return true, the setting is on. false, the setting is off.
|
||||
bool IRElectraAc::getIFeel(void) const { return _.IFeel; }
|
||||
|
||||
/// Set the IFeel mode of the A/C.
|
||||
/// @param[in] on true, the setting is on. false, the setting is off.
|
||||
void IRElectraAc::setIFeel(const bool on) {
|
||||
_.IFeel = on;
|
||||
if (_.IFeel)
|
||||
// Make sure there is a reasonable value in _.SensorTemp
|
||||
setSensorTemp(getSensorTemp());
|
||||
else
|
||||
// Clear any previous stored temp..
|
||||
_.SensorTemp = kElectraAcSensorMinTemp;
|
||||
}
|
||||
|
||||
/// Get the silent Sensor Update setting of the message.
|
||||
/// i.e. Is this _just_ a sensor temp update message from the remote?
|
||||
/// @note The A/C just takes the sensor temp value from the message and
|
||||
/// will not follow any of the other settings in the message.
|
||||
/// @return true, the setting is on. false, the setting is off.
|
||||
bool IRElectraAc::getSensorUpdate(void) const { return _.SensorUpdate; }
|
||||
|
||||
/// Set the silent Sensor Update setting of the message.
|
||||
/// i.e. Is this _just_ a sensor temp update message from the remote?
|
||||
/// @note The A/C will just take the sensor temp value from the message and
|
||||
/// will not follow any of the other settings in the message. If set, the A/C
|
||||
/// unit will also not beep in response to the message.
|
||||
/// @param[in] on true, the setting is on. false, the setting is off.
|
||||
void IRElectraAc::setSensorUpdate(const bool on) { _.SensorUpdate = on; }
|
||||
|
||||
/// Set the Sensor temperature for the IFeel mode.
|
||||
/// @param[in] temp The temperature in degrees celsius.
|
||||
void IRElectraAc::setSensorTemp(const uint8_t temp) {
|
||||
_.SensorTemp = std::min(kElectraAcSensorMaxTemp,
|
||||
std::max(kElectraAcSensorMinTemp, temp)) +
|
||||
kElectraAcSensorTempDelta;
|
||||
}
|
||||
|
||||
/// Get the current sensor temperature setting for the IFeel mode.
|
||||
/// @return The current setting for temp. in degrees celsius.
|
||||
uint8_t IRElectraAc::getSensorTemp(void) const {
|
||||
return std::max(kElectraAcSensorTempDelta, _.SensorTemp) -
|
||||
kElectraAcSensorTempDelta;
|
||||
}
|
||||
|
||||
/// Convert the current internal state into its stdAc::state_t equivalent.
|
||||
/// @return The stdAc equivalent of the native settings.
|
||||
stdAc::state_t IRElectraAc::toCommon(void) const {
|
||||
stdAc::state_t result{};
|
||||
result.protocol = decode_type_t::ELECTRA_AC;
|
||||
result.power = _.Power;
|
||||
result.mode = toCommonMode(_.Mode);
|
||||
result.celsius = true;
|
||||
result.degrees = getTemp();
|
||||
result.sensorTemperature = getSensorTemp();
|
||||
result.fanspeed = toCommonFanSpeed(_.Fan);
|
||||
result.swingv = getSwingV() ? stdAc::swingv_t::kAuto
|
||||
: stdAc::swingv_t::kOff;
|
||||
result.swingh = getSwingH() ? stdAc::swingh_t::kAuto
|
||||
: stdAc::swingh_t::kOff;
|
||||
result.light = getLightToggle();
|
||||
result.turbo = _.Turbo;
|
||||
result.clean = _.Clean;
|
||||
result.iFeel = getIFeel();
|
||||
// Not supported.
|
||||
result.model = -1; // No models used.
|
||||
result.quiet = false;
|
||||
result.econo = false;
|
||||
result.filter = false;
|
||||
result.beep = false;
|
||||
result.sleep = -1;
|
||||
result.clock = -1;
|
||||
return result;
|
||||
}
|
||||
|
||||
/// Convert the current internal state into a human readable string.
|
||||
/// @return A human readable string.
|
||||
String IRElectraAc::toString(void) const {
|
||||
String result = "";
|
||||
result.reserve(160); // Reserve some heap for the string to reduce fragging.
|
||||
if (!_.SensorUpdate) {
|
||||
result += addBoolToString(_.Power, kPowerStr, false);
|
||||
result += addModeToString(_.Mode, kElectraAcAuto, kElectraAcCool,
|
||||
kElectraAcHeat, kElectraAcDry, kElectraAcFan);
|
||||
result += addTempToString(getTemp());
|
||||
result += addFanToString(_.Fan, kElectraAcFanHigh, kElectraAcFanLow,
|
||||
kElectraAcFanAuto, kElectraAcFanAuto,
|
||||
kElectraAcFanMed);
|
||||
result += addBoolToString(getSwingV(), kSwingVStr);
|
||||
result += addBoolToString(getSwingH(), kSwingHStr);
|
||||
result += addToggleToString(getLightToggle(), kLightStr);
|
||||
result += addBoolToString(_.Clean, kCleanStr);
|
||||
result += addBoolToString(_.Turbo, kTurboStr);
|
||||
result += addBoolToString(_.IFeel, kIFeelStr);
|
||||
}
|
||||
if (_.IFeel || _.SensorUpdate) {
|
||||
result += addIntToString(getSensorTemp(), kSensorTempStr, !_.SensorUpdate);
|
||||
result += 'C';
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
#if DECODE_ELECTRA_AC
|
||||
/// Decode the supplied Electra A/C message.
|
||||
/// Status: STABLE / Known working.
|
||||
/// @param[in,out] results Ptr to the data to decode & where to store the decode
|
||||
/// result.
|
||||
/// @param[in] offset The starting index to use when attempting to decode the
|
||||
/// raw data. Typically/Defaults to kStartOffset.
|
||||
/// @param[in] nbits The number of data bits to expect.
|
||||
/// @param[in] strict Flag indicating if we should perform strict matching.
|
||||
/// @return A boolean. True if it can decode it, false if it can't.
|
||||
bool IRrecv::decodeElectraAC(decode_results *results, uint16_t offset,
|
||||
const uint16_t nbits,
|
||||
const bool strict) {
|
||||
if (strict) {
|
||||
if (nbits != kElectraAcBits)
|
||||
return false; // Not strictly a ELECTRA_AC message.
|
||||
}
|
||||
|
||||
// Match Header + Data + Footer
|
||||
if (!matchGeneric(results->rawbuf + offset, results->state,
|
||||
results->rawlen - offset, nbits,
|
||||
kElectraAcHdrMark, kElectraAcHdrSpace,
|
||||
kElectraAcBitMark, kElectraAcOneSpace,
|
||||
kElectraAcBitMark, kElectraAcZeroSpace,
|
||||
kElectraAcBitMark, kElectraAcMessageGap, true,
|
||||
_tolerance, 0, false)) return false;
|
||||
|
||||
// Compliance
|
||||
if (strict) {
|
||||
// Verify the checksum.
|
||||
if (!IRElectraAc::validChecksum(results->state)) return false;
|
||||
}
|
||||
|
||||
// Success
|
||||
results->decode_type = decode_type_t::ELECTRA_AC;
|
||||
results->bits = nbits;
|
||||
// No need to record the state as we stored it as we decoded it.
|
||||
// As we use result->state, we don't record value, address, or command as it
|
||||
// is a union data type.
|
||||
return true;
|
||||
}
|
||||
#endif // DECODE_ELECTRA_AC
|
||||
179
yoRadio/src/IRremoteESP8266/ir_Electra.h
Normal file
179
yoRadio/src/IRremoteESP8266/ir_Electra.h
Normal file
@@ -0,0 +1,179 @@
|
||||
// Copyright 2019-2021 David Conran
|
||||
/// @file
|
||||
/// @brief Support for Electra A/C protocols.
|
||||
/// @see https://github.com/ToniA/arduino-heatpumpir/blob/master/AUXHeatpumpIR.cpp
|
||||
|
||||
// Supports:
|
||||
// Brand: AUX, Model: KFR-35GW/BpNFW=3 A/C
|
||||
// Brand: AUX, Model: YKR-T/011 remote
|
||||
// Brand: Electra, Model: Classic INV 17 / AXW12DCS A/C
|
||||
// Brand: Electra, Model: YKR-M/003E remote
|
||||
// Brand: Frigidaire, Model: FGPC102AB1 A/C
|
||||
// Brand: Subtropic, Model: SUB-07HN1_18Y A/C
|
||||
// Brand: Subtropic, Model: YKR-H/102E remote
|
||||
// Brand: Centek, Model: SCT-65Q09 A/C
|
||||
// Brand: Centek, Model: YKR-P/002E remote
|
||||
// Brand: AEG, Model: Chillflex Pro AXP26U338CW A/C
|
||||
// Brand: Electrolux, Model: YKR-H/531E A/C
|
||||
|
||||
#ifndef IR_ELECTRA_H_
|
||||
#define IR_ELECTRA_H_
|
||||
|
||||
#define __STDC_LIMIT_MACROS
|
||||
#include <stdint.h>
|
||||
#ifndef UNIT_TEST
|
||||
#include <Arduino.h>
|
||||
#endif
|
||||
#include "IRremoteESP8266.h"
|
||||
#include "IRsend.h"
|
||||
#ifdef UNIT_TEST
|
||||
#include "IRsend_test.h"
|
||||
#endif
|
||||
|
||||
/// Native representation of a Electra A/C message.
|
||||
union ElectraProtocol {
|
||||
uint8_t raw[kElectraAcStateLength]; ///< The state of the IR remote
|
||||
struct {
|
||||
// Byte 0
|
||||
uint8_t :8;
|
||||
// Byte 1
|
||||
uint8_t SwingV :3;
|
||||
uint8_t Temp :5;
|
||||
// Byte 2
|
||||
uint8_t :5;
|
||||
uint8_t SwingH :3;
|
||||
// Byte 3
|
||||
uint8_t :6;
|
||||
uint8_t SensorUpdate :1;
|
||||
uint8_t :1;
|
||||
// Byte 4
|
||||
uint8_t :5;
|
||||
uint8_t Fan :3;
|
||||
// Byte 5
|
||||
uint8_t :6;
|
||||
uint8_t Turbo :1;
|
||||
uint8_t :1;
|
||||
// Byte 6
|
||||
uint8_t :3;
|
||||
uint8_t IFeel :1;
|
||||
uint8_t :1;
|
||||
uint8_t Mode :3;
|
||||
// Byte 7
|
||||
uint8_t SensorTemp :8;
|
||||
// Byte 8
|
||||
uint8_t :8;
|
||||
// Byte 9
|
||||
uint8_t :2;
|
||||
uint8_t Clean :1;
|
||||
uint8_t :2;
|
||||
uint8_t Power :1;
|
||||
uint8_t :2;
|
||||
// Byte 10
|
||||
uint8_t :8;
|
||||
// Byte 11
|
||||
uint8_t LightToggle :8;
|
||||
// Byte 12
|
||||
uint8_t Sum :8;
|
||||
};
|
||||
};
|
||||
|
||||
// Constants
|
||||
const uint8_t kElectraAcMinTemp = 16; // 16C
|
||||
const uint8_t kElectraAcMaxTemp = 32; // 32C
|
||||
const uint8_t kElectraAcTempDelta = 8;
|
||||
const uint8_t kElectraAcSwingOn = 0b000;
|
||||
const uint8_t kElectraAcSwingOff = 0b111;
|
||||
|
||||
const uint8_t kElectraAcFanAuto = 0b101;
|
||||
const uint8_t kElectraAcFanLow = 0b011;
|
||||
const uint8_t kElectraAcFanMed = 0b010;
|
||||
const uint8_t kElectraAcFanHigh = 0b001;
|
||||
|
||||
const uint8_t kElectraAcAuto = 0b000;
|
||||
const uint8_t kElectraAcCool = 0b001;
|
||||
const uint8_t kElectraAcDry = 0b010;
|
||||
const uint8_t kElectraAcHeat = 0b100;
|
||||
const uint8_t kElectraAcFan = 0b110;
|
||||
|
||||
const uint8_t kElectraAcLightToggleOn = 0x15;
|
||||
// Light has known ON values of 0x15 (0b00010101) or 0x19 (0b00011001)
|
||||
// Thus common bits ON are: 0b00010001 (0x11)
|
||||
// We will use this for the getLightToggle() test.
|
||||
const uint8_t kElectraAcLightToggleMask = 0x11;
|
||||
// and known OFF values of 0x08 (0b00001000) & 0x05 (0x00000101)
|
||||
const uint8_t kElectraAcLightToggleOff = 0x08;
|
||||
|
||||
// Re: Byte[7]. Or Delta == 0xA and Temperature are stored in last 6 bits,
|
||||
// and bit 7 stores Unknown flag
|
||||
const uint8_t kElectraAcSensorTempDelta = 0x4A;
|
||||
const uint8_t kElectraAcSensorMinTemp = 0; // 0C
|
||||
const uint8_t kElectraAcSensorMaxTemp = 50; // 50C
|
||||
|
||||
// Classes
|
||||
/// Class for handling detailed Electra A/C messages.
|
||||
class IRElectraAc {
|
||||
public:
|
||||
explicit IRElectraAc(const uint16_t pin, const bool inverted = false,
|
||||
const bool use_modulation = true);
|
||||
void stateReset(void);
|
||||
#if SEND_ELECTRA_AC
|
||||
void send(const uint16_t repeat = kElectraAcMinRepeat);
|
||||
/// 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_ELECTRA_AC
|
||||
void begin(void);
|
||||
void on(void);
|
||||
void off(void);
|
||||
void setPower(const bool on);
|
||||
bool getPower(void) const;
|
||||
void setMode(const uint8_t mode);
|
||||
uint8_t getMode(void) const;
|
||||
void setTemp(const uint8_t temp);
|
||||
uint8_t getTemp(void) const;
|
||||
void setFan(const uint8_t speed);
|
||||
uint8_t getFan(void) const;
|
||||
void setSwingV(const bool on);
|
||||
bool getSwingV(void) const;
|
||||
void setSwingH(const bool on);
|
||||
bool getSwingH(void) const;
|
||||
void setClean(const bool on);
|
||||
bool getClean(void) const;
|
||||
void setLightToggle(const bool on);
|
||||
bool getLightToggle(void) const;
|
||||
void setTurbo(const bool on);
|
||||
bool getTurbo(void) const;
|
||||
void setIFeel(const bool on);
|
||||
bool getIFeel(void) const;
|
||||
void setSensorUpdate(const bool on);
|
||||
bool getSensorUpdate(void) const;
|
||||
void setSensorTemp(const uint8_t temp);
|
||||
uint8_t getSensorTemp(void) const;
|
||||
uint8_t* getRaw(void);
|
||||
void setRaw(const uint8_t new_code[],
|
||||
const uint16_t length = kElectraAcStateLength);
|
||||
static bool validChecksum(const uint8_t state[],
|
||||
const uint16_t length = kElectraAcStateLength);
|
||||
static uint8_t calcChecksum(const uint8_t state[],
|
||||
const uint16_t length = kElectraAcStateLength);
|
||||
String toString(void) const;
|
||||
static uint8_t convertMode(const stdAc::opmode_t mode);
|
||||
static uint8_t convertFan(const stdAc::fanspeed_t speed);
|
||||
static stdAc::opmode_t toCommonMode(const uint8_t mode);
|
||||
static stdAc::fanspeed_t toCommonFanSpeed(const uint8_t speed);
|
||||
stdAc::state_t toCommon(void) const;
|
||||
#ifndef UNIT_TEST
|
||||
|
||||
private:
|
||||
IRsend _irsend; ///< instance of the IR send class
|
||||
#else
|
||||
/// @cond IGNORE
|
||||
IRsendTest _irsend; ///< instance of the testing IR send class
|
||||
/// @endcond
|
||||
#endif
|
||||
ElectraProtocol _;
|
||||
void checksum(const uint16_t length = kElectraAcStateLength);
|
||||
};
|
||||
#endif // IR_ELECTRA_H_
|
||||
89
yoRadio/src/IRremoteESP8266/ir_EliteScreens.cpp
Normal file
89
yoRadio/src/IRremoteESP8266/ir_EliteScreens.cpp
Normal file
@@ -0,0 +1,89 @@
|
||||
// Copyright 2020 David Conran
|
||||
/// @file
|
||||
/// @brief Elite Screens protocol support
|
||||
/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1306
|
||||
/// @see https://elitescreens.com/kcfinder/upload/files/FAQ/ir_commands.pdf
|
||||
|
||||
// Supports:
|
||||
// Brand: Elite Screens, Model: Spectrum series
|
||||
// Brand: Elite Screens, Model: VMAX2 / VMAX2 Plus series
|
||||
// Brand: Elite Screens, Model: VMAX Plus4 series
|
||||
// Brand: Elite Screens, Model: Home2 / Home3 series
|
||||
// Brand: Elite Screens, Model: CineTension2 / CineTension3 series
|
||||
// Brand: Elite Screens, Model: ZSP-IR-B / ZSP-IR-W remote
|
||||
// Brand: Lumene Screens, Model: Embassy
|
||||
|
||||
// Known Elite Screens commands:
|
||||
// 0xFEA3387 (STOP)
|
||||
// 0xFDA2256 (UP)
|
||||
// 0xFBA1136 (DOWN)
|
||||
|
||||
// Known Lumene Screens commands:
|
||||
// 0xFDE3322 (STOP)
|
||||
// 0xFEE2221 (UP)
|
||||
// 0xFBE11E0 (DOWN)
|
||||
// 0xF7E2EBD (STEP UP)
|
||||
// 0xEFE1E2C (STEP DOWN)
|
||||
|
||||
#include "IRrecv.h"
|
||||
#include "IRsend.h"
|
||||
#include "IRutils.h"
|
||||
|
||||
|
||||
// Constants
|
||||
const uint16_t kEliteScreensOne = 470;
|
||||
const uint16_t kEliteScreensZero = 1214;
|
||||
const uint16_t kEliteScreensGap = 29200;
|
||||
|
||||
#if SEND_ELITESCREENS
|
||||
/// Send an Elite Screens formatted message.
|
||||
/// Status: BETA / Probably Working.
|
||||
/// @param[in] data The message to be sent.
|
||||
/// @param[in] nbits The number of bits of message to be sent.
|
||||
/// @param[in] repeat The number of times the command is to be repeated.
|
||||
void IRsend::sendElitescreens(uint64_t data, uint16_t nbits, uint16_t repeat) {
|
||||
// Protocol uses a constant bit time encoding.
|
||||
sendGeneric(0, 0, // No header.
|
||||
kEliteScreensOne, kEliteScreensZero,
|
||||
kEliteScreensZero, kEliteScreensOne,
|
||||
0, kEliteScreensGap, data, nbits, 38000, true, repeat, 50);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if DECODE_ELITESCREENS
|
||||
/// Decode the supplied Elite Screens message.
|
||||
/// Status: STABLE / Confirmed working.
|
||||
/// @param[in,out] results Ptr to the data to decode & where to store the decode
|
||||
/// result.
|
||||
/// @param[in] offset The starting index to use when attempting to decode the
|
||||
/// raw data. Typically/Defaults to kStartOffset.
|
||||
/// @param[in] nbits The number of data bits to expect.
|
||||
/// @param[in] strict Flag indicating if we should perform strict matching.
|
||||
/// @return A boolean. True if it can decode it, false if it can't.
|
||||
bool IRrecv::decodeElitescreens(decode_results *results, uint16_t offset,
|
||||
const uint16_t nbits, const bool strict) {
|
||||
// Compliance check.
|
||||
if (strict && nbits != kEliteScreensBits) return false;
|
||||
|
||||
uint64_t data = 0;
|
||||
|
||||
// Data + Footer
|
||||
if (!matchGenericConstBitTime(results->rawbuf + offset, &data,
|
||||
results->rawlen - offset, nbits,
|
||||
// Header (None)
|
||||
0, 0,
|
||||
// Data
|
||||
kEliteScreensOne, kEliteScreensZero,
|
||||
// Footer (None)
|
||||
0, kEliteScreensGap, true)) return false;
|
||||
|
||||
// Success
|
||||
results->decode_type = decode_type_t::ELITESCREENS;
|
||||
results->bits = nbits;
|
||||
results->value = data;
|
||||
results->address = 0;
|
||||
results->command = 0;
|
||||
results->repeat = false;
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
111
yoRadio/src/IRremoteESP8266/ir_Epson.cpp
Normal file
111
yoRadio/src/IRremoteESP8266/ir_Epson.cpp
Normal file
@@ -0,0 +1,111 @@
|
||||
// Copyright 2020 David Conran
|
||||
/// @file
|
||||
/// @brief Support for Epson protocols.
|
||||
/// Epson is an NEC-like protocol, except it doesn't use the NEC style repeat.
|
||||
/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1034
|
||||
|
||||
// Supports:
|
||||
// Brand: Epson, Model: EN-TW9100W Projector
|
||||
// Brand: Epson, Model: VS230 Projector
|
||||
// Brand: Epson, Model: VS330 Projector
|
||||
// Brand: Epson, Model: EX3220 Projector
|
||||
// Brand: Epson, Model: EX5220 Projector
|
||||
// Brand: Epson, Model: EX5230 Projector
|
||||
// Brand: Epson, Model: EX6220 Projector
|
||||
// Brand: Epson, Model: EX7220 Projector
|
||||
|
||||
#define __STDC_LIMIT_MACROS
|
||||
#include <stdint.h>
|
||||
#include <algorithm>
|
||||
#include "IRrecv.h"
|
||||
#include "IRsend.h"
|
||||
#include "IRutils.h"
|
||||
#include "ir_NEC.h"
|
||||
|
||||
#if SEND_EPSON
|
||||
/// Send an Epson formatted message.
|
||||
/// Status: Beta / Probably works.
|
||||
/// @param[in] data The message to be sent.
|
||||
/// @param[in] nbits The number of nbits of message to be sent.
|
||||
/// @param[in] repeat The number of times the command is to be repeated.
|
||||
void IRsend::sendEpson(uint64_t data, uint16_t nbits, uint16_t repeat) {
|
||||
sendGeneric(kNecHdrMark, kNecHdrSpace, kNecBitMark, kNecOneSpace, kNecBitMark,
|
||||
kNecZeroSpace, kNecBitMark, kNecMinGap, kNecMinCommandLength,
|
||||
data, nbits, 38, true, repeat, 33);
|
||||
}
|
||||
|
||||
#endif // SEND_EPSON
|
||||
|
||||
#if DECODE_EPSON
|
||||
/// Decode the supplied Epson message.
|
||||
/// Status: Beta / Probably works.
|
||||
/// @param[in,out] results Ptr to the data to decode & where to store the decode
|
||||
/// result.
|
||||
/// @param[in] offset The starting index to use when attempting to decode the
|
||||
/// raw data. Typically/Defaults to kStartOffset.
|
||||
/// @param[in] nbits The number of data bits to expect.
|
||||
/// @param[in] strict Flag indicating if we should perform strict matching.
|
||||
/// @return A boolean. True if it can decode it, false if it can't.
|
||||
/// @note Experimental data indicates there are at least three messages
|
||||
/// (first + 2 repeats). We only require the first + a single repeat to match.
|
||||
/// This helps us distinguish it from NEC messages which are near identical.
|
||||
bool IRrecv::decodeEpson(decode_results *results, uint16_t offset,
|
||||
const uint16_t nbits, const bool strict) {
|
||||
const uint8_t kEpsonMinMesgsForDecode = 2;
|
||||
|
||||
if (results->rawlen < kEpsonMinMesgsForDecode * (2 * nbits + kHeader +
|
||||
kFooter) + offset - 1)
|
||||
return false; // Can't possibly be a valid Epson message.
|
||||
if (strict && nbits != kEpsonBits)
|
||||
return false; // Not strictly an Epson message.
|
||||
|
||||
uint64_t data = 0;
|
||||
uint64_t first_data = 0;
|
||||
bool first = true;
|
||||
|
||||
for (uint8_t i = 0; i < kEpsonMinMesgsForDecode; i++) {
|
||||
// Match Header + Data + Footer
|
||||
uint16_t delta = matchGeneric(results->rawbuf + offset, &data,
|
||||
results->rawlen - offset, nbits,
|
||||
kNecHdrMark, kNecHdrSpace,
|
||||
kNecBitMark, kNecOneSpace,
|
||||
kNecBitMark, kNecZeroSpace,
|
||||
kNecBitMark, kNecMinGap, true);
|
||||
if (!delta) return false;
|
||||
offset += delta;
|
||||
if (first)
|
||||
first_data = data;
|
||||
else if (data != first_data) return false;
|
||||
first = false; // No longer the first message.
|
||||
}
|
||||
// Compliance
|
||||
// Calculate command and optionally enforce integrity checking.
|
||||
uint8_t command = (data & 0xFF00) >> 8;
|
||||
// Command is sent twice, once as plain and then inverted.
|
||||
if ((command ^ 0xFF) != (data & 0xFF)) {
|
||||
if (strict) return false; // Command integrity failed.
|
||||
command = 0; // The command value isn't valid, so default to zero.
|
||||
}
|
||||
|
||||
// Success
|
||||
results->bits = nbits;
|
||||
results->value = data;
|
||||
results->decode_type = EPSON;
|
||||
// Epson command and address are technically in LSB first order so the
|
||||
// final versions have to be reversed.
|
||||
results->command = reverseBits(command, 8);
|
||||
// Normal Epson (NEC) protocol has an 8 bit address sent,
|
||||
// followed by it inverted.
|
||||
uint8_t address = (data & 0xFF000000) >> 24;
|
||||
uint8_t address_inverted = (data & 0x00FF0000) >> 16;
|
||||
if (address == (address_inverted ^ 0xFF))
|
||||
// Inverted, so it is normal Epson (NEC) protocol.
|
||||
results->address = reverseBits(address, 8);
|
||||
else
|
||||
// Not inverted, so must be Extended Epson (NEC) protocol,
|
||||
// thus 16 bit address.
|
||||
results->address = reverseBits((data >> 16) & UINT16_MAX, 16);
|
||||
results->repeat = !first;
|
||||
return true;
|
||||
}
|
||||
#endif // DECODE_EPSON
|
||||
1100
yoRadio/src/IRremoteESP8266/ir_Fujitsu.cpp
Normal file
1100
yoRadio/src/IRremoteESP8266/ir_Fujitsu.cpp
Normal file
File diff suppressed because it is too large
Load Diff
266
yoRadio/src/IRremoteESP8266/ir_Fujitsu.h
Normal file
266
yoRadio/src/IRremoteESP8266/ir_Fujitsu.h
Normal file
@@ -0,0 +1,266 @@
|
||||
// Copyright 2017 Jonny Graham
|
||||
// Copyright 2018-2022 David Conran
|
||||
// Copyright 2021 siriuslzx
|
||||
|
||||
/// @file
|
||||
/// @brief Support for Fujitsu A/C protocols.
|
||||
/// Fujitsu A/C support added by Jonny Graham
|
||||
/// @warning Use of incorrect model may cause the A/C unit to lock up.
|
||||
/// e.g. An A/C that uses an AR-RAH1U remote may lock up requiring a physical
|
||||
/// power rest, if incorrect model (ARRAH2E) is used with a Swing command.
|
||||
/// The correct model for it is ARREB1E.
|
||||
/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1376
|
||||
|
||||
// Supports:
|
||||
// Brand: Fujitsu, Model: AR-RAH2E remote (ARRAH2E)
|
||||
// Brand: Fujitsu, Model: ASYG30LFCA A/C (ARRAH2E)
|
||||
// Brand: Fujitsu General, Model: AR-RCE1E remote (ARRAH2E)
|
||||
// Brand: Fujitsu General, Model: ASHG09LLCA A/C (ARRAH2E)
|
||||
// Brand: Fujitsu General, Model: AOHG09LLC A/C (ARRAH2E)
|
||||
// Brand: Fujitsu, Model: AR-DB1 remote (ARDB1)
|
||||
// Brand: Fujitsu, Model: AST9RSGCW A/C (ARDB1)
|
||||
// Brand: Fujitsu, Model: AR-REB1E remote (ARREB1E)
|
||||
// Brand: Fujitsu, Model: ASYG7LMCA A/C (ARREB1E)
|
||||
// Brand: Fujitsu, Model: AR-RAE1E remote (ARRAH2E)
|
||||
// Brand: Fujitsu, Model: AGTV14LAC A/C (ARRAH2E)
|
||||
// Brand: Fujitsu, Model: AR-RAC1E remote (ARRAH2E)
|
||||
// Brand: Fujitsu, Model: ASTB09LBC A/C (ARRY4)
|
||||
// Brand: Fujitsu, Model: AR-RY4 remote (ARRY4)
|
||||
// Brand: Fujitsu General, Model: AR-JW2 remote (ARJW2)
|
||||
// Brand: Fujitsu, Model: AR-DL10 remote (ARDB1)
|
||||
// Brand: Fujitsu, Model: ASU30C1 A/C (ARDB1)
|
||||
// Brand: Fujitsu, Model: AR-RAH1U remote (ARREB1E)
|
||||
// Brand: Fujitsu, Model: AR-RAH2U remote (ARRAH2E)
|
||||
// Brand: Fujitsu, Model: ASU12RLF A/C (ARREB1E)
|
||||
// Brand: Fujitsu, Model: AR-REW4E remote (ARREW4E)
|
||||
// Brand: Fujitsu, Model: ASYG09KETA-B A/C (ARREW4E)
|
||||
// Brand: Fujitsu, Model: AR-REB4E remote (ARREB1E)
|
||||
// Brand: Fujitsu, Model: ASTG09K A/C (ARREW4E)
|
||||
// Brand: Fujitsu, Model: ASTG18K A/C (ARREW4E)
|
||||
// Brand: Fujitsu, Model: AR-REW1E remote (ARREW4E)
|
||||
// Brand: Fujitsu, Model: AR-REG1U remote (ARRAH2E)
|
||||
// Brand: OGeneral, Model: AR-RCL1E remote (ARRAH2E)
|
||||
// Brand: Fujitsu General, Model: AR-JW17 remote (ARDB1)
|
||||
|
||||
#ifndef IR_FUJITSU_H_
|
||||
#define IR_FUJITSU_H_
|
||||
|
||||
#define __STDC_LIMIT_MACROS
|
||||
#include <stdint.h>
|
||||
#ifdef ARDUINO
|
||||
#include <Arduino.h>
|
||||
#endif
|
||||
#include "IRrecv.h"
|
||||
#include "IRremoteESP8266.h"
|
||||
#include "IRsend.h"
|
||||
#ifdef UNIT_TEST
|
||||
#include "IRsend_test.h"
|
||||
#endif
|
||||
|
||||
/// Native representation of a Fujitsu A/C message.
|
||||
union FujitsuProtocol {
|
||||
struct {
|
||||
uint8_t longcode[kFujitsuAcStateLength]; ///< The state of the IR remote.
|
||||
uint8_t shortcode[kFujitsuAcStateLengthShort];
|
||||
};
|
||||
struct {
|
||||
// Byte 0~1
|
||||
uint64_t :16; // Fixed header
|
||||
// Byte 2
|
||||
uint64_t :4;
|
||||
uint64_t Id :2; // Device Number/Identifier
|
||||
uint64_t :2;
|
||||
// Byte 3-4
|
||||
uint64_t :16;
|
||||
// Byte 5
|
||||
uint64_t Cmd :8; // short codes:cmd; long codes:fixed value
|
||||
// Byte 6
|
||||
uint64_t RestLength :8; // Nr. of bytes in the message after this byte.
|
||||
// Byte 7
|
||||
uint64_t Protocol :8; // Seems like a protocol version number. Not sure.
|
||||
// Byte 8
|
||||
uint64_t Power :1;
|
||||
uint64_t Fahrenheit :1;
|
||||
uint64_t Temp :6; // Internal representation varies between models.
|
||||
// Byte 9
|
||||
uint64_t Mode :3;
|
||||
uint64_t Clean :1; // Also 10C Heat in ARREW4E.
|
||||
uint64_t TimerType :2;
|
||||
uint64_t :2;
|
||||
// Byte 10
|
||||
uint64_t Fan :3;
|
||||
uint64_t :1;
|
||||
uint64_t Swing :2;
|
||||
uint64_t :2;
|
||||
// Byte 11~13
|
||||
uint64_t OffTimer :11; // Also is the sleep timer value
|
||||
uint64_t OffTimerEnable :1;
|
||||
uint64_t OnTimer :11;
|
||||
uint64_t OnTimerEnable :1;
|
||||
// Byte 14
|
||||
uint64_t :3;
|
||||
uint64_t Filter :1;
|
||||
uint64_t :1;
|
||||
uint64_t unknown :1;
|
||||
uint64_t :1;
|
||||
uint64_t OutsideQuiet :1;
|
||||
// Byte 15
|
||||
uint64_t :0; // Checksum
|
||||
};
|
||||
};
|
||||
|
||||
// Constants
|
||||
const uint8_t kFujitsuAcModeAuto = 0x0; // 0b000
|
||||
const uint8_t kFujitsuAcModeCool = 0x1; // 0b001
|
||||
const uint8_t kFujitsuAcModeDry = 0x2; // 0b010
|
||||
const uint8_t kFujitsuAcModeFan = 0x3; // 0b011
|
||||
const uint8_t kFujitsuAcModeHeat = 0x4; // 0b100
|
||||
|
||||
const uint8_t kFujitsuAcCmdStayOn = 0x00; // b00000000
|
||||
const uint8_t kFujitsuAcCmdTurnOn = 0x01; // b00000001
|
||||
const uint8_t kFujitsuAcCmdTurnOff = 0x02; // b00000010
|
||||
const uint8_t kFujitsuAcCmdEcono = 0x09; // b00001001
|
||||
const uint8_t kFujitsuAcCmdPowerful = 0x39; // b00111001
|
||||
const uint8_t kFujitsuAcCmdStepVert = 0x6C; // b01101100
|
||||
const uint8_t kFujitsuAcCmdToggleSwingVert = 0x6D; // b01101101
|
||||
const uint8_t kFujitsuAcCmdStepHoriz = 0x79; // b01111001
|
||||
const uint8_t kFujitsuAcCmdToggleSwingHoriz = 0x7A; // b01111010
|
||||
|
||||
const uint8_t kFujitsuAcFanAuto = 0x00;
|
||||
const uint8_t kFujitsuAcFanHigh = 0x01;
|
||||
const uint8_t kFujitsuAcFanMed = 0x02;
|
||||
const uint8_t kFujitsuAcFanLow = 0x03;
|
||||
const uint8_t kFujitsuAcFanQuiet = 0x04;
|
||||
|
||||
const float kFujitsuAcMinHeat = 10; // 10C
|
||||
const float kFujitsuAcMinTemp = 16; // 16C
|
||||
const float kFujitsuAcMaxTemp = 30; // 30C
|
||||
const uint8_t kFujitsuAcTempOffsetC = kFujitsuAcMinTemp;
|
||||
const float kFujitsuAcMinHeatF = 50; // 50F
|
||||
const float kFujitsuAcMinTempF = 60; // 60F
|
||||
const float kFujitsuAcMaxTempF = 88; // 88F
|
||||
const uint8_t kFujitsuAcTempOffsetF = 44;
|
||||
|
||||
const uint8_t kFujitsuAcSwingOff = 0x00;
|
||||
const uint8_t kFujitsuAcSwingVert = 0x01;
|
||||
const uint8_t kFujitsuAcSwingHoriz = 0x02;
|
||||
const uint8_t kFujitsuAcSwingBoth = 0x03;
|
||||
|
||||
const uint8_t kFujitsuAcStopTimers = 0b00; // 0
|
||||
const uint8_t kFujitsuAcSleepTimer = 0b01; // 1
|
||||
const uint8_t kFujitsuAcOffTimer = 0b10; // 2
|
||||
const uint8_t kFujitsuAcOnTimer = 0b11; // 3
|
||||
const uint16_t kFujitsuAcTimerMax = 12 * 60; ///< Minutes.
|
||||
|
||||
// Legacy defines.
|
||||
#define FUJITSU_AC_MODE_AUTO kFujitsuAcModeAuto
|
||||
#define FUJITSU_AC_MODE_COOL kFujitsuAcModeCool
|
||||
#define FUJITSU_AC_MODE_DRY kFujitsuAcModeDry
|
||||
#define FUJITSU_AC_MODE_FAN kFujitsuAcModeFan
|
||||
#define FUJITSU_AC_MODE_HEAT kFujitsuAcModeHeat
|
||||
#define FUJITSU_AC_CMD_STAY_ON kFujitsuAcCmdStayOn
|
||||
#define FUJITSU_AC_CMD_TURN_ON kFujitsuAcCmdTurnOn
|
||||
#define FUJITSU_AC_CMD_TURN_OFF kFujitsuAcCmdTurnOff
|
||||
#define FUJITSU_AC_CMD_STEP_HORIZ kFujitsuAcCmdStepHoriz
|
||||
#define FUJITSU_AC_CMD_STEP_VERT kFujitsuAcCmdStepVert
|
||||
#define FUJITSU_AC_FAN_AUTO kFujitsuAcFanAuto
|
||||
#define FUJITSU_AC_FAN_HIGH kFujitsuAcFanHigh
|
||||
#define FUJITSU_AC_FAN_MED kFujitsuAcFanMed
|
||||
#define FUJITSU_AC_FAN_LOW kFujitsuAcFanLow
|
||||
#define FUJITSU_AC_FAN_QUIET kFujitsuAcFanQuiet
|
||||
#define FUJITSU_AC_MIN_TEMP kFujitsuAcMinTemp
|
||||
#define FUJITSU_AC_MAX_TEMP kFujitsuAcMaxTemp
|
||||
#define FUJITSU_AC_SWING_OFF kFujitsuAcSwingOff
|
||||
#define FUJITSU_AC_SWING_VERT kFujitsuAcSwingVert
|
||||
#define FUJITSU_AC_SWING_HORIZ kFujitsuAcSwingHoriz
|
||||
#define FUJITSU_AC_SWING_BOTH kFujitsuAcSwingBoth
|
||||
|
||||
/// Class for handling detailed Fujitsu A/C messages.
|
||||
class IRFujitsuAC {
|
||||
public:
|
||||
explicit IRFujitsuAC(const uint16_t pin,
|
||||
const fujitsu_ac_remote_model_t model = ARRAH2E,
|
||||
const bool inverted = false,
|
||||
const bool use_modulation = true);
|
||||
void setModel(const fujitsu_ac_remote_model_t model);
|
||||
fujitsu_ac_remote_model_t getModel(void) const;
|
||||
void stateReset(void);
|
||||
#if SEND_FUJITSU_AC
|
||||
void send(const uint16_t repeat = kFujitsuAcMinRepeat);
|
||||
/// 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_FUJITSU_AC
|
||||
void begin(void);
|
||||
void stepHoriz(void);
|
||||
void toggleSwingHoriz(const bool update = true);
|
||||
void stepVert(void);
|
||||
void toggleSwingVert(const bool update = true);
|
||||
void setCmd(const uint8_t cmd);
|
||||
uint8_t getCmd(void) const;
|
||||
void setTemp(const float temp, const bool useCelsius = true);
|
||||
float getTemp(void) const;
|
||||
void setFanSpeed(const uint8_t fan);
|
||||
uint8_t getFanSpeed(void) const;
|
||||
void setMode(const uint8_t mode);
|
||||
uint8_t getMode(void) const;
|
||||
void setSwing(const uint8_t mode);
|
||||
uint8_t getSwing(void) const;
|
||||
uint8_t* getRaw(void);
|
||||
bool setRaw(const uint8_t newState[], const uint16_t length);
|
||||
uint8_t getStateLength(void);
|
||||
static bool validChecksum(uint8_t* state, const uint16_t length);
|
||||
bool isLongCode(void) const;
|
||||
void setPower(const bool on);
|
||||
void off(void);
|
||||
void on(void);
|
||||
bool getPower(void) const;
|
||||
void setClean(const bool on);
|
||||
bool getClean(void) const;
|
||||
void setFilter(const bool on);
|
||||
bool getFilter(void) const;
|
||||
void set10CHeat(const bool on);
|
||||
bool get10CHeat(void) const;
|
||||
void setOutsideQuiet(const bool on);
|
||||
bool getOutsideQuiet(void) const;
|
||||
uint8_t getTimerType(void) const;
|
||||
void setTimerType(const uint8_t timertype);
|
||||
uint16_t getOnTimer(void) const;
|
||||
void setOnTimer(const uint16_t nr_mins);
|
||||
uint16_t getOffSleepTimer(void) const;
|
||||
void setOffTimer(const uint16_t nr_mins);
|
||||
void setSleepTimer(const uint16_t nr_mins);
|
||||
void setId(const uint8_t num);
|
||||
uint8_t getId(void) const;
|
||||
void setCelsius(const bool on);
|
||||
bool getCelsius(void) const;
|
||||
static uint8_t convertMode(const stdAc::opmode_t mode);
|
||||
static uint8_t convertFan(stdAc::fanspeed_t speed);
|
||||
static stdAc::opmode_t toCommonMode(const uint8_t mode);
|
||||
static stdAc::fanspeed_t toCommonFanSpeed(const uint8_t speed);
|
||||
stdAc::state_t toCommon(const stdAc::state_t *prev = NULL);
|
||||
String toString(void) const;
|
||||
#ifndef UNIT_TEST
|
||||
|
||||
private:
|
||||
IRsend _irsend; ///< Instance of the IR send class
|
||||
#else
|
||||
/// @cond IGNORE
|
||||
IRsendTest _irsend; ///< Instance of the testing IR send class
|
||||
/// @endcond
|
||||
#endif
|
||||
FujitsuProtocol _;
|
||||
uint8_t _cmd;
|
||||
fujitsu_ac_remote_model_t _model;
|
||||
uint8_t _state_length;
|
||||
uint8_t _state_length_short;
|
||||
bool _rawstatemodified;
|
||||
void checkSum(void);
|
||||
bool updateUseLongOrShort(void);
|
||||
void buildFromState(const uint16_t length);
|
||||
void setOffSleepTimer(const uint16_t nr_mins);
|
||||
};
|
||||
|
||||
#endif // IR_FUJITSU_H_
|
||||
95
yoRadio/src/IRremoteESP8266/ir_GICable.cpp
Normal file
95
yoRadio/src/IRremoteESP8266/ir_GICable.cpp
Normal file
@@ -0,0 +1,95 @@
|
||||
// Copyright 2018 David Conran
|
||||
|
||||
/// @file
|
||||
/// @brief G.I. Cable
|
||||
/// @see https://github.com/cyborg5/IRLib2/blob/master/IRLibProtocols/IRLib_P09_GICable.h
|
||||
/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/447
|
||||
|
||||
// Supports:
|
||||
// Brand: G.I. Cable, Model: XRC-200 remote
|
||||
|
||||
#define __STDC_LIMIT_MACROS
|
||||
#include <stdint.h>
|
||||
#include <algorithm>
|
||||
#include "IRrecv.h"
|
||||
#include "IRsend.h"
|
||||
#include "IRutils.h"
|
||||
|
||||
// Constants
|
||||
const uint16_t kGicableHdrMark = 9000;
|
||||
const uint16_t kGicableHdrSpace = 4400;
|
||||
const uint16_t kGicableBitMark = 550;
|
||||
const uint16_t kGicableOneSpace = 4400;
|
||||
const uint16_t kGicableZeroSpace = 2200;
|
||||
const uint16_t kGicableRptSpace = 2200;
|
||||
const uint32_t kGicableMinCommandLength = 99600;
|
||||
const uint32_t kGicableMinGap =
|
||||
kGicableMinCommandLength -
|
||||
(kGicableHdrMark + kGicableHdrSpace +
|
||||
kGicableBits * (kGicableBitMark + kGicableOneSpace) + kGicableBitMark);
|
||||
|
||||
#if SEND_GICABLE
|
||||
/// Send a raw G.I. Cable formatted message.
|
||||
/// Status: Alpha / Untested.
|
||||
/// @param[in] data The message to be sent.
|
||||
/// @param[in] nbits The number of bits of message to be sent.
|
||||
/// @param[in] repeat The number of times the command is to be repeated.
|
||||
void IRsend::sendGICable(uint64_t data, uint16_t nbits, uint16_t repeat) {
|
||||
sendGeneric(kGicableHdrMark, kGicableHdrSpace, kGicableBitMark,
|
||||
kGicableOneSpace, kGicableBitMark, kGicableZeroSpace,
|
||||
kGicableBitMark, kGicableMinGap, kGicableMinCommandLength, data,
|
||||
nbits, 39, true, 0, // Repeats are handled later.
|
||||
50);
|
||||
// Message repeat sequence.
|
||||
if (repeat)
|
||||
sendGeneric(kGicableHdrMark, kGicableRptSpace, 0, 0, 0,
|
||||
0, // No actual data sent.
|
||||
kGicableBitMark, kGicableMinGap, kGicableMinCommandLength, 0,
|
||||
0, // No data to be sent.
|
||||
39, true, repeat - 1, 50);
|
||||
}
|
||||
#endif // SEND_GICABLE
|
||||
|
||||
#if DECODE_GICABLE
|
||||
/// Decode the supplied G.I. Cable message.
|
||||
/// Status: Alpha / Not tested against a real device.
|
||||
/// @param[in,out] results Ptr to the data to decode & where to store the decode
|
||||
/// result.
|
||||
/// @param[in] offset The starting index to use when attempting to decode the
|
||||
/// raw data. Typically/Defaults to kStartOffset.
|
||||
/// @param[in] nbits The number of data bits to expect.
|
||||
/// @param[in] strict Flag indicating if we should perform strict matching.
|
||||
/// @return A boolean. True if it can decode it, false if it can't.
|
||||
bool IRrecv::decodeGICable(decode_results *results, uint16_t offset,
|
||||
const uint16_t nbits, const bool strict) {
|
||||
if (strict && nbits != kGicableBits)
|
||||
return false; // Not strictly an GICABLE message.
|
||||
|
||||
uint64_t data = 0;
|
||||
// Match Header + Data + Footer
|
||||
uint16_t used;
|
||||
used = matchGeneric(results->rawbuf + offset, &data,
|
||||
results->rawlen - offset, nbits,
|
||||
kGicableHdrMark, kGicableHdrSpace,
|
||||
kGicableBitMark, kGicableOneSpace,
|
||||
kGicableBitMark, kGicableZeroSpace,
|
||||
kGicableBitMark, kGicableMinGap, true);
|
||||
if (!used) return false;
|
||||
offset += used;
|
||||
// Compliance
|
||||
if (strict) {
|
||||
// We expect a repeat frame.
|
||||
if (!matchMark(results->rawbuf[offset++], kGicableHdrMark)) return false;
|
||||
if (!matchSpace(results->rawbuf[offset++], kGicableRptSpace)) return false;
|
||||
if (!matchMark(results->rawbuf[offset++], kGicableBitMark)) return false;
|
||||
}
|
||||
|
||||
// Success
|
||||
results->bits = nbits;
|
||||
results->value = data;
|
||||
results->decode_type = GICABLE;
|
||||
results->command = 0;
|
||||
results->address = 0;
|
||||
return true;
|
||||
}
|
||||
#endif // DECODE_GICABLE
|
||||
63
yoRadio/src/IRremoteESP8266/ir_GlobalCache.cpp
Normal file
63
yoRadio/src/IRremoteESP8266/ir_GlobalCache.cpp
Normal file
@@ -0,0 +1,63 @@
|
||||
// Copyright 2016 Hisham Khalifa
|
||||
// Copyright 2017 David Conran
|
||||
|
||||
/// @file
|
||||
/// @brief Global Cache IR format sender
|
||||
/// Originally added by Hisham Khalifa (http://www.hishamkhalifa.com)
|
||||
/// @see https://irdb.globalcache.com/Home/Database
|
||||
|
||||
// Supports:
|
||||
// Brand: Global Cache, Model: Control Tower IR DB
|
||||
|
||||
#include <algorithm>
|
||||
#include "IRsend.h"
|
||||
|
||||
// Constants
|
||||
const uint16_t kGlobalCacheMaxRepeat = 50;
|
||||
const uint32_t kGlobalCacheMinUsec = 80;
|
||||
const uint8_t kGlobalCacheFreqIndex = 0;
|
||||
const uint8_t kGlobalCacheRptIndex = kGlobalCacheFreqIndex + 1;
|
||||
const uint8_t kGlobalCacheRptStartIndex = kGlobalCacheRptIndex + 1;
|
||||
const uint8_t kGlobalCacheStartIndex = kGlobalCacheRptStartIndex + 1;
|
||||
|
||||
#if SEND_GLOBALCACHE
|
||||
/// Send a shortened GlobalCache (GC) IRdb/control tower formatted message.
|
||||
/// Status: STABLE / Known working.
|
||||
/// @param[in] buf Array of uint16_t containing the shortened GlobalCache data.
|
||||
/// @param[in] len Nr. of entries in the buf[] array.
|
||||
/// @note Global Cache format without the emitter ID or request ID.
|
||||
/// Starts at the frequency (Hertz), followed by nr. of times to emit (count),
|
||||
/// then the offset for repeats (where a repeat will start from),
|
||||
/// then the rest of entries are the actual IR message as units of periodic
|
||||
/// time.
|
||||
/// e.g. sendir,1:1,1,38000,1,1,9,70,9,30,9,... -> 38000,1,1,9,70,9,30,9,...
|
||||
/// @see https://irdb.globalcache.com/Home/Database
|
||||
void IRsend::sendGC(uint16_t buf[], uint16_t len) {
|
||||
uint16_t hz = buf[kGlobalCacheFreqIndex]; // GC frequency is in Hz.
|
||||
enableIROut(hz);
|
||||
uint32_t periodic_time = calcUSecPeriod(hz, false);
|
||||
uint8_t emits =
|
||||
std::min(buf[kGlobalCacheRptIndex], (uint16_t)kGlobalCacheMaxRepeat);
|
||||
// Repeat
|
||||
for (uint8_t repeat = 0; repeat < emits; repeat++) {
|
||||
// First time through, start at the beginning (kGlobalCacheStartIndex),
|
||||
// otherwise for repeats, we start a specified offset from that.
|
||||
uint16_t offset = kGlobalCacheStartIndex;
|
||||
if (repeat) offset += buf[kGlobalCacheRptStartIndex] - 1;
|
||||
// Data
|
||||
for (; offset < len; offset++) {
|
||||
// Convert periodic units to microseconds.
|
||||
// Minimum is kGlobalCacheMinUsec for actual GC units.
|
||||
uint32_t microseconds =
|
||||
std::max(buf[offset] * periodic_time, kGlobalCacheMinUsec);
|
||||
// These codes start at an odd index (not even as with sendRaw).
|
||||
if (offset & 1) // Odd bit.
|
||||
mark(microseconds);
|
||||
else // Even bit.
|
||||
space(microseconds);
|
||||
}
|
||||
}
|
||||
// It's possible that we've ended on a mark(), thus ensure the LED is off.
|
||||
ledOff();
|
||||
}
|
||||
#endif
|
||||
499
yoRadio/src/IRremoteESP8266/ir_Goodweather.cpp
Normal file
499
yoRadio/src/IRremoteESP8266/ir_Goodweather.cpp
Normal file
@@ -0,0 +1,499 @@
|
||||
// Copyright 2019 ribeirodanielf
|
||||
// Copyright 2019 David Conran
|
||||
/// @file
|
||||
/// @brief Support for Goodweather compatible HVAC protocols.
|
||||
/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/697
|
||||
|
||||
#include "ir_Goodweather.h"
|
||||
#include <algorithm>
|
||||
#ifndef ARDUINO
|
||||
#include <string>
|
||||
#endif
|
||||
#include "IRrecv.h"
|
||||
#include "IRremoteESP8266.h"
|
||||
#include "IRsend.h"
|
||||
#include "IRtext.h"
|
||||
#include "IRutils.h"
|
||||
|
||||
using irutils::addBoolToString;
|
||||
using irutils::addIntToString;
|
||||
using irutils::addLabeledString;
|
||||
using irutils::addModeToString;
|
||||
using irutils::addFanToString;
|
||||
using irutils::addTempToString;
|
||||
using irutils::addToggleToString;
|
||||
|
||||
#if SEND_GOODWEATHER
|
||||
/// Send a Goodweather HVAC formatted message.
|
||||
/// Status: BETA / Needs testing on real device.
|
||||
/// @param[in] data The message to be sent.
|
||||
/// @param[in] nbits The number of bits of message to be sent.
|
||||
/// @param[in] repeat The number of times the command is to be repeated.
|
||||
void IRsend::sendGoodweather(const uint64_t data, const uint16_t nbits,
|
||||
const uint16_t repeat) {
|
||||
if (nbits != kGoodweatherBits)
|
||||
return; // Wrong nr. of bits to send a proper message.
|
||||
// Set IR carrier frequency
|
||||
enableIROut(38);
|
||||
|
||||
for (uint16_t r = 0; r <= repeat; r++) {
|
||||
// Header
|
||||
mark(kGoodweatherHdrMark);
|
||||
space(kGoodweatherHdrSpace);
|
||||
|
||||
// Data
|
||||
for (int16_t i = 0; i < nbits; i += 8) {
|
||||
uint16_t chunk = (data >> i) & 0xFF; // Grab a byte at a time.
|
||||
chunk = (~chunk) << 8 | chunk; // Prepend a inverted copy of the byte.
|
||||
sendData(kGoodweatherBitMark, kGoodweatherOneSpace,
|
||||
kGoodweatherBitMark, kGoodweatherZeroSpace,
|
||||
chunk, 16, false);
|
||||
}
|
||||
// Footer
|
||||
mark(kGoodweatherBitMark);
|
||||
space(kGoodweatherHdrSpace);
|
||||
mark(kGoodweatherBitMark);
|
||||
space(kDefaultMessageGap);
|
||||
}
|
||||
}
|
||||
#endif // SEND_GOODWEATHER
|
||||
|
||||
/// Class constructor
|
||||
/// @param[in] pin GPIO to be used when sending.
|
||||
/// @param[in] inverted Is the output signal to be inverted?
|
||||
/// @param[in] use_modulation Is frequency modulation to be used?
|
||||
IRGoodweatherAc::IRGoodweatherAc(const uint16_t pin, const bool inverted,
|
||||
const bool use_modulation)
|
||||
: _irsend(pin, inverted, use_modulation) { stateReset(); }
|
||||
|
||||
/// Reset the internal state to a fixed known good state.
|
||||
void IRGoodweatherAc::stateReset(void) { _.raw = kGoodweatherStateInit; }
|
||||
|
||||
/// Set up hardware to be able to send a message.
|
||||
void IRGoodweatherAc::begin(void) { _irsend.begin(); }
|
||||
|
||||
#if SEND_GOODWEATHER
|
||||
/// Send the current internal state as an IR message.
|
||||
/// @param[in] repeat Nr. of times the message will be repeated.
|
||||
void IRGoodweatherAc::send(const uint16_t repeat) {
|
||||
_irsend.sendGoodweather(getRaw(), kGoodweatherBits, repeat);
|
||||
}
|
||||
#endif // SEND_GOODWEATHER
|
||||
|
||||
/// Get a copy of the internal state as a valid code for this protocol.
|
||||
/// @return A valid code for this protocol based on the current internal state.
|
||||
uint64_t IRGoodweatherAc::getRaw(void) { return _.raw; }
|
||||
|
||||
/// Set the internal state from a valid code for this protocol.
|
||||
/// @param[in] state A valid code for this protocol.
|
||||
void IRGoodweatherAc::setRaw(const uint64_t state) { _.raw = state; }
|
||||
|
||||
/// Change the power setting to On.
|
||||
void IRGoodweatherAc::on(void) { setPower(true); }
|
||||
|
||||
/// Change the power setting to Off.
|
||||
void IRGoodweatherAc::off(void) { setPower(false); }
|
||||
|
||||
/// Change the power setting.
|
||||
/// @param[in] on true, the setting is on. false, the setting is off.
|
||||
void IRGoodweatherAc::setPower(const bool on) {
|
||||
_.Command = kGoodweatherCmdPower;
|
||||
_.Power = on;
|
||||
}
|
||||
|
||||
/// Get the value of the current power setting.
|
||||
/// @return true, the setting is on. false, the setting is off.
|
||||
bool IRGoodweatherAc::getPower(void) const {
|
||||
return _.Power;
|
||||
}
|
||||
|
||||
/// Set the temperature.
|
||||
/// @param[in] temp The temperature in degrees celsius.
|
||||
void IRGoodweatherAc::setTemp(const uint8_t temp) {
|
||||
uint8_t new_temp = std::max(kGoodweatherTempMin, temp);
|
||||
new_temp = std::min(kGoodweatherTempMax, new_temp);
|
||||
if (new_temp > getTemp()) _.Command = kGoodweatherCmdUpTemp;
|
||||
if (new_temp < getTemp()) _.Command = kGoodweatherCmdDownTemp;
|
||||
_.Temp = new_temp - kGoodweatherTempMin;
|
||||
}
|
||||
|
||||
/// Get the current temperature setting.
|
||||
/// @return The current setting for temp. in degrees celsius.
|
||||
uint8_t IRGoodweatherAc::getTemp(void) const {
|
||||
return _.Temp + kGoodweatherTempMin;
|
||||
}
|
||||
|
||||
/// Set the speed of the fan.
|
||||
/// @param[in] speed The desired setting.
|
||||
void IRGoodweatherAc::setFan(const uint8_t speed) {
|
||||
_.Command = kGoodweatherCmdFan;
|
||||
switch (speed) {
|
||||
case kGoodweatherFanAuto:
|
||||
case kGoodweatherFanLow:
|
||||
case kGoodweatherFanMed:
|
||||
case kGoodweatherFanHigh:
|
||||
_.Fan = speed;
|
||||
break;
|
||||
default:
|
||||
_.Fan = kGoodweatherFanAuto;
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the current fan speed setting.
|
||||
/// @return The current fan speed.
|
||||
uint8_t IRGoodweatherAc::getFan(void) const {
|
||||
return _.Fan;
|
||||
}
|
||||
|
||||
/// Set the operating mode of the A/C.
|
||||
/// @param[in] mode The desired operating mode.
|
||||
void IRGoodweatherAc::setMode(const uint8_t mode) {
|
||||
_.Command = kGoodweatherCmdMode;
|
||||
switch (mode) {
|
||||
case kGoodweatherAuto:
|
||||
case kGoodweatherDry:
|
||||
case kGoodweatherCool:
|
||||
case kGoodweatherFan:
|
||||
case kGoodweatherHeat:
|
||||
_.Mode = mode;
|
||||
break;
|
||||
default:
|
||||
_.Mode = kGoodweatherAuto;
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the operating mode setting of the A/C.
|
||||
/// @return The current operating mode setting.
|
||||
uint8_t IRGoodweatherAc::getMode(void) const {
|
||||
return _.Mode;
|
||||
}
|
||||
|
||||
/// Set the Light (LED) Toggle setting of the A/C.
|
||||
/// @param[in] toggle true, the setting is on. false, the setting is off.
|
||||
void IRGoodweatherAc::setLight(const bool toggle) {
|
||||
_.Command = kGoodweatherCmdLight;
|
||||
_.Light = toggle;
|
||||
}
|
||||
|
||||
/// Get the Light (LED) Toggle setting of the A/C.
|
||||
/// @return true, the setting is on. false, the setting is off.
|
||||
bool IRGoodweatherAc::getLight(void) const {
|
||||
return _.Light;
|
||||
}
|
||||
|
||||
/// Set the Sleep Toggle setting of the A/C.
|
||||
/// @param[in] toggle true, the setting is on. false, the setting is off.
|
||||
void IRGoodweatherAc::setSleep(const bool toggle) {
|
||||
_.Command = kGoodweatherCmdSleep;
|
||||
_.Sleep = toggle;
|
||||
}
|
||||
|
||||
/// Get the Sleep Toggle setting of the A/C.
|
||||
/// @return true, the setting is on. false, the setting is off.
|
||||
bool IRGoodweatherAc::getSleep(void) const {
|
||||
return _.Sleep;
|
||||
}
|
||||
|
||||
/// Set the Turbo Toggle setting of the A/C.
|
||||
/// @param[in] toggle true, the setting is on. false, the setting is off.
|
||||
void IRGoodweatherAc::setTurbo(const bool toggle) {
|
||||
_.Command = kGoodweatherCmdTurbo;
|
||||
_.Turbo = toggle;
|
||||
}
|
||||
|
||||
/// Get the Turbo Toggle setting of the A/C.
|
||||
/// @return true, the setting is on. false, the setting is off.
|
||||
bool IRGoodweatherAc::getTurbo(void) const {
|
||||
return _.Turbo;
|
||||
}
|
||||
|
||||
/// Set the Vertical Swing speed of the A/C.
|
||||
/// @param[in] speed The speed to set the swing to.
|
||||
void IRGoodweatherAc::setSwing(const uint8_t speed) {
|
||||
_.Command = kGoodweatherCmdSwing;
|
||||
switch (speed) {
|
||||
case kGoodweatherSwingOff:
|
||||
case kGoodweatherSwingSlow:
|
||||
case kGoodweatherSwingFast:
|
||||
_.Swing = speed;
|
||||
break;
|
||||
default:
|
||||
_.Swing = kGoodweatherSwingOff;
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the Vertical Swing speed of the A/C.
|
||||
/// @return The native swing speed setting.
|
||||
uint8_t IRGoodweatherAc::getSwing(void) const {
|
||||
return _.Swing;
|
||||
}
|
||||
|
||||
/// Set the remote Command type/button pressed.
|
||||
/// @param[in] cmd The command/button that was issued/pressed.
|
||||
void IRGoodweatherAc::setCommand(const uint8_t cmd) {
|
||||
if (cmd <= kGoodweatherCmdLight)
|
||||
_.Command = cmd;
|
||||
}
|
||||
|
||||
/// Get the Command type/button pressed from the current settings
|
||||
/// @return The command/button that was issued/pressed.
|
||||
uint8_t IRGoodweatherAc::getCommand(void) const {
|
||||
return _.Command;
|
||||
}
|
||||
|
||||
/// Convert a stdAc::opmode_t enum into its native mode.
|
||||
/// @param[in] mode The enum to be converted.
|
||||
/// @return The native equivalent of the enum.
|
||||
uint8_t IRGoodweatherAc::convertMode(const stdAc::opmode_t mode) {
|
||||
switch (mode) {
|
||||
case stdAc::opmode_t::kCool: return kGoodweatherCool;
|
||||
case stdAc::opmode_t::kHeat: return kGoodweatherHeat;
|
||||
case stdAc::opmode_t::kDry: return kGoodweatherDry;
|
||||
case stdAc::opmode_t::kFan: return kGoodweatherFan;
|
||||
default: return kGoodweatherAuto;
|
||||
}
|
||||
}
|
||||
|
||||
/// Convert a stdAc::fanspeed_t enum into it's native speed.
|
||||
/// @param[in] speed The enum to be converted.
|
||||
/// @return The native equivalent of the enum.
|
||||
uint8_t IRGoodweatherAc::convertFan(const stdAc::fanspeed_t speed) {
|
||||
switch (speed) {
|
||||
case stdAc::fanspeed_t::kMin:
|
||||
case stdAc::fanspeed_t::kLow: return kGoodweatherFanLow;
|
||||
case stdAc::fanspeed_t::kMedium: return kGoodweatherFanMed;
|
||||
case stdAc::fanspeed_t::kHigh:
|
||||
case stdAc::fanspeed_t::kMax: return kGoodweatherFanHigh;
|
||||
default: return kGoodweatherFanAuto;
|
||||
}
|
||||
}
|
||||
|
||||
/// Convert a stdAc::swingv_t enum into it's native setting.
|
||||
/// @param[in] swingv The enum to be converted.
|
||||
/// @return The native equivalent of the enum.
|
||||
uint8_t IRGoodweatherAc::convertSwingV(const stdAc::swingv_t swingv) {
|
||||
switch (swingv) {
|
||||
case stdAc::swingv_t::kHighest:
|
||||
case stdAc::swingv_t::kHigh:
|
||||
case stdAc::swingv_t::kMiddle: return kGoodweatherSwingFast;
|
||||
case stdAc::swingv_t::kLow:
|
||||
case stdAc::swingv_t::kLowest:
|
||||
case stdAc::swingv_t::kAuto: return kGoodweatherSwingSlow;
|
||||
default: return kGoodweatherSwingOff;
|
||||
}
|
||||
}
|
||||
|
||||
/// Convert a native mode into its stdAc equivalent.
|
||||
/// @param[in] mode The native setting to be converted.
|
||||
/// @return The stdAc equivalent of the native setting.
|
||||
stdAc::opmode_t IRGoodweatherAc::toCommonMode(const uint8_t mode) {
|
||||
switch (mode) {
|
||||
case kGoodweatherCool: return stdAc::opmode_t::kCool;
|
||||
case kGoodweatherHeat: return stdAc::opmode_t::kHeat;
|
||||
case kGoodweatherDry: return stdAc::opmode_t::kDry;
|
||||
case kGoodweatherFan: return stdAc::opmode_t::kFan;
|
||||
default: return stdAc::opmode_t::kAuto;
|
||||
}
|
||||
}
|
||||
|
||||
/// Convert a native fan speed into its stdAc equivalent.
|
||||
/// @param[in] speed The native setting to be converted.
|
||||
/// @return The stdAc equivalent of the native setting.
|
||||
stdAc::fanspeed_t IRGoodweatherAc::toCommonFanSpeed(const uint8_t speed) {
|
||||
switch (speed) {
|
||||
case kGoodweatherFanHigh: return stdAc::fanspeed_t::kMax;
|
||||
case kGoodweatherFanMed: return stdAc::fanspeed_t::kMedium;
|
||||
case kGoodweatherFanLow: return stdAc::fanspeed_t::kMin;
|
||||
default: return stdAc::fanspeed_t::kAuto;
|
||||
}
|
||||
}
|
||||
|
||||
/// Convert the current internal state into its stdAc::state_t equivalent.
|
||||
/// @return The stdAc equivalent of the native settings.
|
||||
stdAc::state_t IRGoodweatherAc::toCommon(void) const {
|
||||
stdAc::state_t result{};
|
||||
result.protocol = decode_type_t::GOODWEATHER;
|
||||
result.power = _.Power;
|
||||
result.mode = toCommonMode(_.Mode);
|
||||
result.celsius = true;
|
||||
result.degrees = getTemp();
|
||||
result.fanspeed = toCommonFanSpeed(_.Fan);
|
||||
result.swingv = (_.Swing == kGoodweatherSwingOff ?
|
||||
stdAc::swingv_t::kOff : stdAc::swingv_t::kAuto);
|
||||
result.turbo = _.Turbo;
|
||||
result.light = _.Light;
|
||||
result.sleep = _.Sleep ? 0: -1;
|
||||
// Not supported.
|
||||
result.model = -1;
|
||||
result.swingh = stdAc::swingh_t::kOff;
|
||||
result.quiet = false;
|
||||
result.econo = false;
|
||||
result.filter = false;
|
||||
result.clean = false;
|
||||
result.beep = false;
|
||||
result.clock = -1;
|
||||
return result;
|
||||
}
|
||||
|
||||
/// Convert the current internal state into a human readable string.
|
||||
/// @return A human readable string.
|
||||
String IRGoodweatherAc::toString(void) const {
|
||||
String result = "";
|
||||
result.reserve(150); // Reserve some heap for the string to reduce fragging.
|
||||
result += addBoolToString(_.Power, kPowerStr, false);
|
||||
result += addModeToString(_.Mode, kGoodweatherAuto, kGoodweatherCool,
|
||||
kGoodweatherHeat, kGoodweatherDry, kGoodweatherFan);
|
||||
result += addTempToString(getTemp());
|
||||
result += addFanToString(_.Fan, kGoodweatherFanHigh, kGoodweatherFanLow,
|
||||
kGoodweatherFanAuto, kGoodweatherFanAuto,
|
||||
kGoodweatherFanMed);
|
||||
|
||||
result += addToggleToString(_.Turbo, kTurboStr);
|
||||
result += addToggleToString(_.Light, kLightStr);
|
||||
result += addToggleToString(_.Sleep, kSleepStr);
|
||||
result += addIntToString(_.Swing, kSwingStr);
|
||||
result += kSpaceLBraceStr;
|
||||
switch (_.Swing) {
|
||||
case kGoodweatherSwingFast:
|
||||
result += kFastStr;
|
||||
break;
|
||||
case kGoodweatherSwingSlow:
|
||||
result += kSlowStr;
|
||||
break;
|
||||
case kGoodweatherSwingOff:
|
||||
result += kOffStr;
|
||||
break;
|
||||
default:
|
||||
result += kUnknownStr;
|
||||
}
|
||||
result += ')';
|
||||
result += addIntToString(_.Command, kCommandStr);
|
||||
result += kSpaceLBraceStr;
|
||||
switch (_.Command) {
|
||||
case kGoodweatherCmdPower:
|
||||
result += kPowerStr;
|
||||
break;
|
||||
case kGoodweatherCmdMode:
|
||||
result += kModeStr;
|
||||
break;
|
||||
case kGoodweatherCmdUpTemp:
|
||||
result += kTempUpStr;
|
||||
break;
|
||||
case kGoodweatherCmdDownTemp:
|
||||
result += kTempDownStr;
|
||||
break;
|
||||
case kGoodweatherCmdSwing:
|
||||
result += kSwingStr;
|
||||
break;
|
||||
case kGoodweatherCmdFan:
|
||||
result += kFanStr;
|
||||
break;
|
||||
case kGoodweatherCmdTimer:
|
||||
result += kTimerStr;
|
||||
break;
|
||||
case kGoodweatherCmdAirFlow:
|
||||
result += kAirFlowStr;
|
||||
break;
|
||||
case kGoodweatherCmdHold:
|
||||
result += kHoldStr;
|
||||
break;
|
||||
case kGoodweatherCmdSleep:
|
||||
result += kSleepStr;
|
||||
break;
|
||||
case kGoodweatherCmdTurbo:
|
||||
result += kTurboStr;
|
||||
break;
|
||||
case kGoodweatherCmdLight:
|
||||
result += kLightStr;
|
||||
break;
|
||||
default:
|
||||
result += kUnknownStr;
|
||||
}
|
||||
result += ')';
|
||||
return result;
|
||||
}
|
||||
|
||||
#if DECODE_GOODWEATHER
|
||||
/// Decode the supplied Goodweather message.
|
||||
/// Status: BETA / Probably works.
|
||||
/// @param[in,out] results Ptr to the data to decode & where to store the decode
|
||||
/// result.
|
||||
/// @param[in] offset The starting index to use when attempting to decode the
|
||||
/// raw data. Typically/Defaults to kStartOffset.
|
||||
/// @param[in] nbits The number of data bits to expect.
|
||||
/// @param[in] strict Flag indicating if we should perform strict matching.
|
||||
/// @return A boolean. True if it can decode it, false if it can't.
|
||||
bool IRrecv::decodeGoodweather(decode_results* results, uint16_t offset,
|
||||
const uint16_t nbits,
|
||||
const bool strict) {
|
||||
if (results->rawlen < 2 * (2 * nbits) + kHeader + 2 * kFooter - 1 + offset)
|
||||
return false; // Can't possibly be a valid Goodweather message.
|
||||
if (strict && nbits != kGoodweatherBits)
|
||||
return false; // Not strictly a Goodweather message.
|
||||
|
||||
uint64_t dataSoFar = 0;
|
||||
uint16_t dataBitsSoFar = 0;
|
||||
match_result_t data_result;
|
||||
|
||||
// Header
|
||||
if (!matchMark(results->rawbuf[offset++], kGoodweatherHdrMark)) return false;
|
||||
if (!matchSpace(results->rawbuf[offset++], kGoodweatherHdrSpace))
|
||||
return false;
|
||||
|
||||
// Data
|
||||
for (; offset <= results->rawlen - 32 && dataBitsSoFar < nbits;
|
||||
dataBitsSoFar += 8) {
|
||||
DPRINT("DEBUG: Attempting Byte #");
|
||||
DPRINTLN(dataBitsSoFar / 8);
|
||||
// Read in a byte at a time.
|
||||
// Normal first.
|
||||
data_result = matchData(&(results->rawbuf[offset]), 8,
|
||||
kGoodweatherBitMark, kGoodweatherOneSpace,
|
||||
kGoodweatherBitMark, kGoodweatherZeroSpace,
|
||||
_tolerance + kGoodweatherExtraTolerance,
|
||||
kMarkExcess, false);
|
||||
if (data_result.success == false) return false;
|
||||
DPRINTLN("DEBUG: Normal byte read okay.");
|
||||
offset += data_result.used;
|
||||
uint8_t data = (uint8_t)data_result.data;
|
||||
// Then inverted.
|
||||
data_result = matchData(&(results->rawbuf[offset]), 8,
|
||||
kGoodweatherBitMark, kGoodweatherOneSpace,
|
||||
kGoodweatherBitMark, kGoodweatherZeroSpace,
|
||||
_tolerance + kGoodweatherExtraTolerance,
|
||||
kMarkExcess, false);
|
||||
if (data_result.success == false) return false;
|
||||
DPRINTLN("DEBUG: Inverted byte read okay.");
|
||||
offset += data_result.used;
|
||||
uint8_t inverted = (uint8_t)data_result.data;
|
||||
DPRINT("DEBUG: data = ");
|
||||
DPRINTLN((uint16_t)data);
|
||||
DPRINT("DEBUG: inverted = ");
|
||||
DPRINTLN((uint16_t)inverted);
|
||||
if (data != (inverted ^ 0xFF)) return false; // Data integrity failed.
|
||||
dataSoFar |= (uint64_t)data << dataBitsSoFar;
|
||||
}
|
||||
|
||||
// Footer.
|
||||
if (!matchMark(results->rawbuf[offset++], kGoodweatherBitMark,
|
||||
_tolerance + kGoodweatherExtraTolerance)) return false;
|
||||
if (!matchSpace(results->rawbuf[offset++], kGoodweatherHdrSpace))
|
||||
return false;
|
||||
if (!matchMark(results->rawbuf[offset++], kGoodweatherBitMark,
|
||||
_tolerance + kGoodweatherExtraTolerance)) return false;
|
||||
if (offset <= results->rawlen &&
|
||||
!matchAtLeast(results->rawbuf[offset], kGoodweatherHdrSpace))
|
||||
return false;
|
||||
|
||||
// Compliance
|
||||
if (strict && (dataBitsSoFar != kGoodweatherBits)) return false;
|
||||
|
||||
// Success
|
||||
results->decode_type = decode_type_t::GOODWEATHER;
|
||||
results->bits = dataBitsSoFar;
|
||||
results->value = dataSoFar;
|
||||
results->address = 0;
|
||||
results->command = 0;
|
||||
return true;
|
||||
}
|
||||
#endif // DECODE_GOODWEATHER
|
||||
154
yoRadio/src/IRremoteESP8266/ir_Goodweather.h
Normal file
154
yoRadio/src/IRremoteESP8266/ir_Goodweather.h
Normal file
@@ -0,0 +1,154 @@
|
||||
// Copyright 2019 ribeirodanielf
|
||||
// Copyright 2019 David Conran
|
||||
|
||||
/// @file
|
||||
/// @brief Support for Goodweather compatible HVAC protocols.
|
||||
/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/697
|
||||
|
||||
// Supports:
|
||||
// Brand: Goodweather, Model: ZH/JT-03 remote
|
||||
|
||||
#ifndef IR_GOODWEATHER_H_
|
||||
#define IR_GOODWEATHER_H_
|
||||
|
||||
#define __STDC_LIMIT_MACROS
|
||||
#include <stdint.h>
|
||||
#ifndef UNIT_TEST
|
||||
#include <Arduino.h>
|
||||
#endif
|
||||
#include "IRremoteESP8266.h"
|
||||
#include "IRsend.h"
|
||||
#ifdef UNIT_TEST
|
||||
#include "IRsend_test.h"
|
||||
#endif
|
||||
|
||||
/// Native representation of a Goodweather A/C message.
|
||||
union GoodweatherProtocol {
|
||||
uint64_t raw; ///< The state of the IR remote in IR code form.
|
||||
struct {
|
||||
// Byte 0
|
||||
uint8_t :8;
|
||||
// Byte 1
|
||||
uint8_t Light :1;
|
||||
uint8_t :2;
|
||||
uint8_t Turbo :1;
|
||||
uint8_t :0;
|
||||
// Byte 2
|
||||
uint8_t Command :4;
|
||||
uint8_t :0;
|
||||
// Byte 3
|
||||
uint8_t Sleep :1;
|
||||
uint8_t Power :1;
|
||||
uint8_t Swing :2;
|
||||
uint8_t AirFlow :1;
|
||||
uint8_t Fan :2;
|
||||
uint8_t :0;
|
||||
// Byte 4
|
||||
uint8_t Temp :4;
|
||||
uint8_t :1;
|
||||
uint8_t Mode :3;
|
||||
uint8_t :0;
|
||||
};
|
||||
};
|
||||
|
||||
// Constants
|
||||
// Timing
|
||||
const uint16_t kGoodweatherBitMark = 580;
|
||||
const uint16_t kGoodweatherOneSpace = 580;
|
||||
const uint16_t kGoodweatherZeroSpace = 1860;
|
||||
const uint16_t kGoodweatherHdrMark = 6820;
|
||||
const uint16_t kGoodweatherHdrSpace = 6820;
|
||||
const uint8_t kGoodweatherExtraTolerance = 12; // +12% extra
|
||||
|
||||
// Modes
|
||||
const uint8_t kGoodweatherAuto = 0b000;
|
||||
const uint8_t kGoodweatherCool = 0b001;
|
||||
const uint8_t kGoodweatherDry = 0b010;
|
||||
const uint8_t kGoodweatherFan = 0b011;
|
||||
const uint8_t kGoodweatherHeat = 0b100;
|
||||
// Swing
|
||||
const uint8_t kGoodweatherSwingFast = 0b00;
|
||||
const uint8_t kGoodweatherSwingSlow = 0b01;
|
||||
const uint8_t kGoodweatherSwingOff = 0b10;
|
||||
// Fan Control
|
||||
const uint8_t kGoodweatherFanAuto = 0b00;
|
||||
const uint8_t kGoodweatherFanHigh = 0b01;
|
||||
const uint8_t kGoodweatherFanMed = 0b10;
|
||||
const uint8_t kGoodweatherFanLow = 0b11;
|
||||
// Temperature
|
||||
const uint8_t kGoodweatherTempMin = 16; // Celsius
|
||||
const uint8_t kGoodweatherTempMax = 31; // Celsius
|
||||
// Commands
|
||||
const uint8_t kGoodweatherCmdPower = 0x00;
|
||||
const uint8_t kGoodweatherCmdMode = 0x01;
|
||||
const uint8_t kGoodweatherCmdUpTemp = 0x02;
|
||||
const uint8_t kGoodweatherCmdDownTemp = 0x03;
|
||||
const uint8_t kGoodweatherCmdSwing = 0x04;
|
||||
const uint8_t kGoodweatherCmdFan = 0x05;
|
||||
const uint8_t kGoodweatherCmdTimer = 0x06;
|
||||
const uint8_t kGoodweatherCmdAirFlow = 0x07;
|
||||
const uint8_t kGoodweatherCmdHold = 0x08;
|
||||
const uint8_t kGoodweatherCmdSleep = 0x09;
|
||||
const uint8_t kGoodweatherCmdTurbo = 0x0A;
|
||||
const uint8_t kGoodweatherCmdLight = 0x0B;
|
||||
// PAD EOF
|
||||
const uint64_t kGoodweatherStateInit = 0xD50000000000;
|
||||
|
||||
|
||||
// Classes
|
||||
/// Class for handling detailed Goodweather A/C messages.
|
||||
class IRGoodweatherAc {
|
||||
public:
|
||||
explicit IRGoodweatherAc(const uint16_t pin, const bool inverted = false,
|
||||
const bool use_modulation = true);
|
||||
void stateReset(void);
|
||||
#if SEND_GOODWEATHER
|
||||
void send(const uint16_t repeat = kGoodweatherMinRepeat);
|
||||
/// 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_GOODWEATHER
|
||||
void begin(void);
|
||||
void on(void);
|
||||
void off(void);
|
||||
void setPower(const bool on);
|
||||
bool getPower(void) const;
|
||||
void setTemp(const uint8_t temp);
|
||||
uint8_t getTemp(void) const;
|
||||
void setFan(const uint8_t speed);
|
||||
uint8_t getFan(void) const;
|
||||
void setMode(const uint8_t mode);
|
||||
uint8_t getMode(void) const;
|
||||
void setSwing(const uint8_t speed);
|
||||
uint8_t getSwing(void) const;
|
||||
void setSleep(const bool toggle);
|
||||
bool getSleep(void) const;
|
||||
void setTurbo(const bool toggle);
|
||||
bool getTurbo(void) const;
|
||||
void setLight(const bool toggle);
|
||||
bool getLight(void) const;
|
||||
void setCommand(const uint8_t cmd);
|
||||
uint8_t getCommand(void) const;
|
||||
uint64_t getRaw(void);
|
||||
void setRaw(const uint64_t state);
|
||||
static uint8_t convertMode(const stdAc::opmode_t mode);
|
||||
static uint8_t convertFan(const stdAc::fanspeed_t speed);
|
||||
static uint8_t convertSwingV(const stdAc::swingv_t swingv);
|
||||
static stdAc::opmode_t toCommonMode(const uint8_t mode);
|
||||
static stdAc::fanspeed_t toCommonFanSpeed(const uint8_t speed);
|
||||
stdAc::state_t toCommon(void) const;
|
||||
String toString(void) const;
|
||||
#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
|
||||
GoodweatherProtocol _;
|
||||
};
|
||||
#endif // IR_GOODWEATHER_H_
|
||||
71
yoRadio/src/IRremoteESP8266/ir_Gorenje.cpp
Normal file
71
yoRadio/src/IRremoteESP8266/ir_Gorenje.cpp
Normal file
@@ -0,0 +1,71 @@
|
||||
// Copyright 2022 Mateusz Bronk (mbronk)
|
||||
/// @file
|
||||
/// @brief Support for the Gorenje cooker hood IR protocols.
|
||||
/// @see https://techfresh.pl/wp-content/uploads/2017/08/Gorenje-DKF-2600-MWT.pdf
|
||||
/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1887
|
||||
|
||||
// Supports:
|
||||
// Brand: Gorenje, Model: DKF 2600 MWT Cooker Hood
|
||||
|
||||
#include "IRrecv.h"
|
||||
#include "IRsend.h"
|
||||
#include "IRutils.h"
|
||||
|
||||
const uint32_t kGorenjeMinGap = 100000U; // 0.1s
|
||||
const uint16_t kGorenjeHdrMark = 0;
|
||||
const uint32_t kGorenjeHdrSpace = 0;
|
||||
const uint16_t kGorenjeBitMark = 1300;
|
||||
const uint32_t kGorenjeOneSpace = 5700;
|
||||
const uint32_t kGorenjeZeroSpace = 1700;
|
||||
const uint16_t kGorenjeFreq = 38000; // Hz
|
||||
const uint16_t kGorenjeTolerance = 7; // %
|
||||
|
||||
#if SEND_GORENJE
|
||||
/// Send a Gorenje Cooker Hood formatted message.
|
||||
/// Status: STABLE / Known working.
|
||||
/// @param[in] data containing the IR command to be sent.
|
||||
/// @param[in] nbits Nr. of bits of the message to send. usually kGorenjeBits
|
||||
/// @param[in] repeat Nr. of times the message is to be repeated.
|
||||
void IRsend::sendGorenje(const uint64_t data, const uint16_t nbits,
|
||||
const uint16_t repeat) {
|
||||
sendGeneric(kGorenjeHdrMark, kGorenjeHdrSpace,
|
||||
kGorenjeBitMark, kGorenjeOneSpace,
|
||||
kGorenjeBitMark, kGorenjeZeroSpace,
|
||||
kGorenjeBitMark, kGorenjeMinGap,
|
||||
data, nbits, kGorenjeFreq, true, repeat, kDutyDefault);
|
||||
}
|
||||
#endif // SEND_GORENJE
|
||||
|
||||
#if DECODE_GORENJE
|
||||
/// Decode the supplied Gorenje Cooker Hood message.
|
||||
/// Status: STABLE / Known working.
|
||||
/// @param[in,out] results Ptr to the data to decode & where to store the
|
||||
/// decoded result
|
||||
/// @param[in] offset The starting index to use when attempting to decode the
|
||||
/// raw data. Typically/Defaults to kStartOffset.
|
||||
/// @param[in] nbits The number of data bits to expect.
|
||||
/// @param[in] strict Flag indicating if we should perform strict matching.
|
||||
/// @return A boolean. True if it can decode it, false if it can't.
|
||||
bool IRrecv::decodeGorenje(decode_results *results, uint16_t offset,
|
||||
const uint16_t nbits, const bool strict) {
|
||||
if (strict && nbits != kGorenjeBits)
|
||||
return false; // We expect Gorenje to be a certain sized message.
|
||||
|
||||
uint64_t data = 0;
|
||||
if (!matchGeneric(results->rawbuf + offset, &data,
|
||||
results->rawlen - offset, nbits,
|
||||
kGorenjeHdrMark, kGorenjeHdrSpace,
|
||||
kGorenjeBitMark, kGorenjeOneSpace,
|
||||
kGorenjeBitMark, kGorenjeZeroSpace,
|
||||
kGorenjeBitMark, kGorenjeMinGap,
|
||||
true, kGorenjeTolerance)) return false;
|
||||
|
||||
// Matched!
|
||||
results->bits = nbits;
|
||||
results->value = data;
|
||||
results->decode_type = decode_type_t::GORENJE;
|
||||
results->command = 0;
|
||||
results->address = 0;
|
||||
return true;
|
||||
}
|
||||
#endif // DECODE_GORENJE
|
||||
750
yoRadio/src/IRremoteESP8266/ir_Gree.cpp
Normal file
750
yoRadio/src/IRremoteESP8266/ir_Gree.cpp
Normal file
@@ -0,0 +1,750 @@
|
||||
// Copyright 2017 Ville Skyttä (scop)
|
||||
// Copyright 2017, 2018 David Conran
|
||||
|
||||
/// @file
|
||||
/// @brief Support for Gree A/C protocols.
|
||||
/// @see https://github.com/ToniA/arduino-heatpumpir/blob/master/GreeHeatpumpIR.h
|
||||
/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1508
|
||||
|
||||
#include "ir_Gree.h"
|
||||
#include <algorithm>
|
||||
#include <cstring>
|
||||
#ifndef ARDUINO
|
||||
#include <string>
|
||||
#endif
|
||||
#include "IRrecv.h"
|
||||
#include "IRremoteESP8266.h"
|
||||
#include "IRsend.h"
|
||||
#include "IRtext.h"
|
||||
#include "IRutils.h"
|
||||
#include "ir_Kelvinator.h"
|
||||
|
||||
// Constants
|
||||
const uint16_t kGreeHdrMark = 9000;
|
||||
const uint16_t kGreeHdrSpace = 4500; ///< See #684 & real example in unit tests
|
||||
const uint16_t kGreeBitMark = 620;
|
||||
const uint16_t kGreeOneSpace = 1600;
|
||||
const uint16_t kGreeZeroSpace = 540;
|
||||
const uint16_t kGreeMsgSpace = 19980; ///< See #1508, #386, & Kelvinator
|
||||
const uint8_t kGreeBlockFooter = 0b010;
|
||||
const uint8_t kGreeBlockFooterBits = 3;
|
||||
|
||||
using irutils::addBoolToString;
|
||||
using irutils::addIntToString;
|
||||
using irutils::addLabeledString;
|
||||
using irutils::addModeToString;
|
||||
using irutils::addModelToString;
|
||||
using irutils::addFanToString;
|
||||
using irutils::addSwingHToString;
|
||||
using irutils::addTempToString;
|
||||
using irutils::minsToString;
|
||||
|
||||
#if SEND_GREE
|
||||
/// Send a Gree Heat Pump formatted message.
|
||||
/// Status: STABLE / Working.
|
||||
/// @param[in] data The message to be sent.
|
||||
/// @param[in] nbytes The number of bytes of message to be sent.
|
||||
/// @param[in] repeat The number of times the command is to be repeated.
|
||||
void IRsend::sendGree(const uint8_t data[], const uint16_t nbytes,
|
||||
const uint16_t repeat) {
|
||||
if (nbytes < kGreeStateLength)
|
||||
return; // Not enough bytes to send a proper message.
|
||||
|
||||
for (uint16_t r = 0; r <= repeat; r++) {
|
||||
// Block #1
|
||||
sendGeneric(kGreeHdrMark, kGreeHdrSpace, kGreeBitMark, kGreeOneSpace,
|
||||
kGreeBitMark, kGreeZeroSpace, 0, 0, // No Footer.
|
||||
data, 4, 38, false, 0, 50);
|
||||
// Footer #1
|
||||
sendGeneric(0, 0, // No Header
|
||||
kGreeBitMark, kGreeOneSpace, kGreeBitMark, kGreeZeroSpace,
|
||||
kGreeBitMark, kGreeMsgSpace, 0b010, 3, 38, false, 0, 50);
|
||||
|
||||
// Block #2
|
||||
sendGeneric(0, 0, // No Header for Block #2
|
||||
kGreeBitMark, kGreeOneSpace, kGreeBitMark, kGreeZeroSpace,
|
||||
kGreeBitMark, kGreeMsgSpace, data + 4, nbytes - 4, 38, false, 0,
|
||||
50);
|
||||
}
|
||||
}
|
||||
|
||||
/// Send a Gree Heat Pump formatted message.
|
||||
/// Status: STABLE / Working.
|
||||
/// @param[in] data The message to be sent.
|
||||
/// @param[in] nbits The number of bits of message to be sent.
|
||||
/// @param[in] repeat The number of times the command is to be repeated.
|
||||
void IRsend::sendGree(const uint64_t data, const uint16_t nbits,
|
||||
const uint16_t repeat) {
|
||||
if (nbits != kGreeBits)
|
||||
return; // Wrong nr. of bits to send a proper message.
|
||||
// Set IR carrier frequency
|
||||
enableIROut(38);
|
||||
|
||||
for (uint16_t r = 0; r <= repeat; r++) {
|
||||
// Header
|
||||
mark(kGreeHdrMark);
|
||||
space(kGreeHdrSpace);
|
||||
|
||||
// Data
|
||||
for (int16_t i = 8; i <= nbits; i += 8) {
|
||||
sendData(kGreeBitMark, kGreeOneSpace, kGreeBitMark, kGreeZeroSpace,
|
||||
(data >> (nbits - i)) & 0xFF, 8, false);
|
||||
if (i == nbits / 2) {
|
||||
// Send the mid-message Footer.
|
||||
sendData(kGreeBitMark, kGreeOneSpace, kGreeBitMark, kGreeZeroSpace,
|
||||
0b010, 3);
|
||||
mark(kGreeBitMark);
|
||||
space(kGreeMsgSpace);
|
||||
}
|
||||
}
|
||||
// Footer
|
||||
mark(kGreeBitMark);
|
||||
space(kGreeMsgSpace);
|
||||
}
|
||||
}
|
||||
#endif // SEND_GREE
|
||||
|
||||
/// Class constructor
|
||||
/// @param[in] pin GPIO to be used when sending.
|
||||
/// @param[in] model The enum of the model to be emulated.
|
||||
/// @param[in] inverted Is the output signal to be inverted?
|
||||
/// @param[in] use_modulation Is frequency modulation to be used?
|
||||
IRGreeAC::IRGreeAC(const uint16_t pin, const gree_ac_remote_model_t model,
|
||||
const bool inverted, const bool use_modulation)
|
||||
: _irsend(pin, inverted, use_modulation) {
|
||||
stateReset();
|
||||
setModel(model);
|
||||
}
|
||||
|
||||
/// Reset the internal state to a fixed known good state.
|
||||
void IRGreeAC::stateReset(void) {
|
||||
// This resets to a known-good state to Power Off, Fan Auto, Mode Auto, 25C.
|
||||
std::memset(_.remote_state, 0, sizeof _.remote_state);
|
||||
_.Temp = 9; // _.remote_state[1] = 0x09;
|
||||
_.Light = true; // _.remote_state[2] = 0x20;
|
||||
_.unknown1 = 5; // _.remote_state[3] = 0x50;
|
||||
_.unknown2 = 4; // _.remote_state[5] = 0x20;
|
||||
}
|
||||
|
||||
/// Fix up the internal state so it is correct.
|
||||
/// @note Internal use only.
|
||||
void IRGreeAC::fixup(void) {
|
||||
setPower(getPower()); // Redo the power bits as they differ between models.
|
||||
checksum(); // Calculate the checksums
|
||||
}
|
||||
|
||||
/// Set up hardware to be able to send a message.
|
||||
void IRGreeAC::begin(void) { _irsend.begin(); }
|
||||
|
||||
#if SEND_GREE
|
||||
/// Send the current internal state as an IR message.
|
||||
/// @param[in] repeat Nr. of times the message will be repeated.
|
||||
void IRGreeAC::send(const uint16_t repeat) {
|
||||
_irsend.sendGree(getRaw(), kGreeStateLength, repeat);
|
||||
}
|
||||
#endif // SEND_GREE
|
||||
|
||||
/// Get a PTR to the internal state/code for this protocol.
|
||||
/// @return PTR to a code for this protocol based on the current internal state.
|
||||
uint8_t* IRGreeAC::getRaw(void) {
|
||||
fixup(); // Ensure correct settings before sending.
|
||||
return _.remote_state;
|
||||
}
|
||||
|
||||
/// Set the internal state from a valid code for this protocol.
|
||||
/// @param[in] new_code A valid code for this protocol.
|
||||
void IRGreeAC::setRaw(const uint8_t new_code[]) {
|
||||
std::memcpy(_.remote_state, new_code, kGreeStateLength);
|
||||
// We can only detect the difference between models when the power is on.
|
||||
if (_.Power) {
|
||||
if (_.ModelA)
|
||||
_model = gree_ac_remote_model_t::YAW1F;
|
||||
else
|
||||
_model = gree_ac_remote_model_t::YBOFB;
|
||||
}
|
||||
if (_.Mode == kGreeEcono) _model = gree_ac_remote_model_t::YX1FSF;
|
||||
}
|
||||
|
||||
/// Calculate and set the checksum values for the internal state.
|
||||
/// @param[in] length The size/length of the state array to fix the checksum of.
|
||||
void IRGreeAC::checksum(const uint16_t length) {
|
||||
// Gree uses the same checksum alg. as Kelvinator's block checksum.
|
||||
_.Sum = IRKelvinatorAC::calcBlockChecksum(_.remote_state, length);
|
||||
}
|
||||
|
||||
/// Verify the checksum is valid for a given state.
|
||||
/// @param[in] state The array to verify the checksum of.
|
||||
/// @param[in] length The length of the state array.
|
||||
/// @return true, if the state has a valid checksum. Otherwise, false.
|
||||
bool IRGreeAC::validChecksum(const uint8_t state[], const uint16_t length) {
|
||||
// Top 4 bits of the last byte in the state is the state's checksum.
|
||||
return GETBITS8(state[length - 1], kHighNibble, kNibbleSize) ==
|
||||
IRKelvinatorAC::calcBlockChecksum(state, length);
|
||||
}
|
||||
|
||||
/// Set the model of the A/C to emulate.
|
||||
/// @param[in] model The enum of the appropriate model.
|
||||
void IRGreeAC::setModel(const gree_ac_remote_model_t model) {
|
||||
switch (model) {
|
||||
case gree_ac_remote_model_t::YAW1F:
|
||||
case gree_ac_remote_model_t::YBOFB:
|
||||
case gree_ac_remote_model_t::YX1FSF: _model = model; break;
|
||||
default: _model = gree_ac_remote_model_t::YAW1F;
|
||||
}
|
||||
}
|
||||
|
||||
/// Get/Detect the model of the A/C.
|
||||
/// @return The enum of the compatible model.
|
||||
gree_ac_remote_model_t IRGreeAC::getModel(void) const { return _model; }
|
||||
|
||||
/// Change the power setting to On.
|
||||
void IRGreeAC::on(void) { setPower(true); }
|
||||
|
||||
/// Change the power setting to Off.
|
||||
void IRGreeAC::off(void) { setPower(false); }
|
||||
|
||||
/// Change the power setting.
|
||||
/// @param[in] on true, the setting is on. false, the setting is off.
|
||||
/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/814
|
||||
void IRGreeAC::setPower(const bool on) {
|
||||
_.Power = on;
|
||||
// May not be needed. See #814
|
||||
_.ModelA = (on && _model == gree_ac_remote_model_t::YAW1F);
|
||||
}
|
||||
|
||||
/// Get the value of the current power setting.
|
||||
/// @return true, the setting is on. false, the setting is off.
|
||||
/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/814
|
||||
bool IRGreeAC::getPower(void) const {
|
||||
// See #814. Not checking/requiring: (_.ModelA)
|
||||
return _.Power;
|
||||
}
|
||||
|
||||
/// Set the default temperature units to use.
|
||||
/// @param[in] on Use Fahrenheit as the units.
|
||||
/// true is Fahrenheit, false is Celsius.
|
||||
void IRGreeAC::setUseFahrenheit(const bool on) { _.UseFahrenheit = on; }
|
||||
|
||||
/// Get the default temperature units in use.
|
||||
/// @return true is Fahrenheit, false is Celsius.
|
||||
bool IRGreeAC::getUseFahrenheit(void) const { return _.UseFahrenheit; }
|
||||
|
||||
/// Set the temp. in degrees
|
||||
/// @param[in] temp Desired temperature in Degrees.
|
||||
/// @param[in] fahrenheit Use units of Fahrenheit and set that as units used.
|
||||
/// false is Celsius (Default), true is Fahrenheit.
|
||||
/// @note The unit actually works in Celsius with a special optional
|
||||
/// "extra degree" when sending Fahrenheit.
|
||||
void IRGreeAC::setTemp(const uint8_t temp, const bool fahrenheit) {
|
||||
float safecelsius = temp;
|
||||
if (fahrenheit)
|
||||
// Covert to F, and add a fudge factor to round to the expected degree.
|
||||
// Why 0.6 you ask?! Because it works. Ya'd thing 0.5 would be good for
|
||||
// rounding, but Noooooo!
|
||||
safecelsius = fahrenheitToCelsius(temp + 0.6);
|
||||
setUseFahrenheit(fahrenheit); // Set the correct Temp units.
|
||||
|
||||
// Make sure we have desired temp in the correct range.
|
||||
safecelsius = std::max(static_cast<float>(kGreeMinTempC), safecelsius);
|
||||
safecelsius = std::min(static_cast<float>(kGreeMaxTempC), safecelsius);
|
||||
// An operating mode of Auto locks the temp to a specific value. Do so.
|
||||
if (_.Mode == kGreeAuto) safecelsius = 25;
|
||||
|
||||
// Set the "main" Celsius degrees.
|
||||
_.Temp = safecelsius - kGreeMinTempC;
|
||||
// Deal with the extra degree fahrenheit difference.
|
||||
_.TempExtraDegreeF = (static_cast<uint8_t>(safecelsius * 2) & 1);
|
||||
}
|
||||
|
||||
/// Get the set temperature
|
||||
/// @return The temperature in degrees in the current units (C/F) set.
|
||||
uint8_t IRGreeAC::getTemp(void) const {
|
||||
uint8_t deg = kGreeMinTempC + _.Temp;
|
||||
if (_.UseFahrenheit) {
|
||||
deg = celsiusToFahrenheit(deg);
|
||||
// Retrieve the "extra" fahrenheit from elsewhere in the code.
|
||||
if (_.TempExtraDegreeF) deg++;
|
||||
deg = std::max(deg, kGreeMinTempF); // Cover the fact that 61F is < 16C
|
||||
}
|
||||
return deg;
|
||||
}
|
||||
|
||||
/// Set the speed of the fan.
|
||||
/// @param[in] speed The desired setting. 0 is auto, 1-3 is the speed.
|
||||
void IRGreeAC::setFan(const uint8_t speed) {
|
||||
uint8_t fan = std::min(kGreeFanMax, speed); // Bounds check
|
||||
if (_.Mode == kGreeDry) fan = 1; // DRY mode is always locked to fan 1.
|
||||
// Set the basic fan values.
|
||||
_.Fan = fan;
|
||||
}
|
||||
|
||||
/// Get the current fan speed setting.
|
||||
/// @return The current fan speed.
|
||||
uint8_t IRGreeAC::getFan(void) const { return _.Fan; }
|
||||
|
||||
/// Set the operating mode of the A/C.
|
||||
/// @param[in] new_mode The desired operating mode.
|
||||
void IRGreeAC::setMode(const uint8_t new_mode) {
|
||||
uint8_t mode = new_mode;
|
||||
switch (mode) {
|
||||
// AUTO is locked to 25C
|
||||
case kGreeAuto: setTemp(25); break;
|
||||
// DRY always sets the fan to 1.
|
||||
case kGreeDry: setFan(1); break;
|
||||
case kGreeCool:
|
||||
case kGreeFan:
|
||||
case kGreeEcono:
|
||||
case kGreeHeat: break;
|
||||
// If we get an unexpected mode, default to AUTO.
|
||||
default: mode = kGreeAuto;
|
||||
}
|
||||
_.Mode = mode;
|
||||
}
|
||||
|
||||
/// Get the operating mode setting of the A/C.
|
||||
/// @return The current operating mode setting.
|
||||
uint8_t IRGreeAC::getMode(void) const { return _.Mode; }
|
||||
|
||||
/// Set the Light (LED) setting of the A/C.
|
||||
/// @param[in] on true, the setting is on. false, the setting is off.
|
||||
void IRGreeAC::setLight(const bool on) { _.Light = on; }
|
||||
|
||||
/// Get the Light (LED) setting of the A/C.
|
||||
/// @return true, the setting is on. false, the setting is off.
|
||||
bool IRGreeAC::getLight(void) const { return _.Light; }
|
||||
|
||||
/// Set the IFeel setting of the A/C.
|
||||
/// @param[in] on true, the setting is on. false, the setting is off.
|
||||
void IRGreeAC::setIFeel(const bool on) { _.IFeel = on; }
|
||||
|
||||
/// Get the IFeel setting of the A/C.
|
||||
/// @return true, the setting is on. false, the setting is off.
|
||||
bool IRGreeAC::getIFeel(void) const { return _.IFeel; }
|
||||
|
||||
/// Set the Wifi (enabled) setting of the A/C.
|
||||
/// @param[in] on true, the setting is on. false, the setting is off.
|
||||
void IRGreeAC::setWiFi(const bool on) { _.WiFi = on; }
|
||||
|
||||
/// Get the Wifi (enabled) setting of the A/C.
|
||||
/// @return true, the setting is on. false, the setting is off.
|
||||
bool IRGreeAC::getWiFi(void) const { return _.WiFi; }
|
||||
|
||||
/// Set the XFan (Mould) setting of the A/C.
|
||||
/// @param[in] on true, the setting is on. false, the setting is off.
|
||||
void IRGreeAC::setXFan(const bool on) { _.Xfan = on; }
|
||||
|
||||
/// Get the XFan (Mould) setting of the A/C.
|
||||
/// @return true, the setting is on. false, the setting is off.
|
||||
bool IRGreeAC::getXFan(void) const { return _.Xfan; }
|
||||
|
||||
/// Set the Sleep setting of the A/C.
|
||||
/// @param[in] on true, the setting is on. false, the setting is off.
|
||||
void IRGreeAC::setSleep(const bool on) { _.Sleep = on; }
|
||||
|
||||
/// Get the Sleep setting of the A/C.
|
||||
/// @return true, the setting is on. false, the setting is off.
|
||||
bool IRGreeAC::getSleep(void) const { return _.Sleep; }
|
||||
|
||||
/// Set the Turbo setting of the A/C.
|
||||
/// @param[in] on true, the setting is on. false, the setting is off.
|
||||
void IRGreeAC::setTurbo(const bool on) { _.Turbo = on; }
|
||||
|
||||
/// Get the Turbo setting of the A/C.
|
||||
/// @return true, the setting is on. false, the setting is off.
|
||||
bool IRGreeAC::getTurbo(void) const { return _.Turbo; }
|
||||
|
||||
/// Set the Econo setting of the A/C.
|
||||
/// @param[in] on true, the setting is on. false, the setting is off.
|
||||
void IRGreeAC::setEcono(const bool on) {
|
||||
_.Econo = on;
|
||||
if (on && getModel() == gree_ac_remote_model_t::YX1FSF)
|
||||
setMode(kGreeEcono);
|
||||
}
|
||||
|
||||
/// Get the Econo setting of the A/C.
|
||||
/// @return true, the setting is on. false, the setting is off.
|
||||
bool IRGreeAC::getEcono(void) const {
|
||||
return _.Econo || getMode() == kGreeEcono;
|
||||
}
|
||||
|
||||
/// Set the Vertical Swing mode of the A/C.
|
||||
/// @param[in] automatic Do we use the automatic setting?
|
||||
/// @param[in] position The position/mode to set the vanes to.
|
||||
void IRGreeAC::setSwingVertical(const bool automatic, const uint8_t position) {
|
||||
_.SwingAuto = automatic;
|
||||
uint8_t new_position = position;
|
||||
if (!automatic) {
|
||||
switch (position) {
|
||||
case kGreeSwingUp:
|
||||
case kGreeSwingMiddleUp:
|
||||
case kGreeSwingMiddle:
|
||||
case kGreeSwingMiddleDown:
|
||||
case kGreeSwingDown:
|
||||
break;
|
||||
default:
|
||||
new_position = kGreeSwingLastPos;
|
||||
}
|
||||
} else {
|
||||
switch (position) {
|
||||
case kGreeSwingAuto:
|
||||
case kGreeSwingDownAuto:
|
||||
case kGreeSwingMiddleAuto:
|
||||
case kGreeSwingUpAuto:
|
||||
break;
|
||||
default:
|
||||
new_position = kGreeSwingAuto;
|
||||
}
|
||||
}
|
||||
_.SwingV = new_position;
|
||||
}
|
||||
|
||||
/// Get the Vertical Swing Automatic mode setting of the A/C.
|
||||
/// @return true, the setting is on. false, the setting is off.
|
||||
bool IRGreeAC::getSwingVerticalAuto(void) const { return _.SwingAuto; }
|
||||
|
||||
/// Get the Vertical Swing position setting of the A/C.
|
||||
/// @return The native position/mode.
|
||||
uint8_t IRGreeAC::getSwingVerticalPosition(void) const { return _.SwingV; }
|
||||
|
||||
/// Get the Horizontal Swing position setting of the A/C.
|
||||
/// @return The native position/mode.
|
||||
uint8_t IRGreeAC::getSwingHorizontal(void) const { return _.SwingH; }
|
||||
|
||||
/// Set the Horizontal Swing mode of the A/C.
|
||||
/// @param[in] position The position/mode to set the vanes to.
|
||||
void IRGreeAC::setSwingHorizontal(const uint8_t position) {
|
||||
if (position <= kGreeSwingHMaxRight)
|
||||
_.SwingH = position;
|
||||
else
|
||||
_.SwingH = kGreeSwingHOff;
|
||||
}
|
||||
|
||||
/// Set the timer enable setting of the A/C.
|
||||
/// @param[in] on true, the setting is on. false, the setting is off.
|
||||
void IRGreeAC::setTimerEnabled(const bool on) { _.TimerEnabled = on; }
|
||||
|
||||
/// Get the timer enabled setting of the A/C.
|
||||
/// @return true, the setting is on. false, the setting is off.
|
||||
bool IRGreeAC::getTimerEnabled(void) const { return _.TimerEnabled; }
|
||||
|
||||
/// Get the timer time value from the A/C.
|
||||
/// @return The number of minutes the timer is set for.
|
||||
uint16_t IRGreeAC::getTimer(void) const {
|
||||
uint16_t hrs = irutils::bcdToUint8((_.TimerTensHr << kNibbleSize) |
|
||||
_.TimerHours);
|
||||
return hrs * 60 + (_.TimerHalfHr ? 30 : 0);
|
||||
}
|
||||
|
||||
/// Set the A/C's timer to turn off in X many minutes.
|
||||
/// @param[in] minutes The number of minutes the timer should be set for.
|
||||
/// @note Stores time internally in 30 min units.
|
||||
/// e.g. 5 mins means 0 (& Off), 95 mins is 90 mins (& On). Max is 24 hours.
|
||||
void IRGreeAC::setTimer(const uint16_t minutes) {
|
||||
uint16_t mins = std::min(kGreeTimerMax, minutes); // Bounds check.
|
||||
setTimerEnabled(mins >= 30); // Timer is enabled when >= 30 mins.
|
||||
uint8_t hours = mins / 60;
|
||||
// Set the half hour bit.
|
||||
_.TimerHalfHr = (mins % 60) >= 30;
|
||||
// Set the "tens" digit of hours.
|
||||
_.TimerTensHr = hours / 10;
|
||||
// Set the "units" digit of hours.
|
||||
_.TimerHours = hours % 10;
|
||||
}
|
||||
|
||||
/// Set temperature display mode.
|
||||
/// i.e. Internal, External temperature sensing.
|
||||
/// @param[in] mode The desired temp source to display.
|
||||
/// @note In order for the A/C unit properly accept these settings. You must
|
||||
/// cycle (send) in the following order:
|
||||
/// kGreeDisplayTempOff(0) -> kGreeDisplayTempSet(1) ->
|
||||
/// kGreeDisplayTempInside(2) ->kGreeDisplayTempOutside(3) ->
|
||||
/// kGreeDisplayTempOff(0).
|
||||
/// The unit will no behave correctly if the changes of this setting are sent
|
||||
/// out of order.
|
||||
/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1118#issuecomment-628242152
|
||||
void IRGreeAC::setDisplayTempSource(const uint8_t mode) {
|
||||
_.DisplayTemp = mode;
|
||||
}
|
||||
|
||||
/// Get the temperature display mode.
|
||||
/// i.e. Internal, External temperature sensing.
|
||||
/// @return The current temp source being displayed.
|
||||
uint8_t IRGreeAC::getDisplayTempSource(void) const { return _.DisplayTemp; }
|
||||
|
||||
/// Convert a stdAc::opmode_t enum into its native mode.
|
||||
/// @param[in] mode The enum to be converted.
|
||||
/// @return The native equivalent of the enum.
|
||||
uint8_t IRGreeAC::convertMode(const stdAc::opmode_t mode) {
|
||||
switch (mode) {
|
||||
case stdAc::opmode_t::kCool: return kGreeCool;
|
||||
case stdAc::opmode_t::kHeat: return kGreeHeat;
|
||||
case stdAc::opmode_t::kDry: return kGreeDry;
|
||||
case stdAc::opmode_t::kFan: return kGreeFan;
|
||||
default: return kGreeAuto;
|
||||
}
|
||||
}
|
||||
|
||||
/// Convert a stdAc::fanspeed_t enum into it's native speed.
|
||||
/// @param[in] speed The enum to be converted.
|
||||
/// @return The native equivalent of the enum.
|
||||
uint8_t IRGreeAC::convertFan(const stdAc::fanspeed_t speed) {
|
||||
switch (speed) {
|
||||
case stdAc::fanspeed_t::kMin: return kGreeFanMin;
|
||||
case stdAc::fanspeed_t::kLow:
|
||||
case stdAc::fanspeed_t::kMedium: return kGreeFanMax - 1;
|
||||
case stdAc::fanspeed_t::kHigh:
|
||||
case stdAc::fanspeed_t::kMax: return kGreeFanMax;
|
||||
default: return kGreeFanAuto;
|
||||
}
|
||||
}
|
||||
|
||||
/// Convert a stdAc::swingv_t enum into it's native setting.
|
||||
/// @param[in] swingv The enum to be converted.
|
||||
/// @return The native equivalent of the enum.
|
||||
uint8_t IRGreeAC::convertSwingV(const stdAc::swingv_t swingv) {
|
||||
switch (swingv) {
|
||||
case stdAc::swingv_t::kHighest: return kGreeSwingUp;
|
||||
case stdAc::swingv_t::kHigh: return kGreeSwingMiddleUp;
|
||||
case stdAc::swingv_t::kMiddle: return kGreeSwingMiddle;
|
||||
case stdAc::swingv_t::kLow: return kGreeSwingMiddleDown;
|
||||
case stdAc::swingv_t::kLowest: return kGreeSwingDown;
|
||||
default: return kGreeSwingAuto;
|
||||
}
|
||||
}
|
||||
|
||||
/// Convert a stdAc::swingh_t enum into it's native setting.
|
||||
/// @param[in] swingh The enum to be converted.
|
||||
/// @return The native equivalent of the enum.
|
||||
uint8_t IRGreeAC::convertSwingH(const stdAc::swingh_t swingh) {
|
||||
switch (swingh) {
|
||||
case stdAc::swingh_t::kAuto: return kGreeSwingHAuto;
|
||||
case stdAc::swingh_t::kLeftMax: return kGreeSwingHMaxLeft;
|
||||
case stdAc::swingh_t::kLeft: return kGreeSwingHLeft;
|
||||
case stdAc::swingh_t::kMiddle: return kGreeSwingHMiddle;
|
||||
case stdAc::swingh_t::kRight: return kGreeSwingHRight;
|
||||
case stdAc::swingh_t::kRightMax: return kGreeSwingHMaxRight;
|
||||
default: return kGreeSwingHOff;
|
||||
}
|
||||
}
|
||||
|
||||
/// Convert a native mode into its stdAc equivalent.
|
||||
/// @param[in] mode The native setting to be converted.
|
||||
/// @return The stdAc equivalent of the native setting.
|
||||
stdAc::opmode_t IRGreeAC::toCommonMode(const uint8_t mode) {
|
||||
switch (mode) {
|
||||
case kGreeCool: return stdAc::opmode_t::kCool;
|
||||
case kGreeHeat: return stdAc::opmode_t::kHeat;
|
||||
case kGreeDry: return stdAc::opmode_t::kDry;
|
||||
case kGreeFan: return stdAc::opmode_t::kFan;
|
||||
default: return stdAc::opmode_t::kAuto;
|
||||
}
|
||||
}
|
||||
|
||||
/// Convert a native fan speed into its stdAc equivalent.
|
||||
/// @param[in] speed The native setting to be converted.
|
||||
/// @return The stdAc equivalent of the native setting.
|
||||
stdAc::fanspeed_t IRGreeAC::toCommonFanSpeed(const uint8_t speed) {
|
||||
switch (speed) {
|
||||
case kGreeFanMax: return stdAc::fanspeed_t::kMax;
|
||||
case kGreeFanMax - 1: return stdAc::fanspeed_t::kMedium;
|
||||
case kGreeFanMin: return stdAc::fanspeed_t::kMin;
|
||||
default: return stdAc::fanspeed_t::kAuto;
|
||||
}
|
||||
}
|
||||
|
||||
/// Convert a native Vertical Swing into its stdAc equivalent.
|
||||
/// @param[in] pos The native setting to be converted.
|
||||
/// @return The stdAc equivalent of the native setting.
|
||||
stdAc::swingv_t IRGreeAC::toCommonSwingV(const uint8_t pos) {
|
||||
switch (pos) {
|
||||
case kGreeSwingUp: return stdAc::swingv_t::kHighest;
|
||||
case kGreeSwingMiddleUp: return stdAc::swingv_t::kHigh;
|
||||
case kGreeSwingMiddle: return stdAc::swingv_t::kMiddle;
|
||||
case kGreeSwingMiddleDown: return stdAc::swingv_t::kLow;
|
||||
case kGreeSwingDown: return stdAc::swingv_t::kLowest;
|
||||
default: return stdAc::swingv_t::kAuto;
|
||||
}
|
||||
}
|
||||
|
||||
/// Convert a native Horizontal Swing into its stdAc equivalent.
|
||||
/// @param[in] pos The native setting to be converted.
|
||||
/// @return The stdAc equivalent of the native setting.
|
||||
stdAc::swingh_t IRGreeAC::toCommonSwingH(const uint8_t pos) {
|
||||
switch (pos) {
|
||||
case kGreeSwingHAuto: return stdAc::swingh_t::kAuto;
|
||||
case kGreeSwingHMaxLeft: return stdAc::swingh_t::kLeftMax;
|
||||
case kGreeSwingHLeft: return stdAc::swingh_t::kLeft;
|
||||
case kGreeSwingHMiddle: return stdAc::swingh_t::kMiddle;
|
||||
case kGreeSwingHRight: return stdAc::swingh_t::kRight;
|
||||
case kGreeSwingHMaxRight: return stdAc::swingh_t::kRightMax;
|
||||
default: return stdAc::swingh_t::kOff;
|
||||
}
|
||||
}
|
||||
|
||||
/// Convert the current internal state into its stdAc::state_t equivalent.
|
||||
/// @return The stdAc equivalent of the native settings.
|
||||
stdAc::state_t IRGreeAC::toCommon(void) {
|
||||
stdAc::state_t result{};
|
||||
result.protocol = decode_type_t::GREE;
|
||||
result.model = _model;
|
||||
result.power = _.Power;
|
||||
result.mode = toCommonMode(_.Mode);
|
||||
result.celsius = !_.UseFahrenheit;
|
||||
result.degrees = getTemp();
|
||||
// no support for Sensor temp.
|
||||
result.iFeel = getIFeel();
|
||||
result.fanspeed = toCommonFanSpeed(_.Fan);
|
||||
if (_.SwingAuto)
|
||||
result.swingv = stdAc::swingv_t::kAuto;
|
||||
else
|
||||
result.swingv = toCommonSwingV(_.SwingV);
|
||||
result.swingh = toCommonSwingH(_.SwingH);
|
||||
result.turbo = _.Turbo;
|
||||
result.econo = getEcono();
|
||||
result.light = _.Light;
|
||||
result.clean = _.Xfan;
|
||||
result.sleep = _.Sleep ? 0 : -1;
|
||||
// Not supported.
|
||||
result.quiet = false;
|
||||
result.filter = false;
|
||||
result.beep = false;
|
||||
result.clock = -1;
|
||||
return result;
|
||||
}
|
||||
|
||||
/// Convert the current internal state into a human readable string.
|
||||
/// @return A human readable string.
|
||||
String IRGreeAC::toString(void) {
|
||||
String result = "";
|
||||
result.reserve(220); // Reserve some heap for the string to reduce fragging.
|
||||
result += addModelToString(decode_type_t::GREE, _model, false);
|
||||
result += addBoolToString(_.Power, kPowerStr);
|
||||
if (_model == gree_ac_remote_model_t::YX1FSF && _.Mode == kGreeEcono) {
|
||||
result += addIntToString(_.Mode, kModeStr);
|
||||
result += kSpaceLBraceStr;
|
||||
result += kEconoStr;
|
||||
result += ')';
|
||||
} else {
|
||||
result += addModeToString(_.Mode, kGreeAuto, kGreeCool, kGreeHeat,
|
||||
kGreeDry, kGreeFan);
|
||||
}
|
||||
result += addTempToString(getTemp(), !_.UseFahrenheit);
|
||||
result += addFanToString(_.Fan, kGreeFanMax, kGreeFanMin, kGreeFanAuto,
|
||||
kGreeFanAuto, kGreeFanMed);
|
||||
result += addBoolToString(_.Turbo, kTurboStr);
|
||||
result += addBoolToString(_.Econo, kEconoStr);
|
||||
result += addBoolToString(_.IFeel, kIFeelStr);
|
||||
result += addBoolToString(_.WiFi, kWifiStr);
|
||||
result += addBoolToString(_.Xfan, kXFanStr);
|
||||
result += addBoolToString(_.Light, kLightStr);
|
||||
result += addBoolToString(_.Sleep, kSleepStr);
|
||||
result += addLabeledString(_.SwingAuto ? kAutoStr : kManualStr,
|
||||
kSwingVModeStr);
|
||||
result += addIntToString(_.SwingV, kSwingVStr);
|
||||
result += kSpaceLBraceStr;
|
||||
switch (_.SwingV) {
|
||||
case kGreeSwingLastPos:
|
||||
result += kLastStr;
|
||||
break;
|
||||
case kGreeSwingAuto:
|
||||
result += kAutoStr;
|
||||
break;
|
||||
default: result += kUnknownStr;
|
||||
}
|
||||
result += ')';
|
||||
result += addSwingHToString(_.SwingH, kGreeSwingHAuto, kGreeSwingHMaxLeft,
|
||||
kGreeSwingHLeft, kGreeSwingHMiddle,
|
||||
kGreeSwingHRight, kGreeSwingHMaxRight,
|
||||
kGreeSwingHOff,
|
||||
// rest are unused.
|
||||
0xFF, 0xFF, 0xFF, 0xFF);
|
||||
result += addLabeledString(
|
||||
_.TimerEnabled ? minsToString(getTimer()) : kOffStr, kTimerStr);
|
||||
uint8_t src = _.DisplayTemp;
|
||||
result += addIntToString(src, kDisplayTempStr);
|
||||
result += kSpaceLBraceStr;
|
||||
switch (src) {
|
||||
case kGreeDisplayTempOff:
|
||||
result += kOffStr;
|
||||
break;
|
||||
case kGreeDisplayTempSet:
|
||||
result += kSetStr;
|
||||
break;
|
||||
case kGreeDisplayTempInside:
|
||||
result += kInsideStr;
|
||||
break;
|
||||
case kGreeDisplayTempOutside:
|
||||
result += kOutsideStr;
|
||||
break;
|
||||
default: result += kUnknownStr;
|
||||
}
|
||||
result += ')';
|
||||
return result;
|
||||
}
|
||||
|
||||
#if DECODE_GREE
|
||||
/// Decode the supplied Gree HVAC message.
|
||||
/// Status: STABLE / Working.
|
||||
/// @param[in,out] results Ptr to the data to decode & where to store the decode
|
||||
/// result.
|
||||
/// @param[in] offset The starting index to use when attempting to decode the
|
||||
/// raw data. Typically/Defaults to kStartOffset.
|
||||
/// @param[in] nbits The number of data bits to expect.
|
||||
/// @param[in] strict Flag indicating if we should perform strict matching.
|
||||
/// @return A boolean. True if it can decode it, false if it can't.
|
||||
bool IRrecv::decodeGree(decode_results* results, uint16_t offset,
|
||||
const uint16_t nbits, bool const strict) {
|
||||
if (results->rawlen <=
|
||||
2 * (nbits + kGreeBlockFooterBits) + (kHeader + kFooter + 1) - 1 + offset)
|
||||
return false; // Can't possibly be a valid Gree message.
|
||||
if (strict && nbits != kGreeBits)
|
||||
return false; // Not strictly a Gree message.
|
||||
|
||||
// There are two blocks back-to-back in a full Gree IR message
|
||||
// sequence.
|
||||
|
||||
uint16_t used;
|
||||
// Header + Data Block #1 (32 bits)
|
||||
used = matchGeneric(results->rawbuf + offset, results->state,
|
||||
results->rawlen - offset, nbits / 2,
|
||||
kGreeHdrMark, kGreeHdrSpace,
|
||||
kGreeBitMark, kGreeOneSpace,
|
||||
kGreeBitMark, kGreeZeroSpace,
|
||||
0, 0, false,
|
||||
_tolerance, kMarkExcess, false);
|
||||
if (used == 0) return false;
|
||||
offset += used;
|
||||
|
||||
// Block #1 footer (3 bits, B010)
|
||||
match_result_t data_result;
|
||||
data_result = matchData(&(results->rawbuf[offset]), kGreeBlockFooterBits,
|
||||
kGreeBitMark, kGreeOneSpace, kGreeBitMark,
|
||||
kGreeZeroSpace, _tolerance, kMarkExcess, false);
|
||||
if (data_result.success == false) return false;
|
||||
if (data_result.data != kGreeBlockFooter) return false;
|
||||
offset += data_result.used;
|
||||
|
||||
// Inter-block gap + Data Block #2 (32 bits) + Footer
|
||||
if (!matchGeneric(results->rawbuf + offset, results->state + 4,
|
||||
results->rawlen - offset, nbits / 2,
|
||||
kGreeBitMark, kGreeMsgSpace,
|
||||
kGreeBitMark, kGreeOneSpace,
|
||||
kGreeBitMark, kGreeZeroSpace,
|
||||
kGreeBitMark, kGreeMsgSpace, true,
|
||||
_tolerance, kMarkExcess, false)) return false;
|
||||
|
||||
// Compliance
|
||||
if (strict) {
|
||||
// Verify the message's checksum is correct.
|
||||
if (!IRGreeAC::validChecksum(results->state)) return false;
|
||||
}
|
||||
|
||||
// Success
|
||||
results->decode_type = GREE;
|
||||
results->bits = nbits;
|
||||
// No need to record the state as we stored it as we decoded it.
|
||||
// As we use result->state, we don't record value, address, or command as it
|
||||
// is a union data type.
|
||||
return true;
|
||||
}
|
||||
#endif // DECODE_GREE
|
||||
240
yoRadio/src/IRremoteESP8266/ir_Gree.h
Normal file
240
yoRadio/src/IRremoteESP8266/ir_Gree.h
Normal file
@@ -0,0 +1,240 @@
|
||||
// Copyright 2016-2022 David Conran
|
||||
|
||||
/// @file
|
||||
/// @brief Support for Gree A/C protocols.
|
||||
/// @see https://github.com/ToniA/arduino-heatpumpir/blob/master/GreeHeatpumpIR.h
|
||||
/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1508
|
||||
/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1821
|
||||
|
||||
// Supports:
|
||||
// Brand: Ultimate, Model: Heat Pump
|
||||
// Brand: EKOKAI, Model: A/C
|
||||
// Brand: RusClimate, Model: EACS/I-09HAR_X/N3 A/C
|
||||
// Brand: RusClimate, Model: YAW1F remote
|
||||
// Brand: Green, Model: YBOFB remote
|
||||
// Brand: Green, Model: YBOFB2 remote
|
||||
// Brand: Gree, Model: YAA1FBF remote
|
||||
// Brand: Gree, Model: YB1F2F remote
|
||||
// Brand: Gree, Model: YAN1F1 remote
|
||||
// Brand: Gree, Model: YX1F2F remote (YX1FSF)
|
||||
// Brand: Gree, Model: VIR09HP115V1AH A/C
|
||||
// Brand: Gree, Model: VIR12HP230V1AH A/C
|
||||
// Brand: Amana, Model: PBC093G00CC A/C
|
||||
// Brand: Amana, Model: YX1FF remote
|
||||
// Brand: Cooper & Hunter, Model: YB1F2 remote
|
||||
// Brand: Cooper & Hunter, Model: CH-S09FTXG A/C
|
||||
// Brand: Vailland, Model: YACIFB remote
|
||||
// Brand: Vailland, Model: VAI5-035WNI A/C
|
||||
// Brand: Soleus Air, Model: window A/C (YX1FSF)
|
||||
|
||||
#ifndef IR_GREE_H_
|
||||
#define IR_GREE_H_
|
||||
|
||||
#define __STDC_LIMIT_MACROS
|
||||
#include <stdint.h>
|
||||
#ifndef UNIT_TEST
|
||||
#include <Arduino.h>
|
||||
#endif
|
||||
#include "IRremoteESP8266.h"
|
||||
#include "IRsend.h"
|
||||
#ifdef UNIT_TEST
|
||||
#include "IRsend_test.h"
|
||||
#endif
|
||||
|
||||
/// Native representation of a Gree A/C message.
|
||||
union GreeProtocol{
|
||||
uint8_t remote_state[kGreeStateLength]; ///< The state in native IR code form
|
||||
struct {
|
||||
// Byte 0
|
||||
uint8_t Mode :3;
|
||||
uint8_t Power :1;
|
||||
uint8_t Fan :2;
|
||||
uint8_t SwingAuto :1;
|
||||
uint8_t Sleep :1;
|
||||
// Byte 1
|
||||
uint8_t Temp :4;
|
||||
uint8_t TimerHalfHr :1;
|
||||
uint8_t TimerTensHr :2;
|
||||
uint8_t TimerEnabled:1;
|
||||
// Byte 2
|
||||
uint8_t TimerHours:4;
|
||||
uint8_t Turbo :1;
|
||||
uint8_t Light :1;
|
||||
uint8_t ModelA :1; // model==YAW1F
|
||||
uint8_t Xfan :1;
|
||||
// Byte 3
|
||||
uint8_t :2;
|
||||
uint8_t TempExtraDegreeF:1;
|
||||
uint8_t UseFahrenheit :1;
|
||||
uint8_t unknown1 :4; // value=0b0101
|
||||
// Byte 4
|
||||
uint8_t SwingV :4;
|
||||
uint8_t SwingH :3;
|
||||
uint8_t :1;
|
||||
// Byte 5
|
||||
uint8_t DisplayTemp :2;
|
||||
uint8_t IFeel :1;
|
||||
uint8_t unknown2 :3; // value = 0b100
|
||||
uint8_t WiFi :1;
|
||||
uint8_t :1;
|
||||
// Byte 6
|
||||
uint8_t :8;
|
||||
// Byte 7
|
||||
uint8_t :2;
|
||||
uint8_t Econo :1;
|
||||
uint8_t :1;
|
||||
uint8_t Sum :4;
|
||||
};
|
||||
};
|
||||
|
||||
// Constants
|
||||
|
||||
const uint8_t kGreeAuto = 0;
|
||||
const uint8_t kGreeCool = 1;
|
||||
const uint8_t kGreeDry = 2;
|
||||
const uint8_t kGreeFan = 3;
|
||||
const uint8_t kGreeHeat = 4;
|
||||
const uint8_t kGreeEcono = 5;
|
||||
|
||||
const uint8_t kGreeFanAuto = 0;
|
||||
const uint8_t kGreeFanMin = 1;
|
||||
const uint8_t kGreeFanMed = 2;
|
||||
const uint8_t kGreeFanMax = 3;
|
||||
|
||||
const uint8_t kGreeMinTempC = 16; // Celsius
|
||||
const uint8_t kGreeMaxTempC = 30; // Celsius
|
||||
const uint8_t kGreeMinTempF = 61; // Fahrenheit
|
||||
const uint8_t kGreeMaxTempF = 86; // Fahrenheit
|
||||
const uint16_t kGreeTimerMax = 24 * 60;
|
||||
|
||||
const uint8_t kGreeSwingLastPos = 0b0000; // 0
|
||||
const uint8_t kGreeSwingAuto = 0b0001; // 1
|
||||
const uint8_t kGreeSwingUp = 0b0010; // 2
|
||||
const uint8_t kGreeSwingMiddleUp = 0b0011; // 3
|
||||
const uint8_t kGreeSwingMiddle = 0b0100; // 4
|
||||
const uint8_t kGreeSwingMiddleDown = 0b0101; // 5
|
||||
const uint8_t kGreeSwingDown = 0b0110; // 6
|
||||
const uint8_t kGreeSwingDownAuto = 0b0111; // 7
|
||||
const uint8_t kGreeSwingMiddleAuto = 0b1001; // 9
|
||||
const uint8_t kGreeSwingUpAuto = 0b1011; // 11
|
||||
|
||||
const uint8_t kGreeSwingHOff = 0b000; // 0
|
||||
const uint8_t kGreeSwingHAuto = 0b001; // 1
|
||||
const uint8_t kGreeSwingHMaxLeft = 0b010; // 2
|
||||
const uint8_t kGreeSwingHLeft = 0b011; // 3
|
||||
const uint8_t kGreeSwingHMiddle = 0b100; // 4
|
||||
const uint8_t kGreeSwingHRight = 0b101; // 5
|
||||
const uint8_t kGreeSwingHMaxRight = 0b110; // 6
|
||||
|
||||
const uint8_t kGreeDisplayTempOff = 0b00; // 0
|
||||
const uint8_t kGreeDisplayTempSet = 0b01; // 1
|
||||
const uint8_t kGreeDisplayTempInside = 0b10; // 2
|
||||
const uint8_t kGreeDisplayTempOutside = 0b11; // 3
|
||||
|
||||
// Legacy defines.
|
||||
#define GREE_AUTO kGreeAuto
|
||||
#define GREE_COOL kGreeCool
|
||||
#define GREE_DRY kGreeDry
|
||||
#define GREE_FAN kGreeFan
|
||||
#define GREE_HEAT kGreeHeat
|
||||
#define GREE_MIN_TEMP kGreeMinTempC
|
||||
#define GREE_MAX_TEMP kGreeMaxTempC
|
||||
#define GREE_FAN_MAX kGreeFanMax
|
||||
#define GREE_SWING_LAST_POS kGreeSwingLastPos
|
||||
#define GREE_SWING_AUTO kGreeSwingAuto
|
||||
#define GREE_SWING_UP kGreeSwingUp
|
||||
#define GREE_SWING_MIDDLE_UP kGreeSwingMiddleUp
|
||||
#define GREE_SWING_MIDDLE kGreeSwingMiddle
|
||||
#define GREE_SWING_MIDDLE_DOWN kGreeSwingMiddleDown
|
||||
#define GREE_SWING_DOWN kGreeSwingDown
|
||||
#define GREE_SWING_DOWN_AUTO kGreeSwingDownAuto
|
||||
#define GREE_SWING_MIDDLE_AUTO kGreeSwingMiddleAuto
|
||||
#define GREE_SWING_UP_AUTO kGreeSwingUpAuto
|
||||
|
||||
// Classes
|
||||
/// Class for handling detailed Gree A/C messages.
|
||||
class IRGreeAC {
|
||||
public:
|
||||
explicit IRGreeAC(
|
||||
const uint16_t pin,
|
||||
const gree_ac_remote_model_t model = gree_ac_remote_model_t::YAW1F,
|
||||
const bool inverted = false, const bool use_modulation = true);
|
||||
void stateReset(void);
|
||||
#if SEND_GREE
|
||||
void send(const uint16_t repeat = kGreeDefaultRepeat);
|
||||
/// 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_GREE
|
||||
void begin(void);
|
||||
void on(void);
|
||||
void off(void);
|
||||
void setModel(const gree_ac_remote_model_t model);
|
||||
gree_ac_remote_model_t getModel(void) const;
|
||||
void setPower(const bool on);
|
||||
bool getPower(void) const;
|
||||
void setTemp(const uint8_t temp, const bool fahrenheit = false);
|
||||
uint8_t getTemp(void) const;
|
||||
void setUseFahrenheit(const bool on);
|
||||
bool getUseFahrenheit(void) const;
|
||||
void setFan(const uint8_t speed);
|
||||
uint8_t getFan(void) const;
|
||||
void setMode(const uint8_t new_mode);
|
||||
uint8_t getMode(void) const;
|
||||
void setLight(const bool on);
|
||||
bool getLight(void) const;
|
||||
void setXFan(const bool on);
|
||||
bool getXFan(void) const;
|
||||
void setSleep(const bool on);
|
||||
bool getSleep(void) const;
|
||||
void setTurbo(const bool on);
|
||||
bool getTurbo(void) const;
|
||||
void setEcono(const bool on);
|
||||
bool getEcono(void) const;
|
||||
void setIFeel(const bool on);
|
||||
bool getIFeel(void) const;
|
||||
void setWiFi(const bool on);
|
||||
bool getWiFi(void) const;
|
||||
void setSwingVertical(const bool automatic, const uint8_t position);
|
||||
bool getSwingVerticalAuto(void) const;
|
||||
uint8_t getSwingVerticalPosition(void) const;
|
||||
void setSwingHorizontal(const uint8_t position);
|
||||
uint8_t getSwingHorizontal(void) const;
|
||||
uint16_t getTimer(void) const;
|
||||
void setTimer(const uint16_t minutes);
|
||||
void setDisplayTempSource(const uint8_t mode);
|
||||
uint8_t getDisplayTempSource(void) const;
|
||||
static uint8_t convertMode(const stdAc::opmode_t mode);
|
||||
static uint8_t convertFan(const stdAc::fanspeed_t speed);
|
||||
static uint8_t convertSwingV(const stdAc::swingv_t swingv);
|
||||
static uint8_t convertSwingH(const stdAc::swingh_t swingh);
|
||||
static stdAc::opmode_t toCommonMode(const uint8_t mode);
|
||||
static stdAc::fanspeed_t toCommonFanSpeed(const uint8_t speed);
|
||||
static stdAc::swingv_t toCommonSwingV(const uint8_t pos);
|
||||
static stdAc::swingh_t toCommonSwingH(const uint8_t pos);
|
||||
stdAc::state_t toCommon(void);
|
||||
uint8_t* getRaw(void);
|
||||
void setRaw(const uint8_t new_code[]);
|
||||
static bool validChecksum(const uint8_t state[],
|
||||
const uint16_t length = kGreeStateLength);
|
||||
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
|
||||
GreeProtocol _;
|
||||
gree_ac_remote_model_t _model;
|
||||
void checksum(const uint16_t length = kGreeStateLength);
|
||||
void fixup(void);
|
||||
void setTimerEnabled(const bool on);
|
||||
bool getTimerEnabled(void) const;
|
||||
};
|
||||
|
||||
#endif // IR_GREE_H_
|
||||
2169
yoRadio/src/IRremoteESP8266/ir_Haier.cpp
Normal file
2169
yoRadio/src/IRremoteESP8266/ir_Haier.cpp
Normal file
File diff suppressed because it is too large
Load Diff
653
yoRadio/src/IRremoteESP8266/ir_Haier.h
Normal file
653
yoRadio/src/IRremoteESP8266/ir_Haier.h
Normal file
@@ -0,0 +1,653 @@
|
||||
// Copyright 2018-2021 crankyoldgit
|
||||
/// @file
|
||||
/// @brief Support for Haier A/C protocols.
|
||||
/// The specifics of reverse engineering the protocols details:
|
||||
/// * HSU07-HEA03 by kuzin2006.
|
||||
/// * YR-W02/HSU-09HMC203 by non7top.
|
||||
/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/404
|
||||
/// @see https://www.dropbox.com/s/mecyib3lhdxc8c6/IR%20data%20reverse%20engineering.xlsx?dl=0
|
||||
/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/485
|
||||
/// @see https://www.dropbox.com/sh/w0bt7egp0fjger5/AADRFV6Wg4wZskJVdFvzb8Z0a?dl=0&preview=haer2.ods
|
||||
/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1804
|
||||
|
||||
// Supports:
|
||||
// Brand: Haier, Model: HSU07-HEA03 remote (HAIER_AC)
|
||||
// Brand: Haier, Model: YR-W02 remote (HAIER_AC_YRW02)
|
||||
// Brand: Haier, Model: HSU-09HMC203 A/C (HAIER_AC_YRW02)
|
||||
// Brand: Haier, Model: V9014557 M47 8D remote (HAIER_AC176)
|
||||
// Brand: Mabe, Model: MMI18HDBWCA6MI8 A/C (HAIER_AC176)
|
||||
// Brand: Mabe, Model: V12843 HJ200223 remote (HAIER_AC176)
|
||||
// Brand: Daichi, Model: D-H A/C (HAIER_AC176)
|
||||
// Brand: Haier, Model: KFR-26GW/83@UI-Ge A/C (HAIER_AC160)
|
||||
|
||||
#ifndef IR_HAIER_H_
|
||||
#define IR_HAIER_H_
|
||||
|
||||
#ifndef UNIT_TEST
|
||||
#include <Arduino.h>
|
||||
#endif
|
||||
#include "IRremoteESP8266.h"
|
||||
#include "IRsend.h"
|
||||
#ifdef UNIT_TEST
|
||||
#include "IRsend_test.h"
|
||||
#endif
|
||||
|
||||
/// Native representation of a Haier HSU07-HEA03 A/C message.
|
||||
union HaierProtocol{
|
||||
///< The state in native IR code form
|
||||
uint8_t remote_state[kHaierACStateLength];
|
||||
struct {
|
||||
// Byte 0
|
||||
uint8_t Prefix;
|
||||
// Byte 1
|
||||
uint8_t Command:4;
|
||||
uint8_t Temp :4;
|
||||
// Byte 2
|
||||
uint8_t CurrHours:5;
|
||||
uint8_t unknown :1; // value=1
|
||||
uint8_t SwingV :2;
|
||||
// Byte 3
|
||||
uint8_t CurrMins:6;
|
||||
uint8_t OffTimer:1;
|
||||
uint8_t OnTimer :1;
|
||||
// Byte 4
|
||||
uint8_t OffHours:5;
|
||||
uint8_t Health :1;
|
||||
uint8_t :0;
|
||||
// Byte 5
|
||||
uint8_t OffMins:6;
|
||||
uint8_t Fan :2;
|
||||
// Byte 6
|
||||
uint8_t OnHours:5;
|
||||
uint8_t Mode :3;
|
||||
// Byte 7
|
||||
uint8_t OnMins:6;
|
||||
uint8_t Sleep :1;
|
||||
uint8_t :0;
|
||||
// Byte 8
|
||||
uint8_t Sum;
|
||||
};
|
||||
};
|
||||
|
||||
// Constants
|
||||
|
||||
const uint8_t kHaierAcPrefix = 0b10100101;
|
||||
|
||||
const uint8_t kHaierAcMinTemp = 16;
|
||||
const uint8_t kHaierAcDefTemp = 25;
|
||||
const uint8_t kHaierAcMaxTemp = 30;
|
||||
const uint8_t kHaierAcCmdOff = 0b0000;
|
||||
const uint8_t kHaierAcCmdOn = 0b0001;
|
||||
const uint8_t kHaierAcCmdMode = 0b0010;
|
||||
const uint8_t kHaierAcCmdFan = 0b0011;
|
||||
const uint8_t kHaierAcCmdTempUp = 0b0110;
|
||||
const uint8_t kHaierAcCmdTempDown = 0b0111;
|
||||
const uint8_t kHaierAcCmdSleep = 0b1000;
|
||||
const uint8_t kHaierAcCmdTimerSet = 0b1001;
|
||||
const uint8_t kHaierAcCmdTimerCancel = 0b1010;
|
||||
const uint8_t kHaierAcCmdHealth = 0b1100;
|
||||
const uint8_t kHaierAcCmdSwing = 0b1101;
|
||||
|
||||
const uint8_t kHaierAcSwingVOff = 0b00;
|
||||
const uint8_t kHaierAcSwingVUp = 0b01;
|
||||
const uint8_t kHaierAcSwingVDown = 0b10;
|
||||
const uint8_t kHaierAcSwingVChg = 0b11;
|
||||
|
||||
const uint8_t kHaierAcAuto = 0;
|
||||
const uint8_t kHaierAcCool = 1;
|
||||
const uint8_t kHaierAcDry = 2;
|
||||
const uint8_t kHaierAcHeat = 3;
|
||||
const uint8_t kHaierAcFan = 4;
|
||||
|
||||
const uint8_t kHaierAcFanAuto = 0;
|
||||
const uint8_t kHaierAcFanLow = 1;
|
||||
const uint8_t kHaierAcFanMed = 2;
|
||||
const uint8_t kHaierAcFanHigh = 3;
|
||||
|
||||
const uint16_t kHaierAcMaxTime = (23 * 60) + 59;
|
||||
|
||||
const uint8_t kHaierAcSleepBit = 0b01000000;
|
||||
|
||||
// Legacy Haier AC defines.
|
||||
#define HAIER_AC_MIN_TEMP kHaierAcMinTemp
|
||||
#define HAIER_AC_DEF_TEMP kHaierAcDefTemp
|
||||
#define HAIER_AC_MAX_TEMP kHaierAcMaxTemp
|
||||
#define HAIER_AC_CMD_OFF kHaierAcCmdOff
|
||||
#define HAIER_AC_CMD_ON kHaierAcCmdOn
|
||||
#define HAIER_AC_CMD_MODE kHaierAcCmdMode
|
||||
#define HAIER_AC_CMD_FAN kHaierAcCmdFan
|
||||
#define HAIER_AC_CMD_TEMP_UP kHaierAcCmdTempUp
|
||||
#define HAIER_AC_CMD_TEMP_DOWN kHaierAcCmdTempDown
|
||||
#define HAIER_AC_CMD_SLEEP kHaierAcCmdSleep
|
||||
#define HAIER_AC_CMD_TIMER_SET kHaierAcCmdTimerSet
|
||||
#define HAIER_AC_CMD_TIMER_CANCEL kHaierAcCmdTimerCancel
|
||||
#define HAIER_AC_CMD_HEALTH kHaierAcCmdHealth
|
||||
#define HAIER_AC_CMD_SWINGV kHaierAcCmdSwing
|
||||
#define HAIER_AC_SWINGV_OFF kHaierAcSwingVOff
|
||||
#define HAIER_AC_SWINGV_UP kHaierAcSwingVUp
|
||||
#define HAIER_AC_SWINGV_DOWN kHaierAcSwingVDown
|
||||
#define HAIER_AC_SWINGV_CHG kHaierAcSwingVChg
|
||||
#define HAIER_AC_AUTO kHaierAcAuto
|
||||
#define HAIER_AC_COOL kHaierAcCool
|
||||
#define HAIER_AC_DRY kHaierAcDry
|
||||
#define HAIER_AC_HEAT kHaierAcHeat
|
||||
#define HAIER_AC_FAN kHaierAcFan
|
||||
#define HAIER_AC_FAN_AUTO kHaierAcFanAuto
|
||||
#define HAIER_AC_FAN_LOW kHaierAcFanLow
|
||||
#define HAIER_AC_FAN_MED kHaierAcFanMed
|
||||
#define HAIER_AC_FAN_HIGH kHaierAcFanHigh
|
||||
|
||||
const uint8_t kHaierAcYrw02MinTempC = 16;
|
||||
const uint8_t kHaierAcYrw02MaxTempC = 30;
|
||||
const uint8_t kHaierAcYrw02MinTempF = 60;
|
||||
const uint8_t kHaierAcYrw02MaxTempF = 86;
|
||||
const uint8_t kHaierAcYrw02DefTempC = 25;
|
||||
|
||||
const uint8_t kHaierAcYrw02ModelA = 0xA6;
|
||||
const uint8_t kHaierAcYrw02ModelB = 0x59;
|
||||
const uint8_t kHaierAc176Prefix = 0xB7;
|
||||
const uint8_t kHaierAc160Prefix = 0xB5;
|
||||
|
||||
const uint8_t kHaierAcYrw02SwingVOff = 0x0;
|
||||
const uint8_t kHaierAcYrw02SwingVTop = 0x1;
|
||||
const uint8_t kHaierAcYrw02SwingVMiddle = 0x2; // Not available in heat mode.
|
||||
const uint8_t kHaierAcYrw02SwingVBottom = 0x3; // Only available in heat mode.
|
||||
const uint8_t kHaierAcYrw02SwingVDown = 0xA;
|
||||
const uint8_t kHaierAcYrw02SwingVAuto = 0xC; // Airflow
|
||||
|
||||
const uint8_t kHaierAc160SwingVOff = 0b0000;
|
||||
const uint8_t kHaierAc160SwingVTop = 0b0001;
|
||||
const uint8_t kHaierAc160SwingVHighest = 0b0010;
|
||||
const uint8_t kHaierAc160SwingVHigh = 0b0100;
|
||||
const uint8_t kHaierAc160SwingVMiddle = 0b0110;
|
||||
const uint8_t kHaierAc160SwingVLow = 0b1000;
|
||||
const uint8_t kHaierAc160SwingVLowest = 0b0011;
|
||||
const uint8_t kHaierAc160SwingVAuto = 0b1100; // Airflow
|
||||
|
||||
const uint8_t kHaierAcYrw02SwingHMiddle = 0x0;
|
||||
const uint8_t kHaierAcYrw02SwingHLeftMax = 0x3;
|
||||
const uint8_t kHaierAcYrw02SwingHLeft = 0x4;
|
||||
const uint8_t kHaierAcYrw02SwingHRight = 0x5;
|
||||
const uint8_t kHaierAcYrw02SwingHRightMax = 0x6;
|
||||
const uint8_t kHaierAcYrw02SwingHAuto = 0x7;
|
||||
|
||||
const uint8_t kHaierAcYrw02FanHigh = 0b001;
|
||||
const uint8_t kHaierAcYrw02FanMed = 0b010;
|
||||
const uint8_t kHaierAcYrw02FanLow = 0b011;
|
||||
const uint8_t kHaierAcYrw02FanAuto = 0b101; // HAIER_AC176 uses `0` in Fan2
|
||||
|
||||
const uint8_t kHaierAcYrw02Auto = 0b000; // 0
|
||||
const uint8_t kHaierAcYrw02Cool = 0b001; // 1
|
||||
const uint8_t kHaierAcYrw02Dry = 0b010; // 2
|
||||
const uint8_t kHaierAcYrw02Heat = 0b100; // 4
|
||||
const uint8_t kHaierAcYrw02Fan = 0b110; // 5
|
||||
|
||||
const uint8_t kHaierAcYrw02ButtonTempUp = 0b00000;
|
||||
const uint8_t kHaierAcYrw02ButtonTempDown = 0b00001;
|
||||
const uint8_t kHaierAcYrw02ButtonSwingV = 0b00010;
|
||||
const uint8_t kHaierAcYrw02ButtonSwingH = 0b00011;
|
||||
const uint8_t kHaierAcYrw02ButtonFan = 0b00100;
|
||||
const uint8_t kHaierAcYrw02ButtonPower = 0b00101;
|
||||
const uint8_t kHaierAcYrw02ButtonMode = 0b00110;
|
||||
const uint8_t kHaierAcYrw02ButtonHealth = 0b00111;
|
||||
const uint8_t kHaierAcYrw02ButtonTurbo = 0b01000;
|
||||
const uint8_t kHaierAcYrw02ButtonSleep = 0b01011;
|
||||
const uint8_t kHaierAcYrw02ButtonTimer = 0b10000;
|
||||
const uint8_t kHaierAcYrw02ButtonLock = 0b10100;
|
||||
const uint8_t kHaierAc160ButtonLight = 0b10101;
|
||||
const uint8_t kHaierAc160ButtonAuxHeating = 0b10110;
|
||||
const uint8_t kHaierAc160ButtonClean = 0b11001;
|
||||
const uint8_t kHaierAcYrw02ButtonCFAB = 0b11010;
|
||||
|
||||
const uint8_t kHaierAcYrw02NoTimers = 0b000;
|
||||
const uint8_t kHaierAcYrw02OffTimer = 0b001;
|
||||
const uint8_t kHaierAcYrw02OnTimer = 0b010;
|
||||
const uint8_t kHaierAcYrw02OnThenOffTimer = 0b100;
|
||||
const uint8_t kHaierAcYrw02OffThenOnTimer = 0b101;
|
||||
|
||||
/// Native representation of a Haier 176 bit A/C message.
|
||||
union HaierAc176Protocol{
|
||||
uint8_t raw[kHaierAC176StateLength]; ///< The state in native form
|
||||
struct {
|
||||
// Byte 0
|
||||
uint8_t Model :8;
|
||||
// Byte 1
|
||||
uint8_t SwingV :4;
|
||||
uint8_t Temp :4; // 16C~30C
|
||||
// Byte 2
|
||||
uint8_t :5;
|
||||
uint8_t SwingH :3;
|
||||
// Byte 3
|
||||
uint8_t :1;
|
||||
uint8_t Health :1;
|
||||
uint8_t :3;
|
||||
uint8_t TimerMode :3;
|
||||
// Byte 4
|
||||
uint8_t :6;
|
||||
uint8_t Power :1;
|
||||
uint8_t :1;
|
||||
// Byte 5
|
||||
uint8_t OffTimerHrs :5;
|
||||
uint8_t Fan :3;
|
||||
// Byte 6
|
||||
uint8_t OffTimerMins:6;
|
||||
uint8_t Turbo :1;
|
||||
uint8_t Quiet :1;
|
||||
// Byte 7
|
||||
uint8_t OnTimerHrs :5;
|
||||
uint8_t Mode :3;
|
||||
// Byte 8
|
||||
uint8_t OnTimerMins :6;
|
||||
uint8_t :1;
|
||||
uint8_t Sleep :1;
|
||||
// Byte 9
|
||||
uint8_t :8;
|
||||
// Byte 10
|
||||
uint8_t ExtraDegreeF :1;
|
||||
uint8_t :4;
|
||||
uint8_t UseFahrenheit:1;
|
||||
uint8_t :2;
|
||||
// Byte 11
|
||||
uint8_t :8;
|
||||
// Byte 12
|
||||
uint8_t Button :5;
|
||||
uint8_t Lock :1;
|
||||
uint8_t :2;
|
||||
// Byte 13
|
||||
uint8_t Sum :8;
|
||||
// Byte 14
|
||||
uint8_t Prefix2 :8;
|
||||
// Byte 15
|
||||
uint8_t :8;
|
||||
// Byte 16
|
||||
uint8_t :6;
|
||||
uint8_t Fan2 :2;
|
||||
// Byte 17
|
||||
uint8_t :8;
|
||||
// Byte 18
|
||||
uint8_t :8;
|
||||
// Byte 19
|
||||
uint8_t :8;
|
||||
// Byte 20
|
||||
uint8_t :8;
|
||||
// Byte 21
|
||||
uint8_t Sum2 :8;
|
||||
};
|
||||
};
|
||||
|
||||
/// Native representation of a Haier 160 bit A/C message.
|
||||
union HaierAc160Protocol{
|
||||
uint8_t raw[kHaierAC160StateLength]; ///< The state in native form
|
||||
struct {
|
||||
// Byte 0
|
||||
uint8_t Model :8;
|
||||
// Byte 1
|
||||
uint8_t SwingV :4;
|
||||
uint8_t Temp :4; // 16C~30C
|
||||
// Byte 2
|
||||
uint8_t :5;
|
||||
uint8_t SwingH :3;
|
||||
// Byte 3
|
||||
uint8_t :1;
|
||||
uint8_t Health :1;
|
||||
uint8_t :3;
|
||||
uint8_t TimerMode :3;
|
||||
// Byte 4
|
||||
uint8_t :6;
|
||||
uint8_t Power :1;
|
||||
uint8_t AuxHeating :1;
|
||||
// Byte 5
|
||||
uint8_t OffTimerHrs :5;
|
||||
uint8_t Fan :3;
|
||||
// Byte 6
|
||||
uint8_t OffTimerMins:6;
|
||||
uint8_t Turbo :1;
|
||||
uint8_t Quiet :1;
|
||||
// Byte 7
|
||||
uint8_t OnTimerHrs :5;
|
||||
uint8_t Mode :3;
|
||||
// Byte 8
|
||||
uint8_t OnTimerMins :6;
|
||||
uint8_t :1;
|
||||
uint8_t Sleep :1;
|
||||
// Byte 9
|
||||
uint8_t :8;
|
||||
// Byte 10
|
||||
uint8_t ExtraDegreeF :1;
|
||||
uint8_t :3;
|
||||
uint8_t Clean :1;
|
||||
uint8_t UseFahrenheit:1;
|
||||
uint8_t :2;
|
||||
// Byte 11
|
||||
uint8_t :8;
|
||||
// Byte 12
|
||||
uint8_t Button :5;
|
||||
uint8_t Lock :1;
|
||||
uint8_t :2;
|
||||
// Byte 13
|
||||
uint8_t Sum :8;
|
||||
// Byte 14
|
||||
uint8_t Prefix :8;
|
||||
// Byte 15
|
||||
uint8_t :6;
|
||||
uint8_t Clean2 :1;
|
||||
uint8_t :1;
|
||||
// Byte 16
|
||||
uint8_t :5;
|
||||
uint8_t Fan2 :3;
|
||||
// Byte 17
|
||||
uint8_t :8;
|
||||
// Byte 18
|
||||
uint8_t :8;
|
||||
// Byte 19
|
||||
uint8_t Sum2 :8;
|
||||
};
|
||||
};
|
||||
|
||||
// Legacy Haier YRW02 remote defines.
|
||||
#define HAIER_AC_YRW02_SWING_OFF kHaierAcYrw02SwingOff
|
||||
#define HAIER_AC_YRW02_SWING_TOP kHaierAcYrw02SwingTop
|
||||
#define HAIER_AC_YRW02_SWING_MIDDLE kHaierAcYrw02SwingMiddle
|
||||
#define HAIER_AC_YRW02_SWING_BOTTOM kHaierAcYrw02SwingBottom
|
||||
#define HAIER_AC_YRW02_SWING_DOWN kHaierAcYrw02SwingDown
|
||||
#define HAIER_AC_YRW02_SWING_AUTO kHaierAcYrw02SwingAuto
|
||||
#define HAIER_AC_YRW02_FAN_HIGH kHaierAcYrw02FanHigh
|
||||
#define HAIER_AC_YRW02_FAN_MED kHaierAcYrw02FanMed
|
||||
#define HAIER_AC_YRW02_FAN_LOW kHaierAcYrw02FanLow
|
||||
#define HAIER_AC_YRW02_FAN_AUTO kHaierAcYrw02FanAuto
|
||||
#define HAIER_AC_YRW02_TURBO_OFF kHaierAcYrw02TurboOff
|
||||
#define HAIER_AC_YRW02_AUTO kHaierAcYrw02Auto
|
||||
#define HAIER_AC_YRW02_COOL kHaierAcYrw02Cool
|
||||
#define HAIER_AC_YRW02_DRY kHaierAcYrw02Dry
|
||||
#define HAIER_AC_YRW02_HEAT kHaierAcYrw02Heat
|
||||
#define HAIER_AC_YRW02_FAN kHaierAcYrw02Fan
|
||||
#define HAIER_AC_YRW02_BUTTON_TEMP_UP kHaierAcYrw02ButtonTempUp
|
||||
#define HAIER_AC_YRW02_BUTTON_TEMP_DOWN kHaierAcYrw02ButtonTempDown
|
||||
#define HAIER_AC_YRW02_BUTTON_SWING kHaierAcYrw02ButtonSwing
|
||||
#define HAIER_AC_YRW02_BUTTON_FAN kHaierAcYrw02ButtonFan
|
||||
#define HAIER_AC_YRW02_BUTTON_POWER kHaierAcYrw02ButtonPower
|
||||
#define HAIER_AC_YRW02_BUTTON_MODE kHaierAcYrw02ButtonMode
|
||||
#define HAIER_AC_YRW02_BUTTON_HEALTH kHaierAcYrw02ButtonHealth
|
||||
#define HAIER_AC_YRW02_BUTTON_TURBO kHaierAcYrw02ButtonTurbo
|
||||
#define HAIER_AC_YRW02_BUTTON_SLEEP kHaierAcYrw02ButtonSleep
|
||||
|
||||
// Classes
|
||||
/// Class for handling detailed Haier A/C messages.
|
||||
class IRHaierAC {
|
||||
public:
|
||||
explicit IRHaierAC(const uint16_t pin, const bool inverted = false,
|
||||
const bool use_modulation = true);
|
||||
#if SEND_HAIER_AC
|
||||
void send(const uint16_t repeat = kHaierAcDefaultRepeat);
|
||||
/// 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_HAIER_AC
|
||||
void begin(void);
|
||||
void stateReset(void);
|
||||
|
||||
void setCommand(const uint8_t command);
|
||||
uint8_t getCommand(void) const;
|
||||
|
||||
void setTemp(const uint8_t temp);
|
||||
uint8_t getTemp(void) const;
|
||||
|
||||
void setFan(const uint8_t speed);
|
||||
uint8_t getFan(void) const;
|
||||
|
||||
uint8_t getMode(void) const;
|
||||
void setMode(const uint8_t mode);
|
||||
|
||||
bool getSleep(void) const;
|
||||
void setSleep(const bool on);
|
||||
bool getHealth(void) const;
|
||||
void setHealth(const bool on);
|
||||
|
||||
int16_t getOnTimer(void) const;
|
||||
void setOnTimer(const uint16_t mins);
|
||||
int16_t getOffTimer(void) const;
|
||||
void setOffTimer(const uint16_t mins);
|
||||
void cancelTimers(void);
|
||||
|
||||
uint16_t getCurrTime(void) const;
|
||||
void setCurrTime(const uint16_t mins);
|
||||
|
||||
uint8_t getSwingV(void) const;
|
||||
void setSwingV(const uint8_t state);
|
||||
|
||||
uint8_t* getRaw(void);
|
||||
void setRaw(const uint8_t new_code[]);
|
||||
static bool validChecksum(uint8_t state[],
|
||||
const uint16_t length = kHaierACStateLength);
|
||||
static uint8_t convertMode(const stdAc::opmode_t mode);
|
||||
static uint8_t convertFan(const stdAc::fanspeed_t speed);
|
||||
static uint8_t convertSwingV(const stdAc::swingv_t position);
|
||||
static stdAc::opmode_t toCommonMode(const uint8_t mode);
|
||||
static stdAc::fanspeed_t toCommonFanSpeed(const uint8_t speed);
|
||||
static stdAc::swingv_t toCommonSwingV(const uint8_t pos);
|
||||
stdAc::state_t toCommon(void) const;
|
||||
String toString(void) const;
|
||||
#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
|
||||
HaierProtocol _;
|
||||
void checksum(void);
|
||||
};
|
||||
|
||||
/// Class for handling detailed Haier 176 bit A/C messages.
|
||||
class IRHaierAC176 {
|
||||
friend class IRHaierACYRW02;
|
||||
public:
|
||||
explicit IRHaierAC176(const uint16_t pin, const bool inverted = false,
|
||||
const bool use_modulation = true);
|
||||
#if SEND_HAIER_AC176
|
||||
virtual void send(const uint16_t repeat = kHaierAc176DefaultRepeat);
|
||||
/// 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_HAIER_AC176
|
||||
void begin(void);
|
||||
void stateReset(void);
|
||||
|
||||
void setModel(const haier_ac176_remote_model_t model);
|
||||
haier_ac176_remote_model_t getModel(void) const;
|
||||
|
||||
void setButton(const uint8_t button);
|
||||
uint8_t getButton(void) const;
|
||||
|
||||
void setUseFahrenheit(const bool on);
|
||||
bool getUseFahrenheit(void) const;
|
||||
void setTemp(const uint8_t temp, const bool fahrenheit = false);
|
||||
uint8_t getTemp(void) const;
|
||||
|
||||
void setFan(const uint8_t speed);
|
||||
uint8_t getFan(void) const;
|
||||
|
||||
uint8_t getMode(void) const;
|
||||
void setMode(const uint8_t mode);
|
||||
|
||||
bool getPower(void) const;
|
||||
void setPower(const bool on);
|
||||
void on(void);
|
||||
void off(void);
|
||||
|
||||
bool getSleep(void) const;
|
||||
void setSleep(const bool on);
|
||||
bool getHealth(void) const;
|
||||
void setHealth(const bool on);
|
||||
|
||||
bool getTurbo(void) const;
|
||||
void setTurbo(const bool on);
|
||||
bool getQuiet(void) const;
|
||||
void setQuiet(const bool on);
|
||||
|
||||
uint8_t getSwingV(void) const;
|
||||
void setSwingV(const uint8_t pos);
|
||||
uint8_t getSwingH(void) const;
|
||||
void setSwingH(const uint8_t pos);
|
||||
|
||||
/// These functions are for backward compatibility.
|
||||
/// Use getSwingV() and setSwingV() instead.
|
||||
uint8_t getSwing(void) const;
|
||||
void setSwing(const uint8_t pos);
|
||||
|
||||
void setTimerMode(const uint8_t setting);
|
||||
uint8_t getTimerMode(void) const;
|
||||
void setOnTimer(const uint16_t mins);
|
||||
uint16_t getOnTimer(void) const;
|
||||
void setOffTimer(const uint16_t mins);
|
||||
uint16_t getOffTimer(void) const;
|
||||
|
||||
bool getLock(void) const;
|
||||
void setLock(const bool on);
|
||||
|
||||
uint8_t* getRaw(void);
|
||||
virtual void setRaw(const uint8_t new_code[]);
|
||||
static bool validChecksum(const uint8_t state[],
|
||||
const uint16_t length = kHaierAC176StateLength);
|
||||
static uint8_t convertMode(const stdAc::opmode_t mode);
|
||||
static uint8_t convertFan(const stdAc::fanspeed_t speed);
|
||||
static uint8_t convertSwingV(const stdAc::swingv_t position);
|
||||
static uint8_t convertSwingH(const stdAc::swingh_t position);
|
||||
static stdAc::opmode_t toCommonMode(const uint8_t mode);
|
||||
static stdAc::fanspeed_t toCommonFanSpeed(const uint8_t speed);
|
||||
static stdAc::swingv_t toCommonSwingV(const uint8_t pos);
|
||||
static stdAc::swingh_t toCommonSwingH(const uint8_t pos);
|
||||
static bool toCommonTurbo(const uint8_t speed);
|
||||
static bool toCommonQuiet(const uint8_t speed);
|
||||
stdAc::state_t toCommon(void) const;
|
||||
String toString(void) const;
|
||||
#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
|
||||
HaierAc176Protocol _;
|
||||
void checksum(void);
|
||||
};
|
||||
|
||||
/// Class for handling detailed Haier ACYRW02 A/C messages.
|
||||
class IRHaierACYRW02 : public IRHaierAC176 {
|
||||
public:
|
||||
explicit IRHaierACYRW02(const uint16_t pin, const bool inverted = false,
|
||||
const bool use_modulation = true);
|
||||
#if SEND_HAIER_AC_YRW02
|
||||
void send(const uint16_t repeat = kHaierAcYrw02DefaultRepeat) override;
|
||||
/// 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_HAIER_AC_YRW02
|
||||
void setRaw(const uint8_t new_code[]) override;
|
||||
static bool validChecksum(
|
||||
const uint8_t state[],
|
||||
const uint16_t length = kHaierACYRW02StateLength);
|
||||
};
|
||||
|
||||
/// Class for handling detailed Haier 160 bit A/C messages.
|
||||
class IRHaierAC160 {
|
||||
public:
|
||||
explicit IRHaierAC160(const uint16_t pin, const bool inverted = false,
|
||||
const bool use_modulation = true);
|
||||
#if SEND_HAIER_AC160
|
||||
virtual void send(const uint16_t repeat = kHaierAc160DefaultRepeat);
|
||||
/// 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_HAIER_AC160
|
||||
void begin(void);
|
||||
void stateReset(void);
|
||||
|
||||
void setButton(const uint8_t button);
|
||||
uint8_t getButton(void) const;
|
||||
|
||||
void setUseFahrenheit(const bool on);
|
||||
bool getUseFahrenheit(void) const;
|
||||
void setTemp(const uint8_t temp, const bool fahrenheit = false);
|
||||
uint8_t getTemp(void) const;
|
||||
|
||||
void setFan(const uint8_t speed);
|
||||
uint8_t getFan(void) const;
|
||||
|
||||
uint8_t getMode(void) const;
|
||||
void setMode(const uint8_t mode);
|
||||
|
||||
bool getPower(void) const;
|
||||
void setPower(const bool on);
|
||||
void on(void);
|
||||
void off(void);
|
||||
|
||||
bool getSleep(void) const;
|
||||
void setSleep(const bool on);
|
||||
bool getClean(void) const;
|
||||
void setClean(const bool on);
|
||||
bool getLightToggle(void) const;
|
||||
void setLightToggle(const bool on);
|
||||
|
||||
bool getTurbo(void) const;
|
||||
void setTurbo(const bool on);
|
||||
bool getQuiet(void) const;
|
||||
void setQuiet(const bool on);
|
||||
bool getAuxHeating(void) const;
|
||||
void setAuxHeating(const bool on);
|
||||
|
||||
uint8_t getSwingV(void) const;
|
||||
void setSwingV(const uint8_t pos);
|
||||
|
||||
void setTimerMode(const uint8_t setting);
|
||||
uint8_t getTimerMode(void) const;
|
||||
void setOnTimer(const uint16_t mins);
|
||||
uint16_t getOnTimer(void) const;
|
||||
void setOffTimer(const uint16_t mins);
|
||||
uint16_t getOffTimer(void) const;
|
||||
|
||||
bool getLock(void) const;
|
||||
void setLock(const bool on);
|
||||
|
||||
bool getHealth(void) const;
|
||||
void setHealth(const bool on);
|
||||
|
||||
uint8_t* getRaw(void);
|
||||
virtual void setRaw(const uint8_t new_code[]);
|
||||
static bool validChecksum(const uint8_t state[],
|
||||
const uint16_t length = kHaierAC160StateLength);
|
||||
static uint8_t convertMode(const stdAc::opmode_t mode);
|
||||
static uint8_t convertFan(const stdAc::fanspeed_t speed);
|
||||
static uint8_t convertSwingV(const stdAc::swingv_t position);
|
||||
static stdAc::opmode_t toCommonMode(const uint8_t mode);
|
||||
static stdAc::fanspeed_t toCommonFanSpeed(const uint8_t speed);
|
||||
static stdAc::swingv_t toCommonSwingV(const uint8_t pos);
|
||||
static bool toCommonTurbo(const uint8_t speed);
|
||||
static bool toCommonQuiet(const uint8_t speed);
|
||||
stdAc::state_t toCommon(const stdAc::state_t *prev = NULL) const;
|
||||
String toString(void) const;
|
||||
#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
|
||||
HaierAc160Protocol _;
|
||||
void checksum(void);
|
||||
};
|
||||
#endif // IR_HAIER_H_
|
||||
1996
yoRadio/src/IRremoteESP8266/ir_Hitachi.cpp
Normal file
1996
yoRadio/src/IRremoteESP8266/ir_Hitachi.cpp
Normal file
File diff suppressed because it is too large
Load Diff
667
yoRadio/src/IRremoteESP8266/ir_Hitachi.h
Normal file
667
yoRadio/src/IRremoteESP8266/ir_Hitachi.h
Normal file
@@ -0,0 +1,667 @@
|
||||
// Copyright 2018-2020 David Conran
|
||||
/// @file
|
||||
/// @brief Support for Hitachi A/C protocols.
|
||||
/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/417
|
||||
/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/453
|
||||
/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/973
|
||||
/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1056
|
||||
/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1060
|
||||
/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1134
|
||||
/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1729
|
||||
/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1757
|
||||
|
||||
// Supports:
|
||||
// Brand: Hitachi, Model: RAS-35THA6 remote
|
||||
// Brand: Hitachi, Model: LT0541-HTA remote (HITACHI_AC1)
|
||||
// Brand: Hitachi, Model: Series VI A/C (Circa 2007) (HITACHI_AC1)
|
||||
// Brand: Hitachi, Model: RAR-8P2 remote (HITACHI_AC424)
|
||||
// Brand: Hitachi, Model: RAS-AJ25H A/C (HITACHI_AC424)
|
||||
// Brand: Hitachi, Model: PC-LH3B (HITACHI_AC3)
|
||||
// Brand: Hitachi, Model: KAZE-312KSDP A/C (HITACHI_AC1)
|
||||
// Brand: Hitachi, Model: R-LT0541-HTA/Y.K.1.1-1 V2.3 remote (HITACHI_AC1)
|
||||
// Brand: Hitachi, Model: RAS-22NK A/C (HITACHI_AC344)
|
||||
// Brand: Hitachi, Model: RF11T1 remote (HITACHI_AC344)
|
||||
// Brand: Hitachi, Model: RAR-2P2 remote (HITACHI_AC264)
|
||||
// Brand: Hitachi, Model: RAK-25NH5 A/C (HITACHI_AC264)
|
||||
// Brand: Hitachi, Model: RAR-3U3 remote (HITACHI_AC296)
|
||||
// Brand: Hitachi, Model: RAS-70YHA3 A/C (HITACHI_AC296)
|
||||
|
||||
#ifndef IR_HITACHI_H_
|
||||
#define IR_HITACHI_H_
|
||||
|
||||
#define __STDC_LIMIT_MACROS
|
||||
#include <stdint.h>
|
||||
#ifndef UNIT_TEST
|
||||
#include <Arduino.h>
|
||||
#endif
|
||||
#include "IRremoteESP8266.h"
|
||||
#include "IRsend.h"
|
||||
#ifdef UNIT_TEST
|
||||
#include "IRsend_test.h"
|
||||
#endif
|
||||
|
||||
/// Native representation of a Hitachi 224-bit A/C message.
|
||||
union HitachiProtocol{
|
||||
uint8_t raw[kHitachiAcStateLength]; ///< The state in native code.
|
||||
struct {
|
||||
// Byte 0~9
|
||||
uint8_t pad0[10];
|
||||
// Byte 10
|
||||
uint8_t Mode :8;
|
||||
// Byte 11
|
||||
uint8_t Temp :8;
|
||||
// Byte 12
|
||||
uint8_t :8;
|
||||
// Byte 13
|
||||
uint8_t Fan :8;
|
||||
// Byte 14
|
||||
uint8_t :7;
|
||||
uint8_t SwingV :1;
|
||||
// Byte 15
|
||||
uint8_t :7;
|
||||
uint8_t SwingH :1;
|
||||
// Byte 16
|
||||
uint8_t :8;
|
||||
// Byte 17
|
||||
uint8_t Power :1;
|
||||
uint8_t :7;
|
||||
// Byte 18~26
|
||||
uint8_t pad1[9];
|
||||
// Byte 27
|
||||
uint8_t Sum :8;
|
||||
};
|
||||
};
|
||||
|
||||
// Constants
|
||||
const uint16_t kHitachiAcFreq = 38000; // Hz.
|
||||
const uint8_t kHitachiAcAuto = 2;
|
||||
const uint8_t kHitachiAcHeat = 3;
|
||||
const uint8_t kHitachiAcCool = 4;
|
||||
const uint8_t kHitachiAcDry = 5;
|
||||
const uint8_t kHitachiAcFan = 0xC;
|
||||
const uint8_t kHitachiAcFanAuto = 1;
|
||||
const uint8_t kHitachiAcFanLow = 2;
|
||||
const uint8_t kHitachiAcFanMed = 3;
|
||||
const uint8_t kHitachiAcFanHigh = 5;
|
||||
const uint8_t kHitachiAcMinTemp = 16; // 16C
|
||||
const uint8_t kHitachiAcMaxTemp = 32; // 32C
|
||||
const uint8_t kHitachiAcAutoTemp = 23; // 23C
|
||||
|
||||
/// Native representation of a Hitachi 53-byte/424-bit A/C message.
|
||||
union Hitachi424Protocol{
|
||||
uint8_t raw[kHitachiAc424StateLength]; ///< The state in native code
|
||||
struct {
|
||||
// Byte 0~10
|
||||
uint8_t pad0[11];
|
||||
// Byte 11
|
||||
uint8_t Button :8;
|
||||
// Byte 12
|
||||
uint8_t :8;
|
||||
// Byte 13
|
||||
uint8_t :2;
|
||||
uint8_t Temp :6;
|
||||
// Byte 14~24
|
||||
uint8_t pad1[11];
|
||||
// Byte 25
|
||||
uint8_t Mode :4;
|
||||
uint8_t Fan :4;
|
||||
// Byte 26
|
||||
uint8_t :8;
|
||||
// Byte 27
|
||||
uint8_t :4;
|
||||
uint8_t Power :1;
|
||||
uint8_t :3;
|
||||
// Byte 28~34
|
||||
uint8_t pad2[7];
|
||||
// Byte 35
|
||||
uint8_t SwingH :3;
|
||||
uint8_t :5;
|
||||
// Byte 36
|
||||
uint8_t :8;
|
||||
// Byte 37
|
||||
uint8_t :5;
|
||||
uint8_t SwingV :1;
|
||||
uint8_t :2;
|
||||
};
|
||||
};
|
||||
|
||||
// HitachiAc424 & HitachiAc344
|
||||
const uint8_t kHitachiAc424ButtonPowerMode = 0x13;
|
||||
const uint8_t kHitachiAc424ButtonFan = 0x42;
|
||||
const uint8_t kHitachiAc424ButtonTempDown = 0x43;
|
||||
const uint8_t kHitachiAc424ButtonTempUp = 0x44;
|
||||
const uint8_t kHitachiAc424ButtonSwingV = 0x81;
|
||||
const uint8_t kHitachiAc424ButtonSwingH = 0x8C;
|
||||
const uint8_t kHitachiAc344ButtonPowerMode = kHitachiAc424ButtonPowerMode;
|
||||
const uint8_t kHitachiAc344ButtonFan = kHitachiAc424ButtonFan;
|
||||
const uint8_t kHitachiAc344ButtonTempDown = kHitachiAc424ButtonTempDown;
|
||||
const uint8_t kHitachiAc344ButtonTempUp = kHitachiAc424ButtonTempUp;
|
||||
const uint8_t kHitachiAc344ButtonSwingV = kHitachiAc424ButtonSwingV;
|
||||
const uint8_t kHitachiAc344ButtonSwingH = kHitachiAc424ButtonSwingH;
|
||||
|
||||
const uint8_t kHitachiAc424MinTemp = 16; // 16C
|
||||
const uint8_t kHitachiAc424MaxTemp = 32; // 32C
|
||||
const uint8_t kHitachiAc344MinTemp = kHitachiAc424MinTemp;
|
||||
const uint8_t kHitachiAc344MaxTemp = kHitachiAc424MaxTemp;
|
||||
const uint8_t kHitachiAc424FanTemp = 27; // 27C
|
||||
|
||||
const uint8_t kHitachiAc424Fan = 1;
|
||||
const uint8_t kHitachiAc424Cool = 3;
|
||||
const uint8_t kHitachiAc424Dry = 5;
|
||||
const uint8_t kHitachiAc424Heat = 6;
|
||||
const uint8_t kHitachiAc344Fan = kHitachiAc424Fan;
|
||||
const uint8_t kHitachiAc344Cool = kHitachiAc424Cool;
|
||||
const uint8_t kHitachiAc344Dry = kHitachiAc424Dry;
|
||||
const uint8_t kHitachiAc344Heat = kHitachiAc424Heat;
|
||||
|
||||
const uint8_t kHitachiAc424FanMin = 1;
|
||||
const uint8_t kHitachiAc424FanLow = 2;
|
||||
const uint8_t kHitachiAc424FanMedium = 3;
|
||||
const uint8_t kHitachiAc424FanHigh = 4;
|
||||
const uint8_t kHitachiAc424FanAuto = 5;
|
||||
const uint8_t kHitachiAc424FanMax = 6;
|
||||
const uint8_t kHitachiAc424FanMaxDry = 2;
|
||||
const uint8_t kHitachiAc344FanMin = kHitachiAc424FanMin;
|
||||
const uint8_t kHitachiAc344FanLow = kHitachiAc424FanLow;
|
||||
const uint8_t kHitachiAc344FanMedium = kHitachiAc424FanMedium;
|
||||
const uint8_t kHitachiAc344FanHigh = kHitachiAc424FanHigh;
|
||||
const uint8_t kHitachiAc344FanAuto = kHitachiAc424FanAuto;
|
||||
const uint8_t kHitachiAc344FanMax = kHitachiAc424FanMax;
|
||||
|
||||
const uint8_t kHitachiAc344SwingHAuto = 0; // 0b000
|
||||
const uint8_t kHitachiAc344SwingHRightMax = 1; // 0b001
|
||||
const uint8_t kHitachiAc344SwingHRight = 2; // 0b010
|
||||
const uint8_t kHitachiAc344SwingHMiddle = 3; // 0b011
|
||||
const uint8_t kHitachiAc344SwingHLeft = 4; // 0b100
|
||||
const uint8_t kHitachiAc344SwingHLeftMax = 5; // 0b101
|
||||
|
||||
|
||||
/// Native representation of a Hitachi 104-bit A/C message.
|
||||
union Hitachi1Protocol{
|
||||
uint8_t raw[kHitachiAc1StateLength]; ///< The state in native code.
|
||||
struct {
|
||||
// Byte 0~2
|
||||
uint8_t pad[3];
|
||||
// Byte 3
|
||||
uint8_t :6;
|
||||
uint8_t Model :2;
|
||||
// Byte 4
|
||||
uint8_t :8;
|
||||
// Byte 5
|
||||
uint8_t Fan :4;
|
||||
uint8_t Mode :4;
|
||||
// Byte 6
|
||||
uint8_t :2;
|
||||
uint8_t Temp :5; // stored in LSB order.
|
||||
uint8_t :1;
|
||||
// Byte 7
|
||||
uint8_t OffTimerLow :8; // nr. of minutes
|
||||
// Byte 8
|
||||
uint8_t OffTimerHigh :8; // & in LSB order.
|
||||
// Byte 9
|
||||
uint8_t OnTimerLow :8; // nr. of minutes
|
||||
// Byte 10
|
||||
uint8_t OnTimerHigh :8; // & in LSB order.
|
||||
// Byte 11
|
||||
uint8_t SwingToggle :1;
|
||||
uint8_t Sleep :3;
|
||||
uint8_t PowerToggle :1;
|
||||
uint8_t Power :1;
|
||||
uint8_t SwingV :1;
|
||||
uint8_t SwingH :1;
|
||||
// Byte 12
|
||||
uint8_t Sum :8;
|
||||
};
|
||||
};
|
||||
// HitachiAc1
|
||||
// Model
|
||||
const uint8_t kHitachiAc1Model_A = 0b10;
|
||||
const uint8_t kHitachiAc1Model_B = 0b01;
|
||||
|
||||
// Mode & Fan
|
||||
const uint8_t kHitachiAc1Dry = 0b0010; // 2
|
||||
const uint8_t kHitachiAc1Fan = 0b0100; // 4
|
||||
const uint8_t kHitachiAc1Cool = 0b0110; // 6
|
||||
const uint8_t kHitachiAc1Heat = 0b1001; // 9
|
||||
const uint8_t kHitachiAc1Auto = 0b1110; // 14
|
||||
const uint8_t kHitachiAc1FanAuto = 1; // 0b0001
|
||||
const uint8_t kHitachiAc1FanHigh = 2; // 0b0010
|
||||
const uint8_t kHitachiAc1FanMed = 4; // 0b0100
|
||||
const uint8_t kHitachiAc1FanLow = 8; // 0b1000
|
||||
|
||||
// Temp
|
||||
const uint8_t kHitachiAc1TempSize = 5; // Mask 0b01111100
|
||||
const uint8_t kHitachiAc1TempDelta = 7;
|
||||
const uint8_t kHitachiAc1TempAuto = 25; // Celsius
|
||||
// Timer
|
||||
const uint8_t kHitachiAc1TimerSize = 16; // Mask 0b1111111111111111
|
||||
// Sleep
|
||||
const uint8_t kHitachiAc1SleepOff = 0b000;
|
||||
const uint8_t kHitachiAc1Sleep1 = 0b001;
|
||||
const uint8_t kHitachiAc1Sleep2 = 0b010;
|
||||
const uint8_t kHitachiAc1Sleep3 = 0b011;
|
||||
const uint8_t kHitachiAc1Sleep4 = 0b100;
|
||||
// Checksum
|
||||
const uint8_t kHitachiAc1ChecksumStartByte = 5;
|
||||
|
||||
|
||||
/// Native representation of a Hitachi 164-bit A/C message.
|
||||
union HitachiAC264Protocol{
|
||||
uint8_t raw[kHitachiAc264StateLength]; ///< The state in native code.
|
||||
struct {
|
||||
// Bytes 0~10
|
||||
uint8_t pad0[11];
|
||||
// Byte 11
|
||||
uint8_t Button :8;
|
||||
// Byte 12
|
||||
uint8_t :8;
|
||||
// Byte 13
|
||||
uint8_t :2;
|
||||
uint8_t Temp :6;
|
||||
// Byte 14
|
||||
uint8_t :8;
|
||||
// Bytes 14~24
|
||||
uint8_t pad1[10];
|
||||
// Byte 25
|
||||
uint8_t Mode :4;
|
||||
uint8_t Fan :4;
|
||||
// Byte 26
|
||||
uint8_t :8;
|
||||
// Byte 27
|
||||
uint8_t :4;
|
||||
uint8_t Power :1;
|
||||
uint8_t :3;
|
||||
// Byte 28
|
||||
uint8_t :8;
|
||||
// Bytes 29~32
|
||||
uint8_t pad2[4];
|
||||
};
|
||||
};
|
||||
|
||||
// HitachiAc264
|
||||
const uint8_t kHitachiAc264ButtonPowerMode = kHitachiAc424ButtonPowerMode;
|
||||
const uint8_t kHitachiAc264ButtonFan = kHitachiAc424ButtonFan;
|
||||
const uint8_t kHitachiAc264ButtonTempDown = kHitachiAc424ButtonTempDown;
|
||||
const uint8_t kHitachiAc264ButtonTempUp = kHitachiAc424ButtonTempUp;
|
||||
const uint8_t kHitachiAc264ButtonSwingV = kHitachiAc424ButtonSwingV;
|
||||
const uint8_t kHitachiAc264MinTemp = kHitachiAc424MinTemp; // 16C
|
||||
const uint8_t kHitachiAc264MaxTemp = kHitachiAc424MaxTemp; // 32C
|
||||
const uint8_t kHitachiAc264Fan = kHitachiAc424Fan;
|
||||
const uint8_t kHitachiAc264Cool = kHitachiAc424Cool;
|
||||
const uint8_t kHitachiAc264Dry = kHitachiAc424Dry;
|
||||
const uint8_t kHitachiAc264Heat = kHitachiAc424Heat;
|
||||
const uint8_t kHitachiAc264FanMin = kHitachiAc424FanMin;
|
||||
const uint8_t kHitachiAc264FanLow = kHitachiAc424FanMin;
|
||||
const uint8_t kHitachiAc264FanMedium = kHitachiAc424FanMedium;
|
||||
const uint8_t kHitachiAc264FanHigh = kHitachiAc424FanHigh;
|
||||
const uint8_t kHitachiAc264FanAuto = kHitachiAc424FanAuto;
|
||||
|
||||
// HitachiAc296
|
||||
union HitachiAC296Protocol{
|
||||
uint8_t raw[kHitachiAc296StateLength];
|
||||
struct {
|
||||
// Byte 0~12
|
||||
uint8_t pad0[13];
|
||||
// Byte 13
|
||||
uint8_t :2;
|
||||
uint8_t Temp :5; // LSB
|
||||
uint8_t :1;
|
||||
uint8_t :8;
|
||||
// Byte 15~16
|
||||
uint8_t :8;
|
||||
uint8_t :8;
|
||||
// Byte 17~24
|
||||
uint8_t OffTimerLow :8; // LSB
|
||||
uint8_t /* Parity */ :8;
|
||||
uint8_t OffTimerHigh :8;
|
||||
uint8_t /* Parity */ :8;
|
||||
uint8_t OnTimerLow :8; // LSB
|
||||
uint8_t /* Parity */ :8;
|
||||
uint8_t OnTimerHigh :4;
|
||||
uint8_t OffTimerActive :1;
|
||||
uint8_t OnTimerActive :1;
|
||||
uint8_t :2;
|
||||
uint8_t /* Parity */ :8;
|
||||
// Byte 25~26
|
||||
uint8_t Mode :4;
|
||||
uint8_t Fan :3;
|
||||
uint8_t :1;
|
||||
uint8_t :8;
|
||||
// Byte 27~28
|
||||
uint8_t :4;
|
||||
uint8_t Power :1;
|
||||
uint8_t :2;
|
||||
uint8_t TimerActive :1;
|
||||
uint8_t :8;
|
||||
// Byte 29~34
|
||||
uint8_t pad1[6];
|
||||
// Byte 35~36
|
||||
uint8_t :4;
|
||||
uint8_t Humidity :4; // LSB
|
||||
uint8_t :8;
|
||||
};
|
||||
};
|
||||
|
||||
// Mode & Fan
|
||||
const uint8_t kHitachiAc296Cool = 0b0011;
|
||||
const uint8_t kHitachiAc296DryCool = 0b0100;
|
||||
const uint8_t kHitachiAc296Dehumidify = 0b0101;
|
||||
const uint8_t kHitachiAc296Heat = 0b0110;
|
||||
const uint8_t kHitachiAc296Auto = 0b0111;
|
||||
const uint8_t kHitachiAc296AutoDehumidifying = 0b1001;
|
||||
const uint8_t kHitachiAc296QuickLaundry = 0b1010;
|
||||
const uint8_t kHitachiAc296CondensationControl = 0b1100;
|
||||
|
||||
const uint8_t kHitachiAc296FanSilent = 0b001;
|
||||
const uint8_t kHitachiAc296FanLow = 0b010;
|
||||
const uint8_t kHitachiAc296FanMedium = 0b011;
|
||||
const uint8_t kHitachiAc296FanHigh = 0b100;
|
||||
const uint8_t kHitachiAc296FanAuto = 0b101;
|
||||
|
||||
const uint8_t kHitachiAc296TempAuto = 1; // Special value for "Auto" op mode.
|
||||
const uint8_t kHitachiAc296MinTemp = 16;
|
||||
const uint8_t kHitachiAc296MaxTemp = 31; // Max value you can store in 5 bits.
|
||||
|
||||
const uint8_t kHitachiAc296PowerOn = 1;
|
||||
const uint8_t kHitachiAc296PowerOff = 0;
|
||||
|
||||
|
||||
// Classes
|
||||
/// Class for handling detailed Hitachi 224-bit A/C messages.
|
||||
/// @see https://github.com/ToniA/arduino-heatpumpir/blob/master/HitachiHeatpumpIR.cpp
|
||||
class IRHitachiAc {
|
||||
public:
|
||||
explicit IRHitachiAc(const uint16_t pin, const bool inverted = false,
|
||||
const bool use_modulation = true);
|
||||
void stateReset(void);
|
||||
#if SEND_HITACHI_AC
|
||||
void send(const uint16_t repeat = kHitachiAcDefaultRepeat);
|
||||
/// 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_HITACHI_AC
|
||||
void begin(void);
|
||||
void on(void);
|
||||
void off(void);
|
||||
void setPower(const bool on);
|
||||
bool getPower(void) const;
|
||||
void setTemp(const uint8_t temp);
|
||||
uint8_t getTemp(void) const;
|
||||
void setFan(const uint8_t speed);
|
||||
uint8_t getFan(void) const;
|
||||
void setMode(const uint8_t mode);
|
||||
uint8_t getMode(void) const;
|
||||
void setSwingVertical(const bool on);
|
||||
bool getSwingVertical(void) const;
|
||||
void setSwingHorizontal(const bool on);
|
||||
bool getSwingHorizontal(void) const;
|
||||
uint8_t* getRaw(void);
|
||||
void setRaw(const uint8_t new_code[],
|
||||
const uint16_t length = kHitachiAcStateLength);
|
||||
static bool validChecksum(const uint8_t state[],
|
||||
const uint16_t length = kHitachiAcStateLength);
|
||||
static uint8_t calcChecksum(const uint8_t state[],
|
||||
const uint16_t length = kHitachiAcStateLength);
|
||||
static uint8_t convertMode(const stdAc::opmode_t mode);
|
||||
static uint8_t convertFan(const stdAc::fanspeed_t speed);
|
||||
static stdAc::opmode_t toCommonMode(const uint8_t mode);
|
||||
static stdAc::fanspeed_t toCommonFanSpeed(const uint8_t speed);
|
||||
stdAc::state_t toCommon(void) const;
|
||||
String toString(void) const;
|
||||
#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
|
||||
HitachiProtocol _;
|
||||
void checksum(const uint16_t length = kHitachiAcStateLength);
|
||||
uint8_t _previoustemp;
|
||||
};
|
||||
|
||||
/// Class for handling detailed Hitachi 104-bit A/C messages.
|
||||
/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1056
|
||||
class IRHitachiAc1 {
|
||||
public:
|
||||
explicit IRHitachiAc1(const uint16_t pin, const bool inverted = false,
|
||||
const bool use_modulation = true);
|
||||
|
||||
void stateReset(void);
|
||||
#if SEND_HITACHI_AC1
|
||||
void send(const uint16_t repeat = kHitachiAcDefaultRepeat);
|
||||
/// 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_HITACHI_AC1
|
||||
void begin(void);
|
||||
void on(void);
|
||||
void off(void);
|
||||
void setModel(const hitachi_ac1_remote_model_t model);
|
||||
hitachi_ac1_remote_model_t getModel(void) const;
|
||||
void setPower(const bool on);
|
||||
bool getPower(void) const;
|
||||
void setPowerToggle(const bool on);
|
||||
bool getPowerToggle(void) const;
|
||||
void setTemp(const uint8_t temp);
|
||||
uint8_t getTemp(void) const;
|
||||
void setFan(const uint8_t speed, const bool force = false);
|
||||
uint8_t getFan(void) const;
|
||||
void setMode(const uint8_t mode);
|
||||
uint8_t getMode(void) const;
|
||||
void setSwingToggle(const bool toggle);
|
||||
bool getSwingToggle(void) const;
|
||||
void setSwingV(const bool on);
|
||||
bool getSwingV(void) const;
|
||||
void setSwingH(const bool on);
|
||||
bool getSwingH(void) const;
|
||||
void setSleep(const uint8_t mode);
|
||||
uint8_t getSleep(void) const;
|
||||
void setOnTimer(const uint16_t mins);
|
||||
uint16_t getOnTimer(void) const;
|
||||
void setOffTimer(const uint16_t mins);
|
||||
uint16_t getOffTimer(void) const;
|
||||
uint8_t* getRaw(void);
|
||||
void setRaw(const uint8_t new_code[],
|
||||
const uint16_t length = kHitachiAc1StateLength);
|
||||
static bool validChecksum(const uint8_t state[],
|
||||
const uint16_t length = kHitachiAc1StateLength);
|
||||
static uint8_t calcChecksum(const uint8_t state[],
|
||||
const uint16_t length = kHitachiAc1StateLength);
|
||||
static uint8_t convertMode(const stdAc::opmode_t mode);
|
||||
static uint8_t convertFan(const stdAc::fanspeed_t speed);
|
||||
static stdAc::opmode_t toCommonMode(const uint8_t mode);
|
||||
static stdAc::fanspeed_t toCommonFanSpeed(const uint8_t speed);
|
||||
stdAc::state_t toCommon(void) const;
|
||||
String toString(void) const;
|
||||
#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
|
||||
Hitachi1Protocol _;
|
||||
void checksum(const uint16_t length = kHitachiAc1StateLength);
|
||||
};
|
||||
|
||||
/// Class for handling detailed Hitachi 53-byte/424-bit A/C messages.
|
||||
class IRHitachiAc424 {
|
||||
friend class IRHitachiAc264;
|
||||
friend class IRHitachiAc344;
|
||||
public:
|
||||
explicit IRHitachiAc424(const uint16_t pin, const bool inverted = false,
|
||||
const bool use_modulation = true);
|
||||
virtual void stateReset(void);
|
||||
#if SEND_HITACHI_AC424
|
||||
virtual void send(const uint16_t repeat = kHitachiAcDefaultRepeat);
|
||||
/// 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_HITACHI_AC424
|
||||
void begin(void);
|
||||
void on(void);
|
||||
void off(void);
|
||||
void setPower(const bool on);
|
||||
bool getPower(void) const;
|
||||
void setTemp(const uint8_t temp, bool setPrevious = true);
|
||||
uint8_t getTemp(void) const;
|
||||
virtual void setFan(const uint8_t speed);
|
||||
uint8_t getFan(void) const;
|
||||
uint8_t getButton(void) const;
|
||||
void setButton(const uint8_t button);
|
||||
void setSwingVToggle(const bool on);
|
||||
bool getSwingVToggle(void) const;
|
||||
void setMode(const uint8_t mode);
|
||||
uint8_t getMode(void) const;
|
||||
uint8_t* getRaw(void);
|
||||
virtual void setRaw(const uint8_t new_code[],
|
||||
const uint16_t length = kHitachiAc424StateLength);
|
||||
static uint8_t convertMode(const stdAc::opmode_t mode);
|
||||
virtual uint8_t convertFan(const stdAc::fanspeed_t speed) const;
|
||||
static stdAc::opmode_t toCommonMode(const uint8_t mode);
|
||||
virtual stdAc::fanspeed_t toCommonFanSpeed(const uint8_t speed) const;
|
||||
virtual stdAc::state_t toCommon(void) const;
|
||||
virtual String toString(void) const;
|
||||
#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
|
||||
Hitachi424Protocol _;
|
||||
void setInvertedStates(void);
|
||||
String _toString(void) const;
|
||||
uint8_t _previoustemp;
|
||||
};
|
||||
|
||||
/// Class for handling detailed Hitachi 15to27-byte/120to216-bit A/C messages.
|
||||
class IRHitachiAc3 {
|
||||
public:
|
||||
explicit IRHitachiAc3(const uint16_t pin, const bool inverted = false,
|
||||
const bool use_modulation = true);
|
||||
|
||||
void stateReset(void);
|
||||
#if SEND_HITACHI_AC3
|
||||
void send(const uint16_t repeat = kHitachiAcDefaultRepeat);
|
||||
/// 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_HITACHI_AC3
|
||||
void begin(void);
|
||||
uint8_t getMode(void);
|
||||
uint8_t* getRaw(void);
|
||||
void setRaw(const uint8_t new_code[],
|
||||
const uint16_t length = kHitachiAc3StateLength);
|
||||
static bool hasInvertedStates(const uint8_t state[], const uint16_t length);
|
||||
#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_state[kHitachiAc3StateLength]; ///< The state in native code.
|
||||
void setInvertedStates(const uint16_t length = kHitachiAc3StateLength);
|
||||
};
|
||||
|
||||
/// Class for handling detailed Hitachi 344-bit A/C messages.
|
||||
class IRHitachiAc344: public IRHitachiAc424 {
|
||||
public:
|
||||
explicit IRHitachiAc344(const uint16_t pin, const bool inverted = false,
|
||||
const bool use_modulation = true);
|
||||
void stateReset(void) override;
|
||||
void setRaw(const uint8_t new_code[],
|
||||
const uint16_t length = kHitachiAc344StateLength) override;
|
||||
stdAc::state_t toCommon(void) const override;
|
||||
#if SEND_HITACHI_AC344
|
||||
void send(const uint16_t repeat = kHitachiAcDefaultRepeat) override;
|
||||
#endif // SEND_HITACHI_AC344
|
||||
void setSwingV(const bool on);
|
||||
bool getSwingV(void) const;
|
||||
void setSwingH(const uint8_t position);
|
||||
uint8_t getSwingH(void) const;
|
||||
static uint8_t convertSwingH(const stdAc::swingh_t position);
|
||||
static stdAc::swingh_t toCommonSwingH(const uint8_t pos);
|
||||
String toString(void) const override;
|
||||
};
|
||||
|
||||
/// Class for handling detailed Hitachi 264-bit A/C messages.
|
||||
class IRHitachiAc264: public IRHitachiAc424 {
|
||||
public:
|
||||
explicit IRHitachiAc264(const uint16_t pin, const bool inverted = false,
|
||||
const bool use_modulation = true);
|
||||
void stateReset(void) override;
|
||||
void setRaw(const uint8_t new_code[],
|
||||
const uint16_t length = kHitachiAc264StateLength) override;
|
||||
void setFan(const uint8_t speed) override;
|
||||
uint8_t convertFan(const stdAc::fanspeed_t speed) const override;
|
||||
stdAc::fanspeed_t toCommonFanSpeed(const uint8_t speed) const override;
|
||||
stdAc::state_t toCommon(void) const override;
|
||||
#if SEND_HITACHI_AC264
|
||||
void send(const uint16_t repeat = kHitachiAcDefaultRepeat) override;
|
||||
#endif // SEND_HITACHI_AC264
|
||||
String toString(void) const override;
|
||||
};
|
||||
|
||||
class IRHitachiAc296 {
|
||||
public:
|
||||
explicit IRHitachiAc296(const uint16_t pin, const bool inverted = false,
|
||||
const bool use_modulation = true);
|
||||
void stateReset(void);
|
||||
|
||||
#if SEND_HITACHI_AC296
|
||||
void send(const uint16_t repeat = kHitachiAcDefaultRepeat);
|
||||
#endif // SEND_HITACHI_AC296
|
||||
void begin(void);
|
||||
void on(void);
|
||||
void off(void);
|
||||
void setPower(const bool on);
|
||||
bool getPower(void) const;
|
||||
void setTemp(const uint8_t temp);
|
||||
uint8_t getTemp(void) const;
|
||||
void setFan(const uint8_t speed);
|
||||
uint8_t getFan(void) const;
|
||||
void setMode(const uint8_t mode);
|
||||
uint8_t getMode(void) const;
|
||||
static bool hasInvertedStates(const uint8_t state[], const uint16_t length);
|
||||
uint8_t* getRaw(void);
|
||||
void setRaw(const uint8_t new_code[],
|
||||
const uint16_t length = kHitachiAc296StateLength);
|
||||
static uint8_t convertMode(const stdAc::opmode_t mode);
|
||||
static uint8_t convertFan(const stdAc::fanspeed_t speed);
|
||||
static stdAc::opmode_t toCommonMode(const uint8_t mode);
|
||||
static stdAc::fanspeed_t toCommonFanSpeed(const uint8_t speed);
|
||||
stdAc::state_t toCommon(void) const;
|
||||
String toString(void) const;
|
||||
#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
|
||||
|
||||
HitachiAC296Protocol _;
|
||||
void setInvertedStates(void);
|
||||
};
|
||||
#endif // IR_HITACHI_H_
|
||||
73
yoRadio/src/IRremoteESP8266/ir_Inax.cpp
Normal file
73
yoRadio/src/IRremoteESP8266/ir_Inax.cpp
Normal file
@@ -0,0 +1,73 @@
|
||||
// Copyright 2019 David Conran (crankyoldgit)
|
||||
/// @file
|
||||
/// @brief Support for the Inax Robot Toilet IR protocols.
|
||||
/// @see https://www.lixil-manual.com/GCW-1365-16050/GCW-1365-16050.pdf
|
||||
/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/706
|
||||
|
||||
// Supports:
|
||||
// Brand: Lixil, Model: Inax DT-BA283 Toilet
|
||||
|
||||
#include <algorithm>
|
||||
#include "IRrecv.h"
|
||||
#include "IRsend.h"
|
||||
#include "IRutils.h"
|
||||
|
||||
// Constants
|
||||
const uint16_t kInaxTick = 500;
|
||||
const uint16_t kInaxHdrMark = 9000;
|
||||
const uint16_t kInaxHdrSpace = 4500;
|
||||
const uint16_t kInaxBitMark = 560;
|
||||
const uint16_t kInaxOneSpace = 1675;
|
||||
const uint16_t kInaxZeroSpace = kInaxBitMark;
|
||||
const uint16_t kInaxMinGap = 40000;
|
||||
|
||||
#if SEND_INAX
|
||||
/// Send a Inax Toilet formatted message.
|
||||
/// Status: STABLE / Working.
|
||||
/// @param[in] data The message to be sent.
|
||||
/// @param[in] nbits The number of bits of message to be sent.
|
||||
/// @param[in] repeat The number of times the command is to be repeated.
|
||||
/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/706
|
||||
void IRsend::sendInax(const uint64_t data, const uint16_t nbits,
|
||||
const uint16_t repeat) {
|
||||
sendGeneric(kInaxHdrMark, kInaxHdrSpace,
|
||||
kInaxBitMark, kInaxOneSpace,
|
||||
kInaxBitMark, kInaxZeroSpace,
|
||||
kInaxBitMark, kInaxMinGap,
|
||||
data, nbits, 38, true, repeat, kDutyDefault);
|
||||
}
|
||||
#endif // SEND_INAX
|
||||
|
||||
#if DECODE_INAX
|
||||
/// Decode the supplied Inax Toilet message.
|
||||
/// Status: Stable / Known working.
|
||||
/// @param[in,out] results Ptr to the data to decode & where to store the result
|
||||
/// @param[in] offset The starting index to use when attempting to decode the
|
||||
/// raw data. Typically/Defaults to kStartOffset.
|
||||
/// @param[in] nbits The number of data bits to expect.
|
||||
/// @param[in] strict Flag indicating if we should perform strict matching.
|
||||
/// @return True if it can decode it, false if it can't.
|
||||
/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/706
|
||||
bool IRrecv::decodeInax(decode_results *results, uint16_t offset,
|
||||
const uint16_t nbits, const bool strict) {
|
||||
if (strict && nbits != kInaxBits)
|
||||
return false; // We expect Inax to be a certain sized message.
|
||||
|
||||
uint64_t data = 0;
|
||||
|
||||
// Match Header + Data + Footer
|
||||
if (!matchGeneric(results->rawbuf + offset, &data,
|
||||
results->rawlen - offset, nbits,
|
||||
kInaxHdrMark, kInaxHdrSpace,
|
||||
kInaxBitMark, kInaxOneSpace,
|
||||
kInaxBitMark, kInaxZeroSpace,
|
||||
kInaxBitMark, kInaxMinGap, true)) return false;
|
||||
// Success
|
||||
results->bits = nbits;
|
||||
results->value = data;
|
||||
results->decode_type = decode_type_t::INAX;
|
||||
results->command = 0;
|
||||
results->address = 0;
|
||||
return true;
|
||||
}
|
||||
#endif // DECODE_INAX
|
||||
131
yoRadio/src/IRremoteESP8266/ir_JVC.cpp
Normal file
131
yoRadio/src/IRremoteESP8266/ir_JVC.cpp
Normal file
@@ -0,0 +1,131 @@
|
||||
// Copyright 2015 Kristian Lauszus
|
||||
// Copyright 2017 David Conran
|
||||
|
||||
/// @file
|
||||
/// @brief Support for JVC protocols.
|
||||
/// Originally added by Kristian Lauszus
|
||||
/// Thanks to zenwheel and other people at the original blog post.
|
||||
/// @see http://www.sbprojects.net/knowledge/ir/jvc.php
|
||||
|
||||
// Supports:
|
||||
// Brand: JVC, Model: PTU94023B remote
|
||||
|
||||
#include <algorithm>
|
||||
#include "IRrecv.h"
|
||||
#include "IRsend.h"
|
||||
#include "IRtimer.h"
|
||||
#include "IRutils.h"
|
||||
|
||||
// Constants
|
||||
const uint16_t kJvcTick = 75;
|
||||
const uint16_t kJvcHdrMarkTicks = 112;
|
||||
const uint16_t kJvcHdrMark = kJvcHdrMarkTicks * kJvcTick;
|
||||
const uint16_t kJvcHdrSpaceTicks = 56;
|
||||
const uint16_t kJvcHdrSpace = kJvcHdrSpaceTicks * kJvcTick;
|
||||
const uint16_t kJvcBitMarkTicks = 7;
|
||||
const uint16_t kJvcBitMark = kJvcBitMarkTicks * kJvcTick;
|
||||
const uint16_t kJvcOneSpaceTicks = 23;
|
||||
const uint16_t kJvcOneSpace = kJvcOneSpaceTicks * kJvcTick;
|
||||
const uint16_t kJvcZeroSpaceTicks = 7;
|
||||
const uint16_t kJvcZeroSpace = kJvcZeroSpaceTicks * kJvcTick;
|
||||
const uint16_t kJvcRptLengthTicks = 800;
|
||||
const uint16_t kJvcRptLength = kJvcRptLengthTicks * kJvcTick;
|
||||
const uint16_t kJvcMinGapTicks =
|
||||
kJvcRptLengthTicks -
|
||||
(kJvcHdrMarkTicks + kJvcHdrSpaceTicks +
|
||||
kJvcBits * (kJvcBitMarkTicks + kJvcOneSpaceTicks) + kJvcBitMarkTicks);
|
||||
const uint16_t kJvcMinGap = kJvcMinGapTicks * kJvcTick;
|
||||
|
||||
#if SEND_JVC
|
||||
/// Send a JVC formatted message.
|
||||
/// Status: STABLE / Working.
|
||||
/// @param[in] data The message to be sent.
|
||||
/// @param[in] nbits The number of bits of message to be sent.
|
||||
/// @param[in] repeat The number of times the command is to be repeated.
|
||||
/// @see http://www.sbprojects.net/knowledge/ir/jvc.php
|
||||
void IRsend::sendJVC(uint64_t data, uint16_t nbits, uint16_t repeat) {
|
||||
// Set 38kHz IR carrier frequency & a 1/3 (33%) duty cycle.
|
||||
enableIROut(38, 33);
|
||||
|
||||
IRtimer usecs = IRtimer();
|
||||
// Header
|
||||
// Only sent for the first message.
|
||||
mark(kJvcHdrMark);
|
||||
space(kJvcHdrSpace);
|
||||
|
||||
// We always send the data & footer at least once, hence '<= repeat'.
|
||||
for (uint16_t i = 0; i <= repeat; i++) {
|
||||
sendGeneric(0, 0, // No Header
|
||||
kJvcBitMark, kJvcOneSpace, kJvcBitMark, kJvcZeroSpace,
|
||||
kJvcBitMark, kJvcMinGap, data, nbits, 38, true,
|
||||
0, // Repeats are handles elsewhere.
|
||||
33);
|
||||
// Wait till the end of the repeat time window before we send another code.
|
||||
uint32_t elapsed = usecs.elapsed();
|
||||
// Avoid potential unsigned integer underflow.
|
||||
// e.g. when elapsed > kJvcRptLength.
|
||||
if (elapsed < kJvcRptLength) space(kJvcRptLength - elapsed);
|
||||
usecs.reset();
|
||||
}
|
||||
}
|
||||
|
||||
/// Calculate the raw JVC data based on address and command.
|
||||
/// Status: STABLE / Works fine.
|
||||
/// @param[in] address An 8-bit address value.
|
||||
/// @param[in] command An 8-bit command value.
|
||||
/// @return A raw JVC message code, suitable for sendJVC()..
|
||||
/// @see http://www.sbprojects.net/knowledge/ir/jvc.php
|
||||
uint16_t IRsend::encodeJVC(uint8_t address, uint8_t command) {
|
||||
return reverseBits((command << 8) | address, 16);
|
||||
}
|
||||
#endif // SEND_JVC
|
||||
|
||||
#if DECODE_JVC
|
||||
/// Decode the supplied JVC message.
|
||||
/// Status: Stable / Known working.
|
||||
/// @param[in,out] results Ptr to the data to decode & where to store the result
|
||||
/// @param[in] offset The starting index to use when attempting to decode the
|
||||
/// raw data. Typically/Defaults to kStartOffset.
|
||||
/// @param[in] nbits The number of data bits to expect.
|
||||
/// @param[in] strict Flag indicating if we should perform strict matching.
|
||||
/// @return True if it can decode it, false if it can't.
|
||||
/// @note JVC repeat codes don't have a header.
|
||||
/// @see http://www.sbprojects.net/knowledge/ir/jvc.php
|
||||
bool IRrecv::decodeJVC(decode_results *results, uint16_t offset,
|
||||
const uint16_t nbits, const bool strict) {
|
||||
if (strict && nbits != kJvcBits)
|
||||
return false; // Must be called with the correct nr. of bits.
|
||||
if (results->rawlen <= 2 * nbits + kFooter - 1 + offset)
|
||||
return false; // Can't possibly be a valid JVC message.
|
||||
|
||||
uint64_t data = 0;
|
||||
bool isRepeat = true;
|
||||
|
||||
// Header
|
||||
// (Optional as repeat codes don't have the header)
|
||||
if (matchMark(results->rawbuf[offset], kJvcHdrMark)) {
|
||||
isRepeat = false;
|
||||
offset++;
|
||||
if (results->rawlen < 2 * nbits + 4)
|
||||
return false; // Can't possibly be a valid JVC message with a header.
|
||||
if (!matchSpace(results->rawbuf[offset++], kJvcHdrSpace)) return false;
|
||||
}
|
||||
|
||||
// Data + Footer
|
||||
if (!matchGeneric(results->rawbuf + offset, &data,
|
||||
results->rawlen - offset, nbits,
|
||||
0, 0,
|
||||
kJvcBitMark, kJvcOneSpace,
|
||||
kJvcBitMark, kJvcZeroSpace,
|
||||
kJvcBitMark, kJvcMinGap, true)) return false;
|
||||
// Success
|
||||
results->decode_type = JVC;
|
||||
results->bits = nbits;
|
||||
results->value = data;
|
||||
// command & address are transmitted LSB first, so we need to reverse them.
|
||||
results->address = reverseBits(data >> 8, 8); // The first 8 bits sent.
|
||||
results->command = reverseBits(data & 0xFF, 8); // The last 8 bits sent.
|
||||
results->repeat = isRepeat;
|
||||
return true;
|
||||
}
|
||||
#endif // DECODE_JVC
|
||||
551
yoRadio/src/IRremoteESP8266/ir_Kelon.cpp
Normal file
551
yoRadio/src/IRremoteESP8266/ir_Kelon.cpp
Normal file
@@ -0,0 +1,551 @@
|
||||
// Copyright 2021 Davide Depau
|
||||
// Copyright 2022 David Conran
|
||||
|
||||
/// @file
|
||||
/// @brief Support for Kelon AC protocols.
|
||||
/// Both sending and decoding should be functional for models of series
|
||||
/// KELON ON/OFF 9000-12000.
|
||||
/// All features of the standard remote are implemented.
|
||||
///
|
||||
/// @note Unsupported:
|
||||
/// - Explicit on/off due to AC unit limitations
|
||||
/// - Explicit swing position due to AC unit limitations
|
||||
/// - Fahrenheit.
|
||||
|
||||
#include <algorithm>
|
||||
#include <cassert>
|
||||
|
||||
#include "ir_Kelon.h"
|
||||
|
||||
#include "IRrecv.h"
|
||||
#include "IRsend.h"
|
||||
#include "IRutils.h"
|
||||
#include "IRtext.h"
|
||||
|
||||
|
||||
using irutils::addBoolToString;
|
||||
using irutils::addIntToString;
|
||||
using irutils::addSignedIntToString;
|
||||
using irutils::addModeToString;
|
||||
using irutils::addFanToString;
|
||||
using irutils::addTempToString;
|
||||
using irutils::addLabeledString;
|
||||
using irutils::minsToString;
|
||||
|
||||
// Constants
|
||||
const uint16_t kKelonHdrMark = 9000;
|
||||
const uint16_t kKelonHdrSpace = 4600;
|
||||
const uint16_t kKelonBitMark = 560;
|
||||
const uint16_t kKelonOneSpace = 1680;
|
||||
const uint16_t kKelonZeroSpace = 600;
|
||||
const uint32_t kKelonGap = 2 * kDefaultMessageGap;
|
||||
const uint16_t kKelonFreq = 38000;
|
||||
|
||||
const uint32_t kKelon168FooterSpace = 8000;
|
||||
const uint16_t kKelon168Section1Size = 6;
|
||||
const uint16_t kKelon168Section2Size = 8;
|
||||
const uint16_t kKelon168Section3Size = 7;
|
||||
|
||||
#if SEND_KELON
|
||||
/// Send a Kelon 48-bit message.
|
||||
/// Status: STABLE / Working.
|
||||
/// @param[in] data The data to be transmitted.
|
||||
/// @param[in] nbits Nr. of bits of data to be sent.
|
||||
/// @param[in] repeat The number of times the command is to be repeated.
|
||||
void IRsend::sendKelon(const uint64_t data, const uint16_t nbits,
|
||||
const uint16_t repeat) {
|
||||
sendGeneric(kKelonHdrMark, kKelonHdrSpace,
|
||||
kKelonBitMark, kKelonOneSpace,
|
||||
kKelonBitMark, kKelonZeroSpace,
|
||||
kKelonBitMark, kKelonGap,
|
||||
data, nbits, kKelonFreq, false, // LSB First.
|
||||
repeat, kDutyDefault);
|
||||
}
|
||||
#endif // SEND_KELON
|
||||
|
||||
#if DECODE_KELON
|
||||
/// Decode the supplied Kelon 48-bit message.
|
||||
/// Status: STABLE / Working.
|
||||
/// @param[in,out] results Ptr to the data to decode & where to store the result
|
||||
/// @param[in] offset The starting index to use when attempting to decode the
|
||||
/// raw data. Typically/Defaults to kStartOffset.
|
||||
/// @param[in] nbits The number of data bits to expect.
|
||||
/// @param[in] strict Flag indicating if we should perform strict matching.
|
||||
/// @return True if it can decode it, false if it can't.
|
||||
bool IRrecv::decodeKelon(decode_results *results, uint16_t offset,
|
||||
const uint16_t nbits, const bool strict) {
|
||||
if (strict && nbits != kKelonBits) return false;
|
||||
|
||||
if (!matchGeneric(results->rawbuf + offset, &(results->value),
|
||||
results->rawlen - offset, nbits,
|
||||
kKelonHdrMark, kKelonHdrSpace,
|
||||
kKelonBitMark, kKelonOneSpace,
|
||||
kKelonBitMark, kKelonZeroSpace,
|
||||
kKelonBitMark, kKelonGap, true,
|
||||
_tolerance, 0, false)) return false;
|
||||
|
||||
results->decode_type = decode_type_t::KELON;
|
||||
results->address = 0;
|
||||
results->command = 0;
|
||||
results->bits = nbits;
|
||||
return true;
|
||||
}
|
||||
#endif // DECODE_KELON
|
||||
|
||||
/// Class constructor
|
||||
/// @param[in] pin GPIO to be used when sending.
|
||||
/// @param[in] inverted Is the output signal to be inverted?
|
||||
/// @param[in] use_modulation Is frequency modulation to be used?
|
||||
IRKelonAc::IRKelonAc(const uint16_t pin, const bool inverted,
|
||||
const bool use_modulation)
|
||||
: _irsend{pin, inverted, use_modulation}, _{} { stateReset(); }
|
||||
|
||||
/// Reset the internals of the object to a known good state.
|
||||
void IRKelonAc::stateReset() {
|
||||
_.raw = 0L;
|
||||
_.preamble[0] = 0b10000011;
|
||||
_.preamble[1] = 0b00000110;
|
||||
}
|
||||
|
||||
#if SEND_KELON
|
||||
|
||||
/// Send the current internal state as an IR message.
|
||||
/// @param[in] repeat Nr. of times the message will be repeated.
|
||||
void IRKelonAc::send(const uint16_t repeat) {
|
||||
_irsend.sendKelon(getRaw(), kKelonBits, repeat);
|
||||
|
||||
// Reset toggle flags
|
||||
_.PowerToggle = false;
|
||||
_.SwingVToggle = false;
|
||||
|
||||
// Remove the timer time setting
|
||||
_.TimerHours = 0;
|
||||
_.TimerHalfHour = 0;
|
||||
}
|
||||
|
||||
/// Ensures the AC is on or off by exploiting the fact that setting
|
||||
/// it to "smart" will always turn it on if it's off.
|
||||
/// This method will send 2 commands to the AC to do the trick
|
||||
/// @param[in] on Whether to ensure the AC is on or off
|
||||
void IRKelonAc::ensurePower(bool on) {
|
||||
// Try to avoid turning on the compressor for this operation.
|
||||
// "Dry grade", when in "smart" mode, acts as a temperature offset that
|
||||
// the user can configure if they feel too cold or too hot. By setting it
|
||||
// to +2 we're setting the temperature to ~28°C, which will effectively
|
||||
// set the AC to fan mode.
|
||||
int8_t previousDry = getDryGrade();
|
||||
setDryGrade(2);
|
||||
setMode(kKelonModeSmart);
|
||||
send();
|
||||
|
||||
setDryGrade(previousDry);
|
||||
setMode(_previousMode);
|
||||
send();
|
||||
|
||||
// Now we're sure it's on. Turn it back off. The AC seems to turn back on if
|
||||
// we don't send this separately
|
||||
if (!on) {
|
||||
setTogglePower(true);
|
||||
send();
|
||||
}
|
||||
}
|
||||
|
||||
#endif // SEND_KELON
|
||||
|
||||
/// Set up hardware to be able to send a message.
|
||||
void IRKelonAc::begin() { _irsend.begin(); }
|
||||
|
||||
/// Request toggling power - will be reset to false after sending
|
||||
/// @param[in] toggle Whether to toggle the power state
|
||||
void IRKelonAc::setTogglePower(const bool toggle) { _.PowerToggle = toggle; }
|
||||
|
||||
/// Get whether toggling power will be requested
|
||||
/// @return The power toggle state
|
||||
bool IRKelonAc::getTogglePower() const { return _.PowerToggle; }
|
||||
|
||||
/// Set the temperature setting.
|
||||
/// @param[in] degrees The temperature in degrees celsius.
|
||||
void IRKelonAc::setTemp(const uint8_t degrees) {
|
||||
uint8_t temp = std::max(kKelonMinTemp, degrees);
|
||||
temp = std::min(kKelonMaxTemp, temp);
|
||||
_previousTemp = _.Temperature;
|
||||
_.Temperature = temp - kKelonMinTemp;
|
||||
}
|
||||
|
||||
/// Get the current temperature setting.
|
||||
/// @return Get current setting for temp. in degrees celsius.
|
||||
uint8_t IRKelonAc::getTemp() const { return _.Temperature + kKelonMinTemp; }
|
||||
|
||||
/// Set the speed of the fan.
|
||||
/// @param[in] speed 0 is auto, 1-5 is the speed
|
||||
void IRKelonAc::setFan(const uint8_t speed) {
|
||||
uint8_t fan = std::min(speed, kKelonFanMax);
|
||||
|
||||
_previousFan = _.Fan;
|
||||
// Note: Kelon fan speeds are backwards! This code maps the range 0,1:3 to
|
||||
// 0,3:1 to save the API's user's sanity.
|
||||
_.Fan = ((static_cast<int16_t>(fan) - 4) * -1) % 4;
|
||||
}
|
||||
|
||||
/// Get the current fan speed setting.
|
||||
/// @return The current fan speed.
|
||||
uint8_t IRKelonAc::getFan() const {
|
||||
return ((static_cast<int16_t>(_.Fan) - 4) * -1) % 4;;
|
||||
}
|
||||
|
||||
/// Set the dehumidification intensity.
|
||||
/// @param[in] grade has to be in the range [-2 : +2]
|
||||
void IRKelonAc::setDryGrade(const int8_t grade) {
|
||||
int8_t drygrade = std::max(kKelonDryGradeMin, grade);
|
||||
drygrade = std::min(kKelonDryGradeMax, drygrade);
|
||||
|
||||
// Two's complement is clearly too bleeding edge for this manufacturer
|
||||
uint8_t outval;
|
||||
if (drygrade < 0)
|
||||
outval = 0b100 | (-drygrade & 0b011);
|
||||
else
|
||||
outval = drygrade & 0b011;
|
||||
_.DehumidifierGrade = outval;
|
||||
}
|
||||
|
||||
/// Get the current dehumidification intensity setting. In smart mode, this
|
||||
/// controls the temperature adjustment.
|
||||
/// @return The current dehumidification intensity.
|
||||
int8_t IRKelonAc::getDryGrade() const {
|
||||
return static_cast<int8_t>(_.DehumidifierGrade & 0b011) *
|
||||
((_.DehumidifierGrade & 0b100) ? -1 : 1);
|
||||
}
|
||||
|
||||
/// Set the desired operation mode.
|
||||
/// @param[in] mode The desired operation mode.
|
||||
void IRKelonAc::setMode(const uint8_t mode) {
|
||||
if (_.Mode == kKelonModeSmart || _.Mode == kKelonModeFan ||
|
||||
_.Mode == kKelonModeDry) {
|
||||
_.Temperature = _previousTemp;
|
||||
}
|
||||
if (_.SuperCoolEnabled1) {
|
||||
// Cancel supercool
|
||||
_.SuperCoolEnabled1 = false;
|
||||
_.SuperCoolEnabled2 = false;
|
||||
_.Temperature = _previousTemp;
|
||||
_.Fan = _previousFan;
|
||||
}
|
||||
_previousMode = _.Mode;
|
||||
|
||||
switch (mode) {
|
||||
case kKelonModeSmart:
|
||||
setTemp(26);
|
||||
_.SmartModeEnabled = true;
|
||||
_.Mode = mode;
|
||||
break;
|
||||
case kKelonModeDry:
|
||||
case kKelonModeFan:
|
||||
setTemp(25);
|
||||
// fallthrough
|
||||
case kKelonModeCool:
|
||||
case kKelonModeHeat:
|
||||
_.Mode = mode;
|
||||
// fallthrough
|
||||
default:
|
||||
_.SmartModeEnabled = false;
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the current operation mode setting.
|
||||
/// @return The current operation mode.
|
||||
uint8_t IRKelonAc::getMode() const { return _.Mode; }
|
||||
|
||||
/// Request toggling the vertical swing - will be reset to false after sending
|
||||
/// @param[in] toggle If true, the swing mode will be toggled when sent.
|
||||
void IRKelonAc::setToggleSwingVertical(const bool toggle) {
|
||||
_.SwingVToggle = toggle;
|
||||
}
|
||||
|
||||
/// Get whether the swing mode is set to be toggled
|
||||
/// @return Whether the toggle bit is set
|
||||
bool IRKelonAc::getToggleSwingVertical() const { return _.SwingVToggle; }
|
||||
|
||||
/// Control the current sleep (quiet) setting.
|
||||
/// @param[in] on The desired setting.
|
||||
void IRKelonAc::setSleep(const bool on) { _.SleepEnabled = on; }
|
||||
|
||||
/// Is the sleep setting on?
|
||||
/// @return The current value.
|
||||
bool IRKelonAc::getSleep() const { return _.SleepEnabled; }
|
||||
|
||||
/// Control the current super cool mode setting.
|
||||
/// @param[in] on The desired setting.
|
||||
void IRKelonAc::setSupercool(const bool on) {
|
||||
if (on) {
|
||||
setTemp(kKelonMinTemp);
|
||||
setMode(kKelonModeCool);
|
||||
setFan(kKelonFanMax);
|
||||
} else {
|
||||
// All reverts to previous are handled by setMode as needed
|
||||
setMode(_previousMode);
|
||||
}
|
||||
_.SuperCoolEnabled1 = on;
|
||||
_.SuperCoolEnabled2 = on;
|
||||
}
|
||||
|
||||
/// Is the super cool mode setting on?
|
||||
/// @return The current value.
|
||||
bool IRKelonAc::getSupercool() const { return _.SuperCoolEnabled1; }
|
||||
|
||||
/// Set the timer time and enable it. Timer is an off timer if the unit is on,
|
||||
/// it is an on timer if the unit is off.
|
||||
/// Only multiples of 30m are supported for < 10h, then only multiples of 60m
|
||||
/// @param[in] mins Nr. of minutes
|
||||
void IRKelonAc::setTimer(uint16_t mins) {
|
||||
const uint16_t minutes = std::min(static_cast<int>(mins), 24 * 60);
|
||||
|
||||
if (minutes / 60 >= 10) {
|
||||
uint8_t hours = minutes / 60 + 10;
|
||||
_.TimerHalfHour = hours & 1;
|
||||
_.TimerHours = hours >> 1;
|
||||
} else {
|
||||
_.TimerHalfHour = (minutes % 60) >= 30 ? 1 : 0;
|
||||
_.TimerHours = minutes / 60;
|
||||
}
|
||||
|
||||
setTimerEnabled(true);
|
||||
}
|
||||
|
||||
/// Get the set timer. Timer set time is deleted once the command is sent, so
|
||||
/// calling this after send() will return 0.
|
||||
/// The AC unit will continue keeping track of the remaining time unless it is
|
||||
/// later disabled.
|
||||
/// @return The timer set minutes
|
||||
uint16_t IRKelonAc::getTimer() const {
|
||||
if (_.TimerHours >= 10)
|
||||
return ((uint16_t) ((_.TimerHours << 1) | _.TimerHalfHour) - 10) * 60;
|
||||
return (((uint16_t) _.TimerHours) * 60) + (_.TimerHalfHour ? 30 : 0);
|
||||
}
|
||||
|
||||
/// Enable or disable the timer. Note that in order to enable the timer the
|
||||
/// minutes must be set with setTimer().
|
||||
/// @param[in] on Whether to enable or disable the timer
|
||||
void IRKelonAc::setTimerEnabled(bool on) { _.TimerEnabled = on; }
|
||||
|
||||
/// Get the current timer status
|
||||
/// @return Whether the timer is enabled.
|
||||
bool IRKelonAc::getTimerEnabled() const { return _.TimerEnabled; }
|
||||
|
||||
/// Get the raw state of the object, suitable to be sent with the appropriate
|
||||
/// IRsend object method.
|
||||
/// @return A PTR to the internal state.
|
||||
uint64_t IRKelonAc::getRaw() const { return _.raw; }
|
||||
|
||||
/// Set the raw state of the object.
|
||||
/// @param[in] new_code The raw state from the native IR message.
|
||||
void IRKelonAc::setRaw(const uint64_t new_code) { _.raw = new_code; }
|
||||
|
||||
/// Convert a standard A/C mode (stdAc::opmode_t) into it a native mode.
|
||||
/// @param[in] mode A stdAc::opmode_t operation mode.
|
||||
/// @return The native mode equivalent.
|
||||
uint8_t IRKelonAc::convertMode(const stdAc::opmode_t mode) {
|
||||
switch (mode) {
|
||||
case stdAc::opmode_t::kCool: return kKelonModeCool;
|
||||
case stdAc::opmode_t::kHeat: return kKelonModeHeat;
|
||||
case stdAc::opmode_t::kDry: return kKelonModeDry;
|
||||
case stdAc::opmode_t::kFan: return kKelonModeFan;
|
||||
default: return kKelonModeSmart; // aka Auto.
|
||||
}
|
||||
}
|
||||
|
||||
/// Convert a standard A/C fan speed (stdAc::fanspeed_t) into it a native speed.
|
||||
/// @param[in] fan A stdAc::fanspeed_t fan speed
|
||||
/// @return The native speed equivalent.
|
||||
uint8_t IRKelonAc::convertFan(stdAc::fanspeed_t fan) {
|
||||
switch (fan) {
|
||||
case stdAc::fanspeed_t::kMin:
|
||||
case stdAc::fanspeed_t::kLow: return kKelonFanMin;
|
||||
case stdAc::fanspeed_t::kMedium: return kKelonFanMedium;
|
||||
case stdAc::fanspeed_t::kHigh:
|
||||
case stdAc::fanspeed_t::kMax: return kKelonFanMax;
|
||||
default: return kKelonFanAuto;
|
||||
}
|
||||
}
|
||||
|
||||
/// Convert a native mode to it's stdAc::opmode_t equivalent.
|
||||
/// @param[in] mode A native operating mode value.
|
||||
/// @return The stdAc::opmode_t equivalent.
|
||||
stdAc::opmode_t IRKelonAc::toCommonMode(const uint8_t mode) {
|
||||
switch (mode) {
|
||||
case kKelonModeCool: return stdAc::opmode_t::kCool;
|
||||
case kKelonModeHeat: return stdAc::opmode_t::kHeat;
|
||||
case kKelonModeDry: return stdAc::opmode_t::kDry;
|
||||
case kKelonModeFan: return stdAc::opmode_t::kFan;
|
||||
default: return stdAc::opmode_t::kAuto;
|
||||
}
|
||||
}
|
||||
|
||||
/// Convert a native fan speed to it's stdAc::fanspeed_t equivalent.
|
||||
/// @param[in] speed A native fan speed value.
|
||||
/// @return The stdAc::fanspeed_t equivalent.
|
||||
stdAc::fanspeed_t IRKelonAc::toCommonFanSpeed(const uint8_t speed) {
|
||||
switch (speed) {
|
||||
case kKelonFanMin: return stdAc::fanspeed_t::kLow;
|
||||
case kKelonFanMedium: return stdAc::fanspeed_t::kMedium;
|
||||
case kKelonFanMax: return stdAc::fanspeed_t::kHigh;
|
||||
default: return stdAc::fanspeed_t::kAuto;
|
||||
}
|
||||
}
|
||||
|
||||
/// Convert the internal A/C object state to it's stdAc::state_t equivalent.
|
||||
/// @return A stdAc::state_t containing the current settings.
|
||||
stdAc::state_t IRKelonAc::toCommon(const stdAc::state_t *prev) const {
|
||||
stdAc::state_t result{};
|
||||
result.protocol = decode_type_t::KELON;
|
||||
result.model = -1; // Unused.
|
||||
// AC only supports toggling it
|
||||
result.power = (prev == nullptr || prev->power) ^ _.PowerToggle;
|
||||
result.mode = toCommonMode(getMode());
|
||||
result.celsius = true;
|
||||
result.degrees = getTemp();
|
||||
result.fanspeed = toCommonFanSpeed(getFan());
|
||||
// AC only supports toggling it
|
||||
result.swingv = stdAc::swingv_t::kAuto;
|
||||
if (prev != nullptr &&
|
||||
(prev->swingv != stdAc::swingv_t::kAuto) ^ _.SwingVToggle)
|
||||
result.swingv = stdAc::swingv_t::kOff;
|
||||
result.turbo = getSupercool();
|
||||
result.sleep = getSleep() ? 0 : -1;
|
||||
// Not supported.
|
||||
result.swingh = stdAc::swingh_t::kOff;
|
||||
result.light = true;
|
||||
result.beep = true;
|
||||
result.quiet = false;
|
||||
result.filter = false;
|
||||
result.clean = false;
|
||||
result.econo = false;
|
||||
result.clock = -1;
|
||||
return result;
|
||||
}
|
||||
|
||||
/// Convert the internal settings into a human readable string.
|
||||
/// @return A String.
|
||||
String IRKelonAc::toString() const {
|
||||
String result = "";
|
||||
// Reserve some heap for the string to reduce fragging.
|
||||
result.reserve(160);
|
||||
result += addTempToString(getTemp(), true, false);
|
||||
result += addModeToString(_.Mode, kKelonModeSmart, kKelonModeCool,
|
||||
kKelonModeHeat, kKelonModeDry, kKelonModeFan);
|
||||
result += addFanToString(_.Fan, kKelonFanMax, kKelonFanMin, kKelonFanAuto,
|
||||
-1, kKelonFanMedium, kKelonFanMax);
|
||||
result += addBoolToString(_.SleepEnabled, kSleepStr);
|
||||
result += addSignedIntToString(getDryGrade(), kDryStr);
|
||||
result += addLabeledString(
|
||||
getTimerEnabled() ? (getTimer() > 0 ? minsToString(getTimer()) : kOnStr)
|
||||
: kOffStr,
|
||||
kTimerStr);
|
||||
result += addBoolToString(getSupercool(), kTurboStr);
|
||||
if (getTogglePower())
|
||||
result += addBoolToString(true, kPowerToggleStr);
|
||||
if (getToggleSwingVertical())
|
||||
result += addBoolToString(true, kSwingVToggleStr);
|
||||
return result;
|
||||
}
|
||||
|
||||
#if SEND_KELON168
|
||||
/// Send a Kelon 168 bit / 21 byte message.
|
||||
/// Status: BETA / Probably works.
|
||||
/// @param[in] data The data to be transmitted.
|
||||
/// @param[in] nbytes Nr. of bytes of data to be sent.
|
||||
/// @param[in] repeat The number of times the command is to be repeated.
|
||||
void IRsend::sendKelon168(const uint8_t data[], const uint16_t nbytes,
|
||||
const uint16_t repeat) {
|
||||
assert(kKelon168StateLength == kKelon168Section1Size + kKelon168Section2Size +
|
||||
kKelon168Section3Size);
|
||||
// Enough bytes to send a proper message?
|
||||
if (nbytes < kKelon168StateLength) return;
|
||||
|
||||
for (uint16_t r = 0; r <= repeat; r++) {
|
||||
// Section #1 (48 bits)
|
||||
sendGeneric(kKelonHdrMark, kKelonHdrSpace,
|
||||
kKelonBitMark, kKelonOneSpace,
|
||||
kKelonBitMark, kKelonZeroSpace,
|
||||
kKelonBitMark, kKelon168FooterSpace,
|
||||
data, kKelon168Section1Size, kKelonFreq, false, // LSB First.
|
||||
0, // No repeats here
|
||||
kDutyDefault);
|
||||
// Section #2 (64 bits)
|
||||
sendGeneric(0, 0,
|
||||
kKelonBitMark, kKelonOneSpace,
|
||||
kKelonBitMark, kKelonZeroSpace,
|
||||
kKelonBitMark, kKelon168FooterSpace,
|
||||
data + kKelon168Section1Size, kKelon168Section2Size,
|
||||
kKelonFreq, false, // LSB First.
|
||||
0, // No repeats here
|
||||
kDutyDefault);
|
||||
// Section #3 (56 bits)
|
||||
sendGeneric(0, 0,
|
||||
kKelonBitMark, kKelonOneSpace,
|
||||
kKelonBitMark, kKelonZeroSpace,
|
||||
kKelonBitMark, kKelonGap,
|
||||
data + kKelon168Section1Size + kKelon168Section2Size,
|
||||
nbytes - (kKelon168Section1Size + kKelon168Section2Size),
|
||||
kKelonFreq, false, // LSB First.
|
||||
0, // No repeats here
|
||||
kDutyDefault);
|
||||
}
|
||||
}
|
||||
#endif // SEND_KELON168
|
||||
|
||||
#if DECODE_KELON168
|
||||
/// Decode the supplied Kelon 168 bit / 21 byte message.
|
||||
/// Status: BETA / Probably Working.
|
||||
/// @param[in,out] results Ptr to the data to decode & where to store the result
|
||||
/// @param[in] offset The starting index to use when attempting to decode the
|
||||
/// raw data. Typically/Defaults to kStartOffset.
|
||||
/// @param[in] nbits The number of data bits to expect.
|
||||
/// @param[in] strict Flag indicating if we should perform strict matching.
|
||||
/// @return True if it can decode it, false if it can't.
|
||||
bool IRrecv::decodeKelon168(decode_results *results, uint16_t offset,
|
||||
const uint16_t nbits, const bool strict) {
|
||||
if (strict && nbits != kKelon168Bits) return false;
|
||||
if (results->rawlen <= 2 * nbits + kHeader + kFooter * 2 - 1 + offset)
|
||||
return false; // Can't possibly be a valid Kelon 168 bit message.
|
||||
|
||||
uint16_t used = 0;
|
||||
|
||||
used = matchGeneric(results->rawbuf + offset, results->state,
|
||||
results->rawlen - offset, kKelon168Section1Size * 8,
|
||||
kKelonHdrMark, kKelonHdrSpace,
|
||||
kKelonBitMark, kKelonOneSpace,
|
||||
kKelonBitMark, kKelonZeroSpace,
|
||||
kKelonBitMark, kKelon168FooterSpace,
|
||||
false, _tolerance, 0, false);
|
||||
if (!used) return false; // Failed to match.
|
||||
offset += used;
|
||||
|
||||
used = matchGeneric(results->rawbuf + offset,
|
||||
results->state + kKelon168Section1Size,
|
||||
results->rawlen - offset, kKelon168Section2Size * 8,
|
||||
0, 0,
|
||||
kKelonBitMark, kKelonOneSpace,
|
||||
kKelonBitMark, kKelonZeroSpace,
|
||||
kKelonBitMark, kKelon168FooterSpace,
|
||||
false, _tolerance, 0, false);
|
||||
if (!used) return false; // Failed to match.
|
||||
offset += used;
|
||||
|
||||
used = matchGeneric(results->rawbuf + offset,
|
||||
results->state + (kKelon168Section1Size +
|
||||
kKelon168Section2Size),
|
||||
results->rawlen - offset,
|
||||
nbits - (kKelon168Section1Size +
|
||||
kKelon168Section2Size) * 8,
|
||||
0, 0,
|
||||
kKelonBitMark, kKelonOneSpace,
|
||||
kKelonBitMark, kKelonZeroSpace,
|
||||
kKelonBitMark, kKelonGap,
|
||||
true, _tolerance, 0, false);
|
||||
if (!used) return false; // Failed to match.
|
||||
|
||||
results->decode_type = decode_type_t::KELON168;
|
||||
results->bits = nbits;
|
||||
return true;
|
||||
}
|
||||
#endif // DECODE_KELON168
|
||||
142
yoRadio/src/IRremoteESP8266/ir_Kelon.h
Normal file
142
yoRadio/src/IRremoteESP8266/ir_Kelon.h
Normal file
@@ -0,0 +1,142 @@
|
||||
// Copyright 2021 Davide Depau
|
||||
|
||||
/// @file
|
||||
/// @brief Support for Kelan AC protocol.
|
||||
/// @note Both sending and decoding should be functional for models of series
|
||||
/// KELON ON/OFF 9000-12000.
|
||||
/// All features of the standard remote are implemented.
|
||||
///
|
||||
/// @note Unsupported:
|
||||
/// - Explicit on/off due to AC unit limitations
|
||||
/// - Explicit swing position due to AC unit limitations
|
||||
/// - Fahrenheit.
|
||||
///
|
||||
/// For KELON168:
|
||||
/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1745
|
||||
|
||||
// Supports:
|
||||
// Brand: Kelon, Model: ON/OFF 9000-12000 (KELON)
|
||||
// Brand: Kelon, Model: DG11R2-01 remote (KELON168)
|
||||
// Brand: Kelon, Model: AST-09UW4RVETG00A A/C (KELON168)
|
||||
// Brand: Hisense, Model: AST-09UW4RVETG00A A/C (KELON168)
|
||||
|
||||
#ifndef IR_KELON_H_
|
||||
#define IR_KELON_H_
|
||||
|
||||
#ifdef UNIT_TEST
|
||||
#include "IRsend_test.h"
|
||||
#endif
|
||||
|
||||
#include "IRremoteESP8266.h"
|
||||
#include "IRsend.h"
|
||||
#include "IRutils.h"
|
||||
|
||||
union KelonProtocol {
|
||||
uint64_t raw;
|
||||
|
||||
struct {
|
||||
uint8_t preamble[2];
|
||||
uint8_t Fan: 2;
|
||||
uint8_t PowerToggle: 1;
|
||||
uint8_t SleepEnabled: 1;
|
||||
uint8_t DehumidifierGrade: 3;
|
||||
uint8_t SwingVToggle: 1;
|
||||
uint8_t Mode: 3;
|
||||
uint8_t TimerEnabled: 1;
|
||||
uint8_t Temperature: 4;
|
||||
uint8_t TimerHalfHour: 1;
|
||||
uint8_t TimerHours: 6;
|
||||
uint8_t SmartModeEnabled: 1;
|
||||
uint8_t pad1: 4;
|
||||
uint8_t SuperCoolEnabled1: 1;
|
||||
uint8_t pad2: 2;
|
||||
uint8_t SuperCoolEnabled2: 1;
|
||||
};
|
||||
};
|
||||
|
||||
// Constants
|
||||
const uint8_t kKelonModeHeat = 0;
|
||||
const uint8_t kKelonModeSmart = 1; // (temp = 26C, but not shown)
|
||||
const uint8_t kKelonModeCool = 2;
|
||||
const uint8_t kKelonModeDry = 3; // (temp = 25C, but not shown)
|
||||
const uint8_t kKelonModeFan = 4; // (temp = 25C, but not shown)
|
||||
const uint8_t kKelonFanAuto = 0;
|
||||
// Note! Kelon fan speeds are actually 0:AUTO, 1:MAX, 2:MED, 3:MIN
|
||||
// Since this is insane, I decided to invert them in the public API, they are
|
||||
// converted back in setFan/getFan
|
||||
const uint8_t kKelonFanMin = 1;
|
||||
const uint8_t kKelonFanMedium = 2;
|
||||
const uint8_t kKelonFanMax = 3;
|
||||
|
||||
const int8_t kKelonDryGradeMin = -2;
|
||||
const int8_t kKelonDryGradeMax = +2;
|
||||
const uint8_t kKelonMinTemp = 18;
|
||||
const uint8_t kKelonMaxTemp = 32;
|
||||
|
||||
|
||||
class IRKelonAc {
|
||||
public:
|
||||
explicit IRKelonAc(uint16_t pin, bool inverted = false,
|
||||
bool use_modulation = true);
|
||||
void stateReset(void);
|
||||
#if SEND_KELON
|
||||
void send(const uint16_t repeat = kNoRepeat);
|
||||
/// 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(); }
|
||||
/// Since the AC does not support actually setting the power state to a known
|
||||
/// value, this utility allow ensuring the AC is on or off by exploiting
|
||||
/// the fact that the AC, according to the user manual, will always turn on
|
||||
/// when setting it to "smart" or "super" mode.
|
||||
void ensurePower(const bool on);
|
||||
#endif // SEND_KELON
|
||||
|
||||
|
||||
void begin(void);
|
||||
void setTogglePower(const bool toggle);
|
||||
bool getTogglePower(void) const;
|
||||
void setTemp(const uint8_t degrees);
|
||||
uint8_t getTemp(void) const;
|
||||
void setFan(const uint8_t speed);
|
||||
uint8_t getFan(void) const;
|
||||
void setDryGrade(const int8_t grade);
|
||||
int8_t getDryGrade(void) const;
|
||||
void setMode(const uint8_t mode);
|
||||
uint8_t getMode(void) const;
|
||||
void setToggleSwingVertical(const bool toggle);
|
||||
bool getToggleSwingVertical(void) const;
|
||||
void setSleep(const bool on);
|
||||
bool getSleep(void) const;
|
||||
void setSupercool(const bool on);
|
||||
bool getSupercool(void) const;
|
||||
void setTimer(const uint16_t mins);
|
||||
uint16_t getTimer(void) const;
|
||||
void setTimerEnabled(const bool on);
|
||||
bool getTimerEnabled(void) const;
|
||||
uint64_t getRaw(void) const;
|
||||
void setRaw(const uint64_t new_code);
|
||||
static uint8_t convertMode(const stdAc::opmode_t mode);
|
||||
static uint8_t convertFan(const stdAc::fanspeed_t fan);
|
||||
static stdAc::opmode_t toCommonMode(const uint8_t mode);
|
||||
static stdAc::fanspeed_t toCommonFanSpeed(const uint8_t speed);
|
||||
stdAc::state_t toCommon(const stdAc::state_t *prev = nullptr) const;
|
||||
String toString(void) const;
|
||||
|
||||
private:
|
||||
#ifndef UNIT_TEST
|
||||
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
|
||||
KelonProtocol _;
|
||||
|
||||
// Used when exiting supercool mode
|
||||
uint8_t _previousMode = 0;
|
||||
uint8_t _previousTemp = kKelonMinTemp;
|
||||
uint8_t _previousFan = kKelonFanAuto;
|
||||
};
|
||||
#endif // IR_KELON_H_
|
||||
581
yoRadio/src/IRremoteESP8266/ir_Kelvinator.cpp
Normal file
581
yoRadio/src/IRremoteESP8266/ir_Kelvinator.cpp
Normal file
@@ -0,0 +1,581 @@
|
||||
// Copyright 2016 David Conran
|
||||
/// @file
|
||||
/// @brief Support for Kelvinator A/C protocols.
|
||||
/// Code to emulate IR Kelvinator YALIF remote control unit, which should
|
||||
/// control at least the following Kelvinator A/C units:
|
||||
/// KSV26CRC, KSV26HRC, KSV35CRC, KSV35HRC, KSV53HRC, KSV62HRC, KSV70CRC,
|
||||
/// KSV70HRC, KSV80HRC.
|
||||
///
|
||||
/// @note Unsupported:
|
||||
/// - All Sleep modes.
|
||||
/// - All Timer modes.
|
||||
/// - "I Feel" button & mode.
|
||||
/// - Energy Saving mode.
|
||||
/// - Low Heat mode.
|
||||
/// - Fahrenheit.
|
||||
|
||||
#include "ir_Kelvinator.h"
|
||||
#include <algorithm>
|
||||
#include <cstring>
|
||||
#ifndef ARDUINO
|
||||
#include <string>
|
||||
#endif
|
||||
#include "IRac.h"
|
||||
#include "IRrecv.h"
|
||||
#include "IRsend.h"
|
||||
#include "IRtext.h"
|
||||
#include "IRutils.h"
|
||||
|
||||
// Constants
|
||||
const uint16_t kKelvinatorTick = 85;
|
||||
const uint16_t kKelvinatorHdrMarkTicks = 106;
|
||||
const uint16_t kKelvinatorHdrMark = kKelvinatorHdrMarkTicks * kKelvinatorTick;
|
||||
const uint16_t kKelvinatorHdrSpaceTicks = 53;
|
||||
const uint16_t kKelvinatorHdrSpace = kKelvinatorHdrSpaceTicks * kKelvinatorTick;
|
||||
const uint16_t kKelvinatorBitMarkTicks = 8;
|
||||
const uint16_t kKelvinatorBitMark = kKelvinatorBitMarkTicks * kKelvinatorTick;
|
||||
const uint16_t kKelvinatorOneSpaceTicks = 18;
|
||||
const uint16_t kKelvinatorOneSpace = kKelvinatorOneSpaceTicks * kKelvinatorTick;
|
||||
const uint16_t kKelvinatorZeroSpaceTicks = 6;
|
||||
const uint16_t kKelvinatorZeroSpace =
|
||||
kKelvinatorZeroSpaceTicks * kKelvinatorTick;
|
||||
const uint16_t kKelvinatorGapSpaceTicks = 235;
|
||||
const uint16_t kKelvinatorGapSpace = kKelvinatorGapSpaceTicks * kKelvinatorTick;
|
||||
|
||||
const uint8_t kKelvinatorCmdFooter = 2;
|
||||
const uint8_t kKelvinatorCmdFooterBits = 3;
|
||||
|
||||
const uint8_t kKelvinatorChecksumStart = 10;
|
||||
|
||||
using irutils::addBoolToString;
|
||||
using irutils::addIntToString;
|
||||
using irutils::addLabeledString;
|
||||
using irutils::addModeToString;
|
||||
using irutils::addFanToString;
|
||||
using irutils::addTempToString;
|
||||
using irutils::addSwingVToString;
|
||||
|
||||
#if SEND_KELVINATOR
|
||||
/// Send a Kelvinator A/C message.
|
||||
/// Status: STABLE / Known working.
|
||||
/// @param[in] data The message to be sent.
|
||||
/// @param[in] nbytes The number of bytes of message to be sent.
|
||||
/// @param[in] repeat The number of times the command is to be repeated.
|
||||
void IRsend::sendKelvinator(const unsigned char data[], const uint16_t nbytes,
|
||||
const uint16_t repeat) {
|
||||
if (nbytes < kKelvinatorStateLength)
|
||||
return; // Not enough bytes to send a proper message.
|
||||
|
||||
for (uint16_t r = 0; r <= repeat; r++) {
|
||||
// Command Block #1 (4 bytes)
|
||||
sendGeneric(kKelvinatorHdrMark, kKelvinatorHdrSpace, kKelvinatorBitMark,
|
||||
kKelvinatorOneSpace, kKelvinatorBitMark, kKelvinatorZeroSpace,
|
||||
0, 0, // No Footer yet.
|
||||
data, 4, 38, false, 0, 50);
|
||||
// Send Footer for the command block (3 bits (b010))
|
||||
sendGeneric(0, 0, // No Header
|
||||
kKelvinatorBitMark, kKelvinatorOneSpace, kKelvinatorBitMark,
|
||||
kKelvinatorZeroSpace, kKelvinatorBitMark, kKelvinatorGapSpace,
|
||||
kKelvinatorCmdFooter, kKelvinatorCmdFooterBits, 38, false, 0,
|
||||
50);
|
||||
// Data Block #1 (4 bytes)
|
||||
sendGeneric(0, 0, // No header
|
||||
kKelvinatorBitMark, kKelvinatorOneSpace, kKelvinatorBitMark,
|
||||
kKelvinatorZeroSpace, kKelvinatorBitMark,
|
||||
kKelvinatorGapSpace * 2, data + 4, 4, 38, false, 0, 50);
|
||||
// Command Block #2 (4 bytes)
|
||||
sendGeneric(kKelvinatorHdrMark, kKelvinatorHdrSpace, kKelvinatorBitMark,
|
||||
kKelvinatorOneSpace, kKelvinatorBitMark, kKelvinatorZeroSpace,
|
||||
0, 0, // No Footer yet.
|
||||
data + 8, 4, 38, false, 0, 50);
|
||||
// Send Footer for the command block (3 bits (B010))
|
||||
sendGeneric(0, 0, // No Header
|
||||
kKelvinatorBitMark, kKelvinatorOneSpace, kKelvinatorBitMark,
|
||||
kKelvinatorZeroSpace, kKelvinatorBitMark, kKelvinatorGapSpace,
|
||||
kKelvinatorCmdFooter, kKelvinatorCmdFooterBits, 38, false, 0,
|
||||
50);
|
||||
// Data Block #2 (4 bytes)
|
||||
sendGeneric(0, 0, // No header
|
||||
kKelvinatorBitMark, kKelvinatorOneSpace, kKelvinatorBitMark,
|
||||
kKelvinatorZeroSpace, kKelvinatorBitMark,
|
||||
kKelvinatorGapSpace * 2, data + 12, 4, 38, false, 0, 50);
|
||||
}
|
||||
}
|
||||
#endif // SEND_KELVINATOR
|
||||
|
||||
/// Class constructor
|
||||
/// @param[in] pin GPIO to be used when sending.
|
||||
/// @param[in] inverted Is the output signal to be inverted?
|
||||
/// @param[in] use_modulation Is frequency modulation to be used?
|
||||
IRKelvinatorAC::IRKelvinatorAC(const uint16_t pin, const bool inverted,
|
||||
const bool use_modulation)
|
||||
: _irsend(pin, inverted, use_modulation) { stateReset(); }
|
||||
|
||||
/// Reset the internals of the object to a known good state.
|
||||
void IRKelvinatorAC::stateReset(void) {
|
||||
for (uint8_t i = 0; i < kKelvinatorStateLength; i++) _.raw[i] = 0x0;
|
||||
_.raw[3] = 0x50;
|
||||
_.raw[11] = 0x70;
|
||||
}
|
||||
|
||||
/// Set up hardware to be able to send a message.
|
||||
void IRKelvinatorAC::begin(void) { _irsend.begin(); }
|
||||
|
||||
/// Fix up any odd conditions for the current state.
|
||||
void IRKelvinatorAC::fixup(void) {
|
||||
// X-Fan mode is only valid in COOL or DRY modes.
|
||||
if (_.Mode != kKelvinatorCool && _.Mode != kKelvinatorDry)
|
||||
setXFan(false);
|
||||
// Duplicate to the 2nd command chunk.
|
||||
_.raw[8] = _.raw[0];
|
||||
_.raw[9] = _.raw[1];
|
||||
_.raw[10] = _.raw[2];
|
||||
checksum(); // Calculate the checksums
|
||||
}
|
||||
|
||||
#if SEND_KELVINATOR
|
||||
/// Send the current internal state as an IR message.
|
||||
/// @param[in] repeat Nr. of times the message will be repeated.
|
||||
void IRKelvinatorAC::send(const uint16_t repeat) {
|
||||
_irsend.sendKelvinator(getRaw(), kKelvinatorStateLength, repeat);
|
||||
}
|
||||
#endif // SEND_KELVINATOR
|
||||
|
||||
/// Get the raw state of the object, suitable to be sent with the appropriate
|
||||
/// IRsend object method.
|
||||
/// @return A PTR to the internal state.
|
||||
uint8_t *IRKelvinatorAC::getRaw(void) {
|
||||
fixup(); // Ensure correct settings before sending.
|
||||
return _.raw;
|
||||
}
|
||||
|
||||
/// Set the raw state of the object.
|
||||
/// @param[in] new_code The raw state from the native IR message.
|
||||
void IRKelvinatorAC::setRaw(const uint8_t new_code[]) {
|
||||
std::memcpy(_.raw, new_code, kKelvinatorStateLength);
|
||||
}
|
||||
|
||||
/// Calculate the checksum for a given block of state.
|
||||
/// @param[in] block A pointer to a block to calc the checksum of.
|
||||
/// @param[in] length Length of the block array to checksum.
|
||||
/// @return The calculated checksum value.
|
||||
/// @note Many Bothans died to bring us this information.
|
||||
uint8_t IRKelvinatorAC::calcBlockChecksum(const uint8_t *block,
|
||||
const uint16_t length) {
|
||||
uint8_t sum = kKelvinatorChecksumStart;
|
||||
// Sum the lower half of the first 4 bytes of this block.
|
||||
for (uint8_t i = 0; i < 4 && i < length - 1; i++, block++)
|
||||
sum += (*block & 0b1111);
|
||||
// then sum the upper half of the next 3 bytes.
|
||||
for (uint8_t i = 4; i < length - 1; i++, block++) sum += (*block >> 4);
|
||||
// Trim it down to fit into the 4 bits allowed. i.e. Mod 16.
|
||||
return sum & 0b1111;
|
||||
}
|
||||
|
||||
/// Calculate the checksum for the internal state.
|
||||
void IRKelvinatorAC::checksum(void) {
|
||||
_.Sum1 = calcBlockChecksum(_.raw);
|
||||
_.Sum2 = calcBlockChecksum(_.raw + 8);
|
||||
}
|
||||
|
||||
/// Verify the checksum is valid for a given state.
|
||||
/// @param[in] state The array to verify the checksum of.
|
||||
/// @param[in] length The size of the state.
|
||||
/// @return A boolean indicating if it is valid.
|
||||
bool IRKelvinatorAC::validChecksum(const uint8_t state[],
|
||||
const uint16_t length) {
|
||||
for (uint16_t offset = 0; offset + 7 < length; offset += 8) {
|
||||
// Top 4 bits of the last byte in the block is the block's checksum.
|
||||
if (GETBITS8(state[offset + 7], kHighNibble, kNibbleSize) !=
|
||||
calcBlockChecksum(state + offset))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/// Set the internal state to have the power on.
|
||||
void IRKelvinatorAC::on(void) { setPower(true); }
|
||||
|
||||
/// Set the internal state to have the power off.
|
||||
void IRKelvinatorAC::off(void) {setPower(false); }
|
||||
|
||||
/// Set the internal state to have the desired power.
|
||||
/// @param[in] on The desired power state.
|
||||
void IRKelvinatorAC::setPower(const bool on) {
|
||||
_.Power = on;
|
||||
}
|
||||
|
||||
/// Get the power setting from the internal state.
|
||||
/// @return A boolean indicating if the power setting.
|
||||
bool IRKelvinatorAC::getPower(void) const {
|
||||
return _.Power;
|
||||
}
|
||||
|
||||
/// Set the temperature setting.
|
||||
/// @param[in] degrees The temperature in degrees celsius.
|
||||
void IRKelvinatorAC::setTemp(const uint8_t degrees) {
|
||||
uint8_t temp = std::max(kKelvinatorMinTemp, degrees);
|
||||
temp = std::min(kKelvinatorMaxTemp, temp);
|
||||
_.Temp = temp - kKelvinatorMinTemp;
|
||||
}
|
||||
|
||||
/// Get the current temperature setting.
|
||||
/// @return Get current setting for temp. in degrees celsius.
|
||||
uint8_t IRKelvinatorAC::getTemp(void) const {
|
||||
return _.Temp + kKelvinatorMinTemp;
|
||||
}
|
||||
|
||||
/// Set the speed of the fan.
|
||||
/// @param[in] speed 0 is auto, 1-5 is the speed
|
||||
void IRKelvinatorAC::setFan(const uint8_t speed) {
|
||||
uint8_t fan = std::min(kKelvinatorFanMax, speed); // Bounds check
|
||||
|
||||
// Only change things if we need to.
|
||||
if (fan != _.Fan) {
|
||||
// Set the basic fan values.
|
||||
_.BasicFan = std::min(kKelvinatorBasicFanMax, fan);
|
||||
// Set the advanced(?) fan value.
|
||||
_.Fan = fan;
|
||||
// Turbo mode is turned off if we change the fan settings.
|
||||
setTurbo(false);
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the current fan speed setting.
|
||||
/// @return The current fan speed.
|
||||
uint8_t IRKelvinatorAC::getFan(void) const {
|
||||
return _.Fan;
|
||||
}
|
||||
|
||||
/// Get the current operation mode setting.
|
||||
/// @return The current operation mode.
|
||||
uint8_t IRKelvinatorAC::getMode(void) const {
|
||||
return _.Mode;
|
||||
}
|
||||
|
||||
/// Set the desired operation mode.
|
||||
/// @param[in] mode The desired operation mode.
|
||||
void IRKelvinatorAC::setMode(const uint8_t mode) {
|
||||
switch (mode) {
|
||||
case kKelvinatorAuto:
|
||||
case kKelvinatorDry:
|
||||
// When the remote is set to Auto or Dry, it defaults to 25C and doesn't
|
||||
// show it.
|
||||
setTemp(kKelvinatorAutoTemp);
|
||||
// FALL-THRU
|
||||
case kKelvinatorHeat:
|
||||
case kKelvinatorCool:
|
||||
case kKelvinatorFan:
|
||||
_.Mode = mode;
|
||||
break;
|
||||
default:
|
||||
setTemp(kKelvinatorAutoTemp);
|
||||
_.Mode = kKelvinatorAuto;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/// Set the Vertical Swing mode of the A/C.
|
||||
/// @param[in] automatic Do we use the automatic setting?
|
||||
/// @param[in] position The position/mode to set the vanes to.
|
||||
void IRKelvinatorAC::setSwingVertical(const bool automatic,
|
||||
const uint8_t position) {
|
||||
_.SwingAuto = (automatic || _.SwingH);
|
||||
uint8_t new_position = position;
|
||||
if (!automatic) {
|
||||
switch (position) {
|
||||
case kKelvinatorSwingVHighest:
|
||||
case kKelvinatorSwingVUpperMiddle:
|
||||
case kKelvinatorSwingVMiddle:
|
||||
case kKelvinatorSwingVLowerMiddle:
|
||||
case kKelvinatorSwingVLowest:
|
||||
break;
|
||||
default:
|
||||
new_position = kKelvinatorSwingVOff;
|
||||
}
|
||||
} else {
|
||||
switch (position) {
|
||||
case kKelvinatorSwingVAuto:
|
||||
case kKelvinatorSwingVLowAuto:
|
||||
case kKelvinatorSwingVMiddleAuto:
|
||||
case kKelvinatorSwingVHighAuto:
|
||||
break;
|
||||
default:
|
||||
new_position = kKelvinatorSwingVAuto;
|
||||
}
|
||||
}
|
||||
_.SwingV = new_position;
|
||||
}
|
||||
|
||||
/// Get the Vertical Swing Automatic mode setting of the A/C.
|
||||
/// @return true, the setting is on. false, the setting is off.
|
||||
bool IRKelvinatorAC::getSwingVerticalAuto(void) const {
|
||||
return _.SwingV & 0b0001;
|
||||
}
|
||||
|
||||
/// Get the Vertical Swing position setting of the A/C.
|
||||
/// @return The native position/mode.
|
||||
uint8_t IRKelvinatorAC::getSwingVerticalPosition(void) const {
|
||||
return _.SwingV;
|
||||
}
|
||||
|
||||
/// Control the current horizontal swing setting.
|
||||
/// @param[in] on The desired setting.
|
||||
void IRKelvinatorAC::setSwingHorizontal(const bool on) {
|
||||
_.SwingH = on;
|
||||
_.SwingAuto = (on || (_.SwingV & 0b0001));
|
||||
}
|
||||
|
||||
/// Is the horizontal swing setting on?
|
||||
/// @return The current value.
|
||||
bool IRKelvinatorAC::getSwingHorizontal(void) const {
|
||||
return _.SwingH;
|
||||
}
|
||||
|
||||
/// Control the current Quiet setting.
|
||||
/// @param[in] on The desired setting.
|
||||
void IRKelvinatorAC::setQuiet(const bool on) {
|
||||
_.Quiet = on;
|
||||
}
|
||||
|
||||
/// Is the Quiet setting on?
|
||||
/// @return The current value.
|
||||
bool IRKelvinatorAC::getQuiet(void) const {
|
||||
return _.Quiet;
|
||||
}
|
||||
|
||||
/// Control the current Ion Filter setting.
|
||||
/// @param[in] on The desired setting.
|
||||
void IRKelvinatorAC::setIonFilter(const bool on) {
|
||||
_.IonFilter = on;
|
||||
}
|
||||
|
||||
/// Is the Ion Filter setting on?
|
||||
/// @return The current value.
|
||||
bool IRKelvinatorAC::getIonFilter(void) const {
|
||||
return _.IonFilter;
|
||||
}
|
||||
|
||||
/// Control the current Light setting.
|
||||
/// i.e. The LED display on the A/C unit that shows the basic settings.
|
||||
/// @param[in] on The desired setting.
|
||||
void IRKelvinatorAC::setLight(const bool on) {
|
||||
_.Light = on;
|
||||
}
|
||||
|
||||
/// Is the Light (Display) setting on?
|
||||
/// @return The current value.
|
||||
bool IRKelvinatorAC::getLight(void) const {
|
||||
return _.Light;
|
||||
}
|
||||
|
||||
/// Control the current XFan setting.
|
||||
/// This setting will cause the unit blow air after power off to dry out the
|
||||
/// A/C device.
|
||||
/// @note XFan mode is only valid in Cool or Dry mode.
|
||||
/// @param[in] on The desired setting.
|
||||
void IRKelvinatorAC::setXFan(const bool on) {
|
||||
_.XFan = on;
|
||||
}
|
||||
|
||||
/// Is the XFan setting on?
|
||||
/// @return The current value.
|
||||
bool IRKelvinatorAC::getXFan(void) const {
|
||||
return _.XFan;
|
||||
}
|
||||
|
||||
/// Control the current Turbo setting.
|
||||
/// @note Turbo mode is turned off if the fan speed is changed.
|
||||
/// @param[in] on The desired setting.
|
||||
void IRKelvinatorAC::setTurbo(const bool on) {
|
||||
_.Turbo = on;
|
||||
}
|
||||
|
||||
/// Is the Turbo setting on?
|
||||
/// @return The current value.
|
||||
bool IRKelvinatorAC::getTurbo(void) const {
|
||||
return _.Turbo;
|
||||
}
|
||||
|
||||
/// Convert a standard A/C mode (stdAc::opmode_t) into it a native mode.
|
||||
/// @param[in] mode A stdAc::opmode_t operation mode.
|
||||
/// @return The native mode equivalent.
|
||||
uint8_t IRKelvinatorAC::convertMode(const stdAc::opmode_t mode) {
|
||||
switch (mode) {
|
||||
case stdAc::opmode_t::kCool: return kKelvinatorCool;
|
||||
case stdAc::opmode_t::kHeat: return kKelvinatorHeat;
|
||||
case stdAc::opmode_t::kDry: return kKelvinatorDry;
|
||||
case stdAc::opmode_t::kFan: return kKelvinatorFan;
|
||||
default: return kKelvinatorAuto;
|
||||
}
|
||||
}
|
||||
|
||||
/// Convert a stdAc::swingv_t enum into it's native setting.
|
||||
/// @param[in] swingv The enum to be converted.
|
||||
/// @return The native equivalent of the enum.
|
||||
uint8_t IRKelvinatorAC::convertSwingV(const stdAc::swingv_t swingv) {
|
||||
switch (swingv) {
|
||||
case stdAc::swingv_t::kHighest: return kKelvinatorSwingVHighest;
|
||||
case stdAc::swingv_t::kHigh: return kKelvinatorSwingVHighAuto;
|
||||
case stdAc::swingv_t::kMiddle: return kKelvinatorSwingVMiddle;
|
||||
case stdAc::swingv_t::kLow: return kKelvinatorSwingVLowAuto;
|
||||
case stdAc::swingv_t::kLowest: return kKelvinatorSwingVLowest;
|
||||
default: return kKelvinatorSwingVAuto;
|
||||
}
|
||||
}
|
||||
|
||||
/// Convert a native mode to it's stdAc::opmode_t equivalent.
|
||||
/// @param[in] mode A native operating mode value.
|
||||
/// @return The stdAc::opmode_t equivalent.
|
||||
stdAc::opmode_t IRKelvinatorAC::toCommonMode(const uint8_t mode) {
|
||||
switch (mode) {
|
||||
case kKelvinatorCool: return stdAc::opmode_t::kCool;
|
||||
case kKelvinatorHeat: return stdAc::opmode_t::kHeat;
|
||||
case kKelvinatorDry: return stdAc::opmode_t::kDry;
|
||||
case kKelvinatorFan: return stdAc::opmode_t::kFan;
|
||||
default: return stdAc::opmode_t::kAuto;
|
||||
}
|
||||
}
|
||||
|
||||
/// Convert a native fan speed to it's stdAc::fanspeed_t equivalent.
|
||||
/// @param[in] speed A native fan speed value.
|
||||
/// @return The stdAc::fanspeed_t equivalent.
|
||||
stdAc::fanspeed_t IRKelvinatorAC::toCommonFanSpeed(const uint8_t speed) {
|
||||
return (stdAc::fanspeed_t)speed;
|
||||
}
|
||||
|
||||
/// Convert the internal A/C object state to it's stdAc::state_t equivalent.
|
||||
/// @return A stdAc::state_t containing the current settings.
|
||||
stdAc::state_t IRKelvinatorAC::toCommon(void) const {
|
||||
stdAc::state_t result{};
|
||||
result.protocol = decode_type_t::KELVINATOR;
|
||||
result.model = -1; // Unused.
|
||||
result.power = _.Power;
|
||||
result.mode = toCommonMode(_.Mode);
|
||||
result.celsius = true;
|
||||
result.degrees = getTemp();
|
||||
result.fanspeed = toCommonFanSpeed(_.Fan);
|
||||
result.swingv = _.SwingV ? stdAc::swingv_t::kAuto : stdAc::swingv_t::kOff;
|
||||
result.swingh = _.SwingH ? stdAc::swingh_t::kAuto : stdAc::swingh_t::kOff;
|
||||
result.quiet = _.Quiet;
|
||||
result.turbo = _.Turbo;
|
||||
result.light = _.Light;
|
||||
result.filter = _.IonFilter;
|
||||
result.clean = _.XFan;
|
||||
// Not supported.
|
||||
result.econo = false;
|
||||
result.beep = false;
|
||||
result.sleep = -1;
|
||||
result.clock = -1;
|
||||
return result;
|
||||
}
|
||||
|
||||
/// Convert the internal settings into a human readable string.
|
||||
/// @return A String.
|
||||
String IRKelvinatorAC::toString(void) const {
|
||||
String result = "";
|
||||
result.reserve(160); // Reserve some heap for the string to reduce fragging.
|
||||
result += addBoolToString(_.Power, kPowerStr, false);
|
||||
result += addModeToString(_.Mode, kKelvinatorAuto, kKelvinatorCool,
|
||||
kKelvinatorHeat, kKelvinatorDry, kKelvinatorFan);
|
||||
result += addTempToString(getTemp());
|
||||
result += addFanToString(_.Fan, kKelvinatorFanMax, kKelvinatorFanMin,
|
||||
kKelvinatorFanAuto, kKelvinatorFanAuto,
|
||||
kKelvinatorBasicFanMax);
|
||||
result += addBoolToString(_.Turbo, kTurboStr);
|
||||
result += addBoolToString(_.Quiet, kQuietStr);
|
||||
result += addBoolToString(_.XFan, kXFanStr);
|
||||
result += addBoolToString(_.IonFilter, kIonStr);
|
||||
result += addBoolToString(_.Light, kLightStr);
|
||||
result += addBoolToString(_.SwingH, kSwingHStr);
|
||||
result += addSwingVToString(_.SwingV, kKelvinatorSwingVAuto,
|
||||
kKelvinatorSwingVHighest,
|
||||
kKelvinatorSwingVHighAuto,
|
||||
kKelvinatorSwingVUpperMiddle,
|
||||
kKelvinatorSwingVMiddle,
|
||||
kKelvinatorSwingVLowerMiddle,
|
||||
kKelvinatorSwingVLowAuto,
|
||||
kKelvinatorSwingVLowest,
|
||||
kKelvinatorSwingVOff,
|
||||
kKelvinatorSwingVAuto, kKelvinatorSwingVAuto,
|
||||
kKelvinatorSwingVAuto);
|
||||
return result;
|
||||
}
|
||||
|
||||
#if DECODE_KELVINATOR
|
||||
/// Decode the supplied Kelvinator message.
|
||||
/// Status: STABLE / Known working.
|
||||
/// @param[in,out] results Ptr to the data to decode & where to store the decode
|
||||
/// result.
|
||||
/// @param[in] offset The starting index to use when attempting to decode the
|
||||
/// raw data. Typically/Defaults to kStartOffset.
|
||||
/// @param[in] nbits The number of data bits to expect.
|
||||
/// @param[in] strict Flag indicating if we should perform strict matching.
|
||||
/// @return A boolean. True if it can decode it, false if it can't.
|
||||
bool IRrecv::decodeKelvinator(decode_results *results, uint16_t offset,
|
||||
const uint16_t nbits, const bool strict) {
|
||||
if (results->rawlen <=
|
||||
2 * (nbits + kKelvinatorCmdFooterBits) + (kHeader + kFooter + 1) * 2 - 1 +
|
||||
offset)
|
||||
return false; // Can't possibly be a valid Kelvinator message.
|
||||
if (strict && nbits != kKelvinatorBits)
|
||||
return false; // Not strictly a Kelvinator message.
|
||||
|
||||
// There are two messages back-to-back in a full Kelvinator IR message
|
||||
// sequence.
|
||||
int8_t pos = 0;
|
||||
for (uint8_t s = 0; s < 2; s++) {
|
||||
match_result_t data_result;
|
||||
|
||||
uint16_t used;
|
||||
// Header + Data Block #1 (32 bits)
|
||||
used = matchGeneric(results->rawbuf + offset, results->state + pos,
|
||||
results->rawlen - offset, 32,
|
||||
kKelvinatorHdrMark, kKelvinatorHdrSpace,
|
||||
kKelvinatorBitMark, kKelvinatorOneSpace,
|
||||
kKelvinatorBitMark, kKelvinatorZeroSpace,
|
||||
0, 0, false,
|
||||
_tolerance, kMarkExcess, false);
|
||||
if (used == 0) return false;
|
||||
offset += used;
|
||||
pos += 4;
|
||||
|
||||
// Command data footer (3 bits, B010)
|
||||
data_result = matchData(
|
||||
&(results->rawbuf[offset]), kKelvinatorCmdFooterBits,
|
||||
kKelvinatorBitMark, kKelvinatorOneSpace,
|
||||
kKelvinatorBitMark, kKelvinatorZeroSpace,
|
||||
_tolerance, kMarkExcess, false);
|
||||
if (data_result.success == false) return false;
|
||||
if (data_result.data != kKelvinatorCmdFooter) return false;
|
||||
offset += data_result.used;
|
||||
|
||||
// Gap + Data (Options) (32 bits)
|
||||
used = matchGeneric(results->rawbuf + offset, results->state + pos,
|
||||
results->rawlen - offset, 32,
|
||||
kKelvinatorBitMark, kKelvinatorGapSpace,
|
||||
kKelvinatorBitMark, kKelvinatorOneSpace,
|
||||
kKelvinatorBitMark, kKelvinatorZeroSpace,
|
||||
kKelvinatorBitMark, kKelvinatorGapSpace * 2,
|
||||
s > 0,
|
||||
_tolerance, kMarkExcess, false);
|
||||
if (used == 0) return false;
|
||||
offset += used;
|
||||
pos += 4;
|
||||
}
|
||||
|
||||
// Compliance
|
||||
if (strict) {
|
||||
// Verify the message's checksum is correct.
|
||||
if (!IRKelvinatorAC::validChecksum(results->state)) return false;
|
||||
}
|
||||
|
||||
// Success
|
||||
results->decode_type = decode_type_t::KELVINATOR;
|
||||
results->bits = nbits;
|
||||
// No need to record the state as we stored it as we decoded it.
|
||||
// As we use result->state, we don't record value, address, or command as it
|
||||
// is a union data type.
|
||||
return true;
|
||||
}
|
||||
#endif // DECODE_KELVINATOR
|
||||
198
yoRadio/src/IRremoteESP8266/ir_Kelvinator.h
Normal file
198
yoRadio/src/IRremoteESP8266/ir_Kelvinator.h
Normal file
@@ -0,0 +1,198 @@
|
||||
// Copyright 2016 David Conran
|
||||
/// @file
|
||||
/// @brief Support for Kelvinator A/C protocols.
|
||||
|
||||
// Supports:
|
||||
// Brand: Kelvinator, Model: YALIF Remote
|
||||
// Brand: Kelvinator, Model: KSV26CRC A/C
|
||||
// Brand: Kelvinator, Model: KSV26HRC A/C
|
||||
// Brand: Kelvinator, Model: KSV35CRC A/C
|
||||
// Brand: Kelvinator, Model: KSV35HRC A/C
|
||||
// Brand: Kelvinator, Model: KSV53HRC A/C
|
||||
// Brand: Kelvinator, Model: KSV62HRC A/C
|
||||
// Brand: Kelvinator, Model: KSV70CRC A/C
|
||||
// Brand: Kelvinator, Model: KSV70HRC A/C
|
||||
// Brand: Kelvinator, Model: KSV80HRC A/C
|
||||
// Brand: Gree, Model: YAPOF3 remote
|
||||
// Brand: Gree, Model: YAP0F8 remote
|
||||
// Brand: Sharp, Model: YB1FA remote
|
||||
// Brand: Sharp, Model: A5VEY A/C
|
||||
|
||||
#ifndef IR_KELVINATOR_H_
|
||||
#define IR_KELVINATOR_H_
|
||||
|
||||
#define __STDC_LIMIT_MACROS
|
||||
#include <stdint.h>
|
||||
#ifndef UNIT_TEST
|
||||
#include <Arduino.h>
|
||||
#endif
|
||||
#include "IRremoteESP8266.h"
|
||||
#include "IRsend.h"
|
||||
#ifdef UNIT_TEST
|
||||
#include "IRsend_test.h"
|
||||
#endif
|
||||
|
||||
/// Native representation of a Kelvinator A/C message.
|
||||
union KelvinatorProtocol{
|
||||
uint8_t raw[kKelvinatorStateLength]; ///< The state in IR code form.
|
||||
struct {
|
||||
// Byte 0
|
||||
uint8_t Mode :3;
|
||||
uint8_t Power :1;
|
||||
uint8_t BasicFan :2;
|
||||
uint8_t SwingAuto :1;
|
||||
uint8_t :1; // Sleep Modes 1 & 3 (1 = On, 0 = Off)
|
||||
// Byte 1
|
||||
uint8_t Temp :4; // Degrees C.
|
||||
uint8_t :4;
|
||||
// Byte 2
|
||||
uint8_t :4;
|
||||
uint8_t Turbo :1;
|
||||
uint8_t Light :1;
|
||||
uint8_t IonFilter :1;
|
||||
uint8_t XFan :1;
|
||||
// Byte 3
|
||||
uint8_t :4;
|
||||
uint8_t :2; // (possibly timer related) (Typically 0b01)
|
||||
uint8_t :2; // End of command block (B01)
|
||||
// (B010 marker and a gap of 20ms)
|
||||
// Byte 4
|
||||
uint8_t SwingV :4;
|
||||
uint8_t SwingH :1;
|
||||
uint8_t :3;
|
||||
// Byte 5~6
|
||||
uint8_t pad0[2]; // Timer related. Typically 0 except when timer in use.
|
||||
// Byte 7
|
||||
uint8_t :4; // (Used in Timer mode)
|
||||
uint8_t Sum1 :4; // checksum of the previous bytes (0-6)
|
||||
// (gap of 40ms)
|
||||
// (header mark and space)
|
||||
// Byte 8~10
|
||||
uint8_t pad1[3]; // Repeat of byte 0~2
|
||||
// Byte 11
|
||||
uint8_t :4;
|
||||
uint8_t :2; // (possibly timer related) (Typically 0b11)
|
||||
uint8_t :2; // End of command block (B01)
|
||||
// (B010 marker and a gap of 20ms)
|
||||
// Byte 12
|
||||
uint8_t :1; // Sleep mode 2 (1 = On, 0=Off)
|
||||
uint8_t :6; // (Used in Sleep Mode 3, Typically 0b000000)
|
||||
uint8_t Quiet :1;
|
||||
// Byte 13
|
||||
uint8_t :8; // (Sleep Mode 3 related, Typically 0x00)
|
||||
// Byte 14
|
||||
uint8_t :4; // (Sleep Mode 3 related, Typically 0b0000)
|
||||
uint8_t Fan :3;
|
||||
// Byte 15
|
||||
uint8_t :4;
|
||||
uint8_t Sum2 :4; // checksum of the previous bytes (8-14)
|
||||
};
|
||||
};
|
||||
|
||||
// Constants
|
||||
const uint8_t kKelvinatorAuto = 0; // (temp = 25C)
|
||||
const uint8_t kKelvinatorCool = 1;
|
||||
const uint8_t kKelvinatorDry = 2; // (temp = 25C, but not shown)
|
||||
const uint8_t kKelvinatorFan = 3;
|
||||
const uint8_t kKelvinatorHeat = 4;
|
||||
const uint8_t kKelvinatorBasicFanMax = 3;
|
||||
const uint8_t kKelvinatorFanAuto = 0;
|
||||
const uint8_t kKelvinatorFanMin = 1;
|
||||
const uint8_t kKelvinatorFanMax = 5;
|
||||
const uint8_t kKelvinatorMinTemp = 16; // 16C
|
||||
const uint8_t kKelvinatorMaxTemp = 30; // 30C
|
||||
const uint8_t kKelvinatorAutoTemp = 25; // 25C
|
||||
|
||||
const uint8_t kKelvinatorSwingVOff = 0b0000; // 0
|
||||
const uint8_t kKelvinatorSwingVAuto = 0b0001; // 1
|
||||
const uint8_t kKelvinatorSwingVHighest = 0b0010; // 2
|
||||
const uint8_t kKelvinatorSwingVUpperMiddle = 0b0011; // 3
|
||||
const uint8_t kKelvinatorSwingVMiddle = 0b0100; // 4
|
||||
const uint8_t kKelvinatorSwingVLowerMiddle = 0b0101; // 5
|
||||
const uint8_t kKelvinatorSwingVLowest = 0b0110; // 6
|
||||
const uint8_t kKelvinatorSwingVLowAuto = 0b0111; // 7
|
||||
const uint8_t kKelvinatorSwingVMiddleAuto = 0b1001; // 9
|
||||
const uint8_t kKelvinatorSwingVHighAuto = 0b1011; // 11
|
||||
|
||||
// Legacy defines (Deprecated)
|
||||
#define KELVINATOR_MIN_TEMP kKelvinatorMinTemp
|
||||
#define KELVINATOR_MAX_TEMP kKelvinatorMaxTemp
|
||||
#define KELVINATOR_HEAT kKelvinatorHeat
|
||||
#define KELVINATOR_FAN_MAX kKelvinatorFanMax
|
||||
#define KELVINATOR_FAN_AUTO kKelvinatorFanAuto
|
||||
#define KELVINATOR_FAN kKelvinatorFan
|
||||
#define KELVINATOR_DRY kKelvinatorDry
|
||||
#define KELVINATOR_COOL kKelvinatorCool
|
||||
#define KELVINATOR_BASIC_FAN_MAX kKelvinatorBasicFanMax
|
||||
#define KELVINATOR_AUTO_TEMP kKelvinatorAutoTemp
|
||||
#define KELVINATOR_AUTO kKelvinatorAuto
|
||||
|
||||
// Classes
|
||||
/// Class for handling detailed Kelvinator A/C messages.
|
||||
class IRKelvinatorAC {
|
||||
public:
|
||||
explicit IRKelvinatorAC(const uint16_t pin, const bool inverted = false,
|
||||
const bool use_modulation = true);
|
||||
void stateReset(void);
|
||||
#if SEND_KELVINATOR
|
||||
void send(const uint16_t repeat = kKelvinatorDefaultRepeat);
|
||||
/// 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_KELVINATOR
|
||||
void begin(void);
|
||||
void on(void);
|
||||
void off(void);
|
||||
void setPower(const bool on);
|
||||
bool getPower(void) const;
|
||||
void setTemp(const uint8_t degrees);
|
||||
uint8_t getTemp(void) const;
|
||||
void setFan(const uint8_t speed);
|
||||
uint8_t getFan(void) const;
|
||||
void setMode(const uint8_t mode);
|
||||
uint8_t getMode(void) const;
|
||||
void setSwingVertical(const bool automatic, const uint8_t position);
|
||||
bool getSwingVerticalAuto(void) const;
|
||||
uint8_t getSwingVerticalPosition(void) const;
|
||||
static uint8_t convertSwingV(const stdAc::swingv_t swingv);
|
||||
static stdAc::swingv_t toCommonSwingV(const uint8_t pos);
|
||||
void setSwingHorizontal(const bool on);
|
||||
bool getSwingHorizontal(void) const;
|
||||
void setQuiet(const bool on);
|
||||
bool getQuiet(void) const;
|
||||
void setIonFilter(const bool on);
|
||||
bool getIonFilter(void) const;
|
||||
void setLight(const bool on);
|
||||
bool getLight(void) const;
|
||||
void setXFan(const bool on);
|
||||
bool getXFan(void) const;
|
||||
void setTurbo(const bool on);
|
||||
bool getTurbo(void) const;
|
||||
uint8_t* getRaw(void);
|
||||
void setRaw(const uint8_t new_code[]);
|
||||
static uint8_t calcBlockChecksum(
|
||||
const uint8_t* block, const uint16_t length = kKelvinatorStateLength / 2);
|
||||
static bool validChecksum(const uint8_t state[],
|
||||
const uint16_t length = kKelvinatorStateLength);
|
||||
static uint8_t convertMode(const stdAc::opmode_t mode);
|
||||
static stdAc::opmode_t toCommonMode(const uint8_t mode);
|
||||
static stdAc::fanspeed_t toCommonFanSpeed(const uint8_t speed);
|
||||
stdAc::state_t toCommon(void) const;
|
||||
String toString(void) const;
|
||||
#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
|
||||
KelvinatorProtocol _;
|
||||
void checksum(void);
|
||||
void fixup(void);
|
||||
};
|
||||
|
||||
#endif // IR_KELVINATOR_H_
|
||||
862
yoRadio/src/IRremoteESP8266/ir_LG.cpp
Normal file
862
yoRadio/src/IRremoteESP8266/ir_LG.cpp
Normal file
@@ -0,0 +1,862 @@
|
||||
// Copyright 2015 Darryl Smith
|
||||
// Copyright 2015 cheaplin
|
||||
// Copyright 2017-2021 David Conran
|
||||
|
||||
/// @file
|
||||
/// @brief Support for LG protocols.
|
||||
/// LG decode originally added by Darryl Smith (based on the JVC protocol)
|
||||
/// LG send originally added by https://github.com/chaeplin
|
||||
/// @see https://github.com/arendst/Tasmota/blob/54c2eb283a02e4287640a4595e506bc6eadbd7f2/sonoff/xdrv_05_irremote.ino#L327-438
|
||||
/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1513
|
||||
|
||||
#include "ir_LG.h"
|
||||
#include <algorithm>
|
||||
#include "IRac.h"
|
||||
#include "IRrecv.h"
|
||||
#include "IRsend.h"
|
||||
#include "IRtext.h"
|
||||
#include "IRutils.h"
|
||||
|
||||
using irutils::addBoolToString;
|
||||
using irutils::addModeToString;
|
||||
using irutils::addModelToString;
|
||||
using irutils::addFanToString;
|
||||
using irutils::addTempToString;
|
||||
using irutils::addToggleToString;
|
||||
using irutils::addSwingVToString;
|
||||
using irutils::addIntToString;
|
||||
|
||||
|
||||
// Constants
|
||||
// Common timings
|
||||
const uint16_t kLgBitMark = 550; ///< uSeconds.
|
||||
const uint16_t kLgOneSpace = 1600; ///< uSeconds.
|
||||
const uint16_t kLgZeroSpace = 550; ///< uSeconds.
|
||||
const uint16_t kLgRptSpace = 2250; ///< uSeconds.
|
||||
const uint16_t kLgMinGap = 39750; ///< uSeconds.
|
||||
const uint32_t kLgMinMessageLength = 108050; ///< uSeconds.
|
||||
// LG (28 Bit)
|
||||
const uint16_t kLgHdrMark = 8500; ///< uSeconds.
|
||||
const uint16_t kLgHdrSpace = 4250; ///< uSeconds.
|
||||
// LG (32 Bit)
|
||||
const uint16_t kLg32HdrMark = 4500; ///< uSeconds.
|
||||
const uint16_t kLg32HdrSpace = 4450; ///< uSeconds.
|
||||
const uint16_t kLg32RptHdrMark = 8950; ///< uSeconds.
|
||||
// LG2 (28 Bit)
|
||||
const uint16_t kLg2HdrMark = 3200; ///< uSeconds.
|
||||
const uint16_t kLg2HdrSpace = 9900; ///< uSeconds.
|
||||
const uint16_t kLg2BitMark = 480; ///< uSeconds.
|
||||
|
||||
const uint32_t kLgAcAKB74955603DetectionMask = 0x0000080;
|
||||
const uint8_t kLgAcChecksumSize = 4; ///< Size in bits.
|
||||
// Signature has the checksum removed, and another bit to match both Auto & Off.
|
||||
const uint8_t kLgAcSwingHOffsetSize = kLgAcChecksumSize + 1;
|
||||
const uint32_t kLgAcSwingHSignature = kLgAcSwingHOff >> kLgAcSwingHOffsetSize;
|
||||
const uint32_t kLgAcVaneSwingVBase = 0x8813200;
|
||||
|
||||
#ifdef VANESWINGVPOS
|
||||
#undef VANESWINGVPOS
|
||||
#endif
|
||||
#define VANESWINGVPOS(code) (code % kLgAcVaneSwingVSize)
|
||||
|
||||
#if SEND_LG
|
||||
/// Send an LG formatted message. (LG)
|
||||
/// Status: Beta / Should be working.
|
||||
/// @param[in] data The message to be sent.
|
||||
/// @param[in] nbits The number of bits of message to be sent.
|
||||
/// Typically kLgBits or kLg32Bits.
|
||||
/// @param[in] repeat The number of times the command is to be repeated.
|
||||
/// @note LG has a separate message to indicate a repeat, like NEC does.
|
||||
void IRsend::sendLG(uint64_t data, uint16_t nbits, uint16_t repeat) {
|
||||
uint16_t repeatHeaderMark = 0;
|
||||
uint8_t duty = kDutyDefault;
|
||||
|
||||
if (nbits >= kLg32Bits) {
|
||||
// LG 32bit protocol is near identical to Samsung except for repeats.
|
||||
sendSAMSUNG(data, nbits, 0); // Send it as a single Samsung message.
|
||||
repeatHeaderMark = kLg32RptHdrMark;
|
||||
duty = 33;
|
||||
repeat++;
|
||||
} else {
|
||||
// LG (28-bit) protocol.
|
||||
repeatHeaderMark = kLgHdrMark;
|
||||
sendGeneric(kLgHdrMark, kLgHdrSpace, kLgBitMark, kLgOneSpace, kLgBitMark,
|
||||
kLgZeroSpace, kLgBitMark, kLgMinGap, kLgMinMessageLength, data,
|
||||
nbits, 38, true, 0, // Repeats are handled later.
|
||||
duty);
|
||||
}
|
||||
|
||||
// Repeat
|
||||
// Protocol has a mandatory repeat-specific code sent after every command.
|
||||
if (repeat)
|
||||
sendGeneric(repeatHeaderMark, kLgRptSpace, 0, 0, 0, 0, // No data is sent.
|
||||
kLgBitMark, kLgMinGap, kLgMinMessageLength, 0, 0, // No data.
|
||||
38, true, repeat - 1, duty);
|
||||
}
|
||||
|
||||
/// Send an LG Variant-2 formatted message. (LG2)
|
||||
/// Status: Beta / Should be working.
|
||||
/// @param[in] data The message to be sent.
|
||||
/// @param[in] nbits The number of bits of message to be sent.
|
||||
/// Typically kLgBits or kLg32Bits.
|
||||
/// @param[in] repeat The number of times the command is to be repeated.
|
||||
/// @note LG has a separate message to indicate a repeat, like NEC does.
|
||||
void IRsend::sendLG2(uint64_t data, uint16_t nbits, uint16_t repeat) {
|
||||
if (nbits >= kLg32Bits) {
|
||||
// Let the original routine handle it.
|
||||
sendLG(data, nbits, repeat); // Send it as a single Samsung message.
|
||||
return;
|
||||
}
|
||||
|
||||
// LGv2 (28-bit) protocol.
|
||||
sendGeneric(kLg2HdrMark, kLg2HdrSpace, kLg2BitMark, kLgOneSpace, kLg2BitMark,
|
||||
kLgZeroSpace, kLg2BitMark, kLgMinGap, kLgMinMessageLength, data,
|
||||
nbits, 38, true, 0, // Repeats are handled later.
|
||||
33); // Use a duty cycle of 33% (Testing)
|
||||
|
||||
// TODO(crackn): Verify the details of what repeat messages look like.
|
||||
// Repeat
|
||||
// Protocol has a mandatory repeat-specific code sent after every command.
|
||||
if (repeat)
|
||||
sendGeneric(kLg2HdrMark, kLgRptSpace, 0, 0, 0, 0, // No data is sent.
|
||||
kLgBitMark, kLgMinGap, kLgMinMessageLength, 0, 0, // No data.
|
||||
38, true, repeat - 1, 50);
|
||||
}
|
||||
|
||||
/// Construct a raw 28-bit LG message code from the supplied address & command.
|
||||
/// Status: STABLE / Works.
|
||||
/// @param[in] address The address code.
|
||||
/// @param[in] command The command code.
|
||||
/// @return A raw 28-bit LG message code suitable for sendLG() etc.
|
||||
/// @note Sequence of bits = address + command + checksum.
|
||||
uint32_t IRsend::encodeLG(uint16_t address, uint16_t command) {
|
||||
return ((address << 20) | (command << kLgAcChecksumSize) |
|
||||
irutils::sumNibbles(command, 4));
|
||||
}
|
||||
#endif // SEND_LG
|
||||
|
||||
#if DECODE_LG
|
||||
/// Decode the supplied LG message.
|
||||
/// Status: STABLE / Working.
|
||||
/// @param[in,out] results Ptr to the data to decode & where to store the result
|
||||
/// @param[in] offset The starting index to use when attempting to decode the
|
||||
/// raw data. Typically/Defaults to kStartOffset.
|
||||
/// @param[in] nbits The number of data bits to expect.
|
||||
/// Typically kLgBits or kLg32Bits.
|
||||
/// @param[in] strict Flag indicating if we should perform strict matching.
|
||||
/// @return True if it can decode it, false if it can't.
|
||||
/// @note LG protocol has a repeat code which is 4 items long.
|
||||
/// Even though the protocol has 28/32 bits of data, only 24/28 bits are
|
||||
/// distinct.
|
||||
/// In transmission order, the 28/32 bits are constructed as follows:
|
||||
/// 8/12 bits of address + 16 bits of command + 4 bits of checksum.
|
||||
/// @note LG 32bit protocol appears near identical to the Samsung protocol.
|
||||
/// They possibly differ on how they repeat and initial HDR mark.
|
||||
/// @see https://funembedded.wordpress.com/2014/11/08/ir-remote-control-for-lg-conditioner-using-stm32f302-mcu-on-mbed-platform/
|
||||
bool IRrecv::decodeLG(decode_results *results, uint16_t offset,
|
||||
const uint16_t nbits, const bool strict) {
|
||||
if (nbits >= kLg32Bits) {
|
||||
if (results->rawlen <= 2 * nbits + 2 * (kHeader + kFooter) - 1 + offset)
|
||||
return false; // Can't possibly be a valid LG32 message.
|
||||
} else {
|
||||
if (results->rawlen <= 2 * nbits + kHeader - 1 + offset)
|
||||
return false; // Can't possibly be a valid LG message.
|
||||
}
|
||||
// Compliance
|
||||
if (strict && nbits != kLgBits && nbits != kLg32Bits)
|
||||
return false; // Doesn't comply with expected LG protocol.
|
||||
|
||||
// Header (Mark)
|
||||
uint32_t kHdrSpace;
|
||||
if (matchMark(results->rawbuf[offset], kLgHdrMark))
|
||||
kHdrSpace = kLgHdrSpace;
|
||||
else if (matchMark(results->rawbuf[offset], kLg2HdrMark))
|
||||
kHdrSpace = kLg2HdrSpace;
|
||||
else if (matchMark(results->rawbuf[offset], kLg32HdrMark))
|
||||
kHdrSpace = kLg32HdrSpace;
|
||||
else
|
||||
return false;
|
||||
offset++;
|
||||
|
||||
// Set up the expected data section values.
|
||||
const uint16_t kBitmark = (kHdrSpace == kLg2HdrSpace) ? kLg2BitMark
|
||||
: kLgBitMark;
|
||||
// Header Space + Data + Footer
|
||||
uint64_t data = 0;
|
||||
uint16_t used = matchGeneric(results->rawbuf + offset, &data,
|
||||
results->rawlen - offset, nbits,
|
||||
0, // Already matched the Header mark.
|
||||
kHdrSpace,
|
||||
kBitmark, kLgOneSpace, kBitmark, kLgZeroSpace,
|
||||
kBitmark, kLgMinGap, true, kUseDefTol, 0, true);
|
||||
if (!used) return false;
|
||||
offset += used;
|
||||
|
||||
// Repeat
|
||||
if (nbits >= kLg32Bits) {
|
||||
// If we are expecting the LG 32-bit protocol, there is always
|
||||
// a repeat message. So, check for it.
|
||||
uint64_t unused;
|
||||
if (!matchGeneric(results->rawbuf + offset, &unused,
|
||||
results->rawlen - offset, 0, // No Data bits to match.
|
||||
kLg32RptHdrMark, kLgRptSpace,
|
||||
kBitmark, kLgOneSpace, kBitmark, kLgZeroSpace,
|
||||
kBitmark, kLgMinGap, true, kUseDefTol)) return false;
|
||||
}
|
||||
|
||||
// The 16 bits before the checksum.
|
||||
uint16_t command = (data >> kLgAcChecksumSize);
|
||||
|
||||
// Compliance
|
||||
if (strict && (data & 0xF) != irutils::sumNibbles(command, 4))
|
||||
return false; // The last 4 bits sent are the expected checksum.
|
||||
// Success
|
||||
if (kHdrSpace == kLg2HdrSpace) // Was it an LG2 message?
|
||||
results->decode_type = LG2;
|
||||
else
|
||||
results->decode_type = LG;
|
||||
results->bits = nbits;
|
||||
results->value = data;
|
||||
results->command = command;
|
||||
results->address = data >> 20; // The bits before the command.
|
||||
return true;
|
||||
}
|
||||
#endif // DECODE_LG
|
||||
|
||||
// LG A/C Class
|
||||
|
||||
/// Class constructor
|
||||
/// @param[in] pin GPIO to be used when sending.
|
||||
/// @param[in] inverted Is the output signal to be inverted?
|
||||
/// @param[in] use_modulation Is frequency modulation to be used?
|
||||
IRLgAc::IRLgAc(const uint16_t pin, const bool inverted,
|
||||
const bool use_modulation)
|
||||
: _irsend(pin, inverted, use_modulation) { stateReset(); }
|
||||
|
||||
/// Reset the internals of the object to a known good state.
|
||||
void IRLgAc::stateReset(void) {
|
||||
setRaw(kLgAcOffCommand);
|
||||
setModel(lg_ac_remote_model_t::GE6711AR2853M);
|
||||
_light = true;
|
||||
_swingv = kLgAcSwingVOff;
|
||||
_swingh = false;
|
||||
for (uint8_t i = 0; i < kLgAcSwingVMaxVanes; i++)
|
||||
_vaneswingv[i] = 0; // Reset to an unused value.
|
||||
updateSwingPrev();
|
||||
}
|
||||
|
||||
/// Set up hardware to be able to send a message.
|
||||
void IRLgAc::begin(void) { _irsend.begin(); }
|
||||
|
||||
#if SEND_LG
|
||||
/// Send the current internal state as an IR message.
|
||||
/// @param[in] repeat Nr. of times the message will be repeated.
|
||||
void IRLgAc::send(const uint16_t repeat) {
|
||||
if (getPower()) {
|
||||
_irsend.send(_protocol, getRaw(), kLgBits, repeat);
|
||||
// Some models have extra/special settings & controls
|
||||
switch (getModel()) {
|
||||
case lg_ac_remote_model_t::LG6711A20083V:
|
||||
// Only send the swing setting if we need to.
|
||||
if (_swingv != _swingv_prev)
|
||||
_irsend.send(_protocol, _swingv, kLgBits, repeat);
|
||||
break;
|
||||
case lg_ac_remote_model_t::AKB74955603:
|
||||
// Only send the swing setting if we need to.
|
||||
if (_swingv != _swingv_prev)
|
||||
_irsend.send(_protocol, _swingv, kLgBits, repeat);
|
||||
// Any "normal" command sent will always turn the light on, thus we only
|
||||
// send it when we want it off. Must be sent last!
|
||||
// Ref: https://github.com/crankyoldgit/IRremoteESP8266/issues/1513#issuecomment-877283080
|
||||
if (!_light) _irsend.send(_protocol, kLgAcLightToggle, kLgBits, repeat);
|
||||
break;
|
||||
case lg_ac_remote_model_t::AKB73757604:
|
||||
// Check if we need to send any vane specific swingv's.
|
||||
for (uint8_t i = 0; i < kLgAcSwingVMaxVanes; i++) // For all vanes
|
||||
if (_vaneswingv[i] != _vaneswingv_prev[i]) // Only send if we must.
|
||||
_irsend.send(_protocol, calcVaneSwingV(i, _vaneswingv[i]), kLgBits,
|
||||
repeat);
|
||||
// and if we need to send a swingh message.
|
||||
if (_swingh != _swingh_prev)
|
||||
_irsend.send(_protocol, _swingh ? kLgAcSwingHAuto : kLgAcSwingHOff,
|
||||
kLgBits, repeat);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
updateSwingPrev(); // Swing changes will have been sent, so make them prev.
|
||||
} else {
|
||||
// Always send the special Off command if the power is set to off.
|
||||
// Ref: https://github.com/crankyoldgit/IRremoteESP8266/issues/1008#issuecomment-570763580
|
||||
_irsend.send(_protocol, kLgAcOffCommand, kLgBits, repeat);
|
||||
}
|
||||
}
|
||||
#endif // SEND_LG
|
||||
|
||||
/// Is the current message a normal (non-special) message?
|
||||
/// @return True, if it is a normal message, False, if it is special.
|
||||
bool IRLgAc::_isNormal(void) const {
|
||||
switch (_.raw) {
|
||||
case kLgAcOffCommand:
|
||||
case kLgAcLightToggle:
|
||||
return false;
|
||||
}
|
||||
if (isSwing()) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
/// Set the model of the A/C to emulate.
|
||||
/// @param[in] model The enum of the appropriate model.
|
||||
void IRLgAc::setModel(const lg_ac_remote_model_t model) {
|
||||
switch (model) {
|
||||
case lg_ac_remote_model_t::AKB75215403:
|
||||
case lg_ac_remote_model_t::AKB74955603:
|
||||
case lg_ac_remote_model_t::AKB73757604:
|
||||
_protocol = decode_type_t::LG2;
|
||||
break;
|
||||
case lg_ac_remote_model_t::GE6711AR2853M:
|
||||
case lg_ac_remote_model_t::LG6711A20083V:
|
||||
_protocol = decode_type_t::LG;
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
_model = model;
|
||||
}
|
||||
|
||||
/// Get the model of the A/C.
|
||||
/// @return The enum of the compatible model.
|
||||
lg_ac_remote_model_t IRLgAc::getModel(void) const { return _model; }
|
||||
|
||||
/// Check if the stored code must belong to a AKB74955603 model.
|
||||
/// @return true, if it is AKB74955603 message. Otherwise, false.
|
||||
/// @note Internal use only.
|
||||
bool IRLgAc::_isAKB74955603(void) const {
|
||||
return ((_.raw & kLgAcAKB74955603DetectionMask) && _isNormal()) ||
|
||||
(isSwingV() && !isSwingVToggle()) || isLightToggle();
|
||||
}
|
||||
|
||||
/// Check if the stored code must belong to a AKB73757604 model.
|
||||
/// @return true, if it is AKB73757604 message. Otherwise, false.
|
||||
/// @note Internal use only.
|
||||
bool IRLgAc::_isAKB73757604(void) const { return isSwingH() || isVaneSwingV(); }
|
||||
|
||||
/// Check if the stored code must belong to a LG6711A20083V model.
|
||||
/// @return true, if it is LG6711A20083V message. Otherwise, false.
|
||||
/// @note Internal use only.
|
||||
bool IRLgAc::_isLG6711A20083V(void) const { return isSwingVToggle(); }
|
||||
|
||||
/// Get a copy of the internal state/code for this protocol.
|
||||
/// @return The code for this protocol based on the current internal state.
|
||||
uint32_t IRLgAc::getRaw(void) {
|
||||
checksum();
|
||||
return _.raw;
|
||||
}
|
||||
|
||||
/// Set the internal state from a valid code for this protocol.
|
||||
/// @param[in] new_code A valid code for this protocol.
|
||||
/// @param[in] protocol A valid decode protocol type to use.
|
||||
void IRLgAc::setRaw(const uint32_t new_code, const decode_type_t protocol) {
|
||||
_.raw = new_code;
|
||||
// Set the default model for this protocol, if the protocol is supplied.
|
||||
switch (protocol) {
|
||||
case decode_type_t::LG:
|
||||
if (isSwingVToggle()) // This model uses a swingv toggle message.
|
||||
setModel(lg_ac_remote_model_t::LG6711A20083V);
|
||||
else // Assume others are a different model.
|
||||
setModel(lg_ac_remote_model_t::GE6711AR2853M);
|
||||
break;
|
||||
case decode_type_t::LG2:
|
||||
setModel(lg_ac_remote_model_t::AKB75215403);
|
||||
break;
|
||||
default:
|
||||
// Don't change anything if it isn't an expected protocol.
|
||||
break;
|
||||
}
|
||||
// Look for model specific settings/features to improve model detection.
|
||||
if (_isAKB74955603()) {
|
||||
setModel(lg_ac_remote_model_t::AKB74955603);
|
||||
if (isSwingV()) _swingv = new_code;
|
||||
}
|
||||
if (_isAKB73757604()) {
|
||||
setModel(lg_ac_remote_model_t::AKB73757604);
|
||||
if (isVaneSwingV()) {
|
||||
// Extract just the vane nr and position part of the message.
|
||||
const uint32_t vanecode = getVaneCode(_.raw);
|
||||
_vaneswingv[vanecode / kLgAcVaneSwingVSize] = VANESWINGVPOS(vanecode);
|
||||
} else if (isSwingH()) {
|
||||
_swingh = (_.raw == kLgAcSwingHAuto);
|
||||
}
|
||||
}
|
||||
_temp = 15; // Ensure there is a "sane" previous temp.
|
||||
_temp = getTemp();
|
||||
}
|
||||
|
||||
/// Calculate the checksum for a given state.
|
||||
/// @param[in] state The value to calc the checksum of.
|
||||
/// @return The calculated checksum value.
|
||||
uint8_t IRLgAc::calcChecksum(const uint32_t state) {
|
||||
return irutils::sumNibbles(state >> kLgAcChecksumSize, 4);
|
||||
}
|
||||
|
||||
/// Verify the checksum is valid for a given state.
|
||||
/// @param[in] state The value to verify the checksum of.
|
||||
/// @return true, if the state has a valid checksum. Otherwise, false.
|
||||
bool IRLgAc::validChecksum(const uint32_t state) {
|
||||
LGProtocol LGp;
|
||||
LGp.raw = state;
|
||||
return calcChecksum(state) == LGp.Sum;
|
||||
}
|
||||
|
||||
/// Calculate and set the checksum values for the internal state.
|
||||
void IRLgAc::checksum(void) {
|
||||
_.Sum = calcChecksum(_.raw);
|
||||
}
|
||||
|
||||
/// Change the power setting to On.
|
||||
void IRLgAc::on(void) { setPower(true); }
|
||||
|
||||
/// Change the power setting to Off.
|
||||
void IRLgAc::off(void) { setPower(false); }
|
||||
|
||||
/// Change the power setting.
|
||||
/// @param[in] on true, the setting is on. false, the setting is off.
|
||||
void IRLgAc::setPower(const bool on) {
|
||||
_.Power = (on ? kLgAcPowerOn : kLgAcPowerOff);
|
||||
if (on)
|
||||
setTemp(_temp); // Reset the temp if we are on.
|
||||
else
|
||||
_setTemp(0); // Off clears the temp.
|
||||
}
|
||||
|
||||
/// Get the value of the current power setting.
|
||||
/// @return true, the setting is on. false, the setting is off.
|
||||
bool IRLgAc::getPower(void) const {
|
||||
return _.Power == kLgAcPowerOn;
|
||||
}
|
||||
|
||||
/// Is the message a Power Off message?
|
||||
/// @return true, if it is. false, if not.
|
||||
bool IRLgAc::isOffCommand(void) const { return _.raw == kLgAcOffCommand; }
|
||||
|
||||
/// Change the light/led/display setting.
|
||||
/// @param[in] on true, the setting is on. false, the setting is off.
|
||||
void IRLgAc::setLight(const bool on) { _light = on; }
|
||||
|
||||
/// Get the value of the current light setting.
|
||||
/// @return true, the setting is on. false, the setting is off.
|
||||
bool IRLgAc::getLight(void) const { return _light; }
|
||||
|
||||
/// Is the message a Light Toggle message?
|
||||
/// @return true, if it is. false, if not.
|
||||
bool IRLgAc::isLightToggle(void) const { return _.raw == kLgAcLightToggle; }
|
||||
|
||||
/// Set the temperature.
|
||||
/// @param[in] value The native temperature.
|
||||
/// @note Internal use only.
|
||||
inline void IRLgAc::_setTemp(const uint8_t value) { _.Temp = value; }
|
||||
|
||||
/// Set the temperature.
|
||||
/// @param[in] degrees The temperature in degrees celsius.
|
||||
void IRLgAc::setTemp(const uint8_t degrees) {
|
||||
uint8_t temp = std::max(kLgAcMinTemp, degrees);
|
||||
temp = std::min(kLgAcMaxTemp, temp);
|
||||
_temp = temp;
|
||||
_setTemp(temp - kLgAcTempAdjust);
|
||||
}
|
||||
|
||||
/// Get the current temperature setting.
|
||||
/// @return The current setting for temp. in degrees celsius.
|
||||
uint8_t IRLgAc::getTemp(void) const {
|
||||
return _isNormal() ? _.Temp + kLgAcTempAdjust : _temp;
|
||||
}
|
||||
|
||||
/// Set the speed of the fan.
|
||||
/// @param[in] speed The desired setting.
|
||||
void IRLgAc::setFan(const uint8_t speed) {
|
||||
uint8_t _speed = speed;
|
||||
// Only model AKB74955603 has these speeds, so convert if we have to.
|
||||
if (getModel() != lg_ac_remote_model_t::AKB74955603) {
|
||||
switch (speed) {
|
||||
case kLgAcFanLowAlt:
|
||||
_.Fan = kLgAcFanLow;
|
||||
return;
|
||||
case kLgAcFanHigh:
|
||||
_.Fan = kLgAcFanMax;
|
||||
return;
|
||||
}
|
||||
}
|
||||
switch (speed) {
|
||||
case kLgAcFanLow:
|
||||
case kLgAcFanLowAlt:
|
||||
_speed = (getModel() != lg_ac_remote_model_t::AKB74955603)
|
||||
? kLgAcFanLow : kLgAcFanLowAlt;
|
||||
break;
|
||||
case kLgAcFanHigh:
|
||||
_speed = (getModel() != lg_ac_remote_model_t::AKB74955603)
|
||||
? kLgAcFanMax : speed;
|
||||
break;
|
||||
case kLgAcFanAuto:
|
||||
case kLgAcFanLowest:
|
||||
case kLgAcFanMedium:
|
||||
case kLgAcFanMax:
|
||||
_speed = speed;
|
||||
break;
|
||||
default:
|
||||
_speed = kLgAcFanAuto;
|
||||
}
|
||||
_.Fan = _speed;
|
||||
}
|
||||
|
||||
/// Get the current fan speed setting.
|
||||
/// @return The current fan speed.
|
||||
uint8_t IRLgAc::getFan(void) const { return _.Fan; }
|
||||
|
||||
/// Get the operating mode setting of the A/C.
|
||||
/// @return The current operating mode setting.
|
||||
uint8_t IRLgAc::getMode(void) const { return _.Mode; }
|
||||
|
||||
/// Set the operating mode of the A/C.
|
||||
/// @param[in] mode The desired operating mode.
|
||||
void IRLgAc::setMode(const uint8_t mode) {
|
||||
switch (mode) {
|
||||
case kLgAcAuto:
|
||||
case kLgAcDry:
|
||||
case kLgAcHeat:
|
||||
case kLgAcCool:
|
||||
case kLgAcFan:
|
||||
_.Mode = mode;
|
||||
break;
|
||||
default:
|
||||
_.Mode = kLgAcAuto;
|
||||
}
|
||||
}
|
||||
|
||||
/// Check if the stored code is a SwingV Toggle message.
|
||||
/// @return true, if it is. Otherwise, false.
|
||||
bool IRLgAc::isSwingVToggle(void) const { return _.raw == kLgAcSwingVToggle; }
|
||||
|
||||
/// Check if the stored code is a Swing message.
|
||||
/// @return true, if it is. Otherwise, false.
|
||||
bool IRLgAc::isSwing(void) const {
|
||||
return ((_.raw >> 12) == kLgAcSwingSignature) || isSwingVToggle();
|
||||
}
|
||||
|
||||
/// Check if the stored code is a non-vane SwingV message.
|
||||
/// @return true, if it is. Otherwise, false.
|
||||
bool IRLgAc::isSwingV(void) const {
|
||||
const uint32_t code = _.raw >> kLgAcChecksumSize;
|
||||
return (code >= (kLgAcSwingVLowest >> kLgAcChecksumSize) &&
|
||||
code < (kLgAcSwingHAuto >> kLgAcChecksumSize)) || isSwingVToggle();
|
||||
}
|
||||
|
||||
/// Check if the stored code is a SwingH message.
|
||||
/// @return true, if it is. Otherwise, false.
|
||||
bool IRLgAc::isSwingH(void) const {
|
||||
return (_.raw >> kLgAcSwingHOffsetSize) == kLgAcSwingHSignature;
|
||||
}
|
||||
|
||||
/// Get the Horizontal Swing position setting of the A/C.
|
||||
/// @return true, if it is. Otherwise, false.
|
||||
bool IRLgAc::getSwingH(void) const { return _swingh; }
|
||||
|
||||
/// Set the Horizontal Swing mode of the A/C.
|
||||
/// @param[in] on true, the setting is on. false, the setting is off.
|
||||
void IRLgAc::setSwingH(const bool on) { _swingh = on; }
|
||||
|
||||
/// Check if the stored code is a vane specific SwingV message.
|
||||
/// @return true, if it is. Otherwise, false.
|
||||
bool IRLgAc::isVaneSwingV(void) const {
|
||||
return _.raw > kLgAcVaneSwingVBase &&
|
||||
_.raw < (kLgAcVaneSwingVBase +
|
||||
((kLgAcSwingVMaxVanes *
|
||||
kLgAcVaneSwingVSize) << kLgAcChecksumSize));
|
||||
}
|
||||
|
||||
/// Set the Vertical Swing mode of the A/C.
|
||||
/// @param[in] position The position/mode to set the vanes to.
|
||||
void IRLgAc::setSwingV(const uint32_t position) {
|
||||
// Is it a valid position code?
|
||||
if (position == kLgAcSwingVOff || position == kLgAcSwingVToggle ||
|
||||
toCommonSwingV(position) != stdAc::swingv_t::kOff) {
|
||||
if (position <= 0xFF) { // It's a short code, convert it.
|
||||
_swingv = (kLgAcSwingSignature << 8 | position) << kLgAcChecksumSize;
|
||||
_swingv |= calcChecksum(_swingv);
|
||||
} else {
|
||||
_swingv = position;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Copy the previous swing settings from the current ones.
|
||||
void IRLgAc::updateSwingPrev(void) {
|
||||
_swingv_prev = _swingv;
|
||||
for (uint8_t i = 0; i < kLgAcSwingVMaxVanes; i++)
|
||||
_vaneswingv_prev[i] = _vaneswingv[i];
|
||||
}
|
||||
|
||||
/// Get the Vertical Swing position setting of the A/C.
|
||||
/// @return The native position/mode.
|
||||
uint32_t IRLgAc::getSwingV(void) const { return _swingv; }
|
||||
|
||||
/// Set the per Vane Vertical Swing mode of the A/C.
|
||||
/// @param[in] vane The nr. of the vane to control.
|
||||
/// @param[in] position The position/mode to set the vanes to.
|
||||
void IRLgAc::setVaneSwingV(const uint8_t vane, const uint8_t position) {
|
||||
if (vane < kLgAcSwingVMaxVanes) // It's a valid vane nr.
|
||||
if (position && position <= kLgAcVaneSwingVLowest) // Valid position
|
||||
_vaneswingv[vane] = position;
|
||||
}
|
||||
|
||||
/// Get the Vertical Swing position for the given vane of the A/C.
|
||||
/// @return The native position/mode.
|
||||
uint8_t IRLgAc::getVaneSwingV(const uint8_t vane) const {
|
||||
return (vane < kLgAcSwingVMaxVanes) ? _vaneswingv[vane] : 0;
|
||||
}
|
||||
|
||||
/// Get the vane code of a Vane Vertical Swing message.
|
||||
/// @param[in] raw A raw number representing a native LG message.
|
||||
/// @return A number containing just the vane nr, and the position.
|
||||
uint8_t IRLgAc::getVaneCode(const uint32_t raw) {
|
||||
return (raw - kLgAcVaneSwingVBase) >> kLgAcChecksumSize;
|
||||
}
|
||||
|
||||
/// Calculate the Vane specific Vertical Swing code for the A/C.
|
||||
/// @return The native raw code.
|
||||
uint32_t IRLgAc::calcVaneSwingV(const uint8_t vane, const uint8_t position) {
|
||||
uint32_t result = kLgAcVaneSwingVBase;
|
||||
if (vane < kLgAcSwingVMaxVanes) // It's a valid vane nr.
|
||||
if (position && position <= kLgAcVaneSwingVLowest) // Valid position
|
||||
result += ((vane * kLgAcVaneSwingVSize + position) << kLgAcChecksumSize);
|
||||
return result | calcChecksum(result);
|
||||
}
|
||||
|
||||
/// Convert a stdAc::opmode_t enum into its native mode.
|
||||
/// @param[in] mode The enum to be converted.
|
||||
/// @return The native equivalent of the enum.
|
||||
uint8_t IRLgAc::convertMode(const stdAc::opmode_t mode) {
|
||||
switch (mode) {
|
||||
case stdAc::opmode_t::kCool: return kLgAcCool;
|
||||
case stdAc::opmode_t::kHeat: return kLgAcHeat;
|
||||
case stdAc::opmode_t::kFan: return kLgAcFan;
|
||||
case stdAc::opmode_t::kDry: return kLgAcDry;
|
||||
default: return kLgAcAuto;
|
||||
}
|
||||
}
|
||||
|
||||
/// Convert a native mode into its stdAc equivalent.
|
||||
/// @param[in] mode The native setting to be converted.
|
||||
/// @return The stdAc equivalent of the native setting.
|
||||
stdAc::opmode_t IRLgAc::toCommonMode(const uint8_t mode) {
|
||||
switch (mode) {
|
||||
case kLgAcCool: return stdAc::opmode_t::kCool;
|
||||
case kLgAcHeat: return stdAc::opmode_t::kHeat;
|
||||
case kLgAcDry: return stdAc::opmode_t::kDry;
|
||||
case kLgAcFan: return stdAc::opmode_t::kFan;
|
||||
default: return stdAc::opmode_t::kAuto;
|
||||
}
|
||||
}
|
||||
|
||||
/// Convert a stdAc::fanspeed_t enum into it's native speed.
|
||||
/// @param[in] speed The enum to be converted.
|
||||
/// @return The native equivalent of the enum.
|
||||
uint8_t IRLgAc::convertFan(const stdAc::fanspeed_t speed) {
|
||||
switch (speed) {
|
||||
case stdAc::fanspeed_t::kMin: return kLgAcFanLowest;
|
||||
case stdAc::fanspeed_t::kLow: return kLgAcFanLow;
|
||||
case stdAc::fanspeed_t::kMedium: return kLgAcFanMedium;
|
||||
case stdAc::fanspeed_t::kHigh: return kLgAcFanHigh;
|
||||
case stdAc::fanspeed_t::kMax: return kLgAcFanMax;
|
||||
default: return kLgAcFanAuto;
|
||||
}
|
||||
}
|
||||
|
||||
/// Convert a native fan speed into its stdAc equivalent.
|
||||
/// @param[in] speed The native setting to be converted.
|
||||
/// @return The stdAc equivalent of the native setting.
|
||||
stdAc::fanspeed_t IRLgAc::toCommonFanSpeed(const uint8_t speed) {
|
||||
switch (speed) {
|
||||
case kLgAcFanMax: return stdAc::fanspeed_t::kMax;
|
||||
case kLgAcFanHigh: return stdAc::fanspeed_t::kHigh;
|
||||
case kLgAcFanMedium: return stdAc::fanspeed_t::kMedium;
|
||||
case kLgAcFanLow:
|
||||
case kLgAcFanLowAlt: return stdAc::fanspeed_t::kLow;
|
||||
case kLgAcFanLowest: return stdAc::fanspeed_t::kMin;
|
||||
default: return stdAc::fanspeed_t::kAuto;
|
||||
}
|
||||
}
|
||||
|
||||
/// Convert a stdAc::swingv_t enum into it's native setting.
|
||||
/// @param[in] swingv The enum to be converted.
|
||||
/// @return The native equivalent of the enum.
|
||||
uint32_t IRLgAc::convertSwingV(const stdAc::swingv_t swingv) {
|
||||
switch (swingv) {
|
||||
case stdAc::swingv_t::kHighest: return kLgAcSwingVHighest;
|
||||
case stdAc::swingv_t::kHigh: return kLgAcSwingVHigh;
|
||||
case stdAc::swingv_t::kMiddle: return kLgAcSwingVMiddle;
|
||||
case stdAc::swingv_t::kLow: return kLgAcSwingVLow;
|
||||
case stdAc::swingv_t::kLowest: return kLgAcSwingVLowest;
|
||||
case stdAc::swingv_t::kAuto: return kLgAcSwingVSwing;
|
||||
default: return kLgAcSwingVOff;
|
||||
}
|
||||
}
|
||||
|
||||
/// Convert a native Vertical Swing into its stdAc equivalent.
|
||||
/// @param[in] code The native code to be converted.
|
||||
/// @return The stdAc equivalent of the native setting.
|
||||
stdAc::swingv_t IRLgAc::toCommonSwingV(const uint32_t code) {
|
||||
switch (code) {
|
||||
case kLgAcSwingVHighest_Short:
|
||||
case kLgAcSwingVHighest: return stdAc::swingv_t::kHighest;
|
||||
case kLgAcSwingVHigh_Short:
|
||||
case kLgAcSwingVHigh: return stdAc::swingv_t::kHigh;
|
||||
case kLgAcSwingVUpperMiddle_Short:
|
||||
case kLgAcSwingVUpperMiddle:
|
||||
case kLgAcSwingVMiddle_Short:
|
||||
case kLgAcSwingVMiddle: return stdAc::swingv_t::kMiddle;
|
||||
case kLgAcSwingVLow_Short:
|
||||
case kLgAcSwingVLow: return stdAc::swingv_t::kLow;
|
||||
case kLgAcSwingVLowest_Short:
|
||||
case kLgAcSwingVLowest: return stdAc::swingv_t::kLowest;
|
||||
case kLgAcSwingVToggle:
|
||||
case kLgAcSwingVSwing_Short:
|
||||
case kLgAcSwingVSwing: return stdAc::swingv_t::kAuto;
|
||||
default: return stdAc::swingv_t::kOff;
|
||||
}
|
||||
}
|
||||
|
||||
/// Convert a native Vane specific Vertical Swing into its stdAc equivalent.
|
||||
/// @param[in] pos The native position to be converted.
|
||||
/// @return The stdAc equivalent of the native setting.
|
||||
stdAc::swingv_t IRLgAc::toCommonVaneSwingV(const uint8_t pos) {
|
||||
switch (pos) {
|
||||
case kLgAcVaneSwingVHigh: return stdAc::swingv_t::kHigh;
|
||||
case kLgAcVaneSwingVUpperMiddle:
|
||||
case kLgAcVaneSwingVMiddle: return stdAc::swingv_t::kMiddle;
|
||||
case kLgAcVaneSwingVLow: return stdAc::swingv_t::kLow;
|
||||
case kLgAcVaneSwingVLowest: return stdAc::swingv_t::kLowest;
|
||||
default: return stdAc::swingv_t::kHighest;
|
||||
}
|
||||
}
|
||||
|
||||
/// Convert a stdAc::swingv_t enum into it's native setting.
|
||||
/// @param[in] swingv The enum to be converted.
|
||||
/// @return The native equivalent of the enum.
|
||||
uint8_t IRLgAc::convertVaneSwingV(const stdAc::swingv_t swingv) {
|
||||
switch (swingv) {
|
||||
case stdAc::swingv_t::kHigh: return kLgAcVaneSwingVHigh;
|
||||
case stdAc::swingv_t::kMiddle: return kLgAcVaneSwingVMiddle;
|
||||
case stdAc::swingv_t::kLow: return kLgAcVaneSwingVLow;
|
||||
case stdAc::swingv_t::kLowest: return kLgAcVaneSwingVLowest;
|
||||
default: return kLgAcVaneSwingVHighest;
|
||||
}
|
||||
}
|
||||
|
||||
/// Convert the current internal state into its stdAc::state_t equivalent.
|
||||
/// @param[in] prev Ptr to the previous state if required.
|
||||
/// @return The stdAc equivalent of the native settings.
|
||||
stdAc::state_t IRLgAc::toCommon(const stdAc::state_t *prev) const {
|
||||
stdAc::state_t result{};
|
||||
// Start with the previous state if given it.
|
||||
if (prev != NULL) {
|
||||
result = *prev;
|
||||
} else {
|
||||
// Set defaults for non-zero values that are not implicitly set for when
|
||||
// there is no previous state.
|
||||
// e.g. Any setting that toggles should probably go here.
|
||||
result.light = true;
|
||||
result.swingv = toCommonSwingV(getSwingV());
|
||||
}
|
||||
result.protocol = _protocol;
|
||||
if (isLightToggle()) {
|
||||
result.light = !result.light;
|
||||
return result;
|
||||
} else {
|
||||
result.light = _light;
|
||||
}
|
||||
result.model = getModel();
|
||||
result.power = getPower();
|
||||
result.mode = toCommonMode(_.Mode);
|
||||
result.celsius = true;
|
||||
result.degrees = getTemp();
|
||||
result.fanspeed = toCommonFanSpeed(_.Fan);
|
||||
if (isSwingV()) result.swingv = toCommonSwingV(getSwingV());
|
||||
if (isVaneSwingV())
|
||||
result.swingv = toCommonVaneSwingV(VANESWINGVPOS(getVaneCode(_.raw)));
|
||||
result.swingh = isSwingH() ? stdAc::swingh_t::kAuto : stdAc::swingh_t::kOff;
|
||||
// Not supported.
|
||||
result.quiet = false;
|
||||
result.turbo = false;
|
||||
result.filter = false;
|
||||
result.clean = false;
|
||||
result.econo = false;
|
||||
result.beep = false;
|
||||
result.sleep = -1;
|
||||
result.clock = -1;
|
||||
return result;
|
||||
}
|
||||
|
||||
/// Convert the current internal state into a human readable string.
|
||||
/// @return A human readable string.
|
||||
String IRLgAc::toString(void) const {
|
||||
String result = "";
|
||||
result.reserve(80); // Reserve some heap for the string to reduce fragging.
|
||||
result += addModelToString(_protocol, getModel(), false);
|
||||
if (_isNormal()) { // A "Normal" generic settings message.
|
||||
result += addBoolToString(getPower(), kPowerStr);
|
||||
if (getPower()) { // Only display the rest if is in power on state.
|
||||
result += addModeToString(_.Mode, kLgAcAuto, kLgAcCool,
|
||||
kLgAcHeat, kLgAcDry, kLgAcFan);
|
||||
result += addTempToString(getTemp());
|
||||
result += addFanToString(_.Fan, kLgAcFanHigh,
|
||||
_isAKB74955603() ? kLgAcFanLowAlt : kLgAcFanLow,
|
||||
kLgAcFanAuto, kLgAcFanLowest, kLgAcFanMedium,
|
||||
kLgAcFanMax);
|
||||
}
|
||||
} else { // It must be a special single purpose code.
|
||||
if (isOffCommand()) {
|
||||
result += addBoolToString(false, kPowerStr);
|
||||
} else if (isLightToggle()) {
|
||||
result += addBoolToString(true, kLightToggleStr);
|
||||
} else if (isSwingH()) {
|
||||
result += addBoolToString(_swingh, kSwingHStr);
|
||||
} else if (isSwingV()) {
|
||||
if (isSwingVToggle())
|
||||
result += addToggleToString(isSwingVToggle(), kSwingVStr);
|
||||
else
|
||||
result += addSwingVToString((uint8_t)(_swingv >> kLgAcChecksumSize),
|
||||
0, // No Auto, See "swing". Unused
|
||||
kLgAcSwingVHighest_Short,
|
||||
kLgAcSwingVHigh_Short,
|
||||
kLgAcSwingVUpperMiddle_Short,
|
||||
kLgAcSwingVMiddle_Short,
|
||||
0, // Unused
|
||||
kLgAcSwingVLow_Short,
|
||||
kLgAcSwingVLowest_Short,
|
||||
kLgAcSwingVOff_Short,
|
||||
kLgAcSwingVSwing_Short,
|
||||
0, 0);
|
||||
} else if (isVaneSwingV()) {
|
||||
const uint8_t vane = getVaneCode(_.raw) / kLgAcVaneSwingVSize;
|
||||
result += addIntToString(vane, kVaneStr);
|
||||
result += addSwingVToString(_vaneswingv[vane],
|
||||
0, // No Auto, See "swing". Unused
|
||||
kLgAcVaneSwingVHighest,
|
||||
kLgAcVaneSwingVHigh,
|
||||
kLgAcVaneSwingVUpperMiddle,
|
||||
kLgAcVaneSwingVMiddle,
|
||||
0, // Unused
|
||||
kLgAcVaneSwingVLow,
|
||||
kLgAcVaneSwingVLowest,
|
||||
// Rest unused
|
||||
0, 0, 0, 0);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/// Check if the internal state looks like a valid LG A/C message.
|
||||
/// @return true, the internal state is a valid LG A/C mesg. Otherwise, false.
|
||||
bool IRLgAc::isValidLgAc(void) const {
|
||||
return validChecksum(_.raw) && (_.Sign == kLgAcSignature);
|
||||
}
|
||||
202
yoRadio/src/IRremoteESP8266/ir_LG.h
Normal file
202
yoRadio/src/IRremoteESP8266/ir_LG.h
Normal file
@@ -0,0 +1,202 @@
|
||||
// Copyright 2017-2021 David Conran
|
||||
|
||||
/// @file
|
||||
/// @brief Support for LG protocols.
|
||||
/// @see https://github.com/arendst/Tasmota/blob/54c2eb283a02e4287640a4595e506bc6eadbd7f2/sonoff/xdrv_05_irremote.ino#L327-438
|
||||
/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1513
|
||||
|
||||
// Supports:
|
||||
// Brand: LG, Model: 6711A20083V remote (LG - LG6711A20083V)
|
||||
// Brand: LG, Model: TS-H122ERM1 remote (LG - LG6711A20083V)
|
||||
// Brand: LG, Model: AKB74395308 remote (LG2)
|
||||
// Brand: LG, Model: S4-W12JA3AA A/C (LG2)
|
||||
// Brand: LG, Model: AKB75215403 remote (LG2)
|
||||
// Brand: LG, Model: AKB74955603 remote (LG2 - AKB74955603)
|
||||
// Brand: LG, Model: A4UW30GFA2 A/C (LG2 - AKB74955603 & AKB73757604)
|
||||
// Brand: LG, Model: AMNW09GSJA0 A/C (LG2 - AKB74955603)
|
||||
// Brand: LG, Model: AMNW24GTPA1 A/C (LG2 - AKB73757604)
|
||||
// Brand: LG, Model: AKB73757604 remote (LG2 - AKB73757604)
|
||||
// Brand: LG, Model: AKB73315611 remote (LG2 - AKB74955603)
|
||||
// Brand: LG, Model: MS05SQ NW0 A/C (LG2 - AKB74955603)
|
||||
// Brand: General Electric, Model: AG1BH09AW101 A/C (LG - GE6711AR2853M)
|
||||
// Brand: General Electric, Model: 6711AR2853M Remote (LG - GE6711AR2853M)
|
||||
|
||||
#ifndef IR_LG_H_
|
||||
#define IR_LG_H_
|
||||
|
||||
#define __STDC_LIMIT_MACROS
|
||||
#include <stdint.h>
|
||||
#ifndef UNIT_TEST
|
||||
#include <Arduino.h>
|
||||
#endif
|
||||
#include "IRremoteESP8266.h"
|
||||
#include "IRsend.h"
|
||||
#include "IRutils.h"
|
||||
#ifdef UNIT_TEST
|
||||
#include "IRsend_test.h"
|
||||
#endif
|
||||
|
||||
/// Native representation of a LG A/C message.
|
||||
union LGProtocol{
|
||||
uint32_t raw; ///< The state of the IR remote in IR code form.
|
||||
struct {
|
||||
uint32_t Sum :4;
|
||||
uint32_t Fan :4;
|
||||
uint32_t Temp :4;
|
||||
uint32_t Mode :3;
|
||||
uint32_t :3;
|
||||
uint32_t Power:2;
|
||||
uint32_t Sign :8;
|
||||
};
|
||||
};
|
||||
|
||||
const uint8_t kLgAcFanLowest = 0; // 0b0000
|
||||
const uint8_t kLgAcFanLow = 1; // 0b0001
|
||||
const uint8_t kLgAcFanMedium = 2; // 0b0010
|
||||
const uint8_t kLgAcFanMax = 4; // 0b0100
|
||||
const uint8_t kLgAcFanAuto = 5; // 0b0101
|
||||
const uint8_t kLgAcFanLowAlt = 9; // 0b1001
|
||||
const uint8_t kLgAcFanHigh = 10; // 0b1010
|
||||
// Nr. of slots in the look-up table
|
||||
const uint8_t kLgAcFanEntries = kLgAcFanHigh + 1;
|
||||
const uint8_t kLgAcTempAdjust = 15;
|
||||
const uint8_t kLgAcMinTemp = 16; // Celsius
|
||||
const uint8_t kLgAcMaxTemp = 30; // Celsius
|
||||
const uint8_t kLgAcCool = 0; // 0b000
|
||||
const uint8_t kLgAcDry = 1; // 0b001
|
||||
const uint8_t kLgAcFan = 2; // 0b010
|
||||
const uint8_t kLgAcAuto = 3; // 0b011
|
||||
const uint8_t kLgAcHeat = 4; // 0b100
|
||||
const uint8_t kLgAcPowerOff = 3; // 0b11
|
||||
const uint8_t kLgAcPowerOn = 0; // 0b00
|
||||
const uint8_t kLgAcSignature = 0x88;
|
||||
|
||||
const uint32_t kLgAcOffCommand = 0x88C0051;
|
||||
const uint32_t kLgAcLightToggle = 0x88C00A6;
|
||||
|
||||
const uint32_t kLgAcSwingVToggle = 0x8810001;
|
||||
const uint32_t kLgAcSwingSignature = 0x8813;
|
||||
const uint32_t kLgAcSwingVLowest = 0x8813048;
|
||||
const uint32_t kLgAcSwingVLow = 0x8813059;
|
||||
const uint32_t kLgAcSwingVMiddle = 0x881306A;
|
||||
const uint32_t kLgAcSwingVUpperMiddle = 0x881307B;
|
||||
const uint32_t kLgAcSwingVHigh = 0x881308C;
|
||||
const uint32_t kLgAcSwingVHighest = 0x881309D;
|
||||
const uint32_t kLgAcSwingVSwing = 0x8813149;
|
||||
const uint32_t kLgAcSwingVAuto = kLgAcSwingVSwing;
|
||||
const uint32_t kLgAcSwingVOff = 0x881315A;
|
||||
const uint8_t kLgAcSwingVLowest_Short = 0x04;
|
||||
const uint8_t kLgAcSwingVLow_Short = 0x05;
|
||||
const uint8_t kLgAcSwingVMiddle_Short = 0x06;
|
||||
const uint8_t kLgAcSwingVUpperMiddle_Short = 0x07;
|
||||
const uint8_t kLgAcSwingVHigh_Short = 0x08;
|
||||
const uint8_t kLgAcSwingVHighest_Short = 0x09;
|
||||
const uint8_t kLgAcSwingVSwing_Short = 0x14;
|
||||
const uint8_t kLgAcSwingVAuto_Short = kLgAcSwingVSwing_Short;
|
||||
const uint8_t kLgAcSwingVOff_Short = 0x15;
|
||||
|
||||
// AKB73757604 Constants
|
||||
// SwingH
|
||||
const uint32_t kLgAcSwingHAuto = 0x881316B;
|
||||
const uint32_t kLgAcSwingHOff = 0x881317C;
|
||||
// SwingV
|
||||
const uint8_t kLgAcVaneSwingVHighest = 1; ///< 0b001
|
||||
const uint8_t kLgAcVaneSwingVHigh = 2; ///< 0b010
|
||||
const uint8_t kLgAcVaneSwingVUpperMiddle = 3; ///< 0b011
|
||||
const uint8_t kLgAcVaneSwingVMiddle = 4; ///< 0b100
|
||||
const uint8_t kLgAcVaneSwingVLow = 5; ///< 0b101
|
||||
const uint8_t kLgAcVaneSwingVLowest = 6; ///< 0b110
|
||||
const uint8_t kLgAcVaneSwingVSize = 8;
|
||||
const uint8_t kLgAcSwingVMaxVanes = 4; ///< Max Nr. of Vanes
|
||||
|
||||
// Classes
|
||||
/// Class for handling detailed LG A/C messages.
|
||||
class IRLgAc {
|
||||
public:
|
||||
explicit IRLgAc(const uint16_t pin, const bool inverted = false,
|
||||
const bool use_modulation = true);
|
||||
void stateReset(void);
|
||||
static uint8_t calcChecksum(const uint32_t state);
|
||||
static bool validChecksum(const uint32_t state);
|
||||
bool isValidLgAc(void) const;
|
||||
#if SEND_LG
|
||||
void send(const uint16_t repeat = kLgDefaultRepeat);
|
||||
/// 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_LG
|
||||
void begin(void);
|
||||
void on(void);
|
||||
void off(void);
|
||||
void setPower(const bool on);
|
||||
bool getPower(void) const;
|
||||
bool isOffCommand(void) const;
|
||||
void setTemp(const uint8_t degrees);
|
||||
uint8_t getTemp(void) const;
|
||||
void setFan(const uint8_t speed);
|
||||
uint8_t getFan(void) const;
|
||||
void setMode(const uint8_t mode);
|
||||
uint8_t getMode(void) const;
|
||||
void setLight(const bool on);
|
||||
bool getLight(void) const;
|
||||
bool isLightToggle(void) const;
|
||||
bool isSwing(void) const;
|
||||
void setSwingH(const bool on);
|
||||
bool getSwingH(void) const;
|
||||
bool isSwingV(void) const;
|
||||
bool isSwingVToggle(void) const;
|
||||
bool isVaneSwingV(void) const;
|
||||
void setSwingV(const uint32_t position);
|
||||
uint32_t getSwingV(void) const;
|
||||
void setVaneSwingV(const uint8_t vane, const uint8_t position);
|
||||
uint8_t getVaneSwingV(const uint8_t vane) const;
|
||||
static uint32_t calcVaneSwingV(const uint8_t vane, const uint8_t position);
|
||||
static uint8_t getVaneCode(const uint32_t raw);
|
||||
bool isSwingH(void) const;
|
||||
void updateSwingPrev(void);
|
||||
uint32_t getRaw(void);
|
||||
void setRaw(const uint32_t new_code,
|
||||
const decode_type_t protocol = decode_type_t::UNKNOWN);
|
||||
static uint8_t convertMode(const stdAc::opmode_t mode);
|
||||
static stdAc::opmode_t toCommonMode(const uint8_t mode);
|
||||
static stdAc::fanspeed_t toCommonFanSpeed(const uint8_t speed);
|
||||
static stdAc::swingv_t toCommonSwingV(const uint32_t code);
|
||||
static stdAc::swingv_t toCommonVaneSwingV(const uint8_t pos);
|
||||
static uint8_t convertFan(const stdAc::fanspeed_t speed);
|
||||
static uint32_t convertSwingV(const stdAc::swingv_t swingv);
|
||||
static uint8_t convertVaneSwingV(const stdAc::swingv_t swingv);
|
||||
stdAc::state_t toCommon(const stdAc::state_t *prev = NULL) const;
|
||||
String toString(void) const;
|
||||
void setModel(const lg_ac_remote_model_t model);
|
||||
lg_ac_remote_model_t getModel(void) const;
|
||||
#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
|
||||
LGProtocol _;
|
||||
uint8_t _temp;
|
||||
bool _light;
|
||||
uint32_t _swingv;
|
||||
uint32_t _swingv_prev;
|
||||
uint8_t _vaneswingv[kLgAcSwingVMaxVanes];
|
||||
uint8_t _vaneswingv_prev[kLgAcSwingVMaxVanes];
|
||||
bool _swingh;
|
||||
bool _swingh_prev;
|
||||
decode_type_t _protocol; ///< Protocol version
|
||||
lg_ac_remote_model_t _model; ///< Model type
|
||||
void checksum(void);
|
||||
void _setTemp(const uint8_t value);
|
||||
bool _isAKB74955603(void) const;
|
||||
bool _isAKB73757604(void) const;
|
||||
bool _isLG6711A20083V(void) const;
|
||||
bool _isNormal(void) const;
|
||||
};
|
||||
|
||||
#endif // IR_LG_H_
|
||||
116
yoRadio/src/IRremoteESP8266/ir_Lasertag.cpp
Normal file
116
yoRadio/src/IRremoteESP8266/ir_Lasertag.cpp
Normal file
@@ -0,0 +1,116 @@
|
||||
// Copyright 2017 David Conran
|
||||
|
||||
/// @file
|
||||
/// @brief Support for Lasertag protocols.
|
||||
/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/366
|
||||
|
||||
// Supports:
|
||||
// Brand: Lasertag, Model: Phaser emitters
|
||||
|
||||
#include <algorithm>
|
||||
#include "IRrecv.h"
|
||||
#include "IRsend.h"
|
||||
#include "IRutils.h"
|
||||
|
||||
// Constants
|
||||
const uint16_t kLasertagMinSamples = 13;
|
||||
const uint16_t kLasertagTick = 333;
|
||||
const uint32_t kLasertagMinGap = kDefaultMessageGap; // Just a guess.
|
||||
const uint8_t kLasertagTolerance = 0; // Percentage error margin.
|
||||
const uint16_t kLasertagExcess = 0; // See kMarkExcess.
|
||||
const uint16_t kLasertagDelta = 165; // Use instead of Excess and Tolerance.
|
||||
const int16_t kSpace = 1;
|
||||
const int16_t kMark = 0;
|
||||
|
||||
#if SEND_LASERTAG
|
||||
/// Send a Lasertag packet/message.
|
||||
/// Status: STABLE / Working.
|
||||
/// @param[in] data The message to be sent.
|
||||
/// @param[in] nbits The number of bits of message to be sent.
|
||||
/// @param[in] repeat The number of times the command is to be repeated.
|
||||
/// @note This protocol is pretty much just raw Manchester encoding.
|
||||
/// @todo Convert this to use `sendManchester()` if we can.`
|
||||
void IRsend::sendLasertag(uint64_t data, uint16_t nbits, uint16_t repeat) {
|
||||
if (nbits > sizeof(data) * 8) return; // We can't send something that big.
|
||||
|
||||
// Set 36kHz IR carrier frequency & a 1/4 (25%) duty cycle.
|
||||
// NOTE: duty cycle is not confirmed. Just guessing based on RC5/6 protocols.
|
||||
enableIROut(36, 25);
|
||||
|
||||
for (uint16_t i = 0; i <= repeat; i++) {
|
||||
// Data
|
||||
for (uint64_t mask = 1ULL << (nbits - 1); mask; mask >>= 1)
|
||||
if (data & mask) { // 1
|
||||
space(kLasertagTick); // 1 is space, then mark.
|
||||
mark(kLasertagTick);
|
||||
} else { // 0
|
||||
mark(kLasertagTick); // 0 is mark, then space.
|
||||
space(kLasertagTick);
|
||||
}
|
||||
// Footer
|
||||
space(kLasertagMinGap);
|
||||
}
|
||||
}
|
||||
#endif // SEND_LASERTAG
|
||||
|
||||
#if DECODE_LASERTAG
|
||||
/// Decode the supplied Lasertag message.
|
||||
/// Status: BETA / Appears to be working 90% of the time.
|
||||
/// @param[in,out] results Ptr to the data to decode & where to store the result
|
||||
/// @param[in] offset The starting index to use when attempting to decode the
|
||||
/// raw data. Typically/Defaults to kStartOffset.
|
||||
/// @param[in] nbits The number of data bits to expect.
|
||||
/// @param[in] strict Flag indicating if we should perform strict matching.
|
||||
/// @return True if it can decode it, false if it can't.
|
||||
/// @note This protocol is pretty much just raw Manchester encoding.
|
||||
/// @see http://www.sbprojects.net/knowledge/ir/rc5.php
|
||||
/// @see https://en.wikipedia.org/wiki/RC-5
|
||||
/// @see https://en.wikipedia.org/wiki/Manchester_code
|
||||
/// @todo Convert to using `matchManchester()` if we can.
|
||||
bool IRrecv::decodeLasertag(decode_results *results, uint16_t offset,
|
||||
const uint16_t nbits, const bool strict) {
|
||||
if (results->rawlen <= kLasertagMinSamples + offset) return false;
|
||||
|
||||
// Compliance
|
||||
if (strict && nbits != kLasertagBits) return false;
|
||||
|
||||
uint16_t used = 0;
|
||||
uint64_t data = 0;
|
||||
uint16_t actual_bits = 0;
|
||||
|
||||
// No Header
|
||||
|
||||
// Data
|
||||
for (; offset <= results->rawlen; actual_bits++) {
|
||||
int16_t levelA =
|
||||
getRClevel(results, &offset, &used, kLasertagTick, kLasertagTolerance,
|
||||
kLasertagExcess, kLasertagDelta);
|
||||
int16_t levelB =
|
||||
getRClevel(results, &offset, &used, kLasertagTick, kLasertagTolerance,
|
||||
kLasertagExcess, kLasertagDelta);
|
||||
if (levelA == kSpace && levelB == kMark) {
|
||||
data = (data << 1) | 1; // 1
|
||||
} else {
|
||||
if (levelA == kMark && levelB == kSpace) {
|
||||
data <<= 1; // 0
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Footer (None)
|
||||
|
||||
// Compliance
|
||||
if (actual_bits < nbits) return false; // Less data than we expected.
|
||||
if (strict && actual_bits != kLasertagBits) return false;
|
||||
|
||||
// Success
|
||||
results->decode_type = LASERTAG;
|
||||
results->value = data;
|
||||
results->address = data & 0xF; // Unit
|
||||
results->command = data >> 4; // Team
|
||||
results->repeat = false;
|
||||
results->bits = actual_bits;
|
||||
return true;
|
||||
}
|
||||
#endif // DECODE_LASERTAG
|
||||
106
yoRadio/src/IRremoteESP8266/ir_Lego.cpp
Normal file
106
yoRadio/src/IRremoteESP8266/ir_Lego.cpp
Normal file
@@ -0,0 +1,106 @@
|
||||
// Copyright 2019 David Conran
|
||||
|
||||
/// @file
|
||||
/// @brief Support for LEGO protocols.
|
||||
/// @note LEGO is a Registrated Trademark of the Lego Group.
|
||||
/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/641
|
||||
/// @see https://github.com/crankyoldgit/IRremoteESP8266/files/2974525/LEGO_Power_Functions_RC_v120.pdf
|
||||
|
||||
// Supports:
|
||||
// Brand: LEGO Power Functions, Model: IR Receiver
|
||||
|
||||
#include <algorithm>
|
||||
#include "IRrecv.h"
|
||||
#include "IRsend.h"
|
||||
#include "IRutils.h"
|
||||
|
||||
|
||||
// Constants
|
||||
const uint16_t kLegoPfBitMark = 158;
|
||||
const uint16_t kLegoPfHdrSpace = 1026;
|
||||
const uint16_t kLegoPfZeroSpace = 263;
|
||||
const uint16_t kLegoPfOneSpace = 553;
|
||||
const uint32_t kLegoPfMinCommandLength = 16000; // 16ms
|
||||
|
||||
|
||||
#if SEND_LEGOPF
|
||||
/// Send a LEGO Power Functions message.
|
||||
/// Status: Beta / Should work.
|
||||
/// @param[in] data The message to be sent.
|
||||
/// @param[in] nbits The number of bits of message to be sent.
|
||||
/// @param[in] repeat The number of times the command is to be repeated.
|
||||
/// @note Non-zero repeats results in at least 5 messages per spec.
|
||||
void IRsend::sendLegoPf(const uint64_t data, const uint16_t nbits,
|
||||
const uint16_t repeat) {
|
||||
uint8_t channelid = ((data >> (nbits - 4)) & 0b11) + 1;
|
||||
if (repeat) {
|
||||
// We are in repeat mode.
|
||||
// Spec says a pause before transmittion.
|
||||
if (channelid < 4) space((4 - channelid) * kLegoPfMinCommandLength);
|
||||
// Spec says there are a minimum of 5 message repeats.
|
||||
for (uint16_t r = 0; r < std::max(repeat, (uint16_t)5); r++) {
|
||||
// Lego has a special repeat mode which repeats a message with varying
|
||||
// start to start times.
|
||||
sendGeneric(kLegoPfBitMark, kLegoPfHdrSpace,
|
||||
kLegoPfBitMark, kLegoPfOneSpace,
|
||||
kLegoPfBitMark, kLegoPfZeroSpace,
|
||||
kLegoPfBitMark, kLegoPfHdrSpace,
|
||||
((r < 2) ? 5 : (6 + 2 * channelid)) * kLegoPfMinCommandLength,
|
||||
data, nbits, 38000, true, 0, kDutyDefault);
|
||||
}
|
||||
} else { // No repeat, just a simple message.
|
||||
sendGeneric(kLegoPfBitMark, kLegoPfHdrSpace,
|
||||
kLegoPfBitMark, kLegoPfOneSpace,
|
||||
kLegoPfBitMark, kLegoPfZeroSpace,
|
||||
kLegoPfBitMark, kLegoPfHdrSpace,
|
||||
kLegoPfMinCommandLength * 5,
|
||||
data, nbits, 38000, true, 0, kDutyDefault);
|
||||
}
|
||||
}
|
||||
#endif // SEND_LEGO
|
||||
|
||||
#if DECODE_LEGOPF
|
||||
/// Decode the supplied LEGO Power Functions message.
|
||||
/// Status: STABLE / Appears to work.
|
||||
/// @param[in,out] results Ptr to the data to decode & where to store the result
|
||||
/// @param[in] offset The starting index to use when attempting to decode the
|
||||
/// raw data. Typically/Defaults to kStartOffset.
|
||||
/// @param[in] nbits The number of data bits to expect.
|
||||
/// @param[in] strict Flag indicating if we should perform strict matching.
|
||||
/// @return True if it can decode it, false if it can't.
|
||||
bool IRrecv::decodeLegoPf(decode_results* results, uint16_t offset,
|
||||
const uint16_t nbits, const bool strict) {
|
||||
// Check if can possibly be a valid LEGO message.
|
||||
if (strict && nbits != kLegoPfBits) return false; // Not what is expected
|
||||
|
||||
uint64_t data = 0;
|
||||
|
||||
// Match Header + Data + Footer
|
||||
if (!matchGeneric(results->rawbuf + offset, &data,
|
||||
results->rawlen - offset, nbits,
|
||||
kLegoPfBitMark, kLegoPfHdrSpace,
|
||||
kLegoPfBitMark, kLegoPfOneSpace,
|
||||
kLegoPfBitMark, kLegoPfZeroSpace,
|
||||
kLegoPfBitMark, kLegoPfMinCommandLength,
|
||||
true)) return false;
|
||||
// Compliance
|
||||
if (strict) {
|
||||
// Verify the Longitudinal Redundancy Check (LRC)
|
||||
uint16_t lrc_data = data;
|
||||
uint8_t lrc = 0xF;
|
||||
for (uint8_t i = 0; i < 4; i++) {
|
||||
lrc ^= (lrc_data & 0xF);
|
||||
lrc_data >>= 4;
|
||||
}
|
||||
if (lrc) return false;
|
||||
}
|
||||
|
||||
// Success
|
||||
results->decode_type = LEGOPF;
|
||||
results->bits = nbits;
|
||||
results->value = data;
|
||||
results->address = ((data >> (nbits - 4)) & 0b11) + 1; // Channel Id
|
||||
results->command = (data >> 4) & 0xFF; // Stuff between Channel Id and LRC.
|
||||
return true;
|
||||
}
|
||||
#endif // DECODE_LEGOPF
|
||||
143
yoRadio/src/IRremoteESP8266/ir_Lutron.cpp
Normal file
143
yoRadio/src/IRremoteESP8266/ir_Lutron.cpp
Normal file
@@ -0,0 +1,143 @@
|
||||
// Copyright 2018 David Conran
|
||||
|
||||
/// @file
|
||||
/// @brief Support for Lutron protocols.
|
||||
/// @note The Lutron protocol uses a sort of Run Length encoding to encode
|
||||
/// its data. There is no header or footer per-se.
|
||||
/// As a mark is the first data we will notice, we always assume the First
|
||||
/// bit of the technically 36-bit protocol is '1'. So it is assumed, and thus
|
||||
/// we only care about the 35 bits of data.
|
||||
/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/515
|
||||
/// @see http://www.lutron.com/TechnicalDocumentLibrary/048158.doc
|
||||
|
||||
// Supports:
|
||||
// Brand: Lutron, Model: SP-HT remote
|
||||
// Brand: Lutron, Model: MIR-ITFS remote
|
||||
// Brand: Lutron, Model: MIR-ITFS-LF remote
|
||||
// Brand: Lutron, Model: MIR-ITFS-F remote
|
||||
|
||||
#define __STDC_LIMIT_MACROS
|
||||
#include <stdint.h>
|
||||
#include <algorithm>
|
||||
#include "IRrecv.h"
|
||||
#include "IRsend.h"
|
||||
#include "IRutils.h"
|
||||
|
||||
|
||||
// Constants
|
||||
const uint16_t kLutronTick = 2288;
|
||||
const uint32_t kLutronGap = 150000; // Completely made up value.
|
||||
const uint16_t kLutronDelta = 400; // +/- 300 usecs.
|
||||
|
||||
#if SEND_LUTRON
|
||||
/// Send a Lutron formatted message.
|
||||
/// Status: Stable / Appears to be working for real devices.
|
||||
/// @param[in] data The message to be sent.
|
||||
/// @param[in] nbits The number of bits of message to be sent.
|
||||
/// @param[in] repeat The number of times the command is to be repeated.
|
||||
/// @note The protocol is really 36 bits long, but the first bit is always a 1.
|
||||
/// So, assume the 1 and only have a normal payload of 35 bits.
|
||||
/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/515
|
||||
void IRsend::sendLutron(uint64_t data, uint16_t nbits, uint16_t repeat) {
|
||||
enableIROut(40000, 40); // 40Khz & 40% dutycycle.
|
||||
for (uint16_t r = 0; r <= repeat; r++) {
|
||||
mark(kLutronTick); // 1st bit is always '1'.
|
||||
// Send the supplied data in MSB First order.
|
||||
for (uint64_t mask = 1ULL << (nbits - 1); mask; mask >>= 1)
|
||||
if (data & mask)
|
||||
mark(kLutronTick); // Send a 1
|
||||
else
|
||||
space(kLutronTick); // Send a 0
|
||||
space(kLutronGap); // Inter-message gap.
|
||||
}
|
||||
}
|
||||
#endif // SEND_LUTRON
|
||||
|
||||
#if DECODE_LUTRON
|
||||
/// Decode the supplied Lutron message.
|
||||
/// Status: STABLE / Working.
|
||||
/// @param[in,out] results Ptr to the data to decode & where to store the result
|
||||
/// @param[in] offset The starting index to use when attempting to decode the
|
||||
/// raw data. Typically/Defaults to kStartOffset.
|
||||
/// @param[in] nbits The number of data bits to expect.
|
||||
/// @param[in] strict Flag indicating if we should perform strict matching.
|
||||
/// @return True if it can decode it, false if it can't.
|
||||
bool IRrecv::decodeLutron(decode_results *results, uint16_t offset,
|
||||
const uint16_t nbits, const bool strict) {
|
||||
// Technically the smallest number of entries for the smallest message is '1'.
|
||||
// i.e. All the bits set to 1, would produce a single huge mark signal.
|
||||
// So no minimum length check is required.
|
||||
if (strict && nbits != kLutronBits)
|
||||
return false; // Not strictly an Lutron message.
|
||||
|
||||
uint64_t data = 0;
|
||||
int16_t bitsSoFar = -1;
|
||||
|
||||
if (nbits > sizeof(data) * 8) return false; // To large to store the data.
|
||||
for (; bitsSoFar < nbits && offset < results->rawlen; offset++) {
|
||||
uint16_t entry = results->rawbuf[offset];
|
||||
// It has to be large enough to qualify as a bit.
|
||||
if (!matchAtLeast(entry, kLutronTick, 0, kLutronDelta)) {
|
||||
DPRINTLN("Entry too small. Aborting.");
|
||||
return false;
|
||||
}
|
||||
// Keep reading bits of the same value until we run out.
|
||||
while (entry != 0 && matchAtLeast(entry, kLutronTick, 0, kLutronDelta)) {
|
||||
bitsSoFar++;
|
||||
DPRINT("Bit: ");
|
||||
DPRINT(bitsSoFar);
|
||||
if (offset % 2) { // Is Odd?
|
||||
data = (data << 1) + 1; // Append a '1'.
|
||||
DPRINTLN(" is a 1.");
|
||||
} else { // Is it Even?
|
||||
data <<= 1; // Append a '0'.
|
||||
DPRINTLN(" is a 0.");
|
||||
if (bitsSoFar == nbits && matchAtLeast(entry, kLutronGap))
|
||||
break; // We've likely reached the end of a message.
|
||||
}
|
||||
// Remove a bit length from the current entry.
|
||||
entry = std::max(entry, (uint16_t)(kLutronTick / kRawTick)) -
|
||||
kLutronTick / kRawTick;
|
||||
}
|
||||
if (offset % 2 && !match(entry, kLutronDelta, 0, kLutronDelta)) {
|
||||
DPRINT("offset = ");
|
||||
DPRINTLN(offset);
|
||||
DPRINT("rawlen = ");
|
||||
DPRINTLN(results->rawlen);
|
||||
DPRINT("entry = ");
|
||||
DPRINTLN(entry);
|
||||
DPRINTLN("Odd Entry has too much left over. Aborting.");
|
||||
return false; // Too much left over to be a good value. Reject it.
|
||||
}
|
||||
if (offset % 2 == 0 && offset <= results->rawlen - 1 &&
|
||||
!matchAtLeast(entry, kLutronDelta, 0, kLutronDelta)) {
|
||||
DPRINT("offset = ");
|
||||
DPRINTLN(offset);
|
||||
DPRINT("rawlen = ");
|
||||
DPRINTLN(results->rawlen);
|
||||
DPRINT("entry = ");
|
||||
DPRINTLN(entry);
|
||||
DPRINTLN("Entry has too much left over. Aborting.");
|
||||
return false; // Too much left over to be a good value. Reject it.
|
||||
}
|
||||
}
|
||||
|
||||
// We got too many bits.
|
||||
if (bitsSoFar > nbits || bitsSoFar < 0) {
|
||||
DPRINTLN("Wrong number of bits found. Aborting.");
|
||||
return false;
|
||||
}
|
||||
// If we got less bits than we were expecting, we need to pad with zeros
|
||||
// until we get the correct number of bits.
|
||||
if (bitsSoFar < nbits) data <<= (nbits - bitsSoFar);
|
||||
|
||||
// Success
|
||||
DPRINTLN("Lutron Success!");
|
||||
results->decode_type = LUTRON;
|
||||
results->bits = bitsSoFar;
|
||||
results->value = data ^ (1ULL << nbits); // Mask off the initial '1'.
|
||||
results->address = 0;
|
||||
results->command = 0;
|
||||
return true;
|
||||
}
|
||||
#endif // DECODE_LUTRON
|
||||
197
yoRadio/src/IRremoteESP8266/ir_MWM.cpp
Normal file
197
yoRadio/src/IRremoteESP8266/ir_MWM.cpp
Normal file
@@ -0,0 +1,197 @@
|
||||
// Copyright 2018 Brett T. Warden
|
||||
|
||||
/// @file
|
||||
/// @brief Disney Made With Magic (MWM) Support
|
||||
/// derived from ir_Lasertag.cpp
|
||||
/// @see https://github.com/crankyoldgit/IRremoteESP8266/pull/557
|
||||
|
||||
// Supports:
|
||||
// Brand: Disney, Model: Made With Magic (Glow With The Show) wand
|
||||
|
||||
#include <algorithm>
|
||||
#include "IRrecv.h"
|
||||
#include "IRsend.h"
|
||||
#include "IRutils.h"
|
||||
|
||||
// Constants
|
||||
const uint16_t kMWMMinSamples = 6; // Msgs are >=3 bytes, bytes have >=2
|
||||
// samples
|
||||
const uint16_t kMWMTick = 417;
|
||||
const uint32_t kMWMMinGap = 30000; // Typical observed delay b/w commands
|
||||
const uint8_t kMWMTolerance = 0; // Percentage error margin.
|
||||
const uint16_t kMWMExcess = 0; // See kMarkExcess.
|
||||
const uint16_t kMWMDelta = 150; // Use instead of Excess and Tolerance.
|
||||
const uint8_t kMWMMaxWidth = 9; // Maximum number of successive bits at a
|
||||
// single level - worst case
|
||||
const int16_t kSpace = 1;
|
||||
const int16_t kMark = 0;
|
||||
|
||||
#if SEND_MWM
|
||||
/// Send a MWM packet/message.
|
||||
/// Status: Implemented.
|
||||
/// @param[in] data The message to be sent.
|
||||
/// @param[in] nbytes The number of bytes of message to be sent.
|
||||
/// @param[in] repeat The number of times the command is to be repeated.
|
||||
/// @note This protocol is 2400 bps serial, 1 start bit (mark),
|
||||
/// 1 stop bit (space), no parity
|
||||
void IRsend::sendMWM(const uint8_t data[], const uint16_t nbytes,
|
||||
const uint16_t repeat) {
|
||||
if (nbytes < 3) return; // Shortest possible message is 3 bytes
|
||||
|
||||
// Set 38kHz IR carrier frequency & a 1/4 (25%) duty cycle.
|
||||
// NOTE: duty cycle is not confirmed. Just guessing based on RC5/6 protocols.
|
||||
enableIROut(38, 25);
|
||||
|
||||
for (uint16_t r = 0; r <= repeat; r++) {
|
||||
// Data
|
||||
for (uint16_t i = 0; i < nbytes; i++) {
|
||||
uint8_t byte = data[i];
|
||||
|
||||
// Start bit
|
||||
mark(kMWMTick);
|
||||
|
||||
// LSB first, space=1
|
||||
for (uint8_t mask = 0x1; mask; mask <<= 1) {
|
||||
if (byte & mask) { // 1
|
||||
space(kMWMTick);
|
||||
} else { // 0
|
||||
mark(kMWMTick);
|
||||
}
|
||||
}
|
||||
// Stop bit
|
||||
space(kMWMTick);
|
||||
}
|
||||
// Footer
|
||||
space(kMWMMinGap);
|
||||
}
|
||||
}
|
||||
#endif // SEND_MWM
|
||||
|
||||
#if DECODE_MWM
|
||||
/// Decode the supplied MWM message.
|
||||
/// Status: Implemented.
|
||||
/// @param[in,out] results Ptr to the data to decode & where to store the result
|
||||
/// @param[in] offset The starting index to use when attempting to decode the
|
||||
/// raw data. Typically/Defaults to kStartOffset.
|
||||
/// @param[in] nbits The number of data bits to expect.
|
||||
/// @param[in] strict Flag indicating if we should perform strict matching.
|
||||
/// @return True if it can decode it, false if it can't.
|
||||
/// @note This protocol is 2400 bps serial, 1 start bit (mark),
|
||||
/// 1 stop bit (space), no parity
|
||||
bool IRrecv::decodeMWM(decode_results *results, uint16_t offset,
|
||||
const uint16_t nbits, const bool strict) {
|
||||
DPRINTLN("DEBUG: decodeMWM");
|
||||
|
||||
// Compliance
|
||||
if (results->rawlen <= kMWMMinSamples + offset) {
|
||||
DPRINTLN("DEBUG: decodeMWM: too few samples");
|
||||
return false;
|
||||
}
|
||||
|
||||
uint16_t used = 0;
|
||||
uint64_t data = 0;
|
||||
uint16_t frame_bits = 0;
|
||||
uint16_t data_bits = 0;
|
||||
|
||||
// No Header
|
||||
|
||||
// Data
|
||||
uint8_t bits_per_frame = 10;
|
||||
for (; offset < results->rawlen && results->bits < 8 * kStateSizeMax;
|
||||
frame_bits++) {
|
||||
DPRINT("DEBUG: decodeMWM: offset = ");
|
||||
DPRINTLN(offset);
|
||||
int16_t level = getRClevel(results, &offset, &used, kMWMTick, kMWMTolerance,
|
||||
kMWMExcess, kMWMDelta, kMWMMaxWidth);
|
||||
if (level < 0) {
|
||||
DPRINTLN("DEBUG: decodeMWM: getRClevel returned error");
|
||||
break;
|
||||
}
|
||||
switch (frame_bits % bits_per_frame) {
|
||||
case 0:
|
||||
// Start bit
|
||||
if (level != kMark) {
|
||||
DPRINTLN("DEBUG: decodeMWM: framing error - invalid start bit");
|
||||
goto done;
|
||||
}
|
||||
break;
|
||||
case 9:
|
||||
// Stop bit
|
||||
if (level != kSpace) {
|
||||
DPRINTLN("DEBUG: decodeMWM: framing error - invalid stop bit");
|
||||
return false;
|
||||
} else {
|
||||
DPRINT("DEBUG: decodeMWM: data_bits = ");
|
||||
DPRINTLN(data_bits);
|
||||
DPRINT("DEBUG: decodeMWM: Finished byte: ");
|
||||
DPRINTLN(uint64ToString(data));
|
||||
results->state[data_bits / 8 - 1] = data & 0xFF;
|
||||
results->bits = data_bits;
|
||||
data = 0;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
// Data bits
|
||||
DPRINT("DEBUG: decodeMWM: Storing bit: ");
|
||||
DPRINTLN((level == kSpace));
|
||||
// Transmission is LSB-first, space=1
|
||||
data |= ((level == kSpace)) << 8;
|
||||
data >>= 1;
|
||||
data_bits++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
done:
|
||||
// Footer (None)
|
||||
|
||||
// Compliance
|
||||
DPRINT("DEBUG: decodeMWM: frame_bits = ");
|
||||
DPRINTLN(frame_bits);
|
||||
DPRINT("DEBUG: decodeMWM: data_bits = ");
|
||||
DPRINTLN(data_bits);
|
||||
if (data_bits < nbits) {
|
||||
DPRINT("DEBUG: decodeMWM: too few bits; expected ");
|
||||
DPRINTLN(nbits);
|
||||
return false; // Less data than we expected.
|
||||
}
|
||||
|
||||
uint16_t payload_length = 0;
|
||||
switch (results->state[0] & 0xf0) {
|
||||
case 0x90:
|
||||
case 0xf0:
|
||||
// Normal commands
|
||||
payload_length = results->state[0] & 0x0f;
|
||||
DPRINT("DEBUG: decodeMWM: payload_length = ");
|
||||
DPRINTLN(payload_length);
|
||||
break;
|
||||
default:
|
||||
if (strict) {
|
||||
// Show commands
|
||||
if (results->state[0] != 0x55 && results->state[1] != 0xAA) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (data_bits < (payload_length + 3) * 8) {
|
||||
DPRINT("DEBUG: decodeMWM: too few bytes; expected ");
|
||||
DPRINTLN((payload_length + 3));
|
||||
return false;
|
||||
}
|
||||
if (strict) {
|
||||
if (payload_length && (data_bits > (payload_length + 3) * 8)) {
|
||||
DPRINT("DEBUG: decodeMWM: too many bytes; expected ");
|
||||
DPRINTLN((payload_length + 3));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Success
|
||||
results->decode_type = MWM;
|
||||
results->repeat = false;
|
||||
return true;
|
||||
}
|
||||
#endif // DECODE_MWM
|
||||
|
||||
// vim: et:ts=2:sw=2
|
||||
154
yoRadio/src/IRremoteESP8266/ir_Magiquest.cpp
Normal file
154
yoRadio/src/IRremoteESP8266/ir_Magiquest.cpp
Normal file
@@ -0,0 +1,154 @@
|
||||
// Copyright 2013 mpflaga
|
||||
// Copyright 2015 kitlaan
|
||||
// Copyright 2017 Jason kendall, David Conran
|
||||
|
||||
/// @file
|
||||
/// @brief Support for MagiQuest protocols.
|
||||
/// @see https://github.com/kitlaan/Arduino-IRremote/blob/master/ir_Magiquest.cpp
|
||||
/// @see https://github.com/mpflaga/Arduino-IRremote
|
||||
|
||||
#include "ir_Magiquest.h"
|
||||
#include <algorithm>
|
||||
#include "IRrecv.h"
|
||||
#include "IRsend.h"
|
||||
#include "IRutils.h"
|
||||
|
||||
#define IS_ZERO(m, s) (((m)*100 / ((m) + (s))) <= kMagiQuestZeroRatio)
|
||||
#define IS_ONE(m, s) (((m)*100 / ((m) + (s))) >= kMagiQuestOneRatio)
|
||||
|
||||
#if SEND_MAGIQUEST
|
||||
/// Send a MagiQuest formatted message.
|
||||
/// Status: Beta / Should be working.
|
||||
/// @param[in] data The message to be sent.
|
||||
/// @param[in] nbits The number of bits of message to be sent.
|
||||
/// @param[in] repeat The number of times the command is to be repeated.
|
||||
void IRsend::sendMagiQuest(const uint64_t data, const uint16_t nbits,
|
||||
const uint16_t repeat) {
|
||||
sendGeneric(0, 0, // No Headers - Technically it's included in the data.
|
||||
// i.e. 8 zeros.
|
||||
kMagiQuestMarkOne, kMagiQuestSpaceOne, kMagiQuestMarkZero,
|
||||
kMagiQuestSpaceZero,
|
||||
0, // No footer mark.
|
||||
kMagiQuestGap, data, nbits, 36, true, repeat, 50);
|
||||
}
|
||||
|
||||
/// Encode a MagiQuest wand_id, and a magnitude into a single 64bit value.
|
||||
/// (Only 48 bits of real data + 8 leading zero bits)
|
||||
/// This is suitable for calling sendMagiQuest() with.
|
||||
/// e.g. sendMagiQuest(encodeMagiQuest(wand_id, magnitude))
|
||||
/// @param[in] wand_id The value for the wand ID.
|
||||
/// @param[in] magnitude The value for the magnitude
|
||||
/// @return A code suitable for calling sendMagiQuest() with.
|
||||
uint64_t IRsend::encodeMagiQuest(const uint32_t wand_id,
|
||||
const uint16_t magnitude) {
|
||||
uint64_t result = 0;
|
||||
result = wand_id;
|
||||
result <<= 16;
|
||||
result |= magnitude;
|
||||
// Shouldn't be needed, but ensure top 8/16 bit are zero.
|
||||
result &= 0xFFFFFFFFFFFFULL;
|
||||
return result;
|
||||
}
|
||||
#endif // SEND_MAGIQUEST
|
||||
|
||||
#if DECODE_MAGIQUEST
|
||||
/// Decode the supplied MagiQuest message.
|
||||
/// Status: Beta / Should work.
|
||||
/// @param[in,out] results Ptr to the data to decode & where to store the result
|
||||
/// @param[in] offset The starting index to use when attempting to decode the
|
||||
/// raw data. Typically/Defaults to kStartOffset.
|
||||
/// @param[in] nbits The number of data bits to expect.
|
||||
/// @param[in] strict Flag indicating if we should perform strict matching.
|
||||
/// @return True if it can decode it, false if it can't.
|
||||
/// @note MagiQuest protocol appears to be a header of 8 'zero' bits, followed
|
||||
/// by 32 bits of "wand ID" and finally 16 bits of "magnitude".
|
||||
/// Even though we describe this protocol as 56 bits, it really only has
|
||||
/// 48 bits of data that matter.
|
||||
/// In transmission order, 8 zeros + 32 wand_id + 16 magnitude.
|
||||
/// @see https://github.com/kitlaan/Arduino-IRremote/blob/master/ir_Magiquest.cpp
|
||||
bool IRrecv::decodeMagiQuest(decode_results *results, uint16_t offset,
|
||||
const uint16_t nbits, const bool strict) {
|
||||
uint16_t bits = 0;
|
||||
uint64_t data = 0;
|
||||
|
||||
if (results->rawlen < (2 * kMagiquestBits) + offset - 1) {
|
||||
DPRINT("Not enough bits to be Magiquest - Rawlen: ");
|
||||
DPRINT(results->rawlen);
|
||||
DPRINT(" Expected: ");
|
||||
DPRINTLN(2 * kMagiquestBits + offset - 1);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Compliance
|
||||
if (strict && nbits != kMagiquestBits) return false;
|
||||
|
||||
// Of six wands as datapoints, so far they all start with 8 ZEROs.
|
||||
// For example, here is the data from two wands
|
||||
// 00000000 00100011 01001100 00100110 00000010 00000010 00010111
|
||||
// 00000000 00100000 10001000 00110001 00000010 00000010 10110100
|
||||
|
||||
// Decode the (MARK + SPACE) bits
|
||||
while (offset + 1 < results->rawlen && bits < nbits - 1) {
|
||||
uint16_t mark = results->rawbuf[offset];
|
||||
uint16_t space = results->rawbuf[offset + 1];
|
||||
if (!matchMark(mark + space, kMagiQuestTotalUsec)) {
|
||||
DPRINT("Not enough time to be Magiquest - Mark: ");
|
||||
DPRINT(mark);
|
||||
DPRINT(" Space: ");
|
||||
DPRINT(space);
|
||||
DPRINT(" Total: ");
|
||||
DPRINT(mark + space);
|
||||
DPRINT("Expected: ");
|
||||
DPRINTLN(kMagiQuestTotalUsec);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (IS_ZERO(mark, space))
|
||||
data = (data << 1) | 0;
|
||||
else if (IS_ONE(mark, space))
|
||||
data = (data << 1) | 1;
|
||||
else
|
||||
return false;
|
||||
|
||||
bits++;
|
||||
offset += 2;
|
||||
|
||||
// Compliance
|
||||
// The first 8 bits of this protocol are supposed to all be 0.
|
||||
// Exit out early as it is never going to match.
|
||||
if (strict && bits == 8 && data != 0) return false;
|
||||
}
|
||||
|
||||
// Last bit is special as the protocol ends with a SPACE, not a MARK.
|
||||
// Grab the last MARK bit, assuming a good SPACE after it
|
||||
if (offset < results->rawlen) {
|
||||
uint16_t mark = results->rawbuf[offset];
|
||||
uint16_t space = (kMagiQuestTotalUsec / kRawTick) - mark;
|
||||
|
||||
if (IS_ZERO(mark, space))
|
||||
data = (data << 1) | 0;
|
||||
else if (IS_ONE(mark, space))
|
||||
data = (data << 1) | 1;
|
||||
else
|
||||
return false;
|
||||
|
||||
bits++;
|
||||
}
|
||||
|
||||
if (bits != nbits) return false;
|
||||
|
||||
if (strict) {
|
||||
// The top 8 bits of the 56 bits needs to be 0x00 to be valid.
|
||||
// i.e. bits 56 to 49 are all zero.
|
||||
if ((data >> (nbits - 8)) != 0) return false;
|
||||
}
|
||||
|
||||
// Success
|
||||
results->decode_type = MAGIQUEST;
|
||||
results->bits = bits;
|
||||
results->value = data;
|
||||
results->address = data >> 16; // Wand ID
|
||||
results->command = data & 0xFFFF; // Magnitude
|
||||
return true;
|
||||
}
|
||||
#endif // DECODE_MAGIQUEST
|
||||
43
yoRadio/src/IRremoteESP8266/ir_Magiquest.h
Normal file
43
yoRadio/src/IRremoteESP8266/ir_Magiquest.h
Normal file
@@ -0,0 +1,43 @@
|
||||
// Copyright 2013 mpflaga
|
||||
// Copyright 2015 kitlaan
|
||||
// Copyright 2017 Jason kendall, David Conran
|
||||
|
||||
/// @file
|
||||
/// @brief Support for MagiQuest protocols.
|
||||
/// @see https://github.com/kitlaan/Arduino-IRremote/blob/master/ir_Magiquest.cpp
|
||||
/// @see https://github.com/mpflaga/Arduino-IRremote
|
||||
|
||||
// Supports:
|
||||
// Brand: MagiQuest, Model: Wand
|
||||
|
||||
#ifndef IR_MAGIQUEST_H_
|
||||
#define IR_MAGIQUEST_H_
|
||||
|
||||
#define __STDC_LIMIT_MACROS
|
||||
#include <stdint.h>
|
||||
#include "IRremoteESP8266.h"
|
||||
#include "IRsend.h"
|
||||
|
||||
/// MagiQuest packet is both Wand ID and magnitude of swish and flick
|
||||
union magiquest {
|
||||
uint64_t llword;
|
||||
uint8_t byte[8];
|
||||
// uint16_t word[4];
|
||||
uint32_t lword[2];
|
||||
struct {
|
||||
uint16_t magnitude;
|
||||
uint32_t wand_id;
|
||||
uint8_t padding;
|
||||
uint8_t scrap;
|
||||
} cmd;
|
||||
};
|
||||
|
||||
const uint16_t kMagiQuestTotalUsec = 1150;
|
||||
const uint8_t kMagiQuestZeroRatio = 30; // usually <= ~25%
|
||||
const uint8_t kMagiQuestOneRatio = 38; // usually >= ~50%
|
||||
const uint16_t kMagiQuestMarkZero = 280;
|
||||
const uint16_t kMagiQuestSpaceZero = 850;
|
||||
const uint16_t kMagiQuestMarkOne = 580;
|
||||
const uint16_t kMagiQuestSpaceOne = 600;
|
||||
const uint32_t kMagiQuestGap = kDefaultMessageGap; // Just a guess.
|
||||
#endif // IR_MAGIQUEST_H_
|
||||
101
yoRadio/src/IRremoteESP8266/ir_Metz.cpp
Normal file
101
yoRadio/src/IRremoteESP8266/ir_Metz.cpp
Normal file
@@ -0,0 +1,101 @@
|
||||
// Copyright 2020 David Conran (crankyoldgit)
|
||||
/// @file
|
||||
/// @brief Support for Metz protocol
|
||||
/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1241
|
||||
|
||||
// Supports:
|
||||
// Brand: Metz, Model: RM16 remote
|
||||
// Brand: Metz, Model: RM17 remote
|
||||
// Brand: Metz, Model: RM19 remote
|
||||
// Brand: Metz, Model: CH610 TV
|
||||
|
||||
#include "IRrecv.h"
|
||||
#include "IRsend.h"
|
||||
#include "IRutils.h"
|
||||
|
||||
// Constants.
|
||||
const uint16_t kMetzHdrMark = 880; ///< uSeconds.
|
||||
const uint16_t kMetzHdrSpace = 2336; ///< uSeconds.
|
||||
const uint16_t kMetzBitMark = 473; ///< uSeconds.
|
||||
const uint16_t kMetzOneSpace = 1640; ///< uSeconds.
|
||||
const uint16_t kMetzZeroSpace = 940; ///< uSeconds.
|
||||
const uint16_t kMetzFreq = 38000; ///< Hz.
|
||||
const uint8_t kMetzAddressBits = 3;
|
||||
const uint8_t kMetzCommandBits = 6;
|
||||
|
||||
#if SEND_METZ
|
||||
/// Send a Metz formatted message.
|
||||
/// Status: Beta / Needs testing against a real device.
|
||||
/// @param[in] data containing the IR command.
|
||||
/// @param[in] nbits Nr. of bits to send. usually kMetzBits
|
||||
/// @param[in] repeat Nr. of times the message is to be repeated.
|
||||
void IRsend::sendMetz(const uint64_t data, const uint16_t nbits,
|
||||
const uint16_t repeat) {
|
||||
sendGeneric(kMetzHdrMark, kMetzHdrSpace, // Header
|
||||
kMetzBitMark, kMetzOneSpace, // Data
|
||||
kMetzBitMark, kMetzZeroSpace,
|
||||
kMetzBitMark, kDefaultMessageGap, // Footer.
|
||||
data, nbits, // Payload
|
||||
kMetzFreq, true, repeat, kDutyDefault);
|
||||
}
|
||||
|
||||
/// Encode a Metz address, command, and toggle bits into a code suitable
|
||||
/// for use with sendMetz().
|
||||
/// @param[in] address A 3-bit address value.
|
||||
/// @param[in] command A 6-bit command value.
|
||||
/// @param[in] toggle Should the toggle bit be set in the result?
|
||||
/// @return A 19-bit value suitable for use with `sendMetz()`.
|
||||
uint32_t IRsend::encodeMetz(const uint8_t address, const uint8_t command,
|
||||
const bool toggle) {
|
||||
return toggle << (2 * (kMetzAddressBits + kMetzCommandBits)) |
|
||||
(address & 0x7) << (2 * kMetzCommandBits + kMetzAddressBits) |
|
||||
(~address & 0x7) << (2 * kMetzCommandBits) |
|
||||
(command & 0x3F) << kMetzCommandBits |
|
||||
(~command & 0x3F);
|
||||
}
|
||||
#endif // SEND_METZ
|
||||
|
||||
#if DECODE_METZ
|
||||
/// Decode the supplied Metz message.
|
||||
/// Status: BETA / Probably works.
|
||||
/// @param[in,out] results Ptr to the data to decode & where to store the decode
|
||||
/// @param[in] offset The starting index to use when attempting to decode the
|
||||
/// raw data. Typically/Defaults to kStartOffset.
|
||||
/// @param[in] nbits The number of data bits to expect.
|
||||
/// @param[in] strict Flag indicating if we should perform strict matching.
|
||||
/// @return A boolean. True if it can decode it, false if it can't.
|
||||
bool IRrecv::decodeMetz(decode_results *results, uint16_t offset,
|
||||
const uint16_t nbits, const bool strict) {
|
||||
if (strict && nbits != kMetzBits) return false;
|
||||
|
||||
uint64_t data = 0;
|
||||
|
||||
// Match Header + Data + Footer
|
||||
if (!matchGeneric(results->rawbuf + offset, &data,
|
||||
results->rawlen - offset, nbits,
|
||||
kMetzHdrMark, kMetzHdrSpace, // Header
|
||||
kMetzBitMark, kMetzOneSpace, // Data
|
||||
kMetzBitMark, kMetzZeroSpace,
|
||||
kMetzBitMark, kDefaultMessageGap, // Footer
|
||||
true, _tolerance, 0, true)) return false;
|
||||
|
||||
uint16_t command = GETBITS64(data, kMetzCommandBits, kMetzCommandBits);
|
||||
uint16_t address = GETBITS64(data, 2 * kMetzCommandBits + kMetzAddressBits,
|
||||
kMetzAddressBits);
|
||||
// Compliance
|
||||
if (strict) {
|
||||
if (command != invertBits(GETBITS64(data, 0, kMetzCommandBits),
|
||||
kMetzCommandBits) ||
|
||||
address != invertBits(GETBITS64(data, 2 * kMetzCommandBits,
|
||||
kMetzAddressBits),
|
||||
kMetzAddressBits)) return false;
|
||||
}
|
||||
// Success
|
||||
results->decode_type = decode_type_t::METZ;
|
||||
results->bits = nbits;
|
||||
results->value = data;
|
||||
results->address = address;
|
||||
results->command = command;
|
||||
return true;
|
||||
}
|
||||
#endif // DECODE_METZ
|
||||
886
yoRadio/src/IRremoteESP8266/ir_Midea.cpp
Normal file
886
yoRadio/src/IRremoteESP8266/ir_Midea.cpp
Normal file
@@ -0,0 +1,886 @@
|
||||
// Copyright 2017 bwze, crankyoldgit
|
||||
/// @file
|
||||
/// @brief Support for Midea protocols.
|
||||
/// Midea added by crankyoldgit & bwze.
|
||||
/// send: bwze/crankyoldgit, decode: crankyoldgit
|
||||
/// @note SwingV has the function of an Ion Filter on Danby A/C units.
|
||||
/// @see https://docs.google.com/spreadsheets/d/1TZh4jWrx4h9zzpYUI9aYXMl1fYOiqu-xVuOOMqagxrs/edit?usp=sharing
|
||||
/// @see https://github.com/crankyoldgit/IRremoteESP8266/pull/1213
|
||||
|
||||
#include "ir_Midea.h"
|
||||
#include "ir_NEC.h"
|
||||
#include <algorithm>
|
||||
#ifndef ARDUINO
|
||||
#include <string>
|
||||
#endif
|
||||
#include "IRrecv.h"
|
||||
#include "IRsend.h"
|
||||
#include "IRtext.h"
|
||||
#include "IRutils.h"
|
||||
|
||||
// Constants
|
||||
const uint16_t kMideaTick = 80;
|
||||
const uint16_t kMideaBitMarkTicks = 7;
|
||||
const uint16_t kMideaBitMark = kMideaBitMarkTicks * kMideaTick;
|
||||
const uint16_t kMideaOneSpaceTicks = 21;
|
||||
const uint16_t kMideaOneSpace = kMideaOneSpaceTicks * kMideaTick;
|
||||
const uint16_t kMideaZeroSpaceTicks = 7;
|
||||
const uint16_t kMideaZeroSpace = kMideaZeroSpaceTicks * kMideaTick;
|
||||
const uint16_t kMideaHdrMarkTicks = 56;
|
||||
const uint16_t kMideaHdrMark = kMideaHdrMarkTicks * kMideaTick;
|
||||
const uint16_t kMideaHdrSpaceTicks = 56;
|
||||
const uint16_t kMideaHdrSpace = kMideaHdrSpaceTicks * kMideaTick;
|
||||
const uint16_t kMideaMinGapTicks =
|
||||
kMideaHdrMarkTicks + kMideaZeroSpaceTicks + kMideaBitMarkTicks;
|
||||
const uint16_t kMideaMinGap = kMideaMinGapTicks * kMideaTick;
|
||||
const uint8_t kMideaTolerance = 30; // Percent
|
||||
const uint16_t kMidea24MinGap = 13000; ///< uSecs
|
||||
|
||||
using irutils::addBoolToString;
|
||||
using irutils::addFanToString;
|
||||
using irutils::addIntToString;
|
||||
using irutils::addLabeledString;
|
||||
using irutils::addModeToString;
|
||||
using irutils::addTempToString;
|
||||
using irutils::addToggleToString;
|
||||
using irutils::minsToString;
|
||||
|
||||
#if SEND_MIDEA
|
||||
/// Send a Midea message
|
||||
/// Status: Alpha / Needs testing against a real device.
|
||||
/// @param[in] data The message to be sent.
|
||||
/// @param[in] nbits The number of bits of message to be sent.
|
||||
/// @param[in] repeat The number of times the command is to be repeated.
|
||||
void IRsend::sendMidea(uint64_t data, uint16_t nbits, uint16_t repeat) {
|
||||
if (nbits % 8 != 0) return; // nbits is required to be a multiple of 8.
|
||||
|
||||
// Set IR carrier frequency
|
||||
enableIROut(38);
|
||||
|
||||
for (uint16_t r = 0; r <= repeat; r++) {
|
||||
// The protocol sends the message, then follows up with an entirely
|
||||
// inverted payload.
|
||||
for (size_t inner_loop = 0; inner_loop < 2; inner_loop++) {
|
||||
// Header
|
||||
mark(kMideaHdrMark);
|
||||
space(kMideaHdrSpace);
|
||||
// Data
|
||||
// Break data into byte segments, starting at the Most Significant
|
||||
// Byte. Each byte then being sent normal, then followed inverted.
|
||||
for (uint16_t i = 8; i <= nbits; i += 8) {
|
||||
// Grab a bytes worth of data.
|
||||
uint8_t segment = (data >> (nbits - i)) & 0xFF;
|
||||
sendData(kMideaBitMark, kMideaOneSpace, kMideaBitMark, kMideaZeroSpace,
|
||||
segment, 8, true);
|
||||
}
|
||||
// Footer
|
||||
mark(kMideaBitMark);
|
||||
space(kMideaMinGap); // Pause before repeating
|
||||
|
||||
// Invert the data for the 2nd phase of the message.
|
||||
// As we get called twice in the inner loop, we will always revert
|
||||
// to the original 'data' state.
|
||||
data = ~data;
|
||||
}
|
||||
space(kDefaultMessageGap);
|
||||
}
|
||||
}
|
||||
#endif // SEND_MIDEA
|
||||
|
||||
// Code to emulate Midea A/C IR remote control unit.
|
||||
|
||||
/// Class constructor
|
||||
/// @param[in] pin GPIO to be used when sending.
|
||||
/// @param[in] inverted Is the output signal to be inverted?
|
||||
/// @param[in] use_modulation Is frequency modulation to be used?
|
||||
IRMideaAC::IRMideaAC(const uint16_t pin, const bool inverted,
|
||||
const bool use_modulation)
|
||||
: _irsend(pin, inverted, use_modulation) { this->stateReset(); }
|
||||
|
||||
/// Reset the state of the remote to a known good state/sequence.
|
||||
void IRMideaAC::stateReset(void) {
|
||||
// Power On, Mode Auto, Fan Auto, Temp = 25C/77F
|
||||
_.remote_state = 0xA1826FFFFF62;
|
||||
_CleanToggle = false;
|
||||
_EconoToggle = false;
|
||||
_8CHeatToggle = false;
|
||||
_LightToggle = false;
|
||||
_Quiet = _Quiet_prev = false;
|
||||
_SwingVToggle = false;
|
||||
_TurboToggle = false;
|
||||
#if KAYSUN_AC
|
||||
_SwingVStep = false;
|
||||
#endif // KAYSUN_AC
|
||||
}
|
||||
|
||||
/// Set up hardware to be able to send a message.
|
||||
void IRMideaAC::begin(void) { _irsend.begin(); }
|
||||
|
||||
#if SEND_MIDEA
|
||||
/// Send the current internal state as an IR message.
|
||||
/// @param[in] repeat Nr. of times the message will be repeated.
|
||||
void IRMideaAC::send(const uint16_t repeat) {
|
||||
_irsend.sendMidea(getRaw(), kMideaBits, repeat);
|
||||
// Handle the toggle/special "one-off" settings if we need to.
|
||||
if (_SwingVToggle && !isSwingVToggle())
|
||||
_irsend.sendMidea(kMideaACToggleSwingV, kMideaBits, repeat);
|
||||
_SwingVToggle = false;
|
||||
#if KAYSUN_AC
|
||||
if (_SwingVStep && !isSwingVStep())
|
||||
_irsend.sendMidea(kMideaACSwingVStep, kMideaBits, repeat);
|
||||
_SwingVStep = false;
|
||||
#endif // KAYSUN_AC
|
||||
if (_EconoToggle && !isEconoToggle())
|
||||
_irsend.sendMidea(kMideaACToggleEcono, kMideaBits, repeat);
|
||||
_EconoToggle = false;
|
||||
if (_TurboToggle && !isTurboToggle())
|
||||
_irsend.sendMidea(kMideaACToggleTurbo, kMideaBits, repeat);
|
||||
_TurboToggle = false;
|
||||
if (_LightToggle && !isLightToggle())
|
||||
_irsend.sendMidea(kMideaACToggleLight, kMideaBits, repeat);
|
||||
_LightToggle = false;
|
||||
if (getMode() <= kMideaACAuto) { // Only available in Cool, Dry, or Auto mode
|
||||
if (_CleanToggle && !isCleanToggle())
|
||||
_irsend.sendMidea(kMideaACToggleSelfClean, kMideaBits, repeat);
|
||||
_CleanToggle = false;
|
||||
} else if (getMode() == kMideaACHeat) { // Only available in Heat mode
|
||||
if (_8CHeatToggle && !is8CHeatToggle())
|
||||
_irsend.sendMidea(kMideaACToggle8CHeat, kMideaBits, repeat);
|
||||
_8CHeatToggle = false;
|
||||
}
|
||||
if (_Quiet != _Quiet_prev)
|
||||
_irsend.sendMidea(_Quiet ? kMideaACQuietOn : kMideaACQuietOff,
|
||||
kMideaBits, repeat);
|
||||
_Quiet_prev = _Quiet;
|
||||
}
|
||||
#endif // SEND_MIDEA
|
||||
|
||||
/// Get a copy of the internal state/code for this protocol.
|
||||
/// @return The code for this protocol based on the current internal state.
|
||||
uint64_t IRMideaAC::getRaw(void) {
|
||||
checksum(); // Ensure correct checksum before sending.
|
||||
return _.remote_state;
|
||||
}
|
||||
|
||||
/// Set the internal state from a valid code for this protocol.
|
||||
/// @param[in] newState A valid code for this protocol.
|
||||
void IRMideaAC::setRaw(const uint64_t newState) { _.remote_state = newState; }
|
||||
|
||||
/// Set the requested power state of the A/C to on.
|
||||
void IRMideaAC::on(void) { setPower(true); }
|
||||
|
||||
/// Set the requested power state of the A/C to off.
|
||||
void IRMideaAC::off(void) { setPower(false); }
|
||||
|
||||
/// Change the power setting.
|
||||
/// @param[in] on true, the setting is on. false, the setting is off.
|
||||
void IRMideaAC::setPower(const bool on) {
|
||||
_.Power = on;
|
||||
}
|
||||
|
||||
/// Get the value of the current power setting.
|
||||
/// @return true, the setting is on. false, the setting is off.
|
||||
bool IRMideaAC::getPower(void) const {
|
||||
return _.Power;
|
||||
}
|
||||
|
||||
/// Is the device currently using Celsius or the Fahrenheit temp scale?
|
||||
/// @return true, the A/C unit uses Celsius natively, false, is Fahrenheit.
|
||||
bool IRMideaAC::getUseCelsius(void) const {
|
||||
return !_.useFahrenheit;
|
||||
}
|
||||
|
||||
/// Set the A/C unit to use Celsius natively.
|
||||
/// @param[in] on true, the setting is on. false, the setting is off.
|
||||
void IRMideaAC::setUseCelsius(const bool on) {
|
||||
if (on == _.useFahrenheit) { // We need to change.
|
||||
uint8_t native_temp = getTemp(!on); // Get the old native temp.
|
||||
_.useFahrenheit = !on; // Cleared is on.
|
||||
setTemp(native_temp, !on); // Reset temp using the old native temp.
|
||||
}
|
||||
}
|
||||
|
||||
/// Set the temperature.
|
||||
/// @param[in] temp The temperature in degrees celsius.
|
||||
/// @param[in] useCelsius true, use the Celsius temp scale. false, is Fahrenheit
|
||||
void IRMideaAC::setTemp(const uint8_t temp, const bool useCelsius) {
|
||||
uint8_t max_temp = kMideaACMaxTempF;
|
||||
uint8_t min_temp = kMideaACMinTempF;
|
||||
if (useCelsius) {
|
||||
max_temp = kMideaACMaxTempC;
|
||||
min_temp = kMideaACMinTempC;
|
||||
}
|
||||
uint8_t new_temp = std::min(max_temp, std::max(min_temp, temp));
|
||||
if (!_.useFahrenheit && !useCelsius) // Native is in C, new_temp is in F
|
||||
new_temp = fahrenheitToCelsius(new_temp) - kMideaACMinTempC;
|
||||
else if (_.useFahrenheit && useCelsius) // Native is in F, new_temp is in C
|
||||
new_temp = celsiusToFahrenheit(new_temp) - kMideaACMinTempF;
|
||||
else // Native and desired are the same units.
|
||||
new_temp -= min_temp;
|
||||
// Set the actual data.
|
||||
_.Temp = new_temp;
|
||||
}
|
||||
|
||||
/// Get the current temperature setting.
|
||||
/// @param[in] celsius true, the results are in Celsius. false, in Fahrenheit.
|
||||
/// @return The current setting for temp. in the requested units/scale.
|
||||
uint8_t IRMideaAC::getTemp(const bool celsius) const {
|
||||
uint8_t temp = _.Temp;
|
||||
if (!_.useFahrenheit)
|
||||
temp += kMideaACMinTempC;
|
||||
else
|
||||
temp += kMideaACMinTempF;
|
||||
if (celsius && _.useFahrenheit) temp = fahrenheitToCelsius(temp) + 0.5;
|
||||
if (!celsius && !_.useFahrenheit) temp = celsiusToFahrenheit(temp);
|
||||
return temp;
|
||||
}
|
||||
|
||||
/// Set the Sensor temperature.
|
||||
/// @param[in] temp The temperature in degrees celsius.
|
||||
/// @param[in] useCelsius true, use the Celsius temp scale. false, is Fahrenheit
|
||||
/// @note Also known as FollowMe
|
||||
void IRMideaAC::setSensorTemp(const uint8_t temp, const bool useCelsius) {
|
||||
uint8_t max_temp = kMideaACMaxSensorTempF;
|
||||
uint8_t min_temp = kMideaACMinSensorTempF;
|
||||
if (useCelsius) {
|
||||
max_temp = kMideaACMaxSensorTempC;
|
||||
min_temp = kMideaACMinSensorTempC;
|
||||
}
|
||||
uint8_t new_temp = std::min(max_temp, std::max(min_temp, temp));
|
||||
if (!_.useFahrenheit && !useCelsius) // Native is in C, new_temp is in F
|
||||
new_temp = fahrenheitToCelsius(new_temp) - kMideaACMinSensorTempC;
|
||||
else if (_.useFahrenheit && useCelsius) // Native is in F, new_temp is in C
|
||||
new_temp = celsiusToFahrenheit(new_temp) - kMideaACMinSensorTempF;
|
||||
else // Native and desired are the same units.
|
||||
new_temp -= min_temp;
|
||||
// Set the actual data.
|
||||
_.SensorTemp = new_temp + 1;
|
||||
setEnableSensorTemp(true);
|
||||
}
|
||||
|
||||
/// Get the current Sensor temperature setting.
|
||||
/// @param[in] celsius true, the results are in Celsius. false, in Fahrenheit.
|
||||
/// @return The current setting for temp. in the requested units/scale.
|
||||
/// @note Also known as FollowMe
|
||||
uint8_t IRMideaAC::getSensorTemp(const bool celsius) const {
|
||||
uint8_t temp = _.SensorTemp - 1;
|
||||
if (!_.useFahrenheit)
|
||||
temp += kMideaACMinSensorTempC;
|
||||
else
|
||||
temp += kMideaACMinSensorTempF;
|
||||
if (celsius && _.useFahrenheit) temp = fahrenheitToCelsius(temp) + 0.5;
|
||||
if (!celsius && !_.useFahrenheit) temp = celsiusToFahrenheit(temp);
|
||||
return temp;
|
||||
}
|
||||
|
||||
/// Enable the remote's Sensor temperature.
|
||||
/// @param[in] on true, the setting is on. false, the setting is off.
|
||||
/// @note Also known as FollowMe
|
||||
void IRMideaAC::setEnableSensorTemp(const bool on) {
|
||||
_.disableSensor = !on;
|
||||
if (on) {
|
||||
setType(kMideaACTypeFollow);
|
||||
} else {
|
||||
setType(kMideaACTypeCommand);
|
||||
_.SensorTemp = kMideaACSensorTempOnTimerOff; // Apply special value if off.
|
||||
}
|
||||
}
|
||||
|
||||
/// Is the remote temperature sensor enabled?
|
||||
/// @return A boolean indicating if it is enabled or not.
|
||||
/// @note Also known as FollowMe
|
||||
bool IRMideaAC::getEnableSensorTemp(void) const { return !_.disableSensor; }
|
||||
|
||||
/// Set the speed of the fan.
|
||||
/// @param[in] fan The desired setting. 1-3 set the speed, 0 for auto.
|
||||
void IRMideaAC::setFan(const uint8_t fan) {
|
||||
_.Fan = (fan > kMideaACFanHigh) ? kMideaACFanAuto : fan;
|
||||
}
|
||||
|
||||
/// Get the current fan speed setting.
|
||||
/// @return The current fan speed.
|
||||
uint8_t IRMideaAC::getFan(void) const {
|
||||
return _.Fan;
|
||||
}
|
||||
|
||||
/// Get the operating mode setting of the A/C.
|
||||
/// @return The current operating mode setting.
|
||||
uint8_t IRMideaAC::getMode(void) const {
|
||||
return _.Mode;
|
||||
}
|
||||
|
||||
/// Set the operating mode of the A/C.
|
||||
/// @param[in] mode The desired operating mode.
|
||||
void IRMideaAC::setMode(const uint8_t mode) {
|
||||
switch (mode) {
|
||||
case kMideaACAuto:
|
||||
case kMideaACCool:
|
||||
case kMideaACHeat:
|
||||
case kMideaACDry:
|
||||
case kMideaACFan:
|
||||
_.Mode = mode;
|
||||
break;
|
||||
default:
|
||||
_.Mode = kMideaACAuto;
|
||||
}
|
||||
}
|
||||
|
||||
/// Set the Sleep setting of the A/C.
|
||||
/// @param[in] on true, the setting is on. false, the setting is off.
|
||||
void IRMideaAC::setSleep(const bool on) {
|
||||
_.Sleep = on;
|
||||
}
|
||||
|
||||
/// Get the Sleep setting of the A/C.
|
||||
/// @return true, the setting is on. false, the setting is off.
|
||||
bool IRMideaAC::getSleep(void) const {
|
||||
return _.Sleep;
|
||||
}
|
||||
|
||||
/// Set the A/C to toggle the vertical swing toggle for the next send.
|
||||
/// @note On Danby A/C units, this is associated with the Ion Filter instead.
|
||||
/// @param[in] on true, the setting is on. false, the setting is off.
|
||||
void IRMideaAC::setSwingVToggle(const bool on) { _SwingVToggle = on; }
|
||||
|
||||
/// Is the current state a vertical swing toggle message?
|
||||
/// @note On Danby A/C units, this is associated with the Ion Filter instead.
|
||||
/// @return true, it is. false, it isn't.
|
||||
bool IRMideaAC::isSwingVToggle(void) const {
|
||||
return _.remote_state == kMideaACToggleSwingV;
|
||||
}
|
||||
|
||||
// Get the vertical swing toggle state of the A/C.
|
||||
/// @note On Danby A/C units, this is associated with the Ion Filter instead.
|
||||
/// @return true, the setting is on. false, the setting is off.
|
||||
bool IRMideaAC::getSwingVToggle(void) {
|
||||
_SwingVToggle |= isSwingVToggle();
|
||||
return _SwingVToggle;
|
||||
}
|
||||
|
||||
#if KAYSUN_AC
|
||||
/// Set the A/C to step the vertical swing for the next send.
|
||||
/// @param[in] on true, the setting is on. false, the setting is off.
|
||||
void IRMideaAC::setSwingVStep(const bool on) { _SwingVStep = on; }
|
||||
|
||||
/// Is the current state a step vertical swing message?
|
||||
/// @return true, it is. false, it isn't.
|
||||
bool IRMideaAC::isSwingVStep(void) const {
|
||||
return _.remote_state == kMideaACSwingVStep;
|
||||
}
|
||||
|
||||
// Get the step vertical swing state of the A/C.
|
||||
/// @return true, the setting is on. false, the setting is off.
|
||||
bool IRMideaAC::getSwingVStep(void) {
|
||||
_SwingVStep |= isSwingVStep();
|
||||
return _SwingVStep;
|
||||
}
|
||||
#endif // KAYSUN_AC
|
||||
|
||||
/// Set the A/C to toggle the Econo (energy saver) mode for the next send.
|
||||
/// @param[in] on true, the setting is on. false, the setting is off.
|
||||
void IRMideaAC::setEconoToggle(const bool on) { _EconoToggle = on; }
|
||||
|
||||
/// Is the current state an Econo (energy saver) toggle message?
|
||||
/// @return true, it is. false, it isn't.
|
||||
bool IRMideaAC::isEconoToggle(void) const {
|
||||
return _.remote_state == kMideaACToggleEcono;
|
||||
}
|
||||
|
||||
// Get the Econo (energy saver) toggle state of the A/C.
|
||||
/// @return true, the setting is on. false, the setting is off.
|
||||
bool IRMideaAC::getEconoToggle(void) {
|
||||
_EconoToggle |= isEconoToggle();
|
||||
return _EconoToggle;
|
||||
}
|
||||
|
||||
/// Set the A/C to toggle the Turbo mode for the next send.
|
||||
/// @param[in] on true, the setting is on. false, the setting is off.
|
||||
void IRMideaAC::setTurboToggle(const bool on) { _TurboToggle = on; }
|
||||
|
||||
/// Is the current state a Turbo toggle message?
|
||||
/// @return true, it is. false, it isn't.
|
||||
bool IRMideaAC::isTurboToggle(void) const {
|
||||
return _.remote_state == kMideaACToggleTurbo;
|
||||
}
|
||||
|
||||
// Get the Turbo toggle state of the A/C.
|
||||
/// @return true, the setting is on. false, the setting is off.
|
||||
bool IRMideaAC::getTurboToggle(void) {
|
||||
_TurboToggle |= isTurboToggle();
|
||||
return _TurboToggle;
|
||||
}
|
||||
|
||||
/// Set the A/C to toggle the Light (LED) mode for the next send.
|
||||
/// @param[in] on true, the setting is on. false, the setting is off.
|
||||
void IRMideaAC::setLightToggle(const bool on) { _LightToggle = on; }
|
||||
|
||||
/// Is the current state a Light (LED) toggle message?
|
||||
/// @return true, it is. false, it isn't.
|
||||
bool IRMideaAC::isLightToggle(void) const {
|
||||
return _.remote_state == kMideaACToggleLight;
|
||||
}
|
||||
|
||||
// Get the Light (LED) toggle state of the A/C.
|
||||
/// @return true, the setting is on. false, the setting is off.
|
||||
bool IRMideaAC::getLightToggle(void) {
|
||||
_LightToggle |= isLightToggle();
|
||||
return _LightToggle;
|
||||
}
|
||||
|
||||
/// Is the current state a Self-Clean toggle message?
|
||||
/// @return true, it is. false, it isn't.
|
||||
bool IRMideaAC::isCleanToggle(void) const {
|
||||
return _.remote_state == kMideaACToggleSelfClean;
|
||||
}
|
||||
|
||||
/// Set the A/C to toggle the Self Clean mode for the next send.
|
||||
/// @note Only works in Cool, Dry, or Auto modes.
|
||||
/// @param[in] on true, the setting is on. false, the setting is off.
|
||||
void IRMideaAC::setCleanToggle(const bool on) {
|
||||
_CleanToggle = on && getMode() <= kMideaACAuto;
|
||||
}
|
||||
|
||||
// Get the Self-Clean toggle state of the A/C.
|
||||
/// @return true, the setting is on. false, the setting is off.
|
||||
bool IRMideaAC::getCleanToggle(void) {
|
||||
_CleanToggle |= isCleanToggle();
|
||||
return _CleanToggle;
|
||||
}
|
||||
|
||||
/// Is the current state a 8C Heat (Freeze Protect) toggle message?
|
||||
/// @note Only works in Heat mode.
|
||||
/// @return true, it is. false, it isn't.
|
||||
bool IRMideaAC::is8CHeatToggle(void) const {
|
||||
return _.remote_state == kMideaACToggle8CHeat;
|
||||
}
|
||||
|
||||
/// Set the A/C to toggle the 8C Heat (Freeze Protect) mode for the next send.
|
||||
/// @note Only works in Heat mode.
|
||||
/// @param[in] on true, the setting is on. false, the setting is off.
|
||||
void IRMideaAC::set8CHeatToggle(const bool on) {
|
||||
_8CHeatToggle = on && getMode() == kMideaACHeat;
|
||||
}
|
||||
|
||||
// Get the 8C Heat (Freeze Protect) toggle state of the A/C.
|
||||
/// @return true, the setting is on. false, the setting is off.
|
||||
bool IRMideaAC::get8CHeatToggle(void) {
|
||||
_8CHeatToggle |= is8CHeatToggle();
|
||||
return _8CHeatToggle;
|
||||
}
|
||||
|
||||
/// Is the current state a Quiet(Silent) message?
|
||||
/// @return true, it is. false, it isn't.
|
||||
bool IRMideaAC::isQuiet(void) const {
|
||||
return (_.remote_state == kMideaACQuietOff ||
|
||||
_.remote_state == kMideaACQuietOn);
|
||||
}
|
||||
|
||||
/// Set the Quiet (Silent) mode for the next send.
|
||||
/// @param[in] on true, the setting is on. false, the setting is off.
|
||||
void IRMideaAC::setQuiet(const bool on) { _Quiet = on; }
|
||||
|
||||
/// Set the Quiet (Silent) mode for the next send.
|
||||
/// @param[in] on true, the setting is on. false, the setting is off.
|
||||
/// @param[in] prev true, previously the setting was on. false, setting was off.
|
||||
void IRMideaAC::setQuiet(const bool on, const bool prev) {
|
||||
setQuiet(on);
|
||||
_Quiet_prev = prev;
|
||||
}
|
||||
|
||||
// Get the Quiet (Silent) mode state of the A/C.
|
||||
/// @return true, the setting is on. false, the setting is off.
|
||||
bool IRMideaAC::getQuiet(void) const {
|
||||
if (isQuiet())
|
||||
return _.remote_state == kMideaACQuietOn;
|
||||
else
|
||||
return _Quiet;
|
||||
}
|
||||
|
||||
/// Calculate the checksum for a given state.
|
||||
/// @param[in] state The value to calc the checksum of.
|
||||
/// @return The calculated checksum value.
|
||||
uint8_t IRMideaAC::calcChecksum(const uint64_t state) {
|
||||
uint8_t sum = 0;
|
||||
uint64_t temp_state = state;
|
||||
|
||||
for (uint8_t i = 0; i < 5; i++) {
|
||||
temp_state >>= 8;
|
||||
sum += reverseBits((temp_state & 0xFF), 8);
|
||||
}
|
||||
sum = 256 - sum;
|
||||
return reverseBits(sum, 8);
|
||||
}
|
||||
|
||||
/// Verify the checksum is valid for a given state.
|
||||
/// @param[in] state The state to verify the checksum of.
|
||||
/// @return true, if the state has a valid checksum. Otherwise, false.
|
||||
bool IRMideaAC::validChecksum(const uint64_t state) {
|
||||
return GETBITS64(state, 0, 8) == calcChecksum(state);
|
||||
}
|
||||
|
||||
/// Calculate & set the checksum for the current internal state of the remote.
|
||||
void IRMideaAC::checksum(void) {
|
||||
// Stored the checksum value in the last byte.
|
||||
_.Sum = calcChecksum(_.remote_state);
|
||||
}
|
||||
|
||||
/// Get the message type setting of the A/C message.
|
||||
/// @return The message type setting.
|
||||
uint8_t IRMideaAC::getType(void) const { return _.Type; }
|
||||
|
||||
/// Set the message type setting of the A/C message.
|
||||
/// @param[in] setting The desired message type setting.
|
||||
void IRMideaAC::setType(const uint8_t setting) {
|
||||
switch (setting) {
|
||||
case kMideaACTypeFollow:
|
||||
_.BeepDisable = false;
|
||||
// FALL-THRU
|
||||
case kMideaACTypeSpecial:
|
||||
_.Type = setting;
|
||||
break;
|
||||
default:
|
||||
_.Type = kMideaACTypeCommand;
|
||||
_.BeepDisable = true;
|
||||
}
|
||||
}
|
||||
|
||||
/// Is the OnTimer enabled?
|
||||
/// @return true for yes, false for no.
|
||||
bool IRMideaAC::isOnTimerEnabled(void) const {
|
||||
return getType() == kMideaACTypeCommand &&
|
||||
_.SensorTemp != kMideaACSensorTempOnTimerOff;
|
||||
}
|
||||
|
||||
/// Get the value of the OnTimer is currently set to.
|
||||
/// @return The number of minutes.
|
||||
uint16_t IRMideaAC::getOnTimer(void) const {
|
||||
return (_.SensorTemp >> 1) * 30 + 30;
|
||||
}
|
||||
|
||||
/// Set the value of the On Timer.
|
||||
/// @param[in] mins The number of minutes for the timer.
|
||||
/// @note Time will be rounded down to nearest 30 min as that is the resolution
|
||||
/// of the actual device/protocol.
|
||||
/// @note A value of less than 30 will disable the Timer.
|
||||
/// @warning On Timer is incompatible with Sensor Temp/Follow Me messages.
|
||||
/// Setting it will disable that mode/settings.
|
||||
void IRMideaAC::setOnTimer(const uint16_t mins) {
|
||||
setEnableSensorTemp(false);
|
||||
uint8_t halfhours = std::min((uint16_t)(24 * 60), mins) / 30;
|
||||
if (halfhours)
|
||||
_.SensorTemp = ((halfhours - 1) << 1) | 1;
|
||||
else
|
||||
_.SensorTemp = kMideaACSensorTempOnTimerOff;
|
||||
}
|
||||
|
||||
/// Is the OffTimer enabled?
|
||||
/// @return true for yes, false for no.
|
||||
bool IRMideaAC::isOffTimerEnabled(void) const {
|
||||
return _.OffTimer != kMideaACTimerOff;
|
||||
}
|
||||
|
||||
/// Get the value of the OffTimer is currently set to.
|
||||
/// @return The number of minutes.
|
||||
uint16_t IRMideaAC::getOffTimer(void) const { return _.OffTimer * 30 + 30; }
|
||||
|
||||
/// Set the value of the Off Timer.
|
||||
/// @param[in] mins The number of minutes for the timer.
|
||||
/// @note Time will be rounded down to nearest 30 min as that is the resolution
|
||||
/// of the actual device/protocol.
|
||||
/// @note A value of less than 30 will disable the Timer.
|
||||
void IRMideaAC::setOffTimer(const uint16_t mins) {
|
||||
uint8_t halfhours = std::min((uint16_t)(24 * 60), mins) / 30;
|
||||
if (halfhours)
|
||||
_.OffTimer = halfhours - 1;
|
||||
else
|
||||
_.OffTimer = kMideaACTimerOff;
|
||||
}
|
||||
|
||||
/// Convert a stdAc::opmode_t enum into its native mode.
|
||||
/// @param[in] mode The enum to be converted.
|
||||
/// @return The native equivalent of the enum.
|
||||
uint8_t IRMideaAC::convertMode(const stdAc::opmode_t mode) {
|
||||
switch (mode) {
|
||||
case stdAc::opmode_t::kCool: return kMideaACCool;
|
||||
case stdAc::opmode_t::kHeat: return kMideaACHeat;
|
||||
case stdAc::opmode_t::kDry: return kMideaACDry;
|
||||
case stdAc::opmode_t::kFan: return kMideaACFan;
|
||||
default: return kMideaACAuto;
|
||||
}
|
||||
}
|
||||
|
||||
/// Convert a stdAc::fanspeed_t enum into it's native speed.
|
||||
/// @param[in] speed The enum to be converted.
|
||||
/// @return The native equivalent of the enum.
|
||||
uint8_t IRMideaAC::convertFan(const stdAc::fanspeed_t speed) {
|
||||
switch (speed) {
|
||||
case stdAc::fanspeed_t::kMin:
|
||||
case stdAc::fanspeed_t::kLow: return kMideaACFanLow;
|
||||
case stdAc::fanspeed_t::kMedium: return kMideaACFanMed;
|
||||
case stdAc::fanspeed_t::kHigh:
|
||||
case stdAc::fanspeed_t::kMax: return kMideaACFanHigh;
|
||||
default: return kMideaACFanAuto;
|
||||
}
|
||||
}
|
||||
|
||||
/// Convert a native mode into its stdAc equivalent.
|
||||
/// @param[in] mode The native setting to be converted.
|
||||
/// @return The stdAc equivalent of the native setting.
|
||||
stdAc::opmode_t IRMideaAC::toCommonMode(const uint8_t mode) {
|
||||
switch (mode) {
|
||||
case kMideaACCool: return stdAc::opmode_t::kCool;
|
||||
case kMideaACHeat: return stdAc::opmode_t::kHeat;
|
||||
case kMideaACDry: return stdAc::opmode_t::kDry;
|
||||
case kMideaACFan: return stdAc::opmode_t::kFan;
|
||||
default: return stdAc::opmode_t::kAuto;
|
||||
}
|
||||
}
|
||||
|
||||
/// Convert a native fan speed into its stdAc equivalent.
|
||||
/// @param[in] speed The native setting to be converted.
|
||||
/// @return The stdAc equivalent of the native setting.
|
||||
stdAc::fanspeed_t IRMideaAC::toCommonFanSpeed(const uint8_t speed) {
|
||||
switch (speed) {
|
||||
case kMideaACFanHigh: return stdAc::fanspeed_t::kMax;
|
||||
case kMideaACFanMed: return stdAc::fanspeed_t::kMedium;
|
||||
case kMideaACFanLow: return stdAc::fanspeed_t::kMin;
|
||||
default: return stdAc::fanspeed_t::kAuto;
|
||||
}
|
||||
}
|
||||
|
||||
/// Convert the current internal state into its stdAc::state_t equivalent.
|
||||
/// @param[in] prev A Ptr to the previous state.
|
||||
/// @return The stdAc equivalent of the native settings.
|
||||
stdAc::state_t IRMideaAC::toCommon(const stdAc::state_t *prev) {
|
||||
stdAc::state_t result{};
|
||||
if (prev != NULL) {
|
||||
result = *prev;
|
||||
} else {
|
||||
// Fixed/Not supported/Non-zero defaults.
|
||||
result.protocol = decode_type_t::MIDEA;
|
||||
result.model = -1; // No models used.
|
||||
result.swingh = stdAc::swingh_t::kOff;
|
||||
result.swingv = stdAc::swingv_t::kOff;
|
||||
result.quiet = false;
|
||||
result.turbo = false;
|
||||
result.econo = false;
|
||||
result.filter = false;
|
||||
result.light = false;
|
||||
result.beep = false;
|
||||
result.sleep = -1;
|
||||
result.clock = -1;
|
||||
}
|
||||
if (isSwingVToggle()) {
|
||||
result.swingv = (result.swingv != stdAc::swingv_t::kOff) ?
|
||||
stdAc::swingv_t::kAuto : stdAc::swingv_t::kOff;
|
||||
return result;
|
||||
}
|
||||
result.power = _.Power;
|
||||
result.mode = toCommonMode(_.Mode);
|
||||
result.celsius = !_.useFahrenheit;
|
||||
result.degrees = getTemp(result.celsius);
|
||||
result.sensorTemperature = getSensorTemp(result.celsius);
|
||||
result.fanspeed = toCommonFanSpeed(_.Fan);
|
||||
result.sleep = _.Sleep ? 0 : -1;
|
||||
result.econo = getEconoToggle();
|
||||
result.clean ^= getCleanToggle();
|
||||
return result;
|
||||
}
|
||||
|
||||
/// Convert the current internal state into a human readable string.
|
||||
/// @return A human readable string.
|
||||
String IRMideaAC::toString(void) {
|
||||
String result = "";
|
||||
const uint8_t message_type = getType();
|
||||
result.reserve(230); // Reserve some heap for the string to reduce fragging.
|
||||
result += addIntToString(message_type, kTypeStr, false);
|
||||
result += kSpaceLBraceStr;
|
||||
switch (message_type) {
|
||||
case kMideaACTypeCommand: result += kCommandStr; break;
|
||||
case kMideaACTypeSpecial: result += kSpecialStr; break;
|
||||
case kMideaACTypeFollow: result += kFollowStr; break;
|
||||
default: result += kUnknownStr;
|
||||
}
|
||||
result += ')';
|
||||
if (message_type != kMideaACTypeSpecial) {
|
||||
result += addBoolToString(_.Power, kPowerStr);
|
||||
result += addModeToString(_.Mode, kMideaACAuto, kMideaACCool,
|
||||
kMideaACHeat, kMideaACDry, kMideaACFan);
|
||||
result += addBoolToString(!_.useFahrenheit, kCelsiusStr);
|
||||
result += addTempToString(getTemp(true));
|
||||
result += '/';
|
||||
result += uint64ToString(getTemp(false));
|
||||
result += 'F';
|
||||
if (getEnableSensorTemp()) {
|
||||
result += kCommaSpaceStr;
|
||||
result += kSensorStr;
|
||||
result += addTempToString(getSensorTemp(true), true, false);
|
||||
result += '/';
|
||||
result += uint64ToString(getSensorTemp(false));
|
||||
result += 'F';
|
||||
} else {
|
||||
result += addLabeledString(
|
||||
isOnTimerEnabled() ? minsToString(getOnTimer()) : kOffStr,
|
||||
kOnTimerStr);
|
||||
}
|
||||
result += addLabeledString(
|
||||
isOffTimerEnabled() ? minsToString(getOffTimer()) : kOffStr,
|
||||
kOffTimerStr);
|
||||
result += addFanToString(_.Fan, kMideaACFanHigh, kMideaACFanLow,
|
||||
kMideaACFanAuto, kMideaACFanAuto, kMideaACFanMed);
|
||||
result += addBoolToString(_.Sleep, kSleepStr);
|
||||
}
|
||||
result += addToggleToString(getSwingVToggle(), kSwingVStr);
|
||||
#if KAYSUN_AC
|
||||
result += addBoolToString(getSwingVStep(), kStepStr);
|
||||
#endif // KAYSUN_AC
|
||||
result += addToggleToString(getEconoToggle(), kEconoStr);
|
||||
result += addToggleToString(getTurboToggle(), kTurboStr);
|
||||
result += addBoolToString(getQuiet(), kQuietStr);
|
||||
result += addToggleToString(getLightToggle(), kLightStr);
|
||||
result += addToggleToString(getCleanToggle(), kCleanStr);
|
||||
result += addToggleToString(get8CHeatToggle(), k8CHeatStr);
|
||||
return result;
|
||||
}
|
||||
|
||||
#if DECODE_MIDEA
|
||||
/// Decode the supplied Midea message.
|
||||
/// Status: Alpha / Needs testing against a real device.
|
||||
/// @param[in,out] results Ptr to the data to decode & where to store the result
|
||||
/// @param[in] offset The starting index to use when attempting to decode the
|
||||
/// raw data. Typically/Defaults to kStartOffset.
|
||||
/// @param[in] nbits The number of data bits to expect.
|
||||
/// Typically kHitachiAcBits, kHitachiAc1Bits, kHitachiAc2Bits,
|
||||
/// kHitachiAc344Bits
|
||||
/// @param[in] strict Flag indicating if we should perform strict matching.
|
||||
bool IRrecv::decodeMidea(decode_results *results, uint16_t offset,
|
||||
const uint16_t nbits, const bool strict) {
|
||||
uint8_t min_nr_of_messages = 1;
|
||||
if (strict) {
|
||||
if (nbits != kMideaBits) return false; // Not strictly a MIDEA message.
|
||||
min_nr_of_messages = 2;
|
||||
}
|
||||
|
||||
// The protocol sends the data normal + inverted, alternating on
|
||||
// each byte. Hence twice the number of expected data bits.
|
||||
if (results->rawlen <
|
||||
min_nr_of_messages * (2 * nbits + kHeader + kFooter) - 1 + offset)
|
||||
return false; // Can't possibly be a valid MIDEA message.
|
||||
|
||||
uint64_t data = 0;
|
||||
uint64_t inverted = 0;
|
||||
|
||||
if (nbits > sizeof(data) * 8)
|
||||
return false; // We can't possibly capture a Midea packet that big.
|
||||
|
||||
for (uint8_t i = 0; i < min_nr_of_messages; i++) {
|
||||
// Match Header + Data + Footer
|
||||
uint16_t used;
|
||||
used = matchGeneric(results->rawbuf + offset, i % 2 ? &inverted : &data,
|
||||
results->rawlen - offset, nbits,
|
||||
kMideaHdrMark, kMideaHdrSpace,
|
||||
kMideaBitMark, kMideaOneSpace,
|
||||
kMideaBitMark, kMideaZeroSpace,
|
||||
kMideaBitMark, kMideaMinGap,
|
||||
i % 2, // No "atleast" on 1st part, but yes on the 2nd.
|
||||
kMideaTolerance);
|
||||
if (!used) return false;
|
||||
offset += used;
|
||||
}
|
||||
|
||||
// Compliance
|
||||
if (strict) {
|
||||
// Protocol requires a second message with all the data bits inverted.
|
||||
// We should have checked we got a second message in the previous loop.
|
||||
// Just need to check it's value is an inverted copy of the first message.
|
||||
uint64_t mask = (1ULL << kMideaBits) - 1;
|
||||
if ((data & mask) != ((inverted ^ mask) & mask)) return false;
|
||||
if (!IRMideaAC::validChecksum(data)) return false;
|
||||
}
|
||||
|
||||
// Success
|
||||
results->decode_type = MIDEA;
|
||||
results->bits = nbits;
|
||||
results->value = data;
|
||||
results->address = 0;
|
||||
results->command = 0;
|
||||
return true;
|
||||
}
|
||||
#endif // DECODE_MIDEA
|
||||
|
||||
#if SEND_MIDEA24
|
||||
/// Send a Midea24 formatted message.
|
||||
/// Status: STABLE / Confirmed working on a real device.
|
||||
/// @param[in] data The message to be sent.
|
||||
/// @param[in] nbits The number of bits of message to be sent.
|
||||
/// @param[in] repeat The number of times the command is to be repeated.
|
||||
/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1170
|
||||
/// @note This protocol is basically a 48-bit version of the NEC protocol with
|
||||
/// alternate bytes inverted, thus only 24 bits of real data, and with at
|
||||
/// least a single repeat.
|
||||
/// @warning Can't be used beyond 32 bits.
|
||||
void IRsend::sendMidea24(const uint64_t data, const uint16_t nbits,
|
||||
const uint16_t repeat) {
|
||||
uint64_t newdata = 0;
|
||||
// Construct the data into bye & inverted byte pairs.
|
||||
for (int16_t i = nbits - 8; i >= 0; i -= 8) {
|
||||
// Shuffle the data to be sent so far.
|
||||
newdata <<= 16;
|
||||
uint8_t next = GETBITS64(data, i, 8);
|
||||
newdata |= ((next << 8) | (next ^ 0xFF));
|
||||
}
|
||||
sendNEC(newdata, nbits * 2, repeat);
|
||||
}
|
||||
#endif // SEND_MIDEA24
|
||||
|
||||
#if DECODE_MIDEA24
|
||||
/// Decode the supplied Midea24 message.
|
||||
/// Status: STABLE / Confirmed working on a real device.
|
||||
/// @param[in,out] results Ptr to the data to decode & where to store the decode
|
||||
/// result.
|
||||
/// @param[in] offset The starting index to use when attempting to decode the
|
||||
/// raw data. Typically/Defaults to kStartOffset.
|
||||
/// @param[in] nbits The number of data bits to expect.
|
||||
/// @param[in] strict Flag indicating if we should perform strict matching.
|
||||
/// @return A boolean. True if it can decode it, false if it can't.
|
||||
/// @note This protocol is basically a 48-bit version of the NEC protocol with
|
||||
/// alternate bytes inverted, thus only 24 bits of real data.
|
||||
/// @warning Can't be used beyond 32 bits.
|
||||
bool IRrecv::decodeMidea24(decode_results *results, uint16_t offset,
|
||||
const uint16_t nbits, const bool strict) {
|
||||
// Not strictly a MIDEA24 message.
|
||||
if (strict && nbits != kMidea24Bits) return false;
|
||||
if (nbits > 32) return false; // Can't successfully match something that big.
|
||||
|
||||
uint64_t longdata = 0;
|
||||
if (!matchGeneric(results->rawbuf + offset, &longdata,
|
||||
results->rawlen - offset, nbits * 2,
|
||||
kNecHdrMark, kNecHdrSpace,
|
||||
kNecBitMark, kNecOneSpace,
|
||||
kNecBitMark, kNecZeroSpace,
|
||||
kNecBitMark, kMidea24MinGap, true)) return false;
|
||||
|
||||
// Build the result by checking every second byte is a complement(inversion)
|
||||
// of the previous one.
|
||||
uint32_t data = 0;
|
||||
for (uint8_t i = nbits * 2; i >= 16;) {
|
||||
// Shuffle the data collected so far.
|
||||
data <<= 8;
|
||||
i -= 8;
|
||||
uint8_t current = GETBITS64(longdata, i, 8);
|
||||
i -= 8;
|
||||
uint8_t next = GETBITS64(longdata, i, 8);
|
||||
// Check they are an inverted pair.
|
||||
if (current != (next ^ 0xFF)) return false; // They are not, so abort.
|
||||
data |= current;
|
||||
}
|
||||
|
||||
// Success
|
||||
results->decode_type = decode_type_t::MIDEA24;
|
||||
results->bits = nbits;
|
||||
results->value = data;
|
||||
results->address = 0;
|
||||
results->command = 0;
|
||||
return true;
|
||||
}
|
||||
#endif // DECODE_MIDEA24
|
||||
276
yoRadio/src/IRremoteESP8266/ir_Midea.h
Normal file
276
yoRadio/src/IRremoteESP8266/ir_Midea.h
Normal file
@@ -0,0 +1,276 @@
|
||||
// Copyright 2017 David Conran
|
||||
|
||||
/// @file
|
||||
/// @brief Support for Midea protocols.
|
||||
/// Midea added by crankyoldgit & bwze
|
||||
/// @see https://docs.google.com/spreadsheets/d/1TZh4jWrx4h9zzpYUI9aYXMl1fYOiqu-xVuOOMqagxrs/edit?usp=sharing
|
||||
/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1342#issuecomment-733721085
|
||||
/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1733
|
||||
|
||||
// Supports:
|
||||
// Brand: Pioneer System, Model: RYBO12GMFILCAD A/C (12K BTU) (MIDEA)
|
||||
// Brand: Pioneer System, Model: RUBO18GMFILCAD A/C (18K BTU) (MIDEA)
|
||||
// Brand: Pioneer System, Model: WS012GMFI22HLD A/C (12K BTU) (MIDEA)
|
||||
// Brand: Pioneer System, Model: WS018GMFI22HLD A/C (12K BTU) (MIDEA)
|
||||
// Brand: Pioneer System, Model: UB018GMFILCFHD A/C (12K BTU) (MIDEA)
|
||||
// Brand: Pioneer System, Model: RG66B6(B)/BGEFU1 remote (MIDEA)
|
||||
// Brand: Comfee, Model: MPD1-12CRN7 A/C (MIDEA)
|
||||
// Brand: Kaysun, Model: Casual CF A/C (MIDEA)
|
||||
// Brand: Keystone, Model: RG57H4(B)BGEF remote (MIDEA)
|
||||
// Brand: MrCool, Model: RG57A6/BGEFU1 remote (MIDEA)
|
||||
// Brand: Midea, Model: FS40-7AR Stand Fan (MIDEA24)
|
||||
// Brand: Danby, Model: DAC080BGUWDB (MIDEA)
|
||||
// Brand: Danby, Model: DAC100BGUWDB (MIDEA)
|
||||
// Brand: Danby, Model: DAC120BGUWDB (MIDEA)
|
||||
// Brand: Danby, Model: R09C/BCGE remote (MIDEA)
|
||||
// Brand: Trotec, Model: TROTEC PAC 2100 X (MIDEA)
|
||||
// Brand: Trotec, Model: TROTEC PAC 3900 X (MIDEA)
|
||||
// Brand: Trotec, Model: RG57H(B)/BGE remote (MIDEA)
|
||||
// Brand: Trotec, Model: RG57H3(B)/BGCEF-M remote (MIDEA)
|
||||
// Brand: Lennox, Model: RG57A6/BGEFU1 remote (MIDEA)
|
||||
// Brand: Lennox, Model: MWMA009S4-3P A/C (MIDEA)
|
||||
// Brand: Lennox, Model: MWMA012S4-3P A/C (MIDEA)
|
||||
// Brand: Lennox, Model: MCFA indoor split A/C (MIDEA)
|
||||
// Brand: Lennox, Model: MCFB indoor split A/C (MIDEA)
|
||||
// Brand: Lennox, Model: MMDA indoor split A/C (MIDEA)
|
||||
// Brand: Lennox, Model: MMDB indoor split A/C (MIDEA)
|
||||
// Brand: Lennox, Model: MWMA indoor split A/C (MIDEA)
|
||||
// Brand: Lennox, Model: MWMB indoor split A/C (MIDEA)
|
||||
// Brand: Lennox, Model: M22A indoor split A/C (MIDEA)
|
||||
// Brand: Lennox, Model: M33A indoor split A/C (MIDEA)
|
||||
// Brand: Lennox, Model: M33B indoor split A/C (MIDEA)
|
||||
|
||||
#ifndef IR_MIDEA_H_
|
||||
#define IR_MIDEA_H_
|
||||
|
||||
#define __STDC_LIMIT_MACROS
|
||||
#include <stdint.h>
|
||||
#ifdef ARDUINO
|
||||
#include <Arduino.h>
|
||||
#endif
|
||||
#include "IRremoteESP8266.h"
|
||||
#include "IRsend.h"
|
||||
#ifdef UNIT_TEST
|
||||
#include "IRsend_test.h"
|
||||
#endif
|
||||
|
||||
/// @note
|
||||
/// Compile-time model specific overrides.
|
||||
/// Uncomment one of these if you have such a devices to better match your A/C.
|
||||
/// It changes some of the special commands/settings.
|
||||
//
|
||||
// #define DANBY_DAC true
|
||||
// #define KAYSUN_AC true
|
||||
|
||||
/// @note Some Pioneer Systems have required a special bit to be set in order
|
||||
/// for the A/C unit to accept the message. We don't currently understand what
|
||||
/// this bit does. See the link for details of how to set this if needed.
|
||||
/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1342#issuecomment-733721085
|
||||
|
||||
/// Native representation of a Midea A/C message.
|
||||
union MideaProtocol{
|
||||
uint64_t remote_state; ///< The state in native IR code form
|
||||
// only use 48bits
|
||||
struct {
|
||||
// Byte 0
|
||||
uint8_t Sum;
|
||||
// Byte 1 (value=0xFF when not in use.)
|
||||
// This byte gets dual usage as Sensor Temp and On Timer
|
||||
// Depending on "Type" below.
|
||||
// When in "OnTimer", the nr of half hours is stored with mask 0b01111110
|
||||
// i.e.
|
||||
// uint8_t :1;
|
||||
// uint8_t OnTimerHalfHours:6;
|
||||
// uint8_t :1;
|
||||
uint8_t SensorTemp:7; ///< Degrees or OnTimer.
|
||||
uint8_t disableSensor:1;
|
||||
// Byte 2 (value=0xFF when not in use.)
|
||||
uint8_t :1; // 0b1
|
||||
uint8_t OffTimer:6; ///< Nr of Half hours. Off is 0b111111
|
||||
uint8_t BeepDisable:1; ///< 0 = no beep in follow me messages, 1 = beep.
|
||||
// Byte 3
|
||||
uint8_t Temp:5;
|
||||
uint8_t useFahrenheit:1;
|
||||
uint8_t :0;
|
||||
// Byte 4
|
||||
uint8_t Mode:3;
|
||||
uint8_t Fan:2;
|
||||
/// @todo Find out what this bit controls.
|
||||
/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1342#issuecomment-733721085
|
||||
uint8_t :1; ///< Unknown, but set on _some_ Pioneer System A/Cs.
|
||||
uint8_t Sleep:1;
|
||||
uint8_t Power:1;
|
||||
// Byte 5
|
||||
uint8_t Type:3; ///< Normal, Special, or FollowMe message type
|
||||
uint8_t Header:5; ///< Typically 0b10100
|
||||
};
|
||||
};
|
||||
|
||||
// Constants
|
||||
const uint8_t kMideaACMinTempF = 62; ///< Fahrenheit
|
||||
const uint8_t kMideaACMaxTempF = 86; ///< Fahrenheit
|
||||
const uint8_t kMideaACMinTempC = 17; ///< Celsius
|
||||
const uint8_t kMideaACMaxTempC = 30; ///< Celsius
|
||||
const uint8_t kMideaACMinSensorTempC = 0; ///< Celsius
|
||||
const uint8_t kMideaACMaxSensorTempC = 37; ///< Celsius
|
||||
const uint8_t kMideaACMinSensorTempF = 32; ///< Fahrenheit
|
||||
const uint8_t kMideaACMaxSensorTempF = 99; ///< Fahrenheit (Guess only!)
|
||||
const uint8_t kMideaACSensorTempOnTimerOff = 0b1111111;
|
||||
const uint8_t kMideaACTimerOff = 0b111111;
|
||||
const uint8_t kMideaACCool = 0; // 0b000
|
||||
const uint8_t kMideaACDry = 1; // 0b001
|
||||
const uint8_t kMideaACAuto = 2; // 0b010
|
||||
const uint8_t kMideaACHeat = 3; // 0b011
|
||||
const uint8_t kMideaACFan = 4; // 0b100
|
||||
const uint8_t kMideaACFanAuto = 0; // 0b00
|
||||
const uint8_t kMideaACFanLow = 1; // 0b01
|
||||
const uint8_t kMideaACFanMed = 2; // 0b10
|
||||
const uint8_t kMideaACFanHigh = 3; // 0b11
|
||||
#if KAYSUN_AC
|
||||
// For Kaysun AC units, Toggle SwingV is 0xA202FFFFFF7E
|
||||
const uint64_t kMideaACToggleSwingV = 0xA202FFFFFF7E;
|
||||
const uint64_t kMideaACSwingVStep = 0xA201FFFFFF7C;
|
||||
#else // KAYSUN_AC
|
||||
const uint64_t kMideaACToggleSwingV = 0xA201FFFFFF7C;
|
||||
#endif // KAYSUN_AC
|
||||
#if DANBY_DAC
|
||||
// For Danby DAC unit, the Ionizer toggle is the same as ToggleSwingV
|
||||
// const uint64_t kMideaACToggleIonizer = 0xA201FFFFFF7C;
|
||||
kSwingVToggleStr = kIonStr;
|
||||
#endif // DANBY_DAC
|
||||
const uint64_t kMideaACToggleEcono = 0xA202FFFFFF7E;
|
||||
const uint64_t kMideaACToggleLight = 0xA208FFFFFF75;
|
||||
const uint64_t kMideaACToggleTurbo = 0xA209FFFFFF74;
|
||||
// Mode must be Auto, Cool, or Dry
|
||||
const uint64_t kMideaACToggleSelfClean = 0xA20DFFFFFF70;
|
||||
// 8C Heat AKA Freeze Protection
|
||||
const uint64_t kMideaACToggle8CHeat = 0xA20FFFFFFF73; // Only in Heat
|
||||
const uint64_t kMideaACQuietOn = 0xA212FFFFFF6E;
|
||||
const uint64_t kMideaACQuietOff = 0xA213FFFFFF6F;
|
||||
|
||||
const uint8_t kMideaACTypeCommand = 0b001; ///< Message type
|
||||
const uint8_t kMideaACTypeSpecial = 0b010; ///< Message type
|
||||
const uint8_t kMideaACTypeFollow = 0b100; ///< Message type
|
||||
|
||||
// Legacy defines. (Deprecated)
|
||||
#define MIDEA_AC_COOL kMideaACCool
|
||||
#define MIDEA_AC_DRY kMideaACDry
|
||||
#define MIDEA_AC_AUTO kMideaACAuto
|
||||
#define MIDEA_AC_HEAT kMideaACHeat
|
||||
#define MIDEA_AC_FAN kMideaACFan
|
||||
#define MIDEA_AC_FAN_AUTO kMideaACFanAuto
|
||||
#define MIDEA_AC_FAN_LOW kMideaACFanLow
|
||||
#define MIDEA_AC_FAN_MED kMideaACFanMed
|
||||
#define MIDEA_AC_FAN_HI kMideaACFanHigh
|
||||
#define MIDEA_AC_POWER kMideaACPower
|
||||
#define MIDEA_AC_SLEEP kMideaACSleep
|
||||
#define MIDEA_AC_MIN_TEMP_F kMideaACMinTempF
|
||||
#define MIDEA_AC_MAX_TEMP_F kMideaACMaxTempF
|
||||
#define MIDEA_AC_MIN_TEMP_C kMideaACMinTempC
|
||||
#define MIDEA_AC_MAX_TEMP_C kMideaACMaxTempC
|
||||
|
||||
// Classes
|
||||
/// Class for handling detailed Midea A/C messages.
|
||||
/// @warning Consider this very alpha code.
|
||||
class IRMideaAC {
|
||||
public:
|
||||
explicit IRMideaAC(const uint16_t pin, const bool inverted = false,
|
||||
const bool use_modulation = true);
|
||||
void stateReset(void);
|
||||
#if SEND_MIDEA
|
||||
void send(const uint16_t repeat = kMideaMinRepeat);
|
||||
/// 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_MIDEA
|
||||
void begin(void);
|
||||
void on(void);
|
||||
void off(void);
|
||||
void setPower(const bool on);
|
||||
bool getPower(void) const;
|
||||
bool getUseCelsius(void) const;
|
||||
void setUseCelsius(const bool celsius);
|
||||
void setTemp(const uint8_t temp, const bool useCelsius = false);
|
||||
uint8_t getTemp(const bool useCelsius = false) const;
|
||||
void setSensorTemp(const uint8_t temp, const bool useCelsius = false);
|
||||
uint8_t getSensorTemp(const bool useCelsius = false) const;
|
||||
void setEnableSensorTemp(const bool on);
|
||||
bool getEnableSensorTemp(void) const;
|
||||
void setFan(const uint8_t fan);
|
||||
uint8_t getFan(void) const;
|
||||
void setMode(const uint8_t mode);
|
||||
uint8_t getMode(void) const;
|
||||
void setRaw(const uint64_t newState);
|
||||
uint64_t getRaw(void);
|
||||
static bool validChecksum(const uint64_t state);
|
||||
void setSleep(const bool on);
|
||||
bool getSleep(void) const;
|
||||
bool isSwingVToggle(void) const;
|
||||
void setSwingVToggle(const bool on);
|
||||
bool getSwingVToggle(void);
|
||||
#if KAYSUN_AC
|
||||
bool isSwingVStep(void) const;
|
||||
void setSwingVStep(const bool on);
|
||||
bool getSwingVStep(void);
|
||||
#endif // KAYSUN_AC
|
||||
bool isEconoToggle(void) const;
|
||||
void setEconoToggle(const bool on);
|
||||
bool getEconoToggle(void);
|
||||
bool isTurboToggle(void) const;
|
||||
void setTurboToggle(const bool on);
|
||||
bool getTurboToggle(void);
|
||||
bool isLightToggle(void) const;
|
||||
void setLightToggle(const bool on);
|
||||
bool getLightToggle(void);
|
||||
bool isCleanToggle(void) const;
|
||||
void setCleanToggle(const bool on);
|
||||
bool getCleanToggle(void);
|
||||
bool is8CHeatToggle(void) const;
|
||||
void set8CHeatToggle(const bool on);
|
||||
bool get8CHeatToggle(void);
|
||||
bool isQuiet(void) const;
|
||||
void setQuiet(const bool on);
|
||||
void setQuiet(const bool on, const bool prev);
|
||||
bool getQuiet(void) const;
|
||||
uint8_t getType(void) const;
|
||||
bool isOnTimerEnabled(void) const;
|
||||
uint16_t getOnTimer(void) const;
|
||||
void setOnTimer(const uint16_t mins);
|
||||
bool isOffTimerEnabled(void) const;
|
||||
uint16_t getOffTimer(void) const;
|
||||
void setOffTimer(const uint16_t mins);
|
||||
static uint8_t convertMode(const stdAc::opmode_t mode);
|
||||
static uint8_t convertFan(const stdAc::fanspeed_t speed);
|
||||
static stdAc::opmode_t toCommonMode(const uint8_t mode);
|
||||
static stdAc::fanspeed_t toCommonFanSpeed(const uint8_t speed);
|
||||
stdAc::state_t toCommon(const stdAc::state_t *prev = NULL);
|
||||
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
|
||||
MideaProtocol _;
|
||||
bool _CleanToggle;
|
||||
bool _EconoToggle;
|
||||
bool _8CHeatToggle;
|
||||
bool _LightToggle;
|
||||
bool _Quiet;
|
||||
bool _Quiet_prev;
|
||||
bool _SwingVToggle;
|
||||
#if KAYSUN_AC
|
||||
bool _SwingVStep;
|
||||
#endif // KAYSUN_AC
|
||||
bool _TurboToggle;
|
||||
void checksum(void);
|
||||
static uint8_t calcChecksum(const uint64_t state);
|
||||
void setType(const uint8_t type);
|
||||
};
|
||||
|
||||
#endif // IR_MIDEA_H_
|
||||
113
yoRadio/src/IRremoteESP8266/ir_MilesTag2.cpp
Normal file
113
yoRadio/src/IRremoteESP8266/ir_MilesTag2.cpp
Normal file
@@ -0,0 +1,113 @@
|
||||
// Copyright 2021 Victor Mukayev (vitos1k)
|
||||
// Copyright 2021 David Conran (crankyoldgit)
|
||||
|
||||
/// @file
|
||||
/// @brief Support for the MilesTag2 IR protocol for LaserTag gaming
|
||||
/// @see http://hosting.cmalton.me.uk/chrism/lasertag/MT2Proto.pdf
|
||||
/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1360
|
||||
|
||||
// Supports:
|
||||
// Brand: Milestag2, Model: Various
|
||||
|
||||
// TODO(vitos1k): This implementation would support only
|
||||
// short SHOT packets(14bits) and MSGs = 24bits. Support
|
||||
// for long MSGs > 24bits is TODO
|
||||
|
||||
#include <algorithm>
|
||||
#include "IRrecv.h"
|
||||
#include "IRsend.h"
|
||||
#include "IRutils.h"
|
||||
|
||||
// Constants
|
||||
// Shot packets have this bit as `0`
|
||||
const uint16_t kMilesTag2ShotMask = 1 << (kMilesTag2ShotBits - 1);
|
||||
// Msg packets have this bit as `1`
|
||||
const uint32_t kMilesTag2MsgMask = 1 << (kMilesTag2MsgBits - 1);
|
||||
const uint8_t kMilesTag2MsgTerminator = 0xE8;
|
||||
const uint16_t kMilesTag2HdrMark = 2400; /// uSeconds.
|
||||
const uint16_t kMilesTag2Space = 600; /// uSeconds.
|
||||
const uint16_t kMilesTag2OneMark = 1200; /// uSeconds.
|
||||
const uint16_t kMilesTag2ZeroMark = 600; /// uSeconds.
|
||||
const uint16_t kMilesTag2RptLength = 32000; /// uSeconds.
|
||||
const uint16_t kMilesTag2StdFreq = 38000; /// Hz.
|
||||
const uint16_t kMilesTag2StdDuty = 25; /// Percentage.
|
||||
|
||||
#if SEND_MILESTAG2
|
||||
/// Send a MilesTag2 formatted Shot/Msg packet.
|
||||
/// Status: ALPHA / Probably works but needs testing with a real device.
|
||||
/// @param[in] data The message to be sent.
|
||||
/// @param[in] nbits The number of bits of message to be sent.
|
||||
/// @param[in] repeat The number of times the command is to be repeated.
|
||||
void IRsend::sendMilestag2(const uint64_t data, const uint16_t nbits,
|
||||
const uint16_t repeat) {
|
||||
sendGeneric(
|
||||
kMilesTag2HdrMark, kMilesTag2Space, // Header
|
||||
kMilesTag2OneMark, kMilesTag2Space, // 1 bit
|
||||
kMilesTag2ZeroMark, kMilesTag2Space, // 0 bit
|
||||
0, // No footer mark
|
||||
kMilesTag2RptLength, data, nbits, kMilesTag2StdFreq, true, // MSB First
|
||||
repeat, kMilesTag2StdDuty);
|
||||
}
|
||||
#endif // SEND_MILESTAG2
|
||||
|
||||
#if DECODE_MILESTAG2
|
||||
/// Decode the supplied MilesTag2 message.
|
||||
/// Status: ALPHA / Probably works but needs testing with a real device.
|
||||
/// @param[in,out] results Ptr to the data to decode & where to store the result
|
||||
/// @param[in] offset The starting index to use when attempting to decode the
|
||||
/// raw data. Typically/Defaults to kStartOffset.
|
||||
/// @param[in] nbits The number of data bits to expect.
|
||||
/// @param[in] strict Flag indicating if we should perform strict matching.
|
||||
/// @return True if it can decode it, false if it can't.
|
||||
/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1360
|
||||
bool IRrecv::decodeMilestag2(decode_results *results, uint16_t offset,
|
||||
const uint16_t nbits, const bool strict) {
|
||||
uint64_t data = 0;
|
||||
// Header + Data + Optional Footer
|
||||
if (!matchGeneric(results->rawbuf + offset, &data,
|
||||
results->rawlen - offset, nbits,
|
||||
kMilesTag2HdrMark, kMilesTag2Space,
|
||||
kMilesTag2OneMark, kMilesTag2Space,
|
||||
kMilesTag2ZeroMark, kMilesTag2Space,
|
||||
0, kMilesTag2RptLength, true)) return false;
|
||||
|
||||
// Compliance
|
||||
if (strict) {
|
||||
switch (nbits) {
|
||||
case kMilesTag2ShotBits:
|
||||
// Is it a valid shot packet?
|
||||
if (data & kMilesTag2ShotMask) return false;
|
||||
break;
|
||||
case kMilesTag2MsgBits:
|
||||
// Is it a valid msg packet? i.e. Msg bit set & Terminator present.
|
||||
if (!(data & kMilesTag2MsgMask) ||
|
||||
((data & 0xFF) != kMilesTag2MsgTerminator))
|
||||
return false;
|
||||
break;
|
||||
default:
|
||||
DPRINT("incorrect nbits:");
|
||||
DPRINTLN(nbits);
|
||||
return false; // The request doesn't strictly match the protocol defn.
|
||||
}
|
||||
}
|
||||
|
||||
// Success
|
||||
results->bits = nbits;
|
||||
results->value = data;
|
||||
results->decode_type = decode_type_t::MILESTAG2;
|
||||
switch (nbits) {
|
||||
case kMilesTag2ShotBits:
|
||||
results->command = data & 0x3F; // Team & Damage
|
||||
results->address = data >> 6; // Player ID.
|
||||
break;
|
||||
case kMilesTag2MsgBits:
|
||||
results->command = (data >> 8) & 0xFF; // Message data
|
||||
results->address = (data >> 16) & 0x7F; // Message ID
|
||||
break;
|
||||
default:
|
||||
results->command = 0;
|
||||
results->address = 0;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
#endif // DECODE_MILESTAG2
|
||||
853
yoRadio/src/IRremoteESP8266/ir_Mirage.cpp
Normal file
853
yoRadio/src/IRremoteESP8266/ir_Mirage.cpp
Normal file
@@ -0,0 +1,853 @@
|
||||
// Copyright 2020-2021 David Conran (crankyoldgit)
|
||||
/// @file
|
||||
/// @brief Support for Mirage protocol
|
||||
/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1289
|
||||
/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1573
|
||||
|
||||
|
||||
#include "ir_Mirage.h"
|
||||
#include <algorithm>
|
||||
#include <cstring>
|
||||
#ifndef ARDUINO
|
||||
#include <string>
|
||||
#endif
|
||||
#include "IRrecv.h"
|
||||
#include "IRsend.h"
|
||||
#include "IRtext.h"
|
||||
#include "IRutils.h"
|
||||
|
||||
using irutils::addBoolToString;
|
||||
using irutils::addFanToString;
|
||||
using irutils::addIntToString;
|
||||
using irutils::addLabeledString;
|
||||
using irutils::addModeToString;
|
||||
using irutils::addModelToString;
|
||||
using irutils::addSwingHToString;
|
||||
using irutils::addSwingVToString;
|
||||
using irutils::addTempToString;
|
||||
using irutils::addToggleToString;
|
||||
using irutils::minsToString;
|
||||
using irutils::bcdToUint8;
|
||||
using irutils::uint8ToBcd;
|
||||
using irutils::sumNibbles;
|
||||
|
||||
// Constants
|
||||
const uint16_t kMirageHdrMark = 8360; ///< uSeconds
|
||||
const uint16_t kMirageBitMark = 554; ///< uSeconds
|
||||
const uint16_t kMirageHdrSpace = 4248; ///< uSeconds
|
||||
const uint16_t kMirageOneSpace = 1592; ///< uSeconds
|
||||
const uint16_t kMirageZeroSpace = 545; ///< uSeconds
|
||||
const uint32_t kMirageGap = kDefaultMessageGap; ///< uSeconds (just a guess)
|
||||
const uint16_t kMirageFreq = 38000; ///< Hz. (Just a guess)
|
||||
|
||||
const uint8_t kMirageAcKKG29AC1PowerOn = 0b00; // 0
|
||||
const uint8_t kMirageAcKKG29AC1PowerOff = 0b11; // 3
|
||||
|
||||
|
||||
#if SEND_MIRAGE
|
||||
/// Send a Mirage formatted message.
|
||||
/// Status: STABLE / Reported as working.
|
||||
/// @param[in] data An array of bytes containing the IR command.
|
||||
/// @param[in] nbytes Nr. of bytes of data in the array. (>=kMirageStateLength)
|
||||
/// @param[in] repeat Nr. of times the message is to be repeated.
|
||||
void IRsend::sendMirage(const uint8_t data[], const uint16_t nbytes,
|
||||
const uint16_t repeat) {
|
||||
sendGeneric(kMirageHdrMark, kMirageHdrSpace,
|
||||
kMirageBitMark, kMirageOneSpace,
|
||||
kMirageBitMark, kMirageZeroSpace,
|
||||
kMirageBitMark, kMirageGap,
|
||||
data, nbytes, kMirageFreq, false, // LSB
|
||||
repeat, kDutyDefault);
|
||||
}
|
||||
#endif // SEND_MIRAGE
|
||||
|
||||
#if DECODE_MIRAGE
|
||||
/// Decode the supplied Mirage message.
|
||||
/// Status: STABLE / Reported as working.
|
||||
/// @param[in,out] results Ptr to the data to decode & where to store the decode
|
||||
/// @param[in] offset The starting index to use when attempting to decode the
|
||||
/// raw data. Typically/Defaults to kStartOffset.
|
||||
/// @param[in] nbits The number of data bits to expect.
|
||||
/// @param[in] strict Flag indicating if we should perform strict matching.
|
||||
/// @return A boolean. True if it can decode it, false if it can't.
|
||||
bool IRrecv::decodeMirage(decode_results *results, uint16_t offset,
|
||||
const uint16_t nbits, const bool strict) {
|
||||
if (strict && nbits != kMirageBits) return false; // Compliance.
|
||||
|
||||
if (!matchGeneric(results->rawbuf + offset, results->state,
|
||||
results->rawlen - offset, nbits,
|
||||
kMirageHdrMark, kMirageHdrSpace,
|
||||
kMirageBitMark, kMirageOneSpace,
|
||||
kMirageBitMark, kMirageZeroSpace,
|
||||
kMirageBitMark, kMirageGap, true,
|
||||
kUseDefTol, kMarkExcess, false)) return false;
|
||||
// Compliance
|
||||
if (strict && !IRMirageAc::validChecksum(results->state)) return false;
|
||||
|
||||
// Success
|
||||
results->decode_type = decode_type_t::MIRAGE;
|
||||
results->bits = nbits;
|
||||
// No need to record the state as we stored it as we decoded it.
|
||||
// As we use result->state, we don't record value, address, or command as it
|
||||
// is a union data type.
|
||||
return true;
|
||||
}
|
||||
|
||||
// Code to emulate Mirage A/C IR remote control unit.
|
||||
|
||||
/// Class constructor
|
||||
/// @param[in] pin GPIO to be used when sending.
|
||||
/// @param[in] inverted Is the output signal to be inverted?
|
||||
/// @param[in] use_modulation Is frequency modulation to be used?
|
||||
IRMirageAc::IRMirageAc(const uint16_t pin, const bool inverted,
|
||||
const bool use_modulation)
|
||||
: _irsend(pin, inverted, use_modulation) { stateReset(); }
|
||||
|
||||
/// Reset the state of the remote to a known good state/sequence.
|
||||
void IRMirageAc::stateReset(void) {
|
||||
// The state of the IR remote in IR code form.
|
||||
static const uint8_t kReset[kMirageStateLength] = {
|
||||
0x56, 0x6C, 0x00, 0x00, 0x20, 0x1A, 0x00, 0x00,
|
||||
0x0C, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x42};
|
||||
setRaw(kReset);
|
||||
_model = mirage_ac_remote_model_t::KKG9AC1;
|
||||
}
|
||||
|
||||
/// Set up hardware to be able to send a message.
|
||||
void IRMirageAc::begin(void) { _irsend.begin(); }
|
||||
|
||||
#if SEND_MITSUBISHI_AC
|
||||
/// Send the current internal state as an IR message.
|
||||
/// @param[in] repeat Nr. of times the message will be repeated.
|
||||
void IRMirageAc::send(const uint16_t repeat) {
|
||||
_irsend.sendMirage(getRaw(), kMirageStateLength, repeat);
|
||||
// Reset any toggles after a send.
|
||||
switch (_model) {
|
||||
case mirage_ac_remote_model_t::KKG29AC1:
|
||||
setCleanToggle(false);
|
||||
setLight(false); // For this model (only), Light is a toggle.
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
#endif // SEND_MITSUBISHI_AC
|
||||
|
||||
/// Get a PTR to the internal state/code for this protocol.
|
||||
/// @return PTR to a code for this protocol based on the current internal state.
|
||||
uint8_t *IRMirageAc::getRaw(void) {
|
||||
checksum();
|
||||
return _.raw;
|
||||
}
|
||||
|
||||
/// Set the internal state from a valid code for this protocol.
|
||||
/// @param[in] data A valid code for this protocol.
|
||||
void IRMirageAc::setRaw(const uint8_t *data) {
|
||||
std::memcpy(_.raw, data, kMirageStateLength);
|
||||
_model = getModel(true);
|
||||
}
|
||||
|
||||
/// Guess the Mirage remote model from the supplied state code.
|
||||
/// @param[in] state A valid state code for this protocol.
|
||||
/// @return The model code.
|
||||
/// @note This result isn't perfect. Both protocols can look the same but have
|
||||
/// wildly different settings.
|
||||
mirage_ac_remote_model_t IRMirageAc::getModel(const uint8_t *state) {
|
||||
Mirage120Protocol p;
|
||||
std::memcpy(p.raw, state, kMirageStateLength);
|
||||
// Check for KKG29AC1 specific settings.
|
||||
if (p.RecycleHeat || p.Filter || p.Sleep_Kkg29ac1 || p.CleanToggle ||
|
||||
p.IFeel || p.OffTimerEnable || p.OnTimerEnable)
|
||||
return mirage_ac_remote_model_t::KKG29AC1;
|
||||
// Check for things specific to KKG9AC1
|
||||
if ((p.Minutes || p.Seconds) || // Is part of the clock set?
|
||||
// Are the timer times set, but not enabled? (enable check filtered above)
|
||||
(p.OffTimerHours || p.OffTimerMins) ||
|
||||
(p.OnTimerHours || p.OnTimerMins))
|
||||
return mirage_ac_remote_model_t::KKG9AC1;
|
||||
// As the above test has a 1 in 3600+ (for 1 second an hour) chance of a false
|
||||
// negative in theory, we are going assume that anything left should be a
|
||||
// KKG29AC1 model.
|
||||
return mirage_ac_remote_model_t::KKG29AC1; // Default.
|
||||
}
|
||||
|
||||
/// Get the model code of the interal message state.
|
||||
/// @param[in] useRaw If set, we try to get the model info from just the state.
|
||||
/// @return The model code.
|
||||
mirage_ac_remote_model_t IRMirageAc::getModel(const bool useRaw) const {
|
||||
return useRaw ? getModel(_.raw) : _model;
|
||||
}
|
||||
|
||||
/// Set the model code of the interal message state.
|
||||
/// @param[in] model The desired model to use for the settings.
|
||||
void IRMirageAc::setModel(const mirage_ac_remote_model_t model) {
|
||||
if (model != _model) { // Only change things if we need to.
|
||||
// Save the old settings.
|
||||
stdAc::state_t state = toCommon();
|
||||
const uint16_t ontimer = getOnTimer();
|
||||
const uint16_t offtimer = getOffTimer();
|
||||
const bool ifeel = getIFeel();
|
||||
const uint8_t sensor = getSensorTemp();
|
||||
// Change the model.
|
||||
state.model = model;
|
||||
// Restore/Convert the settings.
|
||||
fromCommon(state);
|
||||
setOnTimer(ontimer);
|
||||
setOffTimer(offtimer);
|
||||
setIFeel(ifeel);
|
||||
setSensorTemp(sensor);
|
||||
}
|
||||
}
|
||||
|
||||
/// Calculate and set the checksum values for the internal state.
|
||||
void IRMirageAc::checksum(void) { _.Sum = calculateChecksum(_.raw); }
|
||||
|
||||
/// Verify the checksum is valid for a given state.
|
||||
/// @param[in] data The array to verify the checksum of.
|
||||
/// @return true, if the state has a valid checksum. Otherwise, false.
|
||||
bool IRMirageAc::validChecksum(const uint8_t *data) {
|
||||
return calculateChecksum(data) == data[kMirageStateLength - 1];
|
||||
}
|
||||
|
||||
/// Calculate the checksum for a given state.
|
||||
/// @param[in] data The value to calc the checksum of.
|
||||
/// @return The calculated checksum value.
|
||||
uint8_t IRMirageAc::calculateChecksum(const uint8_t *data) {
|
||||
return sumNibbles(data, kMirageStateLength - 1);
|
||||
}
|
||||
|
||||
/// Set the requested power state of the A/C to on.
|
||||
void IRMirageAc::on(void) { setPower(true); }
|
||||
|
||||
/// Set the requested power state of the A/C to off.
|
||||
void IRMirageAc::off(void) { setPower(false); }
|
||||
|
||||
/// Change the power setting.
|
||||
/// @param[in] on true, the setting is on. false, the setting is off.
|
||||
void IRMirageAc::setPower(bool on) {
|
||||
switch (_model) {
|
||||
case mirage_ac_remote_model_t::KKG29AC1:
|
||||
_.Power = on ? kMirageAcKKG29AC1PowerOn : kMirageAcKKG29AC1PowerOff;
|
||||
break;
|
||||
default:
|
||||
// In order to change the power setting, it seems must be less than
|
||||
// kMirageAcPowerOff. kMirageAcPowerOff is larger than half of the
|
||||
// possible value stored in the allocated bit space.
|
||||
// Thus if the value is larger than kMirageAcPowerOff the power is off.
|
||||
// Less than, then power is on.
|
||||
// We can't just aribitarily add or subtract the value (which analysis
|
||||
// indicates is how the power status changes. Very weird, I know!) as that
|
||||
// is not an idempotent action, we must check if the addition or
|
||||
// substraction is needed first. e.g. via getPower()
|
||||
// i.e. If we added or subtracted twice, we would cause a wrap of the
|
||||
// integer and not get the desired result.
|
||||
if (on)
|
||||
_.SwingAndPower -= getPower() ? 0 : kMirageAcPowerOff;
|
||||
else
|
||||
_.SwingAndPower += getPower() ? kMirageAcPowerOff : 0;
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the value of the current power setting.
|
||||
/// @return true, the setting is on. false, the setting is off.
|
||||
bool IRMirageAc::getPower(void) const {
|
||||
switch (_model) {
|
||||
case mirage_ac_remote_model_t::KKG29AC1:
|
||||
return _.Power == kMirageAcKKG29AC1PowerOn;
|
||||
default:
|
||||
return _.SwingAndPower < kMirageAcPowerOff;
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the operating mode setting of the A/C.
|
||||
/// @return The current operating mode setting.
|
||||
uint8_t IRMirageAc::getMode(void) const { return _.Mode; }
|
||||
|
||||
/// Set the operating mode of the A/C.
|
||||
/// @param[in] mode The desired operating mode.
|
||||
void IRMirageAc::setMode(const uint8_t mode) {
|
||||
switch (mode) {
|
||||
case kMirageAcCool:
|
||||
case kMirageAcDry:
|
||||
case kMirageAcHeat:
|
||||
case kMirageAcFan:
|
||||
case kMirageAcRecycle:
|
||||
_.Mode = mode;
|
||||
// Reset turbo if we have to.
|
||||
setTurbo(getTurbo());
|
||||
break;
|
||||
default: // Default to cool mode for anything else.
|
||||
setMode(kMirageAcCool);
|
||||
}
|
||||
}
|
||||
|
||||
/// Set the temperature.
|
||||
/// @param[in] degrees The temperature in degrees celsius.
|
||||
void IRMirageAc::setTemp(const uint8_t degrees) {
|
||||
// Make sure we have desired temp in the correct range.
|
||||
uint8_t celsius = std::max(degrees, kMirageAcMinTemp);
|
||||
_.Temp = std::min(celsius, kMirageAcMaxTemp) + kMirageAcTempOffset;
|
||||
}
|
||||
|
||||
/// Get the current temperature setting.
|
||||
/// @return The current setting for temp. in degrees celsius.
|
||||
uint8_t IRMirageAc::getTemp(void) const { return _.Temp - kMirageAcTempOffset; }
|
||||
|
||||
/// Set the speed of the fan.
|
||||
/// @param[in] speed The desired setting.
|
||||
void IRMirageAc::setFan(const uint8_t speed) {
|
||||
_.Fan = (speed <= kMirageAcFanLow) ? speed : kMirageAcFanAuto;
|
||||
}
|
||||
|
||||
/// Get the current fan speed setting.
|
||||
/// @return The current fan speed/mode.
|
||||
uint8_t IRMirageAc::getFan(void) const { return _.Fan; }
|
||||
|
||||
/// Change the Turbo setting.
|
||||
/// @param[in] on true, the setting is on. false, the setting is off.
|
||||
void IRMirageAc::setTurbo(bool on) {
|
||||
const bool value = (on && (getMode() == kMirageAcCool));
|
||||
switch (_model) {
|
||||
case mirage_ac_remote_model_t::KKG29AC1:
|
||||
_.Turbo_Kkg29ac1 = value;
|
||||
break;
|
||||
default:
|
||||
_.Turbo_Kkg9ac1 = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the value of the current Turbo setting.
|
||||
/// @return true, the setting is on. false, the setting is off.
|
||||
bool IRMirageAc::getTurbo(void) const {
|
||||
switch (_model) {
|
||||
case mirage_ac_remote_model_t::KKG29AC1: return _.Turbo_Kkg29ac1;
|
||||
default: return _.Turbo_Kkg9ac1;
|
||||
}
|
||||
}
|
||||
|
||||
/// Change the Sleep setting.
|
||||
/// @param[in] on true, the setting is on. false, the setting is off.
|
||||
void IRMirageAc::setSleep(bool on) {
|
||||
switch (_model) {
|
||||
case mirage_ac_remote_model_t::KKG29AC1:
|
||||
_.Sleep_Kkg29ac1 = on;
|
||||
break;
|
||||
default:
|
||||
_.Sleep_Kkg9ac1 = on;
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the value of the current Sleep setting.
|
||||
/// @return true, the setting is on. false, the setting is off.
|
||||
bool IRMirageAc::getSleep(void) const {
|
||||
switch (_model) {
|
||||
case mirage_ac_remote_model_t::KKG29AC1: return _.Sleep_Kkg29ac1;
|
||||
default: return _.Sleep_Kkg9ac1;
|
||||
}
|
||||
}
|
||||
|
||||
/// Change the Light/Display setting.
|
||||
/// @param[in] on true, the setting is on. false, the setting is off.
|
||||
/// @note Light is a toggle on the KKG29AC1 model.
|
||||
void IRMirageAc::setLight(bool on) {
|
||||
switch (_model) {
|
||||
case mirage_ac_remote_model_t::KKG29AC1:
|
||||
_.LightToggle_Kkg29ac1 = on;
|
||||
break;
|
||||
default:
|
||||
_.Light_Kkg9ac1 = on;
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the value of the current Light/Display setting.
|
||||
/// @return true, the setting is on. false, the setting is off.
|
||||
/// @note Light is a toggle on the KKG29AC1 model.
|
||||
bool IRMirageAc::getLight(void) const {
|
||||
switch (_model) {
|
||||
case mirage_ac_remote_model_t::KKG29AC1: return _.LightToggle_Kkg29ac1;
|
||||
default: return _.Light_Kkg9ac1;
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the clock time of the A/C unit.
|
||||
/// @return Nr. of seconds past midnight.
|
||||
uint32_t IRMirageAc::getClock(void) const {
|
||||
switch (_model) {
|
||||
case mirage_ac_remote_model_t::KKG29AC1:
|
||||
return 0;
|
||||
default:
|
||||
return ((bcdToUint8(_.Hours) * 60) + bcdToUint8(_.Minutes)) * 60 +
|
||||
bcdToUint8(_.Seconds);
|
||||
}
|
||||
}
|
||||
|
||||
/// Set the clock time on the A/C unit.
|
||||
/// @param[in] nr_of_seconds Nr. of seconds past midnight.
|
||||
void IRMirageAc::setClock(const uint32_t nr_of_seconds) {
|
||||
switch (_model) {
|
||||
case mirage_ac_remote_model_t::KKG29AC1:
|
||||
_.Minutes = _.Seconds = 0; // No clock setting. Clear it just in case.
|
||||
break;
|
||||
default:
|
||||
uint32_t remaining = std::min(
|
||||
nr_of_seconds, (uint32_t)(24 * 60 * 60 - 1)); // Limit to 23:59:59.
|
||||
_.Seconds = uint8ToBcd(remaining % 60);
|
||||
remaining /= 60;
|
||||
_.Minutes = uint8ToBcd(remaining % 60);
|
||||
remaining /= 60;
|
||||
_.Hours = uint8ToBcd(remaining);
|
||||
}
|
||||
}
|
||||
|
||||
/// Set the Vertical Swing setting/position of the A/C.
|
||||
/// @param[in] position The desired swing setting.
|
||||
void IRMirageAc::setSwingV(const uint8_t position) {
|
||||
switch (position) {
|
||||
case kMirageAcSwingVOff:
|
||||
case kMirageAcSwingVLowest:
|
||||
case kMirageAcSwingVLow:
|
||||
case kMirageAcSwingVMiddle:
|
||||
case kMirageAcSwingVHigh:
|
||||
case kMirageAcSwingVHighest:
|
||||
case kMirageAcSwingVAuto:
|
||||
switch (_model) {
|
||||
case mirage_ac_remote_model_t::KKG29AC1:
|
||||
_.SwingV = (position != kMirageAcSwingVOff);
|
||||
break;
|
||||
default:
|
||||
const bool power = getPower();
|
||||
_.SwingAndPower = position;
|
||||
// Power needs to be reapplied after overwriting SwingAndPower
|
||||
setPower(power);
|
||||
}
|
||||
break;
|
||||
default: // Default to Auto for anything else.
|
||||
setSwingV(kMirageAcSwingVAuto);
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the Vertical Swing setting/position of the A/C.
|
||||
/// @return The desired Vertical Swing setting/position.
|
||||
uint8_t IRMirageAc::getSwingV(void) const {
|
||||
switch (_model) {
|
||||
case mirage_ac_remote_model_t::KKG29AC1:
|
||||
return _.SwingV ? kMirageAcSwingVAuto : kMirageAcSwingVOff;
|
||||
default:
|
||||
return _.SwingAndPower - (getPower() ? 0 : kMirageAcPowerOff);
|
||||
}
|
||||
}
|
||||
|
||||
/// Set the Horizontal Swing setting of the A/C.
|
||||
/// @param[in] on true, the setting is on. false, the setting is off.
|
||||
void IRMirageAc::setSwingH(const bool on) {
|
||||
switch (_model) {
|
||||
case mirage_ac_remote_model_t::KKG29AC1:
|
||||
_.SwingH = on;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the Horizontal Swing setting of the A/C.
|
||||
/// @return on true, the setting is on. false, the setting is off.
|
||||
bool IRMirageAc::getSwingH(void) const {
|
||||
switch (_model) {
|
||||
case mirage_ac_remote_model_t::KKG29AC1: return _.SwingH;
|
||||
default: return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// Set the Quiet setting of the A/C.
|
||||
/// @param[in] on true, the setting is on. false, the setting is off.
|
||||
void IRMirageAc::setQuiet(const bool on) {
|
||||
switch (_model) {
|
||||
case mirage_ac_remote_model_t::KKG29AC1:
|
||||
_.Quiet = on;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the Quiet setting of the A/C.
|
||||
/// @return on true, the setting is on. false, the setting is off.
|
||||
bool IRMirageAc::getQuiet(void) const {
|
||||
switch (_model) {
|
||||
case mirage_ac_remote_model_t::KKG29AC1: return _.Quiet;
|
||||
default: return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// Set the CleanToggle setting of the A/C.
|
||||
/// @param[in] on true, the setting is on. false, the setting is off.
|
||||
void IRMirageAc::setCleanToggle(const bool on) {
|
||||
switch (_model) {
|
||||
case mirage_ac_remote_model_t::KKG29AC1:
|
||||
_.CleanToggle = on;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the Clean Toggle setting of the A/C.
|
||||
/// @return on true, the setting is on. false, the setting is off.
|
||||
bool IRMirageAc::getCleanToggle(void) const {
|
||||
switch (_model) {
|
||||
case mirage_ac_remote_model_t::KKG29AC1: return _.CleanToggle;
|
||||
default: return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// Set the Filter setting of the A/C.
|
||||
/// @param[in] on true, the setting is on. false, the setting is off.
|
||||
void IRMirageAc::setFilter(const bool on) {
|
||||
switch (_model) {
|
||||
case mirage_ac_remote_model_t::KKG29AC1:
|
||||
_.Filter = on;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the Filter setting of the A/C.
|
||||
/// @return on true, the setting is on. false, the setting is off.
|
||||
bool IRMirageAc::getFilter(void) const {
|
||||
switch (_model) {
|
||||
case mirage_ac_remote_model_t::KKG29AC1: return _.Filter;
|
||||
default: return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// Set the IFeel setting of the A/C.
|
||||
/// @param[in] on true, the setting is on. false, the setting is off.
|
||||
void IRMirageAc::setIFeel(const bool on) {
|
||||
switch (_model) {
|
||||
case mirage_ac_remote_model_t::KKG29AC1:
|
||||
_.IFeel = on;
|
||||
if (on) {
|
||||
// If no previous sensor temp, default to currently desired temp.
|
||||
if (!_.SensorTemp) _.SensorTemp = getTemp();
|
||||
} else {
|
||||
_.SensorTemp = 0; // When turning it off, clear the Sensor Temp.
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the IFeel setting of the A/C.
|
||||
/// @return on true, the setting is on. false, the setting is off.
|
||||
bool IRMirageAc::getIFeel(void) const {
|
||||
switch (_model) {
|
||||
case mirage_ac_remote_model_t::KKG29AC1: return _.IFeel;
|
||||
default: return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// Set the Sensor Temp setting of the A/C's remote.
|
||||
/// @param[in] degrees The desired sensor temp. in degrees celsius.
|
||||
void IRMirageAc::setSensorTemp(const uint8_t degrees) {
|
||||
switch (_model) {
|
||||
case mirage_ac_remote_model_t::KKG29AC1:
|
||||
_.SensorTemp = std::min(kMirageAcSensorTempMax, degrees) +
|
||||
kMirageAcSensorTempOffset;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the Sensor Temp setting of the A/C's remote.
|
||||
/// @return The current setting for the sensor temp. in degrees celsius.
|
||||
uint16_t IRMirageAc::getSensorTemp(void) const {
|
||||
switch (_model) {
|
||||
case mirage_ac_remote_model_t::KKG29AC1:
|
||||
return _.SensorTemp - kMirageAcSensorTempOffset;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the number of minutes the On Timer is currently set for.
|
||||
/// @return Nr. of Minutes the timer is set for. 0, is the timer is not in use.
|
||||
uint16_t IRMirageAc::getOnTimer(void) const {
|
||||
switch (_model) {
|
||||
case mirage_ac_remote_model_t::KKG29AC1:
|
||||
return _.OnTimerEnable ? _.OnTimerHours * 60 + _.OnTimerMins : 0;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/// Set the number of minutes for the On Timer.
|
||||
/// @param[in] nr_of_mins How long to set the timer for. 0 disables the timer.
|
||||
void IRMirageAc::setOnTimer(const uint16_t nr_of_mins) {
|
||||
uint16_t mins = std::min(nr_of_mins, (uint16_t)(24 * 60));
|
||||
switch (_model) {
|
||||
case mirage_ac_remote_model_t::KKG29AC1:
|
||||
_.OnTimerEnable = (mins > 0);
|
||||
_.OnTimerHours = mins / 60;
|
||||
_.OnTimerMins = mins % 60;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the number of minutes the Off Timer is currently set for.
|
||||
/// @return Nr. of Minutes the timer is set for. 0, is the timer is not in use.
|
||||
uint16_t IRMirageAc::getOffTimer(void) const {
|
||||
switch (_model) {
|
||||
case mirage_ac_remote_model_t::KKG29AC1:
|
||||
return _.OffTimerEnable ? _.OffTimerHours * 60 + _.OffTimerMins : 0;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/// Set the number of minutes for the Off Timer.
|
||||
/// @param[in] nr_of_mins How long to set the timer for. 0 disables the timer.
|
||||
void IRMirageAc::setOffTimer(const uint16_t nr_of_mins) {
|
||||
uint16_t mins = std::min(nr_of_mins, (uint16_t)(24 * 60));
|
||||
switch (_model) {
|
||||
case mirage_ac_remote_model_t::KKG29AC1:
|
||||
_.OffTimerEnable = (mins > 0);
|
||||
_.OffTimerHours = mins / 60;
|
||||
_.OffTimerMins = mins % 60;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/// Convert a native mode into its stdAc equivalent.
|
||||
/// @param[in] mode The native setting to be converted.
|
||||
/// @return The stdAc equivalent of the native setting.
|
||||
stdAc::opmode_t IRMirageAc::toCommonMode(const uint8_t mode) {
|
||||
switch (mode) {
|
||||
case kMirageAcHeat: return stdAc::opmode_t::kHeat;
|
||||
case kMirageAcDry: return stdAc::opmode_t::kDry;
|
||||
case kMirageAcFan: return stdAc::opmode_t::kFan;
|
||||
default: return stdAc::opmode_t::kCool;
|
||||
}
|
||||
}
|
||||
|
||||
/// Convert a native fan speed into its stdAc equivalent.
|
||||
/// @param[in] speed The native setting to be converted.
|
||||
/// @param[in] model The model type to use to influence the conversion.
|
||||
/// @return The stdAc equivalent of the native setting.
|
||||
stdAc::fanspeed_t IRMirageAc::toCommonFanSpeed(const uint8_t speed,
|
||||
const mirage_ac_remote_model_t model) {
|
||||
switch (model) {
|
||||
case mirage_ac_remote_model_t::KKG29AC1:
|
||||
switch (speed) {
|
||||
case kMirageAcKKG29AC1FanHigh: return stdAc::fanspeed_t::kHigh;
|
||||
case kMirageAcKKG29AC1FanMed: return stdAc::fanspeed_t::kMedium;
|
||||
case kMirageAcKKG29AC1FanLow: return stdAc::fanspeed_t::kLow;
|
||||
default: return stdAc::fanspeed_t::kAuto;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
switch (speed) {
|
||||
case kMirageAcFanHigh: return stdAc::fanspeed_t::kHigh;
|
||||
case kMirageAcFanMed: return stdAc::fanspeed_t::kMedium;
|
||||
case kMirageAcFanLow: return stdAc::fanspeed_t::kLow;
|
||||
default: return stdAc::fanspeed_t::kAuto;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Convert a stdAc::opmode_t enum into its native mode.
|
||||
/// @param[in] mode The enum to be converted.
|
||||
/// @return The native equivalent of the enum.
|
||||
uint8_t IRMirageAc::convertMode(const stdAc::opmode_t mode) {
|
||||
switch (mode) {
|
||||
case stdAc::opmode_t::kHeat: return kMirageAcHeat;
|
||||
case stdAc::opmode_t::kDry: return kMirageAcDry;
|
||||
case stdAc::opmode_t::kFan: return kMirageAcFan;
|
||||
default: return kMirageAcCool;
|
||||
}
|
||||
}
|
||||
|
||||
/// Convert a stdAc::fanspeed_t enum into it's native speed.
|
||||
/// @param[in] speed The enum to be converted.
|
||||
/// @param[in] model The model type to use to influence the conversion.
|
||||
/// @return The native equivalent of the enum.
|
||||
uint8_t IRMirageAc::convertFan(const stdAc::fanspeed_t speed,
|
||||
const mirage_ac_remote_model_t model) {
|
||||
uint8_t low;
|
||||
uint8_t med;
|
||||
switch (model) {
|
||||
case mirage_ac_remote_model_t::KKG29AC1:
|
||||
low = kMirageAcKKG29AC1FanLow;
|
||||
med = kMirageAcKKG29AC1FanMed;
|
||||
break;
|
||||
default:
|
||||
low = kMirageAcFanLow;
|
||||
med = kMirageAcFanMed;
|
||||
}
|
||||
switch (speed) {
|
||||
case stdAc::fanspeed_t::kMin:
|
||||
case stdAc::fanspeed_t::kLow: return low;
|
||||
case stdAc::fanspeed_t::kMedium: return med;
|
||||
case stdAc::fanspeed_t::kHigh:
|
||||
case stdAc::fanspeed_t::kMax: return kMirageAcFanHigh;
|
||||
default: return kMirageAcFanAuto;
|
||||
}
|
||||
}
|
||||
|
||||
/// Convert a stdAc::swingv_t enum into it's native setting.
|
||||
/// @param[in] position The enum to be converted.
|
||||
/// @return The native equivalent of the enum.
|
||||
uint8_t IRMirageAc::convertSwingV(const stdAc::swingv_t position) {
|
||||
switch (position) {
|
||||
case stdAc::swingv_t::kHighest: return kMirageAcSwingVHighest;
|
||||
case stdAc::swingv_t::kHigh: return kMirageAcSwingVHigh;
|
||||
case stdAc::swingv_t::kMiddle: return kMirageAcSwingVMiddle;
|
||||
case stdAc::swingv_t::kLow: return kMirageAcSwingVLow;
|
||||
case stdAc::swingv_t::kLowest: return kMirageAcSwingVLowest;
|
||||
case stdAc::swingv_t::kOff: return kMirageAcSwingVOff;
|
||||
default: return kMirageAcSwingVAuto;
|
||||
}
|
||||
}
|
||||
|
||||
/// Convert a native vertical swing postion to it's common equivalent.
|
||||
/// @param[in] pos A native position to convert.
|
||||
/// @return The common vertical swing position.
|
||||
stdAc::swingv_t IRMirageAc::toCommonSwingV(const uint8_t pos) {
|
||||
switch (pos) {
|
||||
case kMirageAcSwingVHighest: return stdAc::swingv_t::kHighest;
|
||||
case kMirageAcSwingVHigh: return stdAc::swingv_t::kHigh;
|
||||
case kMirageAcSwingVMiddle: return stdAc::swingv_t::kMiddle;
|
||||
case kMirageAcSwingVLow: return stdAc::swingv_t::kLow;
|
||||
case kMirageAcSwingVLowest: return stdAc::swingv_t::kLowest;
|
||||
case kMirageAcSwingVAuto: return stdAc::swingv_t::kAuto;
|
||||
default: return stdAc::swingv_t::kOff;
|
||||
}
|
||||
}
|
||||
|
||||
/// Convert the current internal state into its stdAc::state_t equivalent.
|
||||
/// @return The stdAc equivalent of the native settings.
|
||||
stdAc::state_t IRMirageAc::toCommon(void) const {
|
||||
stdAc::state_t result{};
|
||||
result.protocol = decode_type_t::MIRAGE;
|
||||
result.model = _model;
|
||||
result.power = getPower();
|
||||
result.mode = toCommonMode(_.Mode);
|
||||
result.celsius = true;
|
||||
result.degrees = getTemp();
|
||||
result.sensorTemperature = getSensorTemp();
|
||||
result.fanspeed = toCommonFanSpeed(getFan(), _model);
|
||||
result.swingv = toCommonSwingV(getSwingV());
|
||||
result.swingh = getSwingH() ? stdAc::swingh_t::kAuto : stdAc::swingh_t::kOff;
|
||||
result.turbo = getTurbo();
|
||||
result.light = getLight();
|
||||
result.clean = getCleanToggle();
|
||||
result.filter = getFilter();
|
||||
result.sleep = getSleep() ? 0 : -1;
|
||||
result.quiet = getQuiet();
|
||||
result.clock = getClock() / 60;
|
||||
result.iFeel = getIFeel();
|
||||
// Not supported.
|
||||
result.econo = false;
|
||||
result.beep = false;
|
||||
return result;
|
||||
}
|
||||
|
||||
/// Convert & set a stdAc::state_t to its equivalent internal settings.
|
||||
/// @param[in] state The desired state in stdAc::state_t form.
|
||||
void IRMirageAc::fromCommon(const stdAc::state_t state) {
|
||||
stateReset();
|
||||
_model = (mirage_ac_remote_model_t)state.model; // Set directly to avoid loop
|
||||
setPower(state.power);
|
||||
setTemp(state.celsius ? state.degrees : fahrenheitToCelsius(state.degrees));
|
||||
setMode(convertMode(state.mode));
|
||||
setFan(convertFan(state.fanspeed, _model));
|
||||
setTurbo(state.turbo);
|
||||
setSleep(state.sleep >= 0);
|
||||
setLight(state.light);
|
||||
setSwingV(convertSwingV(state.swingv));
|
||||
setSwingH(state.swingh != stdAc::swingh_t::kOff);
|
||||
setQuiet(state.quiet);
|
||||
setCleanToggle(state.clean);
|
||||
setFilter(state.filter);
|
||||
// setClock() expects seconds, not minutes.
|
||||
setClock((state.clock > 0) ? state.clock * 60 : 0);
|
||||
setIFeel(state.iFeel);
|
||||
if (state.sensorTemperature != kNoTempValue) {
|
||||
setSensorTemp(state.celsius ? state.sensorTemperature
|
||||
: fahrenheitToCelsius(state.sensorTemperature));
|
||||
}
|
||||
// Non-common settings.
|
||||
setOnTimer(0);
|
||||
setOffTimer(0);
|
||||
}
|
||||
|
||||
/// Convert the internal state into a human readable string.
|
||||
/// @return A string containing the settings in human-readable form.
|
||||
String IRMirageAc::toString(void) const {
|
||||
String result = "";
|
||||
result.reserve(240); // Reserve some heap for the string to reduce fragging.
|
||||
result += addModelToString(decode_type_t::MIRAGE, _model, false);
|
||||
result += addBoolToString(getPower(), kPowerStr);
|
||||
result += addModeToString(_.Mode, 0xFF, kMirageAcCool,
|
||||
kMirageAcHeat, kMirageAcDry,
|
||||
kMirageAcFan);
|
||||
result += addTempToString(getTemp());
|
||||
uint8_t fanlow;
|
||||
uint8_t fanmed;
|
||||
switch (_model) {
|
||||
case mirage_ac_remote_model_t::KKG29AC1:
|
||||
fanlow = kMirageAcKKG29AC1FanLow;
|
||||
fanmed = kMirageAcKKG29AC1FanMed;
|
||||
break;
|
||||
default: // e.g. Model KKG9AC1
|
||||
fanlow = kMirageAcFanLow;
|
||||
fanmed = kMirageAcFanMed;
|
||||
}
|
||||
result += addFanToString(_.Fan, kMirageAcFanHigh, fanlow, kMirageAcFanAuto,
|
||||
kMirageAcFanAuto, fanmed);
|
||||
result += addBoolToString(getTurbo(), kTurboStr);
|
||||
result += addBoolToString(getSleep(), kSleepStr);
|
||||
switch (_model) {
|
||||
case mirage_ac_remote_model_t::KKG29AC1:
|
||||
result += addBoolToString(_.Quiet, kQuietStr);
|
||||
result += addToggleToString(getLight(), kLightStr);
|
||||
result += addBoolToString(_.SwingV, kSwingVStr);
|
||||
result += addBoolToString(_.SwingH, kSwingHStr);
|
||||
result += addBoolToString(_.Filter, kFilterStr);
|
||||
result += addToggleToString(_.CleanToggle, kCleanStr);
|
||||
result += addLabeledString(getOnTimer() ? minsToString(getOnTimer())
|
||||
: kOffStr,
|
||||
kOnTimerStr);
|
||||
result += addLabeledString(getOffTimer() ? minsToString(getOffTimer())
|
||||
: kOffStr,
|
||||
kOffTimerStr);
|
||||
result += addBoolToString(_.IFeel, kIFeelStr);
|
||||
if (_.IFeel) {
|
||||
result += addIntToString(getSensorTemp(), kSensorTempStr);
|
||||
result += 'C';
|
||||
}
|
||||
break;
|
||||
default: // e.g. Model KKG9AC1
|
||||
result += addBoolToString(getLight(), kLightStr);
|
||||
result += addSwingVToString(getSwingV(),
|
||||
kMirageAcSwingVAuto,
|
||||
kMirageAcSwingVHighest,
|
||||
kMirageAcSwingVHigh,
|
||||
0xFF, // Unused.
|
||||
kMirageAcSwingVMiddle,
|
||||
0xFF, // Unused.
|
||||
kMirageAcSwingVLow,
|
||||
kMirageAcSwingVLowest,
|
||||
kMirageAcSwingVOff,
|
||||
0xFF, 0xFF, 0xFF); // Unused.
|
||||
result += addLabeledString(minsToString(getClock() / 60), kClockStr);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
#endif // DECODE_MIRAGE
|
||||
277
yoRadio/src/IRremoteESP8266/ir_Mirage.h
Normal file
277
yoRadio/src/IRremoteESP8266/ir_Mirage.h
Normal file
@@ -0,0 +1,277 @@
|
||||
// Copyright 2020-2021 David Conran (crankyoldgit)
|
||||
/// @file
|
||||
/// @brief Support for Mirage protocol
|
||||
/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1289
|
||||
/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1573
|
||||
|
||||
|
||||
// Supports:
|
||||
// Brand: Mirage, Model: VLU series A/C
|
||||
// Brand: Maxell, Model: MX-CH18CF A/C
|
||||
// Brand: Maxell, Model: KKG9A-C1 remote
|
||||
// Brand: Tronitechnik, Model: Reykir 9000 A/C
|
||||
// Brand: Tronitechnik, Model: KKG29A-C1 remote
|
||||
|
||||
#ifndef IR_MIRAGE_H_
|
||||
#define IR_MIRAGE_H_
|
||||
|
||||
#define __STDC_LIMIT_MACROS
|
||||
#include <stdint.h>
|
||||
#ifndef UNIT_TEST
|
||||
#include <Arduino.h>
|
||||
#endif
|
||||
#include "IRremoteESP8266.h"
|
||||
#include "IRsend.h"
|
||||
#ifdef UNIT_TEST
|
||||
#include "IRsend_test.h"
|
||||
#endif
|
||||
|
||||
/// Native representation of a Mirage 120-bit A/C message.
|
||||
/// @see https://docs.google.com/spreadsheets/d/1Ucu9mOOIIJoWQjUJq_VCvwgV3EwKaRk8K2AuZgccYEk/edit#gid=0
|
||||
union Mirage120Protocol{
|
||||
uint8_t raw[kMirageStateLength]; ///< The state in code form.
|
||||
struct { // Common
|
||||
// Byte 0
|
||||
uint8_t Header :8; // Header. (0x56)
|
||||
// Byte 1
|
||||
uint8_t Temp :8; // Celsius minus 0x5C.
|
||||
// Byte 2
|
||||
uint8_t :8; // Unknown / Unused.
|
||||
// Byte 3
|
||||
uint8_t :8; // Unknown / Unused.
|
||||
// Byte 4
|
||||
uint8_t Fan :2; // Fan Speed.
|
||||
uint8_t :2; // Unknown / Unused.
|
||||
uint8_t Mode :4; // Cool, Heat, Dry, Fan, Recycle
|
||||
// Byte 5
|
||||
uint8_t :8;
|
||||
// Byte 6
|
||||
uint8_t :8;
|
||||
// Byte 7
|
||||
uint8_t :8;
|
||||
// Byte 8
|
||||
uint8_t :8;
|
||||
// Byte 9
|
||||
uint8_t :8;
|
||||
// Byte 10
|
||||
uint8_t :8;
|
||||
// Byte 11
|
||||
uint8_t :8;
|
||||
// Byte 12
|
||||
uint8_t :8;
|
||||
// Byte 13
|
||||
uint8_t :8;
|
||||
// Byte 14
|
||||
uint8_t Sum :8; // Sum of all the previous nibbles.
|
||||
};
|
||||
struct { // KKG9AC1 remote
|
||||
// Byte 0
|
||||
uint8_t :8; // Header
|
||||
// Byte 1
|
||||
uint8_t :8; // Temp
|
||||
// Byte 2
|
||||
uint8_t :8; // Unknown / Unused.
|
||||
// Byte 3
|
||||
uint8_t :3; // Unknown / Unused.
|
||||
uint8_t Light_Kkg9ac1 :1; // Aka. Display. Seems linked to Sleep mode.
|
||||
uint8_t :4; // Unknown / Unused.
|
||||
// Byte 4
|
||||
uint8_t :8; // Fan & Mode
|
||||
// Byte 5
|
||||
uint8_t :1; // Unknown
|
||||
uint8_t SwingAndPower :7;
|
||||
// Byte 6
|
||||
uint8_t :7; // Unknown / Unused.
|
||||
uint8_t Sleep_Kkg9ac1 :1; // Sleep mode on or off.
|
||||
// Byte 7
|
||||
uint8_t :3; // Unknown / Unused.
|
||||
uint8_t Turbo_Kkg9ac1 :1; // Turbo mode on or off. Only works in Cool mode.
|
||||
uint8_t :4; // Unknown / Unused.
|
||||
// Byte 8
|
||||
uint8_t :8; // Unknown / Unused.
|
||||
// Byte 9
|
||||
uint8_t :8; // Unknown / Unused.
|
||||
// Byte 10
|
||||
uint8_t :8; // Unknown / Unused.
|
||||
// Byte 11
|
||||
uint8_t Seconds :8; // Nr. of Seconds in BCD.
|
||||
// Byte 12
|
||||
uint8_t Minutes :8; // Nr. of Minutes in BCD.
|
||||
// Byte 13
|
||||
uint8_t Hours :8; // Nr. of Hours in BCD.
|
||||
// Byte 14
|
||||
uint8_t :8; // Sum
|
||||
};
|
||||
struct { // KKG29A-C1 remote
|
||||
// Byte 0
|
||||
uint8_t :8; // Header
|
||||
// Byte 1
|
||||
uint8_t :8; // Temp
|
||||
// Byte 2
|
||||
uint8_t :8;
|
||||
// Byte 3
|
||||
uint8_t Quiet :1;
|
||||
uint8_t :7;
|
||||
// Byte 4
|
||||
uint8_t :2; // Fan
|
||||
uint8_t OffTimerEnable :1;
|
||||
uint8_t OnTimerEnable :1;
|
||||
uint8_t :3; // Mode
|
||||
uint8_t :1;
|
||||
// Byte 5
|
||||
uint8_t SwingH :1;
|
||||
uint8_t SwingV :1;
|
||||
uint8_t LightToggle_Kkg29ac1 :1; // Aka. Display Toggle.
|
||||
uint8_t :3;
|
||||
uint8_t Power :2;
|
||||
// Byte 6
|
||||
uint8_t :1;
|
||||
uint8_t Filter :1; // Aka. UVC
|
||||
uint8_t :1;
|
||||
uint8_t Sleep_Kkg29ac1 :1; // Sleep mode on or off.
|
||||
uint8_t :2;
|
||||
uint8_t RecycleHeat :1;
|
||||
uint8_t :1;
|
||||
// Byte 7
|
||||
uint8_t SensorTemp :6; // Temperature at the remote
|
||||
uint8_t CleanToggle :1;
|
||||
uint8_t IFeel :1;
|
||||
// Byte 8
|
||||
uint8_t OnTimerHours :5;
|
||||
uint8_t :2;
|
||||
uint8_t Turbo_Kkg29ac1 :1; // Turbo mode on or off.
|
||||
// Byte 9
|
||||
uint8_t OnTimerMins :6;
|
||||
uint8_t :2;
|
||||
// Byte 10
|
||||
uint8_t OffTimerHours :5;
|
||||
uint8_t :3;
|
||||
// Byte 11
|
||||
uint8_t OffTimerMins :6;
|
||||
uint8_t :2;
|
||||
// Byte 12
|
||||
uint8_t :8;
|
||||
// Byte 13
|
||||
uint8_t :8;
|
||||
// Byte 14
|
||||
uint8_t :8; // Sum
|
||||
};
|
||||
};
|
||||
|
||||
// Constants
|
||||
const uint8_t kMirageAcHeat = 0b001; // 1
|
||||
const uint8_t kMirageAcCool = 0b010; // 2
|
||||
const uint8_t kMirageAcDry = 0b011; // 3
|
||||
const uint8_t kMirageAcRecycle = 0b100; // 4
|
||||
const uint8_t kMirageAcFan = 0b101; // 5
|
||||
|
||||
const uint8_t kMirageAcFanAuto = 0b00; // 0
|
||||
const uint8_t kMirageAcFanHigh = 0b01; // 1
|
||||
const uint8_t kMirageAcFanMed = 0b10; // 2
|
||||
const uint8_t kMirageAcFanLow = 0b11; // 3
|
||||
const uint8_t kMirageAcKKG29AC1FanAuto = 0b00; // 0
|
||||
const uint8_t kMirageAcKKG29AC1FanHigh = 0b01; // 1
|
||||
const uint8_t kMirageAcKKG29AC1FanLow = 0b10; // 2
|
||||
const uint8_t kMirageAcKKG29AC1FanMed = 0b11; // 3
|
||||
|
||||
const uint8_t kMirageAcMinTemp = 16; // 16C
|
||||
const uint8_t kMirageAcMaxTemp = 32; // 32C
|
||||
const uint8_t kMirageAcTempOffset = 0x5C;
|
||||
const uint8_t kMirageAcSensorTempOffset = 20;
|
||||
const uint8_t kMirageAcSensorTempMax = 43; // Celsius
|
||||
|
||||
const uint8_t kMirageAcPowerOff = 0x5F;
|
||||
const uint8_t kMirageAcSwingVOff = 0b0000; // 0
|
||||
const uint8_t kMirageAcSwingVLowest = 0b0011; // 3
|
||||
const uint8_t kMirageAcSwingVLow = 0b0101; // 5
|
||||
const uint8_t kMirageAcSwingVMiddle = 0b0111; // 7
|
||||
const uint8_t kMirageAcSwingVHigh = 0b1001; // 9
|
||||
const uint8_t kMirageAcSwingVHighest = 0b1011; // 11
|
||||
const uint8_t kMirageAcSwingVAuto = 0b1101; // 13
|
||||
|
||||
|
||||
/// Class for handling detailed Mirage 120-bit A/C messages.
|
||||
/// @note Inspired and derived from the work done at: https://github.com/r45635/HVAC-IR-Control
|
||||
/// @warning Consider this very alpha code. Seems to work, but not validated.
|
||||
class IRMirageAc {
|
||||
public:
|
||||
explicit IRMirageAc(const uint16_t pin, const bool inverted = false,
|
||||
const bool use_modulation = true);
|
||||
void stateReset(void);
|
||||
#if SEND_MIRAGE
|
||||
void send(const uint16_t repeat = kMirageMinRepeat);
|
||||
/// 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_MIRAGE
|
||||
void begin(void);
|
||||
void on(void);
|
||||
void off(void);
|
||||
void setPower(const bool on);
|
||||
bool getPower(void) const;
|
||||
void setTemp(const uint8_t degrees);
|
||||
uint8_t getTemp(void) const;
|
||||
void setFan(const uint8_t speed);
|
||||
uint8_t getFan(void) const;
|
||||
void setMode(const uint8_t mode);
|
||||
uint8_t getMode(void) const;
|
||||
uint8_t* getRaw(void);
|
||||
void setRaw(const uint8_t* data);
|
||||
uint32_t getClock(void) const;
|
||||
void setClock(const uint32_t nr_of_seconds);
|
||||
void setTurbo(const bool on);
|
||||
bool getTurbo(void) const;
|
||||
void setLight(const bool on);
|
||||
bool getLight(void) const;
|
||||
void setSleep(const bool on);
|
||||
bool getSleep(void) const;
|
||||
void setSwingV(const uint8_t position);
|
||||
uint8_t getSwingV(void) const;
|
||||
void setSwingH(const bool on);
|
||||
bool getSwingH(void) const;
|
||||
void setQuiet(const bool on);
|
||||
bool getQuiet(void) const;
|
||||
void setCleanToggle(const bool on);
|
||||
bool getCleanToggle(void) const;
|
||||
void setFilter(const bool on);
|
||||
bool getFilter(void) const;
|
||||
void setIFeel(const bool on);
|
||||
bool getIFeel(void) const;
|
||||
void setSensorTemp(const uint8_t degrees);
|
||||
uint16_t getSensorTemp(void) const;
|
||||
uint16_t getOnTimer(void) const;
|
||||
uint16_t getOffTimer(void) const;
|
||||
void setOnTimer(const uint16_t nr_of_mins);
|
||||
void setOffTimer(const uint16_t nr_of_mins);
|
||||
mirage_ac_remote_model_t getModel(const bool useRaw = false) const;
|
||||
void setModel(const mirage_ac_remote_model_t model);
|
||||
static mirage_ac_remote_model_t getModel(const uint8_t *state);
|
||||
static bool validChecksum(const uint8_t* data);
|
||||
static uint8_t calculateChecksum(const uint8_t* data);
|
||||
static uint8_t convertMode(const stdAc::opmode_t mode);
|
||||
static uint8_t convertFan(const stdAc::fanspeed_t speed,
|
||||
const mirage_ac_remote_model_t model = mirage_ac_remote_model_t::KKG9AC1);
|
||||
static uint8_t convertSwingV(const stdAc::swingv_t position);
|
||||
static stdAc::opmode_t toCommonMode(const uint8_t mode);
|
||||
static stdAc::fanspeed_t toCommonFanSpeed(const uint8_t speed,
|
||||
const mirage_ac_remote_model_t model = mirage_ac_remote_model_t::KKG9AC1);
|
||||
static stdAc::swingv_t toCommonSwingV(const uint8_t pos);
|
||||
stdAc::state_t toCommon(void) const;
|
||||
void fromCommon(const stdAc::state_t state);
|
||||
String toString(void) const;
|
||||
#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
|
||||
Mirage120Protocol _;
|
||||
mirage_ac_remote_model_t _model;
|
||||
void checksum(void);
|
||||
};
|
||||
#endif // IR_MIRAGE_H_
|
||||
1727
yoRadio/src/IRremoteESP8266/ir_Mitsubishi.cpp
Normal file
1727
yoRadio/src/IRremoteESP8266/ir_Mitsubishi.cpp
Normal file
File diff suppressed because it is too large
Load Diff
456
yoRadio/src/IRremoteESP8266/ir_Mitsubishi.h
Normal file
456
yoRadio/src/IRremoteESP8266/ir_Mitsubishi.h
Normal file
@@ -0,0 +1,456 @@
|
||||
// Copyright 2009 Ken Shirriff
|
||||
// Copyright 2017-2021 David Conran
|
||||
// Copyright 2019 Mark Kuchel
|
||||
|
||||
/// @file
|
||||
/// @brief Support for Mitsubishi protocols.
|
||||
/// Mitsubishi (TV) decoding added from https://github.com/z3t0/Arduino-IRremote
|
||||
/// Mitsubishi (TV) sending & Mitsubishi A/C support added by David Conran
|
||||
/// @see GlobalCache's Control Tower's Mitsubishi TV data.
|
||||
/// @see https://github.com/marcosamarinho/IRremoteESP8266/blob/master/ir_Mitsubishi.cpp
|
||||
/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/441
|
||||
/// @see https://github.com/r45635/HVAC-IR-Control/blob/master/HVAC_ESP8266/HVAC_ESP8266.ino#L84
|
||||
/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/619
|
||||
/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/888
|
||||
/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/947
|
||||
/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1398
|
||||
/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1399
|
||||
/// @see https://github.com/kuchel77
|
||||
|
||||
// Supports:
|
||||
// Brand: Mitsubishi, Model: TV (MITSUBISHI)
|
||||
// Brand: Mitsubishi, Model: HC3000 Projector (MITSUBISHI2)
|
||||
// Brand: Mitsubishi, Model: MS-GK24VA A/C
|
||||
// Brand: Mitsubishi, Model: KM14A 0179213 remote
|
||||
// Brand: Mitsubishi Electric, Model: PEAD-RP71JAA Ducted A/C (MITSUBISHI136)
|
||||
// Brand: Mitsubishi Electric, Model: 001CP T7WE10714 remote (MITSUBISHI136)
|
||||
// Brand: Mitsubishi Electric, Model: MSH-A24WV A/C (MITSUBISHI112)
|
||||
// Brand: Mitsubishi Electric, Model: MUH-A24WV A/C (MITSUBISHI112)
|
||||
// Brand: Mitsubishi Electric, Model: KPOA remote (MITSUBISHI112)
|
||||
// Brand: Mitsubishi Electric, Model: MLZ-RX5017AS A/C (MITSUBISHI_AC)
|
||||
// Brand: Mitsubishi Electric, Model: SG153/M21EDF426 remote (MITSUBISHI_AC)
|
||||
// Brand: Mitsubishi Electric, Model: MSZ-GV2519 A/C (MITSUBISHI_AC)
|
||||
// Brand: Mitsubishi Electric, Model: RH151/M21ED6426 remote (MITSUBISHI_AC)
|
||||
// Brand: Mitsubishi Electric, Model: MSZ-SF25VE3 A/C (MITSUBISHI_AC)
|
||||
// Brand: Mitsubishi Electric, Model: SG15D remote (MITSUBISHI_AC)
|
||||
// Brand: Mitsubishi Electric, Model: MSZ-ZW4017S A/C (MITSUBISHI_AC)
|
||||
// Brand: Mitsubishi Electric, Model: MSZ-FHnnVE A/C (MITSUBISHI_AC)
|
||||
// Brand: Mitsubishi Electric, Model: RH151 remote (MITSUBISHI_AC)
|
||||
// Brand: Mitsubishi Electric, Model: PAR-FA32MA remote (MITSUBISHI136)
|
||||
|
||||
#ifndef IR_MITSUBISHI_H_
|
||||
#define IR_MITSUBISHI_H_
|
||||
|
||||
#define __STDC_LIMIT_MACROS
|
||||
#include <stdint.h>
|
||||
#ifndef UNIT_TEST
|
||||
#include <Arduino.h>
|
||||
#endif
|
||||
#include "IRremoteESP8266.h"
|
||||
#include "IRsend.h"
|
||||
#ifdef UNIT_TEST
|
||||
#include "IRsend_test.h"
|
||||
#endif
|
||||
|
||||
/// Native representation of a Mitsubishi 144-bit A/C message.
|
||||
union Mitsubishi144Protocol{
|
||||
uint8_t raw[kMitsubishiACStateLength]; ///< The state in code form.
|
||||
struct {
|
||||
// Byte 0~4
|
||||
uint8_t pad0[5];
|
||||
// Byte 5
|
||||
uint8_t :5;
|
||||
uint8_t Power :1;
|
||||
uint8_t :2;
|
||||
// Byte 6
|
||||
uint8_t :3;
|
||||
uint8_t Mode :3;
|
||||
uint8_t ISee : 1;
|
||||
uint8_t :1;
|
||||
// Byte 7
|
||||
uint8_t Temp :4;
|
||||
uint8_t HalfDegree :1;
|
||||
uint8_t :3;
|
||||
// Byte 8
|
||||
uint8_t :4;
|
||||
uint8_t WideVane:4; // SwingH
|
||||
// Byte 9
|
||||
uint8_t Fan :3;
|
||||
uint8_t Vane :3; // SwingV or VaneRight
|
||||
uint8_t VaneBit :1;
|
||||
uint8_t FanAuto :1;
|
||||
// Byte 10
|
||||
uint8_t Clock :8;
|
||||
// Byte 11
|
||||
uint8_t StopClock :8;
|
||||
// Byte 12
|
||||
uint8_t StartClock:8;
|
||||
// Byte 13
|
||||
uint8_t Timer :3;
|
||||
uint8_t WeeklyTimer :1;
|
||||
uint8_t :4;
|
||||
// Byte 14
|
||||
uint8_t :5;
|
||||
uint8_t Ecocool :1;
|
||||
uint8_t :2;
|
||||
// Byte 15
|
||||
uint8_t DirectIndirect:2;
|
||||
uint8_t AbsenseDetect :1;
|
||||
uint8_t :2;
|
||||
uint8_t iSave10C :1; // i-SAVE:mode=Heat & iSave=on AND 10C on remote
|
||||
uint8_t :2;
|
||||
// Byte 16
|
||||
uint8_t :1;
|
||||
uint8_t NaturalFlow :1;
|
||||
uint8_t :1;
|
||||
uint8_t VaneLeft :3; // SwingV(Left)
|
||||
uint8_t :2;
|
||||
// Byte 17
|
||||
uint8_t Sum :8;
|
||||
};
|
||||
};
|
||||
|
||||
// Constants
|
||||
const uint8_t kMitsubishiAcAuto = 0b100;
|
||||
const uint8_t kMitsubishiAcCool = 0b011;
|
||||
const uint8_t kMitsubishiAcDry = 0b010;
|
||||
const uint8_t kMitsubishiAcHeat = 0b001;
|
||||
const uint8_t kMitsubishiAcFan = 0b111;
|
||||
const uint8_t kMitsubishiAcFanAuto = 0;
|
||||
const uint8_t kMitsubishiAcFanMax = 5;
|
||||
const uint8_t kMitsubishiAcFanRealMax = 4;
|
||||
const uint8_t kMitsubishiAcFanSilent = 6;
|
||||
const uint8_t kMitsubishiAcFanQuiet = kMitsubishiAcFanSilent;
|
||||
const float kMitsubishiAcMinTemp = 16.0; // 16C
|
||||
const float kMitsubishiAcMaxTemp = 31.0; // 31C
|
||||
const uint8_t kMitsubishiAcVaneAuto = 0b000; // Vanes move when AC wants to.
|
||||
const uint8_t kMitsubishiAcVaneHighest = 0b001;
|
||||
const uint8_t kMitsubishiAcVaneHigh = 0b010;
|
||||
const uint8_t kMitsubishiAcVaneMiddle = 0b011;
|
||||
const uint8_t kMitsubishiAcVaneLow = 0b100;
|
||||
const uint8_t kMitsubishiAcVaneLowest = 0b101;
|
||||
const uint8_t kMitsubishiAcVaneSwing = 0b111; // Vanes move all the time.
|
||||
const uint8_t kMitsubishiAcVaneAutoMove = kMitsubishiAcVaneSwing; // Deprecated
|
||||
const uint8_t kMitsubishiAcWideVaneLeftMax = 0b0001; // 1
|
||||
const uint8_t kMitsubishiAcWideVaneLeft = 0b0010; // 2
|
||||
const uint8_t kMitsubishiAcWideVaneMiddle = 0b0011; // 3
|
||||
const uint8_t kMitsubishiAcWideVaneRight = 0b0100; // 4
|
||||
const uint8_t kMitsubishiAcWideVaneRightMax = 0b0101; // 5
|
||||
const uint8_t kMitsubishiAcWideVaneWide = 0b0110; // 6
|
||||
const uint8_t kMitsubishiAcWideVaneAuto = 0b1000; // 8
|
||||
const uint8_t kMitsubishiAcDirectOff = 0b00; // Vanes move when AC wants to.
|
||||
const uint8_t kMitsubishiAcIndirect = 0b01;
|
||||
const uint8_t kMitsubishiAcDirect = 0b11;
|
||||
const uint8_t kMitsubishiAcNoTimer = 0;
|
||||
const uint8_t kMitsubishiAcStartTimer = 5;
|
||||
const uint8_t kMitsubishiAcStopTimer = 3;
|
||||
const uint8_t kMitsubishiAcStartStopTimer = 7;
|
||||
|
||||
/// Native representation of a Mitsubishi 136-bit A/C message.
|
||||
union Mitsubishi136Protocol{
|
||||
uint8_t raw[kMitsubishi136StateLength]; ///< The state in code form.
|
||||
struct {
|
||||
// Byte 0~4
|
||||
uint8_t pad[5];
|
||||
// Byte 5
|
||||
uint8_t :6;
|
||||
uint8_t Power :1;
|
||||
uint8_t :1;
|
||||
// Byte 6
|
||||
uint8_t Mode :3;
|
||||
uint8_t :1;
|
||||
uint8_t Temp :4;
|
||||
// Byte 7
|
||||
uint8_t :1;
|
||||
uint8_t Fan :2;
|
||||
uint8_t :1;
|
||||
uint8_t SwingV :4;
|
||||
};
|
||||
};
|
||||
|
||||
const uint8_t kMitsubishi136PowerByte = 5;
|
||||
const uint8_t kMitsubishi136MinTemp = 17; // 17C
|
||||
const uint8_t kMitsubishi136MaxTemp = 30; // 30C
|
||||
const uint8_t kMitsubishi136Fan = 0b000;
|
||||
const uint8_t kMitsubishi136Cool = 0b001;
|
||||
const uint8_t kMitsubishi136Heat = 0b010;
|
||||
const uint8_t kMitsubishi136Auto = 0b011;
|
||||
const uint8_t kMitsubishi136Dry = 0b101;
|
||||
const uint8_t kMitsubishi136SwingVLowest = 0b0000;
|
||||
const uint8_t kMitsubishi136SwingVLow = 0b0001;
|
||||
const uint8_t kMitsubishi136SwingVHigh = 0b0010;
|
||||
const uint8_t kMitsubishi136SwingVHighest = 0b0011;
|
||||
const uint8_t kMitsubishi136SwingVAuto = 0b1100;
|
||||
const uint8_t kMitsubishi136FanMin = 0b00;
|
||||
const uint8_t kMitsubishi136FanLow = 0b01;
|
||||
const uint8_t kMitsubishi136FanMed = 0b10;
|
||||
const uint8_t kMitsubishi136FanMax = 0b11;
|
||||
const uint8_t kMitsubishi136FanQuiet = kMitsubishi136FanMin;
|
||||
|
||||
/// Native representation of a Mitsubishi 112-bit A/C message.
|
||||
union Mitsubishi112Protocol{
|
||||
uint8_t raw[kMitsubishi112StateLength]; ///< The state in code form.
|
||||
struct {
|
||||
// Byte 0~4
|
||||
uint8_t pad0[5];
|
||||
// Byte 5
|
||||
uint8_t :2;
|
||||
uint8_t Power :1;
|
||||
uint8_t :5;
|
||||
// Byte 6
|
||||
uint8_t Mode :3;
|
||||
uint8_t :5;
|
||||
// Byte 7
|
||||
uint8_t Temp :4;
|
||||
uint8_t :4;
|
||||
// Byte 8
|
||||
uint8_t Fan :3;
|
||||
uint8_t SwingV :3;
|
||||
uint8_t :2;
|
||||
// Byte 9~11
|
||||
uint8_t pad1[3];
|
||||
// Byte 12
|
||||
uint8_t :2;
|
||||
uint8_t SwingH :4;
|
||||
uint8_t :2;
|
||||
// Byte 13
|
||||
uint8_t Sum :8;
|
||||
};
|
||||
};
|
||||
|
||||
const uint8_t kMitsubishi112Cool = 0b011;
|
||||
const uint8_t kMitsubishi112Heat = 0b001;
|
||||
const uint8_t kMitsubishi112Auto = 0b111;
|
||||
const uint8_t kMitsubishi112Dry = 0b010;
|
||||
|
||||
const uint8_t kMitsubishi112MinTemp = 16; // 16C
|
||||
const uint8_t kMitsubishi112MaxTemp = 31; // 31C
|
||||
|
||||
const uint8_t kMitsubishi112FanMin = 0b010;
|
||||
const uint8_t kMitsubishi112FanLow = 0b011;
|
||||
const uint8_t kMitsubishi112FanMed = 0b101;
|
||||
const uint8_t kMitsubishi112FanMax = 0b000;
|
||||
const uint8_t kMitsubishi112FanQuiet = kMitsubishi112FanMin;
|
||||
const uint8_t kMitsubishi112SwingVLowest = 0b101;
|
||||
const uint8_t kMitsubishi112SwingVLow = 0b100;
|
||||
const uint8_t kMitsubishi112SwingVMiddle = 0b011;
|
||||
const uint8_t kMitsubishi112SwingVHigh = 0b010;
|
||||
const uint8_t kMitsubishi112SwingVHighest = 0b001;
|
||||
const uint8_t kMitsubishi112SwingVAuto = 0b111;
|
||||
|
||||
const uint8_t kMitsubishi112SwingHLeftMax = 0b0001;
|
||||
const uint8_t kMitsubishi112SwingHLeft = 0b0010;
|
||||
const uint8_t kMitsubishi112SwingHMiddle = 0b0011;
|
||||
const uint8_t kMitsubishi112SwingHRight = 0b0100;
|
||||
const uint8_t kMitsubishi112SwingHRightMax = 0b0101;
|
||||
const uint8_t kMitsubishi112SwingHWide = 0b1000;
|
||||
const uint8_t kMitsubishi112SwingHAuto = 0b1100;
|
||||
|
||||
// Legacy defines (Deprecated)
|
||||
#define MITSUBISHI_AC_VANE_AUTO_MOVE kMitsubishiAcVaneAutoMove
|
||||
#define MITSUBISHI_AC_VANE_AUTO kMitsubishiAcVaneAuto
|
||||
#define MITSUBISHI_AC_MIN_TEMP kMitsubishiAcMinTemp
|
||||
#define MITSUBISHI_AC_MAX_TEMP kMitsubishiAcMaxTemp
|
||||
#define MITSUBISHI_AC_HEAT kMitsubishiAcHeat
|
||||
#define MITSUBISHI_AC_FAN_SILENT kMitsubishiAcFanSilent
|
||||
#define MITSUBISHI_AC_FAN_REAL_MAX kMitsubishiAcFanRealMax
|
||||
#define MITSUBISHI_AC_FAN_MAX kMitsubishiAcFanMax
|
||||
#define MITSUBISHI_AC_FAN_AUTO kMitsubishiAcFanAuto
|
||||
#define MITSUBISHI_AC_DRY kMitsubishiAcDry
|
||||
#define MITSUBISHI_AC_COOL kMitsubishiAcCool
|
||||
#define MITSUBISHI_AC_AUTO kMitsubishiAcAuto
|
||||
|
||||
|
||||
/// Class for handling detailed Mitsubishi 144-bit A/C messages.
|
||||
/// @note Inspired and derived from the work done at: https://github.com/r45635/HVAC-IR-Control
|
||||
/// @warning Consider this very alpha code. Seems to work, but not validated.
|
||||
class IRMitsubishiAC {
|
||||
public:
|
||||
explicit IRMitsubishiAC(const uint16_t pin, const bool inverted = false,
|
||||
const bool use_modulation = true);
|
||||
void stateReset(void);
|
||||
static bool validChecksum(const uint8_t* data);
|
||||
#if SEND_MITSUBISHI_AC
|
||||
void send(const uint16_t repeat = kMitsubishiACMinRepeat);
|
||||
/// 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_MITSUBISHI_AC
|
||||
void begin(void);
|
||||
void on(void);
|
||||
void off(void);
|
||||
void setPower(const bool on);
|
||||
bool getPower(void) const;
|
||||
void setTemp(const float degrees);
|
||||
float getTemp(void) const;
|
||||
void setFan(const uint8_t speed);
|
||||
uint8_t getFan(void) const;
|
||||
void setMode(const uint8_t mode);
|
||||
uint8_t getMode(void) const;
|
||||
void setISave10C(const bool state);
|
||||
bool getISave10C(void) const;
|
||||
void setISee(const bool state);
|
||||
bool getISee(void) const;
|
||||
void setDirectIndirect(const uint8_t position);
|
||||
uint8_t getDirectIndirect(void) const;
|
||||
void setEcocool(const bool state);
|
||||
bool getEcocool(void) const;
|
||||
void setAbsenseDetect(const bool state);
|
||||
bool getAbsenseDetect(void) const;
|
||||
void setNaturalFlow(const bool state);
|
||||
bool getNaturalFlow(void) const;
|
||||
void setVane(const uint8_t position); // controls RIGHT vane on some models
|
||||
uint8_t getVane(void) const;
|
||||
void setVaneLeft(const uint8_t position);
|
||||
uint8_t getVaneLeft(void) const;
|
||||
void setWideVane(const uint8_t position);
|
||||
uint8_t getWideVane(void) const;
|
||||
uint8_t* getRaw(void);
|
||||
void setRaw(const uint8_t* data);
|
||||
uint8_t getClock(void) const;
|
||||
void setClock(const uint8_t clock);
|
||||
uint8_t getStartClock(void) const;
|
||||
void setStartClock(const uint8_t clock);
|
||||
uint8_t getStopClock(void) const;
|
||||
void setStopClock(const uint8_t clock);
|
||||
uint8_t getTimer(void) const;
|
||||
void setTimer(const uint8_t timer);
|
||||
bool getWeeklyTimerEnabled(void) const;
|
||||
void setWeeklyTimerEnabled(const bool on);
|
||||
static uint8_t convertMode(const stdAc::opmode_t mode);
|
||||
static uint8_t convertFan(const stdAc::fanspeed_t speed);
|
||||
static uint8_t convertSwingV(const stdAc::swingv_t position);
|
||||
static uint8_t convertSwingH(const stdAc::swingh_t position);
|
||||
static stdAc::opmode_t toCommonMode(const uint8_t mode);
|
||||
static stdAc::fanspeed_t toCommonFanSpeed(const uint8_t speed);
|
||||
static stdAc::swingv_t toCommonSwingV(const uint8_t pos);
|
||||
static stdAc::swingh_t toCommonSwingH(const uint8_t pos);
|
||||
stdAc::state_t toCommon(void) const;
|
||||
String toString(void) const;
|
||||
#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
|
||||
Mitsubishi144Protocol _;
|
||||
void checksum(void);
|
||||
static uint8_t calculateChecksum(const uint8_t* data);
|
||||
};
|
||||
|
||||
/// Class for handling detailed Mitsubishi 136-bit A/C messages.
|
||||
class IRMitsubishi136 {
|
||||
public:
|
||||
explicit IRMitsubishi136(const uint16_t pin, const bool inverted = false,
|
||||
const bool use_modulation = true);
|
||||
void stateReset(void);
|
||||
#if SEND_MITSUBISHI136
|
||||
void send(const uint16_t repeat = kMitsubishi136MinRepeat);
|
||||
/// 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_MITSUBISHI136
|
||||
void begin(void);
|
||||
static bool validChecksum(const uint8_t* data,
|
||||
const uint16_t len = kMitsubishi136StateLength);
|
||||
void on(void);
|
||||
void off(void);
|
||||
void setPower(const bool on);
|
||||
bool getPower(void) const;
|
||||
void setTemp(const uint8_t degrees);
|
||||
uint8_t getTemp(void) const;
|
||||
void setFan(const uint8_t speed);
|
||||
uint8_t getFan(void) const;
|
||||
void setMode(const uint8_t mode);
|
||||
uint8_t getMode(void) const;
|
||||
void setSwingV(const uint8_t position);
|
||||
uint8_t getSwingV(void) const;
|
||||
void setQuiet(const bool on);
|
||||
bool getQuiet(void) const;
|
||||
uint8_t* getRaw(void);
|
||||
void setRaw(const uint8_t* data);
|
||||
static uint8_t convertMode(const stdAc::opmode_t mode);
|
||||
static uint8_t convertFan(const stdAc::fanspeed_t speed);
|
||||
static uint8_t convertSwingV(const stdAc::swingv_t position);
|
||||
static stdAc::opmode_t toCommonMode(const uint8_t mode);
|
||||
static stdAc::fanspeed_t toCommonFanSpeed(const uint8_t speed);
|
||||
static stdAc::swingv_t toCommonSwingV(const uint8_t pos);
|
||||
stdAc::state_t toCommon(void) const;
|
||||
String toString(void) const;
|
||||
#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
|
||||
Mitsubishi136Protocol _;
|
||||
void checksum(void);
|
||||
};
|
||||
|
||||
/// Class for handling detailed Mitsubishi 122-bit A/C messages.
|
||||
class IRMitsubishi112 {
|
||||
public:
|
||||
explicit IRMitsubishi112(const uint16_t pin, const bool inverted = false,
|
||||
const bool use_modulation = true);
|
||||
void stateReset(void);
|
||||
#if SEND_MITSUBISHI112
|
||||
void send(const uint16_t repeat = kMitsubishi112MinRepeat);
|
||||
/// 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_MITSUBISHI112
|
||||
void begin(void);
|
||||
void on(void);
|
||||
void off(void);
|
||||
void setPower(const bool on);
|
||||
bool getPower(void) const;
|
||||
void setTemp(const uint8_t degrees);
|
||||
uint8_t getTemp(void) const;
|
||||
void setFan(const uint8_t speed);
|
||||
uint8_t getFan(void) const;
|
||||
void setMode(const uint8_t mode);
|
||||
uint8_t getMode(void) const;
|
||||
void setSwingV(const uint8_t position);
|
||||
uint8_t getSwingV(void) const;
|
||||
void setSwingH(const uint8_t position);
|
||||
uint8_t getSwingH(void) const;
|
||||
void setQuiet(const bool on);
|
||||
bool getQuiet(void) const;
|
||||
uint8_t* getRaw(void);
|
||||
void setRaw(const uint8_t* data);
|
||||
static uint8_t convertMode(const stdAc::opmode_t mode);
|
||||
static uint8_t convertFan(const stdAc::fanspeed_t speed);
|
||||
static uint8_t convertSwingV(const stdAc::swingv_t position);
|
||||
static uint8_t convertSwingH(const stdAc::swingh_t position);
|
||||
static stdAc::opmode_t toCommonMode(const uint8_t mode);
|
||||
static stdAc::fanspeed_t toCommonFanSpeed(const uint8_t speed);
|
||||
static stdAc::swingv_t toCommonSwingV(const uint8_t pos);
|
||||
static stdAc::swingh_t toCommonSwingH(const uint8_t pos);
|
||||
stdAc::state_t toCommon(void) const;
|
||||
String toString(void) const;
|
||||
#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
|
||||
Mitsubishi112Protocol _;
|
||||
void checksum(void);
|
||||
};
|
||||
|
||||
#endif // IR_MITSUBISHI_H_
|
||||
1050
yoRadio/src/IRremoteESP8266/ir_MitsubishiHeavy.cpp
Normal file
1050
yoRadio/src/IRremoteESP8266/ir_MitsubishiHeavy.cpp
Normal file
File diff suppressed because it is too large
Load Diff
346
yoRadio/src/IRremoteESP8266/ir_MitsubishiHeavy.h
Normal file
346
yoRadio/src/IRremoteESP8266/ir_MitsubishiHeavy.h
Normal file
@@ -0,0 +1,346 @@
|
||||
// Copyright 2019 David Conran
|
||||
|
||||
/// @file
|
||||
/// @brief Support for Mitsubishi Heavy Industry protocols.
|
||||
/// Code to emulate Mitsubishi Heavy Industries A/C IR remote control units.
|
||||
/// @note This code was *heavily* influenced by ToniA's great work & code,
|
||||
/// but it has been written from scratch.
|
||||
/// Nothing was copied other than constants and message analysis.
|
||||
/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/660
|
||||
/// @see https://github.com/ToniA/Raw-IR-decoder-for-Arduino/blob/master/MitsubishiHeavy.cpp
|
||||
/// @see https://github.com/ToniA/arduino-heatpumpir/blob/master/MitsubishiHeavyHeatpumpIR.cpp
|
||||
|
||||
// Supports:
|
||||
// Brand: Mitsubishi Heavy Industries, Model: RLA502A700B remote (152 bit)
|
||||
// Brand: Mitsubishi Heavy Industries, Model: SRKxxZM-S A/C (152 bit)
|
||||
// Brand: Mitsubishi Heavy Industries, Model: SRKxxZMXA-S A/C (152 bit)
|
||||
// Brand: Mitsubishi Heavy Industries, Model: RKX502A001C remote (88 bit)
|
||||
// Brand: Mitsubishi Heavy Industries, Model: SRKxxZJ-S A/C (88 bit)
|
||||
|
||||
#ifndef IR_MITSUBISHIHEAVY_H_
|
||||
#define IR_MITSUBISHIHEAVY_H_
|
||||
|
||||
#ifndef UNIT_TEST
|
||||
#include <Arduino.h>
|
||||
#endif
|
||||
#include "IRremoteESP8266.h"
|
||||
#include "IRsend.h"
|
||||
#ifdef UNIT_TEST
|
||||
#include "IRsend_test.h"
|
||||
#endif
|
||||
|
||||
/// Native representation of a Mitsubishi Heavy 152-bit A/C message.
|
||||
union Mitsubishi152Protocol{
|
||||
uint8_t raw[kMitsubishiHeavy152StateLength]; ///< State in code form
|
||||
struct {
|
||||
// Byte 0~4
|
||||
uint8_t Sig[5];
|
||||
// Byte 5
|
||||
uint8_t Mode :3;
|
||||
uint8_t Power :1;
|
||||
uint8_t :1;
|
||||
uint8_t Clean :1;
|
||||
uint8_t Filter:1;
|
||||
uint8_t :1;
|
||||
// Byte 6
|
||||
uint8_t :8;
|
||||
// Byte 7
|
||||
uint8_t Temp :4;
|
||||
uint8_t :4;
|
||||
// Byte 8
|
||||
uint8_t :8;
|
||||
// Byte 9
|
||||
uint8_t Fan :4;
|
||||
uint8_t :4;
|
||||
// Byte 10
|
||||
uint8_t :8;
|
||||
// Byte 11
|
||||
uint8_t :1;
|
||||
uint8_t Three :1;
|
||||
uint8_t :2;
|
||||
uint8_t D :1; // binding with "Three"
|
||||
uint8_t SwingV :3;
|
||||
// Byte 12
|
||||
uint8_t :8;
|
||||
// Byte 13
|
||||
uint8_t SwingH :4;
|
||||
uint8_t :4;
|
||||
// Byte 14
|
||||
uint8_t :8;
|
||||
// Byte 15
|
||||
uint8_t :6;
|
||||
uint8_t Night :1;
|
||||
uint8_t Silent :1;
|
||||
};
|
||||
};
|
||||
|
||||
// Constants.
|
||||
const uint8_t kMitsubishiHeavySigLength = 5;
|
||||
|
||||
// ZMS (152 bit)
|
||||
const uint8_t kMitsubishiHeavyZmsSig[kMitsubishiHeavySigLength] = {
|
||||
0xAD, 0x51, 0x3C, 0xE5, 0x1A};
|
||||
|
||||
const uint8_t kMitsubishiHeavyAuto = 0; // 0b000
|
||||
const uint8_t kMitsubishiHeavyCool = 1; // 0b001
|
||||
const uint8_t kMitsubishiHeavyDry = 2; // 0b010
|
||||
const uint8_t kMitsubishiHeavyFan = 3; // 0b011
|
||||
const uint8_t kMitsubishiHeavyHeat = 4; // 0b100
|
||||
|
||||
const uint8_t kMitsubishiHeavyMinTemp = 17; // 17C
|
||||
const uint8_t kMitsubishiHeavyMaxTemp = 31; // 31C
|
||||
|
||||
const uint8_t kMitsubishiHeavy152FanAuto = 0x0; // 0b0000
|
||||
const uint8_t kMitsubishiHeavy152FanLow = 0x1; // 0b0001
|
||||
const uint8_t kMitsubishiHeavy152FanMed = 0x2; // 0b0010
|
||||
const uint8_t kMitsubishiHeavy152FanHigh = 0x3; // 0b0011
|
||||
const uint8_t kMitsubishiHeavy152FanMax = 0x4; // 0b0100
|
||||
const uint8_t kMitsubishiHeavy152FanEcono = 0x6; // 0b0110
|
||||
const uint8_t kMitsubishiHeavy152FanTurbo = 0x8; // 0b1000
|
||||
|
||||
const uint8_t kMitsubishiHeavy152SwingVAuto = 0; // 0b000
|
||||
const uint8_t kMitsubishiHeavy152SwingVHighest = 1; // 0b001
|
||||
const uint8_t kMitsubishiHeavy152SwingVHigh = 2; // 0b010
|
||||
const uint8_t kMitsubishiHeavy152SwingVMiddle = 3; // 0b011
|
||||
const uint8_t kMitsubishiHeavy152SwingVLow = 4; // 0b100
|
||||
const uint8_t kMitsubishiHeavy152SwingVLowest = 5; // 0b101
|
||||
const uint8_t kMitsubishiHeavy152SwingVOff = 6; // 0b110
|
||||
|
||||
const uint8_t kMitsubishiHeavy152SwingHAuto = 0; // 0b0000
|
||||
const uint8_t kMitsubishiHeavy152SwingHLeftMax = 1; // 0b0001
|
||||
const uint8_t kMitsubishiHeavy152SwingHLeft = 2; // 0b0010
|
||||
const uint8_t kMitsubishiHeavy152SwingHMiddle = 3; // 0b0011
|
||||
const uint8_t kMitsubishiHeavy152SwingHRight = 4; // 0b0100
|
||||
const uint8_t kMitsubishiHeavy152SwingHRightMax = 5; // 0b0101
|
||||
const uint8_t kMitsubishiHeavy152SwingHRightLeft = 6; // 0b0110
|
||||
const uint8_t kMitsubishiHeavy152SwingHLeftRight = 7; // 0b0111
|
||||
const uint8_t kMitsubishiHeavy152SwingHOff = 8; // 0b1000
|
||||
|
||||
/// Native representation of a Mitsubishi Heavy 88-bit A/C message.
|
||||
union Mitsubishi88Protocol{
|
||||
uint8_t raw[kMitsubishiHeavy88StateLength]; ///< State in code form
|
||||
struct {
|
||||
// Byte 0~4
|
||||
uint8_t Sig[5];
|
||||
// Byte 5
|
||||
uint8_t :1;
|
||||
uint8_t SwingV5 :1;
|
||||
uint8_t SwingH1 :2;
|
||||
uint8_t :1;
|
||||
uint8_t Clean :1;
|
||||
uint8_t SwingH2 :2;
|
||||
// Byte 6
|
||||
uint8_t :8;
|
||||
// Byte 7
|
||||
uint8_t :3;
|
||||
uint8_t SwingV7 :2;
|
||||
uint8_t Fan :3;
|
||||
// Byte 8
|
||||
uint8_t :8;
|
||||
// Byte 9
|
||||
uint8_t Mode :3;
|
||||
uint8_t Power :1;
|
||||
uint8_t Temp :4;
|
||||
};
|
||||
};
|
||||
|
||||
// ZJS (88 bit)
|
||||
const uint8_t kMitsubishiHeavyZjsSig[kMitsubishiHeavySigLength] = {
|
||||
0xAD, 0x51, 0x3C, 0xD9, 0x26};
|
||||
|
||||
const uint8_t kMitsubishiHeavy88SwingHSize = 2; // Bits (per offset)
|
||||
const uint8_t kMitsubishiHeavy88SwingHOff = 0b0000;
|
||||
const uint8_t kMitsubishiHeavy88SwingHAuto = 0b1000;
|
||||
const uint8_t kMitsubishiHeavy88SwingHLeftMax = 0b0001;
|
||||
const uint8_t kMitsubishiHeavy88SwingHLeft = 0b0101;
|
||||
const uint8_t kMitsubishiHeavy88SwingHMiddle = 0b1001;
|
||||
const uint8_t kMitsubishiHeavy88SwingHRight = 0b1101;
|
||||
const uint8_t kMitsubishiHeavy88SwingHRightMax = 0b0010;
|
||||
const uint8_t kMitsubishiHeavy88SwingHRightLeft = 0b1010;
|
||||
const uint8_t kMitsubishiHeavy88SwingHLeftRight = 0b0110;
|
||||
const uint8_t kMitsubishiHeavy88SwingH3D = 0b1110;
|
||||
|
||||
const uint8_t kMitsubishiHeavy88FanAuto = 0; // 0b000
|
||||
const uint8_t kMitsubishiHeavy88FanLow = 2; // 0b010
|
||||
const uint8_t kMitsubishiHeavy88FanMed = 3; // 0b011
|
||||
const uint8_t kMitsubishiHeavy88FanHigh = 4; // 0b100
|
||||
const uint8_t kMitsubishiHeavy88FanTurbo = 6; // 0b110
|
||||
const uint8_t kMitsubishiHeavy88FanEcono = 7; // 0b111
|
||||
const uint8_t kMitsubishiHeavy88SwingVByte5Size = 1;
|
||||
|
||||
// Mask 0b111
|
||||
const uint8_t kMitsubishiHeavy88SwingVOff = 0b000; // 0
|
||||
const uint8_t kMitsubishiHeavy88SwingVAuto = 0b100; // 4
|
||||
const uint8_t kMitsubishiHeavy88SwingVHighest = 0b110; // 6
|
||||
const uint8_t kMitsubishiHeavy88SwingVHigh = 0b001; // 1
|
||||
const uint8_t kMitsubishiHeavy88SwingVMiddle = 0b011; // 3
|
||||
const uint8_t kMitsubishiHeavy88SwingVLow = 0b101; // 5
|
||||
const uint8_t kMitsubishiHeavy88SwingVLowest = 0b111; // 7
|
||||
|
||||
|
||||
// Classes
|
||||
|
||||
/// Class for handling detailed Mitsubishi Heavy 152-bit A/C messages.
|
||||
class IRMitsubishiHeavy152Ac {
|
||||
public:
|
||||
explicit IRMitsubishiHeavy152Ac(const uint16_t pin,
|
||||
const bool inverted = false,
|
||||
const bool use_modulation = true);
|
||||
void stateReset(void);
|
||||
#if SEND_MITSUBISHIHEAVY
|
||||
void send(const uint16_t repeat = kMitsubishiHeavy152MinRepeat);
|
||||
/// 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_MITSUBISHIHEAVY
|
||||
void begin(void);
|
||||
void on(void);
|
||||
void off(void);
|
||||
|
||||
void setPower(const bool on);
|
||||
bool getPower(void) const;
|
||||
|
||||
void setTemp(const uint8_t temp);
|
||||
uint8_t getTemp(void) const;
|
||||
|
||||
void setFan(const uint8_t fan);
|
||||
uint8_t getFan(void) const;
|
||||
|
||||
void setMode(const uint8_t mode);
|
||||
uint8_t getMode(void) const;
|
||||
|
||||
void setSwingVertical(const uint8_t pos);
|
||||
uint8_t getSwingVertical(void) const;
|
||||
void setSwingHorizontal(const uint8_t pos);
|
||||
uint8_t getSwingHorizontal(void) const;
|
||||
|
||||
void setNight(const bool on);
|
||||
bool getNight(void) const;
|
||||
|
||||
void set3D(const bool on);
|
||||
bool get3D(void) const;
|
||||
|
||||
void setSilent(const bool on);
|
||||
bool getSilent(void) const;
|
||||
|
||||
void setFilter(const bool on);
|
||||
bool getFilter(void) const;
|
||||
|
||||
void setClean(const bool on);
|
||||
bool getClean(void) const;
|
||||
|
||||
void setTurbo(const bool on);
|
||||
bool getTurbo(void) const;
|
||||
|
||||
void setEcono(const bool on);
|
||||
bool getEcono(void) const;
|
||||
|
||||
uint8_t* getRaw(void);
|
||||
void setRaw(const uint8_t* data);
|
||||
|
||||
static bool checkZmsSig(const uint8_t *state);
|
||||
static bool validChecksum(
|
||||
const uint8_t *state,
|
||||
const uint16_t length = kMitsubishiHeavy152StateLength);
|
||||
static uint8_t convertMode(const stdAc::opmode_t mode);
|
||||
static uint8_t convertFan(const stdAc::fanspeed_t speed);
|
||||
static uint8_t convertSwingV(const stdAc::swingv_t position);
|
||||
static uint8_t convertSwingH(const stdAc::swingh_t position);
|
||||
static stdAc::opmode_t toCommonMode(const uint8_t mode);
|
||||
static stdAc::fanspeed_t toCommonFanSpeed(const uint8_t speed);
|
||||
static stdAc::swingv_t toCommonSwingV(const uint8_t pos);
|
||||
static stdAc::swingh_t toCommonSwingH(const uint8_t pos);
|
||||
stdAc::state_t toCommon(void) const;
|
||||
String toString(void) const;
|
||||
#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
|
||||
Mitsubishi152Protocol _;
|
||||
void checksum(void);
|
||||
};
|
||||
|
||||
/// Class for handling detailed Mitsubishi Heavy 88-bit A/C messages.
|
||||
class IRMitsubishiHeavy88Ac {
|
||||
public:
|
||||
explicit IRMitsubishiHeavy88Ac(const uint16_t pin,
|
||||
const bool inverted = false,
|
||||
const bool use_modulation = true);
|
||||
void stateReset(void);
|
||||
#if SEND_MITSUBISHIHEAVY
|
||||
void send(const uint16_t repeat = kMitsubishiHeavy88MinRepeat);
|
||||
/// 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_MITSUBISHIHEAVY
|
||||
void begin(void);
|
||||
void on(void);
|
||||
void off(void);
|
||||
|
||||
void setPower(const bool on);
|
||||
bool getPower(void) const;
|
||||
|
||||
void setTemp(const uint8_t temp);
|
||||
uint8_t getTemp(void) const;
|
||||
|
||||
void setFan(const uint8_t fan);
|
||||
uint8_t getFan(void) const;
|
||||
|
||||
void setMode(const uint8_t mode);
|
||||
uint8_t getMode(void) const;
|
||||
|
||||
void setSwingVertical(const uint8_t pos);
|
||||
uint8_t getSwingVertical(void) const;
|
||||
void setSwingHorizontal(const uint8_t pos);
|
||||
uint8_t getSwingHorizontal(void) const;
|
||||
|
||||
void setTurbo(const bool on);
|
||||
bool getTurbo(void) const;
|
||||
|
||||
void setEcono(const bool on);
|
||||
bool getEcono(void) const;
|
||||
|
||||
void set3D(const bool on);
|
||||
bool get3D(void) const;
|
||||
|
||||
void setClean(const bool on);
|
||||
bool getClean(void) const;
|
||||
|
||||
uint8_t* getRaw(void);
|
||||
void setRaw(const uint8_t* data);
|
||||
|
||||
static bool checkZjsSig(const uint8_t *state);
|
||||
static bool validChecksum(
|
||||
const uint8_t *state,
|
||||
const uint16_t length = kMitsubishiHeavy88StateLength);
|
||||
static uint8_t convertMode(const stdAc::opmode_t mode);
|
||||
static uint8_t convertFan(const stdAc::fanspeed_t speed);
|
||||
static uint8_t convertSwingV(const stdAc::swingv_t position);
|
||||
static uint8_t convertSwingH(const stdAc::swingh_t position);
|
||||
static stdAc::fanspeed_t toCommonFanSpeed(const uint8_t speed);
|
||||
static stdAc::swingv_t toCommonSwingV(const uint8_t pos);
|
||||
static stdAc::swingh_t toCommonSwingH(const uint8_t pos);
|
||||
stdAc::state_t toCommon(void) const;
|
||||
String toString(void) const;
|
||||
#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
|
||||
Mitsubishi88Protocol _;
|
||||
void checksum(void);
|
||||
};
|
||||
#endif // IR_MITSUBISHIHEAVY_H_
|
||||
115
yoRadio/src/IRremoteESP8266/ir_Multibrackets.cpp
Normal file
115
yoRadio/src/IRremoteESP8266/ir_Multibrackets.cpp
Normal file
@@ -0,0 +1,115 @@
|
||||
// Copyright 2020 David Conran
|
||||
|
||||
/// @file
|
||||
/// @brief Support for Multibrackets protocols.
|
||||
/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1103
|
||||
/// @see http://info.multibrackets.com/data/common/manuals/4500_code.pdf
|
||||
|
||||
// Supports:
|
||||
// Brand: Multibrackets, Model: Motorized Swing mount large - 4500
|
||||
|
||||
#include "IRrecv.h"
|
||||
#include "IRsend.h"
|
||||
|
||||
const uint16_t kMultibracketsTick = 5000; // uSeconds
|
||||
const uint16_t kMultibracketsHdrMark = 3 * kMultibracketsTick; // uSeconds
|
||||
const uint16_t kMultibracketsFooterSpace = 6 * kMultibracketsTick; // uSeconds
|
||||
const uint8_t kMultibracketsTolerance = 5; // Percent
|
||||
const uint16_t kMultibracketsFreq = 38000; // Hertz
|
||||
|
||||
#if SEND_MULTIBRACKETS
|
||||
/// Send a Multibrackets formatted message.
|
||||
/// Status: BETA / Appears to be working.
|
||||
/// @param[in] data The message to be sent.
|
||||
/// @param[in] nbits The number of bits of message to be sent.
|
||||
/// @param[in] repeat The number of times the command is to be repeated.
|
||||
void IRsend::sendMultibrackets(uint64_t data, uint16_t nbits, uint16_t repeat) {
|
||||
enableIROut(kMultibracketsFreq);
|
||||
for (uint16_t r = 0; r <= repeat; r++) {
|
||||
uint16_t bits = nbits;
|
||||
// Header
|
||||
mark(kMultibracketsHdrMark);
|
||||
// Data
|
||||
// Send 0's until we get down to a bit size we can actually manage.
|
||||
while (bits > sizeof(data) * 8) {
|
||||
space(kMultibracketsTick);
|
||||
bits--;
|
||||
}
|
||||
// Send the supplied data.
|
||||
for (uint64_t mask = 1ULL << (bits - 1); mask; mask >>= 1)
|
||||
if (data & mask) // Send a 1
|
||||
mark(kMultibracketsTick);
|
||||
else // Send a 0
|
||||
space(kMultibracketsTick);
|
||||
// Footer
|
||||
space(kMultibracketsFooterSpace);
|
||||
}
|
||||
}
|
||||
#endif // SEND_MULTIBRACKETS
|
||||
|
||||
#if DECODE_MULTIBRACKETS
|
||||
/// Decode the Multibrackets message.
|
||||
/// Status: BETA / Appears to be working.
|
||||
/// @param[in,out] results Ptr to the data to decode & where to store the result
|
||||
/// @param[in] offset The starting index to use when attempting to decode the
|
||||
/// raw data. Typically/Defaults to kStartOffset.
|
||||
/// @param[in] nbits The number of data bits to expect.
|
||||
/// @param[in] strict Flag indicating if we should perform strict matching.
|
||||
/// @return True if it can decode it, false if it can't.
|
||||
bool IRrecv::decodeMultibrackets(decode_results *results, uint16_t offset,
|
||||
const uint16_t nbits, const bool strict) {
|
||||
// Compliance
|
||||
if (strict && nbits != kMultibracketsBits)
|
||||
return false; // Doesn't match our protocol defn.
|
||||
|
||||
// Check there is enough unprocessed buffer left.
|
||||
if (results->rawlen < offset) return false;
|
||||
|
||||
// Header
|
||||
int32_t remaining = *(results->rawbuf + offset);
|
||||
if (!matchAtLeast(remaining, kMultibracketsHdrMark, kMultibracketsTolerance))
|
||||
return false;
|
||||
remaining -= (kMultibracketsHdrMark / kRawTick); // Remove the header.
|
||||
|
||||
// We are done with the header. Onto the data.
|
||||
bool bit = true;
|
||||
uint16_t bitsSoFar = 0;
|
||||
uint64_t data = 0;
|
||||
// Keep going till we run out of message or expected bits.
|
||||
while (offset <= results->rawlen && bitsSoFar < nbits) {
|
||||
// Have we finished processing this rawbuf value yet?
|
||||
if (remaining <= 0) { // No more possible "bits" left in this value.
|
||||
// Invert the bit for next time, and move along the rawbuf.
|
||||
bit = !bit;
|
||||
offset++;
|
||||
// Load the next data point if there is one.
|
||||
if (offset <= results->rawlen) remaining = *(results->rawbuf + offset);
|
||||
} else { // Look for more bits in this entry.
|
||||
if (matchAtLeast(remaining, kMultibracketsTick,
|
||||
kMultibracketsTolerance)) { // There is!
|
||||
data <<= 1;
|
||||
data += bit;
|
||||
bitsSoFar++;
|
||||
}
|
||||
remaining -= (kMultibracketsTick / kRawTick); // Remove the "bit".
|
||||
}
|
||||
}
|
||||
|
||||
// Compliance
|
||||
if (bitsSoFar != nbits) return false;
|
||||
|
||||
// Footer
|
||||
if (results->rawlen <= offset && !matchAtLeast(*(results->rawbuf + offset),
|
||||
kMultibracketsFooterSpace,
|
||||
kMultibracketsTolerance))
|
||||
return false;
|
||||
|
||||
// Success
|
||||
results->decode_type = decode_type_t::MULTIBRACKETS;
|
||||
results->value = data;
|
||||
results->bits = nbits;
|
||||
results->address = 0;
|
||||
results->command = 0;
|
||||
return true;
|
||||
}
|
||||
#endif // DECODE_MULTIBRACKETS
|
||||
140
yoRadio/src/IRremoteESP8266/ir_NEC.cpp
Normal file
140
yoRadio/src/IRremoteESP8266/ir_NEC.cpp
Normal file
@@ -0,0 +1,140 @@
|
||||
// Copyright 2009 Ken Shirriff
|
||||
// Copyright 2017 David Conran
|
||||
|
||||
/// @file
|
||||
/// @brief Support for NEC (Renesas) protocols.
|
||||
/// NEC originally added from https://github.com/shirriff/Arduino-IRremote/
|
||||
/// @see http://www.sbprojects.net/knowledge/ir/nec.php
|
||||
|
||||
#define __STDC_LIMIT_MACROS
|
||||
#include "ir_NEC.h"
|
||||
#include <stdint.h>
|
||||
#include <algorithm>
|
||||
#include "IRrecv.h"
|
||||
#include "IRsend.h"
|
||||
#include "IRutils.h"
|
||||
|
||||
// This protocol is used by a lot of other protocols, hence the long list.
|
||||
#if (SEND_NEC || SEND_SHERWOOD || SEND_AIWA_RC_T501 || SEND_SANYO || \
|
||||
SEND_MIDEA24)
|
||||
|
||||
/// Send a raw NEC(Renesas) formatted message.
|
||||
/// Status: STABLE / Known working.
|
||||
/// @param[in] data The message to be sent.
|
||||
/// @param[in] nbits The number of bits of message to be sent.
|
||||
/// @param[in] repeat The number of times the command is to be repeated.
|
||||
/// @note This protocol appears to have no header.
|
||||
/// @see http://www.sbprojects.net/knowledge/ir/nec.php
|
||||
void IRsend::sendNEC(uint64_t data, uint16_t nbits, uint16_t repeat) {
|
||||
sendGeneric(kNecHdrMark, kNecHdrSpace, kNecBitMark, kNecOneSpace, kNecBitMark,
|
||||
kNecZeroSpace, kNecBitMark, kNecMinGap, kNecMinCommandLength,
|
||||
data, nbits, 38, true, 0, // Repeats are handled later.
|
||||
33);
|
||||
// Optional command repeat sequence.
|
||||
if (repeat)
|
||||
sendGeneric(kNecHdrMark, kNecRptSpace, 0, 0, 0, 0, // No actual data sent.
|
||||
kNecBitMark, kNecMinGap, kNecMinCommandLength, 0,
|
||||
0, // No data to be sent.
|
||||
38, true, repeat - 1, // We've already sent a one message.
|
||||
33);
|
||||
}
|
||||
|
||||
/// Calculate the raw NEC data based on address and command.
|
||||
/// Status: STABLE / Expected to work.
|
||||
/// @param[in] address An address value.
|
||||
/// @param[in] command An 8-bit command value.
|
||||
/// @return A raw 32-bit NEC message suitable for use with `sendNEC()`.
|
||||
/// @see http://www.sbprojects.net/knowledge/ir/nec.php
|
||||
uint32_t IRsend::encodeNEC(uint16_t address, uint16_t command) {
|
||||
command &= 0xFF; // We only want the least significant byte of command.
|
||||
// sendNEC() sends MSB first, but protocol says this is LSB first.
|
||||
command = reverseBits(command, 8);
|
||||
command = (command << 8) + (command ^ 0xFF); // Calculate the new command.
|
||||
if (address > 0xFF) { // Is it Extended NEC?
|
||||
address = reverseBits(address, 16);
|
||||
return ((address << 16) + command); // Extended.
|
||||
} else {
|
||||
address = reverseBits(address, 8);
|
||||
return (address << 24) + ((address ^ 0xFF) << 16) + command; // Normal.
|
||||
}
|
||||
}
|
||||
#endif // (SEND_NEC || SEND_SHERWOOD || SEND_AIWA_RC_T501 || SEND_SANYO ||
|
||||
// SEND_MIDEA24)
|
||||
|
||||
// This protocol is used by a lot of other protocols, hence the long list.
|
||||
#if (DECODE_NEC || DECODE_SHERWOOD || DECODE_AIWA_RC_T501 || DECODE_SANYO)
|
||||
/// Decode the supplied NEC (Renesas) message.
|
||||
/// Status: STABLE / Known good.
|
||||
/// @param[in,out] results Ptr to the data to decode & where to store the result
|
||||
/// @param[in] offset The starting index to use when attempting to decode the
|
||||
/// raw data. Typically/Defaults to kStartOffset.
|
||||
/// @param[in] nbits The number of data bits to expect.
|
||||
/// @param[in] strict Flag indicating if we should perform strict matching.
|
||||
/// @return True if it can decode it, false if it can't.
|
||||
/// @note NEC protocol has three variants/forms.
|
||||
/// Normal: an 8 bit address & an 8 bit command in 32 bit data form.
|
||||
/// i.e. address + inverted(address) + command + inverted(command)
|
||||
/// Extended: a 16 bit address & an 8 bit command in 32 bit data form.
|
||||
/// i.e. address + command + inverted(command)
|
||||
/// Repeat: a 0-bit code. i.e. No data bits. Just the header + footer.
|
||||
/// @see http://www.sbprojects.net/knowledge/ir/nec.php
|
||||
bool IRrecv::decodeNEC(decode_results *results, uint16_t offset,
|
||||
const uint16_t nbits, const bool strict) {
|
||||
if (results->rawlen < kNecRptLength + offset - 1)
|
||||
return false; // Can't possibly be a valid NEC message.
|
||||
if (strict && nbits != kNECBits)
|
||||
return false; // Not strictly an NEC message.
|
||||
|
||||
uint64_t data = 0;
|
||||
|
||||
// Header - All NEC messages have this Header Mark.
|
||||
if (!matchMark(results->rawbuf[offset++], kNecHdrMark)) return false;
|
||||
// Check if it is a repeat code.
|
||||
if (matchSpace(results->rawbuf[offset], kNecRptSpace) &&
|
||||
matchMark(results->rawbuf[offset + 1], kNecBitMark) &&
|
||||
(offset + 2 <= results->rawlen ||
|
||||
matchAtLeast(results->rawbuf[offset + 2], kNecMinGap))) {
|
||||
results->value = kRepeat;
|
||||
results->decode_type = NEC;
|
||||
results->bits = 0;
|
||||
results->address = 0;
|
||||
results->command = 0;
|
||||
results->repeat = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Match Header (cont.) + Data + Footer
|
||||
if (!matchGeneric(results->rawbuf + offset, &data,
|
||||
results->rawlen - offset, nbits,
|
||||
0, kNecHdrSpace,
|
||||
kNecBitMark, kNecOneSpace,
|
||||
kNecBitMark, kNecZeroSpace,
|
||||
kNecBitMark, kNecMinGap, true)) return false;
|
||||
// Compliance
|
||||
// Calculate command and optionally enforce integrity checking.
|
||||
uint8_t command = (data & 0xFF00) >> 8;
|
||||
// Command is sent twice, once as plain and then inverted.
|
||||
if ((command ^ 0xFF) != (data & 0xFF)) {
|
||||
if (strict) return false; // Command integrity failed.
|
||||
command = 0; // The command value isn't valid, so default to zero.
|
||||
}
|
||||
|
||||
// Success
|
||||
results->bits = nbits;
|
||||
results->value = data;
|
||||
results->decode_type = NEC;
|
||||
// NEC command and address are technically in LSB first order so the
|
||||
// final versions have to be reversed.
|
||||
results->command = reverseBits(command, 8);
|
||||
// Normal NEC protocol has an 8 bit address sent, followed by it inverted.
|
||||
uint8_t address = (data & 0xFF000000) >> 24;
|
||||
uint8_t address_inverted = (data & 0x00FF0000) >> 16;
|
||||
if (address == (address_inverted ^ 0xFF))
|
||||
// Inverted, so it is normal NEC protocol.
|
||||
results->address = reverseBits(address, 8);
|
||||
else // Not inverted, so must be Extended NEC protocol, thus 16 bit address.
|
||||
results->address = reverseBits((data >> 16) & UINT16_MAX, 16);
|
||||
return true;
|
||||
}
|
||||
#endif // (DECODE_NEC || DECODE_SHERWOOD || DECODE_AIWA_RC_T501 ||
|
||||
// DECODE_SANYO)
|
||||
80
yoRadio/src/IRremoteESP8266/ir_NEC.h
Normal file
80
yoRadio/src/IRremoteESP8266/ir_NEC.h
Normal file
@@ -0,0 +1,80 @@
|
||||
// Copyright 2009 Ken Shirriff
|
||||
// Copyright 2017, 2018 David Conran
|
||||
|
||||
/// @file
|
||||
/// @brief Support for NEC (Renesas) protocols.
|
||||
/// NEC originally added from https://github.com/shirriff/Arduino-IRremote/
|
||||
/// @see http://www.sbprojects.net/knowledge/ir/nec.php
|
||||
|
||||
// Supports:
|
||||
// Brand: Yamaha, Model: RAV561 remote
|
||||
// Brand: Yamaha, Model: RXV585B A/V Receiver
|
||||
// Brand: Aloka, Model: SleepyLights LED Lamp
|
||||
// Brand: Toshiba, Model: 42TL838 LCD TV
|
||||
// Brand: Duux, Model: Blizzard Smart 10K / DXMA04 A/C
|
||||
// Brand: Duux, Model: YJ-A081 TR Remote
|
||||
// Brand: Silan Microelectronics, Model: SC6121-001 IC
|
||||
// Brand: BBK, Model: SP550S 5.1 sound system
|
||||
// Brand: Tanix, Model: TX3 mini Android TV Box
|
||||
|
||||
#ifndef IR_NEC_H_
|
||||
#define IR_NEC_H_
|
||||
|
||||
#include <stdint.h>
|
||||
#include "IRremoteESP8266.h"
|
||||
|
||||
// Constants
|
||||
const uint16_t kNecTick = 560;
|
||||
const uint16_t kNecHdrMarkTicks = 16;
|
||||
const uint16_t kNecHdrMark = kNecHdrMarkTicks * kNecTick;
|
||||
const uint16_t kNecHdrSpaceTicks = 8;
|
||||
const uint16_t kNecHdrSpace = kNecHdrSpaceTicks * kNecTick;
|
||||
const uint16_t kNecBitMarkTicks = 1;
|
||||
const uint16_t kNecBitMark = kNecBitMarkTicks * kNecTick;
|
||||
const uint16_t kNecOneSpaceTicks = 3;
|
||||
const uint16_t kNecOneSpace = kNecOneSpaceTicks * kNecTick;
|
||||
const uint16_t kNecZeroSpaceTicks = 1;
|
||||
const uint16_t kNecZeroSpace = kNecZeroSpaceTicks * kNecTick;
|
||||
const uint16_t kNecRptSpaceTicks = 4;
|
||||
const uint16_t kNecRptSpace = kNecRptSpaceTicks * kNecTick;
|
||||
const uint16_t kNecRptLength = 4;
|
||||
const uint16_t kNecMinCommandLengthTicks = 193;
|
||||
const uint32_t kNecMinCommandLength = kNecMinCommandLengthTicks * kNecTick;
|
||||
const uint32_t kNecMinGap =
|
||||
kNecMinCommandLength -
|
||||
(kNecHdrMark + kNecHdrSpace + kNECBits * (kNecBitMark + kNecOneSpace) +
|
||||
kNecBitMark);
|
||||
const uint16_t kNecMinGapTicks =
|
||||
kNecMinCommandLengthTicks -
|
||||
(kNecHdrMarkTicks + kNecHdrSpaceTicks +
|
||||
kNECBits * (kNecBitMarkTicks + kNecOneSpaceTicks) + kNecBitMarkTicks);
|
||||
|
||||
// IR codes and structure for kids ALOKA SleepyLights LED Lamp.
|
||||
// https://aloka-designs.com/
|
||||
// Ref: https://github.com/crankyoldgit/IRremoteESP8266/issues/1004
|
||||
//
|
||||
// May be useful for someone wanting to control the lamp.
|
||||
//
|
||||
// The lamp is toggled On and Off with the same power button.
|
||||
// The colour, when selected, is the brightest and there are 4 levels of
|
||||
// brightness that decrease on each send of the colour. A fifth send of the
|
||||
// colour resets to brightest again.
|
||||
//
|
||||
// Remote buttons defined left to right, top line to bottom line on the remote.
|
||||
const uint32_t kAlokaPower = 0xFF609F;
|
||||
const uint32_t kAlokaLedWhite = 0xFF906F;
|
||||
const uint32_t kAlokaLedGreen = 0xFF9867;
|
||||
const uint32_t kAlokaLedBlue = 0xFFD827;
|
||||
const uint32_t kAlokaLedPinkRed = 0xFF8877;
|
||||
const uint32_t kAlokaLedRed = 0xFFA857;
|
||||
const uint32_t kAlokaLedLightGreen = 0xFFE817;
|
||||
const uint32_t kAlokaLedMidBlue = 0xFF48B7;
|
||||
const uint32_t kAlokaLedPink = 0xFF6897;
|
||||
const uint32_t kAlokaLedOrange = 0xFFB24D;
|
||||
const uint32_t kAlokaLedYellow = 0xFF00FF;
|
||||
const uint32_t kAlokaNightFade = 0xFF50AF;
|
||||
const uint32_t kAlokaNightTimer = 0xFF7887;
|
||||
const uint32_t kAlokaLedRainbow = 0xFF708F;
|
||||
// Didn't have a better description for it...
|
||||
const uint32_t kAlokaLedTreeGrow = 0xFF58A7;
|
||||
#endif // IR_NEC_H_
|
||||
608
yoRadio/src/IRremoteESP8266/ir_Neoclima.cpp
Normal file
608
yoRadio/src/IRremoteESP8266/ir_Neoclima.cpp
Normal file
@@ -0,0 +1,608 @@
|
||||
// Copyright 2019-2020 David Conran
|
||||
|
||||
/// @file
|
||||
/// @brief Support for Neoclima protocols.
|
||||
/// Analysis by crankyoldgit, AndreyShpilevoy, & griffisc306
|
||||
/// Code by crankyoldgit
|
||||
/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/764
|
||||
/// @see https://drive.google.com/file/d/1kjYk4zS9NQcMQhFkak-L4mp4UuaAIesW/view
|
||||
/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1260
|
||||
|
||||
#include "ir_Neoclima.h"
|
||||
#include <algorithm>
|
||||
#include <cstring>
|
||||
#include "IRrecv.h"
|
||||
#include "IRsend.h"
|
||||
#include "IRtext.h"
|
||||
#include "IRutils.h"
|
||||
|
||||
// Constants
|
||||
const uint16_t kNeoclimaHdrMark = 6112;
|
||||
const uint16_t kNeoclimaHdrSpace = 7391;
|
||||
const uint16_t kNeoclimaBitMark = 537;
|
||||
const uint16_t kNeoclimaOneSpace = 1651;
|
||||
const uint16_t kNeoclimaZeroSpace = 571;
|
||||
const uint32_t kNeoclimaMinGap = kDefaultMessageGap;
|
||||
|
||||
using irutils::addBoolToString;
|
||||
using irutils::addFanToString;
|
||||
using irutils::addIntToString;
|
||||
using irutils::addLabeledString;
|
||||
using irutils::addModeToString;
|
||||
using irutils::addTempToString;
|
||||
|
||||
#if SEND_NEOCLIMA
|
||||
/// Send a Neoclima message.
|
||||
/// Status: STABLE / Known to be working.
|
||||
/// @param[in] data The message to be sent.
|
||||
/// @param[in] nbytes The number of bytes of message to be sent.
|
||||
/// @param[in] repeat The number of times the command is to be repeated.
|
||||
void IRsend::sendNeoclima(const unsigned char data[], const uint16_t nbytes,
|
||||
const uint16_t repeat) {
|
||||
// Set IR carrier frequency
|
||||
enableIROut(38);
|
||||
|
||||
for (uint16_t i = 0; i <= repeat; i++) {
|
||||
sendGeneric(kNeoclimaHdrMark, kNeoclimaHdrSpace,
|
||||
kNeoclimaBitMark, kNeoclimaOneSpace,
|
||||
kNeoclimaBitMark, kNeoclimaZeroSpace,
|
||||
kNeoclimaBitMark, kNeoclimaHdrSpace,
|
||||
data, nbytes, 38000, false, 0, // Repeats are already handled.
|
||||
50);
|
||||
// Extra footer.
|
||||
mark(kNeoclimaBitMark);
|
||||
space(kNeoclimaMinGap);
|
||||
}
|
||||
}
|
||||
#endif // SEND_NEOCLIMA
|
||||
|
||||
/// Class constructor
|
||||
/// @param[in] pin GPIO to be used when sending.
|
||||
/// @param[in] inverted Is the output signal to be inverted?
|
||||
/// @param[in] use_modulation Is frequency modulation to be used?
|
||||
IRNeoclimaAc::IRNeoclimaAc(const uint16_t pin, const bool inverted,
|
||||
const bool use_modulation)
|
||||
: _irsend(pin, inverted, use_modulation) {
|
||||
stateReset();
|
||||
}
|
||||
|
||||
/// Reset the state of the remote to a known good state/sequence.
|
||||
void IRNeoclimaAc::stateReset(void) {
|
||||
static const uint8_t kReset[kNeoclimaStateLength] = {
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6A, 0x00, 0x2A, 0xA5};
|
||||
setRaw(kReset);
|
||||
}
|
||||
|
||||
/// Set up hardware to be able to send a message.
|
||||
void IRNeoclimaAc::begin(void) { _irsend.begin(); }
|
||||
|
||||
/// Calculate the checksum for a given state.
|
||||
/// @param[in] state The array to calc the checksum of.
|
||||
/// @param[in] length The length/size of the array.
|
||||
/// @return The calculated checksum value.
|
||||
uint8_t IRNeoclimaAc::calcChecksum(const uint8_t state[],
|
||||
const uint16_t length) {
|
||||
if (length == 0) return state[0];
|
||||
return sumBytes(state, length - 1);
|
||||
}
|
||||
|
||||
/// Verify the checksum is valid for a given state.
|
||||
/// @param[in] state The array to verify the checksum of.
|
||||
/// @param[in] length The length/size of the array.
|
||||
/// @return true, if the state has a valid checksum. Otherwise, false.
|
||||
bool IRNeoclimaAc::validChecksum(const uint8_t state[], const uint16_t length) {
|
||||
if (length < 2)
|
||||
return true; // No checksum to compare with. Assume okay.
|
||||
return (state[length - 1] == calcChecksum(state, length));
|
||||
}
|
||||
|
||||
/// Calculate & update the checksum for the internal state.
|
||||
/// @param[in] length The length/size of the internal state.
|
||||
void IRNeoclimaAc::checksum(uint16_t length) {
|
||||
if (length < 2) return;
|
||||
_.Sum = calcChecksum(_.raw, length);
|
||||
}
|
||||
|
||||
#if SEND_NEOCLIMA
|
||||
/// Send the current internal state as an IR message.
|
||||
/// @param[in] repeat Nr. of times the message will be repeated.
|
||||
void IRNeoclimaAc::send(const uint16_t repeat) {
|
||||
_irsend.sendNeoclima(getRaw(), kNeoclimaStateLength, repeat);
|
||||
}
|
||||
#endif // SEND_NEOCLIMA
|
||||
|
||||
/// Get a PTR to the internal state/code for this protocol.
|
||||
/// @return PTR to a code for this protocol based on the current internal state.
|
||||
uint8_t *IRNeoclimaAc::getRaw(void) {
|
||||
checksum();
|
||||
return _.raw;
|
||||
}
|
||||
|
||||
/// Set the internal state from a valid code for this protocol.
|
||||
/// @param[in] new_code A valid code for this protocol.
|
||||
/// @param[in] length The length/size of the new_code array.
|
||||
void IRNeoclimaAc::setRaw(const uint8_t new_code[], const uint16_t length) {
|
||||
std::memcpy(_.raw, new_code, std::min(length, kNeoclimaStateLength));
|
||||
}
|
||||
|
||||
/// Set the Button/Command pressed setting of the A/C.
|
||||
/// @param[in] button The value of the button/command that was pressed.
|
||||
void IRNeoclimaAc::setButton(const uint8_t button) {
|
||||
switch (button) {
|
||||
case kNeoclimaButtonPower:
|
||||
case kNeoclimaButtonMode:
|
||||
case kNeoclimaButtonTempUp:
|
||||
case kNeoclimaButtonTempDown:
|
||||
case kNeoclimaButtonSwing:
|
||||
case kNeoclimaButtonFanSpeed:
|
||||
case kNeoclimaButtonAirFlow:
|
||||
case kNeoclimaButtonHold:
|
||||
case kNeoclimaButtonSleep:
|
||||
case kNeoclimaButtonLight:
|
||||
case kNeoclimaButtonEye:
|
||||
case kNeoclimaButtonFollow:
|
||||
case kNeoclimaButtonIon:
|
||||
case kNeoclimaButtonFresh:
|
||||
case kNeoclimaButton8CHeat:
|
||||
case kNeoclimaButtonTurbo:
|
||||
case kNeoclimaButtonEcono:
|
||||
case kNeoclimaButtonTempUnit:
|
||||
_.Button = button;
|
||||
break;
|
||||
default:
|
||||
_.Button = kNeoclimaButtonPower;
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the Button/Command setting of the A/C.
|
||||
/// @return The value of the button/command that was pressed.
|
||||
uint8_t IRNeoclimaAc::getButton(void) const {
|
||||
return _.Button;
|
||||
}
|
||||
|
||||
/// Set the requested power state of the A/C to on.
|
||||
void IRNeoclimaAc::on(void) { setPower(true); }
|
||||
|
||||
/// Set the requested power state of the A/C to off.
|
||||
void IRNeoclimaAc::off(void) { setPower(false); }
|
||||
|
||||
/// Change the power setting.
|
||||
/// @param[in] on true, the setting is on. false, the setting is off.
|
||||
void IRNeoclimaAc::setPower(const bool on) {
|
||||
_.Button = kNeoclimaButtonPower;
|
||||
_.Power = on;
|
||||
}
|
||||
|
||||
/// Get the value of the current power setting.
|
||||
/// @return true, the setting is on. false, the setting is off.
|
||||
bool IRNeoclimaAc::getPower(void) const {
|
||||
return _.Power;
|
||||
}
|
||||
|
||||
/// Set the operating mode of the A/C.
|
||||
/// @param[in] mode The desired operating mode.
|
||||
void IRNeoclimaAc::setMode(const uint8_t mode) {
|
||||
switch (mode) {
|
||||
case kNeoclimaDry:
|
||||
// In this mode fan speed always LOW
|
||||
setFan(kNeoclimaFanLow);
|
||||
// FALL THRU
|
||||
case kNeoclimaAuto:
|
||||
case kNeoclimaCool:
|
||||
case kNeoclimaFan:
|
||||
case kNeoclimaHeat:
|
||||
_.Mode = mode;
|
||||
_.Button = kNeoclimaButtonMode;
|
||||
break;
|
||||
default:
|
||||
// If we get an unexpected mode, default to AUTO.
|
||||
_.Mode = kNeoclimaAuto;
|
||||
_.Button = kNeoclimaButtonMode;
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the operating mode setting of the A/C.
|
||||
/// @return The current operating mode setting.
|
||||
uint8_t IRNeoclimaAc::getMode(void) const {
|
||||
return _.Mode;
|
||||
}
|
||||
|
||||
/// Convert a stdAc::opmode_t enum into its native mode.
|
||||
/// @param[in] mode The enum to be converted.
|
||||
/// @return The native equivalent of the enum.
|
||||
uint8_t IRNeoclimaAc::convertMode(const stdAc::opmode_t mode) {
|
||||
switch (mode) {
|
||||
case stdAc::opmode_t::kCool: return kNeoclimaCool;
|
||||
case stdAc::opmode_t::kHeat: return kNeoclimaHeat;
|
||||
case stdAc::opmode_t::kDry: return kNeoclimaDry;
|
||||
case stdAc::opmode_t::kFan: return kNeoclimaFan;
|
||||
default: return kNeoclimaAuto;
|
||||
}
|
||||
}
|
||||
|
||||
/// Convert a native mode into its stdAc equivalent.
|
||||
/// @param[in] mode The native setting to be converted.
|
||||
/// @return The stdAc equivalent of the native setting.
|
||||
stdAc::opmode_t IRNeoclimaAc::toCommonMode(const uint8_t mode) {
|
||||
switch (mode) {
|
||||
case kNeoclimaCool: return stdAc::opmode_t::kCool;
|
||||
case kNeoclimaHeat: return stdAc::opmode_t::kHeat;
|
||||
case kNeoclimaDry: return stdAc::opmode_t::kDry;
|
||||
case kNeoclimaFan: return stdAc::opmode_t::kFan;
|
||||
default: return stdAc::opmode_t::kAuto;
|
||||
}
|
||||
}
|
||||
|
||||
/// Set the temperature.
|
||||
/// @param[in] temp The temperature in degrees celsius.
|
||||
/// @param[in] celsius Use Fahrenheit (false) or Celsius (true).
|
||||
void IRNeoclimaAc::setTemp(const uint8_t temp, const bool celsius) {
|
||||
uint8_t oldtemp = getTemp();
|
||||
_.UseFah = !celsius;
|
||||
const uint8_t min_temp = celsius ? kNeoclimaMinTempC : kNeoclimaMinTempF;
|
||||
const uint8_t max_temp = celsius ? kNeoclimaMaxTempC : kNeoclimaMaxTempF;
|
||||
const uint8_t newtemp = std::min(max_temp, std::max(min_temp, temp));
|
||||
if (oldtemp > newtemp)
|
||||
_.Button = kNeoclimaButtonTempDown;
|
||||
else if (newtemp > oldtemp)
|
||||
_.Button = kNeoclimaButtonTempUp;
|
||||
_.Temp = newtemp - min_temp;
|
||||
}
|
||||
|
||||
/// Get the current temperature setting.
|
||||
/// @return The current setting for temp. in degrees.
|
||||
/// @note The units of the temperature (F/C) is determined by `getTempUnits()`.
|
||||
uint8_t IRNeoclimaAc::getTemp(void) const {
|
||||
const uint8_t min_temp = getTempUnits() ? kNeoclimaMinTempC
|
||||
: kNeoclimaMinTempF;
|
||||
return _.Temp + min_temp;
|
||||
}
|
||||
|
||||
/// Set the speed of the fan.
|
||||
/// @param[in] speed The desired setting. 0-3, 0 is auto, 1-3 is the speed
|
||||
void IRNeoclimaAc::setFan(const uint8_t speed) {
|
||||
_.Button = kNeoclimaButtonFanSpeed;
|
||||
if (_.Mode == kNeoclimaDry) { // Dry mode only allows low speed.
|
||||
_.Fan = kNeoclimaFanLow;
|
||||
return;
|
||||
}
|
||||
switch (speed) {
|
||||
case kNeoclimaFanAuto:
|
||||
case kNeoclimaFanHigh:
|
||||
case kNeoclimaFanMed:
|
||||
case kNeoclimaFanLow:
|
||||
_.Fan = speed;
|
||||
break;
|
||||
default:
|
||||
// If we get an unexpected speed, default to Auto.
|
||||
_.Fan = kNeoclimaFanAuto;
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the current fan speed setting.
|
||||
/// @return The current fan speed/mode.
|
||||
uint8_t IRNeoclimaAc::getFan(void) const {
|
||||
return _.Fan;
|
||||
}
|
||||
|
||||
/// Convert a stdAc::fanspeed_t enum into it's native speed.
|
||||
/// @param[in] speed The enum to be converted.
|
||||
/// @return The native equivalent of the enum.
|
||||
uint8_t IRNeoclimaAc::convertFan(const stdAc::fanspeed_t speed) {
|
||||
switch (speed) {
|
||||
case stdAc::fanspeed_t::kMin:
|
||||
case stdAc::fanspeed_t::kLow: return kNeoclimaFanLow;
|
||||
case stdAc::fanspeed_t::kMedium: return kNeoclimaFanMed;
|
||||
case stdAc::fanspeed_t::kHigh:
|
||||
case stdAc::fanspeed_t::kMax: return kNeoclimaFanHigh;
|
||||
default: return kNeoclimaFanAuto;
|
||||
}
|
||||
}
|
||||
|
||||
/// Convert a native fan speed into its stdAc equivalent.
|
||||
/// @param[in] speed The native setting to be converted.
|
||||
/// @return The stdAc equivalent of the native setting.
|
||||
stdAc::fanspeed_t IRNeoclimaAc::toCommonFanSpeed(const uint8_t speed) {
|
||||
switch (speed) {
|
||||
case kNeoclimaFanHigh: return stdAc::fanspeed_t::kMax;
|
||||
case kNeoclimaFanMed: return stdAc::fanspeed_t::kMedium;
|
||||
case kNeoclimaFanLow: return stdAc::fanspeed_t::kMin;
|
||||
default: return stdAc::fanspeed_t::kAuto;
|
||||
}
|
||||
}
|
||||
|
||||
/// Set the Sleep setting of the A/C.
|
||||
/// @param[in] on true, the setting is on. false, the setting is off.
|
||||
void IRNeoclimaAc::setSleep(const bool on) {
|
||||
_.Button = kNeoclimaButtonSleep;
|
||||
_.Sleep = on;
|
||||
}
|
||||
|
||||
/// Get the Sleep setting of the A/C.
|
||||
/// @return true, the setting is on. false, the setting is off.
|
||||
bool IRNeoclimaAc::getSleep(void) const {
|
||||
return _.Sleep;
|
||||
}
|
||||
|
||||
/// Set the vertical swing setting of the A/C.
|
||||
/// @param[in] on true, the setting is on. false, the setting is off.
|
||||
void IRNeoclimaAc::setSwingV(const bool on) {
|
||||
_.Button = kNeoclimaButtonSwing;
|
||||
_.SwingV = (on ? kNeoclimaSwingVOn : kNeoclimaSwingVOff);
|
||||
}
|
||||
|
||||
/// Get the vertical swing setting of the A/C.
|
||||
/// @return true, the setting is on. false, the setting is off.
|
||||
bool IRNeoclimaAc::getSwingV(void) const {
|
||||
return _.SwingV == kNeoclimaSwingVOn;
|
||||
}
|
||||
|
||||
/// Set the horizontal swing setting of the A/C.
|
||||
/// @param[in] on true, the setting is on. false, the setting is off.
|
||||
void IRNeoclimaAc::setSwingH(const bool on) {
|
||||
_.Button = kNeoclimaButtonAirFlow;
|
||||
_.SwingH = !on; // Cleared when `on`
|
||||
}
|
||||
|
||||
/// Get the horizontal swing (Air Flow) setting of the A/C.
|
||||
/// @return true, the setting is on. false, the setting is off.
|
||||
bool IRNeoclimaAc::getSwingH(void) const {
|
||||
return !_.SwingH;
|
||||
}
|
||||
|
||||
/// Set the Turbo setting of the A/C.
|
||||
/// @param[in] on true, the setting is on. false, the setting is off.
|
||||
void IRNeoclimaAc::setTurbo(const bool on) {
|
||||
_.Button = kNeoclimaButtonTurbo;
|
||||
_.Turbo = on;
|
||||
}
|
||||
|
||||
/// Get the Turbo setting of the A/C.
|
||||
/// @return true, the setting is on. false, the setting is off.
|
||||
bool IRNeoclimaAc::getTurbo(void) const {
|
||||
return _.Turbo;
|
||||
}
|
||||
|
||||
/// Set the Economy (Energy Saver) setting of the A/C.
|
||||
/// @param[in] on true, the setting is on. false, the setting is off.
|
||||
void IRNeoclimaAc::setEcono(const bool on) {
|
||||
_.Button = kNeoclimaButtonEcono;
|
||||
_.Econo = on;
|
||||
}
|
||||
|
||||
/// Get the Economy (Energy Saver) setting of the A/C.
|
||||
/// @return true, the setting is on. false, the setting is off.
|
||||
bool IRNeoclimaAc::getEcono(void) const {
|
||||
return _.Econo;
|
||||
}
|
||||
|
||||
/// Set the Fresh (air) setting of the A/C.
|
||||
/// @param[in] on true, the setting is on. false, the setting is off.
|
||||
void IRNeoclimaAc::setFresh(const bool on) {
|
||||
_.Button = kNeoclimaButtonFresh;
|
||||
_.Fresh = on;
|
||||
}
|
||||
|
||||
/// Get the Fresh (air) setting of the A/C.
|
||||
/// @return true, the setting is on. false, the setting is off.
|
||||
bool IRNeoclimaAc::getFresh(void) const {
|
||||
return _.Fresh;
|
||||
}
|
||||
|
||||
/// Set the Hold setting of the A/C.
|
||||
/// @param[in] on true, the setting is on. false, the setting is off.
|
||||
void IRNeoclimaAc::setHold(const bool on) {
|
||||
_.Button = kNeoclimaButtonHold;
|
||||
_.Hold = on;
|
||||
}
|
||||
|
||||
/// Get the Hold setting of the A/C.
|
||||
/// @return true, the setting is on. false, the setting is off.
|
||||
bool IRNeoclimaAc::getHold(void) const {
|
||||
return _.Hold;
|
||||
}
|
||||
|
||||
/// Set the Ion (filter) setting of the A/C.
|
||||
/// @param[in] on true, the setting is on. false, the setting is off.
|
||||
void IRNeoclimaAc::setIon(const bool on) {
|
||||
_.Button = kNeoclimaButtonIon;
|
||||
_.Ion = on;
|
||||
}
|
||||
|
||||
/// Get the Ion (filter) setting of the A/C.
|
||||
/// @return true, the setting is on. false, the setting is off.
|
||||
bool IRNeoclimaAc::getIon(void) const {
|
||||
return _.Ion;
|
||||
}
|
||||
|
||||
/// Set the Light(LED display) setting of the A/C.
|
||||
/// @param[in] on true, the setting is on. false, the setting is off.
|
||||
void IRNeoclimaAc::setLight(const bool on) {
|
||||
_.Button = kNeoclimaButtonLight;
|
||||
_.Light = on;
|
||||
}
|
||||
|
||||
/// Get the Light (LED display) setting of the A/C.
|
||||
/// @return true, the setting is on. false, the setting is off.
|
||||
bool IRNeoclimaAc::getLight(void) const {
|
||||
return _.Light;
|
||||
}
|
||||
|
||||
/// Set the 8°C Heat setting of the A/C.
|
||||
/// @param[in] on true, the setting is on. false, the setting is off.
|
||||
/// @note This feature maintains the room temperature steadily at 8°C and
|
||||
/// prevents the room from freezing by activating the heating operation
|
||||
/// automatically when nobody is at home over a longer period during severe
|
||||
/// winter.
|
||||
void IRNeoclimaAc::set8CHeat(const bool on) {
|
||||
_.Button = kNeoclimaButton8CHeat;
|
||||
_.CHeat = on;
|
||||
}
|
||||
|
||||
/// Get the 8°C Heat setting of the A/C.
|
||||
/// @return true, the setting is on. false, the setting is off.
|
||||
bool IRNeoclimaAc::get8CHeat(void) const {
|
||||
return _.CHeat;
|
||||
}
|
||||
|
||||
/// Set the Eye (Sensor) setting of the A/C.
|
||||
/// @param[in] on true, the setting is on. false, the setting is off.
|
||||
void IRNeoclimaAc::setEye(const bool on) {
|
||||
_.Button = kNeoclimaButtonEye;
|
||||
_.Eye = on;
|
||||
}
|
||||
|
||||
/// Get the Eye (Sensor) setting of the A/C.
|
||||
/// @return true, the setting is on. false, the setting is off.
|
||||
bool IRNeoclimaAc::getEye(void) const {
|
||||
return _.Eye;
|
||||
}
|
||||
|
||||
/// Is the A/C unit using Fahrenheit or Celsius for temperature units.
|
||||
/// @return false, Fahrenheit. true, Celsius.
|
||||
bool IRNeoclimaAc::getTempUnits(void) const {
|
||||
return !_.UseFah;
|
||||
}
|
||||
|
||||
/* DISABLED
|
||||
TODO(someone): Work out why "on" is either 0x5D or 0x5F
|
||||
void IRNeoclimaAc::setFollow(const bool on) {
|
||||
setButton(kNeoclimaButtonFollow);
|
||||
if (on)
|
||||
remote_state[8] = kNeoclimaFollowMe;
|
||||
else
|
||||
remote_state[8] = 0;
|
||||
}
|
||||
*/
|
||||
|
||||
/// Get the Follow Me setting of the A/C.
|
||||
/// @return true, the setting is on. false, the setting is off.
|
||||
bool IRNeoclimaAc::getFollow(void) const {
|
||||
return (_.Follow & kNeoclimaFollowMe) == kNeoclimaFollowMe;
|
||||
}
|
||||
|
||||
/// Convert the current internal state into its stdAc::state_t equivalent.
|
||||
/// @return The stdAc equivalent of the native settings.
|
||||
stdAc::state_t IRNeoclimaAc::toCommon(void) const {
|
||||
stdAc::state_t result{};
|
||||
result.protocol = decode_type_t::NEOCLIMA;
|
||||
result.model = -1; // No models used.
|
||||
result.power = _.Power;
|
||||
result.mode = toCommonMode(_.Mode);
|
||||
result.celsius = getTempUnits();
|
||||
result.degrees = getTemp();
|
||||
result.fanspeed = toCommonFanSpeed(_.Fan);
|
||||
result.swingv = getSwingV() ? stdAc::swingv_t::kAuto
|
||||
: stdAc::swingv_t::kOff;
|
||||
result.swingh = getSwingH() ? stdAc::swingh_t::kAuto
|
||||
: stdAc::swingh_t::kOff;
|
||||
result.turbo = _.Turbo;
|
||||
result.econo = _.Econo;
|
||||
result.light = _.Light;
|
||||
result.filter = _.Ion;
|
||||
result.sleep = _.Sleep ? 0 : -1;
|
||||
// Not supported.
|
||||
result.quiet = false;
|
||||
result.clean = false;
|
||||
result.beep = false;
|
||||
result.clock = -1;
|
||||
return result;
|
||||
}
|
||||
|
||||
/// Convert the current internal state into a human readable string.
|
||||
/// @return A human readable string.
|
||||
String IRNeoclimaAc::toString(void) const {
|
||||
String result = "";
|
||||
result.reserve(110); // Reserve some heap for the string to reduce fragging.
|
||||
result += addBoolToString(_.Power, kPowerStr, false);
|
||||
result += addModeToString(_.Mode, kNeoclimaAuto, kNeoclimaCool,
|
||||
kNeoclimaHeat, kNeoclimaDry, kNeoclimaFan);
|
||||
result += addTempToString(getTemp(), getTempUnits());
|
||||
result += addFanToString(_.Fan, kNeoclimaFanHigh, kNeoclimaFanLow,
|
||||
kNeoclimaFanAuto, kNeoclimaFanAuto, kNeoclimaFanMed);
|
||||
result += addBoolToString(getSwingV(), kSwingVStr);
|
||||
result += addBoolToString(getSwingH(), kSwingHStr);
|
||||
result += addBoolToString(_.Sleep, kSleepStr);
|
||||
result += addBoolToString(_.Turbo, kTurboStr);
|
||||
result += addBoolToString(_.Econo, kEconoStr);
|
||||
result += addBoolToString(_.Hold, kHoldStr);
|
||||
result += addBoolToString(_.Ion, kIonStr);
|
||||
result += addBoolToString(_.Eye, kEyeStr);
|
||||
result += addBoolToString(_.Light, kLightStr);
|
||||
result += addBoolToString(getFollow(), kFollowStr);
|
||||
result += addBoolToString(_.CHeat, k8CHeatStr);
|
||||
result += addBoolToString(_.Fresh, kFreshStr);
|
||||
result += addIntToString(_.Button, kButtonStr);
|
||||
result += kSpaceLBraceStr;
|
||||
switch (_.Button) {
|
||||
case kNeoclimaButtonPower: result += kPowerStr; break;
|
||||
case kNeoclimaButtonMode: result += kModeStr; break;
|
||||
case kNeoclimaButtonTempUp: result += kTempUpStr; break;
|
||||
case kNeoclimaButtonTempDown: result += kTempDownStr; break;
|
||||
case kNeoclimaButtonSwing: result += kSwingStr; break;
|
||||
case kNeoclimaButtonFanSpeed: result += kFanStr; break;
|
||||
case kNeoclimaButtonAirFlow: result += kAirFlowStr; break;
|
||||
case kNeoclimaButtonHold: result += kHoldStr; break;
|
||||
case kNeoclimaButtonSleep: result += kSleepStr; break;
|
||||
case kNeoclimaButtonLight: result += kLightStr; break;
|
||||
case kNeoclimaButtonEye: result += kEyeStr; break;
|
||||
case kNeoclimaButtonFollow: result += kFollowStr; break;
|
||||
case kNeoclimaButtonIon: result += kIonStr; break;
|
||||
case kNeoclimaButtonFresh: result += kFreshStr; break;
|
||||
case kNeoclimaButton8CHeat: result += k8CHeatStr; break;
|
||||
case kNeoclimaButtonTurbo: result += kTurboStr; break;
|
||||
case kNeoclimaButtonEcono: result += kEconoStr; break;
|
||||
case kNeoclimaButtonTempUnit: result += kCelsiusFahrenheitStr; break;
|
||||
default: result += kUnknownStr;
|
||||
}
|
||||
result += ')';
|
||||
return result;
|
||||
}
|
||||
|
||||
#if DECODE_NEOCLIMA
|
||||
/// Decode the supplied Neoclima message.
|
||||
/// Status: STABLE / Known working
|
||||
/// @param[in,out] results Ptr to the data to decode & where to store the result
|
||||
/// @param[in] offset The starting index to use when attempting to decode the
|
||||
/// raw data. Typically/Defaults to kStartOffset.
|
||||
/// @param[in] nbits The number of data bits to expect.
|
||||
/// @param[in] strict Flag indicating if we should perform strict matching.
|
||||
/// @return True if it can decode it, false if it can't.
|
||||
bool IRrecv::decodeNeoclima(decode_results *results, uint16_t offset,
|
||||
const uint16_t nbits, const bool strict) {
|
||||
// Compliance
|
||||
if (strict && nbits != kNeoclimaBits)
|
||||
return false; // Incorrect nr. of bits per spec.
|
||||
|
||||
// Match Main Header + Data + Footer
|
||||
uint16_t used;
|
||||
used = matchGeneric(results->rawbuf + offset, results->state,
|
||||
results->rawlen - offset, nbits,
|
||||
kNeoclimaHdrMark, kNeoclimaHdrSpace,
|
||||
kNeoclimaBitMark, kNeoclimaOneSpace,
|
||||
kNeoclimaBitMark, kNeoclimaZeroSpace,
|
||||
kNeoclimaBitMark, kNeoclimaHdrSpace, false,
|
||||
_tolerance, 0, false);
|
||||
if (!used) return false;
|
||||
offset += used;
|
||||
// Extra footer.
|
||||
uint64_t unused;
|
||||
if (!matchGeneric(results->rawbuf + offset, &unused,
|
||||
results->rawlen - offset, 0, 0, 0, 0, 0, 0, 0,
|
||||
kNeoclimaBitMark, kNeoclimaHdrSpace, true)) return false;
|
||||
|
||||
// Compliance
|
||||
if (strict) {
|
||||
// Check we got a valid checksum.
|
||||
if (!IRNeoclimaAc::validChecksum(results->state, nbits / 8)) return false;
|
||||
}
|
||||
|
||||
// Success
|
||||
results->decode_type = decode_type_t::NEOCLIMA;
|
||||
results->bits = nbits;
|
||||
// No need to record the state as we stored it as we decoded it.
|
||||
// As we use result->state, we don't record value, address, or command as it
|
||||
// is a union data type.
|
||||
return true;
|
||||
}
|
||||
#endif // DECODE_NEOCLIMA
|
||||
198
yoRadio/src/IRremoteESP8266/ir_Neoclima.h
Normal file
198
yoRadio/src/IRremoteESP8266/ir_Neoclima.h
Normal file
@@ -0,0 +1,198 @@
|
||||
// Copyright 2019 David Conran
|
||||
|
||||
/// @file
|
||||
/// @brief Support for Neoclima protocols.
|
||||
/// Analysis by crankyoldgit & AndreyShpilevoy
|
||||
/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/764
|
||||
/// @see https://drive.google.com/file/d/1kjYk4zS9NQcMQhFkak-L4mp4UuaAIesW/view
|
||||
|
||||
// Supports:
|
||||
// Brand: Neoclima, Model: NS-09AHTI A/C
|
||||
// Brand: Neoclima, Model: ZH/TY-01 remote
|
||||
// Brand: Soleus Air, Model: TTWM1-10-01 A/C
|
||||
// Brand: Soleus Air, Model: ZCF/TL-05 remote
|
||||
|
||||
#ifndef IR_NEOCLIMA_H_
|
||||
#define IR_NEOCLIMA_H_
|
||||
|
||||
#define __STDC_LIMIT_MACROS
|
||||
#include <stdint.h>
|
||||
#ifndef UNIT_TEST
|
||||
#include <Arduino.h>
|
||||
#endif
|
||||
#include "IRremoteESP8266.h"
|
||||
#include "IRsend.h"
|
||||
#ifdef UNIT_TEST
|
||||
#include "IRsend_test.h"
|
||||
#endif
|
||||
|
||||
/// Native representation of a Neoclima A/C message.
|
||||
union NeoclimaProtocol {
|
||||
uint8_t raw[kNeoclimaStateLength]; ///< State of the remote in code.
|
||||
struct {
|
||||
// Byte 0
|
||||
uint8_t :8;
|
||||
// Byte 1
|
||||
uint8_t :1;
|
||||
uint8_t CHeat :1;
|
||||
uint8_t Ion :1;
|
||||
uint8_t :5;
|
||||
// Byte 2
|
||||
uint8_t :8;
|
||||
// Byte 3
|
||||
uint8_t Light :1;
|
||||
uint8_t :1;
|
||||
uint8_t Hold :1;
|
||||
uint8_t Turbo :1;
|
||||
uint8_t Econo :1;
|
||||
uint8_t :1;
|
||||
uint8_t Eye :1;
|
||||
uint8_t :1;
|
||||
// Byte 4
|
||||
uint8_t :8;
|
||||
// Byte 5
|
||||
uint8_t Button :5;
|
||||
uint8_t :2;
|
||||
uint8_t Fresh :1;
|
||||
// Byte 6
|
||||
uint8_t :8;
|
||||
// Byte 7
|
||||
uint8_t Sleep :1;
|
||||
uint8_t Power :1;
|
||||
uint8_t SwingV :2;
|
||||
uint8_t SwingH :1;
|
||||
uint8_t Fan :2;
|
||||
uint8_t UseFah :1;
|
||||
// Byte 8
|
||||
uint8_t Follow :8;
|
||||
// Byte 9
|
||||
uint8_t Temp :5;
|
||||
uint8_t Mode :3;
|
||||
// Byte 10
|
||||
uint8_t :8;
|
||||
// Byte 11
|
||||
uint8_t Sum :8;
|
||||
};
|
||||
};
|
||||
|
||||
// Constants
|
||||
|
||||
const uint8_t kNeoclimaButtonPower = 0x00;
|
||||
const uint8_t kNeoclimaButtonMode = 0x01;
|
||||
const uint8_t kNeoclimaButtonTempUp = 0x02;
|
||||
const uint8_t kNeoclimaButtonTempDown = 0x03;
|
||||
const uint8_t kNeoclimaButtonSwing = 0x04;
|
||||
const uint8_t kNeoclimaButtonFanSpeed = 0x05;
|
||||
const uint8_t kNeoclimaButtonAirFlow = 0x07;
|
||||
const uint8_t kNeoclimaButtonHold = 0x08;
|
||||
const uint8_t kNeoclimaButtonSleep = 0x09;
|
||||
const uint8_t kNeoclimaButtonTurbo = 0x0A;
|
||||
const uint8_t kNeoclimaButtonLight = 0x0B;
|
||||
const uint8_t kNeoclimaButtonEcono = 0x0D;
|
||||
const uint8_t kNeoclimaButtonEye = 0x0E;
|
||||
const uint8_t kNeoclimaButtonFollow = 0x13;
|
||||
const uint8_t kNeoclimaButtonIon = 0x14;
|
||||
const uint8_t kNeoclimaButtonFresh = 0x15;
|
||||
const uint8_t kNeoclimaButton8CHeat = 0x1D;
|
||||
const uint8_t kNeoclimaButtonTempUnit = 0x1E;
|
||||
|
||||
const uint8_t kNeoclimaSwingVOn = 0b01;
|
||||
const uint8_t kNeoclimaSwingVOff = 0b10;
|
||||
const uint8_t kNeoclimaFanAuto = 0b00;
|
||||
const uint8_t kNeoclimaFanHigh = 0b01;
|
||||
const uint8_t kNeoclimaFanMed = 0b10;
|
||||
const uint8_t kNeoclimaFanLow = 0b11;
|
||||
|
||||
const uint8_t kNeoclimaFollowMe = 0x5D; // Also 0x5F
|
||||
|
||||
const uint8_t kNeoclimaMinTempC = 16; // 16C
|
||||
const uint8_t kNeoclimaMaxTempC = 32; // 32C
|
||||
const uint8_t kNeoclimaMinTempF = 61; // 61F
|
||||
const uint8_t kNeoclimaMaxTempF = 90; // 90F
|
||||
const uint8_t kNeoclimaAuto = 0b000;
|
||||
const uint8_t kNeoclimaCool = 0b001;
|
||||
const uint8_t kNeoclimaDry = 0b010;
|
||||
const uint8_t kNeoclimaFan = 0b011;
|
||||
const uint8_t kNeoclimaHeat = 0b100;
|
||||
|
||||
// Classes
|
||||
/// Class for handling detailed Neoclima A/C messages.
|
||||
class IRNeoclimaAc {
|
||||
public:
|
||||
explicit IRNeoclimaAc(const uint16_t pin, const bool inverted = false,
|
||||
const bool use_modulation = true);
|
||||
void stateReset(void);
|
||||
#if SEND_NEOCLIMA
|
||||
void send(const uint16_t repeat = kNeoclimaMinRepeat);
|
||||
/// 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_NEOCLIMA
|
||||
void begin(void);
|
||||
void setButton(const uint8_t button);
|
||||
uint8_t getButton(void) const;
|
||||
void on(void);
|
||||
void off(void);
|
||||
void setPower(const bool on);
|
||||
bool getPower(void) const;
|
||||
void setMode(const uint8_t mode);
|
||||
uint8_t getMode(void) const;
|
||||
void setTemp(const uint8_t temp, const bool celsius = true);
|
||||
uint8_t getTemp(void) const;
|
||||
void setFan(const uint8_t speed);
|
||||
uint8_t getFan(void) const;
|
||||
void setSwingV(const bool on);
|
||||
bool getSwingV(void) const;
|
||||
void setSwingH(const bool on);
|
||||
bool getSwingH(void) const;
|
||||
void setSleep(const bool on);
|
||||
bool getSleep(void) const;
|
||||
void setTurbo(const bool on);
|
||||
bool getTurbo(void) const;
|
||||
void setEcono(const bool on);
|
||||
bool getEcono(void) const;
|
||||
void setFresh(const bool on);
|
||||
bool getFresh(void) const;
|
||||
void setHold(const bool on);
|
||||
bool getHold(void) const;
|
||||
void setIon(const bool on);
|
||||
bool getIon(void) const;
|
||||
void setLight(const bool on);
|
||||
bool getLight(void) const;
|
||||
void set8CHeat(const bool on);
|
||||
bool get8CHeat(void) const;
|
||||
void setEye(const bool on);
|
||||
bool getEye(void) const;
|
||||
bool getTempUnits(void) const;
|
||||
// DISABLED: See TODO in ir_Neoclima.cpp
|
||||
// void setFollow(const bool on);
|
||||
bool getFollow(void) const;
|
||||
uint8_t* getRaw(void);
|
||||
void setRaw(const uint8_t new_code[],
|
||||
const uint16_t length = kNeoclimaStateLength);
|
||||
static bool validChecksum(const uint8_t state[],
|
||||
const uint16_t length = kNeoclimaStateLength);
|
||||
static uint8_t calcChecksum(const uint8_t state[],
|
||||
const uint16_t length = kNeoclimaStateLength);
|
||||
String toString(void) const;
|
||||
static uint8_t convertMode(const stdAc::opmode_t mode);
|
||||
static uint8_t convertFan(const stdAc::fanspeed_t speed);
|
||||
static stdAc::opmode_t toCommonMode(const uint8_t mode);
|
||||
static stdAc::fanspeed_t toCommonFanSpeed(const uint8_t speed);
|
||||
stdAc::state_t toCommon(void) const;
|
||||
#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
|
||||
NeoclimaProtocol _;
|
||||
void checksum(const uint16_t length = kNeoclimaStateLength);
|
||||
};
|
||||
|
||||
#endif // IR_NEOCLIMA_H_
|
||||
74
yoRadio/src/IRremoteESP8266/ir_Nikai.cpp
Normal file
74
yoRadio/src/IRremoteESP8266/ir_Nikai.cpp
Normal file
@@ -0,0 +1,74 @@
|
||||
// Copyright 2009 Ken Shirriff
|
||||
// Copyright 2017 David Conran
|
||||
|
||||
/// @file
|
||||
/// @brief Nikai
|
||||
/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/309
|
||||
|
||||
// Supports:
|
||||
// Brand: Nikai, Model: Unknown LCD TV
|
||||
|
||||
#include <algorithm>
|
||||
#include "IRrecv.h"
|
||||
#include "IRsend.h"
|
||||
#include "IRutils.h"
|
||||
|
||||
// Constants
|
||||
const uint16_t kNikaiTick = 500;
|
||||
const uint16_t kNikaiHdrMarkTicks = 8;
|
||||
const uint16_t kNikaiHdrMark = kNikaiHdrMarkTicks * kNikaiTick;
|
||||
const uint16_t kNikaiHdrSpaceTicks = 8;
|
||||
const uint16_t kNikaiHdrSpace = kNikaiHdrSpaceTicks * kNikaiTick;
|
||||
const uint16_t kNikaiBitMarkTicks = 1;
|
||||
const uint16_t kNikaiBitMark = kNikaiBitMarkTicks * kNikaiTick;
|
||||
const uint16_t kNikaiOneSpaceTicks = 2;
|
||||
const uint16_t kNikaiOneSpace = kNikaiOneSpaceTicks * kNikaiTick;
|
||||
const uint16_t kNikaiZeroSpaceTicks = 4;
|
||||
const uint16_t kNikaiZeroSpace = kNikaiZeroSpaceTicks * kNikaiTick;
|
||||
const uint16_t kNikaiMinGapTicks = 17;
|
||||
const uint16_t kNikaiMinGap = kNikaiMinGapTicks * kNikaiTick;
|
||||
|
||||
#if SEND_NIKAI
|
||||
/// Send a Nikai formatted message.
|
||||
/// Status: STABLE / Working.
|
||||
/// @param[in] data The message to be sent.
|
||||
/// @param[in] nbits The number of bits of message to be sent.
|
||||
/// @param[in] repeat The number of times the command is to be repeated.
|
||||
void IRsend::sendNikai(uint64_t data, uint16_t nbits, uint16_t repeat) {
|
||||
sendGeneric(kNikaiHdrMark, kNikaiHdrSpace, kNikaiBitMark, kNikaiOneSpace,
|
||||
kNikaiBitMark, kNikaiZeroSpace, kNikaiBitMark, kNikaiMinGap, data,
|
||||
nbits, 38, true, repeat, 33);
|
||||
}
|
||||
#endif // SEND_NIKAI
|
||||
|
||||
#if DECODE_NIKAI
|
||||
/// Decode the supplied Nikai message.
|
||||
/// Status: STABLE / Working.
|
||||
/// @param[in,out] results Ptr to the data to decode & where to store the result
|
||||
/// @param[in] offset The starting index to use when attempting to decode the
|
||||
/// raw data. Typically/Defaults to kStartOffset.
|
||||
/// @param[in] nbits The number of data bits to expect.
|
||||
/// @param[in] strict Flag indicating if we should perform strict matching.
|
||||
bool IRrecv::decodeNikai(decode_results *results, uint16_t offset,
|
||||
const uint16_t nbits, const bool strict) {
|
||||
if (strict && nbits != kNikaiBits)
|
||||
return false; // We expect Nikai to be a certain sized message.
|
||||
|
||||
uint64_t data = 0;
|
||||
|
||||
// Match Header + Data + Footer
|
||||
if (!matchGeneric(results->rawbuf + offset, &data,
|
||||
results->rawlen - offset, nbits,
|
||||
kNikaiHdrMark, kNikaiHdrSpace,
|
||||
kNikaiBitMark, kNikaiOneSpace,
|
||||
kNikaiBitMark, kNikaiZeroSpace,
|
||||
kNikaiBitMark, kNikaiMinGap, true)) return false;
|
||||
// Success
|
||||
results->bits = nbits;
|
||||
results->value = data;
|
||||
results->decode_type = NIKAI;
|
||||
results->command = 0;
|
||||
results->address = 0;
|
||||
return true;
|
||||
}
|
||||
#endif // DECODE_NIKAI
|
||||
1350
yoRadio/src/IRremoteESP8266/ir_Panasonic.cpp
Normal file
1350
yoRadio/src/IRremoteESP8266/ir_Panasonic.cpp
Normal file
File diff suppressed because it is too large
Load Diff
271
yoRadio/src/IRremoteESP8266/ir_Panasonic.h
Normal file
271
yoRadio/src/IRremoteESP8266/ir_Panasonic.h
Normal file
@@ -0,0 +1,271 @@
|
||||
// Copyright 2018 David Conran
|
||||
|
||||
/// @file
|
||||
/// @brief Support for Panasonic protocols.
|
||||
/// @see Panasonic A/C support heavily influenced by https://github.com/ToniA/ESPEasy/blob/HeatpumpIR/lib/HeatpumpIR/PanasonicHeatpumpIR.cpp
|
||||
|
||||
// Supports:
|
||||
// Brand: Panasonic, Model: TV (PANASONIC)
|
||||
// Brand: Panasonic, Model: NKE series A/C (PANASONIC_AC NKE/2)
|
||||
// Brand: Panasonic, Model: DKE series A/C (PANASONIC_AC DKE/3)
|
||||
// Brand: Panasonic, Model: DKW series A/C (PANASONIC_AC DKE/3)
|
||||
// Brand: Panasonic, Model: PKR series A/C (PANASONIC_AC DKE/3)
|
||||
// Brand: Panasonic, Model: JKE series A/C (PANASONIC_AC JKE/4)
|
||||
// Brand: Panasonic, Model: CKP series A/C (PANASONIC_AC CKP/5)
|
||||
// Brand: Panasonic, Model: RKR series A/C (PANASONIC_AC RKR/6)
|
||||
// Brand: Panasonic, Model: CS-ME10CKPG A/C (PANASONIC_AC CKP/5)
|
||||
// Brand: Panasonic, Model: CS-ME12CKPG A/C (PANASONIC_AC CKP/5)
|
||||
// Brand: Panasonic, Model: CS-ME14CKPG A/C (PANASONIC_AC CKP/5)
|
||||
// Brand: Panasonic, Model: CS-E7PKR A/C (PANASONIC_AC DKE/2)
|
||||
// Brand: Panasonic, Model: CS-Z9RKR A/C (PANASONIC_AC RKR/6)
|
||||
// Brand: Panasonic, Model: CS-Z24RKR A/C (PANASONIC_AC RKR/6)
|
||||
// Brand: Panasonic, Model: CS-YW9MKD A/C (PANASONIC_AC JKE/4)
|
||||
// Brand: Panasonic, Model: A75C2311 remote (PANASONIC_AC CKP/5)
|
||||
// Brand: Panasonic, Model: A75C2616-1 remote (PANASONIC_AC DKE/3)
|
||||
// Brand: Panasonic, Model: A75C3704 remote (PANASONIC_AC DKE/3)
|
||||
// Brand: Panasonic, Model: A75C3747 remote (PANASONIC_AC JKE/4)
|
||||
// Brand: Panasonic, Model: CS-E9CKP series A/C (PANASONIC_AC32)
|
||||
// Brand: Panasonic, Model: A75C2295 remote (PANASONIC_AC32)
|
||||
// Brand: Panasonic, Model: A75C4762 remote (PANASONIC_AC RKR/6)
|
||||
|
||||
#ifndef IR_PANASONIC_H_
|
||||
#define IR_PANASONIC_H_
|
||||
|
||||
#define __STDC_LIMIT_MACROS
|
||||
#include <stdint.h>
|
||||
#ifdef ARDUINO
|
||||
#include <Arduino.h>
|
||||
#endif
|
||||
#include "IRremoteESP8266.h"
|
||||
#include "IRsend.h"
|
||||
#ifdef UNIT_TEST
|
||||
#include "IRsend_test.h"
|
||||
#endif
|
||||
|
||||
// Constants
|
||||
const uint16_t kPanasonicFreq = 36700;
|
||||
const uint16_t kPanasonicAcExcess = 0;
|
||||
// Much higher than usual. See issue #540.
|
||||
const uint16_t kPanasonicAcTolerance = 40;
|
||||
|
||||
const uint8_t kPanasonicAcAuto = 0; // 0b000
|
||||
const uint8_t kPanasonicAcDry = 2; // 0b010
|
||||
const uint8_t kPanasonicAcCool = 3; // 0b011
|
||||
const uint8_t kPanasonicAcHeat = 4; // 0b010
|
||||
const uint8_t kPanasonicAcFan = 6; // 0b110
|
||||
const uint8_t kPanasonicAcFanMin = 0;
|
||||
const uint8_t kPanasonicAcFanLow = 1;
|
||||
const uint8_t kPanasonicAcFanMed = 2;
|
||||
const uint8_t kPanasonicAcFanHigh = 3;
|
||||
const uint8_t kPanasonicAcFanMax = 4;
|
||||
const uint8_t kPanasonicAcFanAuto = 7;
|
||||
const uint8_t kPanasonicAcFanDelta = 3;
|
||||
const uint8_t kPanasonicAcPowerOffset = 0;
|
||||
const uint8_t kPanasonicAcTempOffset = 1; // Bits
|
||||
const uint8_t kPanasonicAcTempSize = 5; // Bits
|
||||
const uint8_t kPanasonicAcMinTemp = 16; // Celsius
|
||||
const uint8_t kPanasonicAcMaxTemp = 30; // Celsius
|
||||
const uint8_t kPanasonicAcFanModeTemp = 27; // Celsius
|
||||
const uint8_t kPanasonicAcQuietOffset = 0;
|
||||
const uint8_t kPanasonicAcPowerfulOffset = 5; // 0b100000
|
||||
// CKP & RKR models have Powerful and Quiet bits swapped.
|
||||
const uint8_t kPanasonicAcQuietCkpOffset = kPanasonicAcPowerfulOffset;
|
||||
const uint8_t kPanasonicAcPowerfulCkpOffset = kPanasonicAcQuietOffset;
|
||||
const uint8_t kPanasonicAcSwingVHighest = 0x1; // 0b0001
|
||||
const uint8_t kPanasonicAcSwingVHigh = 0x2; // 0b0010
|
||||
const uint8_t kPanasonicAcSwingVMiddle = 0x3; // 0b0011
|
||||
const uint8_t kPanasonicAcSwingVLow = 0x4; // 0b0100
|
||||
const uint8_t kPanasonicAcSwingVLowest = 0x5; // 0b0101
|
||||
const uint8_t kPanasonicAcSwingVAuto = 0xF; // 0b1111
|
||||
|
||||
const uint8_t kPanasonicAcSwingHMiddle = 0x6; // 0b0110
|
||||
const uint8_t kPanasonicAcSwingHFullLeft = 0x9; // 0b1001
|
||||
const uint8_t kPanasonicAcSwingHLeft = 0xA; // 0b1010
|
||||
const uint8_t kPanasonicAcSwingHRight = 0xB; // 0b1011
|
||||
const uint8_t kPanasonicAcSwingHFullRight = 0xC; // 0b1100
|
||||
const uint8_t kPanasonicAcSwingHAuto = 0xD; // 0b1101
|
||||
const uint8_t kPanasonicAcChecksumInit = 0xF4;
|
||||
const uint8_t kPanasonicAcOnTimerOffset = 1;
|
||||
const uint8_t kPanasonicAcOffTimerOffset = 2;
|
||||
const uint8_t kPanasonicAcTimeSize = 11; // Bits
|
||||
const uint8_t kPanasonicAcTimeOverflowSize = 3; // Bits
|
||||
const uint16_t kPanasonicAcTimeMax = 23 * 60 + 59; // Mins since midnight.
|
||||
const uint16_t kPanasonicAcTimeSpecial = 0x600;
|
||||
|
||||
const uint8_t kPanasonicAcIonFilterByte = 22; // Byte
|
||||
const uint8_t kPanasonicAcIonFilterOffset = 0; // Bit
|
||||
|
||||
const uint8_t kPanasonicKnownGoodState[kPanasonicAcStateLength] = {
|
||||
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};
|
||||
|
||||
/// Class for handling detailed Panasonic A/C messages.
|
||||
class IRPanasonicAc {
|
||||
public:
|
||||
explicit IRPanasonicAc(const uint16_t pin, const bool inverted = false,
|
||||
const bool use_modulation = true);
|
||||
void stateReset(void);
|
||||
#if SEND_PANASONIC_AC
|
||||
void send(const uint16_t repeat = kPanasonicAcDefaultRepeat);
|
||||
/// 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_PANASONIC_AC
|
||||
void begin(void);
|
||||
void on(void);
|
||||
void off(void);
|
||||
void setPower(const bool on);
|
||||
bool getPower(void);
|
||||
void setTemp(const uint8_t temp, const bool remember = true);
|
||||
uint8_t getTemp(void);
|
||||
void setFan(const uint8_t fan);
|
||||
uint8_t getFan(void);
|
||||
void setMode(const uint8_t mode);
|
||||
uint8_t getMode(void);
|
||||
void setRaw(const uint8_t state[]);
|
||||
uint8_t *getRaw(void);
|
||||
static bool validChecksum(const uint8_t *state,
|
||||
const uint16_t length = kPanasonicAcStateLength);
|
||||
static uint8_t calcChecksum(const uint8_t *state,
|
||||
const uint16_t length = kPanasonicAcStateLength);
|
||||
void setQuiet(const bool on);
|
||||
bool getQuiet(void);
|
||||
void setPowerful(const bool on);
|
||||
bool getPowerful(void);
|
||||
void setIon(const bool on);
|
||||
bool getIon(void);
|
||||
void setModel(const panasonic_ac_remote_model_t model);
|
||||
panasonic_ac_remote_model_t getModel(void);
|
||||
void setSwingVertical(const uint8_t elevation);
|
||||
uint8_t getSwingVertical(void);
|
||||
void setSwingHorizontal(const uint8_t direction);
|
||||
uint8_t getSwingHorizontal(void);
|
||||
static uint16_t encodeTime(const uint8_t hours, const uint8_t mins);
|
||||
uint16_t getClock(void);
|
||||
void setClock(const uint16_t mins_since_midnight);
|
||||
uint16_t getOnTimer(void);
|
||||
void setOnTimer(const uint16_t mins_since_midnight, const bool enable = true);
|
||||
void cancelOnTimer(void);
|
||||
bool isOnTimerEnabled(void);
|
||||
uint16_t getOffTimer(void);
|
||||
void setOffTimer(const uint16_t mins_since_midnight,
|
||||
const bool enable = true);
|
||||
void cancelOffTimer(void);
|
||||
bool isOffTimerEnabled(void);
|
||||
static uint8_t convertMode(const stdAc::opmode_t mode);
|
||||
static uint8_t convertFan(const stdAc::fanspeed_t speed);
|
||||
static uint8_t convertSwingV(const stdAc::swingv_t position);
|
||||
static uint8_t convertSwingH(const stdAc::swingh_t position);
|
||||
static stdAc::opmode_t toCommonMode(const uint8_t mode);
|
||||
static stdAc::fanspeed_t toCommonFanSpeed(const uint8_t speed);
|
||||
static stdAc::swingv_t toCommonSwingV(const uint8_t pos);
|
||||
static stdAc::swingh_t toCommonSwingH(const uint8_t pos);
|
||||
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_state[kPanasonicAcStateLength]; ///< The state in code form.
|
||||
uint8_t _swingh;
|
||||
uint8_t _temp;
|
||||
void fixChecksum(const uint16_t length = kPanasonicAcStateLength);
|
||||
static uint16_t _getTime(const uint8_t ptr[]);
|
||||
static void _setTime(uint8_t * const ptr, const uint16_t mins_since_midnight,
|
||||
const bool round_down);
|
||||
};
|
||||
|
||||
/// Native representation of a Panasonic 32-bit A/C message.
|
||||
union PanasonicAc32Protocol {
|
||||
uint32_t raw; ///< The state in IR code form.
|
||||
struct {
|
||||
// Byte 0
|
||||
uint8_t :3;
|
||||
uint8_t SwingH :1;
|
||||
uint8_t SwingV :3;
|
||||
uint8_t :1; ///< Always appears to be set. (1)
|
||||
// Byte 1
|
||||
uint8_t :8; // Always seems to be 0x36.
|
||||
// Byte 2
|
||||
uint8_t Temp :4;
|
||||
uint8_t Fan :4;
|
||||
// Byte 3
|
||||
uint8_t Mode :3;
|
||||
uint8_t PowerToggle :1; // 0 means toggle, 1 = keep the same.
|
||||
uint8_t :4;
|
||||
};
|
||||
};
|
||||
|
||||
const uint8_t kPanasonicAc32Fan = 1; // 0b001
|
||||
const uint8_t kPanasonicAc32Cool = 2; // 0b010
|
||||
const uint8_t kPanasonicAc32Dry = 3; // 0b011
|
||||
const uint8_t kPanasonicAc32Heat = 4; // 0b010
|
||||
const uint8_t kPanasonicAc32Auto = 6; // 0b110
|
||||
|
||||
const uint8_t kPanasonicAc32FanMin = 2;
|
||||
const uint8_t kPanasonicAc32FanLow = 3;
|
||||
const uint8_t kPanasonicAc32FanMed = 4;
|
||||
const uint8_t kPanasonicAc32FanHigh = 5;
|
||||
const uint8_t kPanasonicAc32FanMax = 6;
|
||||
const uint8_t kPanasonicAc32FanAuto = 0xF;
|
||||
const uint8_t kPanasonicAc32SwingVAuto = 0x7; // 0b111
|
||||
const uint32_t kPanasonicAc32KnownGood = 0x0AF136FC; ///< Cool, Auto, 16C
|
||||
|
||||
/// Class for handling detailed Panasonic 32bit A/C messages.
|
||||
class IRPanasonicAc32 {
|
||||
public:
|
||||
explicit IRPanasonicAc32(const uint16_t pin, const bool inverted = false,
|
||||
const bool use_modulation = true);
|
||||
void stateReset(void);
|
||||
#if SEND_PANASONIC_AC32
|
||||
void send(const uint16_t repeat = kPanasonicAcDefaultRepeat);
|
||||
/// 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_PANASONIC_AC32
|
||||
void begin(void);
|
||||
void setPowerToggle(const bool on);
|
||||
bool getPowerToggle(void) const;
|
||||
void setTemp(const uint8_t temp);
|
||||
uint8_t getTemp(void) const;
|
||||
void setFan(const uint8_t fan);
|
||||
uint8_t getFan(void) const;
|
||||
void setMode(const uint8_t mode);
|
||||
uint8_t getMode(void) const;
|
||||
void setRaw(const uint32_t state);
|
||||
uint32_t getRaw(void) const;
|
||||
void setSwingVertical(const uint8_t pos);
|
||||
uint8_t getSwingVertical(void) const;
|
||||
void setSwingHorizontal(const bool on);
|
||||
bool getSwingHorizontal(void) const;
|
||||
static uint8_t convertMode(const stdAc::opmode_t mode);
|
||||
static uint8_t convertFan(const stdAc::fanspeed_t speed);
|
||||
static uint8_t convertSwingV(const stdAc::swingv_t position);
|
||||
static stdAc::opmode_t toCommonMode(const uint8_t mode);
|
||||
static stdAc::fanspeed_t toCommonFanSpeed(const uint8_t speed);
|
||||
static stdAc::swingv_t toCommonSwingV(const uint8_t pos);
|
||||
stdAc::state_t toCommon(const stdAc::state_t *prev = NULL) const;
|
||||
String toString(void) const;
|
||||
#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
|
||||
PanasonicAc32Protocol _; ///< The state in code form.
|
||||
};
|
||||
|
||||
#endif // IR_PANASONIC_H_
|
||||
143
yoRadio/src/IRremoteESP8266/ir_Pioneer.cpp
Normal file
143
yoRadio/src/IRremoteESP8266/ir_Pioneer.cpp
Normal file
@@ -0,0 +1,143 @@
|
||||
// Copyright 2009 Ken Shirriff
|
||||
// Copyright 2017, 2018 David Conran
|
||||
// Copyright 2018 Kamil Palczewski
|
||||
// Copyright 2019 s-hadinger
|
||||
|
||||
/// @file
|
||||
/// @brief Pioneer remote emulation
|
||||
/// @see http://www.adrian-kingston.com/IRFormatPioneer.htm
|
||||
/// @see https://github.com/crankyoldgit/IRremoteESP8266/pull/547
|
||||
/// @see https://www.pioneerelectronics.com/PUSA/Support/Home-Entertainment-Custom-Install/IR+Codes/A+V+Receivers
|
||||
/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1220
|
||||
/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1749#issuecomment-1028122645
|
||||
|
||||
// Supports:
|
||||
// Brand: Pioneer, Model: AV Receivers
|
||||
// Brand: Pioneer, Model: VSX-324 AV Receiver
|
||||
// Brand: Pioneer, Model: AXD7690 Remote
|
||||
|
||||
#define __STDC_LIMIT_MACROS
|
||||
#include <stdint.h>
|
||||
#include <algorithm>
|
||||
#include "IRrecv.h"
|
||||
#include "IRsend.h"
|
||||
#include "IRutils.h"
|
||||
|
||||
// Constants
|
||||
// Ref: https://github.com/crankyoldgit/IRremoteESP8266/issues/1220
|
||||
const uint16_t kPioneerTick = 534; ///< uSeconds.
|
||||
const uint16_t kPioneerHdrMark = 8506; ///< uSeconds.
|
||||
const uint16_t kPioneerHdrSpace = 4191; ///< uSeconds.
|
||||
const uint16_t kPioneerBitMark = 568; ///< uSeconds.
|
||||
const uint16_t kPioneerOneSpace = 1542; ///< uSeconds.
|
||||
const uint16_t kPioneerZeroSpace = 487; ///< uSeconds.
|
||||
const uint32_t kPioneerMinCommandLength = 84906; ///< uSeconds.
|
||||
const uint32_t kPioneerMinGap = 25181; ///< uSeconds.
|
||||
|
||||
#if SEND_PIONEER
|
||||
/// Send a raw Pioneer formatted message.
|
||||
/// Status: STABLE / Expected to be working.
|
||||
/// @param[in] data The message to be sent.
|
||||
/// @param[in] nbits The number of bits of message to be sent.
|
||||
/// @param[in] repeat The number of times the command is to be repeated.
|
||||
void IRsend::sendPioneer(const uint64_t data, const uint16_t nbits,
|
||||
const uint16_t repeat) {
|
||||
// If nbits is to big, abort.
|
||||
if (nbits > sizeof(data) * 8) return;
|
||||
for (uint16_t r = 0; r <= repeat; r++) {
|
||||
// don't use NEC repeat but repeat the whole sequence
|
||||
if (nbits > 32) {
|
||||
sendGeneric(kPioneerHdrMark, kPioneerHdrSpace,
|
||||
kPioneerBitMark, kPioneerOneSpace,
|
||||
kPioneerBitMark, kPioneerZeroSpace,
|
||||
kPioneerBitMark, kPioneerMinGap,
|
||||
kPioneerMinCommandLength,
|
||||
data >> 32, nbits - 32, 40, true, 0, 33);
|
||||
}
|
||||
sendGeneric(kPioneerHdrMark, kPioneerHdrSpace,
|
||||
kPioneerBitMark, kPioneerOneSpace,
|
||||
kPioneerBitMark, kPioneerZeroSpace,
|
||||
kPioneerBitMark, kPioneerMinGap,
|
||||
kPioneerMinCommandLength,
|
||||
data, nbits > 32 ? 32 : nbits, 40, true, 0, 33);
|
||||
}
|
||||
}
|
||||
|
||||
/// Calculate the raw Pioneer data code based on two NEC sub-codes
|
||||
/// Status: STABLE / Expected to work.
|
||||
/// @param[in] address A 16-bit "published" NEC value.
|
||||
/// @param[in] command A 16-bit "published" NEC value.
|
||||
/// @return A raw 64-bit Pioneer message code for use with `sendPioneer()``
|
||||
/// @note Address & Command can be take from a decode result OR from the
|
||||
/// spreadsheets located at:
|
||||
/// https://www.pioneerelectronics.com/PUSA/Support/Home-Entertainment-Custom-Install/IR+Codes/A+V+Receivers
|
||||
/// where the first part is considered the address,
|
||||
/// and the second the command.
|
||||
/// e.g.
|
||||
/// "A556+AF20" is an Address of 0xA556 & a Command of 0xAF20.
|
||||
/// @note If the Address is 0, use it like the following:
|
||||
/// `irsend.sendPioneer(irsend.encodePioneer(0, 0xAA1C), 32, 1);` or
|
||||
/// `irsend.sendPioneer(irsend.encodePioneer(0xAA1C, 0xAA1C), 64, 0);`
|
||||
/// @see https://github.com/crankyoldgit/IRremoteESP8266/issues/1749#issuecomment-1028122645
|
||||
uint64_t IRsend::encodePioneer(const uint16_t address, const uint16_t command) {
|
||||
return (((uint64_t)encodeNEC(address >> 8, address & 0xFF)) << 32) |
|
||||
encodeNEC(command >> 8, command & 0xFF);
|
||||
}
|
||||
#endif // SEND_PIONEER
|
||||
|
||||
#if DECODE_PIONEER
|
||||
/// Decode the supplied Pioneer message.
|
||||
/// Status: STABLE / Should be working. (Self decodes & real examples)
|
||||
/// @param[in,out] results Ptr to the data to decode & where to store the result
|
||||
/// @param[in] offset The starting index to use when attempting to decode the
|
||||
/// raw data. Typically/Defaults to kStartOffset.
|
||||
/// @param[in] nbits The number of data bits to expect.
|
||||
/// @param[in] strict Flag indicating if we should perform strict matching.
|
||||
/// @return True if it can decode it, false if it can't.
|
||||
bool IRrecv::decodePioneer(decode_results *results, uint16_t offset,
|
||||
const uint16_t nbits, const bool strict) {
|
||||
if (results->rawlen < 2 * (nbits + kHeader + kFooter) - 1 + offset)
|
||||
return false; // Can't possibly be a valid Pioneer message.
|
||||
if (strict && nbits != kPioneerBits)
|
||||
return false; // Not strictly an Pioneer message.
|
||||
|
||||
uint64_t data = 0;
|
||||
results->value = 0;
|
||||
for (uint16_t section = 0; section < 2; section++) {
|
||||
// Match Header + Data + Footer
|
||||
uint16_t used;
|
||||
used = matchGeneric(results->rawbuf + offset, &data,
|
||||
results->rawlen - offset, nbits / 2,
|
||||
kPioneerHdrMark, kPioneerHdrSpace,
|
||||
kPioneerBitMark, kPioneerOneSpace,
|
||||
kPioneerBitMark, kPioneerZeroSpace,
|
||||
kPioneerBitMark, kPioneerMinGap, true);
|
||||
if (!used) return false;
|
||||
offset += used;
|
||||
uint8_t command = data >> 8;
|
||||
uint8_t command_inverted = data;
|
||||
uint8_t address = data >> 24;
|
||||
uint8_t address_inverted = data >> 16;
|
||||
// Compliance
|
||||
if (strict) {
|
||||
if (command != (command_inverted ^ 0xFF))
|
||||
return false; // Command integrity failed.
|
||||
if (address != (address_inverted ^ 0xFF))
|
||||
return false; // Address integrity failed.
|
||||
}
|
||||
results->value = (results->value << (nbits / 2)) + data;
|
||||
// NEC-like commands and addresses are technically in LSB first order so the
|
||||
// final versions have to be reversed.
|
||||
uint16_t code = reverseBits((command << 8) + address, 16);
|
||||
if (section)
|
||||
results->command = code;
|
||||
else
|
||||
results->address = code;
|
||||
}
|
||||
|
||||
// Success
|
||||
results->bits = nbits;
|
||||
results->decode_type = PIONEER;
|
||||
return true;
|
||||
}
|
||||
#endif // DECODE_PIONEER
|
||||
107
yoRadio/src/IRremoteESP8266/ir_Pronto.cpp
Normal file
107
yoRadio/src/IRremoteESP8266/ir_Pronto.cpp
Normal file
@@ -0,0 +1,107 @@
|
||||
// Copyright 2017 David Conran
|
||||
|
||||
/// @file
|
||||
/// @brief Pronto code message generation
|
||||
/// @see http://www.etcwiki.org/wiki/Pronto_Infrared_Format
|
||||
/// @see http://www.remotecentral.com/features/irdisp2.htm
|
||||
/// @see http://harctoolbox.org/Glossary.html#ProntoSemantics
|
||||
/// @see https://irdb.globalcache.com/
|
||||
|
||||
// Supports:
|
||||
// Brand: Pronto, Model: Pronto Hex
|
||||
|
||||
#include <algorithm>
|
||||
#include "IRsend.h"
|
||||
|
||||
// Constants
|
||||
const float kProntoFreqFactor = 0.241246;
|
||||
const uint16_t kProntoTypeOffset = 0;
|
||||
const uint16_t kProntoFreqOffset = 1;
|
||||
const uint16_t kProntoSeq1LenOffset = 2;
|
||||
const uint16_t kProntoSeq2LenOffset = 3;
|
||||
const uint16_t kProntoDataOffset = 4;
|
||||
|
||||
#if SEND_PRONTO
|
||||
/// Send a Pronto Code formatted message.
|
||||
/// Status: STABLE / Known working.
|
||||
/// @param[in] data An array of uint16_t containing the pronto codes.
|
||||
/// @param[in] len Nr. of entries in the data[] array.
|
||||
/// @param[in] repeat Nr. of times to repeat the message.
|
||||
/// @note Pronto codes are typically represented in hexadecimal.
|
||||
/// You will need to convert the code to an array of integers, and calculate
|
||||
/// it's length.
|
||||
/// e.g.
|
||||
/// @code
|
||||
/// A Sony 20 bit DVD remote command.
|
||||
/// "0000 0067 0000 0015 0060 0018 0018 0018 0030 0018 0030 0018 0030 0018
|
||||
/// 0018 0018 0030 0018 0018 0018 0018 0018 0030 0018 0018 0018 0030 0018
|
||||
/// 0030 0018 0030 0018 0018 0018 0018 0018 0030 0018 0018 0018 0018 0018
|
||||
/// 0030 0018 0018 03f6"
|
||||
/// @endcode
|
||||
/// converts to:
|
||||
/// @code{.cpp}
|
||||
/// uint16_t prontoCode[46] = {
|
||||
/// 0x0000, 0x0067, 0x0000, 0x0015,
|
||||
/// 0x0060, 0x0018, 0x0018, 0x0018, 0x0030, 0x0018, 0x0030, 0x0018,
|
||||
/// 0x0030, 0x0018, 0x0018, 0x0018, 0x0030, 0x0018, 0x0018, 0x0018,
|
||||
/// 0x0018, 0x0018, 0x0030, 0x0018, 0x0018, 0x0018, 0x0030, 0x0018,
|
||||
/// 0x0030, 0x0018, 0x0030, 0x0018, 0x0018, 0x0018, 0x0018, 0x0018,
|
||||
/// 0x0030, 0x0018, 0x0018, 0x0018, 0x0018, 0x0018, 0x0030, 0x0018,
|
||||
/// 0x0018, 0x03f6};
|
||||
/// // Send the Pronto(Sony) code. Repeat twice as Sony's require that.
|
||||
/// sendPronto(prontoCode, 46, kSonyMinRepeat);
|
||||
/// @endcode
|
||||
/// @see http://www.etcwiki.org/wiki/Pronto_Infrared_Format
|
||||
/// @see http://www.remotecentral.com/features/irdisp2.htm
|
||||
void IRsend::sendPronto(uint16_t data[], uint16_t len, uint16_t repeat) {
|
||||
// Check we have enough data to work out what to send.
|
||||
if (len < kProntoMinLength) return;
|
||||
|
||||
// We only know how to deal with 'raw' pronto codes types. Reject all others.
|
||||
if (data[kProntoTypeOffset] != 0) return;
|
||||
|
||||
// Pronto frequency is in Hz.
|
||||
uint16_t hz =
|
||||
(uint16_t)(1000000U / (data[kProntoFreqOffset] * kProntoFreqFactor));
|
||||
enableIROut(hz);
|
||||
|
||||
// Grab the length of the two sequences.
|
||||
uint16_t seq_1_len = data[kProntoSeq1LenOffset] * 2;
|
||||
uint16_t seq_2_len = data[kProntoSeq2LenOffset] * 2;
|
||||
// Calculate where each sequence starts in the buffer.
|
||||
uint16_t seq_1_start = kProntoDataOffset;
|
||||
uint16_t seq_2_start = kProntoDataOffset + seq_1_len;
|
||||
|
||||
uint32_t periodic_time_x10 = calcUSecPeriod(hz / 10, false);
|
||||
|
||||
// Normal (1st sequence) case.
|
||||
// Is there a first (normal) sequence to send?
|
||||
if (seq_1_len > 0) {
|
||||
// Check we have enough data to send the complete first sequence.
|
||||
if (seq_1_len + seq_1_start > len) return;
|
||||
// Send the contents of the 1st sequence.
|
||||
for (uint16_t i = seq_1_start; i < seq_1_start + seq_1_len; i += 2) {
|
||||
mark((data[i] * periodic_time_x10) / 10);
|
||||
space((data[i + 1] * periodic_time_x10) / 10);
|
||||
}
|
||||
} else {
|
||||
// There was no first sequence to send, it is implied that we have to send
|
||||
// the 2nd/repeat sequence an additional time. i.e. At least once.
|
||||
repeat++;
|
||||
}
|
||||
|
||||
// Repeat (2nd sequence) case.
|
||||
// Is there a second (repeat) sequence to be sent?
|
||||
if (seq_2_len > 0) {
|
||||
// Check we have enough data to send the complete second sequence.
|
||||
if (seq_2_len + seq_2_start > len) return;
|
||||
|
||||
// Send the contents of the 2nd sequence.
|
||||
for (uint16_t r = 0; r < repeat; r++)
|
||||
for (uint16_t i = seq_2_start; i < seq_2_start + seq_2_len; i += 2) {
|
||||
mark((data[i] * periodic_time_x10) / 10);
|
||||
space((data[i + 1] * periodic_time_x10) / 10);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif // SEND_PRONTO
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user